PAGE 58,132
TITLE DISK ----- 06/10/85 FIXED DISK BIOS
.286C
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	DISK_IO
	PUBLIC	DISK_SETUP
	PUBLIC	HD_INT

	EXTAN	CMOS_READ:NEAR
	EXTRN	CMOS_WRITE:NEAR
	EXTRN	DDS:NEAR
	EXTRN	E_MSG:NEAR
	EXTRN	F1780:NEAR
	EXTRN	F1781:NEAR
	EXTRN	F1782:NEAR
	EXTRN	F1790:NEAR
	EXTRN	F1791;NEAR
	EXTRN 	FD_TBL:NEAR

;--- INT 13H ----------------------------------------------------------------------------
;											:
; FIXED DISK I/O INTERFACE								:
;											:
;	THIS INTERFACE PROVIDES ACCESS TO 5 1/4" FIXED DISKS THROUGH			:
;	THE IBM FIXED DISK CONTROLLER.							:
;											:
;	THE BIOS ROUTINES ARE  MEANT TO BE ACCESSED THROUGH 				:
;	SOFTWARE INTERRUPTS ONLY.  ANY ADDRESSES PRESENT IN				:
;	THESE LISTINGS ARE  INCLUDED ONLY FOR COMPLETENESS,				:
;	NOT FOR REFERENCE. APPLICATIONS WHICH REFERENCE ANY				:
;	ABSOLUTE ADDRESSES WITHIN THE CODE SEGMENTS OF BIOS				:
;	VIOLATE THE STRUCTURE AND DESIGN OF BIOS.					:
;											:
;----------------------------------------------------------------------------------------
;											:
; INPUT	 (AH)=	HEX COMMAND VALUE							:
;											:
;	(AH)= 00H	RESET DISK (DL = 80H,81H) / DISKETTE				:
;	(AH)= 01H	READ THE STATUS OF THE LAST DISK OPERATION INTO (AL)		:
;			 NOTE: DL < 80H - DISKETTE					:
;			       DL > 80H - DISK						:
;	(AH)= 02H	READ THE DESIRED SECTORS INTO MEMORY				:
;	(AH)= 03H	WRITE THE DESIRED SECTORS FROM MEMORY				:
;	(AH)= 04H	VERIFY THE DESIRED SECTORS					:
;	(AH)= 05H	FORMAT THE DESIRED TRACK					:
;	(AH)= 06H	UNUSED								:
;	(AH)= 07H	UNUSED								:
;	(AH)= 08H	RETURN THE CURRENT DRIVE PARAMETERS				:
;	(AH)= 09H	INITIALIZE DRIVE PAIR CHARACTERISTICS				:
;			 INTERRUPT 41 POINTS TO DATA BLOCK FOR DRIVE 0			:
;			 INTERRUPT 46 POINTS TO DATA BLOCK FOR DRIVE 1			:
;	(AH)= 0AH	READ LONG							:
;	(AH)= 0BH	WRITE LONG (READ A WRITE LONG ENCOMPASS 512 + 4 BYTES ECC)	:
;	(AH)= 0CH	SEEK								:
;	(AH)= 0DH	ALTERNATE DISK RESET (SEE DL)					:
;	(AH)= 0EH	UNUSED								:
;	(AH)= 0FH	UNUSED								:
;	(AH)= 10H	TEST DRIVE READY						:
;	(AH)= 11H	RECALIBRATE							:
;	(AH)= 12H	UNUSED								:
;	(AH)= 13H	UNUSED								:
;	(AH)= 14H	CONTROLLER INTERNAL DIAGNOSTIC					:
;	(AH)= 15H	READ DASD TYPE							:
;											:
;----------------------------------------------------------------------------------------
;											:
;	REGISTERS USED FOR FIXED DISK OPERATIONS					:
;											:
;		(DL)	-  DRIVE NUMBER     (80H-81H FOR DISK. VALUE CHECKED)		:
;		(DH)	-  HEAD NUMBER	   (0-15 ALLOWED, NOT VALUE CHECKED)		:
;		(CH)	-  CYLINDER NUMBER  (0-1023, NOT VALUE CHECKED)(SEE CL)		:
;		(CL)	-  SECTOR NUMBER    (1-17, NOT VALUE CHECKED)			:
;											:
;			   NOTE: HIGH 2 BITS OF CYLINDER NUMBER ARE PLACED		:
;			   IN THE HIGH 2 BITS OF THE CL REGISTER			:
;			   10 BITS TOTAL)						:
;											:
;		(AL)	-  NUMBER OF SECTORS (MAXIMUM POSSIBLE RANGE 1-80H,		:
;				     FOR READ/WRITE LONG 1-79H)				:
;											:
;		(ES:BX)	-  ADDRESS OF BUFFER FOR READS AND WRITES,			:
;			   (NOT REQUIRED FOR VERIFY)					:
;											:
;		FORMAT (AH=5) ES:BX POINTS TO A 512 BYTE BUFFER. THE FIRST		:
;			   2*(SECTORS/TRACK) BYTES CONTAIN F,N FOR EACH SECTOR.		:
;			   F = 00H FOR A GOOD SECTOR					:
;			       80H FOR A BAD SECTOR					:
;			   N = SECTOR NUMBER						:
;			   FOR AN INTERLEAVE OF 2 AND 17 SECTORS/TRACK			:
;			   THE TABLE SHOULD BE:						:
;											:
;		   DB	00H,01H,00H,0AH,00H,02H,00H,0BH,00H,03H,00H,0CH			:
;		   DB	00H,04H,00H,0DH,00H,05H,00H,0EH,00H,06H,00H,0FH			:
;		   DB	00H,07H,00H,10H,00H,08H,00H,11H,00H,09H				:
;											:
;----------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------
; OUTPUT										:
;	AH = STATUS OF CURRENT OPERATION						:
;	     STATUS BITS ARE DEFINED IN THE EQUATES BELOW				:
;	CY = 0  SUCCESSFUL OPERATION (AH=0 ON RETURN)					:
;	CY = 1  FAILED OPERATION (AH HAS ERROR REASON)					:
;											:
;	NOTE:	ERROR 11H  INDICATES THAT THE DATA READ HAD A RECOVERABLE		:
;		ERROR WHICH WAS CORRECTED BY THE ECC ALGORITHM.  THE DATA		:
;		IS PROBABLY GOOD,   HOWEVER THE BIOS ROUTINE INDICATES AN		:
;		ERROR TO ALLOW THE CONTROLLING PROGRAM A CHANCE TO DECIDE		:
;		FOR ITSELF.  THE  ERROR  MAY  NOT  RECUR  IF  THE DATA IS 		:
;		REWRITTEN.								:
;											:
;	IF DRIVE PARAMETERS WERE REQUESTED (DL >= 80H),					:
;	   INPUT:									:
;	     (DL) = DRIVE NUMBER							:
;	   OUTPUT:									:
;	     (DL) = NUMBER OF CONSECUTIVE ACKNOWLEDGING DRIVES ATTACHED (1-2)		:
;		    (CONTROLLER CARD ZERO TALLY ONLY)					:
;	     (DH) = MAXIMUM USEABLE VALUE FOR HEAD NUMBER				:
;	     (CH) = MAXIMUM USEABLE VALUE FOR CYLINDER NUMBER				:
;	     (CL) = MAXIMUM USEABLE VALUE FOR SECTOR NUMBER				:
;		    AND CYLINDER NUMBER HIGH BITS					:
;											:
;	IF READ DASD TYPE WAS REQUESTED,						:
;											:
;	AH = 0 - NOT PRESENT								:
;	     1 - DISKETTE - NO CHANGE LINE AVAILABLE					:
;	     2 - DISKETTE - CHANGE LINE AVAILABLE					:
;	     3 - FIXED DISK								:
;											:
;	CX,DX = NUMBER OF 512 BYTE BLOCKS WHEN AH = 3					:
;											:
;	REGISTERS WILL BE PRESERVED EXCEPT WHEN THEY ARE USED TO RETURN			:
;	INFORMATION.									:
;											:
;	NOTE: IF AN ERROR IS REPORTED BY THE DISK CODE, THE APPROPRIATE			:
;		ACTION IS TO RESET THE DISK, THEN RETRY THE OPERATION.			:
;											:
;----------------------------------------------------------------------------------------

