*************************************************************
*		NAME BIOS				    *
**************************************************************
*		I/O DRIVERS FOR CP/M                        *
*************************************************************
*		VERSION 1.0	03/17/78                    *
*		RUNS ON Z80, 8080, OR 8085                  *
*               MODIFIED BY M.A. SASS                       *
*		VER. 1.1				    *
*               6 MS STEPPING RATE                          *
*		VER. 1.2				    *
*		FORMAT COMPATIBLE WITH 179X FAMILY	    *
*************************************************************
	ORG	0F000H
;
;
NBYTES	EQU	128	; BYTES PER SECTOR
;
;
RBASE	EQU	0000H		; START OF RAM
TADDR	EQU	RBASE+40H	; TRANSFER ADDRESS
UNIT	EQU	RBASE+42H	; DRIVE #
SCTR	EQU	RBASE+43H	; SECTOR
TRK	EQU	RBASE+44H	; TRACK
NREC	EQU	RBASE+45H	; # OF SECTORS
ERMASK	EQU	RBASE+46H	; ERROR MASK
ERSTAT	EQU	RBASE+47H	; ERROR STATUS
IDSV	EQU	RBASE+48H	; 4BYTE
CMDSV	EQU	RBASE+4CH	; COMMAND SAVE
SPSV	EQU	RBASE+4DH	; SP SAVE ( 2 BYTES )
SSTACK	EQU	RBASE+80H	; SYSTEM STACK
COLD	EQU	RBASE+80H	; COLD START ADDRESS
;
;
;	PORTS USED BY DISK CONTROLLER
;
X	EQU	60H	; DRIVE SELECT PORT
SELECT	EQU	X+3	; DRIVE SELECT PORT
STATUS	EQU	X+4	; STATUS PORT
TRACK	EQU	X+5	; TRACK PORT
SECTOR	EQU	X+6	; SECTOR PORT
DATA	EQU	X+7	; DATA PORT
CMD	EQU	X+4	; COMMAND PORT
;
;
;	SERIAL I/O PORTS
;
;
SDATA	EQU	1	; SERIAL DATA PORT
SSTAT	EQU	0	; SERIAL STATUS PORT
RXRMSK	EQU	1	; RX RDY MASK
TXRMSK	EQU	80H	; TX RDY MASK
;
;
; DISK ERROR  STATUS BITS (ERSTAT)
;
;
; BIT 7 - DRIVE NOT READY
; BIT 6 - WRITE PROTECT
; BIT 5 - TRACK SEEK ERROR
; BIT 4 - SECTOR NOT FOUND
; BIT 3 - CRC ERROR
; BIT 2 - DATA LOST
; BIT 1 - DRQ BIT
; BIT 0 - WRITE DELETED SECTOR READ
;
;
; DISK CONTROLLER COMMAND CODES
;
;
RDACMD	EQU	0C4H	; READ ADDRESS CMD
RDCMD	EQU	88H	; READ SECTOR CMD
WRCMD	EQU	0A8H	; WRITE SECTOR CMD
WRTCMD	EQU	0F4H	; WRITE TRACK CMD
;
;
;
;	CP/M SYSTEM LINKAGES
;
;
BIOS	JMP	BOOT	; COLD START ENTRY
WBOOTE	JMP	BOOT	; WARM START ENTRY
CSE	JMP	CONST	; CONSOLE STATUS
CIE	JMP	CONIN	; READ CONSOLE
COE	JMP	CONOUT	; WRITE CONSOLE
LSTE	JMP	LIST	; WRITE PRINTER
PCHE	JMP	PUNCH	; WRITE PUNCH
RDRE	JMP	READER	; READ READER
HME	JMP	HOME	; MOVE DISK TO TRK 00
SDSKE	JMP	SELDSK	; SELECT DISK DRIVE
STRKE	JMP	SETTRK	; SET DISK TRACK
SSECE	JMP	SETSEC	; SET DISK SECTOR
SDMAE	JMP	SETDMA	; SET MEM ADDR FOR READ/WRITE
RDE	JMP	READ	; READ A SECTOR
WRE	JMP	WRITE	; WRITE A SECTOR
LDE	JMP	LOADER	; LOAD 'N' SECTORS
SVE	JMP	SAVER	; SAVE 'N' RECORDS
FMATE	JMP	FMAT	; FORMAT A TRACK
SCANE	JMP	SCAN	; SCAN FOR OPERAND
PTXTE	JMP	PTXT	; PRINT TEXT
PACCE	JMP	PACC	; PRINT ACC
;
;
;	MINI / FULL SIZE VARIABLES
;
;	SET UP THESE BYTES FOR THE TYPE OF
;	DRIVE TO BE USED.  THESE ARE THE ONLY
;	SOFTWARE CHANGES WITHIN BIOS TO CONVERT
;	FROM MINI TO FULL SIZE DRIVES (OR VISA
;	VERSA).
;
;
;			MINI	FULL	SIZE
NSCTRS	DB	1AH	; 12	1A	SCTRS/TRK
NTRKS	DB	4DH	; 23	4D	TRACKS/SIDE
GAP1	DB	2FH	; 14	2F	FORMAT GAP1
GAP2	DB	0BH	; 11	0B	FORMAT GAP2
GAP3	DB	1AH	; 0E	1A	FORMAT GAP3
RSCMD	DB	09H	; 0B	09	RESTORE CMD
SKCMD	DB	1DH	; 1F	1D	SEEK CMD
SKNCMD	DB	19H	; 1B	19	SEEK W/NO VER
HLDLY	DB	23H	; 4B	23	HEAD LOAD DELAY
;
;
;	DELAY CONSTANTS MUST BE SET UP FOR
;	THE CPU CLOCK RATE USED:
;			2MHZ	2.5MHZ	3MHZ	4MHZ
;
DLYCON	DB	8EH	; 8E	B2	D8	00
;
BOOT	IN	7FH	; CLEAR H. W.
	LXI SP,SSTACK
	LXI H,COLD
	SHLD TADDR
	MVI A,1
	STA NREC
	STA SCTR
	XRA A
	STA UNIT
	STA TRK
	CALL LOADER	; BOOT IN CP/M (EXCEPT CBIOS)
	JZ COLD		; JUMP TO COLD START
	HLT		; IF BAD DISK, HALT
