	.IF	LISTE
	.LIST
	.PAGE
	.ELSE
	.NOLIST
	.ENDC
;****************************************************************************
;
;	Winchester Disk Driver - part 1
;
;	File:	SAGE.BIOSE.TEXT
;	Date:	30-Oct-83
;	Issue:	2C
;
;
;	COPYRIGHT (c) 1983 SAGE Computer Technology
;	All Rights Reserved
;
;****************************************************************************
;
;	History:
;
;	2     23-Mar-83 Initial SAGE IV release.
;	2A     2-Jun-83 Set up interrupt driven seek.
;	2B     3-Aug-83 Improved recalibrate from cylinder 0.
;			Improved LBN overflow check.
;			Increase seek timeout to 5 seconds.
;	2C    30-Oct-83 Allow 16 heads.	 Added extra head settle time option.
;			Set up alternate direct seek.
;
;****************************************************************************


;****************************************************************************
;
;	Error Codes:
;
;	 0	No error
;	-1	Could not initialized VCO.
;	-2	
;	-3	Recalibrate failure
;	-4	Drive not ready
;	-5	Missing address mark
;	-6	Timeout waiting for data
;	-7	Overrun (if detectable)
;	-8	CRC error
;	-9	Verify error (read after write)
;	-10.	Write protect violation
;	-11.	Address out of range
;	-12.	Wrong cylinder
;	-13.
;	-14.	Bad device number
;
;****************************************************************************



.PAGE
;****************************************************************************
;
;	Winchester Initialization
;
;****************************************************************************

WCINIT
	MOVE.B	#90H,WP_1CB	;Init 8255 #1
	MOVE.B	#90H,WP_2CB	;Init 8255 #2
	MOVE.B	#0,WP_1B
	MOVE.B	#0C9H,WP_2B
	MOVE.B	#0,WP_1C
	MOVE.B	#5H,WP_2C
	MOVE.W	#226H,VCOCAL	;Initialize VCO parameters
	MOVE.W	#2,VCOTOL
	BSR	WVCOINIT
	ST	WG_INIT		;Force to look initialized
	RTS


.PAGE
;****************************************************************************
;
;	VCO Initialization
;
;	Uses: D0,D1,D2, A1
;
;****************************************************************************

WVCOINIT
	MOVEM.L D3-D5,-(SP)
	MOVE.B	#0,WP_2CB	;Force VCO to minimum frequency
	MOVEQ	#-128.,D0	;Start search in middle of range
	MOVEQ	#0,D2		;Difference

$10	MOVE.B	D0,WP_DTOA	;Apply trial value to D/A converter
	MOVE.B	D0,WG_VCODA	;Save for troubleshooting
	MOVEQ	#80.,D1		;Delay for 100 microseconds
$20	DBF	D1,$20
	LEA	WP_2A,A1	;Set up port
	MOVEQ	#5-1,D5		;Set up success counter
	
$25	MOVEM.L D0/D2,-(SP)
	MOVE	SR,-(SP)	;Disable interrupts
	MOVE	#2700H,SR
	MOVE.B	#0B0H,WP_8253+6 ;Mode 0
	MOVE.B	#0,WP_8253+4	;Zero count
	MOVE.B	#0,WP_8253+4
	BSR	RDREAL		;Get time value
	MOVE	(SP)+,SR	;Re-enable interrupt
	MOVE.W	D1,D4		;Save the time count
	MOVE.W	#9600.-1,D1	;Wait approximately 12 milliseconds