SENSE_FAIL	EQU	0FFH	; NOT IMPLEMENTED
NO_ERR		EQU	0E0H	; STATUS ERROR/ERROR REGISTER=0
WRTTE_FAULT	EQU	0CCH 	; WRITE FAULT ON SELECTED DRIVE
UNDEF_ERR	EQU	0BBH 	; UNDEFINED ERROR OCCURRED
NOT_RDY		EQU	0AAH	; DRIVE NOT READY
TIME_OUT	EQU	80H	; ATTACHMENT FAILED TO RESPOND
BAD_SEEK	EQU	40H	; SEEK OPERATION FAILED
BAD_CNTLR	EQU	20H	; CONTROLLER HAS FAILED
DATA_CORRECTED	EQU	11H	; ECC CORRECTED DATA ERROR
BAD_ECC		EQU	10H	; BAD ECC ON DISK READ
BAD_TRACK	EQU	0BH	; NOT IMPLEMENTED
BAD_SECTOR	EQU	0AH	; BAD SECTOR FLAG DETECTED
DMA_BOUNDARY	EQU	09H	; DATA EXTENDS TOO FAR
INIT_FAIL	EQU	07H	; DRIVE PARAMETER ACTIVITY FAILED
BAD_RESET	EQU	05H	; RESET FAILED
RECORD_NOT_FND	EQU	04H	; REQUESTED SECTOR NOT FOUND
BAD_ADDR_MARK	EQU	02H	; ADDRESS MARK NOT FOUND
BAD_CMD		EQU	01H	; BAD COMMAND PASSED TO DISK I/O


;------------------------------------------------------------
;				:
; FIXED DISK PARAMETER TABLE		:
;  -  THE TABLE IS COMPOSED OF A BLOCK DEFINED AS:	:
;				:
;  +0   (1 WORD) - MAXIMUM NUMBER OF CYLINDERS	:
;  +2   (1 BYTE) - MAXIMUM NUMBER OF HEADS	:
;  +3   (1 WORD) - NOT USED/SEE PC-XT		:
;  +5   (1 WORD) - STARTING WRITE PRECOMPENSATION CYL	:
;  +7   (1 BYTE) - MAXIMUM ECC DATA BURST LENGTH	:
;  +8   (1 BYTE) - CONTROL BYTE		:
;			BIT    7 DISABLE RETRIES -OR-	:
;			BIT    6 DISABLE RETRIES	:
;			BIT    3 MORE THAN 8 HEADS	:
;  +9   (3 BYTES)- NOT USED/SEE PC-XT		:
; +12   (1 WORD) - LANDING ZONE		:
; +14   (1 BYTE) - NUMBER OF SECTORS/TRACK	:
; +15   (1 BYTE) - RESERVED FOR FUTURE USE	:
;					:
;	- TO DYNAMICALLY DEFINE A SET OF PARAMETERS	:
;	  BUILD A TABLE FOR UP TO 15 TYPES AND PLACE	:
;	  THE CORRESPONDING VECTOR INTO INTERRUPT 41	:
;	  FOR DRIVE 0 AND INTERRUPT 46 FOR DRIVE 1.	:
;					:
;------------------------------------------------------------
;------------------------------------------------------------
;				:
; HARDWARE SPECIFIC VALUES		:
;				:
;  -  CONTROLLER I/O PORT		:
;     > WHEN READ FROM:		:
;	HF_PORT+0 - READ DATA (FROM CONTROLLER TO CPU)	:
;	HF_PORT+1 - GET ERROR REGISTER		:
;	HF_PORT+2 - GET SECTOR COUNT		:
;	HF_PORT+3 - GET SECTOR NUMBER		:
;	HF_PORT+4 - GET CYLINDER LOW		:
;	HF_PORT+5 - GET CYLINDER HIGH (2 BITS)	:
;	HF_PORT+6 - GET SIZE/DRIVE/HEAD		:
;	HF_PORT+7 - GET STATUS REGISTER		:
;				:
;     > WHEN WRITTEN TO:		:
;	HF_PORT+0 - WRITE DATA (FROM CPU TO CONTROLLER)	:
;	HF_PORT+1 - SET PRECOMPENSATION CYLINDER	:
;	HF_PORT+2 - SET SECTOR COUNT		:
;	HF_PORT+3 - SET SECTOR NUMBER		:
;	HF_PORT+4 - SET CYLINDER LOW		:
;	HF_PORT+5 - SET CYLINDER HIGH (2 BITS)	:
;	HF_PORT+6 - SET SIZE/DRIVE/HEAD		:
;	HF_PORT+7 - SET COMMAND REGISTER		:
;				:
;------------------------------------------------------------

HF_PORT		EQU	01F0H	; DISK PORT
HF_REG_PORT	EQU	03F6H

;-----		STATUS REGISTER

ST_ERROR	EQU	00000001B	;
ST_INDEX	EQU	00000010B	;
ST_CORRCTD	EQU	00000100B	; ECC CORRECTION SUCCESSFUL
ST_DRQ		EQU	00001000B	;
ST_SEEK_COMPL	EQU	00010000B	; SEEK COMPLETE
ST_WRT_FLT	EQU	00100000B	; WRITE FAULT
ST_READY	EQU	01000000B	;
ST_BUSY		EQU	10000000B	;

;-----		ERROR REGISTER

ERR_DAM		EQU	00000001B	; DATA ADDRESS MARK NOT FOUND
ERR_TRK_0	EQU	00000010B	; TRACK 0 NOT FOUND ON RECAL
ERR_ABORT	EQU	00000100B	; ABORTED COMMAND
;		EQU	00001000B	; NOT USED
ERR_ID		EQU	00010000B	; ID NOT FOUND
;		EQU	00100000B	; NOT USED
ERR_DATA_ECC	EQU	01000000B
ERR_BAD_BLOCK	EQU	10000000B


RECAL_CMD	EQU	00010000B	; DRIVE RECAL   (10H)
READ_CMD	EQU	00100000B	;       READ    (20H)
WRITE_CMD	EQU	00110000B	;       WRITE   (30H)
VERIFY_CMD	EQU	01000000B	;       VERIFY  (40H)
FMTTRK_CMD	EQU	01010000B	; FORMAT TRACK  (50H)
INIT_CMD	EQU	01100000B	;   INITIALIZE  (60H)
SEEK_CMD	EQU	01110000B	;       SEEK    (70H)
DIAG_CMD	EQU	10010000B	; DIAGNOSTIC    (90H)
SET_PARM_CMD	EQU	10010001B	; DRIVE PARMS   (91H)
NO_RETRIES	EQU	00000001B	; CHD MODIFIER  (01H)
ECC_MODE	EQU	00000010B	; CMD MODIFIER  (02H)
BUFFER_MODE	EQU	00001000B	; CMD MODIFIER  (08H)

MAX_FILE	EQU	2
S_MAX_FILE	EQU	2

DELAY_1		EQU	25H	; DELAY FOR OPERATION COMPLETE
DELAY_2		EQU	0600H	; DELAY FOR READY
DELAY_3		EQU	0100H	; DELAY FOR DATA REQUEST

HF-FAIL		EQU	08H	; CMOS FLAG IN BYTE 0EH

;-----		COMMAND BLOCK REFERENCE

@CMD_BLOCK	EQU	BYTE PTR [BP]-8	; @CMD_BLOCK REFERENCES BLOCK HEAD IN SS
					;  (BP) POINTS TO COMMAND BLOCK TAIL
					;       AS DEFINED BY THE "ENTER" PARMS

;----------------------------------------------------------------
;	FIXED DISK I/O SETUP		:
;				:
;  - ESTABLISH TRANSFER VECTORS FOR THE FIXED DISK	:
;  - PERFORM POWER ON DIAGNOSTICS		:
;     SHOULD AN ERROR OCCUR A "1701" MESSAGE IS DISPLAYED	:
;				:
;----------------------------------------------------------------
	ASSUME	CS:CODE,DS:ABS0	; WORK OFF DS REGISTER

