
	.Z80

;                   *****************
;	            SECTOR DEBLOCKING 
;                   *****************
;
;	         HOME DISK ENTRY ROUTINE
;	   THIS ROUTINE IS USED TO RECALIBRATE 
;	  THE CURRENT SELECTED DISK TO TRACK #0
;
HOME:
	IF	USRCPM
	XOR	A
	LD	(TRK),A
	LD	(TRK16),A
	LD	(TRK16+1),A	; 2.0 COMPATIBLE
	LD	A,(NODISK)	; CHECK CURRENT SELECT DISK
	CP	'M'-'A'		; LOCAL DISK?
	JP	NC,DHOME	; YES
	RET			; NO
	ENDIF
;
DHOME:	LD	A,(HSTWRT)	; CHECK WRITE PENDING DATA IN BUFFER
	OR	A
	JR	NZ,HOMED	; IF WRITE DATA PENDING THEN SKIP
	LD	(HSTACT),A
HOMED:
	IF	USRCPM
	XOR	A
	LD	(TRK),A		; RESET SEEK TRACK TO ZERO
	LD	H,A
	LD	L,A
	LD	(TRK16),HL
	ENDIF
;
	IF	STDCPM
	LD	BC,0
	CALL	SETTRK
	ENDIF
;
	RET
;
;	WE JUMP TO HERE WHENEVER THE USER WANTS TO
;	RESTART THE SYSTEM, I.E., REREAD CP/M WITHOUT
;	MODIFYING THE BIOS.  FLUSH ALL WRITE PENDING
;	DATA TO DISKS.
;
	IF	USRCPM
DWBOOT:	LD	HL,HSTACT	;CHECK DISK BUFFER ACTIVE
	BIT	0,(HL)
	RES	0,(HL)		;RESET BUFFER ACTIVE FLAG ANY WAY
	RET	Z
	LD	HL,HSTWRT
	BIT	0,(HL)		;ELSE CHECK BUFFER WRITE PENDING
	RES	0,(HL)		;RESET BUFFER WRITE PENDING
	CALL	NZ,WRITEHST	;IF WRITE PENDING THEN WRITE TO IT
	RET			;SOMEBODY ELSE IS RESPONSIBLE
	ENDIF
;	             DISK SELECT ENTRY ROUTINE
;	ACCEPTS: C = DRIVE SELECT (WITH C=0 FOR SELECT DRIVE"A")
;	RETURNS: HL = DPB POINTER OF DRIVE SELECT (IF C=VALID DRIVE NUMBER)
;		 HL = 0H IF (C=INVALID DRIVE NUMBER)
;	SETS:    CP/M SECTORS PER TRACK
;		 NUMBER OF SECTORS PER BLOCK	

SELDSK:
	IF	USRCPM
.8080
	LXI  	H,0		;SET UP FOR ERROR CODE
	MOV  	A,E		;LOW BIT 0 IF FIRST TIME
	STA  	SELFLG		;SAVE IN REQBLK
	LDA  	NDISK		;GET NUMBER OF REMOTE DISK
	MOV  	B,A
	MOV  	A,C		;GET NEW DISK NUMBER.
	CMP  	B		;CHECK IF DRIVE EXISTS
	STA	NODISK
	STA	SELDRV		;SAVE SELECTED DRIVE		MMMOST2.0
	JNC	DSELDSK		;SELECT LOCAL DISK DRIVES
;
;	SELECT DRIVE AS A FUNCTION OF H,L
;
SELDS1:
	MOV L,A			;LOAD DISK NUMBER AND ZERO BYTE
	LXI  D,DPBASE		;POINT TO DISK PARM START.
	DAD  H			;*2
	DAD  H			; *4
	DAD  H			;  *8
	DAD  H			;   *16
	DAD  D			;COMPUTE INDEX FOR THE DRIVE
;
;	IF LOW BIT OF E=0 THEN GET NEW DPB FROM MMMOST
;
	LDA	SELFLG
	ANI	01H
	MVI	A,0
	RET 			;SHOULD BE RNZ FOR NORMAL USE
	ENDIF
.Z80
DSELDSK:
	LD	A,C		; GET CURRENT DRIVE
	LD	(SEKDSK),A
;
	IF	STDCPM
	CP	MAXDRV		; CHECK WITH MAXIMUM DRIVES
	JR	C,SELDR1
	LD	HL,0		; ERROR FOR ILLEGAL DRIVE
	RET
SELDR1:
	LD	B,0
;
SELDR2:	LD	HL,DISKTAB	; DISK PARAMETER BLOCK OFFSET
	ADD	HL,BC
	LD	A,(HL)
	RLCA
	RLCA
	RLCA
	RLCA
	LD	C,A
;
	LD	HL,DPBH		; DISK PARAMETER BLOCK BASE ADDRESS
	ADD	HL,BC
	PUSH	HL		; SAVE DPB ADDRESS
	LD	DE,DPBOFF
	ADD	HL,DE
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	EX	DE,HL
	LD	A,(HL)
	LD	(SPTCPM),A	; CP/M SECTOR PER TRACK
	LD	DE,HDBOFF
	ADD	HL,DE
	LD	A,(HL)
	INC	A
	LD	(SECBLK),A	; # OF SECTORS PER BLOCK
	POP	HL
	ENDIF
;
	IF	USRCPM
;
; ACCEPTS: C = DRIVE SELECT (WITH C=0 FOR SELECT DRIVE"A")
; RETURNS: HL = DPB POINTER OF DRIVE SELECT (IF C=VALID DRIVE NUMBER)
;          HL = 0H IF (C=INVALID DRIVE NUMBER)
;
	LD	B,A
	IN	A,(IDSWITCH)
	AND 	38H
	CP	30H
	LD	A,B
	LD	HL,0
	RET	Z
	SUB	'M'-'A'
	RET	M
	CP	MAXDRV
	RET	NC		;EXIT IF INVALID DRIVE
	CALL	FLPHR1		;CONVERT TO REAL DISK
	LD	A,(REALDSK)	;GET REAL DISK NUMBER TO A
