*TITLE	'QCS/DVR - DosPLUS II RIGID I/O PACKAGE - 1.0'
;
;	THIS DRIVER IS DESIGNED FOR THE 'QCS' SERIES OF
;	OF HARD DISKS. WD1000 CONTROLLER IMPLEMENTATION.
;
;	THIS PACKAGE CONSISTS OF TEN ROUTINES:
;
;	0 -	$DCHECK - CHECK DRIVE READY
;	1 -	$DHOME	- HOME/INITIALIZE DRIVE
;	2 -	$DSEEK	- SEEK SPECIFIED ADDRESS
;	3 -	$DREAD	- READ SECTOR W/SEEK
;	4 -	$DVERF	- VERIFY SECTOR W/SEEK
;	5 -	$DWRITE - WRITE SECTOR W/SEEK
;	6 -	$SREAD	- READ SYSTEM SECTOR W/SEEK
;	7 -	$SWRITE - WRITE SYSTEM SECTOR W/SEEK
;	8 -	$DFORMT - FORMAT TRACK W/SEEK
;	9 -	$DWRTE1 - WRITE SECTOR W/AM
;
*EJECT
;
;	MISC EQUIVALENCES
;
VDLINE	EQU	9		;VIDEO LINE SVC
BINHEX	EQU	24		;BINARY/HEX CONV
ERROR	EQU	39		;POST ERROR SVC
CR	EQU	13		;ASCII C/R
;
;	DISK DRIVER CONSTANTS
;
DCT	EQU	0FEH		;DCT IDENTIFIER
ERR8	EQU	8		;DRIVE NOT READY
ER67	EQU	67		;DRIVE NOT IN SYSTEM
ER81	EQU	81		;ILLEGAL OUTPUT CHANNEL
ER97	EQU	97		;DIR UNREADABLE
;
;	SYSTEM ENTRY EQUIVALENCES
;
SVC	EQU	08H		;EXECUTE SVC
IODISK	EQU	10H		;DISK I/O MANAGER
REGSAV	EQU	0303H		;REGISTER SAVER
DDIVD	EQU	0315H		;8 X 8 DIVIDE
FETMEM	EQU	0330H		;FETCH MEMORY
;
;	HARDWARE ADDRESSING
;
HD#D	EQU	50H		;HDC DATA REGISTER
HD#E	EQU	51H		;HDC ERROR REGISTER
HD#W	EQU	51H		;HDC WRITE PRECOMP
HD#X	EQU	52H		;HDC SECTOR COUNT
HD#S	EQU	53H		;HDC SECTOR REGISTER
HD#L	EQU	54H		;HDC CYLINDER LOW
HD#H	EQU	55H		;HDC CYLINDER HIGH
HD#Q	EQU	56H		;HDC SIZE/HEAD/DRIVE
HD#C	EQU	57H		;HDC COMMAND/STATUS
;
;	WD-1000 COMMANDS
;
HREST	EQU	00010000B	;RESTORE & INIT
HREAD	EQU	00100000B	;READ SECTOR
HWRTE	EQU	00110000B	;WRITE SECTOR
HFRMT	EQU	01010000B	;FORMAT TRACK
HSEEK	EQU	01110000B	;SEEK ADDRESS
;
*EJECT
;	INITIALIZATION CODE SEQUENCE
;
;	ENT	BC =>	LOW$
;		DE =>	HIGH$
;		HL =>	COMMAND LINE
;		IX =>	DCT
;
	ORG	2800H
;
START	LD	HL,TITLE	;'DRIVER TITLE'
	LD	BC,TL<8+CR	;LEN+TERM
	LD	A,VDLINE	;VIDEO LINE
	RST	SVC
	JP	NZ,POSTE	;IF ERROR
;
	BIT	6,(IX+0)	;DCT?
	LD	A,ER81		;ILLEGAL I/O CHANNEL
	JP	Z,POSTE 	;IF NOT DCT
;
;	FETCH MEMORY FROM DOS
;
	LD	BC,ENDDVR-BEGDVR
	CALL	FETMEM		;FETCH MEMORY
	JP	NZ,POSTE	;IF ERROR
	PUSH	HL		;SAVE => START FREE
	LD	BC,BEGDVR
	OR	A
	SBC	HL,BC		;HL = OFFSET
	LD	C,L
	LD	B,H		;BC = OFFSET
;
;	ADJUST DRIVER ADDRESSES
;
	LD	IY,TABLE	;IY => ADD TABLE
	LD	A,27		;ENTRY COUNT