DISK_SETUP	PROC	NEAR
	CLI
	MOV	AX,ABS0				; GET ABSOLUTE SEGMENT
	MOV	DS,AX				; SET SEGMENT REGISTER
	MOV	AX,WORD PTR @ORG_VECTOR		; GET DISKETTE VECTOR
	MOV	WORD PTR @DISK_VECTOR,AX	;  INTO INT 40H
	MOV	AX,WORD PTR @ORG_VECTOR+2
	MOV	WORD PTR @DISK_VECTOR+2,AX
	MOV	WORD PTR @ORG_VECTOR,OFFSET DISK_IO	; FIXED DISK HANDLER
	MOV	WORD PTR @ORG_VECTOR+2,CS
	MOV	WORD PTR @HDISK_INT,OFFSET HD_INT	; FIXED DISK INTERRUPT
	MOV	WORD PTR @HDISK_INT_2,CS
	MOV	WORD PTR @HF_TBL_VEC,OFFSET FD_TBL	; PARM TABLE DRIVE 80
	MOV	WORD PTR @HF_TBL_VEC+2,CS
	MOV	WORD PTR @HF1_TBL_VEC,OFFSET FD_TBL	; PARM TABLE DRIVE 81
	MOV	WORD PTR @HF1_TBL_VEC+2,CS
	IN	AL,INTB01			; TURN ON SECOND INTERRUPT CHIP
	AND	AL,0BFH
	JMP	$+2
	OUT	INTB01,AL
	IN	AL,INTA01			; LET INTERRUPTS PASS THRU TO
	AND	AL,0FBH				;  SECOND CHIP
	JMP	$+2
	OUT	INTA01,AL

	STI
	ASSUME	DS:DATA,ES:ABS0
	PUSH	DS				; MOVE ABS0 POINTER TO
	POP	ES				; EXTRA SEGMENT POINTER
	CALL	DDS				; ESTABLISH DATA SEGMENT
	MOV	@DISK_STATUS1,0			; RESET THE STATUS INDICATOR
	MOV	@HF_NUM,0			; ZERO NUMBER OF FIXED DISKS
	MOV	@CONTROL_BYTE,0
	MOV	AL,CMOS_DIAG+NMI
	CALL	CMOS_READ			; CHECK CMOS VALIDITY
	MOV	AH,AL				; SAVE CMOS FLAG
	AND	AL,BAD_BAT+BAD_CKSUM		; CHECK FOR VALID CMOS
	JZ	L1
	JMP	POD_DONE			; CMOS NOT VALID -- NO FIXED DISK
L1:
	AND	AH,NOT HF_FAIL			; ALLOW FIXED DISK IPL
	MOV	AL,CMOS_DIAG+NMI		; WRITE IT BACK
	CALL	CMOS_WRITE	
	MOV	AL,CMOS_DISK+NMI
	CALL	CMOS_READ
	MOV	@PORT_OFF,0		; ZERO CARD OFFSET
	MOV	BL,AL			; SAVE FIXED DISK BYTE
	AND	AX,00F0H		; GET FIRST DRIVE TYPE AS OFFSET
	JZ	POD_DONE		; NO FIXED DISKS

	CMP	AL,0F0H			; CHECK FOR EXTENDED DRIVE TYPE BYTE USE
	JNE	L2			; USE DRIVE TYPE 1 --> 14 IF NOT IN USE

	MOV	AL,CMOS_DISK_1+NMI	; GET EXTENDED TYPE POP DRIVE C:
	CALL	CMOS_READ		; FROM CMOS
	CMP	AL,0			; IS TYPE SET TO ZERO
	JE	POD_DONE		; EXIT IF NOT VALID AND NO FIXED DISKS
	CMP	AL,47			; IS TYPE WITHIN VALID RANGE
	JA	POD_DONE		; EXIT WITH NO FIXED DISKS IF NOT VALID
	SHL	AX,4			; ADJUST TYPE TO HIGH NIBBLE
L2:
	ADD	AX,OFFSET FD_TBL-16D	; COMPUTE OFFSET OF FIRST DRIVE TABLE
	MOV	WORD PTR @HF_TBL_VEC,AX	; SAVE IN VECTOR POINTER
	MOV	@HF_NUM,1		; AT LEAST ONE DRIVE
	MOV	AL,BL
	SHL	AL,4			; GET SECOND DRIVE TYPE
	JZ	SHORT L4		; ONLY ONE DRIVE
	MOV	AH,0

	CMP	AL,0F0H			; CHECK FOR EXTENDED DRIVE TYPE BYTE USE
	JNE	L3			; USE DRIVE TYPE 1 --> 14 IF NOT IN USE

	MOV	AL,CMOS_DISK_2+NMI	; GET EXTENDED TYPE FOR DRIVE C:
	CALL	CMOS_READ		; FROM CMOS
	CMP	AL,0			; IS TYPE SET TO ZERO
	JE	L4			; SKIP IF SECOND FIXED DISK NOT VALID
	CMP	AL,47			; IS TYPE WITHIN VALID RANGE
	JA	L4			; SKIP IF NOT VALID
	SHL	AX,4			; ADJUST TYPE TO HIGH NIBBLE
L3:
	ADD	AX,OFFSET FD_TBL-16D	; COMPUTE OFFSET FOR SECOND FIXED DISK
	MOV	bX,AX
	CMP	WORD PTR CS:[BX],0	; CHECK FOR ZERO CYLINDERS IN TABLE
	JE	L4			; SKIP DRIVE IF NOT A VALID TABLE ENTRY
	MOV	WORD PTR @HF1_TBL_VEC,AX
	MOV	@HF_NUM,2		; TWO DRIVES

L4:	MOV	DL,80H			; CHECK THE CONTROLLER
	MOV	AH,14H			; USE CONTROLLER DIAGNOSTIC COMMAND
	INT	13H			; CALL BIOS WITH DIAGNOSTIC COMMAND
	JC	CTL_ERRX		; DISPLAY ERROR MESSAGE IF BAD RETURN
	MOV	AX,@TIMER_LOW		; GET START TIMER COUNTS
	MOV	BX,AX
	ADD	AX,6*182		; 60 SECONDS* 18.2
	MOV	CX,AX
	CALL	HD_RESET_1		; SET UP DRIVE 0
	CMP	@HF_NUM,1		; WERE THERE TWO DRIVES?
	JBE	POD_DONE		; NO-ALL DONE
	MOV	DL,81H			; SET UP DRIVE 1
	CALL	HD_RESET_1
POD_DONE:
	RET

;-----	POD_ERROR

CTL_ERRX:
	MOV	SI,OFFSET F1782		; CONTROLLER ERROR
	CALL	SET_FAIL		; DO NOT IPL FROM DISK
	CALL	E_MSG			; DISPLAY ERROR AND SET (BP) ERROR FLAG
	JMP	POD_DONE

HD_RESET_1	PROC	NEAR
	PUSH	BX			; SAVE TIMER LIMITS
	PUSH	CX
RES_1:	MOV	AH,09H			; SET DRIVE PARAMETERS
	INT	13H
	JC	RES_2
	MOV	AH,11H			; RECALIBRATE DRIVE
	INT	13H
	JNC	RES_CK			; DRIVE OK
RES_2:	CALL	POD_TCHK		; CHECK TIME OUT
	JNC	RES_1
RES_FL:	MOV	SI,OFFSET F1781		; INDICATE DISK 1 FAILURE
	TEST	DL,1
	JNZ	RES_E1
	MOV	SI,OFFSET F1780		; INDICATE DISK 0 FAILURE
	CALL	SET_FAIL		; DO NOT TRY TO IPL DISK 0
	JMP	SHORT RES_E1
RES_RS:	MOV	AH,00H			; RESET THE DRIVE
	INT	13H