;
;
;	DISK NUMBER IS IN PROPER RANGE
;
	LD	L,A
	LD	H,0
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL		;MULTIPLY BY 16
	LD	DE,DPBH		;BASE OF PARAMETER BLOCK
	ADD	HL,DE		;HL=.DPB(CURDSK)
	EX	DE,HL
	LD	HL,10
	ADD	HL,DE
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	LD	A,(BC)
	LD	(SECPTRK),A	;SET SECTOR PER TRACK FOR BLOCK/DEBLOCKING
	INC	BC
	INC	BC
	INC	BC
	LD	A,(BC)
	LD	(SECBLK),A
	EX	DE,HL		;RESTORE DPB POINTER TO HL
	ENDIF
	RET
;
;	SETTRK -- SET READ/WRITE TRACK
;	ACCEPTS: C = TRACK NUMBER
;
SETTRK:	
	IF	STDCPM
	LD	(SEKTRK),BC
	ENDIF
;
	IF	USRCPM
	LD	A,C		;GET NEW TRACK NUMBER
	LD	(TRK),A		;UPDATE OLD WITH NEW
	LD	(TRK16),A
	LD	A,B
	LD	(TRK16+1),A	;2.0 COMPATIBLE
	ENDIF
;
	RET
;
;	SETSEC -- SET READ/WRITE SECTOR
;	ACCEPTS: C = SECTOR NUMBER
;
SETSEC:	LD	A,C
;
	IF	STDCPM
	LD	(SEKSEC),A
	ENDIF
;
	IF	USRCPM
	LD	(SECT),A
	LD	A,B
	LD	(SECT+1),A	;2.0 COMPATIBLE
	ENDIF
;
	RET
;
;	SETDMA -- SET READ/WRITE DMA BUFFER ADDRESS
;
SETDMA:	LD	(DMAADR),BC
	RET
;
;	SECTRAN -- TRANSLATE SECTOR
;	ACCEPTS: BC = LOGICAL SECTOR
;	RETURNS; HL = PHYSICAL SECTOR
;
SECTRN:	
	IF	USRCPM
	LD	A,(NODISK)	;GET CURRENT SELECTED DRIVE
	CP	'M'-'A'		;LOCAL?
	ENDIF
;
	LD	H,B
	LD	L,C
;
	IF	USRCPM
	RET	C		;NO
	INC	HL		;YES, LOCAL DRIVE
	ENDIF
;	
	RET
;
;	READ CP/M SECTOR ENTRY POINT
;	THIS ROUTINE READ THE SELECTED CP/M SECTOR
;	INTO SELECTED DMA BUFFER POINTER
;
READ:	
	IF	STDCPM
	LD	A,(SEKDSK)	; CHECK IF SELECTED DISK EXISTS
	CP	MAXDRV
	LD	A,1
	RET 	NC		; IF NOT, RETURN WITH ERROR FLAG
	ENDIF
;
	IF	USRCPM
;
; 	READ THE SECTOR AT SECT, FROM THE PRESENT TRACK.
; 	USE STARTING ADDRESS AT DMAADR.
.8080
	LDA	NODISK		;IF CURRENT SELECTED 
	CPI	'M'-'A'		;DRIVE IS LOCAL 
	JNC	DREAD		;READ FROM LOCAL FLOPPY/HARD
READH1:	LXI	D,128 		;SAVE BYTE AT END OF BUFFER
	LHLD	DMAADR
	DAD	D
	MOV	A,M
	STA	SAVBUF
	MVI	A,READC
	STA	IRQCOD		;INITIATE A READ REQUEST
;
	CALL	SETERRC		;SET ERROR RETRY COUNT
;
READ1:	LXI	H,IRQBLK
	MVI	B,10
	CALL	PNXO
	CPI	0
	JZ	XIIOST		;CONTINUE
;
	CALL	DECERRC		;RETRY - 1
	JNZ	READ1		;RETRY TO SEND 
				;RQBLK AGAIN IF ERROR.
	MVI 	B,6		;XER OUT ERROR			MMMOST2.0
	JMP 	PRNTERR		;				MMMOST2.0
;
XIIOST:	LXI	H,STABUF	;GET READ RETURN CODE
	MVI	B,4
	CALL	PNXI
	CPI	0 
	MVI	A,1		;				MMMOST2.0
	STA	SELFLG		;				MMMOST2.0
	JZ	TSTRER		;IF RETURN CODE OK THEN SKIP
	CALL	DECERRC		;ELSE ERROR RETRY - 1
	JNZ	READ1		;RETRY AGAIN IF 
				;COUNT IS NOT EQUAL ZERO
	MVI	B,7		;				MMMOST2.0
	JMP	PRNTERR		;				MMMOST2.0
TSTRER:	LDA	STABUF+3	;				MMMOST2.0
	CPI	0
	JZ	GETSEC
	MOV	B,A		;				MMMOST2.0
	MVI	C,1		;				MMMOST2.0
	CALL	PRNTERR		;				MMMOST2.0
	LDA	STABUF+2	;				MMMOST2.0
	RET			;				MMMOST2.0
;
GETSEC:	LHLD	DMAADR
	MVI	B,128
	CALL	PNXI		;TRANSFER SECTOR IN
	MOV	C,A		;SAVE RETURN CODE
	LXI	D,128
	LHLD	DMAADR
	DAD	D
	LDA	SAVBUF
	MOV	M,A
	MOV	A,C
	CPI	0
	RZ
;
	CALL	DECERRC		;RETRY -1
	JNZ	READ1		;IF NOT ZERO THEN DO AGAIN