$30	DBF	D1,$30
	MOVE	SR,-(SP)	;Disable interrupts
	MOVE	#2700H,SR
	MOVE.B	#80H,WP_8253+6	;Latch data
	MOVE.B	WP_8253+4,D3	;Read the counter data
	ROL.W	#8,D3
	MOVE.B	WP_8253+4,D3
	ROL.W	#8,D3
	BSR	RDREAL
	MOVE	(SP)+,SR	;Re-enable interrupt
	MOVEM.L (SP)+,D0/D2
	SUB.W	D4,D1		;Find difference in time count
	NEG.W	D3		;Find VCO count
	MULU	#64.,D3
	DIVU	D1,D3		;D3 will be frequency in KHZ
	
	MOVE.W	D3,VCOVAL	;Save value
	SUB.W	VCOCAL,D3	;Check against calibration value
	BEQ.S	$100		;Found exact match
	BGT.S	$80		;Freqency was too high
;	Frequency was too low
	NEG.W	D3		;Make difference positive
	CMP.W	VCOTOL,D3
	BLE.S	$100		;Last value is in range
	ADDQ.B	#1,D2
	BEQ.S	$90		;Could not converge on value
	SUBQ.B	#1,D0
$70	BNE	$10		;Try next number

;	D/A converter to stops
	MOVE.B	#1,WG_VCOF	;Set VCO failure flag
	BRA.S	$110		;Value is out of range

;	Frequency was too high
$80	CMP.W	VCOTOL,D3
	BLE.S	$100		;Last value is in range
	ADDQ.B	#1,D2
	BEQ.S	$90		;Could not converge on value
	ADDQ.B	#1,D0
	BRA	$70

;	Here is best value
$90	MOVE.B	#2,WG_VCOF	;Indicate out of tolerance
	BRA.S	$110

 ;	Save good value
$100	DBF	D5,$25		;Check success counter
	ST	WG_INIT		;Set initialized flag
	CLR.B	WG_VCOF		;Indicate success

$110	MOVE.B	#1,WP_2CB	;Remove VCO force
	MOVE.W	#9600.-1,D1	;Wait approximately 12 milliseconds
$120	DBF	D1,$120
	MOVEM.L (SP)+,D3-D5
	RTS


.PAGE
;****************************************************************************
;
;	Calculate transfer parameters for Winchester
;
;	From:
;	  WT_LBN	Logical block number
;	  WT_LENG	Length of transfer
;	  WT_CTRL	Control information flags
;
;	Calculate:
;	  WT_CYL	Cylinder address
;	  WT_HEAD	Head address
;	  WT_LEAD	Number of bytes leading data in track
;	  WT_NUMB	Number of data bytes in track
;	  WT_TRAIL	Number of trailing bytes in track
;	  WT_LTN	Logical track number
;
;	Uses: D0,D1,D2,	 A1
;
;****************************************************************************

WDCALC
	MOVE.L	WT_LBN(A4),D1	;Find logical track number on whole disk
	BTST	#5,WI_TEST+1(A4) ;Check for direct seek to a cylinder
	BNE	$60		;Found direct seek, use block as cylinder
	BTST	#6,WT_CTRL(A4)	;Check for direct seek (alternate spec)
	BNE	$60		;Found direct seek, use block as cylinder
	MOVEQ	#0,D0
	MOVE.B	WI_SPT(A4),D0	;Get sectors per track
	MOVE.W	D0,D2
	SWAP	D1
	CMP.W	D0,D1
	BHI	$100		;Block number was too big
	SWAP	D1
	DIVU	D0,D1		;Logical track number
	
	SWAP	D1		;Find number of leading bytes
	MOVE.W	WI_BPS(A4),D0	;Bytes per Sector
	MULU	D0,D2		;Bytes per Track
	MULU	D1,D0		;Number of leading bytes
	CLR.W	D1
	SWAP	D1
	SUB.W	D0,D2		;Bytes remaining on track
	EXT.L	D2
	MOVE.W	D0,WT_LEAD(A4)	;Number of leading bytes
	MOVE.L	WT_LENG(A4),D0
	CMP.L	D0,D2
	BLT.S	$10		;Not enough data in track for total
	
	SUB.W	D0,D2		;Find trailing bytes
	MOVE.W	D2,WT_TRAIL(A4) ;Trailing data bytes
	
	MOVE.W	D0,WT_NUMB(A4)	;Number of data bytes
	BRA.S	$20

