;----------------------------------------------------------------
; USERPOLY.ASM
; 
;USER AREA for Lifeboat CP/M2 (North Star DD controller)
;
;  This file provides I/O support for the Poly-88 which is
;  running the POLEX PROM in socket 2 on the CPU board.
;  
;  1.0   7-18-16   Mike Douglas
;
;  1.1	 7-27-16   Mike Douglas, add IOBYTE support.
;
;  1.2   6-10-17   Mike Douglas
;	 Add FASTOFF assembly option to turn off drive
;	 motors whenever CONIN is called. Since disk
;	 activity is complete whenever a program is
;	 waiting on user input, CONIN serves well as
;	 a "disk idle" indicator. By turning the drive
;	 motor off at this point, the long 9.6 sec wait
;	 to remove or swap disks is avoided. Also, the
;	 urge to type commands fast to beat the disk
;	 shutdown is also avoided.
;
;  1.3  11/15/17   Mike 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 FF (ready).
;			
;  IOBYTE location: 271Dh from start of USER area
;
;  To install this patch:
;    1) Set MSIZE below and assemble this file.
;    2) Run MOVCPM and save the image to a file (e.g., CPM.COM)
;       as prompted.
;    3) Use DDT to install the patch in the CP/M image:
;       A>DDT CPM.COM
;       -IUSERPOLY.HEX
;       -R5200		(replace 5200 with value of "OFFSET" below)
;       -G0		(G zero, not "oh", or type ctrl-c)
;       A>SYSGEN	(hit return at source prompt to use
;		 	 image in memory, then follow remaining
;			 prompts).
;
;----------------------------------------------------------------

; Change MSIZE to the desired CP/M memory size in K.

MSIZE	equ	56		;Distribution size

; Set FASTOFF to 1 to turn off drive motors when CONIN
;   is called (i.e., program is not using disks).

FASTOFF	equ	1
DISKOFF	equ	0EB17h		;FDC reset command

; These equates are automatically changed by MSIZE.

BIAS	EQU	(MSIZE-20)*1024-0700H
CCP	EQU	3400H+BIAS	; base of CCP
BDOS	EQU	CCP+0806H	; base of BDOS
BIOS	EQU	CCP+1600H	; base of BIOS
USER	EQU	CCP+1D00H	; base of USER area

OFFSET	equ	2700H-USER	; offset to overlay SYSGEN image

; 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


;----------------------------------------------------------------
; Jump Table
;    The BIOS accesses the user I/O routines through this jump
;    table. The table must remain at the start of the user area
;    and in the same order.
;----------------------------------------------------------------
	org	USER

ucoldst	jmp	coldIni		;cold start init
uwarmst	jmp	warmIni		;warm start init
uconst	jmp	const		;console input status
uconin	jmp	conin		;console input
uconout	jmp	conout		;console output
ulist	jmp	list		;printer output on serial portl
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

; The next 8 bytes may be accessed externally and must remain in the
;     same order and immediately follow the jump table

usrLen	dw	USEREND-USER	;Length of USER AREA
defIob	db	95h		;default IOBYTE
				;   LST = LPT (serial port)
				;   PUN = PTP (serial port)
				;   RDR = PTR (serial port)
				;   CON = CRT (keyboard/video)
				;   at 271Dh in a DDT loaded CPM image
hsType	db	0		;Handshaking type (not used)
nullOc	db	0		;Printer nulls (not used)
lastChr	db	0,0,0		;Reserved

;-----------------------------------------------------------------------
; coldIni - cold start initialization
;   This entry is called during cold boot.
;-----------------------------------------------------------------------
coldIni	lda	defIob		;initialize default I/O byte for CP/M
	sta	IOBYTE
	ret

;-----------------------------------------------------------------------
; warmIni - warm start initialization
;   This entry is called during each warm boot.
;-----------------------------------------------------------------------
warmIni	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	equ	$

 IF FASTOFF
	lda	DISKOFF		;turn off drive motors
 ENDIF

	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

USEREND	equ	$		;end of user area
	
	end