;------------------------------------------------------------------------
;
;USER AREA for Lifeboat CP/M2 (Micropolis disk controller)
;  Customized for a Poly-88 running the POLEX PROM. POLEX takes care of
;  run-time swapping of RAM and PROM in the lower 4K of address space
;  and provides console and serial I/O for CP/M.
;
;  Change MSIZE to the desired CP/M memory size in K. 
;  Change MAXTRK to 76 for Mod-II drives, 34 for Mod-I drives.
;  Set FSTEP0 and/or FSTEP1 for fast stepping drives
;
;  Patch a CP/M image saved after running MOVCPM for the Micropolis
;  as follows:
;
;	A>DDT CPMxx.COM
;
;	-IPOLYIO.HEX
;	-Rxxxx      (where xxxx = OFFSET defined below)
;
;  The default IOBYTE can be patched in a CPM image loaded in DDT at
;  address 261Dh or changed in this file.
;
;  Version History
;
;  Rev	   Date	    	Desc
;  1.0	12/03/13   M. Douglas, Original
;
;  1.1	06/25/16   M. Douglas, support 77 and 35 track drives in
;	   a single file. Implement required BIOS patches with static
;	   data and code in this file. Add support for fast stepping
;	   drives (3ms step interval)
;
;  1.2	07/25/16   M. Douglas, add IOBYTE support.
;
;  1.3  11/15/17   M. Douglas, in conOut, don't send two CR's in a row
;	   to the video output. Fix listSt status check when CRT is
;	   the output device (always return FFh "ready")	
;
;------------------------------------------------------------------------
MSIZE	EQU	56		;Distribution size

;MAXTRK	EQU	76		;Select max track number
MAXTRK	EQU	34

FSTEP0	EQU	0		;drive 0 fast step (3ms) if set
FSTEP1	EQU	0		;drive 1 fast step (3ms) if set

; These equates are automatically changed by MSIZE.

BIOS	EQU 	(MSIZE*1024)-0C00H	;Memory location of BIOS
USER	EQU	BIOS+600H	;and of this USER AREA
OFFSET	EQU	2600H-USER	;To overlay SYSGEN image

; Location of drive parameters to patch in the BIOS

BLOCKS	EQU	USER+0245H	;location of blocks in BIOS
BSH	EQU	USER+0297H	;location of BSH in DPH in BIOS
BLM	EQU	USER+0298H	;location of BLM in DPH in BIOS
DRM	EQU	USER+029CH	;location of DRM in DPH in BIOS

; Step rate patch for 3ms step rate versus 30ms

DRVNUM	equ	BIOS+08A4h	;address of drive number in BIOS
STPLOAD	EQU	BIOS+03C2h	;address of LXI D,30 instruction in BIOS

; IOBYTE related equates

IOBYTE	EQU	03H		;run-time location of IOBYTE in CP/M
SHFTCON	EQU	1		;left rotate count to align CON in bits 2,1
SHFTRDR	EQU	7		;left rotate count to align RDR
SHFTPUN	EQU	5		;left rotate count to align PUN
SHFTLST	EQU	3		;left rotate count to align LST

; POLEX (Custom Poly-88 Executive) Entry points

PXCSTA	EQU	0F403h		;console input status
PXCIN	EQU	0F406h		;console input
PXCOUT	EQU	0F409h		;console output
PXSSTA	EQU	0F40Ch		;serial port input status
PXSIN	EQU	0F40Fh		;serial port input
PXSOUT	EQU	0F412h		;serial port output
PXSOST	EQU	0F415h		;serial port output status

;----------------------------------------------------------------------
;  Disk size parameters and the mode byte are in the BIOS image just
;  prior to the USER area.
;----------------------------------------------------------------------
	ORG	USER-6		;Disk size parameters in BIOS image

DRVTRKS	DB	MAXTRK,MAXTRK,MAXTRK,MAXTRK	;Max track per drive
	DB	01Bh		;This constant is in the BIOS
MODE	DB	0D0h		;Mode byte with interrupt enable set

;----------------------------------------------------------------------
; JUMP TABLE - Jumps MUST remain here in same order.
;----------------------------------------------------------------------
	ORG	USER		;Start of USER AREA
UCINIT	JMP	CINITR		;Cold boot init
UWINIT	JMP	WINITR		;Warm boot init
UCONST	JMP	const		;Console input status
UCONIN	JMP	conin		;Console input
UCONOUT	JMP	conout		;Console output
ULIST	JMP	list		;Printer output on serial port
UPUNCH	JMP	punch		;Punch output on serial port
UREADER	JMP	reader		;Reader input on serial port
ULISTST	JMP	listst		;Printer output status on serial port