ADJ1	LD	L,(IY+0)
	LD	H,(IY+1)	;HL => ADD LOCATION
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		;DE = ADDRESS
	EX	DE,HL		;HL = ADDRESS
	ADD	HL,BC		;HL = NEW ADDRESS
	EX	DE,HL		;DE = NEW ADDRESS
	LD	(HL),D
	DEC	HL
	LD	(HL),E		;SET NEW ADD
	LD	DE,2		;OFFSEY
	ADD	IY,DE		;IY => NEXT ADD
	DEC	A		;DONE?
	JR	NZ,ADJ1 	;IF NOT
	POP	DE		;DE => START FREE
;
;	INSTALL DRIVER HERE
;
	LD	(IX+1),E	;ADD TO DCT
	LD	(IX+2),D	;ADD TO DCT
	LD	HL,BEGDVR	;HL => DRIVER
	LD	BC,ENDDVR-BEGDVR
	LDIR			;MOVE!
	SET	7,(IX+5)	;5"
	SET	5,(IX+5)	;HD
	RES	1,(IX+5)	;FIXED
	SET	0,(IX+5)	;LOG IT!
	RES	3,(IX+0)	;DEVICE ACTIVE
;
	LD	HL,MES1+20	;'0000H'
	LD	E,(IX+1)
	LD	D,(IX+2)	;DE => DRIVER
	LD	B,0		;BIN TO HEX
	LD	A,BINHEX	;SVC ROUTINE
	RST	SVC
	LD	HL,MES1 	;'DRIVER INSTALLED'
	LD	BC,M1L<8+CR	;LEN+TERM
	LD	A,VDLINE	;VIDEO LINE
	RST	SVC
	RET	Z		;NO ERRORS
;
;	POST ERROR MESSAGE
;
POSTE	AND	7FH		;NO ABORT
	LD	B,A		;B = CODE
	LD	A,ERROR 	;POST ERROR
	RST	SVC
	XOR	A		;Z STATUS
	RET
;
*EJECT
;	TABLE OF NON-RELOCATABLE ADDRESSES
;
TABLE	DEFW	RL1
	DEFW	RL2
	DEFW	RL3
	DEFW	RL4
	DEFW	RL5
	DEFW	RL6
	DEFW	RL7
	DEFW	RL8
	DEFW	RL9
	DEFW	RL10
	DEFW	RL11
	DEFW	RL12
	DEFW	RL13
	DEFW	RL14
	DEFW	RL15
	DEFW	RL16
	DEFW	RL17
	DEFW	RL18
	DEFW	RL19
	DEFW	RL20
	DEFW	RL21
	DEFW	RL22
	DEFW	RL23
	DEFW	RL24
	DEFW	RL25
	DEFW	RL26
	DEFW	RL27
;
;	MESSAGES AND TEXT STRINGS
;
TITLE	DEFB	CR
	DEFM	'QCS/DVR - DosPLUS II Rigid Disk Driver - Series A.00'
	DEFB	CR
	DEFM	'(c)(p) Copyright 1982 by MicroPower, Inc.'
	DEFB	CR
TL	EQU	$-TITLE
;
MES1	DEFM	'Driver installed at 0000H'
M1L	EQU	$-MES1
;
*EJECT
;	FLOPPY/HARD DRIVER ENTRY
;
BEGDVR	EQU	$
HCODE	BIT	5,(IY+5)	;HARD?
	JR	NZ,HCODE1	;IF YES
	LD	A,ER67		;DRIVE NOT IN SYSTEM
	OR	A		;NZ STATUS
	RET
;
HCODE1	PUSH	HL		;SAVE BUFFER
	LD	HL,DTABLE	;ROUTINE TABLE
RL17	EQU	$-2
	ADD	A,A		;* 2
	ADD	A,L
	LD	L,A		;HL => ENTRY
	JR	NC,$+3
	INC	H
	LD	A,(HL)		;LSB
	INC	HL		;NEXT
	LD	H,(HL)		;MSB
	LD	L,A		;HL => ROUTINE
	EX	(SP),HL 	;RESTORE BUFFER
	RET			;GO!
;
DTABLE	DEFW	DCHECK		;CHECK DRIVE READY
RL18	EQU	$-2
	DEFW	DHOME		;HOME/INIT DRIVE
RL19	EQU	$-2
	DEFW	DSEEK		;SEEK ADDRESS
RL20	EQU	$-2
	DEFW	DREAD		;READ SECTOR
RL21	EQU	$-2
	DEFW	DVERF		;VERIFY SECTOR
RL22	EQU	$-2
	DEFW	DWRITE		;WRITE SECTOR
RL23	EQU	$-2
	DEFW	SREAD		;READ SYSTEM SECTOR
RL24	EQU	$-2
	DEFW	SWRITE		;WRITE SYSTEM SECTOR
RL25	EQU	$-2
	DEFW	DFORM		;FORMAT TRACK
RL26	EQU	$-2
	DEFW	DWRIT1		;WRITE SECTOR W/SYS