;
	MVI	B,7		;TRANSFER IN ERROR CODE		MMMOST2.0
	JMP	PRNTERR		;				MMMOST2.0
	ENDIF
.Z80
DREAD:	XOR	A	
	LD	(UNACNT),A
	LD	A,1
	LD	(READOP),A	; SET READ OPERATION
	LD	(RSFLAG),A	; MUST READ DATA
	LD	A,WRUAL
	LD	(WRTYPE),A	; TREAT AS UNALLOC
	JP	RWOPER		; PERFORM THE READ
;
;	WRITE ROUTINE ENTRY POINT
;	THIS ROUTINE WRITE THE SELECTED DMA BUUFER POINTER
;	INTO SELECTED CP/M SECTOR
;
WRITE:	
	IF	STDCPM
	LD	A,(SEKDSK)
	CP	MAXDRV
	LD	A,1
	RET	NC
	ENDIF
.8080
	IF	USRCPM
	LDA	NODISK		;IF CURRENT SELECTED 
	CPI	'M'-'A'		;DRIVE IS LOCAL 
	JNC	DWRITE		;WRITE TO LOCAL FLOPPY/HARD DISK
;
WITEH1:	MVI	A,WRTCD
	STA	IRQCOD
	MOV	A,C		;GET WRITE TYPE FROM BDOS
	STA	WRTYPE		;0=NORMAL 
				;1=DIRECTORY
				;2=FIRST IN BLOCK
	CALL	SETERRC		;SET ERROR RETRY COUNT
;
WRITE1:	LXI	H,IRQBLK
	MVI	B,10
	CALL	PNXO		;MAKE THE READ REQUEST
	CPI	0
	JZ	XFRDAT
;
	CALL	DECERRC		;RETRY -1
	JNZ	WRITE1		;RETRY TO SEND 
				;THE RQBLK AGAIN
	MVI	B,6		;				MMMOST2.0
	JMP	PRNTERR		;				MMMOST2.0
;
XFRDAT:	LHLD	DMAADR
	MVI	B,128
	CALL	PNXO
	CPI	0
	JZ	CKWCD
;
	CALL	DECERRC		;RETRY - 1
	JNZ	WRITE1
;
	MVI	B,6		;				MMMOST2.0
	CALL	PRNTERR		;				MMMOST2.0
	JMP	CKTYP2
;
CKWCD:	LXI	H,STABUF	;CHECK WRITE RETURN CODE
	MVI	B,4
	CALL	PNXI
	CPI	0
	MVI	A,1		;				MMMOST2.0
	STA	SELFLG		;				MMMOST2.0
	JZ	TSTER
;
	CALL	DECERRC		;DO IT AGAIN IF 
	JNZ	WRITE1		;MORE RETRY COUNT LEFT
;
	MVI	B,7		;				MMMOST2.0
	CALL	PRNTERR		;				MMMOST2.0
	JMP	CKTYP2		;				MMMOST2.0
;
TSTER:	LDA	STABUF+3	;				MMMOST2.0
	CPI	0
	RZ 
;*
;*	M M M O S T   2 . 0
;*
	MOV	B,A
	MVI	C,2		;TO ERROR PRINTING ROUTINE
	CALL	PRNTERR
	LDA	WRTYPE
	CPI	2
	JZ	WBOOT
	JMP	BRET
;
CKTYP2:	LDA	WRTYPE
	CPI	2
	JZ	WBOOT
BBDRET:	MVI	A,0FFH
	STA	STABUF+2
BRET:	LDA	STABUF+2
	RET
	ENDIF
.Z80
DWRITE:	XOR	A
	LD	(READOP),A	; SET OPERATION TO WRITE
	LD	A,C		; SAVE WRITE TYPE
	LD	(WRTYPE),A
	CP	WRUAL		; WRITE UNALLOCATED?
	JR	NZ,CHKUNA	; CHECK FOR UNALLOC
;
;	WRITE TO UNALLOCATION, SET PARAMETERS
;
	IF	STDCPM
	LD	HL,SECBLK	; NEXT UNALLOCATION
	LD	DE,UNACNT
	LD	BC,5
	LDIR
	ENDIF
;
	IF	USRCPM
	LD	HL,(SECBLK)	;NEXT UNALLOCATION
	LD	(UNACNT),HL	;UNACNT, UNADSK
	LD	HL,(TRK16)
	LD	(UNATRK),HL	;UNATRK
	LD	A,(SECT)
	LD	(UNASEC),A	;UNASEC
	ENDIF
;
CHKUNA:
;	CHECK FOR WRITE TO UNALLOCATED SECTOR
;
	LD	A,(UNACNT)	; ANY UNALLOCATED SECTOR REMAIN?
	OR	A
	JR	Z,ALLOC		; SKIP IF NOT
;
;	MORE UNALLOCATED SECTOR REMAIN
;
	DEC	A		; UNACNT = UNACNT-1
	LD	(UNACNT),A
	LD	A,(SEKDSK)	; GET SEEK DISK INTO A
	LD	HL,UNADSK
	CP	(HL)		; SEEK.DSK = UNALL.DSK?
	JR	NZ,ALLOC	; SKIP IF NOT
;
;	DISK ARE THE SAME
;
	LD	HL,(UNATRK)
	CALL	SKTRKCMP
	JR	NZ,ALLOC	; SKIP IF NOT
;
;	TRACKS ARE THE SAME
;
	IF	STDCPM
	LD	A,(SEKSEC)	; GET SEEK SECTOR INTO A
	ENDIF
;
	IF	USRCPM
	LD	A,(SECT)
	ENDIF
;
	LD	HL,UNASEC
	CP	(HL)		; SEEK.SEC = UNALL.SEC?
	JR	NZ,ALLOC	; SKIP IF NOT