$10	MOVE.W	D2,WT_NUMB(A4)	;Number of data bytes
	CLR.W	WT_TRAIL(A4)	;Number of Trailing data bytes

;	Check for limit on logical device
$20	MOVE.B	WT_CHAN(A4),D0	;Isolate device partition
	ANDI.W	#0FH,D0
	LSL.B	#2,D0
	MOVE.W	WI_MAP(A4,D0.W),D2 ;Get base of area
	BNE.S	$25		;Partition exists
	TST.W	D0
	BNE.S	$110		;Only partition zero allowed to start at 0
$25	ADD.W	D2,D1		;Form mapped track number
	BCS.S	$100		;Track too large
	ADDQ.W	#2,D0
	CMP.W	WI_MAP(A4,D0.W),D1
	BHI.S	$100		;Track too large
	
;	Now check for logical track in Bad Track table
	BTST	#5,WT_CTRL(A4)	;Check if formatting
	BNE.S	$50		;Bypass bad track map if formatting
	LEA	WI_BTTBL(A4),A1 ;Address bad track table
$30	MOVE.W	(A1)+,D0	;Get bad track number
	BEQ.S	$50		;No more bad tracks
	CMP.W	D0,D1
	BCS.S	$50		;Target track < Bad track
	ADDQ.W	#1,D1		;Skip a track
	BRA	$30

$50	MOVE.W	D1,WT_LTN(A4)	;Set up logical track number
	MOVEQ	#0,D0
	MOVE.B	WI_HEADS(A4),D0
	DIVU	D0,D1		;Find cylinder number
$60	MOVE.W	D1,WT_CYL(A4)
	SWAP	D1
	MOVE.W	D1,WT_HEAD(A4)	;Head number
	CLR.B	D0		;EQ means success
	RTS

$100	MOVE.B	#-11.,WS_ERR(A4) ;Block number out of range
	RTS

$110	MOVE.B	#-14.,WS_ERR(A4) ;Bad device number
	RTS

.PAGE
;****************************************************************************
;
;	Winchester Recalibrate
;
;	Uses: D0, D1
;
;****************************************************************************

WDRECAL
	MOVE.L	D2,-(SP)
	MOVE.W	WI_NCYL(A4),D2	;Set up number of cylinders * 2 as timeout
	ADD.W	D2,D2
	CLR.B	WS_ERR(A4)
$10	BTST	#WB_TRK0,WP_1A
	BEQ.S	$30		;Recalibrate is done
	SUBQ.W	#1,D2
	BEQ	$110		;Recalibrate failure
	MOVE.B	#0CH,WP_1CB	;Step toward track zero
	MOVEQ	#1,D1		;Only 1 step
	BSR	WDSTEP		;Step the head
	TST.B	WS_ERR(A4)
	BNE	$160		;Found stepping error
	MOVE.W	#2400.,D0	;Force a 3 millisecond delay
$20	DBF	D0,$20
	BRA	$10

$30	MOVEQ	#20.,D0
	SWAP	D0
$40	BTST	#WB_SKCMP,WP_1A ;Check if seek complete
	BEQ.S	$50		;Seek is complete
	SUBQ.L	#1,D0
	BNE	$40
	BEQ	$110		;Error
	
$50	MOVE.B	#0DH,WP_1CB	;Step away from track 0
	MOVEQ	#20.,D1		;Twenty steps
	BSR	WDSTEP		;Step the head
	TST.B	WS_ERR(A4)
	BNE.S	$160		;Found stepping error
	MOVE.W	#30000.,D0	;Force a 20 millisecond delay
	ADD.W	#18000.,D0
$60	DBF	D0,$60
	
	MOVEQ	#20.,D0
	SWAP	D0
$70	BTST	#WB_SKCMP,WP_1A ;Check if seek complete
	BEQ.S	$80		;Seek is complete
	SUBQ.L	#1,D0
	BNE	$70
	BRA	$110		;Error

