	PAGE	60
	TITLE	'Cromemco 4FDC/16FDC BIOS in 8080 Code'
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;	modifications by Mike Goetz and Hank Kee - 9/2/81
;
;	corrected PIP A:=B:filename.ext on Persci 277/299
;		Persci's must be configured as A: and B:
;		and/or C: and D: since they have but one
;		arm to access two drives
;	modified sign-on message to properly generate size
;		from MEMSIZE definition during assembly
;	corrected current drive assignment on warm boot
;	inclusion of LIST serial driver for CCS 8250 (30 CPS)
;		option to support LA34/LA36
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;
; This version modified for 4 drives, A, B, C, D, 2-8" and 2-5"
; In this first version, the boot drive is "A:" only, the 1st 8"
;
; MISC EQUATES
;
PERSCI	EQU	0		;ONE IF PERSCI DRIVE
LARGESW	EQU	1 OR PERSCI	;ONE IF MAXI DRIVE
NBRDRV	EQU	2		;ONE TO FOUR (MUST BE SAME TYPE)
BIGIO	EQU	1		;ZERO IF NO LIST, NO PUNCH
SERIAL	EQU	1		;SERIAL PRINTER OPTION W/8250
;
VERS	EQU	22		;VERSION 2.2
MEMSIZ	EQU	48		;48K SYSTEM
MEMT	EQU	MEMSIZ*1024
MEMONES:EQU	MEMSIZ-(MEMSIZ/10*10)
MEMTENS:EQU	MEMSIZ/10
;
; THE FOLLOWING PARAMETER VALUES FOR VERSION 2.X (INCL. 2.2)
;
BOOTSIZ	EQU	1*128
CCPSIZ	EQU	16*128
BDOSIZ	EQU	28*128
BIOSIZ	EQU	12*128		;7 ON DISK, EXCESS UNINITIALIZED RAM
STACK	EQU	MEMT
;
BIOS	EQU	MEMT-BIOSIZ
BDOS	EQU	BIOS-BDOSIZ
CCP	EQU	BDOS-CCPSIZ
BASE	EQU	0		;BASE OF USABLE RAM
IOBYTE	EQU	BASE+3		;I/O DEVICE ASSIGNMENT BYTE
LOGDSK	EQU	BASE+4		;WHERE CPM STORES DEFAULT DRIVE
FCB	EQU	BASE+5CH	;DEFAULT FILE CONTROL BLOCK
BUFF	EQU	BASE+80H	;DEFAULT DISK BUFFER
TPA	EQU	BASE+100H	;BASE OF TRANSIENT PGM AREA
;
; LOCATIONS AT SYSGEN TIME
;
SYSGEN	EQU	900H
BOOTGEN	EQU	SYSGEN
CCPGEN	EQU	BOOTGEN+BOOTSIZ
BDOSGEN	EQU	CCPGEN+CCPSIZ
BIOSGEN	EQU	BDOSGEN+BDOSIZ
BIOSEND	EQU	BOOTGEN+(52*128)-1
OFFSET	EQU	CCPGEN-CCP
;
;	CROMEMCO 4FDC I/O ASSIGNMENTS
;
CSTAT	EQU	00H		;3P + S, 4FDC, TU-UART, OR SCC
CONDATA	EQU	01H
CIMODE	EQU	02H		;(3P + S DOESNT HAVE THESE)
CIMASK	EQU	03H
PARPORT	EQU	04H		;NOT USED
;
FDCSTAT	EQU	30H		;4FDC OR CCS 2422 BOARD
FDCTRK	EQU	31H
FDCSECT	EQU	32H
FDCDATA	EQU	33H
FDCFLAG	EQU	34H
;
MBANKPT	EQU	40H		;MEMORY BANKING PORT
;
;	1771/179X EQUATES
;
HDLOAD	EQU	08H
VERIFY	EQU	04H
;
		IF	BIGIO = 1