;
;	MATCH, MOVE "UNASEC" TO NEXT SECTOR FOR FUTURE REF.
;
	INC	(HL)		; UNASEC = UNASEC+1
	LD	C,(HL)
;
	IF	STDCPM
	LD	A,(SPTCPM)
	ENDIF
	IF	USRCPM
	LD	A,(SECPTRK)
	ENDIF
;
	CP	C		; CHECK END OF SEC IN CURRENT TRACK
;
	IF	STDCPM
	JR	NZ,NOOVF	; SKIP IF NO OVERFLOW
	ENDIF
	IF	USRCPM
	JR	NC,NOOVF
	ENDIF
;
;	OVERFLOW TO NEXT TRACK
;
	LD	(HL),0		; PRESET UNASEC = 1
	LD	HL,UNATRK
	INC	(HL)		; UNATRK = UNATRK+1
;
NOOVF:
;
;	MATCH FOUND, MARK AS UNNECESSARY READ
;
	XOR	A
	LD	(RSFLAG),A	; SET PRE-READ FLAG = NO
	JR	RWOPER		; PERFORM WRITE CP/M SECTOR
;
ALLOC:
;
;	NOT AN UNALLOCATED SECTOR, REQUIRES PRE-READ
;
	XOR	A
	LD	(UNACNT),A	; RESET UNALLOC. COUNTER = 0
	INC	A
	LD	(RSFLAG),A	; SET PRE-READ FLAG = YES.
;
;	COMMON CODE FOR READ AND WRITE ROUTINE
;
RWOPER:	XOR	A
	LD	(ERFLAG),A	; RESET ERROR FLAG
;
	IF	STDCPM
	LD	A,(SEKSEC)	; CONVERT SEEEK SECTOR TO HOST SECTOR
	ENDIF
;
	IF	USRCPM
	LD	A,(SECT)
	DEC	A
	ENDIF
	OR	A		; CLEAR CARRY FLAG
	RRA
;
	IF	USRCPM
	INC	A
	ENDIF
;	
	LD	(SEKHST),A	; SEKHST = ((SEKSEC-1)/2)+1
;
;	ACTIVE HOST SECTOR?
;
	LD	HL,HSTACT
	BIT	0,(HL)
	SET	0,(HL)		; ALWAY BECOMES 1
	JR	Z,FILHST	; FILL BUFFER IN ACTIVE FLAG = 0
;
;	HOST BUFFER ACTIVE, SAME AS SEEK BUFFER?
;
	LD	A,(SEKDSK)
	LD	HL,HSTDSK
	CP	(HL)		; SAME DISK?
	JR	NZ,NOMATCH	; SKIP IF NOT MATCH
;
;	SAME DISK, SAME TRACK?
;
	LD	HL,(HSTTRK)
	CALL	SKTRKCMP
	JR	NZ,NOMATCH	; SKIP IF NOT MATCH
;
;	SAME DISK, SAME TRACK, SAME BUFFER?
;
	LD	A,(SEKHST)
	LD	HL,HSTSEC
	CP	(HL)
	JR	Z,MATCH		; SKIP IF MATCH
;
NOMATCH:
;
;	PROPER DISK, BUT NOT CORRECT SECTOR
;
	LD	A,(HSTWRT)	; WRITE DATA PENDING IN BUFFER?
	OR	A
	JR	Z,FILHST	; SKIP IF NO WRITE PENDING
	CALL	WRITEHST	; IF YES, THEN WRITE DATA TO DISK
	LD	A,(ERFLAG)
	OR	A
	JR	Z,FILHST	; SKIP IF NO ERROR
FILHS2:
	XOR	A
	LD	(HSTACT),A	; RESET HOST ACTIVE FLAG
;
MEXIT:	LD	A,FDTOUT
	LD	(FDTIME),A
	LD	A,(ERFLAG)
	OR	A
	RET
;
FILHST:
;	MAY HAVE TO PRE-READ DATA TO HOST BUFFER
;
	LD	A,(SEKDSK)
	LD	(HSTDSK),A
;
	IF	STDCPM
	LD	HL,(SEKTRK)
	ENDIF
;
	IF	USRCPM
	LD	HL,(TRK16)
	ENDIF
;
	LD	(HSTTRK),HL
	LD	A,(SEKHST)
	LD	(HSTSEC),A
	LD	A,(RSFLAG)	; CHECK BUFFER REQUIRES PRE-READ
	OR	A
	CALL	NZ,READHST	; IF YES, THEN READ DATA FROM DISK
;
	LD	A,(ERFLAG)	; CHECK FOR I/O ERROR
	OR	A
	JR	NZ,FILHS2	; IF ERROR THEN SKIP
;
FILHS1:	XOR	A		; INTO BUFFER
	LD	(HSTWRT),A	; RESET PENDING WRITE FLAG
;
MATCH:
;
;	COPY DATA TO OR FROM BUFFER
;
	IF	STDCPM
	LD	A,(SEKSEC)	; COMPUTE RELATIVE HOST BUFFER ADDRESS
	ENDIF
;
	IF	USRCPM
	LD	A,(SECT)
	DEC	A
	ENDIF
;
	AND	SECMSK
	RRA
	LD	H,A
	LD	A,0
	RRA
	LD	L,A		; HL = ((SEKSEC-1)&SECMSK)*128
;
;	HL HAS RELATIVE HOST BUFFER ADDRESS
;
	LD	DE,HSTBUF
	ADD	HL,DE
	EX	DE,HL		; SAVE HOST ADDRESS INTO DE
	LD	HL,(DMAADR)	; GET/PUT CP/M DATA BUFFER POINTER
	EX	DE,HL
	LD	BC,128		; SET BC = LENGTH TO MOVE
	LD	A,(READOP)	; WHICH WAY?
	OR	A
	JR	NZ,RWMOVE	; SKIP IF READ OPERATION
