;---------------------------------------------------------------------
; 8 JULY 2010
;---------------------------------------------------------------------
; VERSION 2.0 SIGNIFICANT CLEANUP TO WORK AS STDBIOS ON DD DISK
; SPINWRITER 3550 SUPPORT ON CCS 2720 16 MAY 2010
; MODIFIED TO USE Z80 RELATIVE JUMPS 26 APRIL 2010
; MODIFIED TO SUPPORT CCS 2720 PORT A FOR LIST DEVICE 26 APRIL 2010
; MODIFIED FOR HEAD LOAD DELAY 16 DEC 80
; MODIFIED FOR PROPER UNIT/STPRAT CORRELATION 9 DEC 80
; MODIFIED FOR SEEK ERROR TRAPPING 10 DEC 80
; MODIFIED FOR NOT READY ERROR TRAPPING 10 DEC 80
; MODIFIED FOR MINI/MAXI CONDITIONALS 11 DEC 80
; MODIFIED FOR UNALLOCATED WRITE CORRECTIONS 8 SEP 80
; MODIFIED FOR AUTO-BOOT OPERATION 7 SEP 80
; MODIFIED FOR COMPUTED SKEW FACTORS 7 SEP 80
; MODIFIED FOR WARM BOOTS FROM MINIS 14 AUG 80
; MODIFIED FOR PROPER DEFAULT DISK RESTORAL ON WARM BOOT 12 AUG 80
; MODIFIED FOR 2 MHZ 8080 OPERATION 12 AUG 80
;---------------------------------------------------------------------
; THIS BIOS IS SET UP FOR AUTO SELECT OF DISK CHAR

BVERS	EQU	20		;1ST DIGIT = MAJOR REV, 2ND = MINOR REV

TRUE:	EQU	0FFFFH
FALSE:	EQU	NOT TRUE
;---------------------------------------------------------------------
; CP/M EQUATES
;---------------------------------------------------------------------
;"BIAS" IS ADDRESS OFFSET FROM 3400H FOR MEMORY SYSTEMS OTHER
;THAN 20K.

VERS	EQU	22	;CP/M VERSION NUMBER
MSIZE	EQU	46	;CP/M MEMORY SIZE IN KILOBYTES
BIAS	EQU	(MSIZE-20)*1024
CCP	EQU	3400H+BIAS	;BASE OF CCP
BDOS	EQU	CCP+806H	;BASE OF BDOS
BIOS	EQU	CCP+1600H	;BASE OF BIOS

CCPCLR	EQU	CCP+3		;WARM START CCP AND CLEAR COMMAND BUFFER
				;(SELECTED DRIVE IN C)

;---------------------------------------------------------------------
; PAGE 0 RESERVED LOCATIONS FOR CP/M
;---------------------------------------------------------------------
WBOOTV	EQU	0		;WARM BOOT ENTRY TO CPM
IOBYTE	EQU	3		;INTEL IOBYTE LOCATION
CDISK	EQU	4
BDOSV	EQU	5		;VECTOR TO BDOS

;---------------------------------------------------------------------
; PAGE 0 RESERVED MEMORY LOCATIONS FOR Z80 RESTART INSTRUCTIONS
;---------------------------------------------------------------------
RSTV0	EQU	00H
RSTV1	EQU	08H
RSTV2	EQU	10H
RSTV3	EQU	18H
RSTV4	EQU	20H
RSTV5	EQU	28H
RSTV6	EQU	30H
RSTV7	EQU	38H

BIOSV	EQU	3FH		;PLACE TO PARK VIOS VERSION
	
;---------------------------------------------------------------------
; PAGE 0 RESERVED MEMORY LOCATIONS FOR BIOS
;---------------------------------------------------------------------
DISKNO	EQU	40H		;ACTIVE DISK NUMBER
TRACK	EQU	DISKNO+1	;ACTIVE TRACK NUMBER
SECTOR	EQU	TRACK+1		;ACTIVE SECTOR NUMBER
SIDE	EQU	SECTOR+1	;ACTIVE SIDE NUMBER

SEKDSK:	EQU	44H		;REQUESTED DISK
SEKTRK:	EQU	SEKDSK+1	;REQUESTED TRACK
SEKHST:	EQU	SEKTRK+1	;REQUESTED PHYSICAL SECTOR NUMBER
SEKSID:	EQU	SEKHST+1	;REQUESTED SIDE

SEKSEC:	EQU	SEKSID+1	;REQUESTED LOGICAL SECTOR # (0,1,2,...)
SEKSEL:	EQU	SEKSEC+1	;SELECT BITS FOR REQUESTED DISK

LUNIT	EQU	4AH		;LAST USED DRIVE
CUNIT	EQU	LUNIT+1		;CURRENT DRIVE
HSTBUF	EQU	4CH		;HOST BUFFER ADDRESS (2 BYTES)

IDSV	EQU	4EH		;SECTOR ID SAVE AREA
IDTRAC	EQU	IDSV		;TRACK ADDRESS
IDSIDE	EQU	IDSV+1		;SIDE NUMBER
IDSECT	EQU	IDSV+2		;SECTOR ADDRESS
IDSSIZ	EQU	IDSV+3		;SECTOR LENGTH (0-3)
IDCRC1	EQU	IDSV+4		;CRC 1
IDCRC2	EQU	IDSV+5		;CRC 2
IDJNK1	EQU	IDSV+6		;JUNK BECAUSE CODE READS 8 BYTES
IDJNK2	EQU	IDSV+7		;JUNK 

TBUF	EQU	80H
TPA	EQU	100H

;---------------------------------------------------------------------
;Z80 INSTRUCTION OPCODES
;---------------------------------------------------------------------
JR	EQU	18H
JRNZ	EQU	20H
JRZ	EQU	28H
JRNC	EQU	30H
JRC	EQU	38H
DJNZ	EQU	10H
LDIR	EQU	0B0EDH	;LDIR WITH BYTES REVERSED FOR DW
BIT0D	EQU	042CBH	;BIT 0,D WITH BYTES REVERSED FOR DW

;---------------------------------------------------------------------
;ASSEMBLY OPTIONS FOR 8" AND 5.25" DISK DRIVE SUPPORT
;---------------------------------------------------------------------
MINI	EQU	TRUE	;FLAG FOR 5.25" DISK SUPPORT
MAXI	EQU	TRUE	;CONDITIONAL FLAG FOR 8" SUPPORT
BOTH	EQU	MINI AND MAXI

;---------------------------------------------------------------------
; EQUATES FOR 8250-BASED SERIAL PORT ON THE CCS 2810 CPU BOARD
;---------------------------------------------------------------------
SDATA	EQU	20H	;SERIAL DATA PORT
SINTEN	EQU	SDATA+1	;SERIAL INTERRUPT ENABLE PORT
SIDENT	EQU	SDATA+2	;SERIAL INTERRUPT IDENTIFICATION PORT
SLCTRL	EQU	SDATA+3	;SERIAL LINE CONTROL PORT
SMDMCT	EQU	SDATA+4	;SERIAL MODEM CONTROL PORT
SLSTAT	EQU	SDATA+5	;SERIAL LINE STATUS PORT
SMDMST	EQU	SDATA+6	;SERIAL MODEM STATUS PORT

RXRDY	EQU	00000001B	;RECEIVE DATA AVAILABLE BIT
TXMTY	EQU	00100000B	;TRANSMIT BUFFER EMPTY BIT

; WHEN THE AUTO-BOOT JUMPER IS ENABLED, THE 2810
;  SERIAL PORT WILL BE INITIALIZED TO 9600 BAUD.
;  TO SELECT A DIFFERENT BAUD RATE, CHANGE SBAUD
;  TO ONE OF THE FOLLOWING VALUES:
;
;	BAUD RATE	SBAUD
;	50		2304
;	75		1536
;	110		1047
;	134.5		857
;	150		768
;	300		384
;	600		192
;	1200		96
;	1800		64
;	2000		58
;	2400		48
;	3600		32
;	4800		24
;	7200		16
;	9600		12
;	19200		6
;	38400		3
;	56000		2
;
SBAUD:	EQU	12	;9600 BAUD DIVISOR FOR 2810

;---------------------------------------------------------------------
; CCS 2720 QUAD PARALLEL PORT EQUATES
;---------------------------------------------------------------------
PPORTA	EQU	08H		;PORT A IN/OUT
PPORTB	EQU	PPORTA+1	;PORT B IN/OUT
PPORTC	EQU	PPORTA+2	;PORT C IN/OUT
PPORTD	EQU	PPORTA+3	;PORT C OUT ONLY
PSTAT	EQU	PPORTA+3	;PORT STATUS INPUT

PPAIMT	EQU	00000001B	;PORT A INPUT EMPTY
PPADAV	EQU	00000010B	;PORT A OUTPUT AVAILABLE
PPBIMT	EQU	00000100B	;PORT B INPUT EMPTY
PPBDAV	EQU	00001000B	;PORT B OUTPUT AVAILABLE
PPCIMT	EQU	00010000B	;PORT C INPUT EMPTY
PPCDAV	EQU	00100000B	;PORT C OUTPUT AVAILABLE
PPDDAV	EQU	01000000B	;PORT D OUTPUT AVAILABLE

; EQUATES FOR SPINWRITER SUPPORT WITH PORT D OUT AS CONTROL

PPASTR	EQU	11111110B	;ACTIVE LOW DATA STROBE
PPAAF	EQU	11111101B	;ACTIVE LOW AUTO-FEED
PPAINI	EQU	11111011B	;ACTIVE LOW INITIALIZE
PPASLI	EQU	11110111B	;ACTIVE LOW SELECT TO PRINTER

; PORT A SPINWRITER CONTROL WITH PORT A INPUT FOR STATUS

PPABSY	EQU	00000001B	;ACTIVE HIGH BUSY
PPAPE	EQU	00000010B	;ACTIVE HIGH PAPER EMPTY
PPASEL	EQU	00000100B	;LOW MEANS PRINTER IS IN LOCAL MODE
PPAERR	EQU	00001000B	;ACTIVE LOW PRINTER ERROR

;---------------------------------------------------------------------
; CCS 2422 FLOPPY DISK CONTROLLER EQUATES
;---------------------------------------------------------------------
BCTRL	EQU	4	;DISK CONTROL 2 PORT
BSTAT	EQU	4	;STATUS PORT 2
DSTAT	EQU	30H	;DISK STATUS PORT
DCMMD	EQU	DSTAT	;DISK COMMAND PORT
DTRCK	EQU	DSTAT+1	;DISK TRACK PORT
DSCTR	EQU	DSTAT+2	;DISK SECTOR PORT
DDATA	EQU	DSTAT+3	;DISK DATA PORT
DFLAG	EQU	DSTAT+4	;DISK FLAG PORT
DCTRL	EQU	DSTAT+4	;DISK CONTROL PORT

; WD1793 COMMANDS

RSTRCM	EQU	00000000B	;(I) RESTORE
SEEKCM	EQU	00010000B	;(I) SEEK
STEPCM	EQU	00100000B	;(I) STEP
STPICM	EQU	01000000B	;(I) STEP-IN
STPOCM	EQU	01100000B	;(I) STEP OUT
RSECCM	EQU	10000000B	;(II) READ SECTOR
WSECCM	EQU	10100000B	;(II) WRITE SECTOR
RADRCM	EQU	11000000B	;(III) READ ADDRESS
RTRKCM	EQU	11100000B	;(III) READ TRACK
WTRKCM	EQU	11110000B	;(III) WRITE TRACK
FINTCM	EQU	11010000B	;(IV) FORCE INTERRUPT

; 1793 COMMAND OPTIONS (FIRST LETTER MATCHES 1793 DATA SHEET DEFINITIONS)