;
;	NON-DISK I/O DRIVER LINKAGES 
;
CONST	IN SSTAT		; CONSOLE STATUS
	ANI	RXRMSK
	MVI	A,0
	RNZ!CMA!RET
; CONSOLE INPUT
;
CONIN	IN	SSTAT		; KEYBOARD STATUS
	ANI	RXRMSK
	JNZ	CONIN
	IN	SDATA
	ANI	7FH	; STRIP PARITY
	RET		; RETURN TO CALLER WITH DATA IN A REG
;
; CONSOLE OUTPUT
;
CONOUT	PUSH	PSW		; SAVE STATUS
	IN	SSTAT
	ANI TXRMSK		; TX BFR EMPTY
	JNZ CONOUT+1		; TRY AGAIN TIL EMPTY
	MOV A,C			; GET FROM 'C' REG THE DATA
	OUT SDATA
	POP	PSW		; RESTORE
	RET
;
; READER INPUT
;
READER	RET
;
; LISTING OUTPUT
;
LIST	RET
;
;
; PUNCH OUTPUT
;
PUNCH	RET
;
;
;
;	DISK CONTROLLER LINKAGES
;
;
HOME	CALL DRVSET		; SELECT DRIVE
	LXI	H,0	
	DAD	SP
	SHLD	SPSV
	LDA	RSCMD
	CALL	CMDI
	XRA	A
	RET
;
;	SELECT DISK DRIVE
;
SELDSK	MOV	A,C
	STA	UNIT
	RET