;
;	WRITE OPERATION, MARK AND SWITCH DIRECTION
;
	LD	A,1
	LD	(HSTWRT),A	; SET WRITE PENDING FLAG ON
	EX	DE,HL		; SOURCE/DESTINATION POINTERS SWAP
;
RWMOVE:
;
;	BC INITIALLY 128, DE DESTINATION, HL IS SOURCE POINTER
;
	LDIR
;
;	DATA HAS BEEN MOVE TO/FROM HOST BUFFER
;
	LD	A,(WRTYPE)	; CHECK WRITE TYPE OPERATION
	CP	WRDIR		; DIRECTORY WRITE?
	JR	NZ,RWEXIT	; IF NOT A DIRECTORY WRITE THEN EXIT
				; WITH ERROR FLAG IN A
	LD	A,(ERFLAG)
	OR	A		; ELSE CHECK FOR ERROR
	JP	NZ,FILHS2	; IF ERROR THEN EXIT
	XOR	A
	LD	(HSTWRT),A	; RESET WRITE PENDING FLAG
	CALL	WRITEHST
RWEXIT:
	LD	A,(ERFLAG)
	OR	A
	JP	Z,MEXIT		; EXIT IF NO ERROR
	JP	FILHS2
;
;	16-BIT COMPARE BETWEEN (HL) AND (SEKTRK)
;
SKTRKCMP:
	IF	STDCPM
	LD	DE,(SEKTRK)
	ENDIF
;
	IF	USRCPM
	LD	DE,(TRK16)
	ENDIF
;
	OR	A
	SBC	HL,DE
	RET
;
;	IDENTIFY WHICH DRIVE WE'RE TALKING TO, FLOPPY OR HARD
;
	IF	STDCPM
LOCREL:	LD	A,(HSTDSK)
	LD	C,A
	LD	B,0
	LD	HL,CTLTAB
	ADD	HL,BC
	LD	A,(HL)
	RET
	ENDIF
;
	IF	USRCPM
FLPHRD:	LD	A,(HSTDSK)
	SUB	'M'-'A'
FLPHR1:	AND	03		;STRIP OFF UNUSED BITS
	LD	L,A
	LD	H,0
	ADD	HL,HL		;COMPUTE OFFSET ADDRESS
	LD	DE,LDSKTB
	ADD	HL,DE		;ADDRESS REAL DISK # AND TYPE
	LD	A,(HL)
	LD	(REALDSK),A	;SAVE REAL DISK NUMBER
	INC	HL
	LD	A,(HL)
	OR	A		;RETURN Z = 1 IF FLOPPY ; Z = 0 IF HARD DISK
	RET
	ENDIF
;
;	PERFORM PHYSICAL READ
;
READHST:
	IF	STDCPM
	CALL	LOCREL
	DEC	A
	ENDIF
;
	IF	USRCPM
	CALL	FLPHRD
	ENDIF
;
	JP	FREAD
;
;	PERFORM PHYSICAL WRITE
;
WRITEHST:
	IF	STDCPM
	CALL	LOCREL
	DEC	A
	ENDIF
;
	IF	USRCPM
	CALL	FLPHRD
	ENDIF
;
	JP	FWRITE
;
; 	HERE ARE THE SECTOR READ/WRITE FUNCTIONS. WE
; 	WANT TO READ/WRITE TO DISK USING THE PARAMETERS
; 	SECTOR, TRACK, DMA ADDRESS.
;
FREAD:	LD	A,RDSCMD	; FDC READ COMMAND
	JR	RDWRSEC		; CODE COMMON TO READ AND WRITE
;
FWRITE:	LD	A,WTSCMD	; FDC WRITE COMMAND
;
RDWRSEC:
	LD	(COMAND),A
;
	LD	HL,FDTIME
	LD	(HL),0		; DISABLE MOTOR
	INC	HL
	LD	A,(HL)		; CHECK FLOPPY MOTOR
	LD	(HL),1		; INDICATE FLOPPY MOTOR IS ON
	OR	A
	LD	A,(DSELCMD)	; CURRENT DISK SELECT AND CONFIGURE
	OUT	(DSELOP),A	; NOW TURN ON MOTOR
	JR	NZ,RWCM0
;
;	WAIT	541 MS FOR MOTOR TO GET SPEED
;
	SBC	HL,HL		; SET HL = 0
RWCM2:	DEC	HL
	LD	A,H
	OR	L
	LD	A,(HL)		; DUMMY INST.
	JR	NZ,RWCM2
;
RWCM0:	
	IF	STDCPM
	LD	A,(HSTDSK)
	ENDIF
;
	IF	USRCPM
	LD	A,(REALDSK)
	ENDIF
;
	LD	HL,DRIVE	; CURRENT DRIVE
	CP	(HL)		; SAME DRIVE?
	CALL	NZ,DRVSEL	; NO. HARDWARE SELECTS DRIVE
;
RWCM1:	LD	A,(HSTSEC)	; CHECK SELECT SECTOR > PHYSICAL SEC/TRK
;
	IF	STDCPM	
	CP	FPSPT0
	ENDIF
;
	IF	USRCPM
	CP	FPSPT0+1
	ENDIF
;
	JP	C,SSIDE1	; IF NOT THEN SELECT SIDE #1
;
;	ELSE SELECT SIDE #2 OF SELETED DISK
;
	LD	A,(DSELCMD)
	AND	11110000B	; DDEN, SIDE#2, MOTOR ON
	SET	0,A		
	LD	(DSELCMD),A
	OUT	(DSELOP),A
	LD	A,(HSTSEC)
	SUB	FPSPT0		; SET TSEC=HSTSEC-PSPT0