;
;	OTHER DEFINITIONS (OPTIONAL)
;
BAUDRATE	EQU	01H		;110 BAUD FOR READER ?
RFDCSTAT	EQU	10H
RBAUDPORT	EQU	RFDCSTAT	;TU-UART BOARD FOR PAPER TAPE
RFDCDATA	EQU	RFDCSTAT+1
PFDCSTAT	EQU	10H
PBAUDPORT	EQU	PFDCSTAT	;PAPER TAPE PUNCH ALSO
PFDCDATA	EQU	PFDCSTAT+1
LFDCSTAT	EQU	54H		;PARALLEL PRINTER POARD
LFDCDATA	EQU	LFDCSTAT
		ENDIF

TRUE	EQU	-1
FALSE	EQU	0
NULLS	EQU	6		; NUMBER OF NULLS AFTER LF
LF	EQU	0AH		; LINE FEED

SDATA	EQU	20H		; DATA PORT
SINTEN	EQU	21H		; INTERRUPT REGISTER
SIDENT	EQU	22H		; INTERRUPT IDENTIFICATION
SLCTRL	EQU	23H		; LINE CONTROL REGISTER
SMDMCT	EQU	24H		; MODEM CONTROL REGISTER
SLSTAT	EQU	25H		; LINE STATUS REGISTER
SMDMST	EQU	26H		; MODEM STATUS REGISTER
SSTATP	EQU	25H		; SERIAL PRINTER STATUS PORT (INPUT)
STBE	EQU	20H		; SERIAL PRINTER TBE BIT
;
	ORG	BIOS
;
;	CPM JUMP VECTOR ENTRY POINTS
;
	JMP	CBOOT
	JMP	WBOOT
	JMP	CONSTAT
	JMP	CONIN
	JMP	CONOUT
	JMP	LIST
	JMP	PUNCH
	JMP	READER
	JMP	HOME	
	JMP	SELDSK		
	JMP	SETTRK
	JMP	SETSEC
	JMP	SETDMA
	JMP	READ
	JMP	WRITE
	JMP	LISTAT
	JMP	SECTRAN
;
;	CP/M 2.2 DISK CONTROL BLOCKS
;
DPBASE	DW	XLT8
	DW	0000
	DW	0000
	DW	0000
	DW	DIRBUF
	DW	SD8PBLK
	DW	CSV0
	DW	ALV0
;
	IF	NBRDRV > 1
	DW	XLT8
	DW	0000
	DW	0000
	DW	0000
	DW	DIRBUF
	DW	SD8PBLK
	DW	CSV1
	DW	ALV1
	ENDIF
;
	IF	NBRDRV > 2
	DW	XLT8
	DW	0000
	DW	0000
	DW	0000
	DW	DIRBUF
	DW	SD8PBLK
	DW	CSV2
	DW	ALV2
	ENDIF
;
	IF	NBRDRV > 3
	DW	XLT8
	DW	0000
	DW	0000
	DW	0000
	DW	DIRBUF
	DW	SD8PBLK
	DW	CSV3
	DW	ALV3
	ENDIF
;
;	disk parameter blockS, common to all disks
;	this implements a "standard" CP/M directory
;	with 64 entries, and 1K blocks.
;
SD8PBLK	DW	26		;sectors per track
	DB	3		;block shift factor
	DB	7		;block mask
	DB	0		;null mask
	DW	242		;disk size-1
	DW	63		;directory max
	DW	00C0H		;INITIAL FREE SPACE MAP
	DW	16		;check size
	DW	2		;track offset
;
;	this implements the cdos standard 5" single density
;	18 sectors (128 bytes) per track, 40 tracks, 3 reserved
;	sectors, 83k of storage
;
SD5PBLK	DW	18		;sectors per track
	DB	3		;block shift factor
	DB	7		;block mask
	DB	0		;null mask
	DW	82		;disk size-1
	DW	63		;directory max
	DW	00C0H		;INITIAL FREE SPACE MAP
	DW	16		;check size
	DW	3		;track offset

;
;	sector translate vector
;
XLT8	DB	1,7,13,19,25,5,11,17,23,3,9,15,21
	DB	2,8,14,20,26,6,12,18,24,4,10,16,22
;
XLT5	DB	1,6,11,16,3,8,13,18,5
	DB	10,15,2,7,12,17,4,9,14
