;********************************************************
;*							*
;*	DISK INPUT/OUTPUT DRIVER SUBROUTINE PACKAGE	*
;*	FOR WESTERN DIGITAL 1771 DISK CONTROLLER	*
;*							*
;*	bullet-proof error recovery added 12-APR-80	*
;*							*
;********************************************************
;
;
;	EQUATES FOR DISK CONTROLLER PORTS AND COMMAND CODES
;
STSREG	EQU	WD1771+0	;STATUS REGISTER
CMDREG	EQU	WD1771+0	;COMMAND REGISTER
TRKREG	EQU	WD1771+1	;TRACK REGISTER
SECREG	EQU	WD1771+2	;SECTOR REGISTER
DATREG	EQU	WD1771+3	;DATA REGISTER
;
RDCMD	EQU	10001000B	;READ COMMAND
WRTCMD	EQU	10101000B	;WRITE COMMAND
SKCMD	EQU	00011100B	;SEEK COMMAND
FINCMD	EQU	11010000B	;FORCE INTR COMMAND
RSTCMD	EQU	00001100B	;RESTORE COMMAND
HLOAD	EQU	00000100B	;RD/WRT HEAD LOAD ENABLE
;
RET	EQU	0C9H		;SUBROUTINE RETURN INSTR OPCODE
NMIVEC	EQU	0066H		;THE NON-MASKABLE INTERRUPT IS
				;USED FOR DATA SYNCRONIZATION BETWEEN
				;THE Z-80 AND 1771 DISK CONTROLLER
;
;
;
SELECT:	LD	A,C		;GET UNIT# PASSED IN C AND
	CP	4		; CHECK FOR MAXIMUM VALID#
	RET	NC		;ERROR IF NUMBER > 3
	CALL	TURNON		;MAKE SURE DISKS ARE TURNED ON
	IN	A,(BITDAT)
	LD	B,A		;SAVE CURRENT DRIVE SELECT DATA
	AND	11111000B	;MERGE IN NEW DRIVE UNIT# IN C
	OR	C		; IN PLACE OF THE CURRENT ONE
	OUT	(BITDAT),A	; TO SELECT THE NEW DISK DRIVE
	CALL	FORCE		;TEST NEW DRIVE'S READY STATUS
	JR	Z,SEL2-$	; AND CONTINUE IF ITS READY
	LD	A,B
	OUT	(BITDAT),A	;ELSE PUT BACK OLD DRIVE SELECT DATA
	LD	A,10000000B	; AND RETURN DRIVE-NOT-READY STATUS
	RET

SEL2:	LD	HL,UNIT		;POINT HL TO DRIVE SELECT DATA
	LD	A,(HL)		;LOAD A WITH CURRENT UNIT#
	LD	(HL),C		; AND STORE NEW UNIT# FROM C
	CP	255		;TEST IF NO DRIVE HAS BEEN SELECTED
	JR	Z,SEL3-$	; YET AND SKIP NEXT SEGMENT IF SO
	INC	HL		;POINT TO HEAD POSITION TABLE
	ADD	A,L		; AND ADD IN NEW UNIT# AS INDEX
	LD	L,A
	IN	A,(TRKREG)	;GET CURRENT HEAD POSITION
	LD	(HL),A		; AND STORE IN TABLE @ HL
SEL3:	LD	HL,TRKTAB
	LD	A,L
	ADD	A,C		;INDEX INTO TABLE TO GET
	LD	L,A		; HEAD POSITION OF NEW DRIVE
	LD	A,(HL)
	CP	255		;TEST IF NEW DRIVE HAS EVER BEEN
	JR	Z,HOME-$	; SELECTED AND DO A HOME IF NOT
	OUT	(TRKREG),A	;OUTPUT THE DRIVE'S CURRENT HEAD
	XOR	A		; POSITION TO THE TRACK REGISTER
	RET
;
;
;
HOME:	CALL	READY		;CLEAR DISK CONTROLLER
	RET	NZ		;EXIT IF DRIVE NOT READY
	XOR	A
	LD	(TRACK),A	;SET TRACK# IN MEM TO ZERO