;
;	SET TRACK NUMBER
;
SETTRK	MOV	A,C
	STA	TRK
	RET
;
;	SET SECTOR NUMBER
;
SETSEC	MOV	A,C
	STA	SCTR
	RET
;
;	SET MEMORY TRANSFER ADDRESS FOR DISK READ/WRITE
;
SETDMA	PUSH	B
	POP	H
	SHLD	TADDR
	RET
;
;	READ A SECTOR
;
READ	MVI	B,10
READ1	PUSH	B
	CALL	RDSC	; READ A SECTOR
	POP	B
	RZ		; RETURN IF NO ERROR
	DCR	B
	JNZ	READ1	; 10 RETRIES
	XRA	A
	INR	A
	RET		; ERROR RETURN
;
;	WRITE A SECTOR
;
WRITE	MVI	B,10
WRITE1	PUSH	B
	CALL	WRSC	; WRITE A SECTOR
	POP	B
	RZ		; RETURN IF NO ERROR
	DCR	B
	JNZ	WRITE1
	XRA	A
	INR	A
	RET		; ERROR RETURN
;
;
;
;
;	END OF COMMAND
;
;
;
END11	IN	STATUS
	ANI	1
	JNZ	END11
	CALL	DWAIT
	IN	STATUS
	MOV	D,A
	LDA	ERMASK
	ANA	D	; CHECK FOR ERRORS
	RZ
END1	MOV	A,D
END2	STA	ERSTAT	; SAVE ERROR BITS
	ORI	1
	LHLD	SPSV
	SPHL!RET
;
;
;
;	SELECT SIDE AND SEEK TRACK
;
SEEK	MVI	B,0
	LDA	NTRKS
	MOV	C,A
	LDA	TRK
	CMP	C
	JC	SEEK1
	MVI	B,10H	; SIDE 2
	SUB	C
SEEK1	MOV	C,A	; SAVE NEW TRK #
	IN	TRACK	; PRESENT TRACK
	CMP	C
	PUSH	PSW
	IN	SELECT
	CMA
	MOV	E,A
	ANI	10	; CHECK SIDE SELECT
	CMP	B
	JZ	SEEK3	; IF SAME SIDE JUMP
	MOV	A,E
	ANI	0FH
	ORA	B
	CMA
	OUT	SELECT	; SELECT NEW SIDE
	MVI	A,1
	CALL	MSEC
SEEK3	POP	PSW
	RZ		; IF SAME TRK, RETURN
	MVI	B,3	; 3 RETRIES
SEEK2	MOV	A,C	; TRK #
	OUT	DATA	;
	LDA	SKCMD	; SEEK COMMAND
	PUSH	B	; SAVE RETRY COUNT AND TRK #
	CALL	CMDI
	POP	B
	IN	STATUS
	ANI	10H	; TEST FOR SEEK ERROR
	RZ		; IF NO ERROR, RETURN
	LDA	RSCMD
	PUSH	B
	CALL	CMDI	; RESTOR DISK
	POP	B
	DCR	B
	JNZ	SEEK2
	MVI	A,20H
	JMP	END2
;
;
; CHECK DRIVE SELECT AND CHANGE IF DIFFERENT
;
DRVSET	IN	SELECT
	CMA
	ANI	0FH
	JZ	DRVS1	; IF NONE, JUMP
	MVI	C,0FFH
DRVS0	INR	C	; CONVERT TO DRV#
	RAR
	JNC	DRVS0
	LDA	UNIT
	ANI	3
	CMP	C
	RZ		; IF SAME DRV, RETURN
DRVS1	CALL	DRSEL
	XRA	A
	INR	A
	RET
;
;
;
; READ PRESENT DISK ADDRESS (TRK AND SECTOR)
;
;
IDRD	LXI	H,IDSV
	MVI	B,4
	MVI	A,0F8H
	STA	ERMASK
	CALL	SWEB
	MVI	A,RDACMD	; 'READ ADDRESS' CMD
	CALL	RDSC0		; READ ID
	LDA	IDSV
	OUT	TRACK
	RET