RES_CK:	MOV	AH,08H			; GET MAX CYLINDER,HEAD,SECTOR
	MOV	BL,DL			; SAVE DRIVE CODE
	INT	13H
	JC	RES_ER
	MOV	WORD PTR @NEC_STATUS,CX	; SAVE MAX CYLINDER, SECTOR
	MOV	DL,BL			; RESTORE DRIVE CODE
RES_3:	MOV	AX,0401H		; VERIFY THE LAST SECTOR
	INT	13H
	JNC	RES_OK			; VERIFY OK
	CMP	AH,BAD_SECTOR		; OK ALSO IF JUST ID READ
	JE	RES_OK
	CMP	AH,DATA_CORRECTED
	JE	RES_OK
	CMP	AH,BAD_ECC
	JE	RES_OK
	CALL	POD_TCHK		; CHECK FOR TIME OUT
	JC	RES_ER			; FAILED
	MOV	CX,WORD PTR @NEC_STATUS	; GET SECTOR ADDRESS, AND CYLINDER
	MOV	AL,CL			; SEPARATE OUT SECTOR NUMBER
	AND	AL,3FH
	DEC	AL			; TRY PREVIOUS ONE
	JZ	RES_RS			; WE'VE TRIED ALL SECTORS ON TRACK
	AND	CL,0C0H			; KEEP CYLINDER BITS
	OR	CL,AL			; MERGE SECTOR WITH CYLINDER BITS
	MOV	WORD PTR @NEC_STATUS,CX 	; SAVE CYLINDER, NEW SECTOR NUMBER
	JMP	RES_3			; TRY AGAIN
RES_ER:	MOV	SI,OFFSET F1791		; INDICATE DISK 1 ERROR
	TEST	DL,1
	JNZ	RES_E1
	MOV	SI,OFFSET F1790		; INDICATE DISK 0 ERROR
RES_E1:
	CALL	E_MSG			; DISPLAY ERROR AND SET (BP) ERROR FLAG
RES_OK:	POP	CX			; RESTORE TIMER LIMITS
	POP	AX
	RET
HD_RESET_1	ENDP

SET_FAIL	PROC	NEAR
	MOV	AX,X*(CMOS_DIAG+NMI)	; GET CMOS ERROR BYTE
	CALL	CMOS_READ
	OR	AL,HF_FAIL		; SET DO NOT IPL FROM DISK FLAG
	XCHG	AH,AL			; SAVE IT
	CALL	CMOS_WRITE		; PUT IT OUT
	RET
SET-FAIL	ENDP

POD_TCHK	PROC	NEAR		; CHECK FOR 30 SECOND TIME OUT
	POP	AX			; SAVE RETURN
	POP	CX			; GET TIME OUT LIMITS
	POP	BX
	PUSH	BX			; AND SAVE THEM AGAIN
	PUSH	CX
	PUSH	AX			; RESTORE RETURN
	MOV	AX,@TIMER_LOW		; AX = CURRENT TIME
					; BX = START TIME
					; CX = END TIME
	CMP	BX,CX
	JB	TCHK1			; START < END
	CMP	BX,AX
	JB	TCHKG			; END < START < CURRENT
	JMP	SHDRT TCHK2		; END, CURRENT < START
TCHK1:	CMP	AX,BX
	JB	TCHKNG			; CURRENT < START < END
TCHK2:	CMP	AX CX
	JB	TCHKG			; START < CURRENT < END
					; OR CURRENT < END < START
TCHKNG:	STC				; CARRY SET INDICATES TIME OUT
	RET
TCHKG:	CLC				; INDICATE STILL TIME
	RET
POD_TCHK	ENDP

DISK_SETUP	ENDP


;------------------------------------------
;	FIXED DISK BIOS ENTRY POINT	:
;------------------------------------------

OISK_IO	PROC	FAR
	ASSUME	DS:DATA,ES:NOTHING
	CMP	DL,80H			; TEST FOR FIXED DISK DRIVE
	JAE	A1			; YES, HANDLE HERE
	INT	40H			; DISKETTE HANDLER

RET_2:	
	RET	2			; BACK TO CALLER
A1:
	STI				; ENABLE INTERRUPTS
	OR	AH,AH
	JNZ	A2
	INT	40H			; RESET NEC WHEN AH=0
	SUB	AH,AH
	CMP	DL,(80H + S_MAX_FILE - 1)
	JA	RET_2
A2:
	CMP	AH,08H			; GET PARAMETERS IS A SPECIAL CASE
	JNZ	A3
	JMP	GET_PARM_N
A3:	CMP	AH,15H			; READ DASD TYPE IS ALSO
	JNZ	A4
	JMP	READ_DASD_TYPE

A4:					; 	  SAVE REGISTERS DURING OPERATION
	ENTER	8,0			; SAVE (BP) AND MAKE ROOM FOR @CMD_BLOCK
	PUSH	BX			;  IN THE STACK, THE COMMAND BLOCK IS:
	PUSH	CX			;   @CMD_BLOCK == BYTE PTR [BP]-8
	PUSH	DX
	PUSH	DS
	PUSH	ES
	PUSH	SI
	PUSH	DI
	OR	AH,AH			; CHECK FOR RESET
	JNZ	A5
	MOV	DL,80H			; FORCE DRIVE 80 FOR RESET
A5:	CALL	DISK_IO_CONT		; PERFORM THE OPERATION
	CALL	DDS			; ESTABLISH SEGMENT
	MAY	AH,@DISK_STATUS1	; GET STATUS FROM OPERATION
	CMP	AH,1			; SET THE CARRY FLAG TO INDICATE
	CMC				; SUCCESS OR FAILURE
	POP	DI			; RESTORE REGISTERS
	POP	SI
	POP	ES
	POP	DS
	POP	DX
	POP	CX
	POP	BX
	LEAVE				; ADJUST (SP) AND RESTORE (BP)
	RET	2			; THROW AWAY SAVED FLAGS
DISK_IO	ENDP

M1	LABEL	WORD			; FUNCTION TRANSFER TABLE
	DW	DISK_RESET		; 000H
	DW	RETURN_STATUS		; 001H
	DW	DISK_READ		; 002H
	DW	DISK_WRITE		; 003H
	DW	DISK_VERF		; 004H
	DW	FMT_TRK			; 005H
	DW	BAD_COMMAND		; 006H  FORMAT BAD SECTORS
	DW	BAD_COMMAND		; 007H  FORMAT DRIVE
	DW	BAD_COMMAND		; 008H  RETURN PARAMETERS
	DW	INIT_DRV		; 009H
	DW	RD_LONG			; 00AH
	DW	WR_LONG			; 00BH
	DW	DISK_SEEK		; 00CH
	DW	DISK_RESET		; 00DH
	DW	BAD_COMMAND		; 00EH  READ BUFFER
	DW	BAD_COMMAND		; 00FH  WRITE BUFFER
	DW	TST_RDY			; 010H
	DW	HDISK_RECAL		; 011H
	DW	BAD_COMMAND		; 012H  MEMORY DIAGNOSTIC
	DW	BAD_COMMAND		; 013H  DRIVE DIAGNOSTIC
	DW	CTLR_DIAGNOSTIC		; 014H  CONTROLLER DIAGNOSTIC
M1L	EQU	$-M1

DISK_IO_CONT	PROC	NEAR
	CALL	DDS			; ESTABLISH SEGMENT
	CMP	AH,01H			; RETURN STATUS
	JNZ	SU0
	JMP	RETURN_STATUS