SETSIDE:
	IF	STDCPM
	INC	A		; PHYSICAL SECTOR TRANSLATION
	ENDIF
;
	LD	(TSEC),A
	IN	A,(FDCDAT)	; CLEAR DATA REGISTER	
;
; CODE COMMON TO READ/WRITE SECTOR OPERATIONS
;
	LD	A,10		; NO. OF RETRIES
	LD	(FRETRY),A	; ON DISK I/O OPERATION
;
RDWT10:	CALL	SEEK		; SEEK THE USER DEFINED TRACK
	JP	NZ,RDWT30	; ADJUST RETRY COUNTER
;
	LD	A,(TSEC)	; SET UP FDC
	OUT	(FDCSEC),A	; SECTOR REGISTER
;
	LD	HL,HSTBUF	; SET HL = HOST BUFFER ADDRESS
	LD	C,FDCDAT	; SET C = FDC DATA REGISTER
	LD	B,0		; SET B = 256 BYTE COUNTER
	LD	A,(COMAND)	; SET UP FDC
	BIT	5,A		; DECIDE READ/WRITE OPERATION
	JR	Z,RREAD		; GO TO READ OPERATION
	LD	DE,CPUWT	; RETURN ADDRESS FOR OUTPUT
	PUSH	DE		; PUT IT ON STACK
	JP	COMMIO		; JUMP TO COMMON I/O ROUTINE
RREAD:	LD	DE,CPURD	; RETURN ADDRESS FOR INPUT
	PUSH	DE		; PUT IT ON STACK
;
COMMIO:	DI			; DISABLE SYSTEM INTERRUPT
	LD	DE,0FFFFH	; SET DE = TIMER
	OUT	(FDCCMD),A	; COMMAND READ/WRITE OPERATION
;
	LD	A,15		; WAIT FOR 60 US TO READ STATUS REGISTER
DELAY1:	DEC	A
	JR	NZ,DELAY1
;
DRQRDY:	IN	A,(FDCCMD)	; READ STATUS REGISTER
	BIT	1,A		; DRQ BIT SET?
	RET	NZ		; YES
	DEC	DE		; DECREMENT TIMER
	LD	A,D
	OR	E
	JR	Z,TERROR	; TIME-OUT(ERROR)
	IN	A,(FDCCMD)	; READ STATUS REGISTER AGAIN
	BIT	1,A		; DRQ BIT SET?
	RET	NZ		; YES
	BIT	0,A		; FDC BUSY?
	JR	NZ,DRQRDY	; YES
;
TERROR: POP	DE		; RESTORE REGISTERS
	CALL	WAITW3		; ERROR
	PUSH	AF		; SAVE ERROR CODE IN A REG
;
;
	IN	A,(FDCDAT)	; CLEAR DATA REGISTER BUFFER
	POP	AF		; RESTORE ERROR CODE IN A REG
	JP	RDWT30		; CHECK FOR RETRY
;
RDDRQ:	IN	A,(FDCCMD)	; GET FDC STATUS
	AND	02H		; DRQ BIT SET FOR NEXT BYTE?
	JR	Z,RDDRQ		; NOT YET
CPURD:	INI			; INPUT AN ASSEMBLED BYTE
	JR	NZ,RDDRQ	; ALL BYTES READ-IN?
	JR	ALDONE		; YES
;
WTDRQ:	IN	A,(FDCCMD)	; READ STATUS
	AND	02H		; DRQ BIT SET FOR NEXT BYTE?
	JR	Z,WTDRQ		; NO, WAIT
CPUWT:	OUTI			; OUPUT AN ASSEMBLED BYTE
	JR	NZ,WTDRQ	; ALL BYTES WRITE-OUT?
;
ALDONE:	IN	A,(FDCCMD)	; YES, GET FDC STATUS 
	BIT	0,A		; COMMAND COMPLETED?
	JR	NZ,ALDONE	; NO, WAIT
	AND	00011100B	; MASK OUT THE BITS
;
	LD	(ERFLAG),A	; SAVE ERROR FLAG
	AND	0CH		; ANY ERROR: CRC, DATA LOST?
	JR	NZ,RDWT30	; ERROR ON READ/WRITE
;
RDWT20:	LD	A,(ERFLAG)	; RETURN CODE
	EI			; ENABLE SYSTEM INTERRUPT
	RET			; DISK I/O SUCCESSFUL
;	
; 	WE HAD AN ERROR ON READ/WRITE OPERATION
;
RDWT30:	LD	HL,FRETRY	; MORE RETRIES LEFT?
	DEC	(HL)		; BUMP DOWN COUNT
	JR	Z,RDWT33	; EXIT IF RETRY COUNT = 0
	LD	A,(HL)
	CP	7		; CHECK RETRY COUNT = 7
	JR	Z,RDWT31	; IF YES, THEN HOME THE HEAD.
	CP	4		; CHECK RETRY COUNT = 4
	JP	NZ,RDWT10
;
RDWT31:	LD	A,FRCCMD	; FORCE FDC TO TERMINATE.
	OUT	(FDCCMD),A
	CALL	WAIT		; WAIT FOR FDC COMPLETED
	LD	A,RESCMD
	OUT	(FDCCMD),A	; THEN FORCE FDC TO HOME THE HEAD TO TRACK #0
	CALL	WAIT		; WAIT FOR FDC COMPLETED.
	JP	RDWT10		; NOW RETRY READ/WRITE OPERATION.
;	
RDWT33:	CALL	ERRPRT		; PRINT DETAILED ERROR MESSAGE
	JR	RDWT20
;
SSIDE1:	LD	A,(DSELCMD)
	SET	2,A		; SET SIDE SELECT = SIDE #1
	LD	(DSELCMD),A
	OUT	(DSELOP),A
	LD	A,(HSTSEC)	; AND (TSEC)=HSTSEC
	JP	SETSIDE