RESTOR:	LD	B,RSTCMD	;LOAD B WITH A RESTORE COMMAND
	CALL	STEP		;EXECUTE HEAD MOVING OPERATION
	XOR	00000100B	;GET TRUE TRACK 0 STATUS
	AND	10011100B	;MASK TO ERROR BITS
	RET			;RETURN 1771 STATUS IN A
;
;
;
SEEK:	CALL	READY		;CLEAR DISK CONTROLLER
	RET	NZ		;EXIT IF DRIVE NOT READY
	LD	A,C		;GET TRACK# DATA FROM C AND
	CP	77		; CHECK FOR MAXIMUM VALID#
	RET	NC		;FORGET IT IF TRACK# > 76
	LD	(TRACK),A	; ELSE STORE TRACK# FOR SEEK
	OUT	(DATREG),A	;OUTPUT TRACK # TO 1771
	LD	B,SKCMD		;LOAD B WITH A SEEK COMMAND AND
	CALL	STEP		; GO SEEK WITH PROPER STEP RATE
	AND	10011000B	;MASK TO READY,SEEK AND CRC ERROR
	RET	Z		; BITS AND RETURN IF ALL GOOD

	CALL	RESTOR		;ELSE TRY TO RE-CAILBRATE HEAD
	RET	NZ		;ERROR IF WE CAN'T FIND TRACK 0
	LD	A,C
	OUT	(DATREG),A	;OUTPUT TRACK# TO 1771
	LD	B,SKCMD
	CALL	STEP		;TRY TO SEEK THE TRACK AGAIN
	AND	10011000B	
	RET			;RETURN FINAL SEEK STATUS IN A
;
;
;
WRITE:	CALL	READY		;CLEAR THE DISK CONTROLLER
	RET	NZ		;EXIT IF DRIVE NOT READY
	BIT	6,A
	RET	NZ		;EXIT IF DISK IS WRITE-PROTECTED
	LD	B,WRTCMD
	JR	RDWRT-$

READ:	CALL	READY		;CLEAR DISK CONTROLLER
	RET	NZ		;EXIT IF DRIVE NOT READY
	LD	B,RDCMD
RDWRT:	LD	(IOPTR),HL	;STORE DISK I/O DATA POINTER
	LD	HL,SECTOR
	LD	(HL),C		;STORE SECTOR# FOR READ/WRITE
	INC	HL
	LD	(HL),B		;SAVE READ/WRITE COMMAND BYTE
	INC	HL
	LD	(HL),2		;SET DISK OPERATION RE-TRY COUNT
RW1:	DI			;NO INTERRUPTS DURING DISK I/O
	LD	HL,NMIVEC	;SAVE BYTE AT NMI VECTOR LOCATION
	LD	D,(HL)		; IN D FOR DURATION OF READ/WRITE
	LD	(HL),RET	; LOOP AND REPLACE IT WITH A RET
	LD	HL,RECLEN
	LD	B,(HL)		;B=NUMBER OF BYTES/SECTOR
	LD	C,DATREG	;C=1771 DATA REGISTER PORT#
	LD	HL,(IOPTR)	;HL=DISK READ/WRITE DATA POINTER
	LD	A,(SECTOR)	;GET SECTOR NUMBER
	OUT	(SECREG),A	;OUTPUT SECTOR# TO 1771
	CALL	FORCE		;ISSUE A FORCE INTERRUPT COMMAND
	BIT	5,A		; TO TEST CURRENT HEAD LOAD STATUS
	LD	A,(CMDTYP)	;GET READ OR WRITE COMMAND BYTE
	JR	NZ,RW2-$	;JUMP IF HEAD IS ALREADY LOADED
	OR	HLOAD		; ELSE MERGE IN HLD BIT
RW2:	CALL	CMDOUT		;START THE 1771 DOING IT'S THING
	BIT	5,A		;TEST IF COMMAND IS A READ OR WRITE
	JR	NZ,WLOOP-$	; AND JUMP TO THE CORRECT LOOP