RS03MS	EQU	00000000B	;(I) 3 MS STEP RATE
RS06MS	EQU	00000001B	;(I) 6 MS STEP RATE
RS10MS	EQU	00000010B	;(I) 10 MS STEP RATE
RS15MS	EQU	00000011B	;(I) 15 MS STEP RATE
VERTRK	EQU	00000100B	;(I) VERIFY ON DEST TRACK
HDLOAD	EQU	00001000B	;(I) HEAD LOAD BIT
UPDTTR	EQU	00010000B	;(I) UPDATE TRACK REG FOR STEP COMMANDS
MULTCM	EQU	00010000B	;(II) READ/WRITE MULTIPLE RECORDS
ED15MS	EQU	00000100B	;(II & III) SETTLING 15 MS DELAY

;COMPOSITE COMMANDS USED IN THIS PROGRAM

RESTOR	EQU	RSTRCM+HDLOAD		;BASIS OF RESTORE COMMAND
SEEKV	EQU	SEEKCM+HDLOAD+VERTRK	;BASIS OF SEEK COMMAND
STEPI	EQU	STPICM+UPDTTR+HDLOAD	;BASIS OF STEP IN COMMAND
RDSEC	EQU	RSECCM+HDLOAD		;BASIS OF READ SECTOR COMMAND
RDADD	EQU	RADRCM+ED15MS		;READ ADDRESS COMMAND

;CONTROL REGISTER 1 (DCTRL) OUTPUT BITS

DCDS1	EQU	00000001B	;DRIVE SELECT 1
DCDS2	EQU	00000010B	;DRIVE SELECT 2
DCDS3	EQU	00000100B	;DRIVE SELECT 3
DCDS4	EQU	00001000B	;DRIVE SELECT 4
DCMAXI	EQU	00010000B	;0=MINI DISK, 1 = MAXI DISK
DCMOTO	EQU	00100000B	;MOTOR ON
DCDDEN	EQU	01000000B	;DOUBLE DENSITY ENABLE
DCAUTOW	EQU	10000000B	;AUTO WAIT


;CONTROL REGISTER 2 (BCTRL) OUTPUT BITS

BCEJECT	EQU	00000100B	;REMOTE EJECT CONTROL FOR PERSCI DRIVES
BCFSEEK	EQU	00010000B	;FAST SEEK FOR VOICE-COIL DRIVES
BCSIDE0	EQU	01000000B	;0=SIDE 1, 1=SIDE 0
BCROMEN	EQU	10000000B	;1 ENABLES ONBOARD ROM ON CCS 2422

;COMPOSITE BCTRL VALUES USED TO SELECT DISK SIDE
;  NOTE THAT ROM IS ACTUALLY DISABLED VIA THE PAGE REGISTER
;  NOTE THAT FAST SEEK IS DISABLED VIA HARDWARE JUMPER

SIDE0	EQU	BCROMEN+BCSIDE0+BCFSEEK
SIDE1	EQU	BCROMEN+BCFSEEK

;1793 STATUS (DSTAT) AFTER A COMMAND

SBUSY	EQU	00000001B	;(ALL) BUSY
SCRCERR	EQU	00001000B	;(ALL) CRC ERROR
SWRPROT	EQU	01000000B	;(ALL) WRITE PROTECT
SNOTRDY	EQU	10000000B	;(ALL) DRIVE NOT READY
SINDEX	EQU	00000010B	;(I) INDEX
STRACK0	EQU	00000100B	;(I) TRACK 0
SSEEKER	EQU	00010000B	;(I) SEEK ERROR
SHEADLD	EQU	00100000B	;(I) HEAD LOADED
SDATRQ	EQU	00000010B	;(II & III) DATA REQUEST STATE
SLOSTD	EQU	00000100B	;(II & III) LOST DATA ERROR
SRNFER	EQU	00010000B	;(II & III) RECORD NOT FOUND ERROR
SRCTYF	EQU	00100000B	;(II & III) RECORD TYPE FAULT

;STATUS REGISTER 1 (DFLAG) INPUT BITS

DFINTRQ	EQU	00000001B	;STATE OF 1793 INTRQ SIGNAL, ACTIVE HIGH
DFDS1	EQU	00000010B	;STATE OF DS1 IN CONTROL REG 1
DFDS2	EQU	00000100B	;STATE OF DS2 IN CONTROL REG 1
DFDS3	EQU	00001000B	;STATE OF DS3 IN CONTROL REG 1
DFDS4	EQU	00010000B	;STATE OF DS4 IN CONTROL REG 1
DFSHLD	EQU	00100000B	;STATE OF 1793 HLD SIGNAL, ACTIVE HIGH
DFAUTOB	EQU	01000000B	;STATE OF AUTO-BOOT JUMPER, 0=AUTO-BOOT
DFSDRQ	EQU	10000000B	;STATE OF 1793 DRQ SIGNAL, 1=READY

;STATUS REGISTER 2 (BSTAT) INPUT BITS

BTRK0	EQU	00000001B	;WHEN ON TRACK 0: 0=MINI, 1=MAXI
BMINI	EQU	00000010B	;INVERSE OF CCTRL REG BIT: 0=MAXI, 1=MINI
BWPRT	EQU	00000100B	;0=SELECTED DISK IS WRITE-PROTECTED
BSIDE0	EQU	00001000B	;STATE OF SIDE0 BIT IN CONTROL REG 2
BINDEX	EQU	00010000B	;STATE OF INDEX SIGNAL, ACTIVE LOW
BDDEN	EQU	00100000B	;STATE OF DDEN SIGNAL IN CONTROL REG 1
BSIDED	EQU	01000000B	;0=SELECTED DISK IS 2-SIDED