;
SIGNON	DB	0DH,0AH,0AH
	DB	MEMTENS+30H
	DB	MEMONES+30H
	DB	'k CP/M version 2.2'
	DB	0DH,0AH,0
;
SELDSK:	LXI	H,0
	MOV	A,C		;C CONTAINS REQUESTED DRIVE NO.
	CPI	NBRDRV
	RNC			;IGNORE IF TOO HIGH
	STA	DKNUMB
	MOV	L,C		;L=disk number 0,1,2,3
	DAD	H		;*2
	DAD	H		;*4
	DAD	H		
	DAD	H		;*16 (size of each header)
	LXI	D,DPBASE
	DAD	D		;HL=.dpbase(diskno*16)
	RET	
;
;TRANSLATE SECTOR IN C USING TABLE AT DE
;
SECTRAN:MVI	B,0
	XCHG			;TABLE ADDR TO HL
	DAD	B		;GET ADDRESS
	MOV	L,M		;GET BYTE
	MVI	H,0		;ANSWER IN HL
	RET
;
SETSEC:	MOV	A,C		;JUST SAVE SECTOR NUMBER
	STA	DKSECT
	RET
;
SETDMA:	PUSH	H
	MOV	L,C
	MOV	H,B
	SHLD	DKDMA
	POP	H
	RET
;
;	ERROR CHECKING READ AND WRITE RTNS FOR
;	CROMEMCO CBIOS
;
READ:	CALL	CLEAR
RTYRD:	CALL	RD4FDC		;READ SECTOR
	RZ			;SUCCESSFUL READ. RETURN
	CALL	ERROR		;INCREMENT RTYCNT
	JNZ	RTYRD		;RETRY 20 TIMES
	ORI	01H		;CP/M CONVENTION FOR PERMANENT ERROR
	RET	
;
CLEAR:	XRA	A
	STA	TRKERCT	;ZERO OUT TRACK ERROR COUNTER
	STA	RTYCNT	;ZERO OUT CRC ERROR COUNTER
	RET	
;
WRITE:	CALL	CLEAR
RTYWRT:	CALL	WR4FDC	;WRITE SECTOR
	RZ			;SUCCESSFUL WRITE. RETURN
	CALL	ERROR		;INCREMENT RTYCNT
	JNZ	RTYWRT		;RETRY 20 TIMES
	ORI	01H		;CP/M CONVENTION FOR PERMANENT ERROR
	RET	
;
ERROR:	PUSH	H
	ANI	10H		;CHECK FOR NRF
	JNZ	TRKERR	
	LXI	H,RTYCNT
	INR	M		;INCREMENT RTYCNT
	MOV	A,M
	POP	H
	SUI	20		;20 TRIES?
	RET	
;
TRKERR:	LXI	H,TRKERCT
	INR	M		;INCREMENT NO OF TRACK ERRORS
	MOV	A,M
	SUI	10		;ALLOW ONLY 10 TRACK ERRORS
	POP	H
	RZ			;IF >10, RETURN A FAILURE
	PUSH	B
	CALL	HOME		;HOME THE HEAD
	LDA	DKTRACK
	MOV	C,A		;GET TRACK IN C
	CALL	SETTRK		;RESEEK TO CORRECT TRACK
	POP	B
	ORI	0FFH		;RETRY
	RET	
;
;	RESTORE THE DISK TO TRACK ZERO
;
HOME:	SUB	A		;ZERO OUT TRACK COUNTER
	STA	DKTRACK
	CALL	DSKSEL		;NOW SELECT THE DISK
	OUT	FDCFLAG
	MVI	A,02H+HDLOAD+VERIFY	;USE SLOW STEPPING SPEED FOR ALL DISKS
	OUT	FDCSTAT
RSTI:	IN	FDCFLAG		;NOW CHECK STATUS
	RAR
	JNC	RSTI		;LOOP BACK UNTIL DONE
	JMP	SEEKTST
;
;	HERE, ACTUALLY DO THE READ OPERATION
;
RD4FDC:	PUSH	B
	PUSH	H		;FIRST, SAVE REGS
	PUSH	D
	MVI	E,88H		;READ COMMAND (VALID FOR 1771, 179X)
	CALL	INIT4FDC