;
;
;
; READ SECTOR COMMAND
;
;
RDSC	LXI	H,0
	DAD	SP
	SHLD	SPSV
	CALL	DRVSET
	CNZ	IDRD	; NEW DRIVE
	CALL	SEEK	; SEEK TRACK
	MVI	A,0FEH
	STA	ERMASK
	CALL	TRINT
	MVI	A,RDCMD	; READ COMM@ND
	CALL	HLCHK	; SET UP HEAD LOAD BIT
RDSC0	STA	CMDSV
	OUT	CMD	; OUTPUT COMMAND
RDSC1	IN	DATA
	MOV	M,A
	INX	H
	DCR	B
	JNZ	RDSC1
	CALL	END11
	XRA	A
	RET
;
;
; WRITE SECTOR COMMAND
;
WRSC	LXI	H,0
	DAD	SP
	SHLD	SPSV
	CALL	DRVSET
	CNZ	IDRD
	CALL	SEEK
	MVI	A,0FCH
	STA	ERMASK
	CALL	TRINT
	MVI	A,WRCMD
	CALL	HLCHK
	STA	CMDSV
	OUT	CMD	; OUTPUT COMMAND
WRSC1	MOV	A,M	; MEMORY TO A
	OUT	DATA
	INX	H	; BUMP IT
	DCR	B
	JNZ	WRSC1	; DO IT AGAIN TILL DONE
	CALL	END11	; CLEAN UP THE ACT
	XRA	A	; CLEAR EVERY THING
	RET		; GO HOME
;
;
; TYPE I COMMANDS
;
;
CMDI	STA	CMDSV	; SAVE COMMAND
	MVI	A,80H
	STA	ERMASK	; SET UP ERROR MASK
CMDII	IN	STATUS	; GET STATUS
	ANI	1	; MASK IT
	JNZ	CMDII	; WAIT FOR NOT BUSY
	IN	SELECT	; NOT BUSY
	ANI	1FH
	ORI	60H
	OUT	SELECT	; NO WAIT STATE ENBL
	LDA	CMDSV	; RESTORE COMMAND
	OUT	CMD	; OUTPUT COMMAND
	MVI	A,1
	CALL	MSEC	; WAIT FOR STATUS
	JMP	END11
;
;
;
; DISABLE WAIT STATES
;
DWAIT	IN	SELECT
	ORI	0E0H	; NEG. TRUE AND
	OUT	SELECT
	RET
;
;
; ENABLE WAIT STATES
;
;
SWEB	IN	SELECT
	ANI	1FH
	OUT	SELECT
	RET
;
;
; SELECT DRIVE
;
;
DRSEL	LDA	UNIT
	ANI	3	; ONLY 4 DRIVES
	ORA	A	; CHECK FOR ZERO
	MOV	C,A
	MVI	A,1
	JZ	DRSEL3	; NO SHIFTING
DRSEL2	RLC		; SHIFT LEFT
	DCR	C
	JNZ	DRSEL2
DRSEL3	CMA
	OUT	SELECT
	LDA	HLDLY	; HEAD DELAY 
	CALL	MSEC	; DELAY 35 MILLISECS
	IN	STATUS
	ANI	80H	; MASK
	JZ	DRSEL4
	POP	PSW
	MVI	A,40H	; IF DRIVE NOT RDY, ERR
	JMP	END2	; CLEAN IT UP
DRSEL4	RET
;
;
; DELAY ( A REG ) * 1 MILLISECOND
;
;
MSEC	MOV	B,A
MSEC0	LDA	DLYCON
MSEC1	DCR	A
	JNZ	MSEC1
	DCR	B
	JNZ	MSEC0
	RET
;
;
; INITIALIZE FOR DISK TRANSFER
;
;
TRINT	LHLD	TADDR
	MVI	B,NBYTES
	LDA	SCTR
	OUT	SECTOR	; SELECT SECTOR
	JMP	SWEB	; ENABLE WAIT