; STEP RATES MAY BE TAILORED TO MEET INDIVIDUAL DRIVE REQUIREMENTS.
;  TO DO SO, DETERMINE THE PORPER TRACK-TO-TRACK STEP RATES FROM
;  YOUR DISK DRIVE'S TECHNICAL MANUAL, AND SET THE STEP5 (FOR MINI
;  DRIVES) AND/OR STEP8 (FOR 8" DRIVES) TO THE VALUE SHOWN IN
;  THE FOLLOWING TABLE:
;
;	VALUE	MINI DISKS	8" DISKS
;	  O	   6 MS		  3 MS
;	  1	  12 MS		  6 MS
;	  2	  20 MS		 10 MS
;	  3	  30 MS		 15 MS

STEP5	EQU	3	;MINI DISK STEP RATE
STEP8	EQU	1	;8" DISK STEP RATE

TRIES	EQU	10	;NUMBER OF RETRY ATTEMPTS

;DEBLOCK PARAMETERS

WRALL	EQU	0
WRDIR	EQU	1
WRUAL	EQU	2

;---------------------------------------------------------------------
;ASCII EQUATES
;---------------------------------------------------------------------
NULL	EQU	0
BELL	EQU	7	;ASCII BELL CHARACTER
CR	EQU	0DH	;ASCII CARRIAGE RETURN
LF	EQU	0AH	;ASCII LINE FEED

;************************************************************************
	ORG	BIOS	;ORIGIN OF THIS PROGRAM				*
;************************************************************************

;------------------------------------------------------------------------
;JUMP VECTORS FOR INDIVIDUAL ROUTINES
;------------------------------------------------------------------------
	JMP	BOOT	;COLD START
WBOOTE:	JMP	WBOOT	;WARM START
	JMP	CONST	;CONSOLE OUTPUT STATUS
	JMP	CONI	;CONSOLE CHARACTER IN
	JMP	CONOUT	;CONSOLE CHARACTER OUT
	JMP	LIST	;LIST CHARACTER OUT
	JMP	PUNCH	;PUNCH CHARACTER OUT
	JMP	READER	;READER CHARACTER IN
	JMP	HOME	;MOVE HEAD TO HOME POSITION
	JMP	SELDSK	;SELECT DISK
	JMP	SETTRK	;SET TRACK NUMBER
	JMP	SETSEC	;SET SECTOR NUMBER
	JMP	SETDMA	;SET DMA ADDRESS
	JMP	READ	;READ DISK
	JMP	WRITE	;WRITE DISK
	JMP	LISTST	;RETURN LIST STATUS
	JMP	SECTRAN	;SECTOR TRANSLATE

;************************************************************************
;INDIVIDUAL SUBROUTINES TO PERFORM EACH BIOS FUNCTION			*
;************************************************************************

;************************************************************************
; BIOS FUNCTION WARM BOOT						*
; PRIMARY ENTRY AT WBOOT						*
; OTHER ENTRY POINTS:							*
;    BOOT0 - ENTRY FROM ON-TIME BOOT CODE				*
;************************************************************************
;---------------------------------------------------------------------
; RETRY CP/M LOAD IF NOT TOO MANY TIMES ALREADY
;---------------------------------------------------------------------
RETRY:	DCR	C	;C HAS REMAINING RETRIES

	DB	JRNZ,(LOAD1-$-1)MOD 256

	LXI	H,BOTMSG
	CALL	PRTRD	;PRINT MSG THEN WAIT FOR USER INPUT
	CALL	PCRLF	;PRINT CR LF AFTER USER INPUT

WBOOT:	LXI	SP,TPA	;BOOT STACK (GROWS DOWN FROM THIS ADDRESS)

;---------------------------------------------------------------------
; SET UP TO LOAD CP/M'S CCP AND BDOS FROM DISK
;---------------------------------------------------------------------
	MVI	C,TRIES		;# OF RETRIES

; SET DISKNO = 0 AND TRACK = 0

LOAD1:	XRA	A		;CLEAR A ALSO FOR CLEARING PRMTBL LATER
	MOV	H,A		;SET THE UNIT AND TRACK BOTH TO 0
	MOV	L,A
	SHLD	DISKNO

; SET SECTOR = 2 AND SIDE = 0D0H

	LXI	H,SIDE0*256+2	;SET THE SIDE AND SECTOR
	SHLD	SECTOR

; ZERO OUT PRMTBL FOR ALL 4 DRIVES

	MVI	B,12		;ZERO OUT PRMTBL
	LXI	H,PRMTBL
LOAD1A:	MOV	M,A		;A=0 HERE
	INX	H

	DB	DJNZ,(LOAD1A-$-1)MOD 256


; SELECT DRIVE A

	PUSH	B
	MOV	C,A		;A=0 HERE - BOOT FROM DRIVE 0
	MOV	E,A		;SET NEW UNIT INDICATOR
	CALL	SELDSKA
	POP	B

; REMEMBER CURRENT UNIT

	LDA	PRMTBL+1
	STA	CUNIT

; SET LOAD ADDRESS FOR CCP

	LXI	H,CCP
	SHLD	HSTBUF

; DETERMINE IF THIS IS A MINI OR MAXI DISK, THEN SET THE
; SECTORS/TRACK ASSUMING TRACK 0 IS SINGLE-DENSITY, 128 BYTES/SECTOR

	IF	MAXI
	MVI	B,26		;HOLD THE SPT IN (B)
	ENDIF
	IF	BOTH
	IN	BSTAT		;SET # OF SECTORS FOR TRK00
	ANI	BMINI		;ISOLATE MINI/MAXI BIT

	DB	JRZ,(LOAD2-$-1)MOD 256

	ENDIF
	IF	MINI
	MVI	B,18		;SET MINI SPT
	ENDIF

; READ ONE SECTOR AND RETRY ON FAILURE

LOAD2:	PUSH	B
	CALL	DREAD
	POP	B
	ORA	A

	DB	JRNZ,(RETRY-$-1)MOD 256

; NEXT SECTOR ON THIS TRACK, UNLESS THIS IS THE LAST SECTOR

	SHLD	HSTBUF
	MOV	D,H		;SAVE THE PAGE ADDRESS
	LXI	H,SECTOR	;POINT TO THE SECTOR HOLD
	MOV	A,M		;SEE IF READY FOR NEXT TRACK
	SUB	B

	DB	JRC,(LOAD3-$-1)MOD 256

; NEXT TRACK

	MOV	M,A		;RESET THE SECTOR COUNT
	DCX	H		;POINT TO THE TRACK
	INR	M		;ADVANCE IT
	INX	H		;POINT BACK TO THE SECTOR
	IF	BOTH
	IN	BSTAT		;SET THE NEW SPT VALUE
	ANI	BMINI		;SEE IF MINI OR MAXI

	DB	JRZ,(LOAD3-$-1)MOD 256

	ENDIF
	IF	MINI
	LDA	CUNIT		;SEE IF DOUBLE DENSITY
	ANI	DCDDEN		;CURRENT VALUE OK IF DDEN SET

	DB	JRNZ,(LOAD3-$-1)MOD 256

	LDA	IDSSIZ		;GET SECTOR SIZE INDICATOR
	DCR	A		;SEE WHICH SIZE
	JM	LOAD3		;JUMP IF 128 BYTE SECTORS
	MVI	B,10		;SPT FOR 256 BYTE SECTORS

	DB	JRZ,(LOAD3-$-1)MOD 256

	MVI	B,5		;SPT FOR 512 BYTE SECTORS
	ENDIF

;TEST TO SEE IF WE HAVE LOADED ENOUGH DATA
;&&&&&&&&&&THIS SEEMS WRONG FOR MOST SECTOR SIZES&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
LOAD3:	INR	M
	LDA	IDSSIZ	;SEE IF ENOUGH LOADED IN
	ADD	D
	SUI	BIOS/256

	DB	JRC,(LOAD2-$-1)MOD 256

;END OF LOAD OPERATION, SET PARAMETERS AND GO TO CP/M
;---------------------------------------------------------------------
; COMMON BOOT CODE FOR COLD AND WARM BOOTS
;---------------------------------------------------------------------
BOOT0:

; INSTALL JUMP VECTORS IN PAGE 0

	MVI	A,JMP		;GET JUMP OP CODE
	STA	WBOOTV		;RESET THE JUMP VECTORS
	STA	BDOSV
	STA	RSTV7		;ANY RESTART WILL WARM-BOOT.
	LXI	H,WBOOTE
	SHLD	WBOOTV+1
	LXI	H,BDOS
	SHLD	BDOSV+1
	LXI	H,RSTER		;OTHER RST VECTORS ARE NOP'ED OUT
	SHLD	RSTV7+1

	MVI	A,BVERS		;PUT BIOS VERSION WHERE WE CAN FIND IT
	STA	BVERS

; SET UP CP/M BUFFER POINTERS AND FLAGS

	LXI	H,DBUF		;SET UP BUFFER ADDRESS
	SHLD	HSTBUF
	LXI	H,TBUF		;DEFAULT DMA ADDRESS IS 80H
	SHLD	DMAAD
	MOV	L,H		;HL=0
	SHLD	HSTACT		;HOST IS NOT ACTIVE

; GET CURRENT DISK FOR CP/M

	LDA	CDISK
	MOV	C,A		;SELECT DRIVE 0 AND SEND TO THE CCP
	JMP	CCPCLR		;GO TO CPM WITH NO INITIAL COMMAND

;************************************************************************
; BIOS FUNCTION SELECT DISK - SELECT DISK GIVEN BY REGISTER C		*
; PRIMARY ENTRY AT SELDSK						*
;    C = DISK NUMBER (0-3)						*
; OTHER ENTRY POINTS:							*
;    SELDSKA (CALLED FROM LOAD)						*
;        A = C = DISK NUMBER (0-3)					*
;        E = 0 TO FORCE NEW SELECTION					*
;************************************************************************
SELDSK:

; SEE IF THIS DISK IS ALREADY SELECTED
; E=0 FOR NEW DISK, E=1 MEANS NOT NEW

	LXI	H,DISKNO	;SAME DISK AS BEFORE?
	MvI	E,0		;E=0 WILL MEAN NEW DISK
	MOV	A,C		;C HAS DESIRED DISK NO
	CMP	M		;SAME DISK AS BEFORE?

	DB	JRNZ,(ITSNEW-$-1)MOD 256

	INR	E		;E=1 MEANS NOT NEW DISK
ITSNEW:

; A = C = DISK NUMBER (0-3)
; E = 0 MEANS NEW SELECTION

SELDSKA:	STA	SEKDSK
	LXI	H,0		;ERROR RETURN CODE
	CPI	4		;MUST BE BETWEEN 0 AND 3
	RNC			;NO CARRY IF 4,5,...

;DISK NUMBER IS IN THE PROPER RANGE
;COMPUTE PROPER DISK PARAMETER HEADER ADDRESS

	MOV	L,A		;L=DISK NUMBER 0,1,2,3
	DAD	H		;*2
	DAD	H		;*4
	DAD	H		;*8
	DAD	H		;*16 (SIZE OF EACH HEADER)

	MOV	A,E		;GET THE NEW UNIT INDICATOR BIT
	LXI	D,DPBASE
	DAD	D		;HL=.DPBASE(DISKNO*16)
	RAR			;TEST THE NEW UNIT BIT

	PUSH	PSW		;SAVE PARTICULARLY THE CARRY FLAG
	PUSH	H
	MOV	A,C		;DISK NUMBER INTO A
	CALL	FDSB0		;GET SELECT BITS FROM PARAMETER TABLE
	STA	SEKSEL
	POP	H
	POP	PSW		;CARRY MEANS SAME DISK AS BEFORE

	MOV	A,C		;RETURN THE DISKNO
	RC			;RETURN IF NOT NEW UNIT

	PUSH	H		;SAVE (H,L)
	LHLD	LUNIT		;FIRST, SAVE CURRENT DISK ASSIGNMENTS
	PUSH	H
	LHLD	DISKNO
	LDA	SIDE
	MOV	H,A
	PUSH	H
	MOV	A,C		;REGET DESIRED UNIT
	STA	DISKNO		;SET NEW DISK NUMBER
	MVI	A,SIDE0		;SELECT SIDE 0 OF NEW DISK
	STA	SIDE
	PUSH	B
	CALL	IDRD1		;FIND OUT WHAT IS OUT THERE

	DB	JRNZ,(SELERR-$-1)MOD 256

	MOV	C,A		;GET THE SECTOR SIZE
	DCX	H		;POINT TO THE SELBITS
	MOV	B,M		;GET THEM
	LXI	D,4		;ADDRESS TABLE ENTRY OFFSET
	IF	MINI
	LXI	H,MSELTBL-4	;MINI TABLE ADDRESS
	ENDIF
	IF	BOTH
	MOV	A,B		;REGET THE SELBITS
	ANI	10H		;ISOLATE THE MINI DRIVE BIT

	DB	JRZ,(SETUP1-$-1)MOD 256

	ENDIF
	IF	MAXI
	LXI	H,SELTBL-4	;ELSE, SET THE 8" TABLE ADDRESS
	IN	BSTAT		;CHECK FOR DOUBLE SIDED DISK
	ANI	BSIDED		;ISOLATE THE TWO-SIDED BIT

	DB	JRNZ,(SETUP1-$-1)MOD 256

	LXI	H,SELTBLA-4
	ENDIF
SETUP1:	MOV	A,B		;CHECK FOR DOUBLE DENSITY
	STA	SEKSEL
	ANI	DCDDEN		;ISOLATE THE BIT

	DB	JRZ,(SETUP3-$-1)MOD 256

	DAD	D		;OFFSET TO DOUBLE DENSITY ENTRIES
	DAD	D
	DAD	D
	DAD	D
SETUP3:	DAD	D		;OFFSET TABLE ADDRESS
	DCR	C
	JP	SETUP3

	XCHG			;SAVE THE POINTER
	CMP	A		;ZERO OUT FLAGS

SELERR:	POP	B		;RESTORE THE REGISTERS
	POP	H		;RESTORE THE CURRENT DRIVE
	MOV	A,H		;RESTORE THE SIDE
	STA	SIDE
	MOV	A,L		;AND THE DISKNO
	STA	DISKNO
	POP	H
	SHLD	LUNIT
	POP	H

	DB	JRNZ,(SELERRA-$-1)MOD 256

	PUSH	H		;GET, RESAVE DP BLOCK POINTER
	LDAX	D		;MOVE THE TABLE ENTRIES
	MOV	M,A
	INX	D
	INX	H
	LDAX	D
	MOV	M,A
	INX	D
	PUSH	D		;SAVE IT FOR A MOMENT

	LXI	D,9
	DAD	D		;OFFSET THE POINTER
	POP	D		;REGET THE TABLE ADDRESS

	LDAX	D		;MOVE THE TABLE ENTRIES
	MOV	M,A
	INX	D
	INX	H
	LDAX	D
	MOV	M,A
	POP	H

	MOV	A,C
	RET

SELERRA:	LXI	H,0
	MOV	A,C
	RET

;************************************************************************
; BIOS FUNCTION SET TRACK - SET TRACK GIVEN BY REGISTER C		*
; PRIMARY ENTRY AT SETTRK						*
;************************************************************************
SETTRK:	MOV	A,C
	STA	SEKTRK
	RET

;************************************************************************
; BIOS FUNCTION SET SECTOR - SET SECTOR GIVEN BY REGISTER C, WITH THE	*
; MSB INDICATING SIDE							*
; PRIMARY ENTRY AT SETSEC						*
;************************************************************************
SETSEC:

; PEEL OFF JUST THE SECTOR, AND ADJUST TO START WITH SECTOR 0

	MOV	A,C
	ANI	7FH		;STRIP OFF SIDE INDICATOR
	DCR	A
	STA	SEKSEC

; NOW PEEL OFF THE SIDE, AND SET SEKSID WITH THE RIGHT BCTRL VALUE

	MOV	A,C		;REGET SECTOR NUMBER
	RAL			;ISOLATE SIDE BIT
	MVI	A,SIDE0		;SET UP SIDE SELECT BITS
	DB	JRNC,(STSEC1-$-1)MOD 256
	MVI	A,SIDE1		;SELECT SIDE 1
STSEC1:	STA	SEKSID		;SET THE SIDE SELECT BITS

	RET

;************************************************************************
; BIOS FUNCTION SECTOR TRANSLATE - TRANSLATE THE SECTOR GIVEN IN BC	*
; USING THE TRANSLATE TABLE GIVEN IN DE, FRO THE DRIVE IN SEKDSK	*
; PRIMARY ENTRY AT SECTRAN:						*
;     B = 0								*
;     C = LOGICAL (128-BYTE) SECTOR NUMBER				*
; ON EXIT:								*
;    LOGICAL (128-BYTE) SKEWED SECTOR NUMBER (WITH SIDE) IS IN HL	*
;    CPMSEC HAS THE REQUESTED LOGICAL SECTOR 				*
;    NXTSCT HAS	THE NEXT FULL PHYSICAL SECTOR NUMBER			*   
;************************************************************************
SECTRAN:	PUSH	D	;SAVE THE TABLE ADDRESS
	LDA	SEKDSK
	CALL	FDSB0		;GET THE PARAMETER TABLE ADDRESS
	INX	H		;POINT TO THE SECTOR SIZE ENTRY
	MOV	D,M		;SECTOR SIZE NOW IN D
	PUSH	D		;SAVE SECTOR SIZE FOR LATER USE
	MOV	A,C		;GET THE DESIRED SECTOR
	STA	CPMSEC		;SAVE IT FOR EXIT
	RAL

SECT0:	ORA	A		;CONVERT TO PHYSICAL SECTOR NUMBER
	RAR
	DCR	D
	JP	SECT0

	POP	D		;RECOVER SECTOR SIZE IN D
	POP	H		;REGET TABLE ADDRESS FROM INPUT
	CMP	M		;SEE IF SIDE 1
	MVI	B,0		;SET FOR SIDE 0
	DB	JRC,(SECT1-$-1)MOD 256

	MVI	B,80H		;FLAG BIT FOR SIDE 1
	SUB	M
SECT1:
	PUSH	B		;SAVE SIDE IN B & REQUESTED SECTOR IN C
	MOV	B,D		;SAVE SECTOR SIZE IN B
	MOV	E,A		;SET UP TO BUILD SKEW
	MOV	D,M		;GET THE SKEW FACTOR
	INX	H
	MVI	C,0FFH		;GET A -1

SECT2:	INR	C		;BUILD SECTOR OFFSET IN C
	SUB	M
	DB	JRNC,(SECT2-$-1)MOD 256

	INX	H		;POINT TO SKEW FACTOR
	XRA	A		; AND GET A ZERO

SECT3:	ADD	M		;BUILD THE SKEWED SECTOR NUMBER
	DCR	E
	JP	SECT3

	SUB	M
	ADD	C		;ADD THE OFFSET

SECT4:	SUB	D		;INSURE NUMBER IS IN RANGE
	DB	JRNC,(SECT4-$-1)MOD 256

	ADD	D
	MOV	L,A		;MOVE SKEWED NUMBER OVER INTO L
	INR	A		;SET PHYS SECTOR FOR ANTIC. LOGIC
	STA	NXTSCT
	XRA	A		;CONVERT PHYSICAL TO LOGICAL SECTOR
	MOV	H,A

SECT6:	DCR	B
	JM	SECT7		;JUMP IF DONE

	STC
	ADC	A		;BULD THE MASK
	DAD	H		;OFFSET THE SECTOR NUMBER
	DB	JR,(SECT6-$-1)MOD 256

SECT7:	POP	B		;RECOVER REQUESTED SIDE AND SECTOR
	ANA	C		;STRIP OUT THE SUB-SECTOR
	ORA	L		;ADD IT TO THE SKEWED SECTOR
	INR	A
	ORA	B		;ADD ON THE SIDE SELECT BIT
	MOV	L,A
	RET

;************************************************************************
; BIOS FUNCTION SET DMA - SET DMA ADDRESS AS SPECIFIED IN BC		*
; PRIMARY ENTRY AT SETDMA						*
;************************************************************************
SETDMA:	MOV	L,C		;LOW ORDER ADDRESS
	MOV	H,B		;HIGH ORDER ADDRESS
	SHLD	DMAAD		;SAVE THE ADDRESS
	RET

;************************************************************************
; BIOS FUNCTION HOME - SET CURRENT DRIVE TO TRACK 0			*
; PRIMARY ENTRY AT HOME							*
;************************************************************************
HOME:	SUB	A		;CLEAR THE ACCUMULATOR AND SET Z
	STA	SEKTRK
	RET

;************************************************************************
;DEBLOCK ROUTINES							*
;************************************************************************

;************************************************************************
; BIOS FUNCTION READ - READ SECTOR (DEBLOCKED)				*
; PRIMARY ENTRY AT READ							*
;************************************************************************
READ:	MVI	A,WRUAL		;TREAT READ AS UNALLOCATED
	STA	WRTYPE
	STA	READOP		;SET A READ OPERATION

	DB	JR,(ALLOC-$-1)MOD 256

;************************************************************************
; BIOS FUNCTION WRITE - WRITE SECTOR (DEBLOCKED)			*
; PRIMARY ENTRY AT READ							*
;************************************************************************
WRITE:	XRA	A		;FLAG AS A WRITE OPERATION
	STA	READOP
	MOV	A,C		;WRITE TYPE FROM CP/M
	STA	WRTYPE
	CPI	WRUAL		;SEE IF UNALLOCATED

	DB	JRNZ,(CHKUNA-$-1)MOD 256

;------------------------------------------------------------------------
;UNALLOCATED WRITE, SET PARAMETERS
;------------------------------------------------------------------------
	CALL	PHYSEC		;CONVERT TO PHYSICAL SECTOR
	LHLD	SEKHST		;SET PHYSICAL SECTOR AND SIZE
	SHLD	UNASEC
	LHLD	SEKDSK		;GET THE DISK NUMBER
	SHLD	UNADSK		;SAVE IT FOR UNALLOCATED WRITE
	MOV	A,L
	CALL	DPFND		;GET DP TABLE ADDRESS
	INX	D		;OFFSET TO THE BLOCK MASK
	INX	D
	INX	D
	LDAX	D		;GET THE BLOCK MASK
	INR	A		;A = LOGICAL SECTORS PER BLOCK
	STA	UNACNT	;SAVE THE SECTOR COUNT

CHKUNA:	LXI	H,UNACNT	;SEE IF ANY UNALLOCATED SPACE AVAILABLE

	XRA	A
	CMP	M

	DB	JRZ,(ALLOC1-$-1)MOD 256

	DCR	M		;ELSE, USE SOME OF IT
	CALL	PHYSEC		;SET THE PHYSICAL SECTOR NUMBER
	LXI	D,UNADSK
	CALL	COMP		;COMPARE THE UNITS
	JZ	NXTSEC		;GO DO THE WRITE

;------------------------------------------------------------------------
; ALLOCATED WRITE
;------------------------------------------------------------------------
ALLOC:	XRA	A		;ALLOCATED WRITE REQUIRES PREREAD
	STA	UNACNT

ALLOC1:	INR	A
ALLOC2:	STA	RSFLAG

	XRA	A		;GET A ZERO
	STA	ERFLAG		;RESET THE ERROR FLAG

	LXI	H,HSTACT	;SEE IF HOST ACTIVE
	ORA	M
	MVI	M,1		;MARK IT ACTIVE FOR NEXT TIME
;				FILL THE HOST BUFFER IF EMPTY
	DB	JRZ,(FILHST-$-1)MOD 256

	CALL	PHYSEC		;SET THE PHYSICAL SECTOR NUMBER
	LXI	D,DISKNO
	CALL	COMP		;COMPARE THE UNITS

	DB	JRZ,(MATCH-$-1)MOD 256

; NO MATCH

	LDA	HSTWRT		;SEE IF HOST WRITTEN
	ORA	A
	CNZ	DWRITA		;PURGE THE BUFFER IF NEED BE

FILHST:	CALL	PHYSEC
	MOV	A,D
	STA	IDSSIZ
	LHLD	SEKDSK		;SET UP TO FILL THE BUFFER
	SHLD	DISKNO
	LHLD	SEKHST		;GET THE SECTOR, SIDE SELECT
	SHLD	SECTOR		;SAVE THEM
	LDA	SEKSEL
	STA	CUNIT
	LDA	RSFLAG
	ORA	A
	CNZ	DREAD		;FILL IT IF NEED BE
	LXI	H,ERFLAG
	ORA	M
	MOV	M,A
	XRA	A		;RESET PENDING WRITE FLAG
	STA	HSTWRT

; BUILD A SECTOR MASK IN A TO LOCATE THE PARTICULAR LOGICAL (128-BYTE)
; SECTOR IN THE PHYSICAL SECTOR

MATCH:	LHLD	IDSSIZ-1	;GET SECTOR SIZE IN H (JUNK IN L)
	XRA	A		;GET A ZERO

MATCH1:	ADC	A		;BUILD SECTOR MASK
	DCR	H
	STC
	JP	MATCH1		;LOOP TIL MASK IS BUILT

; FIND THE PARTICULAR 128-BYTE LOGICAL SECTOR WITHIN THE WHOLE SECTOR

	LHLD	SEKSEC		;GET SEKSEC INTO L
	ANA	L		;FIND THE RELATIVE SECTOR

; A=SECTOR OFFSET. COMPUTE ADDRESSES FOR TRANSFER TO/FROM BUFFER

	LHLD	DMAAD		;GET DMA ADDRESS INTO DE
	XCHG

	LXI	H,DBUF-80H	;BUILD ADDRESS FOR CPM SECTOR
	LXI	B,80H		;# OF BYTES IN LOGICAL SECTOR

MATCH2:	DAD	B
	DCR	A
	JP	MATCH2

; DE = DMA ADDRESS, HL = BUFFER ADDRESS. SWAP THEM FOR A WRITE

	LDA	READOP		;SEE IF READ OR WRITE
	ORA	A
	DB	JRNZ,(RWMOVE-$-1)MOD 256

	XCHG			;WRITE: SWAP POINTERS
	INR	A		; AND MARK WRITE OPERATION
	STA	HSTWRT
RWMOVE:

; DE HAS THE DESTINATION, HL HAS THE SOURCE, BC HAS BYTE COUNT

	DW	LDIR		;BLOCK MOVE WITH Z80 INSTRUCTION

;UPDATE THE DIRECTORY IF NECESSARY

	LDA	WRTYPE		;GET WRITE TYPE
	DCR	A		;SEE IF DIRECTORY ENTRY
	LDA	ERFLAG		;GET THE ERROR FLAG
	RNZ			;DONE IF NOT DIRECTORY ENTRY

	ORA	A		;SEE IF ANY ERRORS
	RNZ			;RETURN IF SO

	STA	HSTWRT		;RESET HOST WRITTEN

;PURGE THE WRITE BUFFER

DWRITA:	CALL	DWRITE		;WRITE BUFFER TO DISK
	LXI	H,ERFLAG
	ORA	M		;GET THE ERROR INDICATIONS
	MOV	M,A		;STORE THE ERROR FLAGS
	RET

;---------------------------------------------------------------------
;UNALLOCATED WRITE NEXT SECTOR ANTICIPATION LOGIC
;---------------------------------------------------------------------
NXTSEC:	LDA	SEKDSK		;FIND ADDRESS OF DP TABLE
	CALL	DPFND
	LDA	CPMSEC		;GET LAST LOGICAL SECTOR NUMBER
	INR	A
	XCHG			;DP TABLE ADDRESS TO (H,L)
	CMP	M		;SEE IF OVERFLOW

	DB	JRNC,(NXTSC2-$-1)MOD 256

	LXI	H,-11		;SAME TRACK, NOW SEE WHICH SIDE/SECTOR
	DAD	D
	MOV	E,M		;GET ADDRESS OF SKEW TABLE
	INX	H
	MOV	D,M
	MOV	C,A		;SET UP TO CALL SECTRAN
	MVI	B,0
	CALL	SECTRAN		;TRANSLATE THE SECTOR
	MVI	H,SIDE0		;SIDE 0 SELECT
	JP	NXTSC1		;JUMP ID SIDE 0

	MVI	H,SIDE1		;ELSE, SET SIDE 1
NXTSC1:	LDA	NXTSCT		;GET THE NEXT PHYSICAL SECTOR
	MOV	L,A		;SET THE SECTOR

	DB	JR,(NXTSC3-$-1)MOD 256

NXTSC2:	LXI	H,UNATRK	;SET FOR NEXT TRACK
	INR	M
	LXI	H,SIDE0*256+1	;SIDE 0 SECTOR 1
NXTSC3:	SHLD	UNASEC		;SET THE NEXT SIDE, SECTOR
	XRA	A		;GET A ZERO
	JMP	ALLOC2		;GO BACK TO MAINSTREAM

;---------------------------------------------------------------------
; FIND ADDRESS OF DISK PARAMETER TABLE
; ON ENTRY:
;    A = DISK NUMBER
; ON EXIT:
;   DE = ADDRESS OF DISK PARAMETER TABLE FOR THIS DRIVE
;---------------------------------------------------------------------
DPFND:	LXI	H,DPBASE-6	;DEVELOP ADDRESS OF DPTABLE
	LXI	D,16

DPFND1:	DAD	D
	DCR	A
	JP	DPFND1

	MOV	E,M	;PULL THE ADDRESS
	INX	H
	MOV	D,M
	RET

;---------------------------------------------------------------------
; LOGICAL TO PHYSICAL SECTOR TRANSLATION ROUTINE
; ON ENTRY:
;    SEKDSK = DESIRED DISK
;    SEKSEC = LOGICAL (128-BYTE) SECTOR NUMBER
; ON EXIT:
;    A = SEKHST = PHYSICAL SECTOR NUMBER
;    D = SECTOR SIZE (0-3) FROM PRMTBL
;---------------------------------------------------------------------
PHYSEC:	LDA	SEKDSK	;GET THE DESIRED UNIT NUMBER

; LOOK UP HOW MANY BYTES PER SECTOR FOR THIS DISK

	CALL	FDSB0	;GET PRMTBL POINTER
	INX	H	;POINT TO THE SECTOR SIZE
	MOV	D,M	;GET THE SECTOR SIZE  (0-3)
	PUSH	D
	LDA	SEKSEC	;GET THE LOGICAL SECTOR NUMBER

; COMPUTE PHYSICAL SECTOR NUMBER

	RAL
PHYSC1:	ORA	A	;RESET THE CARRY BIT
	RAR		;CONVERT TO PHYSICAL SECTOR
	DCR	D
	JP	PHYSC1

	INR	A	;(A) NOW HAS PHYSICAL SECTOR #

;PUT RESULTS WHERE THEY GO AND RETURN

	STA	SEKHST	;SAVE RESULTING SECTOR NUMBER
	POP	D	;RECOVER SECTOR SIZE
	RET

;---------------------------------------------------------------------
;COMPARE THE UNITS
; COMPARES {DISK, TRACK, SECTOR, SIDE} OF UNIT POINTED TO BY DE
; WITH THOSE IN SEKDSK THROUGH SEKDSK+3. 
; ON EXIT:
;    Z IS SET IF THE UNITS ARE THE SAME, CLEARED OTHERWISE.
;---------------------------------------------------------------------
COMP:	LXI	H,SEKDSK
	MVI	B,4

COMP1:	LDAX	D
	SUB	M
	RNZ		;DONE IF NO COMPARE

	INX	H	;POINT TO NEXT ENTRY
	INX	D
	DB	DJNZ,(COMP1-$-1)MOD 256

	RET

;************************************************************************
; THE FOLLOWING ROUTINES DO THE PRIMATIVE DISK ACCESSES.
;     IN ALL CASES, ONE SECTOR OF DATA IS TRANSFERRED. IF THE DISK HAS
;     NOT BEEN PREVIOUSLY ACCESSED, THESE ROUTINES WILL AUTOMATICALLY
;     DETERMINE THE DISK TYPE (8" OR 5"), SINGLE OR DOUBLE DENSITY,
;     AND SECTOR SIZE.
;
;     BEFORE THE DESIRED DATA IS TRANSFERRED, THE DESIRED TRACK IS
;     SEEKED OUT, THE DESIRED SECTOR AND SIZE IS SET, THEN THE ACTUAL
;     DATA TRANSFER.
;
;     UP TO 'TRIES' TRIES WILL BE ATTEMPTED BEFORE THE DATA TRANSFER IS
;     ABORTED.  ON RETURN TO THE CALLING ROUTINE, THE A REGISTER WILL
;     CONTAIN A ZERO IF THE OPERATION WAS SUCCESSFULL, OR NON-ZERO IF
;     NOT SUCCESSFUL. THE FLAG REGISTER WILL NOT NECESSARILY CORRESPOND
;     WITH THE REGISTER CONTENT.
;
; EXTERNAL ENTRY POINTS:
;    DREAD
;    DWRITE
;    IDRD1
;    FDSB0
;
;************************************************************************
;---------------------------------------------------------------------
; READ OR WRITE SECTOR, WITH RETRIES
; ON ENTRY:
;    (HSTBUF) = RAM ADDRESS FOR TRANSFER
; ON EXIT:
;    HL = NEXT ADDRESS IN RAM
;---------------------------------------------------------------------
DREAD:	DB	3EH		;SIM. MVI A INSTR
DWRITE:	XRA	A		;SET WRITE FLAG - XRA A OPCODE=0AFH
	MOV	D,A		;SAVE IT FOR LATER USE
	MVI	C,TRIES		;NUMBER OF RETRIES

RWAGN:	PUSH	B
	CALL	SEEK
	CZ	RDWR		;KEEP GOING ON SUCCESFUL SEEK
	POP	B
	RZ			;RETURN SUCCESSFULLY

	DCR	C		;SEE IF ALL TRIES ARE DONE
	RZ			;YES, ERROR RETURN
	ANI	SRNFER		;SEE IF RNF OR SEEK ERROR
	CNZ	EOJB		;RESTORE DRIVE IF SO

	DB	JR,(RWAGN-$-1)MOD 256

;---------------------------------------------------------------------
; READ OR WRITE
; ON ENTRY:
;     A = READ SECTOR CMMD
;     B = BYTE COUNT/4
;     D = 0 FOR WRITE, XXXXXXX1B FOR READ
;    HL = RAM ADDRESS FOR TRANSFER
; ON EXIT:
;     A = 0 AND Z = 1 ON SUCCESS
;    HL = NEXT ADDRESS IN RAM
;---------------------------------------------------------------------
RDWR:	DW	BIT0D		;TEST READ/WRITE (Z80 COMMAND)
	DI

	DB	JRZ,(WRDAT-$-1)MOD 256

;---------------------------------------------------------------------
; READ SECTOR (FAST AS POSSIBLE - PROBABLY WORKS WITH 2MHZ CPU)
; ON ENTRY:
;    A = READ SECTOR CMMD
;    B = BYTE COUNT/4
; ON EXIT:
;    A=0 AND Z=1 ON SUCCESS
;    HL = NEXT ADDRESS IN RAM
;---------------------------------------------------------------------
RDAT:	DCX	H	;BECAUSE OF INITIAL INCREMENT
	OUT	DCMMD	;DISK COMMAND PORT

READ1:	IN	DDATA	;(10) (27 CYCLES IN-TO-IN = 13.5uSec)
	INX	H	;(5)
	MOV	M,A	;(7)
	INX	H	;(5)
	IN	DDATA	;(10) (27 CYCLES IN-TO-IN = 13.5 uSec)
	MOV	M,A	;(7)
	INX	H	;(5)
	IN	DDATA	;(10) (22 CYCLES IN-TO-IN = 11 uSec)
	MOV	M,A	;(7)
	INX	H	;(5)
	DCR	B	;(5) SETS/CLEARS Z FOR THE END OF THE LOOP
	IN	DDATA	;(10) (27 CYCLES IN-TO-IN = 13.5 uSec)
	MOV	M,A	;(7)
	JNZ	READ1	;(10)

	INX	H
	CALL	EOJ
	ANI	SNOTRDY+SRNFER+SCRCERR+SLOSTD
			;ISOLATE READ ERROR BITS
	RET

;---------------------------------------------------------------------
; WRITE SECTOR (FAST AS POSSIBLE, MAYBE FAST ENOUGH FOR A 2 MHZ CPU)
; ON ENTRY:
;    A=READ SECTOR CMMD
;    B = BYTE COUNT/4
; ON EXIT:
;    A=0 AND Z=1 ON SUCCESS
;    HL = NEXT ADDRESS IN RAM
;---------------------------------------------------------------------
WRDAT:	ORI	WSECCM	;MAKE INTO WRITE COMMAND
	OUT	DCMMD	;DISK COMMAND PORT

WRT1:	MOV	A,M	;(7)
	OUT	DDATA	;(10) (27 CYCLES OUT-TO-OUT= 13.5 uSec)
	INX	H	;(5)
	MOV	A,M	;(7)
	INX	H	;(5)
	OUT	DDATA	;(10) (27 CYCLES OUT-TO-OUT= 13.5 uSec)
	MOV	A,M	;(7)
	INX	H	;(5)
	OUT	DDATA	;(10) (22 CYCLES IN-TO-IN = 11 uSec)
	MOV	A,M	;(7)
	INX	H	;(5)
	DCR	B	;(5) SETS/CLEARS Z FOR THE END OF THE LOOP
	OUT	DDATA	;(10) (27 CYCLES OUT-TO-OUT= 13.5 uSec)
	JNZ	WRT1	;(10)

	DB	JR,(EOJ-$-1)MOD 256

;---------------------------------------------------------------------
; FINISH COMMAND
; ENTRY AT EOJB RESTORES THE ALREADY-SELECTED DRIVE
; ENTRY AT EOJA IS USED FOR SEEKS AND STEP-INS, COMMAND IN B
; ENRTY AT EOJC DOES THE COMMAND IN A, WITHOUT LOOKING UP STEP RATE
; ENTRY AT EOJ FINISHES A READ OR WRITE, THEN GETS STATUS
; ENTRY AT EOJ1 JUST GETS STATUS
;---------------------------------------------------------------------
EOJB:	MVI	B,RESTOR	;BASIS OF RESTORE COMMAND

EOJA:	PUSH	H		;SAVE (H,L)
	PUSH	B

EOJA1:	CALL	FDSB		;GET THE PRMTBL POINTER
	POP	B
	DCX	H		;POINT TO THE STEP RATE ENTRY
	MOV	A,M		;GET THE STEP RATE
	POP	H		;RESTORE (H,L)
	ORA	B		;ADD ON THE COMMAND TO THE STEP RATE

EOJC:	OUT	DCMMD		;DO THE COMMAND

EOJ:	EI

; WAIT FOR 1793 TO FINISH COMMAND

	IN	DFLAG		;DISK FLAG PORT
	RAR

	DB	JRNC,(EOJ-$-1)MOD 256

; GET AND MASK THE DISK STATUS. RETURN IF READY

EOJ1:	IN	DSTAT		;GET THE DISK STATUS
	ANI	SNOTRDY+SWRPROT+SRCTYF+SRNFER+SCRCERR+SLOSTD
	RP			;DONE IF DRIVE IS READY

; DRIVE NOT READY - REPORT ERROR, WAIT FOR INPUT, AND WARM-BOOT

	PUSH	H		;SAVE MEMORY POINTER
	PUSH	B		;SAVE COMMAND

	LDA	DISKNO		;GET DRIVE NUMBER
	ADI	'A'		;CONVERT TO ASCII
	STA	DN2MSG		;PUT IT INTO MESSAGE
	LXI	H,DNRMSG
	CALL	PRTRD		;PRINT THE MESSAGE

	JMP	WBOOT

;---------------------------------------------------------------------
; SEEK
; ON ENTRY:
;    C = TARGET TRACK
; SUCCESFUL EXIT: 
;    A = THE RIGHT READ SECTOR COMMAND
;    B = BYTES PER SECTOR / 4
;   HL = DMA ADDRESS
;    Z = 1
;---------------------------------------------------------------------
SEEK:	CALL	IDRD		;INSURE HEADER HAS BEEN READ
	RNZ			;ERROR RETURN

SEEK1:	LDA	SECTOR		;SET THE SECTOR
	OUT	DSCTR		;DISK SECTOR PORT
	IN	DTRCK		;READ THE CURRENT TRACK SETTING
	MOV	C,A		;SAVE FOR A MOMENT
	LDA	TRACK		;GET DESIRED TRACK
	CMP	C		;SEE IF SEEK IS NEEDED

	DB	JRZ,(RDWRT-$-1)MOD 256

;SEEK TRACK IN A

	OUT	DDATA		;SET THE SEEK TRACK
	ORA	A		;SEE IF TRACK 0 IS DESIRED
	MVI	C,0		;NO AUTOWAIT WANTED
	LDA	CUNIT
	CALL	SU2		;RESET THE DENSITY BIT IF SO
	MVI	B,SEEKV		;BUILD THE SEEK COMMAND
	CALL	EOJA		;DO THE SEEK
	ANI	98H		;SEEK ERROR MASK
	RNZ			;DONE IF SEEK ERROR

RDWRT:	MVI	C,DCAUTOW	;AUTO-WAIT BIT
	CALL	SETUP

; BUILD READ/WRITE COMMAND FOR DCMMD

	IN	DFLAG
	ANI	DFSHLD		;SEE IF HEAD IS LOADED
	MVI	A,ED15MS

	DB	JRZ,(RDWRT1-$-1)MOD 256

	XRA	A		;ELSE, RESET THE HEAD LOAD DELAY FLAG

RDWRT1:	ADI	RDSEC		;BUILD A READ SECTOR COMMAND
	MOV	C,A		;SAVE THE COMMAND IN C
	IN	DTRCK		;SEE IF ON TRACK 0
	ORA	A		;IF SO, SET SECTOR SIZE = 0

	DB	JRZ,(RDWRT3-$-1)MOD 256

; COMPUTE B = BYTES/SECTOR/4

	INX	H		;GET THE SECTOR SIZE
	MOV	A,M

RDWRT3:	MOV	B,A		;PUT IN B FOR LOOP CONTROL
	MVI	A,10H		;SECTOR BASE LENGTH
RDWRT0:	ADD	A
	DCR	B
	JP	RDWRT0

;LOAD UP REGISTERS FOR RETURN

	MOV	B,A		;B GETS BYTES PER SECTOR / 4
	MOV	A,C		;DCTRL COMMAND INTO C
	LHLD	HSTBUF		;GET THE DMA ADDRESS

RDWRT2:	CMP	A		;CLEAR THE FLAGS
	RET

;---------------------------------------------------------------------
; GET THE ID FOR THIS DRIVE USING THE READ ADDRESS COMMAND
; ALSO COMPUTE SECTORS PER TRACK FOR THIS DISK
; ENTRY AT IDRD WILL UPDATE DRIVE INFO ONLY IF THE DRIVE CHANGED
; ENTRY AT IDRD1 WILL UPDATE ALWAYS
; ON EXIT:
;      A = SECTOR SIZE (0-3)
;     HL = ADDRESS OF THE RIGHT SECTOR SIZE BYTE IN PRMTBL
;     Z CLEARED IF ERROR
;---------------------------------------------------------------------
IDRD:	LHLD	LUNIT
	MOV	A,H		;GET THE CUNIT VALUE
	CMP	L		;SEE IF SAME AS LUNIT
	RZ			;RETURN IF SO

IDRD1:	MVI	C,TRIES-1	;WILL COUNT DOWN TO -1 HERE
IDRD3:	PUSH	B		;SAVE RETRY COUNTER

; PREPARE DCTRL AND BCTRL FOR THE READ ADDRESS COMMAND

	MVI	C,DCAUTOW	;SET THE AUTO-WAIT BIT
	CALL	SETUP		;SETS UP HL

; ISSUE READ ADDRESS COMMAND TO DETERMINE BYTES/SECTOR

	PUSH	H		;SAVE POINTER
	LXI	H,IDSV		;SET UP TO READ ADDRESS
	MVI	B,2		;SET UP TO READ (6)8 BYTES OF DATA
	MVI	A,RDADD		;READ ADDRESS COMMAND
	DI
	CALL	RDAT		;ON RETURN, Z MEANS GOOD READ

	POP	H		;RESTORE POINTER
	POP	B		;RECOVER RETRY COUNTER
	DB	JRZ,(IDRD2-$-1)MOD 256

; ON BAD SINGLE-DENSITY READ-ADDRESS, TRY AGAIN WITH DOUBLE-DENSITY
; IF ALREADY DOUBLE DENSITY, IT'S A REAL ERROR.

	MVI	A,DCDDEN	;SEE IF DDEN IS SET
	CMP	M
	JNC	IDRD4		;GO TRY DOUBLE-DENSITY

; ERROR DURING READ-ADDRESS - RETRY (WITH SINGLE-DENSITY) IF WE CAN

	DCR	C		;BUMP RETRY COUNTER
	RM			;ERROR RETURN WITH Z CLEARED	

	MVI	A,0FFH XOR DCDDEN	;TURN OF DDEN
	ANA	M		;RETRY WITH SINGLE DENSITY

IDRD4:	ORA	M		;ELSE, TRY AGAIN WITH DOUBLE DENSITY
	MOV	M,A
	DB	JR,(IDRD3-$-1)MOD 256

; TRACK 0 (AND MAYBE 1) MIGHT BE DIFFERENT BYTES/SECTOR, SO STEP IN
; AND TRY AGAIN IF WE ARE ON TRACK 0 OR 1

IDRD2:	IN	DSCTR		;GET THE TRACK # FROM ADDRESS READ
	OUT	DTRCK		;SET THE TRACK REGISTER
	CPI	2		;INSURE NOT ON TRACK 0 OR 1
	DB	JRC,(IDRD5-$-1)MOD 256

;UPDATE VARIOUS STORAGE FOR NEW VALUES

	MOV	A,M		;REGET SELBITS
	STA	LUNIT		;UPDATE LAST USED UNIT
	STA	CUNIT
	INX	H		;SET THE SECTOR SIZE
	LDA	IDSSIZ
	MOV	M,A

; NORMAL EXIT WITH CARRY CLEARED AND Z SET

	CMP	A		;CLEAR CARRY AND SET Z FOR NO ERROR
	RET			;NORMAL EXIT FROM SUBROUTINE

IDRD5:	MVI	B,STEPI		;BUILD A STEP-IN COMMAND
	CALL	EOJA
	DB	JR,(IDRD1-$-1)MOD 256

;---------------------------------------------------------------------
;SET UP DRIVE SPECIFIED IN A
; PRIMARY ENTRY AT SETUP
;     AUTO-WAIT BIT SET IN C AS NEEDED
; ENTRY AT SU2 SETS UP DCTRL AND BCTRL
;     A = VALUE FOR DCTRL
;     IF Z FLAG SET, CLEAR DOUBLE-DENSITY IN A FIRST
; ON EXIT:
;     HL = ADDRESS OF THE RIGHT SELECT BITS IN PRMTBL
;---------------------------------------------------------------------
SETUP:	CALL	FDSB		;GET THE DISK SELECT BITS

; SKIP INIT CODE IF ALREADY SELECTED

	DB	JRNZ,(SU0-$-1)MOD 256

; CREATE VALUE FOR DCTRL PORT, SAVE IT IN PRMTBL, AND WRITE IT TO DCTRL

	STC			;DRIVE SELECT BIT
SET1:	RAL			;SHIFT BIT INTO POSITION

	DB	DJNZ,(SET1-$-1)MOD 256

	IF	MINI
	ORI	DCMOTO		;ADD ON MOTOR BIT
	MOV	M,A		;SAVE IT
	OUT	DCTRL		;SELECT THE DRIVE
	ENDIF

; RESTORE THE DISK AND DETERMINE MAXI/MINI FROM TRACK 00 IN BSTAT

	IF	BOTH
	MVI	A,RESTOR OR RS15MS	;GET WORST CASE RESTORE COMMAND
	CALL	EOJC		;RESTORE THE DRIVE
	IN	BSTAT		;READ THE MINI TRK00 BIT
	RAR			;ISOLATE IT
	ENDIF
	IF	MINI
	MVI	A,STEP5		;MINI STEP RATE BITS
	ENDIF
	IF	BOTH

	DB	JRNC,(SET2-$-1)MOD 256

; 8" DRIVE: CHECK FOR DOUBLE-SIDED, FINISH 8" DCTRL, GET STEP RATE

	MOV	A,M		;REGET THE SELBITS
	ENDIF
	IF	MAXI
	ORI	DCMOTO+DCMAXI	;ADD ON THE 8", MOTOR ON BITS
	OUT	DCTRL
	MOV	M,A
	IN	BSTAT		;SEE IF A DOUBLE-SIDED DRIVE
	ANI	BSIDED		;ISOLATE THE BIT
;			SET 3 MS STEP RATE FOR DOUBLE-SIDED DRIVES
	DB	JRZ,(SET2-$-1)MOD 256

	MVI	A,STEP8	;SET MAXI STEP RATE
	ENDIF

; SET THE STEP RATE IN PRMTBL

SET2:	DCX	H		;POINT TO STEP RATE HOLD
	MOV	M,A		;SET THE STEP RATE
	INX	H		;RESET POINTER

; TURN OFF DOUBLE-DENSITY IF TRACK 0

SU0:	IN	DTRCK		;ELSE, SEE IF TRACK ZERO
	ORA	A
	IN	DDATA		;CLEAR OUT ANY JUNK
	MOV	A,M		;REGET THE SELBITS

; TURN OFF DDEN IF Z FLAG SET

SU2:	DB	JRNZ,(SU1-$-1)MOD 256

	ANI	DCDDEN XOR 0FFH	;INSURE DDEN IS RESET

; WRITE COMPLETE DCTRL VALUE TO DCTRL, AND SIDE TO BCTRL

SU1:	ORA	C		;ADD ON AUTOWAIT BIT
	OUT	DCTRL		;OUTPUT THE SELBITS
	LDA	SIDE		;SET THE SIDE SELECT
	OUT	BCTRL
	JMP	EOJ1		;GO INSURE A UNIT IS THERE

;---------------------------------------------------------------------
; GET DISK SELECT BITS FROM PARAMETER TABLE
; ON EXIT:
;      A = SELECT BITS FOR THIS DRIVE
;      B = DISKNO+1
;     HL = ADDRESS OF THE RIGHT SELECT BITS IN PRMTBL
;      Z = 1 IF NO DRIVE SELECTED
;---------------------------------------------------------------------
FDSB:	LDA	DISKNO		;GET THE SELECTED UNIT

FDSB0:	LXI	H,PRMTBL-2	;SEE IF DRIVE HAS BEEN ACTIVE
	INR	A		;OFFSET FOR LOOP CONTROL
	MOV	B,A		;SAVE FOR LATER USE
FDSB1:	INX	H		;OFFSET TABLE POINTER
	INX	H
	INX	H
	DCR	A

	DB	JRNZ,(FDSB1-$-1)MOD 256
				;A = 0 HERE
	ADD	M		;READ THE SELECT BITS, SET Z IF ZERO
	RET

;---------------------------------------------------------------------
;FIXED DATA TABLES FOR FOUR-DRIVE STANDARD
;IBM-COMPATIBLE 8" DISKS
;---------------------------------------------------------------------
;DISK PAAMETER HEADER FOR DISK 00
DPBASE:	DW	0,0
	DW	0,0
	DW	DIRBF,0
	DW	CHK00,ALL00
;DISK PARAMETER HEADER FOR DISK 01
	DW	0,0
	DW	0,0
	DW	DIRBF,0
	DW	CHK01,ALL01
;DISK PARAMETER HEADER FOR DISK 02
	DW	0,0
	DW	0,0
	DW	DIRBF,0
	DW	CHK02,ALL02
;DISK PARAMETER HEADER FOR DISK 03
	DW	0,0
	DW	0,0
	DW	DIRBF,0
	DW	CHK03,ALL03

;---------------------------------------------------------------------
;PARAMETER TABLE FOR DRIVE-UNIQUE CONSTANTS
;---------------------------------------------------------------------
PRMTBL:	DB	0,0,0	;DRIV 0 STP RAT, SLCT BYTS, SCTR SZ
	DB	0,0,0	;	1
	DB	0,0,0	;	2
	DB	0,0,0	;	3
;---------------------------------------------------------------------
; TABLES FOR 1-SIDED 8" DISKS
;---------------------------------------------------------------------
	IF	MAXI
;
;SECTOR TRANSLATE VECTOR
;
SELTBL:	DW	T826,DP8S0
	DW	T815,DP8S1
	DW	T88,DP8S2
	DW	T84,DP8S2
	DW	T848,DP8D0
	DW	T826,DP8D1
	DW	T815,DP8D2
	DW	T88,DP8D3
;
DP8S0:	DW	26	;SECTORS PER TRACK
	DB	3	;BLOCK SHIFT FACTOR
	DB	7	;;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	242	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8S1:	DW	30	;SECTORS PER TRACK (15 256-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	139	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8S2:	DW	32	;SECTORS PER TRACK (8 512-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	149	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D0:	DW	48	;SECTORS PER TRACK (ILLEGAL 128 BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	195	;BLOCKS PER DISKETTE
	DW	63	;#DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D1:	DW	52	;SECTORS PER TRACK (26 256-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	242	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D2:	DW	60	;SECTORS PER TRACK (15 512-BYTE SECTORS)
	DB	5	;BLOCK SHIFT FACTOR
	DB	31	;BLOCK MASK
	DB	3	;EXTENT MASK
	DW	139	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D3:	DW	64	;SECTORS PER TRACK (8 1024-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	299	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET

;---------------------------------------------------------------------
;TABLES FOR 2-SIDED 8" DISKS
;---------------------------------------------------------------------
;
;SECTOR TRANSLATE VECTOR
;
SELTBLA:	DW	T826,DP8D1
	DW	T815,DP8D2
	DW	T88,DP8D3
	DW	T84,DP8D3
	DW	T848,DP8D0A
	DW	T826,DP8D1A
	DW	T815,DP8D2A
	DW	T88,DP8D3A
;
DP8D0A:	DW	96	;SECTORS PER TRACK
	DB	5	;BLOCK SHIFT FACTOR
	DB	31	;BLOCK MASK
	DB	3	;EXTENT MASK
	DW	225	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D1A:	DW	104	;SECTORS PER TRACK
	DB	5	;BLOCK SHIFT FACTOR
	DB	31	;BLOCK MASK
	DB	3	;EXTENT MASK
	DW	242	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D2A:	DW	120	;SECTORS PER TRACK
	DB	6	;BLOCK SHIFT FACTOR
	DB	63	;BLOCK MASK
	DB	7	;EXTENT MASK
	DW	139	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
DP8D3A:	DW	128	;SECTORS PER TRACK
	DB	6	;BLOCK SHIFT FACTOR
	DB	63	;BLOCK MASK
	DB	7	;EXTENT MASK
	DW	149	;BLOCKS PER DISKETTE
	DW	127	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	32	;DIR CHECK VECTOR SIZE
	DW	2	;SYSTEM TRACK OFFSET
;
T848:	DB	48,48,1
T826:	DB	26,13,6
T815:	DB	15,15,4
T88:	DB	8,8,3
T84:	DB	4,2,2
	ENDIF


;---------------------------------------------------------------------
;TABLES FOR 1-SIDED 5.25" DISKS
;---------------------------------------------------------------------
	IF	MINI
;
;SECTOR TRANSLATE VECTOR
;
MSELTBL:	DW	T518,DP5S0
	DW	T510,DP5S1
	DW	T55,DP5S1
	DW	T52,DP5S3
	DW	T529,DP5D0
	DW	T518,DP5D1
	DW	T510,DP5D2
	DW	T55,DP5D2
DP5S0:	DW	18	;SECTORS PER TRACK
	DB	3	;BLOCK SHIFT FACTOR
	DB	7	;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	71	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
DP5S1:	DW	20	;SECTORS PER TRACK (10 256-BYTE SECOTRS)
	DB	3	;BLOCK SHIFT FACTOR
	DB	7	;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	79	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
DP5S3:	DW	16	;SECTORS PER TRACK (4 512-BYTE SECTORS)
	DB	3	;BLOCK SHIFT FACTOR
	DB	7	;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	63	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
DP5D0:	DW	29	;SECTORS PER TRACK (ILLEGAL 128-BYTE SECTORS)
	DB	3	;BLOCK SHIFT FACTOR
	DB	7	;BLOCK MASK
	DB	0	;EXTENT MASK
	DW	115	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	192	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
DP5D1:	DW	36	;SECTORS PER TRACK (18 256-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	71	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
DP5D2:	DW	40	;SECTORS PER TRACK (10 512-BYTE SECTORS)
	DB	4	;BLOCK SHIFT FACTOR
	DB	15	;BLOCK MASK
	DB	1	;EXTENT MASK
	DW	79	;BLOCKS PER DISKETTE
	DW	63	;# DIRECTORY ENTRIES
	DB	128	;ALLOC 0
	DB	0	;ALLOC 1
	DW	16	;DIR CHECK VECTOR SIZE
	DW	3	;SYSTEM TRACK OFFSET
T529:	DB	29,29,7
T518:	DB	18,9,4
T510:	DB	10,10,3
T55:	DB	5,5,2
T52:	DB	2,2,1
	ENDIF
;
;END OF FIXED TABLES
;
;---------------------------------------------------------------------
; RESTART ERROR
;---------------------------------------------------------------------
RSTER:	LXI	H,RSTMSG
COMERR:	CALL	PMSG
	JMP	WBOOTV	;GO TO WARM BOOT

;---------------------------------------------------------------------
; GENERIC I/O ROUTINE FOR NONEXISTANT DEVICES
;---------------------------------------------------------------------
IOER:	XRA	A	;RESET IOBYTE
	STA	IOBYTE
	LXI	H,IOMSG	;ERROR MESSAGE ADDRESS

	DB	JR,(COMERR-$-1)MOD 256

;---------------------------------------------------------------------
; HANDLERS FOR I/O CALLS TO BIOS. THESE HANDLERS IMPLEMENT THE
; IOBYTE, THOUGH MANY OF THE DRIVERS STUB OUT AT THE IOER ERROR
; HANDLER. NOTE THAT THESE ROUTINES MAKE HEAVY USE OF THE Z80
; RELATIVE JUMPS. SINCE THESE JUMPS ARE IMPLEMENTED FOR AN 8080
; ASSEMBLER, THERE IS NO CHECKING TO SEE OF THE JUMP DISTANCE IS
; TOO FAR - SO YOU MUST CHECK THESE MANUALLY AFTER CODE CHANGES.
;---------------------------------------------------------------------

;************************************************************************
; BIOS FUNCTION CONSOLE OUT						*
; ON ENTRY:								*
;    CHARACTER IS IN C							*
;************************************************************************
CO:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	DB	JRZ,(TTYOUT-$-1)MOD 256

	CPI	2
	DB	JRC,(CRTOUT-$-1)MOD 256
	DB	JRNZ,(CUSO1-$-1)MOD 256

;	CONSOLE OUT = LIST DEVICE, SO FALL INTO LO

;************************************************************************
; BIOS FUNCTION LIST OUT						*
; ON ENTRY:								*
;    CHARACTER IS IN C							*
;************************************************************************
LO:	LDA	IOBYTE
	ANI	0C0H	;ISOLATE LIST ASGT
	DB	JRZ,(TTYOUT-$-1)MOD 256

	CPI	80H	
	DB	JRC,(CRTOUT-$-1)MOD 256
	DB	JRZ,(LPRT-$-1)MOD 256
	DB	JR,(LUSE1-$-1)MOD 256

;************************************************************************
; BIOS FUNCTION CONSOLE INPUT STATUS					*
; ON EXIT:								*
;    CHARACTER AVAILABLE: A=0FFH, Z SET					*
;    CHARACTER NOT AVAILABLE: A=0, Z CLEARED				*
;************************************************************************
CSTS:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	DB	JRZ,(TTST-$-1)MOD 256

	CPI	2
	DB	JRC,(CRTST-$-1)MOD 256
	DB	JRNZ,(CUST1-$-1)MOD 256

;	CONSOLE INPUT = BATCH, SO FALL INTO READER IN STATUS

;------------------------------------------------------------------------
; READER INPUT STATUS (NOT A BIOS FUNCTION)
; ON EXIT:
;    CHARACTER AVAILABLE: A=0FFH, Z SET
;    CHARACTER NOT AVAILABLE: A=0, Z CLEARED
;------------------------------------------------------------------------
RDRST:	LDA	IOBYTE
	ANI	0CH	;ISOLATE READER ASGT
	DB	JRZ,(TTST-$-1)MOD 256
	CPI	8

	DB	JRC,(PTRST-$-1)MOD 256
	DB	JRZ,(RUST1-$-1)MOD 256
	DB	JR,(RUST2-$-1)MOD 256

;************************************************************************
; BIOS FUNCTION CONSOLE INPUT - WAIT FOR CONSOLE CHARACTER		*
; ON EXIT:								*
;    CHARACTER IS IN A							*
;************************************************************************
CI:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	DB	JRZ,(TTYIN-$-1)MOD 256

	CPI	2
	DB	JRC,(CRTIN-$-1)MOD 256
	DB	JRNZ,(CUST1-$-1)MOD 256

;	CONSOLE = BATCH, SO FALL INTO READER INPUT

;************************************************************************
; BIOS FUNCTION READER INPUT - WAIT FOR READER CHARACTER		*
; ON EXIT:								*
;    CHARACTER IS IN A							*
;************************************************************************
RI:	LDA	IOBYTE
	ANI	0CH	;ISOLATE BATCH ASGT
	DB	JRZ,(TTYRDR-$-1)MOD 256

	CPI	8
	DB	JRC,(PTRIN-$-1)MOD 256
	DB	JRZ,(RUSI1-$-1)MOD 256
	DB	JR,(RUSI2-$-1)MOD 256

;************************************************************************
; BIOS FUNCTION LIST OUTPUT STATUS					*
; ON EXIT:								*
;    DEVICE AVAILABLE: A=0FFH, Z SET					*
;    DEVICE BUSY: A=0, Z CLEARED					*
;************************************************************************
LSTAT:	LDA	IOBYTE
	ANI	0C0H	;ISOLATE THE LIST DEVICE ASSIGNMENT
	DB	JRZ,(TTOST-$-1)MOD 256

	CPI	80H
	DB	JRC,(HSPST-$-1)MOD 256
	DB	JRZ,(LPRTS-$-1)MOD 256
	DB	JR,(LUST1-$-1)MOD 256

;************************************************************************
; BIOS FUNCTION PUNCH OUT						*
; ON ENTRY:								*
;    CHARACTER IS IN C							*
;************************************************************************
PO:	LDA	IOBYTE
	ANI	30H	;ISOLATE PUNCH ASGT
	DB	JRZ,(TTPNCH-$-1)MOD 256

	CPI	20H
	DB	JRC,(HSP-$-1)MOD 256
	DB	JRZ,(PUSO1-$-1)MOD 256
	DB	JR,(PUSO2-$-1)MOD 256

;************************************************************************
; I/O DRIVERS FOR THE 8250 ASYNC COMM ELEMENT ON THE CCS 2810
;************************************************************************
TTYOUT:	CALL	TTOST	;GET 8250 LINE STATUS
	DB	JRZ,(TTYOUT-$-1)MOD 256

	MOV	A,C	;C HAS THE CHARATER TO SEND
	OUT	SDATA	;SO SEND IT TO THE 8250
	RET

TTST:	IN	SLSTAT	;GET 8250 LINE STATUS
	ANI	RXRDY	;SEE IF RECEIVE DATA AVAILABLE
FINST:	RZ		;RETURN WITH 0 IF NOT READY
	MVI	A,0FFH	;RETURN WITH FF IF READY
	RET

TTYIN:	CALL	TTST	;GET 8250 LINE STATUS
	DB	JRZ,(TTYIN-$-1)MOD 256

	IN	SDATA	;READ THE DATA
	RET

TTOST:	IN	SLSTAT	;GET 8250 LINE STATUS
	ANI	TXMTY	;ISOLATE TX BUFFER EMPTY BIT
	DB	JR,(FINST-$-1)MOD 256

;************************************************************************
; I/O DRIVERS FOR THE CCS 2720 PARALLEL PORT BOARD
;************************************************************************
LPRT:	CALL	LPRTS		;GET PRINTER AVAILABILITY

	DB	JRZ,(LPRT-$-1)MOD 256

	MOV	A,C		;GET CHARACTER TO PRINT
	OUT	PPORTA		;AND PRINT IT
	MVI	A,PPASLI AND PPASTR	;STROBE DATA
	OUT	PPORTD		;(10)
	MVI	A,PPASLI	;(7)
	NOP			;(4)
	OUT	PPORTD		;21 CYCLES = 5.25 USEC FOR 4MHZ CPU
	RET

LPRTS:	IN	PPORTA		;STATUS PORT FOR ALL PARALLEL PORTS
	XRI	0FFH		;SO THAT 0 MEANS BUSY
	ANI	PPABSY		;THIS PORT'S READY BIT
	DB	JR,(FINST-$-1)MOD 256

;************************************************************************
; NONEXISTANT DEVICE DRIVERS
;************************************************************************
;
; EQUATES FOR ADDITIONAL CONSOLE DEVICES
;
CRTIN:	EQU	IOER
CRTOUT:	EQU	IOER
CRTST:	EQU	IOER
CUSI1:	EQU	IOER	;UNASSIGNED USER CONSOLE (INPUT)
CUSO1:	EQU	IOER	;UNASSIGNED USER CONSOLE (OUTPUT)
CUST1:	EQU	IOER
;
; EQUATES FOR ADDITIONAL PAPER TAPE PUNCH DEVICES
;
TTPNCH:	EQU	TTYOUT	;UNASSIGNED TELETYPE PUNCH
HSP:	EQU	IOER	;UNASSIGNED HIGH SPEED PUNCH
HSPST:	EQU	IOER	;UNASSIGNED HIGH SPEED PUNCH STATUS
PUSO1:	EQU	IOER	;UNASSIGNED USER PUNCH 1
PUSO2:	EQU	IOER	;UNASSIGNED USER PUNCH 2
;
; EQUATES FOR ADDITIONAL LIST DEVICES
;
LUSE1:	EQU	IOER	;UNASSIGNED LIST DEVICE 1
LUST1:	EQU	IOER	;UNASSIGNED LIST DEVICE 1 STATUS
;
; EQUATES FOR ADDITIONAL PAPER TAPE READER DEVICES
;
TTYRDR:	EQU	TTYIN	;UNASSIGNED TELETYPE PAPER TAPE READER
PTRIN:	EQU	IOER	;UNASSIGNED HIGH SPEED PAPER TAPE READER
PTRST:	EQU	IOER	;UNASSIGNED HS PTR STATUS
RUSI1:	EQU	IOER	;UNASSIGNED PAPER TAPE READER 1
RUST1:	EQU	IOER	;UNASSIGNED PAPER TAPE READER 1 (STATUS)
RUSI2:	EQU	IOER	;UNASSIGNED PAPER TAPE READER 2
RUST2:	EQU	IOER	;UNASSIGNED PAPER TAPE READER 2 (STATUS)

;************************************************************************
; SUBROUTINE PRTRD PRINTS THE MESSAGE POINTED TO BY HL, THEN WAITS
; FOR USER INPUT. ROUTINE CONI READS THE CONSOLE AND STRIPS OFF
; THE ASCII PARITY BIT.
; ENTRY AT CONI IS ACTUALLY THE BIOS FUNCTION CALL FOR CONSOLE INPUT.
;************************************************************************
PRTRD:	CALL	PMSG

CONI:	CALL	CI	;GET THE NEXT CHARACTER
	ANI	7FH	;STRIP OFF THE PARITY BIT
	RET

;************************************************************************
; SUBROUTINE TO PRINT THE STRING AT (HL) ONTO THE CONSOLE.
; THE FIRST BYTE OF THE STRING IS ITS LENGTH. A CR-LF-NULL-NULL IS
; PRINTED BEFORE AND AFTER THE STRING.
;************************************************************************
PMSG:	PUSH	H	;SAVE POINTER TO ACTUAL STRING
	CALL	PCRLF	;PRINT INITIAL CRLF

	POP	H	;RECOVER STRING POINTER
	CALL	OUTSTR	;PRINT THE STRING NOW
;	FALL INTO PCRLF TO PRINT FINAL CR LF

;************************************************************************
; SUBROUTINE TO A CR-LF-NULL-NULL.
;************************************************************************
PCRLF:	LXI	H,CRMSG
;	FALL INTO OUTSTR

;************************************************************************
; SUBROUTINE TO PRINT THE STRING AT (HL)
;************************************************************************
OUTSTR:	MOV	B,M	;GET STRING LENGTH
OSLOOP:	INX	H	;NEXT CHARACTER
	MOV	C,M
	CALL	CO	;PRINT TO CONSOLE
	DB	DJNZ,(OSLOOP-$-1)MOD 256
	RET

;************************************************************************
; MESSAGE STRINGS
;************************************************************************
IOMSG:	DB	15,BELL,'I/O ASGT ERROR'
RSTMSG:	DB	8,BELL,'RST ERR'
DNRMSG:	DB	18,BELL,'DRIVE '
DN2MSG:	DB	0,' NOT READY'
BOTMSG:	DB	12,BELL,'CANNOT BOOT'
CRMSG:	DB	4,CR,LF,NULL,NULL

;************************************************************************
; ONE-TIME CODE, USED ONLY DURING COLD BOOT
; NOTE THAT THIS CODE GETS OVERWRITTEN BY WRITES TO THE UNINITIALIZED
; DATA AREA.
;************************************************************************
BOOT:	IN	DFLAG		;SEE IF AUTO-BOOT IN PROGRESS
	ANI	DFAUTOB		;ISOLATE THE BOOT BIT
	DB	JRNZ,(BOOTA-$-1)MOD 256	;JUMP IF NOT AUTO-BOOT

;---------------------------------------------------------------------
; SET UP THE SERIAL PORT IF THE ROM MONITOR DIDN'T ALREADY DO SO
;---------------------------------------------------------------------
	MVI	A,0FH		;SET MODEM CONTROL REGISTER
	OUT	SMDMCT
	MVI	A,83H		;SET BAUD RATE DIVISOR ACCESS
	OUT	SLCTRL
	MVI	A,SBAUD/256	;SET DIVISOR HIGH BYTE
	OUT	SINTEN
	MVI	A,SBAUD MOD 256
	OUT	SDATA		;SET DIVISOR LOW BYTE
	MVI	A,3		;SET 8250 LINE CONTROL
	OUT	SLCTRL
	XRA	A		;SET HANDSHAKE LINES ACTIVE
	OUT	SINTEN
	OUT	SLSTAT
BOOTA:

;---------------------------------------------------------------------
; BEGIN RESET PULSE TO INITIALIZE PRINTER
; USE THE FOLLOWING CODE TO KEEP THE INIT SIGNAL LOW AT LEAST 75 uSec
;---------------------------------------------------------------------
	MVI	A,PPASLI
	OUT	PPORTD		;SELECT PRINTER
	MVI	A,PPASLI AND PPAINI
	OUT	PPORTD

;---------------------------------------------------------------------
; SET UP STACK IN TBUF
;---------------------------------------------------------------------
	LXI	SP,TPA		;TEMP STACK (GROWS DOWN FROM THIS ADDRESS)

;---------------------------------------------------------------------
; INITIALIZE MEMORY
;---------------------------------------------------------------------
	XRA	A
	MOV	H,A		;CLEAR FROM ADDRESS 0
	MOV	L,A
	MOV	B,A		;CLEAR 256 BYTES, UP TO TPA

CLRP0:	MOV	M,A
	INX	H
	DB	DJNZ,(CLRP0-$-1)MOD 256

;INITIALIZE NON-ZERO ENTRIES

	LXI	H,SIDE0*256+1	;SET SIDE=0 AND SECTOR=1
	SHLD	SECTOR

;SELECT DRIVE A

	XRA	A		;DRIVE MUST BE IN C AND A FOR THIS CALL
	MOV	C,A		;C=DRIVE TO SELECT =0
	MOV	E,A		;SET NEW UNIT INDICATOR
	CALL	SELDSKA		;DOES NOT TOUCH DATA AREA BELOW...

; REMEMBER CURRENT UNIT

	LDA	PRMTBL+1
	STA	CUNIT

;---------------------------------------------------------------------
; RESTORE THE DISK TO TRACK 0
;---------------------------------------------------------------------
	CALL	EOJB

;---------------------------------------------------------------------
; PRINT SIGN-ON MESSAGE (AFTER MEMORY CLEAR TO ENSURE IOBYTE=0)
;---------------------------------------------------------------------
	LXI	H,LOGMSG	;SIGN ONTO THE SYSTEM
	CALL	PMSG

;---------------------------------------------------------------------
; END RESET PULSE TO PRINTER
;---------------------------------------------------------------------
	MVI	A,PPASLI	;END INIT PULSE, STAY SELECTED
	OUT	PPORTD

;---------------------------------------------------------------------
; GO RUN CODE THAT IS COMMON TO WARM BOOT
;---------------------------------------------------------------------
	JMP	BOOT0	;GO DO THE BOOT COMMON CODE

LOGMSG:	DB	17+30,MSIZE/10+'0',MSIZE MOD 10 + '0'
	DB	'k CP/M vers '
	DB	VERS/10+'0','.',VERS MOD 10+'0'
	DB	CR,LF,'Double Density BIOS vers '
	db	BVERS/10+'0','.',BVERS MOD 10+'0'

;---------------------------------------------------------------------
; THE REMAINDER OF THE CBIOS IS RESERVED UNINITIALIZED DATA AREA, AND
; DOES NOT NEED TO BE PART OF THE SYSTEM MEMORY IMAGE (THE SPACE MUST
; BE AVAILABLE,HOWEVER, BETWEEN "BEGDAT" AND "ENDDAT").
; NOTE THAT THESE DATA OVERWRITE THE ONE-TIME COLD BOOT CODE ABOVE.
;---------------------------------------------------------------------
	ORG	BOOT

BEGDAT	EQU	$	;BEGINNING OF DATA AREA

DMAAD:	DS	2	;DIRECT MEMORY ADDRESS

HSTACT:	DS	1
HSTWRT:	DS	1

UNADSK:	DS	1
UNATRK:	DS	1
UNASEC:	DS	1
UNASID:	DS	1
UNACNT:	DS	1
CPMSEC:	DS	1
NXTSCT:	DS	1

ERFLAG:	DS	1
RSFLAG:	DS	1
READOP:	DS	1
WRTYPE:	DS	1

;SCRATCH RAM AREA FOR BDOS USE

DIRBF:	DS	128	;SCRATCH DIRECTORY AREA
ALL00:	DS	38	;ALLOCATION VECTOR 0
ALL01:	DS	38	;ALLOCATION VECTOR 1
ALL02:	DS	38	;ALLOCATION VESTOR 2
ALL03:	DS	38	;ALLOCATION VECTOR 3
CHK00:	DS	32	;CHECK VECTOR 0
CHK01:	DS	32	;CHECK VECTOR 1
CHK02:	DS	32	;CHECK VECTOR 2
CHK03:	DS	32	;CHECK VECTOR 3

DBUF:	DS	1024	;DISK BUFFER

ENDDAT	EQU	$	;END OF DATA AREA
DATSIZ	EQU	$-BEGDAT	;SIZE OF DATA AREA

CONST:	EQU	CSTS
CONOUT:	EQU	CO
LIST:	EQU	LO
PUNCH:	EQU	PO
READER:	EQU	RI
LISTST:	EQU	LSTAT

	END
