; HDWD1/ASM - Western Digital driver 1000/1010 - 06/10/84
	TITLE	'<Winchester Driver & Initializer>'
	SUBTTL	'<General WD controller driver>'
	ORG	3200H
;*=*=*
;       Version equates
;*=*=*
LF	EQU	10
CR	EQU	13
MINTRK	EQU	3
MAXTRK	EQU	404
*GET	SVCMAC
;
;*=*=*
;       Change Log
;Log in drive if possible 04/26/83
;Store total head count for drive 04/29/83
;
HARDWP	EQU	0C0H
HDCONT	EQU	0C1H
;*=*=*
;       Calling Sequence
;        B  => disk command
;        C  => logical drive number
;        D  => logical cylinder number
;        E  => logical sector number
;        HL => sector I/O buffer address
;        IY => drive code table address
;*=*=*
;       bit 2 => wait enable
;       bit 3 => device enable
;       bit 4 => software reset
;*=*=*
DATA	EQU	0C8H		;Data transfer port
ERROR	EQU	DATA+1		;Error code port
WRP	EQU	DATA+1		;Write precompensation port
SECNT	EQU	DATA+2		;Sector count
SECNO	EQU	DATA+3		;Sector number port
CYLLO	EQU	DATA+4		;Cylinder lo
CYLHI	EQU	DATA+5		;Cylinder hi
SDH	EQU	DATA+6		;Size/Drive/Head port
STATUS	EQU	DATA+7		;Status port
COMMAND	EQU	DATA+7		;Command port
;*=*=*
;       Western Digital Controller OP codes
;
; 0001rrrr - Restore drive
; 0111rrrr - Seek sector/head/cyl
; 0010d000 - Read sector
; 00110000 - Write sector
; 01010000 - Format track
;
;       rrrr = step rate
;       d    = 0=programmed I/O, 1=DMA
;*=*=*
;       Winchester drive code tables
;*=*=*
DCTAB	EQU	$
	DB	0,0,0		;Push to correct posn
TYPQTY	EQU	1
DRVSEL	EQU	3		;DCT pos for select
;*=*=*
;       Defaults for ST-506
;*=*=*
	DB	8!4		;5" fixed hard drive
	DB	10H		;Alien, starting head = 0
	DB	0		;<=keep head count here
	DB	153-1		;Max cylinder
	DB	3<5+31		;4 heads & 32 sectors/tk
	DB	0<5+15		;Y "grans/tk" 16 sec/gran
	DB	76		;Directory cyl
;*=*=*
;       start of driver
;*=*=*
DISK	JR	START		;Branch around linkage
	DW	$-$		;Last byte used
	DB	4,'$HD1'	;Progname
;
RETRYR	LD	HL,0<8!79
	LD	BC,2<8!'H'
	@@VDCTL
;Init done by ICONFG
	IFDEF	HDCONT
INIT	LD	A,10H		;Software reset
	OUT	(HDCONT),A
	LD	B,40H		;Wait about .25 sec.
	@@PAUSE
	LD	A,0CH		;Set device & wait enable
	OUT	(HDCONT),A
	EX	(SP),HL
	EX	(SP),HL
	IN	A,(STATUS)
	AND	80H		;Check for ready status
	JR	NZ,RETRYR	;Try again if no ready
;*=*=*
;       Restore the WDC to set the step rate
;*=*=*
	ELSE
INIT	XOR	A
	ENDIF
;
	OUT	(SDH),A		;  set the step rate
	LD	A,16H		;* set 3ms-restore
STP1	EQU	$-1		;+set by user entry if >3
	OUT	(COMMAND),A	;* execute command
	CALL	DWR1		;Wait until not busy
R1	EQU	$-2
	XOR	A
	OUT	(CYLHI),A
	OUT	(CYLLO),A	;Reset for WD1010
	LD	A,1<4!0		;Restore w/step rate
STP2	EQU	$-1		;+set by user entry
	OUT	(COMMAND),A
	LD	HL,0<8!79
	LD	BC,2<8!' '	;Blank out 'H'
	@@VDCTL
	LD	B,50		;Delay about .32 sec.
	@@PAUSE
	CALL	DWR2		;Wait for completion