; This 8 byte data area used externally MUST remain.

LENUA	DW	USRLEN		;Length of USER AREA

USRIOB	DB	95H		;"I" Initial IOBYT
				;   LST = LPT (serial port)
				;   PUN = PTP (serial port)
				;   RDR = PTR (serial port)
				;   CON = CRT (keyboard/video)
				;   at 261Dh in a DDT loaded CPM image

HSTYPE	DB	0		;"H" Handshaking type
NULLOC	DB	0		;"J" Printer nulls
lastChr	DB	0,0,0		;Reserved

;-----------------------------------------------------------------------
; CINITR, WINITR 
;   Cold and warm boot initialization
;-----------------------------------------------------------------------
WINITR
	;Any warm boot initialization goes here.
	RET

; Cold boot initialization: If using a 35 track drive, patch the BIOS
;   to use 1K blocks instead of 2K blocks and reduce the number
;   of directory entries to 64 from 128 to more efficiently use
;   the smaller disk.

CINITR	LDA	USRIOB		;Intialize default I/O byte for CP/M
	STA	IOBYTE

 IF MAXTRK SHR 1		;Test for 35 track disks

	MVI	A,131		;Use 132 1K blocks vs 66 2K blocks	
	STA	BLOCKS

	MVI	A,3		;Shift factor for 1K blocks
	STA	BSH

	MVI	A,7		;Mask for 1K blocks
	STA	BLM

	MVI	A,63		;64 directory entries vs 128
	STA	DRM
	
 ENDIF

; Patch for optional fast stepping drives.
;   Patch the BIOS with a call to STEPDLY below where the LXI D,30
;   instruction to set a 30ms step time is located.

 IF (FSTEP0 OR FSTEP1)

	MVI	A,CALL		;patch in a call to STEPDLY
	STA	STPLOAD		;patch the BIOS
	LXI	H,STEPDLY	;HL->STEPDLY subroutine
	SHLD	STPLOAD+1	;put in as call destination address

 ENDIF

	RET

;-----------------------------------------------------------------------
; STEPDLY - return track step delay in ms in de based on drive
;   number. If drive zero, return 3ms (Tandon). Otherwise,
;   return 30ms (Micropolis).
;-----------------------------------------------------------------------
STEPDLY	LXI	D,3		;assume 3ms Tandon step rate
	LDA	DRVNUM		;a=drive number

 IF FSTEP0

	ORA	A		;test for drive 0
	RZ			;drive 0 is fast step

 ENDIF

 IF FSTEP1

	CPI	1		;test for drive 1
	RZ			;drive 1 is fast step

 ENDIF

	LXI	D,30		;otherwise, use 30ms Micropolis step
	RET

;--------------------------------------------------------------------------
; IOBYTE Assignments
;
;    CON device (bits 1,0):
;	00 - TTY uses serial port
;	01 - CRT uses keyboard/video*
;	10 - BAT indirect through RDR logical device
;	11 - UC1 uses serial port
;
;    RDR device (bits 3,2):
;	00 - TTY uses serial port
;	01 - PTR uses serial port*
;	10 - UR1 uses serial port
;	11 - UR2 uses keyboard/video
;
;    PUN device (bits 5,4):
;	00 - TTY uses serial port
;	01 - PTP uses serial port*
;	10 - UP1 uses serial port
;	11 - UP2 uses keyboard/video
; 
;    LST device bits (7,6):
;	00 - TTY uses serial port
;	01 - CRT uses keyboard/video
;	10 - LPT uses serial port*
;	11 - UL1 uses serial port
;
;* Default setting
;---------------------------------------------------------------------------

; These I/O routines clobber A and B

;---------------------------------------------------------------------------
;  CON device I/O
;---------------------------------------------------------------------------

; const - Console status BIOS entry point. Return FFh if character ready,
;    return zero otherwise. 

const	mvi	b,SHFTCON	;B=console IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSSTA		;TTY use serial port
	dw	PXCSTA		;CRT use keyboard/video
	dw	batchSt		;BAT indirect through RDR logical device
	dw	PXSSTA		;UC1 use serial port

; conin - Console input BIOS entry point. Return character in A 