;
; 	THIS ROUTINE DOES THE ACTUAL SEEKING.
; 	TRACK NO. IS PASSED IN TRACK VARIABLE.
;
SEEK:	PUSH	HL
	IN	A,(FDCTRK)	; SAVE FDC DEVICE TRACK REG.
	LD	L,A
	LD	A,(HSTTRK)	; GET TRACK NO. BACK
	CP	L		; TEST CURRENT TRACK = SELECT TRACK
	JR	Z,SEEK0		; IF YES THEN SKIP.
	OUT	(FDCDAT),A	; OUTPUT TO DATA PORT
	LD	A,SEECMD	; THIS IS THE SEEK COMMAND
	OUT	(FDCCMD),A	; GIVE THE ORDER TO FDC
;
; 	NOW TEST FOR COMPLETION
;
	CALL	WAIT		; WAIT UNTIL NOT BUSY
	AND	SEEMSK		; AND OUT TRACK 0 BIT
	JR	Z,SEEK1		; IF NO ERROR THEN SKIP.
;
SEEK0:	POP	HL
	RET
;
SEEK1:	LD	HL,FDCDEL
SEEK2:	DEC	HL
	LD	A,H
	OR	L
	JP	NZ,SEEK2
	POP	HL
	RET
;
; 	THIS ROUTINE LOOPS UNTIL THE FDC IS
; 	IN A NON-BUSY STATE, OR UNTIL A NOT-
; 	READY CONDITION IS DETECTED.
;
WAIT:	LD	A,15		; WE MUST DELAY ABOUT
WAIT10:	DEC	A		; 60 USECS. BEFORE READING 
	JR	NZ,WAIT10	; THE STATUS.
;
WAIT20:	IN	A,(FDCCMD)	; READ THE FDC STATUS
	LD	(ERFLAG),A	; SAVE IT FOR LATER
	BIT	FDCBSY,A	; ARE WE PROCESSING A COMMAND?
	JR	NZ,WAIT20	; YES, LOOP UNTIL NOT BUSY
	LD	(UNMSTA),A	; UNMASKED STATUS (FOR DEBUG)
	AND	ERRMSK		; MASK OFF NON-ERROR BITS
	LD	(ERFLAG),A	; SAVE STATUS FOR LATER
	RET
;
; 	THIS ROUTINE LOOPS UNTIL THE FDC IS
; 	IN A NON-BUSY STATE, OR UNTIL A NOT-
; 	READY CONDITION IS DETECTED.  TIME-OUT
; 	FOR DRIVE BUSY OCCURS AT APPROX.
; 	1.25 SECONDS AFTER COMMAND INITIATION.
;
WAITWT:	LD	A,15		; WE MUST DELAY ABOUT
WAITW1:	DEC	A		; 60 USECS. BEFORE READING 
	JR	NZ,WAITW1	; THE STATUS.
;
WAITW3:	LD	(TEMPBC),BC	; SAVE REGISTERS
	LD	(TEMPDE),DE
	LD	(TEMPHL),HL
	LD	BC,0C000H	; TIME OUT FOR 1.25 SECONDS
	LD	A,0
	LD	(JMPSW),A	; RESET THE FLAG FOR MESSAGE
;
WAITW2:	IN	A,(FDCCMD)	; READ THE FDC STATUS--2.75
	LD	(ERFLAG),A	; SAVE IT FOR LATER--3.25
	LD	(ERFLAG),A	; 	
;
	PUSH	AF		; SAVE REGISTERS--2.75
	LD	A,(JMPSW)	; GET MESSAGE FLAG--3.25
	CP	0		; MESSAGE ALREADY SENT OUT?--1.75
	JR	NZ,WAIT30	; YES, SKIP--1.75
	DEC	BC		; COUNT DOWN--1.50
	LD	A,B		; --2.25
	OR	C		; TIME OUT?--1.00
	CALL	Z,SIGMSG	; YES, PRINT MESSAGE--2.50
;
WAIT40:	POP	AF		; RESTORE REGISTERS--2.50
	BIT	FDCBSY,A	; ARE WE PROCESSING A COMMAND?--2.00
	JR	NZ,WAITW2	; NO, RETURN ERROR CODE--3.00
;
	LD	(UNMSTA),A	; UNMASKED STATUS (FOR DEBUG)
	AND	ERRMSK		; MASK OFF NON-ERROR BITS
	LD	(ERFLAG),A	; SAVE STATUS FOR LATER
;
WAIT50:	LD	HL,(TEMPHL)	; RESTORE REGISTERS
	LD	DE,(TEMPDE)
	LD	BC,(TEMPBC)
	RET
;
WAIT30:	CALL	CONST		; CONSOLE STATUS
	CP	0		; AVAILABLE INPUT?
	JR	Z,WAIT40	; NO, SKIP
	CALL	CONIN		; GET CHARACTER
	CP	03H		; CONTROL-C?
	JR	NZ,WAIT40	; NO, LOOP BACK
;
	POP	AF		; RESTORE REGISTERS
	LD	HL,(TEMPHL)
	LD	DE,(TEMPDE)
	LD	BC,(TEMPBC)
;
	LD	A,FRCCMD	; FORCE TERMINATE
	OUT	(FDCCMD),A
	OUT	(FDCCMD),A
	LD	A,(HSTDSK)	; GET CURRENTLY SELECTED DRIVE
	LD	B,A
	LD	A,(CDISK)	; GET LOGGED DRIVE
	AND	0FH
	CP	B
	JR	NZ,GOWBT
	LD	A,(CDISK)
	AND	0F0H
	LD	(CDISK),A	