R2	EQU	$-2
	JR	NZ,RETRYR	;Retry if error
PCPTRK	LD	A,00
	OUT	(ERROR),A	;Set precomp trk# / 4
LINK	RET			;End call before install
	DB	'WD'		;2 more bytes for @ICNFG
;
;*=*=*
;     Driver start
;*=*=*
START	BIT	3,B
	JR	NZ,DIO		;Jump if I/O request
; treat any other request as TSTBSY
	IFDEF	HARDWP
TSTBSY	LD	A,(IY+3)	;P/u drive address
	AND	3		;  & remove other junk
	LD	C,A		;Save for counter
	IN	A,(HARDWP)	;P/u the front panel WP
	DB	0DCH		;Ignore next 2 by CALL C,
ALIGN	RLCA			;Align hard WP bit to b7
	DEC	C		;  according to drive
	JR	NZ,ALIGN	;  address
	OR	(IY+3)		;Merge software WP
	ELSE
;
TSTBSY	LD	A,(IY+3)	;Just dct bit
	ENDIF
;
	AND	10000000B	;Mask all but bit 7
	SLA	A		;Write prot to carry
	RET	NC		;W/0 in a
	LD	A,01000000B	;Show floppy type WP
	RET			;W/Z set
;*=*=*
;       Disk I/O requested
;*=*=*
DIO	PUSH	HL
	PUSH	DE
	BIT	2,B		;Test input or output
	JR	Z,INPUT		;Go if read/verify
	CALL	TSTBSY		;If Write, check first
R3	EQU	$-2
	LD	A,15		;Write prot error
	JR	C,POP2		;Ret w/error
	LD	A,B		;Get fcn
	SUB	13		;WRSEC?
	JR	Z,WRSEC
	DEC	A		;Or WRSYS
	JR	Z,WRSEC		;Go on output
	LD	A,8		;Not available..for format
	JR	POP2		;Set NZ/ret w/error
;*=*=*
;       Sector read routine
;*=*=*
INPUT	PUSH	BC		;Save fcn #
	CALL	SETUP		;Establish drive
R6	EQU	$-2
	LD	A,20H		;Set controller OP code
	OUT	(COMMAND),A	;  & send to WDC
	CALL	DWR1		;Delay/wait for completion
R7	EQU	$-2
	POP	DE		;Fcn to D
	PUSH	AF		;Save status fm disk
	BIT	1,D		;Sector verify?
	JR	Z,READ		;Go if read
VRLP	IN	E,(C)
	DJNZ	VRLP
	DB	11H		;Skip next via LD DE,
READ	INIR			;Get data
	POP	AF		;Recover completion status
	LD	HL,RDTBL	;Error xlate
R8	EQU	$-2
	JR	NZ,ENDERR	;Go on error
	POP	DE		;Adjust read error
	LD	A,D		;  code if system
	CP	(IY+9)		;  sector was read
	LD	A,6
	JR	Z,POP1		;Go if system
	XOR	A
	JR	POP1
;*=*=*
;       sector WRITE routine
;*=*=*
;
WRSEC	CALL	SETUP		;Establish drive linkage
R9	EQU	$-2
;*=*=*
;       Routine to pass WRITE Command to WDC & do it
;*=*=*
	LD	A,30H		;Init write command
	OUT	(COMMAND),A	;  & pass to WDC
	OTIR			;Output the data
	LD	HL,WRTBL	;Point to error xlate
R10	EQU	$-2
	CALL	DWR1		;Wait for completion
R11	EQU	$-2
	JR	Z,EXIT
;
ENDERR	CALL	WD1010		;Reset controller if needed
R12	EQU	$-2
	IN	A,(ERROR)	;Grab error code
ERR1	RLCA			;Find error TYPE
	INC	HL		;Bump counter
	JR	NC,ERR1		;Loop for error byte
	LD	A,(HL)		;P/u error code
;
EXIT	EQU	$		;Restore registers
POP2	POP	DE
POP1	POP	HL
	OR	A		;Set flag