SU0:
	MOV	@DISK_STATUS1,0		; RESET THE STATUS INDICATOR
	PUSH	BX			; SAVE DATA ADDRESS
	MOV	BL,@HF_NUM		; GET NUMBER OF DRIVES
	PUSH	AX
	AND	DL,7FH			; GET DRIVE AS 0 OR 1
	CMP	BL,DL
	JBE	BAD_COMMAND_POP		; INVALID DRIVE
	PUSH	ES
	CALL	GET_VEC			; GET DISK PARAMETERS
	MOV	AX,WORD PTR ES:[BX][5]	; GET WRITE PRE-COMPENSATION CYLINDER
	SHR	AX,2
	MOV	@CMD_BLOCK,AL
	MOV	AL,BYTE PTR ES:[BX][8]	; GET CONTROL BYTE MODIFIER
	PUSH	DX
	MOV	DX,HF_REG_PORT
	OUT	DX,AL			; SET EXTRA HEAD OPTION
	POP	DX
	POP	ES
	MOV	AH,@CONTROL_BYTE	; SET EXTRA HEAD OPTION IN
	AND	AH,0C0H			; CONTROL BYTE
	OR	AH,AL
	MOV	@CONTROL_BYTE,AH
	POP	AX
	MOV	@CMD_BLOCK+1,AL		; SECTOR COUNT
	PUSH	AX
	MOV	AL,CL			; GET SECTOR NUMBER
	AND	AL,3FH
	MOV	@CMD_BLOCK+2,AL
	MOV	@CMD_BLOCK+3,CH		; GET CYLINDER NUMBER
	MOV	AL,CL
	MOV	@CMD_BLOCK+4,AL		; CYLINDER HIGH ORDER 2 BITS
	MOV	AL,DL			; DRIVE NUMBER
	SHL	AL,4
	AND	DH,0FH			; HEAD NUMBER
	OR	AL,DH
	OR	AL,80H OR 20H		; ECC AND 512 BYTE SECTORS
	MOV	@CMD_BLOCK+5,AL		; ECC/SIZE/DRIVE/HEAD
	POP	AX
	PUSH	AX
	MOV	AL,AH			; GET INTO LOW BYTE
	XOR	AH,AH			; ZERO HIGH BYTE
	SAL	AX,1			; *2 FOR TABLE LOOKUP
	MOV	SI,AX			; PUT INTO SI FOR BRANCH
	CMP	AX,M1L			; TEST WITHIN RANGE
	JNB	BAD_COMMAND_POP
	POP	AX			; RESTORE AX
	POP	BX			; AND DATA ADDRESS
	PUSH	CX
	PUSH	AX			; ADJUST ES:BX
	MOV	CX,BX			; GET 3 HIGH ORDER NIBBLES OF BX
	SHR	CX,4
	MOV	AX,ES
	ADD	AX,CX
	MOV	ES,AX
	AND	BX,000FH		; ES:BX CHANGED TO ES:000X
	POP	AX
	POP	CX
	JMP	WORD PTR CS:[SI + OFFSET M1]
BAD_COMMAND_POP:
	POP	AX
	POP	BX
BAD_COMMAND:
	MOV	@DISK_STATUS1,BAD_CMD	; COMMAND ERROR
	MOV	AL,0
	RET
DISK_IO_CONT	ENDP

;------------------------------------------
;	RESET THE DISK SYSTEM (AH=00H)	:
;------------------------------------------

DISK_RESET	PROC	NEAR
	CLI
	IN	AL,INTB01		; GET THE MASK REGISTER
	JMP	$+2
	AND	AL,0BFH			; ENABLE FIXED DISK INTERRUPT
	OUT	INTB01,AL
	STI				; START INTERRUPTS
	MOV	AL,04H
	MOV	DX,HF_REG_PORT
	OUT	DX,AL			; RESET
	MOV	CX,10			; DELAY COUNT
DRD:	DEC	CX
	JNZ	DRD			; WAIT 4.8 MICRO-SEC
	MOV	AL,@CONTROL_BYTE
	AND	AL,0FH			; SET HEAD OPTION
	OUT	DX,AL			; TURN RESET OFF
	CALL	NOT_BUSY
	JNZ	DRERR			; TIME OUT ON RESET
	MOV	DX,HF_PORT+1
	IN	AL,DX			; GET RESET STATUS
	CMP	AL,1
	JNZ	DRERR			; BAD RESET STATUS
	AND	@CMD_BLOCK+5,0EFH	; SET TO DRIVE 0
	SUB	DL,DL
	CALL	INIT_DRV		; SET MAX HEADS
	CALL	HDISK_RECAL		; RECAL TO RESET SEEK SPEED
	CMP	@HF_NUM,1		; CHECK FOR DRIVE
	JBE	DRE
	OR	@CMD_BLOCK+5,010H	; SET TO DRIVE
	MOV	DL,1
	CALL	INIT_DRV		; SET MAX HEADS
	CALL	HDISK_RECAL		; RECAL TO RESET SEEK SPEED
DRE:	MOV	@DISK_STATUS1,0		; IGNORE ANY SET UP ERRORS
	RET
DRERR:	MOV	@DISK_STATUS1,BAD_RESET	; CARD FAILED
	RET
DISK_RESET	ENDP

;------------------------------------------
;	DISK STATUS ROUTINE (AH = 01H)	:
;------------------------------------------

RETURN_STATUS	PROc	NEAR
	MOV	AL,@DISK_STATUS1	; OBTAIN PREVIOUS STATUS
	MOV	@DISK_STATUS1,0		; RESET STATUS
	RET
RETURN_STATUS 	ENDP

;------------------------------------------
;	DISK READ ROUTINE     (AH = 02H)	:
;------------------------------------------

DISK_READ	PROC 	NEAR
	MOV	@CMD_BLOCK+6, READ_CMD
	JMP	COMMANDI
DISK_READ	ENDP

;------------------------------------------
;	DISK WRITE ROUTINE    (AH = 03H)	:
;------------------------------------------

DISK_WRITE	PROC	NEAR
	MOV	@CMD_BLOCK+6,WRITE_CMD
	JMP 	COMMANDO
DISK_WRITE 	ENDP

:------------------------------------------
;	DISK VERIFY           (AH = 04H)	:
;------------------------------------------

DISK_VERF	PROC	NEAR
	MOV	@CMD_BLOCK+6,VERIFY_CMD
	CALL	COMMAND
	JNZ	VERF_EXIT		; CONTROLLER STILL BUSY
	CALL	WAIT
	JNZ	VERF EXIT		; TIME OUT
	CALL	CHECK_STATUS
VERF_EXIT:
	RET
DISK_VERF 	ENDP

;------------------------------------------
;	FORMATTING            (AH = 05H)	:
;------------------------------------------

FMT_TRK 	PROC	NEAR		; FORMAT TRACK  (AH = 005H)
	MOV	@CMD_BLOCK+6,FMTTRK_CMD
	PUSH	ES
	PUSH	BX
	CALL	GET_VEC			; GET DISK PARAMETERS ADDRESS
	MOV	AL,ES:[BX][14]		; GET SECTORS/TRACK
	MOV	@CMD_BLOCK+1,AL		; SET SECTOR COUNT IN COMMAND
	POP	BX
	POP	ES
FMT_TRK	JMP	CMD_OF			; GO EXECUTE THE COMMAND
	ENDP

;------------------------------------------
;	READ DASD TYPE        (AH = 15H)	: 
;------------------------------------------

READ_DASD_TYPE	LABEL	NEAR
READ_D_T		PROC	FAR	; GET DRIVE PARAMETERS
	PUSH	DS			; SAVE REGISTERS
	PUSH	ES
	PUSH	BX
	ASSUME 	DS:DATA
	CALL	DDS			; ESTABLISH ADDRESSING
	MOV	@DISK_STATUS1,0
	MOV	BL,@HF_NUM		; GET NUMBER OF DRIVES
	AND	DL,7FH			; GET DRIVE NUMBER
	CMP	BL,DL
	JBE	RDT_NOT_PRESENT		; RETURN DRIVE NOT PRESENT
	CALL	GET_VEC			; GET DISK PARAMETER ADDRESS
	MOV	AL,ES:[BX][2]		; HEADS
	MOV	CL,ES:[BX][14]
	IMUL	CL			; NUMBER OF SECTORS
	MOV	CX,ES:[BX]		; MAX NUMBER OF CYLINDERS
	DEC	CX			; LEAVE ONE FOR DIAGNOSTICS
	IMUL	CX			; NUMBER OF SECTORS
	MOV	CX,DX			; HIGH ORDER HALF
	MOV	DX,AX			; LOW ORDER HALF
	SUB	AX,AX
	MOV	AH,03H			; INDICATE FIXED DISK