$80	MOVE.W	WI_NCYL(A4),D2	;Set up number of cylinders * 2 as timeout
	ADD.W	D2,D2
$90	BTST	#WB_TRK0,WP_1A
	BEQ.S	$130		;Recalibrate is done
	SUBQ.W	#1,D2
	BEQ.S	$110		;Recalibrate failure
	MOVE.B	#0CH,WP_1CB	;Step toward track zero
	MOVEQ	#1,D1		;Only 1 step
	BSR	WDSTEP		;Step the head
	TST.B	WS_ERR(A4)
	BNE.S	$160		;Found stepping error
	MOVE.W	#2400.,D0	;Force a 3 millisecond delay
$100	DBF	D0,$100
	BRA	$90

;	Recalibrate failed
$110	MOVEQ	#-3,D0		;Recalibrate failure
	BRA.S	$150

;	Recalibrate is good
	MOVEQ	#20.,D0
	SWAP	D0
$120	BTST	#WB_SKCMP,WP_1A ;Check if seek complete
	BEQ.S	$130		;Seek is complete
	SUBQ.L	#1,D0
	BNE	$120
	BRA	$110		;Seek never completed

$130	MOVE.W	#15200.,D0	;Head settle time
$140	DBF	D0,$140
	CLR.B	D0		;EQ means recalibrate good
	CLR.W	WS_CCYL(A4)	;Clear current cylinder number
	ST	WS_RCAL(A4)	;Indicate recalibrated
$150	MOVE.B	D0,WS_ERR(A4)
$160	MOVE.L	(SP)+,D2
	RTS

.PAGE
;****************************************************************************
;
;	Winchester Seek
;
;	Uses: D0,D1,D2,A0
;
;****************************************************************************

WDSEEK
	CLR.B	WS_ERR(A4)	;Clear error flag
	BTST	#WB_SKCMP,WP_1A ;Check if seek complete
	BNE	$100		;Seek is not complete
	TST.B	WS_RCAL(A4)	;Check for recalibrate
	BNE.S	$10		;No recalibrate
	BSR	WDRECAL		;Recalibrate the drive
	BNE.S	$110		;Recalibrate error
$10	MOVE.B	WP_1C,D0	;Set up head number
	ANDI.B	#0F0H,D0
	OR.W	WT_HEAD(A4),D0
	MOVE.B	D0,WP_1C
	MOVE.W	WS_CCYL(A4),D1	;Get current cylinder number
	SUB.W	WT_CYL(A4),D1	;New cylinder number
	BEQ.S	$110		;Already at cylinder
	BGT.S	$20		;Current cylinder > new cylinder
;	Current cylinder is < new cylinder
	NEG.W	D1		;Make number positive
	MOVE.B	#0DH,WP_1CB	;Step to higher track
	BRA.S	$30

;	Current cylinder is > new cylinder
$20	MOVE.B	#0CH,WP_1CB	;Step to lower track
$30	BSR	WDSTEP		;Step the drive
	MOVE.W	WT_CYL(A4),WS_CCYL(A4) ;Update current cylinder
	LEA	WT_TIMO,A0
	PEA	WDSKTO
	MOVE.L	(SP)+,4(A0)	;Set up completion address
	MOVEQ	#0,D0
	MOVE.W	D1,D0		;Find number of steps at normal rate
	CMPI.W	#3,D0
	BLE.S	$40		;Low step count
	MOVEQ	#3,D0		;Max steps = 3
$40	SUB.W	D0,D1		;Take steps from original
	MULU	WI_STEPT(A4),D0 ;Calculate time for stepping
	MULU	WI_SLEWT(A4),D1 ;Remainder is slew time
	ADD.W	D1,D0
	ADD.W	WI_SETLT(A4),D0 ;Add in head settle time
	MOVE.L	D0,8(A0)	;Set up time
	BSET	#7,P8259+2	;Disable foreground interrupts
	BSR	ESCHED		;Enter the schedule
	RTS
	