RL27	EQU	$-2
;
*EJECT
;	$DCHECK - CHECK DRIVE READY
;
;	ENT	IY =>	DCT
;
;	EXT	Z SET IF DRIVE READY
;		C SET IF WRITE PROT
;
DCHECK	LD	B,0
	LD	E,B		;BE = SECTOR
	CALL	UPTASK		;UPDATE TASK FILE
RL1	EQU	$-2
	IN	A,(HD#C)	;GET STATUS
	CPL			;INVERT
	AND	40H		;READY?
	RET	NZ		;NOT AVAIL
	LD	A,(IY+5)	;GET SOFT WP
	RLCA			;WP TO 7
	AND	80H		;WP ONLY
	ADD	A,A		;WP TO CRY
	RET
;
*EJECT
;	$SREAD - READ SYSTEM SECTOR
;
;	ENT	 E =	SECTOR (0-3 IF DCT ASSUMED)
;		IY =>	DCT (ASSUMED)
;		HL =>	I/O BUFFER
;
;	EXT	IY =>	 CORRECT DCT
;
SREAD	BIT	0,(IY+5)	;LOG DISK?
	CALL	NZ,SREAD3	;IF YES
RL2	EQU	$-2
	RET	NZ		;IF ERROR
	LD	D,(IY+18)	;DIR CYLINDER
	LD	A,3		;DREAD
	RST	IODISK		;DO IT!
	RET
;
SREAD3	CALL	REGSAV		;SAVE REGISTERS
	LD	DE,0002H	;CYL,SEC
	LD	B,D		;BE =SECTOR
	CALL	DHOME		;HOME DRIVE
RL3	EQU	$-2
	LD	A,3		;DVERF
	RST	IODISK		;DO IT!
	RET	NZ		;IF ERROR
;
	LD	A,(HL)		;GET 1ST CHAR
	CP	'D'             ;'DCT' ?
	LD	A,ER97		;UNKNOWN DISK
	RET	NZ		;IF ERROR
	RES	0,(IY+5)	;DISK LOGGED
	INC	HL		;NEXT
	INC	HL
	INC	HL		;HL => DATA
	LD	DE,13		;OFFSET
	ADD	IY,DE		;IY => DCT PERISH
	PUSH	IY
	POP	DE		;DE => DCT PERISH
	LD	BC,10		;COUNT
	LDIR			;MOVE!
	XOR	A		;NO ERROR
	RET
;
*EJECT
;	$DHOME - SEEK TRACK 0
;
;	ENT	IY =>	DCT
;
DHOME	LD	A,HREST+3	;STEP
	CALL	HFCNW		;HOME
RL4	EQU	$-2
	LD	A,(IY+6)	;GET STEP
	OR	HREST		;RESTORE CMD
	CALL	HFCNW		;ISSUE CMD
RL5	EQU	$-2
	LD	(IY+11),0	;HEAD AT 0
	RET
;
*EJECT
;	$DSEEK - DISK SEEK FUNCTION
;
;	ENT	DE =	CYL,SEC
;		IY =>	DCT
;
DSEEK	LD	A,(IY+6)	;STEP RATE
	OR	HSEEK		;SEEK COMMAND
	CALL	HFCNW		;ISSUE CMD
RL6	EQU	$-2
	LD	(IY+11),D	;SET CYL
	RET			;SEEK COMMAND
;
*EJECT
;	READ SECTOR ROUTINES
;
;	ENT	 B =	LSN HIGH
;		 E =	LSN LOW
;		 D =	CYLINDER
;		HL =>	I/O BUFFER
;
DREAD	CALL	DIOP
RL7	EQU	$-2
	DEFB	HREAD		;HDC READ
	DEFB	5		;RETRY COUNT
	DEFB	100		;ERROR OFFSET
	DEFB	1		;I/O TYPE
;
DVERF	CALL	DIOP
RL8	EQU	$-2
	DEFB	HREAD		;HDC READ
	DEFB	2		;RETRY COUNT
	DEFB	100		;ERROR OFFSET
	DEFB	1		;I/O TYPE
;
;	WRITE SECTOR ROUTINES
;
;	ENT	 B =	LSN HIGH
;		 E =	LSN LOW
;		 D =	CYLINDER
;		HL =>	I/O BUFFER
;
DWRITE	CALL	DIOP
RL9	EQU	$-2
	DEFB	HWRTE		;HDC WRITE
	DEFB	5		;RETRY COUNT
	DEFB	108		;ERROR OFFSET
	DEFB	2		;I/O TYPE
;
SWRITE	LD	D,(IY+18)	;DIR TRACK
DWRIT1	CALL	DIOP
RL10	EQU	$-2
	DEFB	HWRTE		;HDC WRITE
	DEFB	5		;RETRY COUNT
	DEFB	108		;ERROR OFFSET
	DEFB	2		;I/O TYPE
;
;	FORMAT TRACK ROUTINE
;
;	ENT	 B =	LSN HIGH
;		 E =	LSN LOW
;		 D =	CYLINDER
;		HL =>	I/O BUFFER
;
DFORM	CALL	DIOP
RL11	EQU	$-2
	DEFB	HFRMT		;HDC FORMAT
	DEFB	1		;RETRY COUNT
	DEFB	108		;ERROR OFFSET
	DEFB	4		;I/O TYPE
;
*EJECT
;	DISK I/O OPERATION
;
;	ENT	 B =	LSN HIGH
;		 E =	LSN LOW
;		 D =	CYLINDER
;		HL =>	I/O BUFFER
;		IY =>	DRIVE CONTROL TABLE
;		SP =>	FDC FUNCTION
;		SP+1 => RETRY COUNT
;		SP+2 => ERROR CODE OFFSET
;		SP+3 => XFER OPCODE
;
DIOP	POP	IX		;IX => INFO
	BIT	0,(IX+3)	;INPUT?
	JR	NZ,$+9		;IF YES
	LD	A,(IY+5)	;GET FLAGS
	AND	40H		;WP?
	JR	NZ,DIOERR	;ERROR!
;
;	WINCHESTER I/O ROUTINE
;
DIOP1	CALL	UPTASK		;UPDATE TASK FILE
RL12	EQU	$-2
	LD	BC,0<8+HD#D	;COUNT & DATA REG
	LD	A,(IX+0)	;HDC COMMAND
	OUT	(HD#C),A	;ISSUE CMD
	BIT	0,(IX+3)	;INPUT?
	JR	NZ,DIOP2	;IF YES
;
;	HD OUTPUT OPERATION
;
	OTIR			;WRITE DATA
	CALL	HBUSY		;WAIT TIL READY
RL13	EQU	$-2
	JR	DIOP3		;ALL DONE!
;
;	HD INPUT OPERATION
;
DIOP2	CALL	HBUSY		;WAIT TIL READY
RL14	EQU	$-2
	INIR			;READ DATA
;
;	GET HD ERROR STATUS
;
DIOP3	IN	A,(HD#E)	;GET ERROR CODE
	LD	B,A		;B = CODE
	IN	A,(HD#C)	;GET STATUS
	AND	1		;ERROR?
	RET	Z		;IF NOT
;
DIOERR	LD	A,(IX+2)	;ERROR OFFSET
DIOER1	RRC	B		;BIT TO CRY
	RET	C		;ANY?
	INC	A		;ERROR CODE
	JR	DIOER1		;TIL FOUND
;
*EJECT
;	UPDATE HARD DISK TASK FILE
;
;	ENT	 B =	LSN HIGH
;		 E =	LSN LOW
;		 D =	CYLINDER NUMBER
;		IY =>	DCT
;
UPTASK	PUSH	HL		;SAVE
	LD	A,(IY+8)	;CYL OFFSET
	ADD	A,D		;CYLINDER
	OUT	(HD#L),A	;SET CYL LOW
	LD	A,(IY+9)	;CYL OFFSET
	ADC	A,0		;MSB
	OUT	(HD#H),A	;SET CYL HIGH
	LD	H,B
	LD	L,E		;HL = SECTOR
	LD	A,(IY+16)	;SEC/TRACK
	CALL	DDIVD		;GET SRFCE,SEC
	ADD	A,(IY+10)	;SECTOR OFFSET
	OUT	(HD#S),A	;SET SECTOR
	LD	A,(IY+12)	;BINARY DRIVE
	RLCA
	RLCA
	RLCA			;TO BITS 3-5
	ADD	A,(IY+7)	;HEAD OFFSET
	ADD	A,L		;+ HEAD
	OUT	(HD#Q),A	;SET SIZE/DRIVE/HEAD
	POP	HL		;RESTORE
	RET
;
;	ISSUE HD FUNCTION & WAIT
;
;	ENT	 A =	DISK FUNCTION
;		IY =>	DCT
;
HFCNW	PUSH	AF		;SAVE CMD
	CALL	UPTASK		;UPDATE TASK FILE
RL15	EQU	$-2
	POP	AF		;GET CMD
	OUT	(HD#C),A	;ISSUE CMD
	CALL	HBUSY		;WAIT TIL READY
RL16	EQU	$-2
	IN	A,(HD#C)	;GET STATUS
	AND	1		;ERROR?
	RET	Z		;IF NOT
	IN	A,(HD#E)	;A = ERROR
	RET
;
;	WAIT FOR HDC READY
;
HBUSY	IN	A,(HD#C)	;HDC STATUS
	RLCA			;BUSY?
	JR	C,HBUSY 	;WAIT
	RET
ENDDVR	EQU	$
;
	END	START
 