RDT2:	POP	BX			; RESTORE REGISTERS
	POP	ES
	POP	DS
	CLC				; CLEAR CARRY
	RET	2
RDT_NOT_PRESENT:
	SUB	AX,AX			; DRIVE NOT PRESENT RETURN
	MOV	CX,AX			; ZERO BLOCK COUNT
	MOV	DX,AX
	JMP	RDT2
READ_D_T	ENDP


;------------------------------------------
;	GET PARAMETERS        (AH = 08H)	:
;------------------------------------------

GET_PARM_N	LABEL	NEAR
GET_PARM	PROC	FAR		; GET DRIVE PARAMETERS
	PUSH	DS			; SAVE REGISTERS
	PUSH	ES
	PUSH	BX
	ASSUME	DS:ABS0
	MOV	AX,ABS0			; ESTABLISH ADDRESSING
	MOV	DS,AX
	TEST	DL,1			; CHECK FOR DRIVE 1
	JZ	G0
	LES	BX,@HF1_TBL_VEC
	JMP	SHORT G1
G0:	LES	BX,@HF_TBL_VEC
	ASSUME	DS:DATA
G1:
	CALL	DDS			; ESTABLISH SEGMENT
	SUB	DL,80H
	CMP	DL,MAX_FILE		; TEST WITHIN RANGE
	JAE	G4		
	MOV	@DISK_STATUS1,0
	MOV	AX,ES:[BX]		; MAX NUMBER OF CYLINDERS
	SUB	AX,2			; ADJUST FOR 0-N
	MOV	CH,AL
	AND	AX,0300H		; HIGH TWO BITS OF CYLINDER
	SHR	AX,1
	SHR	AX,1
	OR	AL,ES:[BX][14]		; SECTORS
	MOV	CL,AL
	MOV	DH,ES:[BX][2]		; HEADS
	DEC	DH			; 0-N RANGE
	MOV	DL,@HF_NUM		; DRIVE COUNT
	SUB	AX,AX
G5:
	POP	BX			; RESTORE REGISTERS
	POP	ES
	POP	DS
	RET	2
G4:
	MOV	@DISK_STATUS1,INIT_FAIL	; OPERATION FAILED
	MOV	AH,INIT_FAIL
	SUB	AL,AL
	SUB	DX,DX
	SUB	CX,CX
	STC				; SET ERROR FLAG
	JMP	G5
GET_PARM	ENDP

;------------------------------------------
;	INITIALIZE DRIVE      (AH = 09H)	:
;------------------------------------------

INIT_DRV	PROC	NEAR
	MOV	@CMD_BLOCK+6,SET_PARM_CMD
	CALL	GET_VEC			; ES:BX -> PARAMETER BLOCK
	MOV	AL,ES:[BX][2]		; GET NUMBER OF HEADS
	DEC	AL			; CONVERT TO 0-INDEX
	MOV	AH,@CMD_BLOCK+5		; GET SDH REGISTER
	AND	AH,0F0H			; CHANCE HEAD NUMBER
	OR	AH,AL			;  TO MAX HEAD
	MOV	@CMD_BLOCK+5,AH
	MOV	AL,ES:[BX][14]		; MAX SECTOR NUMBER
	MOV	@CMD_BLOCK+1,AL
	SUB	AX,AX
	MOV	@CMD_BLOCK+3,AL		; ZERO FLAGS
	CALL	COMMAND			; TELL CONTROLLER
	JNZ	INIT_EXIT		; CONTROLLER BUSY ERROR
	CALL	NOT_BUSY		; WAIT FOR IT TO BE DONE
	JNZ	INIT_EXIT		; TIME OUT
	CALL	CHECK_STATUS
INIT_EXIT:
	RET
INIT_DRV	ENDP

;------------------------------------------
;	READ LONG             (AH = 0AH)	:
;------------------------------------------

RD_LONG	PROC	NEAR
	MOV	@CMD_BLOCK+6,READ_CMD OR ECC_MODE
	JMP 	COMMANDI
RD_LONG 	ENDP

;------------------------------------------
;	WRITE LONG            (AH = 0BH)	:
;------------------------------------------

WR_LONG	PROC	NEAR
	MOV	@CMD_BLOCK+6,WRITE_CMD OR ECC_MODE
	JMP 	COMMANDO
WR_LONG 	ENDP

;------------------------------------------
;	SEEK                  AH = 0CH)	:
;------------------------------------------

DISK_SEEK	PROC	NEAR
	MOV	@CMD_BLOCK+6,SEEK_CMD
	CALL	COMMAND
	JNZ	DS_EXIT			; CONTROLLER BUSY ERROR
	CALL	WAIT
	JNZ	DS_EXIT			; TIME OUT ON SEEK
	CALL	CHECK_STATUS
	CMP	@DISK_STATUS1,BAD_SEEK
	JNE	DS_EXIT
	MOV	@DISK_STATUS1,0
DS_EXIT: 
	RET
DISK_SEEK	ENDP

;------------------------------------------
;	TEST DISK READY       (AH = 10H)	:
;------------------------------------------

TST_RDY	PROC	NEAR	; WAIT FOR CONTROLLER
	CALL	NOT_BUSY
	JNZ	TR_EX
	MOV	AL,CMD_BLOCK+5		; SELECT DRIVE
	MOV	DX,HF_PORT+6
	OUT	DX,AL
	CALL	CHECK_ST		; CHECK STATUS ONLY
	JNZ	TR_EX
	MOV	@DISK_STATUS1,0		; WIPE OUT DATA CORRECTED ERROR
TR_EX:	RET
TST_RDY	ENDP

;------------------------------------------
;	RECALIBRATE           (AH = 10H) :
;------------------------------------------

HDISK_RECAL	PROC	NEAR
	MOV	@CMD_BLOCK+6,RECAL_CMD
	CALL	COMMAND			; START THE OPERATION
	JNZ	RECAL_EXIT		; ERROR
	CALL	WAIT			; WAIT FOR COMPLETION
	JZ	RECAL_X			; TIME OUT ONE OK ?
	CALL	WAIT			; WAIT FOR COMPLETION LONGER
	JNZ	RECAL_EXIT		; TIME OUT TWO TIMES IS ERROR
RECAL_X:
	CALL	CHECK STATUS
	CMP	@DISK_STATUS1,BAD_SEEK	; SEEK NOT COMPLETE
	JNE	RECAL_EXIT		; IS OK
	MOV	@DISK_STATUS1,0
RECAL_EXIT:
	CMP	@DISK_STATUS1,0
	RET
HDISK_RECAL	ENDP

;------------------------------------------
;	CONTROLLER DIAGNOSTIC (AH = 14H)	:
;------------------------------------------

CTLR_DIAGNOSTIC	PROC 	NEAR
	CLI				; DISABLE INTERRUPTS WHILE CHANGING M
	IN	AL,INTB01		; TURN ON SECOND INTERRUPT CHIP
	AND	AL,0BFH
	JMP	$+2
	OUT	INTB01,AL
	IN	AL,INTA01		; LET INTERRUPTS PASS THRU TO
	AND	AL,0FBH			; SECOND CHIP
	JMP	$+2
	OUT	INTA01,AL
	STI
	CALL	NOT_BUSY		; WAIT FOR CARD
	JNZ	CD_ERR			; BAD CARD
	MOV	DX,HF_PORT+7
	MOV	AL,DIAG_CMD		; START DIAGNOSE
	OUT	DX,AL
	CALL	NOT_BUSY		; WAIT FOR IT TO COMPLETE
	MOV	AH,TIME_OUT
	JNZ	CD_EXIT			; TIME OUT ON DIAGNOSTIC
	MOV	DX,HF_PORT+1		; GET ERROR REGISTER
	IN	AL,DX
	MOV	@HF_ERROR,AL		; SAVE IT
	MOV	AH,0
	CMP	AL,1			; CHECK FOR ALL OK
	JE	SHORT CD_EXIT