RDI1:	IN	FDCFLAG		;NOW CHECK FLAGS
	RAR
	JC	RDI3		;IF PREMATURELY DONE, STOP
;	INI
	IN	FDCDATA
	MOV	M,A
	INX	H
	DCR	B
;
	JNZ	RDI1		;READ ANOTHER BYTE INTO CORE UNTIL DONE
RDI2:	IN	FDCFLAG		;CHECK FLAGS
	RAR
	JNC	RDI2		;LOOP UNTIL READY
RDI3:	IN	FDCSTAT	;NOW CHECK STATUS
	ANI	9CH
RDWREND:EI
	POP	D		;RESTORE REGS
	POP	H
	POP	B
	RET
;
;	ACTUALLY DO WRITE OPERATION
;
WR4FDC:	PUSH	B
	PUSH	H		;SAVE REGS
	PUSH	D
	MVI	E,0A8H		;WRITE COMMAND (VALID FOR 1771, 179X)
	CALL	INIT4FDC
WRI1:	IN	FDCFLAG		;CHECK FLAGS
	RAR
	JC	WRI3		;IF PREMATURELY DONE, STOP
;	OUTI
	MOV	A,M
	OUT	FDCDATA
	INX	H
	DCR	B
	JNZ	WRI1		;WRITE DATA FROM MEMORY TIL DONE
WRI2:	IN	FDCFLAG		;CHECK FLAGS
	RAR
	JNC	WRI2		;LOOP TIL DONE WITH WHOLE OPERATION
WRI3:	IN	FDCSTAT	;NOW CHECK ERROR STATUS
	ANI	0FCH
	JMP	RDWREND
;
;	SET THE TRACK, AND MOVE DISK ARM THERE
;
SETTRK:	MOV	A,C
	STA	DKTRACK		;STORE THE TRACK NUMBER
	SUB	A
	CALL	DSKSEL		;NOW SELECT THE DISK
	OUT	FDCFLAG
	MOV	A,C
	OUT	FDCDATA		;TELL 1771 ABOUT TRACK WANTED
	LDA	DKSECT
	OUT	FDCSECT	;TELL IT ABOUT SECTOR WANTED
	PUSH	H
	PUSH	D
	LXI	H,DRVTBL	;NOW, SEE WHERE DISK ARM IS NOW
	LDA	DKNUMB
	MOV	E,A		;LOOK UP IN TABLE
	MVI	D,0
	DAD	D		;GET BYTE
	MOV	A,M
	OUT	FDCTRK	;TELL 1771 WHERE ARM IS NOW
	MOV	A,M
	SUB	C
	POP	D		;NOW CAN RESTORE THE REGS
	POP	H
	RZ			;IF ALREADY AT THAT TRACK, QUIT
	MVI	A,12H-LARGESW*2+HDLOAD+VERIFY
	OUT	FDCSTAT	;PERFORM THE SEEK THAT IS NEEDED
SKI:	IN	FDCFLAG		;CHECK FLAGS
	RAR
	JNC	SKI		;LOOP UNITL OPERATION DONE
SEEKTST:IN	FDCSTAT	;NOW CHECK ERROR STATUS
	ANI	98H
	RNZ			;ZERO IS ALL OK
	PUSH	D
	MVI	D,0		;UPDATE TRACK TABLE
	LDA	DKNUMB
	MOV	E,A		;WITH NEW POSITION
;
	IF	PERSCI = 1
	CALL	ADDSHFT		;PERSCI DRIVES HAVE ONLY ONE ARM
	MOV	A,E
	XRI	01H
	MOV	E,A
	CALL	ADDSHFT
	POP	D
	RET
	ENDIF
;
ADDSHFT:			;IF NOT PERSCI, FALL THRU HERE
	PUSH	H
	LXI	H,DRVTBL	;GET CORRECT ADDRESS OF DISK
	DAD	D
	LDA	DKTRACK		;AND GET CURRENT TRACK NUMBER
	MOV	M,A
	SUB	A		;PUT IT IN
	POP	H
;
	IF	PERSCI = 0
	POP	D		;AND MISC CLEANUP
	ENDIF
;
	RET