conin	mvi	b,SHFTCON	;B=console IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSIN		;TTY use serial port
	dw	PXCIN		;CRT use keyboard/video
	dw	reader		;BAT indirect through RDR logical device
	dw	PXSIN		;UC1 use the serial port

; conout - Console output BIOS entry point. Output the character in C.

conout	mvi	b,SHFTCON	;B=console IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSOUT		;TTY use serial port
	dw	crtOut		;CRT use keyboard/video
	dw	list		;BAT indirect through LST logical device
	dw	PXSOUT		;UC1 user serial port

; crtOut - Output character in C to video display. Do not send two
;    CR's in a row since CR's also line feed on this display.

crtOut	lda	lastChr		;A=last character sent
	cmp	c		;sending same character?
	mov	a,c		;update last character sent
	sta	lastChr
	jnz	PXCOUT		;not same, OK to display it

	cpi	0Dh		;two CR's in a row?
	jnz	PXCOUT		;no, OK to display it
	
	ret			;else, skip the CR

;---------------------------------------------------------------------------
; RDR device - Reader input character BIOS entry point. Return in A
;---------------------------------------------------------------------------
reader	mvi	b,SHFTRDR	;B=reader IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSIN		;TTY use serial port
	dw	PXSIN		;PTR use serial port
	dw	PXSIN		;UR1 use serial port
	dw	PXCIN		;UR2 use keyboard/video

;---------------------------------------------------------------------------
; PUN device - Punch output character BIOS entry point. Byte to send in C.
;---------------------------------------------------------------------------
punch	mvi	b,SHFTPUN	;B=punch IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSOUT		;TTY use serial port
	dw	PXSOUT		;PTP use serial port
	dw	PXSOUT		;UP1 use serial port
	dw	PXCOUT		;UP2 use keyboard/video

;---------------------------------------------------------------------------
;  LST device I/O
;---------------------------------------------------------------------------

; listst - List output test BIOS entry point. Return FFh if list device
;    is ready, 0 if not ready

listst	mvi	b,SHFTLST	;B=list IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSSTA		;TTY use serial port
	dw	crtSt		;CRT use keyboard/video
	dw	PXSSTA		;LPT use serial port
	dw	PXSSTA		;UL1 use serial port

; list - List output character BIOS entry point. Send the character
;    passed in C out the list port

list	mvi	b,SHFTLST	;B=list IOBYTE alignment shift count
	call	doIo		;call the proper I/O routine below
	dw	PXSOUT		;TTY use serial port
	dw	crtOut		;CRT use keyboard/video
	dw	PXSOUT		;LPT use serial port
	dw	PXSOUT		;UL1 use serial port

crtSt	mvi	a,0FFh		;always return "ready" for video out
	ret

;---------------------------------------------------------------------------
;  BAT device I/O - The BAT device is assigned as a physical device
;     for CON. However, BAT is actually a logical device that uses
;     whatever RDR is assigned to for input and whatever LST is assigned
;     to for output. The only jump table that does not already exist
;     is the input test routine for the reader devices
;---------------------------------------------------------------------------

; batchSt - BAT device input status

batchSt	mvi	b,SHFTRDR	;BAT in uses RDR device
	call	doIo		;call the proper I/O routine below
	dw	PXSSTA		;TTY use serial port	
	dw	PXSSTA		;RDR use serial port
	dw	PXSSTA		;UR1 use serial port
	dw	PXCSTA		;UR2 use keyboard/video

;---------------------------------------------------------------------------
; doIo - Perform physical I/O device based on the IOBYTE. The value passed
;    in B contains the left-shift count to align the calling device's IOBYTE
;    field into bits 2 and 1 (not 1 and 0).
;--------------------------------------------------------------------------- 
doIo	xthl			;save HL, HL=table address from stack
	push	d		;save DE

	lda	IOBYTE		;A=CP/M IO BYTE

ioAlign	rlc			;rotate into bits 2 and 1
	dcr	b
	jnz	ioAlign

; The 2 bit device field is aligned in bits 2 and 1. Index into the
;    jump table of the caller and call the proper I/O routine.

	ani	6		;get the 2 bit device field alone
	mov	e,a		;form DE=offset into jump table
	mvi	d,0

	dad	d		;add offset for the selected device

	mov	e,m		;E=lsb of where to jump
	inx	h
	mov	d,m		;D=msb of where to jump
	xchg			;HL=where to jump

	pop	d		;restore DE
	xthl			;restore HL, jump address on stack
	ret			;jump to proper I/O routine

USRLEN	EQU	$-UCINIT	;Length of USER AREA

	END