CD_ERR:	MOV	AH,BAD_CNTLR
CD_EXIT: 
	MOV	@DISK_STATUS1,AH
	RET
CTLR_DIAGNOSTIC	ENDP

;------------------------------------------
; COMMANDI		:
;	REPEATEDLY INPUTS DATA TILL	:
;	NSECTOR RETURNS ZERO	:
;------------------------------------------

COMMANDI:
	CALL	CHECK_DMA		; CHECK 64K BOUNDARY ERROR
	JC	CMD_ABORT
	MOV	DI,BX
	CALL	COMMAND			; OUTPUT COMMAND
	JNZ	CMD_ABORT

CMD_I1:
	CALL	WAIT			; WAIT FOR DATA REQUEST INTERRUPT
	JNZ	TM_OUT			; TIME OUT
	MOV	CX,256D			; SECTOR SIZE IN WORDS
	MOV	DX,HF_PORT
	CLI
	CLD
	REP	INSW			; GET THE SECTOR
	STI
	TEST	@CMD_BLOCK+6,ECC_MODE	; CHECK FOR NORMAL INPUT
	JZ	CMD_I3
	CALL	WAIT_DRQ		; WAIT FOR DATA REQUEST
	JC	TM_OUT
	MOV	DX,HF_PORT
	MOV	CX,4			; GET ECC BYTES
CMD_I2:	IN	AL,DX
	MOV	ES:BYTE PTR [DI],AL	; GO SLOW FOR BOARD
	INC	DI
	LOOP	CMD_I2
CMD_I3:	CALL	CHECK_STATUS
	JNZ	CMD_ABORT		; ERROR RETURNED
	DEC	@CMD_BLOCK+1		; CHECK FOR MORE
	JNZ	SHORT CMD_I1
CMD_ABORT:
TM_OUT: 	RET

;------------------------------------------
; COMMANDO		:
;	REPEATEDLY OUTPUTS DATA TILL	:
;	NSECTOR RETURNS ZERO	:
;------------------------------------------
COMMANDO:
	CALL	CHECK_DMA		; CHECK 64K BOUNDARY ERROR
	JC	CMD_ALORT
CMD_OF:	MOV	SI,BX
	CALL	COMMAND			; OUTPUT COMMAND
	JNZ	CMD_ABORT
	CALL	WAIT_DRQ		; WAIT FOR DATA REQUEST
	JC	TM_OUT			; TOO LONG
CMD_O1:	PUSH	DS
	PUSH	ES			; MOVE ES TO DS
	POP	DS
	MOV	CX,256D			; PUT THE DATA OUT TO THE CARD
	MOV	DX,HF_PORT
	CLI
	CLD
	REP	OUTSW
	STI
	POP	DS			; RESTORE DS
	TEST	@CMD_BLOCK+6,ECC_MODE	; CHECK FOR NORMAL OUTPUT
	JZ	CMD_O3
	CALL	WAIT_DRQ		; WAIT FOR DATA REQUEST
	JC	TM_OUT
	MOV	DX,HF_PORT
	MOV	CX,4			; OUTPUT THE ECC BYTES
CMD_O2:	MOV	AL,ES:BYTE PTR [SI]
	OUT	DX,AL
	INC	SI
	LOOP	CMD_O2
CMD_O3:
	CALL	WAIT			; WAIT FOR SECTOR COMPLETE INTERRUPT
	JNZ	TM_OUT			; ERROR RETURNED
	CALL	CHECK_STATUS
	JNZ	CMD_ABORT
	TEST	@HF_STATUS,ST_DRQ	; CHECK FOR MORE
	JNZ	SHORT CMD_O1
	RET

;------------------------------------------------------
; COMMAND				:
;	THIS ROUTINE OUTPUTS THE COMMAND BLOCK		:
; OUTPUT					:
;	BL = STATUS			:
;	BH = ERROR REGISTER			:
;------------------------------------------------------

COMMAND	PROC	NEAR
	PUSH	AX			; WAIT FOR SEEK COMPLETE AND READY
	MOV	CX,DELAY_2		; SET INITIAL DELAY BEFORE TEST
COMMAND1:
	PUSH	CX			; SAVE LOOP COUNT
	CALL	TST_RDY			; CHECK DRIVE READY
	POP	CX
	JZ	COMMAND2		; DRIVE IS READY
	CMP	@DISK_STATUS1,TIME_OUT	; TST_RDY TIMED OUT--GIVE UP
	JZ	CMD_TIMEOUT
	LOOP	COMMAND1		; KEEP TRYING FOR A WHILE
	JMP	SHORT COMMAND4		; ITS NOT GOING TO GET READY
COMMAND2:
	POP	BX
	PUSH	DI
	MOV	@HF_INT_FLAG,0		; RESET INTERRUPT FLAG
	CLI				; INHIBIT INTERRUPTS WHILE CHANGING MASK
	IN	AL,INTB01		; TURN ON SECOND INTERRUPT CHIP
	AND	AL,0BFH
	JMP	$+2
	OUT	INTB01,AL
	IN	AL,INTA01		; LET INTERRUPTS PASS THRU TO
	AND	AL,0FBH			;  SECOND CHIP
	JMP	$+2
	OUT	INTA01,AL
	STI
	XOR	DI,DI			; INDEX THE COMMAND TABLE
	MOV	DX,HF_PORT+1		; DISK ADDRESS
	TEST	@CONTROL_BYTE,0C0H	; CHECK FOR RETRY SUPPRESSION
	JZ	COMMAND3
	MOV	AL,@CMD_BLOCK+6		; YES-GET OPERATION CODE
	AND	AL,0F0H			; GET RID OF MODIFIERS
	CMP	AL,20H			; 20H-40H IS READ, WRITE, VERIFY
	JE	COMMAN03
	CMP	AL,40H
	JA	COMMANOJ
	OR	CMD_BLOCK+6,NO_RETRIES 	; VALID OPERATION FOR RETRY SUPPRESS
COMMAND3:
	MOV	AL,[@CMD_BLOCK+DI]	; GET THE COMMAND STRING BYTE
	OUT	DX,AL			; GIVE IT TO CONTROLLER
	INC	DI			; NEXT BYTE IN COMMAND BLOCK
	INC	DX			; NEXT DISK ADAPTER REGISTER
	CMP	DX,HF_PORT+8		; ALL DONE?
	JNZ	COMMAND3		; NO--GO DO NEXT ONE
	POP	DI
	RET				; ZERO FLAG IS SET
CMD_TIMEOUT:
	MOV	@DISK_STATUS1,BAD_CNTLR
COMMAND4:
	POP	EX
	CMP	@DISK_STATUS1,0		; SET CONDITION CODE FOR CALLER
	RET
COMMAND 	ENDP

;------------------------------------------
;	WAIT FOR INTERRUPT	:
; -----------------------------------------
WAIT	PROC	NEAR
	STI				; MAKE SURE INTERRUPTS ARE ON
	SUB	CX,CX			; SET INITIAL DELAY BEFORE TEST
	CLC
	MOV	AX,9000H		; DEVICE WAIT INTERRUPT
	INT	15H
	JC	WT2			; DEVICE TIMED OUT
	MOV	DL,DELAY_1		; SET DELAY COUNT

;----- 	WAIT LOOP

WT1:	TEST	@HF_INT_FLAG,80H	; TEST FOR INTERRUPT
	LOOPZ 	WT1
	JNZ	WT3			; INTERRUPT--LETS GO
	DEC	BL
	JNZ	WT1			; KEEP TRYING FOR A WHILE

WT2:	MOV	@DISK_STATUS1,TIME_OUT	; REPORT TIME OUT ERROR
	JMP	SHORT WT4
WT3:	MOV	@DISK_STATUS1,0
	MOV	@HF_INT_FLAG,0
WT4:	CMP	@DISK_STATUS1,0	; SET CONDITION CODE FOR CALLER
	RET
WAIT	ENDP