;
;	INITIALIZE THE 4FDC, BY SELECTING THE DISK AND
;	TURNING ON THE DISK MOTORS, AND INITIALIZE THE
;	REGISTERS FOR THE I/O OPERATION
;
INIT4FDC:
	MVI	A,80H		;FIRST, SELECT THE DISK
	CALL	DSKSEL
	LHLD	DKDMA		;INITIALIZE HL AND BC REGS FOR I/O
	LXI	B,8000H+FDCDATA	;80H IS 128 BYTE SECTORS
	MOV	D,A		;SAVE THE A REG
	DI			;DISABLE INTERRUPTS
	LDA	DKSECT
	OUT	FDCSECT	;SET THE SECTOR WANTED
	IN	FDCFLAG
	CMA			;SEE IF HEAD IS LOADED
	ANI	20H
	JZ	IN0
	MVI	A,04H		;THIS IS THE HEAD LOAD BIT
IN0:	ADD	E		;ADD HEAD LOAD BIT TO COMMAND
	MOV	E,A
	MOV	A,D		;RESTORE THE A REG
	OUT	FDCFLAG
	MOV	A,E
	OUT	FDCSTAT	;OUTPUT THE COMMAND
	RET
;
;	FIGURE OUT WHICH DISK TO SELECT AND PUT BIT THERE
;
DSKSEL:	PUSH	B		;FIRST, SAVE THIS, ITS NEEDED
	MOV	C,A
	LDA	DKNUMB	;GET DISK NUMBER 0-3
	MOV	B,A
	INR	B		;ADD ONE
	SUB	A
	STC			;SET A BIT IN TO BE SHIFTED
SHFTBIT:RAL			;NOW, ROTATE BITS OVER
;	DJNZ	SHFTBIT
	DCR	B
	JNZ	SHFTBIT
;
	MOV	B,A		;SAVE THE NEW VALUE
	XRA	A
	ORI	20H+LARGESW*10H ;CONDITION 4FDC FOR MOTOR ON AND MAXI
	ORA	B
	ORA	C		;OR IN DRIVE SELECT AND COMMAND WANTED
	POP	B
	RET
;
;	GETS CONTROL ON A WARM START
;
WBOOT:
	MVI	A,01H		;FIRST, RESTORE DEFAULT BANKING
	OUT	MBANKPT
;
	LXI	SP,80H
	LDA	DKNUMB
	STA	CURDRV		;STORE SELECTED DRIVE
;
STARTBOOT:
	MVI	C,0
	CALL	SELDSK		;SELECT DRIVE A TO REBOOT
	CALL	HOME
	LXI	H,CCP-128	;WILL INCREMENT BY 128 LATER
	SHLD	DKDMA
	LXI	B,44*256+1	;SECTOR COUNT, FIRST SECTOR-1
RDSEC:	MOV	A,C		;C IS SECTOR NUMBER
	CPI	18+LARGESW*8
	JZ	NXTTRK
	LXI	D,128		;INCREMENT DMA ADDRESS
	LHLD	DKDMA
	DAD	D
	SHLD	DKDMA		;STORE DMA ADDRESS
	INR	C		;INCREMENT SECTOR NUMBER
	CALL	SETSEC
	CALL	READ		;READ THE DATA
	JNZ	WBOOT		;ON READ FAILURE TRY AGAIN
;	DJNZ	RDSEC
	DCR	B
	JNZ	RDSEC
	JMP	BOOT		;READ CCP AND BDOS;
NXTTRK:	LDA	DKTRACK
	CPI	2-LARGESW
	JZ	BOOT		;STOP AT TRACK 2
	MOV	C,A
	INR	C		;SEEK NEXT TRACK IF NOT
	CALL	SETTRK
	MVI	C,0
	JMP	RDSEC
;
; GET HERE AFTER A COLD BOOT
;
CBOOT:	LXI	SP,80H		;INITIALIZE STACK FOR ROUTINE
	LXI	H,SIGNON
	CALL	PRMSG		;PRINT MESSAGE
BOOT:	DI	
	MVI	A,0C3H		;SET UP PARAMETERS ON PAGE 0
	STA	0
	LXI	H,BIOS+3
	SHLD	1
	SHLD	7*8+1
	STA	5
	LXI	H,BDOS+6
	SHLD	6