;	Seek is not complete
$100	MOVEQ	#-3,D0
	MOVE.B	D0,WS_ERR(A4)	;EQ indicates success
$110	ST	WT_SKDN(A4)	;Set up seek done flag
	RTS


.PAGE
;****************************************************************************
;
;	Winchester Drive select
;
;	A4 must point to Drive Configuration Table
;
;	Uses: D0
;
;****************************************************************************

WDSELECT
	MOVE.B	WP_1B,D0	;Get previous value
	ANDI.B	#0F0H,D0
	OR.B	WI_SELCT(A4),D0
	MOVE.B	D0,WP_1B
	BTST	#0,WI_STYPE(A4) ;Check for SyQuest door open test
	BEQ.S	$100		;No door open test
	BTST	#WB_CHNG,WP_1A	;Check cartridge changed flag
	BNE.S	$100		;Cartridge not changed
	CLR.B	WS_INIT(A4)	;Mark drive uninitialized
$100	RTS


;****************************************************************************
;
;	Winchester Drive Step
;
;	D1 = Step count
;
;****************************************************************************

WDSTEP
	MOVEM.L D0-D2,-(SP)
	LSL.W	#1,D1		;Multiply step count by 2
	SUBQ.W	#1,D1		; and subtract 1
	MOVE.B	#36H,WP_8253+6	;Set counter 0 int mode 3
	MOVE.B	#0B0H,WP_8253+6 ;Set counter 2 into mode 0
	MOVE.W	WI_STPCT(A4),D0
	MOVE.B	D0,WP_8253	;Low count on counter 0
	LSR.W	#8,D0
	MOVE.B	D0,WP_8253	;High count on counter 0
	MOVEQ	#24.,D2
$1	DBF	D2,$1
	MOVE.B	D1,WP_8253+4	;Low count on counter 2
	ROR.W	#8,D1
	MOVE.B	D1,WP_8253+4	;High count on counter 2
	MOVEQ	#24.,D2
$2	DBF	D2,$2
	BCLR	#0,WP_2B	;Enable stepping
	MOVEQ	#80.,D2
$3	DBF	D2,$3
	MOVEQ	#-1,D1		;Timeout count
$10	MOVE.B	#80H,WP_8253+6	;Latch counter
	MOVE.B	WP_8253+4,D0	;Check count
	ROL.W	#8,D0
	MOVE.B	WP_8253+4,D0
	ROL.W	#8,D0
	BEQ.S	$20		;Done
	BMI.S	$20		;Done
	DBF	D1,$10		;Try checking again
	MOVE.B	#-3,WS_ERR(A4)	;Step timeout failure
	
$20	BSET	#0,WP_2B	;Disable stepping
	MOVEM.L (SP)+,D0-D2
	RTS


;	Seek Completion Check
WDSKCOMP
	MOVEM.L D0-D2/A0-A2/A4,-(SP)
	MOVEA.L WG_VARS,A4
	MOVEQ	#10,D1		;Set up timeout count, about 5 seconds
	SWAP	D1
$10	ROR.B	#8,D1		;Just for delay
	BTST	#WB_SKCMP,WP_1A ;Check for seek complete
	BEQ.S	$15		;Found seek complete
	SUBQ.L	#1,D1
	BNE.S	$10		;No timeout
	MOVE.B	#-3,WS_ERR(A4)	;Set up error code for timeout
	BRA.S	$20

$15	CLR.B	WS_ERR(A4)
$20	BSET	#0,WP_2B	;Disable stepping
	NEG.W	D1
	MOVE.W	D1,WI_SKUND(A4) ;Save seek under count
	MOVE.W	WI_EXHST(A4),D1
	BEQ	WDDKIO1		;No extra head settle time
$30	ROR.B	#8,D1		;Just for delay
	DBF	D1,$30
	BRA	WDDKIO1


                                                                                                                                                                                                                                                                                                                                                                                                                                          