RLOOP:	HALT
	INI		
	JP	NZ,RLOOP
	CALL	BUSY		;LOOP UNTIL 1771 COMES UN-BUSY
	AND	10011100B	;MASK OFF TO READY, NOT FOUND, CRC
	JR	RW3-$		; AND LOST DATA STATUS BITS

WLOOP:	HALT
	OUTI
	JP	NZ,WLOOP
	CALL	BUSY
	AND	10111100B	;MASK OFF AS ABOVE PLUS WRITE FAULT
RW3:	LD	HL,NMIVEC
	LD	(HL),D		;RESTORE BYTE @ NMI VECTOR
	EI
	RET	Z		;RETURN IF NO DISK I/O ERRORS
	LD	HL,RETRY
	DEC	(HL)		;DECREMENT RE-TRY COUNT AND
	JR	NZ,RW4-$	; EXECUTE COMAND AGAIN IF NOT=0
	OR	A
	RET			;ELSE RETURN 1771 ERROR STATUS

RW4:	LD	HL,TRACK
	LD	C,(HL)		;GET TRACK# FOR CURRENT OPERATION
	CALL	SEEK		;TRY TO RE-CAILBRATE THE HEAD
	JR	RW1-$		; BEFORE READING OR WRITING AGAIN
;
;
;
STEP:	LD	A,(SPEED)	;GET STEP SPEED VARIABLE
	AND	00000011B
	OR	B		;MERGE WITH SEEK/HOME COMMAND IN B
	CALL	CMDOUT		;OUTPUT COMMAND AND DELAY
BUSY:	IN	A,(STSREG)
	BIT	0,A		;TEST BUSY BIT FROM
	JR	NZ,BUSY-$	; 1771 AND LOOP TILL=0
	RET
;
;
;
CMDOUT:	OUT	(CMDREG),A	;OUTPUT A COMMAND TO THE 1771
	CALL	PAUSE		;WASTE 44 MICROSECONDS
PAUSE:	EX	(SP),HL
	EX	(SP),HL
	RET
;
;
;
READY:	CALL	TURNON		;KEEP THOSE DISKS SPINING FOLKS
FORCE:	LD	A,FINCMD	;ISSUE A FORCE INTERRUPT COMMAND
	CALL	CMDOUT	
	IN	A,(STSREG)	;READ STATUS REGISTER CONTENTS
	BIT	7,A		;TEST DRIVE NOT READY BIT
	RET
;
;
;
TURNON:	LD	A,30
	LD	(MOTOR),A	;RE-LOAD THE MOTOR TURN-OFF TIMER
	CALL	PAUSE
	IN	A,(BITDAT)
	BIT	2,A		;TEST IF MOTORS HAVE BEEN STOPPED
	RET	Z		; AND EXIT IF STILL TURNED ON
	AND	10111011B	;ELSE RE-ENABLE THE DRIVE SELECTS
	OUT	(BITDAT),A	; AND ACTIVATE THE MOTOR RELAY
	PUSH	BC
	LD	B,0		;SET READY LOOP MAX TIMEOUT
TURN2:	CALL	WAIT		;WAIT 1/93 SECOND AND TEST READY
	JR	Z,TURN3-$	;EXIT LOOP IF DRIVE READY
	DJNZ	TURN2-$		; ELSE TRY AGAIN UP TO 256 TIMES
TURN3:	LD	B,9
TURN4:	CALL	WAIT		;GIVE ABOUT 1/10 SECOND MORE DELAY
	DJNZ	TURN4-$
	POP	BC
	RET
;
;
WAIT:	IN	A,(CTC3)	;GET CURRENT CTC3 COUNT VALUE
	LD	C,A
WAIT2:	IN	A,(CTC3)
	CP	C		;TEST IF CTC3 HAS CHANGED BY 1 COUNT
	JR	Z,WAIT2-$	; AND LOOP UNTIL IT CHANGES
	JR	FORCE-$		; THEN GO TEST DRIVE READY STATUS
;
;
;
;