;
;		CROMEMCO INITIALIZATION HERE (READER, LIST NOT NEEDED)
;		NOTE .. THE CONSOLE TUART IS INITIALIZED BY RDOS
;
	IF	BIGIO = 1
	MVI	A,BAUDRATE
	OUT	PBAUDPORT
	ENDIF
;
	IF	SERIAL = 1
	CALL	L2INIT
	ENDIF
;
	LXI	B,80H		;SET DEFAULT DMA ADDR
	CALL	SETDMA
;
	LDA	CURDRV		;RESELECT THE DRIVE THAT WAS ACTIVE
	MOV	C,A		;  BEFORE THE WARM START
	EI	
	JMP	CCP
;
;
ERRMSG:
	DB	13,10,'?? ERROR ??',13,10,0
;
;PRINT MESSAGE AT H,L UNTIL 0
;
PRMSG:	MOV	A,M
	ORA	A		;ZERO?
	RZ	
	MOV	C,A		;GO PRINT CHAR
	CALL	CONOUT
	INX	H		;GET NEXT CHAR
	JMP	PRMSG
;
;
;HARDWARE UART CONSOLE ROUTINES
;
CONSTAT:
	IN	CSTAT	;CHECK CONSOLE STATUS
	ANI	40H
	RZ			;ZERO MEANS NO INPUT BYTE READY
	MVI	A,0FFH
	RET			;FF MEANS INPUT
;
CONIN:
	CALL	CONSTAT		;CHECK STATUS TIL GOT A BYTE
	JZ	CONIN
	IN	CONDATA	;READ THE BYTE AND KILL OFF 80H
	ANI	7FH
;
;	THE FOLLOWING CODE REPLACES THE DEL KEY WITH A CTRL-U,
;	SO THAT DEL MEANS LINE DELETE. USE BACKSPACE TO DELETE
;	A SINGLE CHARACTER.
;
;	CPI	127		; DEL
;	RNZ	
;	SUI	127-21		; CTRL-U
	RET	
;
;	CONSOLE OUTPUT ROUTINE
;
CONOUT:
	IN	CSTAT	;FIRST, LOOP UNITL TRANSMITTER BUFFER
	ANI	80H		;IS EMPTY
	JZ	CONOUT
	MOV	A,C		;NOW, OUTPUT CHARACTER TO CONSOLE
	OUT	CONDATA
	RET	
;
;	LIST STATUS CHECK FOR CP/M 2.2
;
LISTAT:
	IF	BIGIO = 1
	IN	LFDCSTAT	;IF LIST TRANSMITTER IS BUSY,
	CMA			;THEN RETURN WITH ZERO
	ANI	20H
	RZ	
	ORI	0FFH		;LIST TRANSMITTER BUFFER IS EMPTY
	RET
	ENDIF
;
LIST:
	IF BIGIO = 1 AND SERIAL = 0
	CALL	LISTAT		;CHECK IF PRINTER BUSY
	JZ	LIST
	MOV	A,C		;NOW OUTPUT CHARACTER
;	SET	7,A
	ORI	80H
	OUT	LFDCDATA,A	;WITH HIGH STROBE
;	RES	7,A
	ANI	7FH
	OUT	LFDCDATA,A	;NOW LOW STROBE
;	SET	7,A
	ORI	80H
	OUT	LFDCDATA,A	;NOW HIGH STROBE AGAIN
	RET
	ENDIF
;
	IF	SERIAL = 1
	MOV	A,C
	JMP	L2OUT
	ENDIF
;
PUNCH:
	IF	BIGIO = 1
	IN	PFDCSTAT	;CHECK IF PUNCH BUFFER EMPTY
	ANI	80H
	JZ	PUNCH		;LOOP UNTIL READY
	MOV	A,C
	OUT	PFDCDATA	;OUTPUT CHARACTER
	RET
	ENDIF
;
READER:
	IF	BIGIO = 1
	IN	RFDCSTAT	;SEE IF READER BUFFER FULL
	ANI	40H
	JZ	READER		;LOOP UNTIL FULL
	IN	RFDCDATA	;AND READ IT
	RET	
	ENDIF