;------------------------------------------
;	WAIT FOR CONTROLLER NOT BUSY	:
;------------------------------------------
NOT_BUSY	PROC	NEAR
	STI				; MAKE SURE INTERRUPTS ARE ON
	PUSH	BX
	MOV	BL,DELAY_1
	SUB	CX,CX			; SET INITIAL DELAY BEFORE TEST
	MOV	DX,HF_PORT+7
NB1:	IN	AL,DX			; CHECK STATUS
	TEST	AL,ST_BUSY
	LOOPNZ	NB1
	JZ	NB2			; NOT BUSY--LETS GO
	DEC	BL
	JNZ	NB1			; KEEP TRYING FOR A WHILE

	MOV	@DISK_STATUS1,TIME_OUT	; REPORT TIME OUT ERROR
	JMP	SHORT NB3
NB2;	MOV	@DISK_STATUS1,0
NB3:	POP	BX
	CMP	@DISK_STATUS1,0		; SET CONDITION CODE FOR CALLER
	RET
NOT_BUSY	ENDP

;------------------------------------------
;	WAIT FOR DATA REQUEST	:
;------------------------------------------
WAIT_DRQ		PROC	NEAR
	MOV	CX,DELAY_ 3
	MOV	DX,HF_PORT+7
WQ_1:	IN	AL,DX			; GET STATUS
	TEST	AL,ST_DRQ		; WAIT FOR DRQ
	JNZ	WQ_OK
	LOOP	WQ_1			; KEEP TRYING FOR A SHORT WHILE
	MOV	@DISK_STATUS1,TIME_OUT	; ERROR
	STC
	RET
WQ_OK:	CLC
	RET
WAIT-DRO	ENDP

;------------------------------------------
;	CHECK FIXED DISK STATUS	:
;------------------------------------------
CHECK-STATUS	PROC	NEAR
	CALL	CHECK_ST		; CHECK THE STATUS BYTE
	JNZ	CHECK_S1		; AN ERROR WAS FOUND
	TEST	AL,ST_ERROR		; WERE THERE ANY OTHER ERRORS
	JZ	CHECK_S1		; NO ERROR REPORTED
	CALL	CHECK_ER		; ERROR REPORTED
CHECK_S1:
	CMP	@DISK_STATUS1,0		; SET STATUS FOR CALLER
	RET
CHECK STATUS	ENDP

;------------------------------------------
;	CHECK FIXED DISK STATUS BYTE	:
;------------------------------------------
CHECK ST	PROC	NEAR
	MOV	DX,HF_PORT+7		; GET THE STATUS
	IN	AL,DX
	MOV	@HF_STATUS,AL
	MOV	AH,0
	TEST	AL,ST_BUSY		; IF STILL BUSY
	JNZ	CKST_EXIT		; REPORT OK
	MOV	AH,WRITE_FAULT
	TEST	AL,ST_WRT_FLT		; CHECK FOR WRITE FAULT
	JNZ	CKST_EXIT
	MOV	AH,NOT_RDY
	TEST	AL,ST_READY		; CHECK FOR NOT READY
	JZ	CKST_EXIT
	MOV	AH,BAD_SEEK
	TEST	AL,ST_SEEK_COMPL	; CHECK FOR SEEK NOT COMPLETE
	JZ	CKST_EXIT
	MOV	AH,DATA_CORRECTED
	TEST	AL,ST_CORRCTD		; CHECK FOR CORRECTED ECC
	JNZ	CKST_EXIT
	MOV	AH,0
CKST_EXIT:
	MOV	@DISK_STATUS1,AH	; SET ERROR FLAG
	CMP	AH,DATA_CORRECTED	; KEEP GOING WITH DATA CORRECTED
	JZ	CKST_EX1
	CMP	AH,0
CKST_EX1:
	RET
CHECK_ST	ENDP

;------------------------------------------
;	CHECK FIXED DISK ERROR REGISTER	:
;------------------------------------------
CHECK_ER	PROC	NEAR
	MOV	DX,HF_PORT+1		; GET THE ERROR REGISTER
	IN	AL,DX
	MOV	@HF_ERROR,AL
	PUSH	BX
	MOV	CX,8			; TEST ALL 8 BITS
CK1:	SHL	AL,1			; MOVE NEXT ER OR BIT TO CARRY
	JC	CK2			; FOUND THE ERROR
	LOOP	CK1			; KEEP TRYING
CK2:	MOV	BX,OFFSET ERR_TBL	; COMPUTE ADDRESS OF
	ADD	BX,CX	; ERROR CODE
	MOV	AH,BYTE PTR CS:[BX]	; GET ERROR CODE
CKEX:	MOV	@DISK_STATUS1,AH	; SAVE ERROR CODE
	POP	BX
	CMP	AH,0
	RET

ERR_TBL	DB	NO_ERR
	DB	BAD_ADDR_MARK,BAD_SEEK,BAD_CMD,UNDEF_ERR
	DB	RECORD_NOT_FND,UNDEF_ERR,BAD_ECC,BAD_SECTOR
CHECK_ER	ENDP

;--------------------------------------------------------
; CHECK_DMA				  :
;  -CHECK ES:BX AND # SECTORS TO MAKE SURE THAT IT WILL :
;   FIT WITHOUT SEGMENT OVERFLOW.			  :
;  -ES:BX HAS BEEN REVISED TO THE FORMAT SSSS:000X	  :
;  -OK IF # SECTORS < 80H (7FH IF LONG READ OR WRITE)	  :
;  -OK IF # SECTORS = 80H (7FH) AND BX <= 00H (04H)	  :
;  -ERROR OTHERWISE			  :
;--------------------------------------------------------
CHECK_DMA	PROC	NEAR
	PUSH	AX			; SAVE REGISTERS
	MOV	AX,8000H		; AH = MAX # SECTORS  AL = MAX OFFSET
	TEST	@CMD_BLOCK+6,ECC_MODE
	JZ	CKD1
	MOV	AX,7F04H		; ECC IS 4 MORE BYTES
CKD1:	CMP	AH,@CMD_BLOCK+1		; NUMBER OF SECTORS
	JA	CKDOK			; IT WILL FIT
	JB	CKDERR			; TOO MANY
	CMP	AL,BL			; CHECK OFFSET ON MAX SECTORS
	JB	CKDERR			; ERROR
CKDOK:	CLC				; CLEAR CARRY
	POP	AX
	RET				; NORMAL RETURN
CKDERR:	STC				; INDICATE ERROR
	MOV	@DISK_STATUS1,DMA_BOUNDARY
	POP	AX
	RET
CHECK_DMA	ENDP



;------------------------------------------
;	SET UP ES:BX-> DISK PARMS	:
;------------------------------------------
GET_VEC	PROC	NEAR
	SUB	AX,AX			; GET DISK PARAMETER ADDRESS
	MOV	ES,AX
	ASSUME	ES:ABS0
	TEST	DL,1
	JZ	GV_0
	LES	BX,@HF1_TBL_VEC		; ES:BX -> DRIVE PARAMETERS
	JMP	SHORT GV_EXIT
GV_0:
	LES	BX,@HF_TBL_VEC		; ES:BX -> DRIVE PARAMETERS
GV_EXIT:
	RET
GET_VEC	ENDP

;--- HARDWARE INT 76H - ( IRQ LEVEL 14 ) --------------------------------
;								:
;	FIXED DISK INTERRUPT ROUTINE						:
;								:
;------------------------------------------------------------------------

HD_INT	PROC	NEAR
	PUSH	AX
	PUSH	DS
	CALL	DDS
	MOV	@HF_INT_FLAG,0FFH	; ALL DONE
	MOV	AL,EOI			; NON-SPECIFIC END OF INTERRUPT
	OUT	INTB00,AL		; FOR CONTROLLER #2
	JMP	$+2			; WAIT
	OUT	INTA00,AL		; FOR CONTROLLER #1
	POP	DS 
	STI				; RE-ENABLE INTERRUPTS
	MOV	AX,9100H		; DEVICE POST
	INT	15H			;  INTERRUPT
	POP	AX
	IRET				; RETURN FROM INTERRUPT

HD_INT	ENDP

	DB	'06/10/85'		; RELEASE MARKER

CODE	ENDS
	END