;
;
;
; CHECK FOR HEAD LOADED, IF NOT SET
; 'H' BIT IN COMMAND
;
;
HLCHK	MOV	D,A	; SAVE COMMAND IN D
	MVI	A,0D0H
	OUT	CMD	; 'FORCE INTERRUPT' CMD
	MVI	A,4
HLCHK0	DCR	A
	JNZ	HLCHK0
	IN	STATUS
	ANI	20H
	MOV	A,D	; RESTORE COMMAND
	RNZ		; IF HEAD LOADED, RET
	ORI	4	; SET 'H' BIT
	RET
;
;
; LOAD # OF SECTORS IN (NREC)
;
;
LOADER	CALL	READ	; READ A SECTOR
	JNZ	ERR
	CALL	INCP	; INC TRK/SCTR
	JNZ	LOADER
	RET
;
;
; SAVE # OF SECTORS IN (NREC)
;
;
SAVER	CALL	WRITE
	JNZ	ERR
	CALL	INCP
	JNZ	SAVER
	RET
;
;  INC SECTOR AND TRACK
;
;
INCP	LHLD	TADDR
	LXI	D,NBYTES
	DAD	D
	SHLD	TADDR
	LXI	H,NREC	; POINT TO NREC
	DCR	M
	RZ
	DCX	H
	DCX	H	; POINT TO SCTR
	INR	M	; INC SCTR #
	LDA	NSCTRS
	INR	A
	CMP	M	; LAST SCTR ON TRK?
	RNZ		; IF NOT, RETURN
	MVI	M,1	; SET TO SCTR 1
	INX	H	; POINT TO TRK
	INR	M	; INC TRK #
	ORA	A
	RET
;
;
; ERROR MESSAGE 'DISK ERR#'
;
;
ERR	LXI	H,ERMSG
	CALL	PTXT
	LDA	ERSTAT
	CALL	PACC
	MVI	C,0AH	; LINE FEED
	CALL	CONOUT	; CONSOLE
	MVI	C,0DH	; CARRIAGE RETURN
	JMP	CONOUT	;CONSOLE
	RET
;
ERMSG	DB	'DISK ERR# ',3
;
;
; PRINT TEXT
;
;
PTXT	MOV	A,M	; FETCH A BYTE
	CPI	3	; TERMINATOR
	RZ
	MOV	C,A
	CALL	CONOUT	; CONSOLE
	INX	H	; BUMP TE BUMP
	JMP	PTXT	; TIL DONE
;
;
; PRINT ACCUMULATOR
;
;
PACC	PUSH	PSW
	RRC!RRC!RRC!RRC
	CALL	PRVAL
	POP	PSW
;
;
PRVAL	ANI	0FH
	ADI	90H
	DAA
	ACI	40H
	DAA
	MOV	C,A
	JMP	CONOUT	; PRINT IT
;
;
; ASCII TO BINARY CONVERSION
;
;
ASBIN	SUI	30H
	CPI	10
	RM
	SUI	7
	RET
;
;
; CHECK FOR VALID HEX CHARACTER
;
;
AORN	CPI	'0'
	JC	AORN2	; JUMP IF < 30H
	CPI	'9'+1
	JC	AORN1	; JUMP IF < 3AH
	CPI	'A'-1
	JC	AORN2	; JUMP IF < 'A'
	CPI	'F'+1
	JNC	AORN2	; JUMP IF <'G'
AORN1	XRA	A
	RET		; VALID DATA RET
AORN2	XRA	A
	INR	A
	RET		; NOT HEX CHAR
;
;
; CHECK FOR TERMINATOR
;
;
TERMCK	CPI	' '	; SPACE
	RZ
	CPI	','	; COMMA
	RZ		; THERE
	CPI	'^'	; UP ARROW
	JZ	TCHK0
	CPI	'.'	; PERIOD
	JZ	TCHK0
	CPI	0DH	; CR
	RNZ