;
	IF	BIGIO NE 1
	XRA	A,A		;DUMMY ROUTINE FOR LIST,PUNCH,READER
	RET
	ENDIF
;
;  Serial Printer Initialization Routine
;
L2INIT:	MVI	A,0FH		; SETUP THE 8250
	OUT	SMDMCT	
	MVI	A,83H		; SET DIVISOR REGISTER ACCESS
	OUT	SLCTRL	
	MVI	A,80H		; SET THE DIVISOR TO 384=300 BAUD
	OUT	SDATA
	MVI	A,01H		;
	OUT	SINTEN
	MVI	A,03H		; SET DATA REGISTER ACCESS
	OUT	SLCTRL	
	MVI	A,0H		; DISABLE INTERRUPT
	OUT	SINTEN	
	OUT	SLSTAT		; RESET ERROR FLAGS
	RET
;
;  Get Serial Printer Output Status
;  Upon Exit:	A = -1 (FFH) and Z-flag is reset if ready for char.
;		A = 0 and Z-flag is set if not ready for character
;
L2RDY:	IN	SSTATP	; GET LIST-OUT STATUS
	ANI	STBE		; CHECK PRINTER TBE FLAG
	RZ			; PRINTER NOT READY FOR CHARACTER
	MVI	A,-1		; PRINTER READY FOR CHARACTER
	RET
;
;  Serial Printer Output Routine
;  Upon Entry:	A contains the character to be output
;
L2OUT:	PUSH	PSW		; SAVE CHARACTER FOR A MOMENT
L2OT30:	CALL	L2RDY		; GET LIST-OUT STATUS
	JZ	L2OT30		; ZERO MEANS PRINTER BUSY
	POP	PSW		; RESTORE CHARACTER
	OUT	SDATA		; OUTPUT THE CHARACTER
	CPI	LF		; CHECK FOR END OF LINE
	RNZ			; RETURN IF NOT LINE FEED CHARACTER
	MVI	A,NULLS+1	; IF LF, GET NUMBER OF NULLS
L2RTN:	DCR	A		; CHECK FOR 0 NULLS AT TOP OF LOOP
	RZ			; RETURN IF ALL NULLS OUTPUT
	PUSH	PSW		; SAVE NULLS COUNTER
	SUB	A		; PRINT A SINGLE NULL
	CALL	L2OUT		;	CHARACTER (RECURSIVE)
	POP	PSW		; RESTORE NULLS COUNTER
	JMP	L2RTN		; LOOP TO PRINT NEXT NULL
;
DKNUMB	EQU	4		;THIS IS WHERE THE DISK NUMBER IS
				;KEPT IN NORMAL CP/M
CURDRV	DB	0		;THE SAVE AREA FOR THE DEFAULT DRIVE
DRVTBL	DB	2-LARGESW,0,0,0	;FOUR DRIVES MAX
DKSECT	DB	0		;SECTOR NUMBER
DKTRACK	DB	0		;TRACK NUMBER
DKDMA	DW	0		;DMA ADDRESS
TRKERCT	DB	0		;NUMBER OF TRACK ERRORS
RTYCNT	DB	0		;NUMBER OF READ ERRORS
;
;	FROM HERE ON, THE AREAS ARE INITIALIZED BY CP/M AS NEEDED
;
DIRBUF	DS	128		;SAVE AREA FOR DISK DIRECTORY OPERATIONS
ALV0	DS	32
;
	IF	NBRDRV > 1
ALV1	DS	32
	ENDIF
;
	IF	NBRDRV > 2
ALV2	DS	32
	ENDIF
;
	IF	NBRDRV > 3
ALV3	DS	32
	ENDIF
;
CSV0	DS	16
;
	IF	NBRDRV > 1
CSV1	DS	16
	ENDIF
;
	IF	NBRDRV > 2
CSV2	DS	16
	ENDIF
;
	IF	NBRDRV > 3
CSV3	DS	16
	ENDIF
;

;  Note:  The last assembled byte of this module MUST NOT be a Define
;  Storage (DS or DEFS) pseudo-op to assure proper operation with CDOSGEN

	END