;This is to take care of the WD1010 controller CIP bit
WD1010	PUSH	AF
	IN	A,(STATUS)	;
	AND	00000010B	;Bit 1 is CIP for 1010
	JR	Z,CLEAN		;All OK
	LD	A,10H		;Do a software reset
	OUT	(HDCONT),A
	LD	A,0CH		;Then re-enable
	OUT	(HDCONT),A
CLEAN	POP	AF		;Continue
	RET
;*=*=*
;       LDOS error conversion table
; Bit  Error            RD   WR
;  7  Bad block detect   7   14
;  6  CRC - Data field   4   12
;  5  CRC - ID field     1    9
;  4  ID not found       5   13
;  3  Not used          127 127
;  2  Aborted command    8    8
;  1  TR000 error        2   10
;  0  DAM not found      3   11
;*=*=*
RDTBL	EQU	$-1
	DB	7,4,1,5,127,8,2,3
WRTBL	EQU	$-1
	DB	14,12,9,13,127,8,10,11
;
DWR1	EX	(SP),HL		;Delay time to settle
	EX	(SP),HL		; the controller
;
DWR2	IN	A,(STATUS)	;Wait until controller
	RLCA			;  is no longer busy
	JR	C,DWR2
	IN	A,(STATUS)	;Get status again
	AND	00000001B	;NZ=error
	RET
;*=*=*
;       SETUP disk
;       IY= DCT
;       D = Cylinder # (0-202)
;       E = Sector # (0-255)
;*=*=*
SETUP	EQU	$		;Wait until not busy
;*=*=*
;       Select routine
;*=*=*
	PUSH	HL		;Save buffer pointer
;*=*=*
;       Recalc cyl, sector to head, sector, cyl
;*=*=*
RECALC	LD	L,D		;Xfer cylinder
	LD	H,0
;
	BIT	5,(IY+4)	;Double track?
	JR	Z,$+3		;Go if not
	ADD	HL,HL		;Cyl * 2
;*****
;       Check on track advance
;*****
	LD	A,(IY+7)	;P/u sectors/track
	PUSH	DE
	LD	D,A		;Hang on to value
	AND	1FH		;Strip off other data
	LD	E,A
	INC	E		;Adj for zero offset
	LD	C,E		;Keep sec per track
	XOR	D		;Get # of heads
	RLCA			;Shift heads to 0-2
	RLCA
	RLCA
	INC	A		;Adjust for zero offset
	PUSH	BC
	LD	C,A
	@@MUL8
	POP	BC
	DEC	A		;Adjust for compare
	POP	DE		;Rcvr sector
	CP	E		;Is sector on this track?
	JR	NC,REC1		;Bypass if yes
	CPL			;Else subtract off a
	ADD	A,E		; track's # of sectors
	LD	E,A		;Reset sector #
	INC	HL		;  and bump cyl #
REC1	@@DIV8			;Sector#/sectors per head
	LD	D,A		;Xfer head # needed
	LD	A,(IY+4)	;P/u starting head
	AND	0FH		;Strip off other data
	ADD	A,D		;Add in relative head
	LD	D,A		;Point to physical head
;*****
;       Xfer head, sector, & cylinder data to WDC
;*****
	LD	A,(IY+3)	;Grab the drive select
	AND	3
	RLCA			;Shift to bits 3-4
	RLCA
	RLCA
	OR	D		;Merge in head select
	OUT	(SDH),A		;Transfer to WDC
	LD	A,E
	OUT	(SECNO),A
	LD	A,L
	OUT	(CYLLO),A
	LD	A,H
	OUT	(CYLHI),A
	POP	HL		;Rcvr buffer pointer
	LD	BC,0<8!DATA	;Set buflen & xfer port
	RET
;
DISKEND	EQU	$
;*=*=*
; some patch space - INSTALL must be patched also to load -
;*=*=*
	DW	0,0,0,0,0,0,0,0,0,0
	DW	0,0,0,0,0,0,0,0,0,0
	DW	0,0,0,0,0,0,0,0,0,0
	DW	0,0,0,0,0,0,0,0,0,0
RELTAB	DW	R1,R2,R3,R6,R7
	DW	R8,R9,R10,R11,R12
TABLEN	EQU	$-RELTAB/2
	DW	0,0,0,0		;Patch addresses
; end of actual I/O driver
;
	PAGE