TCHK0	PUSH	B
	MVI	C,0AH
	CALL	CONOUT
	POP	B
	XRA	A
	RET
;
;
; SCAN FOR OPERAND FROM KEYBOARD
;
;
; EXIT WITH DATA IN  HL, AND TERMINATOR
; IN C. IF VALID DATA, RETURN WITH
; ZERO FLAG SET. B CONTAINS # OF CHARACTERS ENTERED
;
;
SCAN	LXI	H,0
	MOV	B,L
SCAN1	CALL	ECHO
	INR	B	; INC CHAR COUNT
	CALL	TERMCK
	RZ		; IF TERMINATOR, RETURN
	CALL	AORN	; VALID DATA CHEECK
	RNZ		; IF NOT RETURN
	MOV	A,C
	CALL	ASBIN	; CONVERT TO BINARY
	DAD	H
	DAD	H
	DAD	H
	DAD	H	; P SHIFT 4 BITS
	ADD	L
	MOV	L,A
	JMP	SCAN1
;
;
;
ECHO	CALL	CONIN
	MOV	C,A
	JMP	CONOUT
;
;
; FORMAT A TRACK ( IBM MINI DISK FORMAT )
;
;
FMAT	LDA	TRK
	OUT	DATA
	LDA	SKNCMD
	CALL	CMDI
	MVI	L,1
	LDA	NSCTRS
	MOV	H,A
	LDA	GAP1
	MOV	B,A
	XRA	A
	STA	ERMASK
	CALL	SWEB
	MVI	A,WRTCMD
	STA	CMDSV
	OUT	CMD	; OUTPUT COMMAND
	XRA	A
	CMA		; ACC TO FF
FMAT3	OUT	DATA	; GAP1
	DCR	B
	JNZ	FMAT3
	MVI	A,0FCH	; INDEX MARK
	OUT	DATA
	LDA	GAP3
	MOV	B,A
	XRA	A
	CMA		; ACC TO ONE'S
FMAT4	OUT	DATA
	DCR	B
	JNZ	FMAT4
FMAT51	MVI	B,6
	XRA	A	; ACC TO ZERO'S
FMAT41	OUT	DATA
	DCR	B
	JNZ	FMAT41
FMAT5	MVI	A,0FEH	; ID ADDRESS MARK
	OUT	DATA
	IN	TRACK
	OUT	DATA	; TRACK #
	XRA	A
	OUT	DATA	; 0 GAP
	MOV	A,L
	OUT	DATA	; SECTOR #
	XRA	A
	OUT	DATA	; SECTOR LENGTH = 128 BYTES
	MVI	A,0F7H	; SEND CRC ( 2 BYTES )
	OUT	DATA
	LDA	GAP2
	MOV	B,A
	XRA	A
	CMA		; ACC TO ONE'S
FMAT6	OUT	DATA	; GAP2
	DCR	B
	JNZ	FMAT6
	MVI	B,6
	XRA	A	; SET TO ZERO
FMAT61	OUT	DATA
	DCR	B
	JNZ	FMAT61
	MVI	A,0FBH	; DATA ADDRESS MARK
	OUT	DATA
	MVI	A,0E5H
	MVI	B,128D
FMAT7	OUT	DATA	; 128 E5'S IN DATA FIELD
	DCR	B
	JNZ	FMAT7
	MVI	A,0F7H
	OUT	DATA	; CRC ( 2 BYTES )
	LDA	GAP3
	MOV	B,A
	XRA	A
	CMA		; SET TO ONES'S
FMAT8	OUT	DATA	; GAP3
	DCR	B
	JNZ	FMAT8
	INR	L	; INC SECTOR #
	DCR	H	; CHECK FOR LAST SECTOR
	JNZ	FMAT51	; WRITE ALL SECTORS
	CALL	END11
	XRA	A
	RET
	END