GOWBT:	JP	BIOS+3H		; WARM-BOOT
;
;	SELECT THE DRIVE IN HARDWARE.
;	DRIVE TO BE SELECTED IS PASSED IN REGISTER A.
;
DRVSEL:	LD	(HL),A		; CURRENT SELECT DISK = HSTDSK

	IF	TPCI
	LD	B,A
	LD	A,(NODRV)	; CHECK SINGLE DRIVE SYSTEM
	DEC	A		; IF TWO DRIVE EXIST, BRANCH TO NORMAL
	LD	A,B		; RESTORE DISK SELECTED
	JR	Z,DRSEL1	; OPERATION
	LD	HL,CURDRV	; COMPARE CURRENT DRIVE WITH PREVIOUS
	CP	(HL)		; DRIVE
	JR	Z,DRSEL1	; IF SAME BRANCH TO NORMAL OPERATION
	LD	(CURDRV),A	; OTHERWISE, UPDATE CURRENT DRIVE
	IF	STDCPM
	ADD	A,041H		; MAKE IT TO ASCII STARTING 'A'
	ENDIF
	IF	USRCPM
	ADD	A,4DH		; MAKE IT TO ASCII STARTING 'M'
	ENDIF
	LD	(DISKET),A	; SAVE IT FOR MESSAGE
	LD	HL,DRMSG	; PRINT MESSAGE
	CALL	PRNT
	CALL	CONIN	
	LD	HL,CRLF
	CALL	PRNT
	LD	A,0		; ALWAYS SELECT DISK "A"
	ENDIF

DRSEL1:	LD	B,A		; CONVERT SELECT DISK TO HARDWARE SWITCH
	INC	B		; B = 1 --> A = 1110 1111B
	LD	A,11110111B	; B = 2 --> A = 1101 1111B 
	                        ; B = 3 --> A = 1011 1111B 
				; B = 4 --> A = 0111 1111B 
SW1:	RLCA
	DJNZ	SW1
;
	AND	0F0H		; PICK-UP HIGH ORDER 4-BITS
	LD	B,A
	LD	A,(DSELCMD)	; GET DRIVE SELECT COMMAND
	AND 	0FH		; STRIP OFF CURRENT DRIVE SELECTED BIT
	OR	B		; MERGE WITH NEW DRIVE SELECT BIT
	LD	(DSELCMD),A
	OUT	(DSELOP),A	; SET HARDWARE TO SELECT NEW DRIVE
;
;	UPDATE TRACK-REG BYTE READ SECTOR ID HEADER
;
READID:	LD	A,10		; SET RETRY COUNT = 10
	LD	(FRETRY),A
;
RDID2:	LD	A,RDACMD	; SET A = FDC READ ID COMMAND
	OUT	(FDCCMD),A
	CALL	WAITWT
	IN	A,(FDCDAT)	; CLEAR DATA REGISTER IN FDC
	LD	A,(ERFLAG)
	AND	00011000B
	JR	NZ,RDID1	; IF ERROR THEN TRY AGAIN
	IN	A,(FDCSEC)	; GET TRACK ADDRESS TO A
	OUT	(FDCTRK),A	; UPDATE TRACK REG IN FDC
	RET
;
RDID1:	LD	HL,FRETRY	; MORE RETRY LEFT?
	LD	A,(HL)
	DEC	(HL)		; BUMP DOWN COUNT
	OR	A
	JR	NZ,RDID2	; TRY AGAIN IF COUNT NOT ZERO
;
	LD	A,RESCMD	; THEN FORCE FDC TO HOME HEAD TO TRACK #0
	OUT	(FDCCMD),A
	CALL	WAIT	
	LD	A,(ERFLAG)	; RETUREN WITH ERROR FLAG IN A-REG
	OR	A
	RET
;
;
; 	THIS ROUTINE PRINTS DOOR MESSAGE WHEN THERE 
; 	IS AN ACCESS TO A DRIVE WITH DOOR OPENED.
;
SIGMSG:	LD	A,1		; SET FLAG
	LD	(JMPSW),A
	LD	A,(HSTDSK)	; CURRENT DRIVE
	ADD	A,41H		; MAKE IT ASCII 
	LD	(VDRIVE),A	; SAVE FOR LATER
	LD	HL,RDYMSG	; PRINT DOOR MESSAGE
	CALL	PRNT
	RET
;
; 	THIS ROUTINE FILLS OUT THE VARIABLE VALUES FROM CURRENT
; 	STATE AND FDC STATUS CODE IN TABLE AND REPORT TO OPERATOR
;
ERRPRT:	LD	HL,MSG3		; MESSAGE HEADER
	CALL	PRNT
	LD	A,(COMAND)	; GET CURRENT COMMAND
	CP	80H		; READ COMMAND?
	JR	NZ,WRMSG	; NO, SKIP
	LD	HL,MSGRC	; READ MESSAGE
	JR	EEP
WRMSG:	LD	HL,MSGWC	; WRITE MESSAGE
EEP:	CALL	PRNT		; CURRENT COMMAND MESSAGE
	CALL	ERRHST		; GET DETAILED MESSAGE AND PRINT
	LD	HL,FSCODE	; GET STATUS TABLE ADDRESS
	LD	A,(ERFLAG)	; GET FDC STATUS CODE 
	CALL	FTAB		; CONVERT IT TO HEX AND FILL IN TABLE
	LD	HL,MSG5
	CALL	PRNT
	RET
;
; 	GENERAL PORT INITIALIZATION ROUTINE
;	ENTRY:		HL	->	PORT ADDRESS
;					BYTE COUNT
;					DATA
;					 |
;					 |
;
PRTINI:	LD	C,(HL)		; GET PORT ADDRESS
	INC	HL		
	LD	B,(HL)		; GET THE COUNT
	INC	HL		; POINT TO FIRST DATA BYTE
	OTIR			; BLOCK OUTPUT
	RET
;
; END OF TPCIFLOP.MAC


