PAGE 118,121
TITLE DSKETTE -- 04/21/86  DISKETTE BIOS
.286C
.XLIST
INCLUDE DSEG.INC
INCLUDE POSTEQU.INC
.LIST
SUBTTL (DSK1.ASM)
.LIST
;-- INT  13H -------------------------------------------------------------------
; DISKETTE I/O
;	THIS INTERFACE PROVIDES DISK ACCESS TO THE 5.25 INCH 360 KB,
;	1.2 MB, 720 KB, AND 1.44 MB DISKETTE DRIVES.
; INPUT
;	(AH)=00H  RESET DISKETTE SYSTEM
;		  HARD RESET TO NEC. PREPARE COMMAND, RECALIBRATE REQUIRED
;		  ON ALL DRIVES
;-------------------------------------------------------------------------------
;	(AH)=01H  READ THE STATUS OF THE SYSTEM INTO (AH)
;		  @DISKETTE_STATUS FROM LAST OPERATION IS USED
;-------------------------------------------------------------------------------
;	REGISTERS FOR READ/WRITE/VERIFY/FORMAT
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;	(DH) - HEAD NUMBER (0-1 ALLOWED, NOT VALUE CHECKED)
;	(CH) - TRACK NUMBER (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 TRACK NUMBER
;		 320/360   320/360	     0-39
;		 320/360    1.2M	     0-39
;		  1.2M	    1.2M	     0-79
;		  720K	    720K	     0-79
;		  1.44M     1.44M	     0-79
;	(CL) - SECTOR NUMBER (NOT VALUE CHECKED, NOT USED FOR FORMAT)
;		  MEDIA     DRIVE	 SECTOR NUMBER
;		 320/360   320/360	      1-8/9
;		 320/360    1.2M	      1-8/9
;		  1.2M	    1.2M	      1-15
;		  720K	    720K	      1-9
;		  1.44M     1.44M	      1-18
;	(AL) - NUMBER OF SECTORS (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 MAX NUMBER OF SECTORS
;		 320/360   320/360		 8/9
;		 320/360    1.2M		 8/9
;		  1.2M	    1.2M		 15
;		  720K	    720K		 9
;		  1.44M     1.44M		 18
;
;	(ES:BX) - ADDRESS OF BUFFER ( REQUIRED FOR VERIFY)
;
;-------------------------------------------------------------------------------
;	(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
;		  (ES,BX) MUST POINT TO THE COLLECTION OF DESIRED ADDRESS FIELDS
;		  FOR THE TRACK.  EACH FIELD IS COMPOSED OF 4 BYTES, (C,H,R,N),
;		  WHERE C = TRACK NUMBER, H=HEAD NUMBER, R = SECTOR NUMBER,
;		  N= NUMBER OF BYTES PER SECTOR (00=128,01=256,02=512,03=1024).
;		  THERE MUST BE ONE ENTRY FOR EVERY SECTOR ON THE TRACK.
;		  THIS INFORMATION IS USED TO FIND THE REQUESTED SECTOR DURING
;		  READ/WRITE ACCESS.
;
;		  PRIOR TO FORMATTING A DISKETTE, IF THERE EXISTS MORE THAN
;		  ONE SUPPORTED MEDIA FORMAT TYPE WITHIN THE DRIVE IN QUESTION,
;		  THEN "SET DASD TYPE" (INT 13H, AH = 17H) OR "SET MEDIA TYPE"
;		  (INT 13H, AH = 18H) MUST BE CALLED TO SET THE DISKETTE TYPE
;		  THAT IS TO BE FORMATED. IF "SET DASD TYPE" OR "SET MEDIA TYPE"
;		  IS NOT CALLED, THE FORMAT ROUTINE WILL ASSUME THE MEDIA FORMAT
;		  TO BE THE MAXIMUM CAPACITY OF THE DRIVE.
;
;		  THESE PARAMETERS OF DISK_BASE MUST BE CHANGED IN ORDER TO
;		  FORMAT THE FOLLOWING MEDIAS:
;		  ---------------------------------------------
;		  : MEDIA  :	 DRIVE	    : PARM 1 : PARM 2 :
;		  ---------------------------------------------
;		  : 320K   : 320K/360K/1.2M :  50H   :	 8    :
;		  : 360K   : 320K/360K/1.2M :  50H   :	 9    :
;		  : 1.2M   : 1.2M	    :  54H   :	15    :
;		  : 720K   : 720K/1.44M     :  50H   :	 9    :
;		  : 1.44M  : 1.44M	    :  6CH   :	18    :
;		  ---------------------------------------------
;		  NOTES: - PARM 1 = GAP LENGTH FOR FORMAT
;			 - PARM 2 = EOT (LAST SECTOR ON TRACK)
;			 - DISK_BASE IS POINTED TO BY DISK POINTER LOCATED
;			   AT ABSOLUTE ADDRESS 0:78.
;			 - WHEN FORMAT OPERATIONS ARE COMPLETE, THE PARAMETERS
;			   SHOULD BE RESTORED TO THEIR RESPECTIVE INITIAL VALUES
;
;-------------------------------------------------------------------------------
;	(AH)=08H  READ DRIVE PARAMETERS
;	REGISTERS
;	  INPUT
;	    (DL) - DRIVE NUMBER (0-1 ALLOWED. VALUE CHECKED)
;	  OUTPUT
;	    (ES:DI) POINTS TO DISK BASE
;	    (CH) - LOW ORDER 8 OF 10 BITS MAXIMUM NUMBER OF TRACKS
;	    (CL) - BITS 7 & 6 - HIGH ORDER TWO BITS OF MAXIMUM TRACKS
;		   BITS 5 THRU 0 - MAXIMUM SECTORS PER TRACK
;	    (DH) - MAXIMUM HEAD NUMBER
;	    (DL) - NUMBER OF DISKETTE DRIVES INSTALLED
;	    (BH) - 0
;	    (BL) - BITS 7 THRU 4 - 0
;		   BITS 3 THRU 0 - VALID DRIVE TYPE VALUE IN CMOS
;	    (AX) - 0
;	   UNDER THE FOLLOWING CIRCUMSTANCES:
;	    (1) THE DRIVE NUMBER IS INVALID,
;	    (2) THE DRIVE TYPE IS UNKNOWN AND CMOS IS NOT PRESENT,
;	    (3) THE DRIVE TYPE IS UNKNOWN AND CMOS IS BAD,
;	    (4) OR THE DRIVE TYPE IS UNKNOWN AND THE CMOS DRIVE TYPE IS INVALID
;	    THEN ES,AX,BX,CX,DH,DI=0 ; DL=NUMBER OF DRIVES.
;	    IF NO DRIVES ARE PRESENT THEN: ES,AX,BX,CX,DX,DI=0.
;	    @DSKETTE_STATUS = 0 AND CY IS RESET.
;-------------------------------------------------------------------------------
;	(AH)=15H READ DASD TYPE
;	OUTPUT REGISTERS
;	(AH) - ON RETURN IF CARRY FLAG NOT SET, OTHERWISE ERROR
;		 00 - DRIVE NOT PRESENT
;		 01 - DISKETTE, NO CHANGE LINE AVAILABLE
;		 02 - DISKETTE, CHANGE LINE AVAILABLE
;		 03 - FIXED DISK
;	(DL) - DRIVE NUMBER (0-1 ALLOWED. VALUE CHECKED)
;
;-------------------------------------------------------------------------------
;	(AH)=16H DISK CHANGE LINE STATUS
;	OUTPUT REGISTERS
;	(AH)=00 - DISK CHANGE LINE NOT ACTIVE
;	     06 - DISK CHANGE LINE ACTIVE & CARRY BIT ON
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;
;-------------------------------------------------------------------------------
;	(AH)=17H SET DASD TYPE FOR FORMAT
;	INPUT REGISTERS
;	(AL) -	 00 - NOT USED
;		 01 - DISKETTE 320/360K IN 360K DRIVE
;		 02 - DISKETTE 360K IN 1.2M DRIVE
;		 03 - DISKETTE 1.2M IN 1.2M DRIVE
;		 04 - DISKETTE 720K IN 720K DRIVE
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED;
;		DO NOT USE WHEN DISKETTE ATTACH CARD USED)
;-------------------------------------------------------------------------------
;	(AH)=18H SET MEDIA TYPE FOR FORMAT
;	INPUT REGISTERS
;	(CH) - LOW ORDER 8 OF 10 BITS MAXIMUM NUMBER OF TRACKS
;	(CL) - BITS 7 & 6 - HIGH ORDER TWO BITS OF MAXIMUM TRACKS
;	       BITS 5 THRU 0 - MAXIMUM SECTORS PER TRACK
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;	OUTPUT REGISTERS
;	(ES:DI) - POINTER TO DRIVE PARAMETERS TABLE FOR THIS MEDIA TYPE,
;		  UNCHANGED IF (AH) IS NON-ZERO
;	(AH) - 00H, CY = 0, TRACK AND SECTORS/TRACK COMBINATION IS SUPPORTED
;	     - 01H, CY = 1, FUNCTION IS NOT AVAILABLE
;	     - 0CH, CY = 1, TRACK AND SECTORS/TRACK COMBINATION IS NOT SUPPORTED
;			    OR DRIVE TYPE UNKNOWN
;	     - 80H, CY = 1, TIME OUT (DISKETTE NOT PRESENT)
;-------------------------------------------------------------------------------
;	DISK CHANGE STATUS IS ONLY CHECKED WHEN A MEDIA SPECIFIED IS OTHER
;	THAN 360 KB DRIVE.  IF THE DISK CHANGE LINE IS FOUND TO BE
;	ACTIVE THE FOLLOWING ACTIONS TAKE PLACE:
;		ATTEMPT TO RESET DISK CHANGE LINE TO INACTIVE STATE.
;		IF ATTEMPT SUCCEEDS SET DASD TYPE FOR FORMAT AND RETURN DISK
;		CHANGE ERROR CODE
;		IF ATTEMPT FAILS RETURN TIMEOUT ERROR CODE AND SET DASD TYPE
;		TO A PREDETERMINED STATE INDICATING MEDIA TYPE UNKNOWN.
;	IF THE DISK CHANGE LINE IN INACTIVE PERFORM SET DASD TYPE FOR FORMAT.
;
; DATA VARIABLE -- @DISK_POINTER
;	DOUBLE WORD POINTER TO THE CURRENT SET OF DISKETTE PARAMETERS
;-------------------------------------------------------------------------------
; OUTPUT FOR ALL FUNCTIONS
;	AH = STATUS OF OPERATION
;		STATUS BITS ARE DEFINED IN THE EQUATES FOR @DSKETTE_STATUS
;		VARIABLE IN THE DATA SEGMENT OF THIS MODULE
;	CY = 0	SUCCESSFUL OPERATION (AH=0 ON RETURN, EXCEPT FOR READ DASD
;		TYPE AH=(15)).
;	CY = 1	FAILED OPERATION (AH HAS ERROR REASON)
;	FOR READ/WRITE/VERIFY
;		DS,BX,DX,CX PRESERVED
;	NOTE: IF AN ERROR IS REPORTED BY THE DISKETTE CODE, THE APPROPRIATE
;		ACTION IS TO RESET THE DISKETTE, THEN RETRY THE OPERATION.
;		ON READ ACCESSES, NO MOTOR START DELAY IS TAKEN, SO THAT
;		THREE RETRIES ARE REQUIRED ON READS TO ENSURE THAT THE
;		PROBLEM IS NOT DUE TO MOTOR START-UP.
;-------------------------------------------------------------------------------
; DISKETTE STATE MACHINE - ABSOLUTE ADDRESS 40:90 (DRIVE A) & 91 (DRIVE B)
;   -----------------------------------------------------------------
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   |	7   |	6   |	5   |	4   |	3   |	2   |	1   |	0   |
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   -----------------------------------------------------------------
;	|	|	|	|	|	|	|	|
;	|	|	|	|	|	-----------------
;	|	|	|	|	|		|
;	|	|	|	|    RESERVED		|
;	|	|	|	|		  PRESENT STATE
;	|	|	|	|	000: 360K IN 360K DRIVE UNESTABLISHED
;	|	|	|	|	001: 360K IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	010: 1.2M IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	011: 360K IN 360K DRIVE ESTABLISHED
;	|	|	|	|	100: 360K IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|	101: 1.2M IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|	110: RESERVED
;	|	|	|	|	111: NONE OF THE ABOVE
;	|	|	|	|
;	|	|	|	------> MEDIA/DRIVE ESTABLISHED
;	|	|	|
;	|	|	--------------> DOUBLE STEPPING REQUIRED (360K IN 1.2M
;	|	|			DRIVE)
;	|	|
;	------------------------------> DATA TRANSFER RATE FOR THIS DRIVE:
;
;						00: 500 KBS
;						01: 300 KBS
;						10: 250 KBS
;						11: RESERVED
;

;-------------------------------------------------------------------------------
; STATE OPERATION STARTED - ABSOLUTE ADDRESS 40:92 (DRIVE A) & 93 (DRIVE B)
;-------------------------------------------------------------------------------
; PRESENT CYLINDER NUMBER - ABSOLUTE ADDRESS 40:94 (DRIVE A) & 95 (DRIVE B)
;-------------------------------------------------------------------------------
SUBTTL (DSK2.ASM)
PAGE

MD_STRUC	STRUC
MD_SPEC1	DB	?	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
MD_SPEC2	DB	?	; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
MD_OFF_TIM	DB	?	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
MD_BYT_SEC	DB	?	; 512 BYTES/SECTOR
MD_SEC_TRK	DB	?	; EOT ( LAST SECTOR ON TRACK)
MD_GAP		DB	?	; GAP LENGTH
MD_DTL		DB	?	; DTL
MD_GAP3 	DB	?	; GAP LENGTH FOR FORMAT
MD_FIL_BYT	DB	?	; FILL BYTE FOR FORMAT
MD_HD_TIM	DB	?	; HEAD SETTLE TIME (MILLISECONDS)
MD_STR_TIM	DB	?	; MOTOR START TIME (1/8 SECONDS)
MD_MAX_TRK	DB	?	; MAX. TRACK NUMBER
MD_RATE 	DB	?	; DATA TRANSFER RATE
MD_STRUC	ENDS

BIT7OFF 	EQU	7FH
BIT7ON		EQU	80H

	PUBLIC	DISK_INT_1
	PUBLIC	SEEK
	PUBLIC	DSKETTE_SETUP
	PUBLIC	DISKETTE_IO_1

	EXTRN	CMOS_READ:NEAR
	EXTRN	DDS:NEAR
	EXTRN	DISK_BASE:NEAR
	EXTRN	WAITF:NEAR


CODE	SEGMENT BYTE PUBLIC

	ASSUME	CS:CODE,DS:DATA,ES:DATA

;----------------------------------------------------------------
;	DRIVE TYPE TABLE					:
;----------------------------------------------------------------
DR_TYPE 	DB	01		; DRIVE TYPE, MEDIA TABLE
		DW	OFFSET MD_TBL1
		DB	02+BIT7ON
		DW	OFFSET MD_TBL2
DR_DEFAULT	DB	02
		DW	OFFSET MD_TBL3
		DB	03
		DW	OFFSET MD_TBL4
		DB	04+BIT7ON
		DW	OFFSET MD_TBL5
		DB	04
		DW	OFFSET MD_TBL6
DR_TYPE_E	=$			; END OF TABLE
DR_CNT		EQU	(DR_TYPE_E-DR_TYPE)/3

;----------------------------------------------------------------
;	MEDIA DRIVE/PARAMETER TABLES				:
;----------------------------------------------------------------

;----------------------------------------------------------------
;	360 KB MEDIA IN 360 KB DRIVE				:
;----------------------------------------------------------------

MD_TBL1 	LABEL BYTE
	DB	11011111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	09		; EOT ( LAST SECTOR ON TRACK)
	DB	02AH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	050H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	39		; MAX. TRACK NUMBER
	DB	RATE_250	; DATA TRANSFER RATE

;----------------------------------------------------------------
;	360 KB MEDIA IN 1.2 MB DRIVE				:
;----------------------------------------------------------------

MD_TBL2 	LABEL BYTE
	DB	11011111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	09		; EOT ( LAST SECTOR ON TRACK)
	DB	02AH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	050H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	39		; MAX. TRACK NUMBER
	DB	RATE_300	; DATA TRANSFER RATE

;----------------------------------------------------------------
;	1.2 MB MEDIA IN 1.2 MB DRIVE				:
;----------------------------------------------------------------

MD_TBL3 	LABEL BYTE
	DB	11011111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	15		; EOT ( LAST SECTOR ON TRACK)
	DB	01BH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	054H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	79		; MAX. TRACK NUMBER
	DB	RATE_500	; DATA TRANSFER RATE

;----------------------------------------------------------------
;	720 KB MEDIA IN 720 KB DRIVE				:
;----------------------------------------------------------------

MD_TBL4 	LABEL BYTE
	DB	11011111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	09		; EOT ( LAST SECTOR ON TRACK)
	DB	02AH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	050H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	79		; MAX. TRACK NUMBER
	DB	RATE_250	; DATA TRANSFER RATE

;----------------------------------------------------------------
;	720 KB MEDIA IN 1.44 MB DRIVE				:
;----------------------------------------------------------------

MD_TBL5 	LABEL BYTE
	DB	11011111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	09		; EOT ( LAST SECTOR ON TRACK)
	DB	02AH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	050H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	79		; MAX. TRACK NUMBER
	DB	RATE_250	; DATA TRANSFER RATE

;----------------------------------------------------------------
;	1.44 MB MEDIA IN 1.44 MB DRIVE				:
;----------------------------------------------------------------

MD_TBL6 	LABEL BYTE
	DB	10101111B	; SRT=D, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT TIME AFTER OPERATION TILL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	18		; EOT ( LAST SECTOR ON TRACK)
	DB	01BH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	06CH		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	15		; HEAD SETTLE TIME (MILLISECONDS)
	DB	8		; MOTOR START TIME (1/8 SECONDS)
	DB	79		; MAX. TRACK NUMBER
	DB	RATE_500	; DATA TRANSFER RATE

DISKETTE_IO_1	PROC	FAR		;>>> ENTRY POINT FOR ORG 0EC59H

	STI				; INTERRUPTS BACK ON
	PUSH	BP			; USER REGISTER
	PUSH	DI			; USER REGISTER
	PUSH	DX			; HEAD #, DRIVE # OR USER REGISTER
	PUSH	BX			; BUFFER OFFSET PARAMETER OR REGISTER
	PUSH	CX			; TRACK #-SECTOR # OR USER REGISTER
	MOV	BP,SP			; BP	 => PARAMETER LIST DEP. ON AH
					; [BP]	 = SECTOR #
					; [BP+1] = TRACK #
					; [BP+2] = BUFFER OFFSET
					; FOR RETURN OF DRIVE PARAMETERS:
					; CL/[BP] = BITS 7&6 HI BITS OF MAX CYL
					;	    BITS 0-5 MAX SECTORS/TRACK
					; CH/[BP+1] = LOW 8 BITS OF MAX CYL.
					; BL/[BP+2] = BITS 7-4 = 0
					;	      BITS 3-0 = VALID CMOS TYPE
					; BH/[BP+3] = 0
					; DL/[BP+4] = # DRIVES INSTALLED
					; DH/[BP+5] = MAX HEAD #
					; DI/[BP+6] = OFFSET TO DISK BASE
	PUSH	DS			; BUFFER SEGMENT PARM OR USER REGISTER
	PUSH	SI			; USER REGISTERS
	CALL	DDS			; SEGMENT OF BIOS DATA AREA TO DS
	CMP	AH,(FNC_TAE-FNC_TAB)/2	; CHECK FOR > LARGEST FUNCTION
	JB	OK_FUNC 		; FUNCTION OK

	MOV	AH,14H			; REPLACE WITH KNOWN INVALID FUNCTION

OK_FUNC:
	CMP	AH,1			; RESET OR STATUS ?
	JBE	OK_DRV			; IF RESET OR STATUS DRIVE ALWAYS OK
	CMP	AH,8			; READ DRIVE PARMS ?
	JZ	OK_DRV			; IF SO DRIVE CHECKED LATER
	CMP	DL,1			; DRIVES 0 AND 1 OK
	JBE	OK_DRV			; IF 0 OR 1 THEN JUMP
	MOV	AH,14H			; REPLACE WITH KNOWN INVALID FUNCTION

OK_DRV:
	MOV	CL,AH			; CL =	FUNCTION
	XOR	CH,CH			; CX = FUNCTION
	SHL	CL,1			; FUNCTION TIMES 2
	MOV	BX,OFFSET FNC_TAB	; LOAD START OF FUNCTION TABLE
	ADD	BX,CX			; ADD OFFSET INTO TABLE => ROUTINE
	MOV	AH,DH			; AX = HEAD #,# OF SECTORS OR DASD TYPE
	XOR	DH,DH			; DX = DRIVE #
	MOV	SI,AX			; SI = HEAD #,# OF SECTORS OR DASD TYPE
	MOV	DI,DX			; DI = DRIVE #
	MOV	AH,@DSKETTE_STATUS	; LOAD STATUS TO AH FOR STATUS FUNCTION
	MOV	@DSKETTE_STATUS,0	; INITIALIZE FOR ALL OTHERS

;	THROUGHOUT THE DISKETTE BIOS, THE FOLLOWING INFORMATION IS CONTAINED IN
;	THE FOLLOWING MEMORY LOCATIONS AND REGISTERS. NOT ALL DISKETTE BIOS
;	FUNCTIONS REQUIRE ALL OF THESE PARAMETERS.
;
;		DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS OR DASD TYPE FOR FORMAT
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
;	ACROSS CALLS TO SUBROUTINES THE CARRY FLAG (CY=1), WHERE INDICATED IN
;	SUBROUTINE PROLOGUES, REPRESENTS AN EXCEPTION RETURN (NORMALLY AN ERROR
;	CONDITION). IN MOST CASES, WHEN CY = 1, @DSKETTE_STATUS CONTAINS THE
;	SPECIFIC ERROR CODE.
					; (AH) = @DSKETTE_STATUS
	CALL	WORD PTR CS:[BX]	; CALL THE REQUESTED FUNCTION

	POP	SI			; RESTORE ALL REGISTERS
	POP	DS
	POP	CX
	POP	BX
	POP	DX
	POP	DI
	MOV	BP,SP
	PUSH	AX
	PUSHF
	POP	AX
	MOV	[BP+6],AX
	POP	AX
	POP	BP
	IRET

;----------------------------------------------------------------
FNC_TAB DW	DISK_RESET		; AH = 00H; RESET
	DW	DISK_STATUS		; AH = 01H; STATUS
	DW	DISK_READ		; AH = 02H; READ
	DW	DISK_WRITE		; AH = 03H; WRITE
	DW	DISK_VERF		; AH = 04H; VERIFY
	DW	DISK_FORMAT		; AH = 05H; FORMAT
	DW	FNC_ERR 		; AH = 06H; INVALID
	DW	FNC_ERR 		; AH = 07H; INVALID
	DW	DISK_PARMS		; AH = 08H; READ DRIVE PARAMETERS
	DW	FNC_ERR 		; AH = 09H; INVALID
	DW	FNC_ERR 		; AH = 0AH; INVALID
	DW	FNC_ERR 		; AH = 0BH; INVALID
	DW	FNC_ERR 		; AH = 0CH; INVALID
	DW	FNC_ERR 		; AH = 0DH; INVALID
	DW	FNC_ERR 		; AH = 0EH; INVALID
	DW	FNC_ERR 		; AH = 0FH; INVALID
	DW	FNC_ERR 		; AH = 10H; INVALID
	DW	FNC_ERR 		; AH = 11H; INVALID
	DW	FNC_ERR 		; AH = 12H; INVALID
	DW	FNC_ERR 		; AH = 13H; INVALID
	DW	FNC_ERR 		; AH = 14H; INVALID
	DW	DISK_TYPE		; AH = 15H; READ DASD TYPE
	DW	DISK_CHANGE		; AH = 16H; CHANGE STATUS
	DW	FORMAT_SET		; AH = 17H; SET DASD TYPE
	DW	SET_MEDIA		; AH = 18H; SET MEDIA TYPE
FNC_TAE EQU	$			; END
DISKETTE_IO_1	ENDP
;----------------------------------------------------------------
; DISK_RESET  (AH = 00H)					:
;	RESET THE DISKETTE SYSTEM.				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_RESET	PROC	NEAR
	MOV	DX,03F2H		; ADAPTER CONTROL PORT
	CLI				; NO INTERRUPTS
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,00111111B		; KEEP SELECTED AND MOTOR ON BITS
	ROL	AL,4			; MOTOR VALUE TO HIGH NIBBLE
					; DRIVE SELECT TO LOW NIBBLE
	OR	AL,00001000B		; TURN ON INTERRUPT ENABLE
	OUT	DX,AL			; RESET THE ADAPTER
	MOV	@SEEK_STATUS,0		; SET RECALIBRATE REQUIRED ON ALL DRIVES
	JMP	$+2			; WAIT FOR I/O
	JMP	$+2			; WAIT FOR I/O (TO INSURE MINIMUM
					;   PULSE WIDTH)
	OR	AL,00000100B		; TURN OFF RESET BIT
	OUT	DX,AL			; RESET THE ADAPTER
	STI				; ENABLE THE INTERRUPTS
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	DR_ERR			; IF ERROR, RETURN IT
	MOV	CX,11000000B		; CL = EXPECTED @NEC_STATUS

NXT_DRV:
	PUSH	CX			; SAVE FOR CALL
	MOV	AX,OFFSET DR_POP_ERR	; LOAD NEC_OUTPUT ERROR ADDRESS
	PUSH	AX			; "
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	POP	AX			; THROW AWAY ERROR RETURN
	CALL	RESULTS 		; READ IN THE RESULTS
	POP	CX			; RESTORE AFTER CALL
	JC	DR_ERR			; ERROR RETURN
	CMP	CL,@NEC_STATUS		; TEST FOR DRIVE READY TRANSITION
	JNZ	DR_ERR			; EVERYTHING OK
	INC	CL			; NEXT EXPECTED @NEC_STATUS
	CMP	CL,11000011B		; ALL POSSIBLE DRIVES CLEARED
	JBE	NXT_DRV 		; FALL THRU IF 11000100B OR >

	CALL	SEND_SPEC		; SEND SPECIFY COMMAND TO NEC

RESBAC:
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

DR_POP_ERR:
	POP	CX			; CLEAR STACK
DR_ERR:
	OR	@DSKETTE_STATUS,BAD_NEC ; SET ERROR CODE
	JMP	SHORT RESBAC		; RETURN FROM RESET
DISK_RESET	ENDP

;----------------------------------------------------------------
; DISK_STATUS  (AH = 01H)					:
;	DISKETTE STATUS.					:
; ON ENTRY:	AH = STATUS OF PREVIOUS OPERATION		:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_STATUS	PROC	NEAR
	MOV	@DSKETTE_STATUS,AH	; PUT BACK FOR SETUP_END
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
DISK_STATUS	ENDP
;----------------------------------------------------------------
; DISK_READ  (AH = 02H) 					:
;	DISKETTE READ.						:
; ON ENTRY:	DI     = DRIVE #				:
;		SI-HI  = HEAD # 				:
;		SI-LOW = # OF SECTORS				:
;		ES     = BUFFER SEGMENT 			:
;		[BP]   = SECTOR #				:
;		[BP+1] = TRACK #				:
;		[BP+2] = BUFFER OFFSET				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_READ	PROC	NEAR
	AND	@MOTOR_STATUS,01111111B ; INDICATE A READ OPERATION
	MOV	AX,0E646H		; AX = NEC COMMAND, DMA COMMAND
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_READ	ENDP
;----------------------------------------------------------------
; DISK_WRITE  (AH = 03H)					:
;	DISKETTE WRITE. 					:
; ON ENTRY:	DI     = DRIVE #				:
;		SI-HI  = HEAD # 				:
;		SI-LOW = # OF SECTORS				:
;		ES     = BUFFER SEGMENT 			:
;		[BP]   = SECTOR #				:
;		[BP+1] = TRACK #				:
;		[BP+2] = BUFFER OFFSET				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_WRITE	PROC	NEAR
	MOV	AX,0C54AH		; AX = NEC COMMAND, DMA COMMAND
	OR	@MOTOR_STATUS,10000000B ; INDICATE WRITE OPERATION
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_WRITE	ENDP
;----------------------------------------------------------------
; DISK_VERF  (AH = 04H) 					:
;	DISKETTE VERIFY.					:
; ON ENTRY:	DI     = DRIVE #				:
;		SI-HI  = HEAD # 				:
;		SI-LOW = # OF SECTORS				:
;		ES     = BUFFER SEGMENT 			:
;		[BP]   = SECTOR #				:
;		[BP+1] = TRACK #				:
;		[BP+2] = BUFFER OFFSET				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_VERF	PROC	NEAR
	AND	@MOTOR_STATUS,01111111B ; INDICATE A READ OPERATION
	MOV	AX,0E642H		; AX = NEC COMMAND, DMA COMMAND
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_VERF	ENDP
;----------------------------------------------------------------
; DISK_FORMAT  (AH = 05H)					:
;	DISKETTE FORMAT.					:
; ON ENTRY:	DI     = DRIVE #				:
;		SI-HI  = HEAD # 				:
;		SI-LOW = # OF SECTORS				:
;		ES     = BUFFER SEGMENT 			:
;		[BP]   = SECTOR #				:
;		[BP+1] = TRACK #				:
;		[BP+2] = BUFFER OFFSET				:
;		@DISK_POINTER POINTS TO THE PARAMETER TABLE OF	:
;			 THIS DRIVE				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DISK_FORMAT	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	CALL	FMT_INIT		; ESTABLISH STATE IF UNESTABLISHED
	OR	@MOTOR_STATUS,10000000B ; INDICATE WRITE OPERATION
	CALL	MED_CHANGE		; CHECK MEDIA CHANGE AND RESET IF SO
	JC	FM_DON			; MEDIA CHANGED, SKIP
	CALL	SEND_SPEC		; SEND SPECIFY COMMAND TO NEC
	CALL	CHK_LASTRATE		; ZF=1 ATTEMPT RATE IS SAME AS LAST RATE
	JZ	FM_WR			; YES, SKIP SPECIFY COMMAND
	CALL	SEND_RATE		; SEND DATA RATE TO CONTROLLER
FM_WR:
	CALL	FMTDMA_SET		; SET UP THE DMA FOR FORMAT
	JC	FM_DON			; RETURN WITH ERROR
	MOV	AH,04DH 		; ESTABLISH THE FORMAT COMMAND
	CALL	NEC_INIT		; INITIALIZE THE NEC
	JC	FM_DON			; ERROR - EXIT
	MOV	AX,OFFSET FM_DON	; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	DL,3			; BYTES/SECTOR VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,4			; SECTORS/TRACK VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,7			; GAP LENGTH VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,8			; FILLER BYTE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	POP	AX			; THROW AWAY ERROR
	CALL	NEC_TERM		; TERMINATE, RECEIVE STATUS, ETC,
FM_DON:
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
DISK_FORMAT	ENDP
;----------------------------------------------------------------
; FNC_ERR							:
;	INVALID FUNCTION REQUESTED OR INVALID DRIVE:		:
;	SET BAD COMMAND IN STATUS.				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
FNC_ERR PROC	NEAR			; INVALID FUNCTION REQUEST
	MOV	AX,SI			; RESTORE AL
	MOV	AH,BAD_CMD		; SET BAD COMMAND ERROR
	MOV	@DSKETTE_STATUS,AH	; STORE IN DATA AREA
	STC				; SET CARRY INDICATING ERROR
	RET
FNC_ERR ENDP
;----------------------------------------------------------------
; DISK_PARMS  (AH = 08H)					:
;	READ DRIVE PARAMETERS.					:
; ON ENTRY:							:
;	DI = DRIVE #						:
; ON EXIT:							:
;	CL/[BP]   = BITS 7 & 6 HI 2 BITS OF MAX CYLINDER	:
;		    BITS 0-5 MAX SECTORS/TRACK			:
;	CH/[BP+1] = LOW 8 BITS OF MAX CYLINDER			:
;	BL/[BP+2] = BITS 7-4 = 0				:
;		    BITS 3-0 = VALID CMOS DRIVE TYPE		:
;	BH/[BP+3] = 0						:
;	DL/[BP+4] = # DRIVES INSTALLED (VALUE CHECKED)		:
;	DH/[BP+5] = MAX HEAD #					:
;	DI/[BP+6] = OFFSET TO DISK_BASE 			:
;	ES	  = SEGMENT OF DISK_BASE			:
;	AX	  = 0						:
;								:
; NOTE : THE ABOVE INFORMATION IS STORED IN THE USERS STACK AT	:
;	 THE LOCATIONS WHERE THE MAIN ROUTINE WILL POP THEM	:
;	 INTO THE APPROPRIATE REGISTERS BEFORE RETURNING TO THE :
;	 CALLER.						:
;----------------------------------------------------------------
DISK_PARMS	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH,
	MOV	WORD PTR [BP+2],0	; DRIVE TYPE = 0
	MOV	AX,@EQUIP_FLAG		; LOAD EQUIPMENT FLAG FOR # DISKETTES
	AND	AL,11000001B		; KEEP DISKETTE DRIVE BITS
	MOV	DL,2			; DISKETTE DRIVES = 2
	CMP	AL,01000001B		; 2 DRIVES INSTALLED ?
	JZ	STO_DL			; IF YES JUMP

	DEC	DL			; DISKETTE DRIVES = 1
	CMP	AL,00000001B		; 1 DRIVE INSTALLED ?
	JNZ	NON_DRV 		; IF NO JUMP

STO_DL: MOV	[BP+4],DL		; STORE NUMBER OF DRIVES
	CMP	DI,1			; CHECK FOR VALID DRIVE
	JA	NON_DRV1		; DRIVE INVALID
	MOV	BYTE PTR[BP+5],1	; MAXIMUM HEAD NUMBER = 1
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN AL
	JC	CHK_EST 		; ON CMOS BAD CHECKSUM ESTABLISHED
	OR	AL,AL			; TEST FOR NO DRIVE TYPE
	JZ	CHK_EST 		; JUMP IF SO
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	JC	CHK_EST 		; TYPE NOT IN TABLE (POSSIBLE BAD CMOS)
	MOV	[BP+2],AL		; STORE VALID CMOS DRIVE TYPE
	MOV	CL,CS:[BX].MD_SEC_TRK	; GET SECTOR/TRACK
	MOV	CH,CS:[BX].MD_MAX_TRK	; GET MAX. TRACK NUMBER
	JMP	SHORT STO_CX		; CMOS_GOOD, USE CMOS

CHK_EST:
	MOV	AH,@DSK_STATE[DI]	; LOAD STATE FOR THIS DRIVE
	TEST	AH,MED_DET		; CHECK FOR ESTABLISHED STATE
	JZ	NON_DRV1		; CMOS BAD/INVALID AND UNESTABLISHED

USE_EST:
	AND	AH,RATE_MSK		; ISOLATE STATE
	CMP	AH,RATE_250		; RATE 250 ?
	JNE	USE_EST2		; NO, GO CHECK OTHER RATE

;--- DATA RATE IS 250 KBS, TRY 360 KB TABLE FIRST

	MOV	AL,01			; DRIVE TYPE 1 (360KB)
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	MOV	CL,CS:[BX].MD_SEC_TRK	; GET SECTOR/TRACK
	MOV	CH,CS:[BX].MD_MAX_TRK	; GET MAX. TRACK NUMBER
	TEST	@DSK_STATE[DI],TRK_CAPA ; 80 TRACK ?
	JZ	STO_CX			; MUST BE 360KB DRIVE

;--- IS IT 1.44 MB DRIVE

PARM144:
	MOV	AL,04			; DRIVE TYPE 4 (1.44MB)
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	MOV	CL,CS:[BX].MD_SEC_TRK	; GET SECTOR/TRACK
	MOV	CH,CS:[BX].MD_MAX_TRK	; GET MAX. TRACK NUMBER

STO_CX:
	MOV	[BP],CX 		; SAVE POINTER IN STACK FOR RETURN
	MOV	[BP+6],BX		; SAVE IN STACK
	MOV	AX,CS			; SEGMENT DISK_BASE (SAME AS THIS ONE)
	MOV	ES,AX			; ES IS SEGMENT OF TABLE

DP_OUT: CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	XOR	AX,AX			; CLEAR
	CLC
	RET

;-----	NO DRIVE PRESENT HANDLER

NON_DRV:
	MOV	BYTE PTR [BP+4],0	; CLEAR NUMBER OF DRIVES

NON_DRV1:
	CMP	DI,80H			; CHECK FOR FIXED MEDIA TYPE REQUEST
	JB	NON_DRV2		; CONTINUE IF NOT REQUEST FALL THROUGH

;-----	FIXED DISK REQUEST FALL THROUGH ERROR

	CALL	XLAT_OLD		; ELSE TRANSLATE TO COMPATIBLE MODE
	MOV	AX,SI			; RESTORE AL
	MOV	AH,BAD_CMD		; SET BAD COMMAND ERROR
	STC				; SET ERROR RETURN CODE
	RET

NON_DRV2:
	XOR	AX,AX			; CLEAR PARMS IF NO DRIVES OR CMOS BAD
	MOV	[BP],AX 		; TRACKS, SECTORS/TRACK = 0
	MOV	[BP+5],AH		; HEAD = 0
	MOV	[BP+6],AX		; OFFSET TO DISK_BASE = 0
	MOV	ES,AX			; ES IS SEGMENT OF TABLE
	JMP	SHORT DP_OUT

;--- DATA RATE IS EITHER 300 KBS OR 500 KBS, TRY 1.2 MB TABLE FIRST

USE_EST2:
	MOV	AL,02			; DRIVE TYPE 2 (1.2MB)
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	MOV	CL,CS:[BX].MD_SEC_TRK	; GET SECTOR/TRACK
	MOV	CH,CS:[BX].MD_MAX_TRK	; GET MAX. TRACK NUMBER
	CMP	AH,RATE_300		; RATE 300 ?
	JE	STO_CX			; MUST BE 1.2MB DRIVE
	JMP	SHORT PARM144		; ELSE, IT IS 1.44MB DRIVE

DISK_PARMS	ENDP

;----------------------------------------------------------------
; DISK_TYPE  (AH = 15H) 					:
;	THIS ROUTINE RETURNS THE TYPE OF MEDIA INSTALLED.	:
;  ON ENTRY:	DI = DRIVE #					:
;								:
;  ON EXIT:	AH = DRIVE TYPE, CY=0				:
;----------------------------------------------------------------
DISK_TYPE	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	MOV	AL,@DSK_STATE[DI]	; GET PRESENT STATE INFORMATION
	OR	AL,AL			; CHECK FOR NO DRIVE
	JZ	NO_DRV
	MOV	AH,NOCHGLN		; NO CHANGE LINE FOR 40 TRACK DRIVE
	TEST	AL,TRK_CAPA		; IS THIS DRIVE AN 80 TRACK DRIVE?
	JZ	DT_BACK 		; IF NO JUMP
	MOV	AH,CHGLN		; CHANGE LINE FOR 80 TRACK DRIVE

DT_BACK:
	PUSH	AX			; SAVE RETURN VALUE
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	POP	AX			; RESTORE RETURN VALUE
	CLC				; NO ERROR
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
NO_DRV:
	XOR	AH,AH			; NO DRIVE PRESENT OR UNKNOWN
	JMP	SHORT DT_BACK
DISK_TYPE ENDP
;----------------------------------------------------------------
; DISK_CHANGE  (AH = 16H)					:
;	THIS ROUTINE RETURNS THE STATE OF THE DISK CHANGE LINE. :
;								:
; ON ENTRY:	DI : DRIVE #					:
;								:
; ON EXIT:	AH = @DSKETTE_STATUS				:
;		     00 - DISK CHANGE LINE INACTIVE, CY = 0	:
;		     06 - DISK CHANGE LINE ACTIVE, CY = 1	:
;----------------------------------------------------------------
DISK_CHANGE	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	MOV	AL,@DSK_STATE[DI]	; GET MEDIA STATE INFORMATION
	OR	AL,AL			; DRIVE PRESENT ?
	JZ	DC_NON			; JUMP IF NO DRIVE
	TEST	AL,TRK_CAPA		; 80 TRACK DRIVE ?
	JZ	SETIT			; IF SO , CHECK CHANGE LINE

DC0:	CALL	READ_DSKCHNG		; GO CHECK STATE OF DISK CHANGE LINE
	JZ	FINIS			; CHANGE LINE NOT ACTIVE

SETIT:	MOV	@DSKETTE_STATUS,MEDIA_CHANGE	; INDICATE MEDIA REMOVED

FINIS:	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

DC_NON:
	OR	@DSKETTE_STATUS,TIME_OUT	; SET TIMEOUT, NO DRIVE
	JMP	SHORT FINIS
DISK_CHANGE ENDP

;----------------------------------------------------------------
; FORMAT_SET  (AH = 17H)					:
;	THIS ROUTINE IS USED TO ESTABLISH THE TYPE OF		:
;	MEDIA TO BE USED FOR THE FOLLOWING FORMAT OPERATION.	:
;								:
; ON ENTRY:	SI LOW : DASD TYPE FOR FORMAT			:
;		DI     : DRIVE #				:
;								:
; ON EXIT:	@DSKETTE_STATUS REFLECTS STATUS 		:
;		AH = @DSKETTE_STATUS				:
;		CY = 1 IF ERROR 				:
;----------------------------------------------------------------
FORMAT_SET	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	PUSH	SI			; SAVE DASD TYPE
	MOV	AX,SI			; AH = ? , AL = DASD TYPE
	XOR	AH,AH			; AH = 0 , AL = DASD TYPE
	MOV	SI,AX			; SI = DASD TYPE
	AND	@DSK_STATE[DI],NOT MED_DET+DBL_STEP+RATE_MSK	; CLEAR STATE
	DEC	SI			; CHECK FOR 320/360K MEDIA & DRIVE
	JNZ	NOT_320 		; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+RATE_250 ; SET TO 320/360
	JMP	SHORT S0

NOT_320:
	CALL	MED_CHANGE		; CHECK FOR TIME_OUT
	CMP	@DSKETTE_STATUS,TIME_OUT
	JZ	S0			; IF TIME OUT TELL CALLER

S3:	DEC	SI			; CHECK FOR 320/360K IN 1.2M DRIVE
	JNZ	NOT_320_12		; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+DBL_STEP+RATE_300 ; SET STATE
	JMP	SHORT S0

NOT_320_12:
	DEC	SI			; CHECK FOR 1.2M MEDIA IN 1.2M DRIVE
	JNZ	NOT_12			; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+RATE_500 ; SET STATE VARIABLE
	JMP	SHORT S0		; RETURN TO CALLER

NOT_12:
	DEC	SI			; CHECK FOR SET DASD TYPE 04
	JNZ	FS_ERR			; BAD COMMAND EXIT IF NOT VALID TYPE

	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE DETERMINED ?
	JZ	ASSUME			; IF STILL NOT DETERMINED ASSUME
	MOV	AL,MED_DET+RATE_300
	TEST	@DSK_STATE[DI],FMT_CAPA ; MULTIPLE FORMAT CAPABILITY ?
	JNZ	OR_IT_IN		; IF 1.2 M THEN DATA RATE 300

ASSUME:
	MOV	AL,MED_DET+RATE_250	; SET UP

OR_IT_IN:
	OR	@DSK_STATE[DI],AL	; OR IN THE CORRECT STATE

S0:	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	POP	BX			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

FS_ERR:
	MOV	@DSKETTE_STATUS,BAD_CMD ; UNKNOWN STATE,BAD COMMAND
	JMP	SHORT S0

FORMAT_SET	ENDP

;----------------------------------------------------------------
; SET_MEDIA  (AH = 18H) 					:
;	THIS ROUTINE SETS THE TYPE OF MEDIA AND DATA RATE	:
;	TO BE USED FOR THE FOLLOWING FORMAT OPERATION.		:
; ON ENTRY:							:
;	[BP]	 SECTOR PER TRACK				:
;	[BP+1] = TRACK #					:
;	DI     = DRIVE #					:
; ON EXIT:							:
;	@DSKETTE_STATUS REFLECTS STATUS 			:
;	IF NO ERROR:						:
;	    AH = 0						:
;	    CY = 0						:
;	    ES = SEGMENT OF MEDIA/DRIVE PARAMETER TABLE 	:
;	    DI/[BP+6] = OFFSET OF MEDIA/DRIVE PARAMETER TABLE	:
;	IF ERROR:						:
;	    AH = @DSKETTE_STATUS				:
;	    CY = 1						:
;----------------------------------------------------------------
SET_MEDIA	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	TEST	@DSK_STATE[DI],TRK_CAPA ; CHECK FOR CHANGE LINE AVAILABLE
	JZ	SM_CMOS 		; JUMP IF 40 TRACK DRIVE
	CALL	MED_CHANGE		; RESET CHANGE LINE
	CMP	@DSKETTE_STATUS,TIME_OUT ; IF TIME OUT TELL CALLER
	JE	SM_RTN
	MOV	@DSKETTE_STATUS,0	; CLEAR STATUS
SM_CMOS:
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN (AL)
	JC	MD_NOT_FND		; ERROR IN CMOS
	OR	AL,AL			; TEST FOR NO DRIVE
	JZ	SM_RTN			; RETURN IF SO
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	JC	MD_NOT_FND		; TYPE NOT IN TABLE (BAD CMOS)
	PUSH	DI			; SAVE REG.
	XOR	BX,BX			; BX = INDEX TO DR_TYPE TABLE
	MOV	CX,DR_CNT		; CX = LOOP COUNT
DR_SEARCH:
	MOV	AH,CS:DR_TYPE[BX]	; GET DRIVE TYPE
	AND	AH,BIT7OFF		; MASK OUT MSB
	CMP	AL,AH			; DRIVE TYPE MATCH ?
	JNE	NXT_MD			; NO, CHECK NEXT DRIVE TYPE

	MOV	DI,CS:WORD PTR DR_TYPE[BX+1] ; DI = MEDIA/DRIVE PARAMETER TABLE

	MOV	AH,CS:[DI].MD_SEC_TRK	; GET SECTOR/TRACK
	CMP	[BP],AH 		; MATCH ?
	JNE	NXT_MD			; NO, CHECK NEXT MEDIA
	MOV	AH,CS:[DI].MD_MAX_TRK	; GET MAX. TRACK #
	CMP	[BP+1],AH		; MATCH ?
	JE	MD_FND			; YES, GO GET RATE
NXT_MD:
	ADD	BX,3			; CHECK NEXT DRIVE TYPE
	LOOP	DR_SEARCH
	POP	DI			; RESTORE REG.
MD_NOT_FND:
	MOV	@DSKETTE_STATUS,MED_NOT_FND	; ERROR, MEDIA TYPE NOT FOUND
	JMP	SHORT SM_RTN		; RETURN

MD_FND:
	MOV	AL,CS:[DI].MD_RATE	; GET RATE
	CMP	AL,RATE_300		; DOUBLE STEP REQUIRED FOR RATE 300
	JNE	MD_SET
	OR	AL,DBL_STEP
MD_SET:
	MOV	[BP+6],DI		; SAVE TABLE POINTER IN STACK
	OR	AL,MED_DET		; SET MEDIA ESTABLISHED
	POP	DI			; RESTORE REG.
	AND	@DSK_STATE[DI],NOT MED_DET+DBL_STEP+RATE_MSK	; CLEAR STATE
	OR	@DSK_STATE[DI],AL	; SET STATE
	MOV	AX,CS			; SEGMENT MEDIA/DRIVE PARAMETER TABLE
	MOV	ES,AX			; ES IS SEGMENT OF TABLE
SM_RTN:
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	RET
SET_MEDIA	ENDP

;----------------------------------------------------------------
; DR_TYPE_CHECK 						:
;	CHECK IF THE GIVEN DRIVE TYPE IN REGISTER (AL)		:
;	IS SUPPORTED IN BIOS DRIVE TYPE TABLE			:
; ON ENTRY:							:
;	AL = DRIVE TYPE 					:
; ON EXIT:							:
;	CS = SEGMENT OF MEDIA/DRIVE PARAMETER TABLE (CODE)	:
;	CY = 0	 DRIVE TYPE SUPPORTED				:
;	     BX = OFFSET TO MEDIA/DRIVE PARAMETER TABLE 	:
;	CY = 1	 DRIVE TYPE NOT SUPPORTED			:
; REGISTERS ALTERED:  BX					:
;----------------------------------------------------------------
DR_TYPE_CHECK	PROC	NEAR
	PUSH	AX
	PUSH	CX
	XOR	BX,BX			; BX = INDEX TO DR_TYPE TABLE
	MOV	CX,DR_CNT		; CX = LOOP COUNT
TYPE_CHK:
	MOV	AH,CS:DR_TYPE[BX]	; GET DRIVE TYPE
	CMP	AL,AH			; DRIVE TYPE MATCH ?
	JE	DR_TYPE_VALID		; YES, RETURN WITH CARRY RESET
	ADD	BX,3			; CHECK NEXT DRIVE TYPE
	LOOP	TYPE_CHK
	STC				; DRIVE TYPE NOT FOUND IN TABLE
	JMP	SHORT TYPE_RTN
DR_TYPE_VALID:
	MOV	BX,CS:WORD PTR DR_TYPE[BX+1]	; BX = MEDIA TABLE
TYPE_RTN:
	POP	CX
	POP	AX
	RET
DR_TYPE_CHECK	ENDP

;----------------------------------------------------------------
; SEND_SPEC							:
;	SEND THE SPECIFY COMMAND TO CONTROLLER USING DATA FROM	:
;	THE DRIVE PARAMETER TABLE POINTED BY @DISK_POINTER	:
; ON ENTRY:	@DISK_POINTER = DRIVE PARAMETER TABLE		:
; ON EXIT:	NONE						:
; REGISTERS ALTERED: CX, DX					:
;----------------------------------------------------------------
SEND_SPEC	PROC	NEAR
	PUSH	AX			; SAVE AX
	MOV	AX,OFFSET SPECBAC	; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	AH,03H			; SPECIFY COMMAND
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	SUB	DL,DL			; FIRST SPECIFY BYTE
	CALL	GET_PARM		; GET PARAMETER TO AH
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	MOV	DL,1			; SECOND SPECIFY BYTE
	CALL	GET_PARM		; GET PARAMETER TO AH
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	POP	AX			; POP ERROR RETURN
SPECBAC:
	POP	AX			; RESTORE ORIGINAL AX VALUE
	RET
SEND_SPEC	ENDP

;----------------------------------------------------------------
; SEND_SPEC_MD							:
;	SEND THE SPECIFY COMMAND TO CONTROLLER USING DATA FROM	:
;	THE MEDIA/DRIVE PARAMETER TABLE POINTED BY (CS:BX)	:
; ON ENTRY:	CS:BX = MEDIA/DRIVE PARAMETER TABLE		:
; ON EXIT:	NONE						:
; REGISTERS ALTERED: AX 					:
;----------------------------------------------------------------
SEND_SPEC_MD	PROC	NEAR
	PUSH	AX			; SAVE RATE DATA
	MOV	AX,OFFSET SPEC_ESBAC	; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	AH,03H			; SPECIFY COMMAND
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	MOV	AH,CS:[BX].MD_SPEC1	; GET 1ST SPECIFY BYTE
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	MOV	AH,CS:[BX].MD_SPEC2	; GET SECOND SPECIFY BYTE
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	POP	AX			; POP ERROR RETURN
SPEC_ESBAC:
	POP	AX			; RESTORE RATE
	RET
SEND_SPEC_MD	ENDP
PAGE
;----------------------------------------------------------------
; XLAT_NEW							:
;	TRANSLATES DISKETTE STATE LOCATIONS FROM COMPATIBLE	:
;	MODE TO NEW ARCHITECTURE.				:
;								:
; ON ENTRY:	DI : DRIVE					:
;----------------------------------------------------------------
XLAT_NEW	PROC	NEAR		;
	CMP	DI,1			; VALID DRIVE
	JA	XN_OUT			; IF INVALID BACK
	CMP	@DSK_STATE[DI],0	; NO DRIVE ?
	JZ	DO_DET			; IF NO DRIVE ATTEMPT DETERMINE
	MOV	CX,DI			; CX = DRIVE NUMBER
	SHL	CL,2			; CL = SHIFT COUNT, A=0, B=4
	MOV	AL,@HF_CNTRL		; DRIVE INFORMATION
	ROR	AL,CL			; TO LOW NIBBLE
	AND	AL,DRV_DET+FMT_CAPA+TRK_CAPA	; KEEP DRIVE BITS
	AND	@DSK_STATE[DI],NOT DRV_DET+FMT_CAPA+TRK_CAPA
	OR	@DSK_STATE[DI],AL	; UPDATE DRIVE STATE
XN_OUT:
	RET

DO_DET:
	CALL	DRIVE_DET		; TRY TO DETERMINE
	RET

XLAT_NEW	ENDP
;----------------------------------------------------------------
; XLAT_OLD							:
;	TRANSLATES DISKETTE STATE LOCATIONS FROM NEW		:
;	ARCHITECTURE TO COMPATIBLE MODE.			:
;								:
; ON ENTRY:	DI : DRIVE					:
;----------------------------------------------------------------
XLAT_OLD	PROC	NEAR
	CMP	DI,1			; VALID DRIVE ?
	JA	XO_OUT			; IF INVALID BACK
	CMP	@DSK_STATE[DI],0	; NO DRIVE ?
	JZ	XO_OUT			; IF NO DRIVE TRANSLATE DONE

;-----	TEST FOR SAVED DRIVE INFORMATION ALREADY SET

	MOV	CX,DI			; CX = DRIVE NUMBER
	SHL	CL,2			; CL = SHIFT COUNT, A=0, B=4
	MOV	AH,FMT_CAPA		; LOAD MULTIPLE DATA RATE BIT MASK
	ROR	AH,CL			; ROTATE BY MASK
	TEST	@HF_CNTRL,AH		; MULTIPLE-DATA RATE DETERMINED ?
	JNZ	SAVE_SET		; IF SO, NO NEED TO RE-SAVE

;-----	ERASE DRIVE BITS IN @HF_CNTRL FOR THIS DRIVE

	MOV	AH,DRV_DET+FMT_CAPA+TRK_CAPA	; MASK TO KEEP
	ROR	AH,CL			; FIX MASK TO KEEP
	NOT	AH			; TRANSLATE MASK
	AND	@HF_CNTRL,AH		; KEEP BITS FROM OTHER DRIVE INTACT

;-----	ACCESS CURRENT DRIVE BITS AND STORE IN @HF_CNTRL

	MOV	AL,@DSK_STATE[DI]	; ACCESS STATE
	AND	AL,DRV_DET+FMT_CAPA+TRK_CAPA	; KEEP DRIVE BITS
	ROR	AL,CL			; FIX FOR THIS DRIVE
	OR	@HF_CNTRL,AL		; UPDATE SAVED DRIVE STATE

;-----	TRANSLATE TO COMPATIBILITY MODE

SAVE_SET:
	MOV	AH,@DSK_STATE[DI]	; ACCESS STATE
	MOV	BH,AH			; TO BH FOR LATER
	AND	AH,RATE_MSK		; KEEP ONLY RATE
	CMP	AH,RATE_500		; RATE 500 ?
	JZ	CHK_144 		; YES, 1.2/1.2 OR 1.44/1.44
	MOV	AL,M3D1U		; AL = 360 IN 1.2 UNESTABLISHED
	CMP	AH,RATE_300		; RATE 300 ?
	JNZ	CHK_250 		; IF SO FALL THRU
	TEST	BH,DBL_STEP		; CHECK FOR DOUBLE STEP
	JNZ	TST_DET 		; MUST BE 360 IN 1.2

UNKNO:
	MOV	AL,MED_UNK		; 'NONE OF THE ABOVE'
	JMP	SHORT AL_SET		; PROCESS COMPLETE

CHK_144:
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN (AL)
	JC	UNKNO			; ERROR, SET 'NONE OF THE ABOVE'
	CMP	AL,02			; 1.2MB DRIVE ?
	JNE	UNKNO			; NO, GO SET 'NONE OF THE ABOVE'
	MOV	AL,M1D1U		; AL = 1.2 IN 1.2 UNESTABLISHED
	JMP	SHORT TST_DET

CHK_250:
	MOV	AL,M3D3U		; AL = 360 IN 360 UNESTABLISHED
	CMP	AH,RATE_250		; RATE 250 ?
	JNZ	UNKNO			; IF SO FALL THRU
	TEST	BH,TRK_CAPA		; 80 TRACK CAPABILITY ?
	JNZ	UNKNO			; IF SO JUMP, FALL THRU TEST DET

TST_DET:
	TEST	BH,MED_DET		; DETERMINED ?
	JZ	AL_SET			; IF NOT THEN SET
	ADD	AL,3			; MAKE DETERMINED/ESTABLISHED

AL_SET:
	AND	@DSK_STATE[DI],NOT DRV_DET+FMT_CAPA+TRK_CAPA  ; CLEAR DRIVE
	OR	@DSK_STATE[DI],AL	; REPLACE WITH COMPATIBLE MODE
XO_OUT:
	RET
XLAT_OLD	ENDP
PAGE
;----------------------------------------------------------------
; RD_WR_VF							:
;	COMMON READ, WRITE AND VERIFY				:
;	MAIN LOOP FOR STATE RETRIES.				:
;								:
; ON ENTRY:	AH : READ/WRITE/VERIFY DMA PARAMETER		:
;		AL : READ/WRITE/VERIFY NEC PARAMETER		:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
RD_WR_VF	PROC	NEAR
	PUSH	AX			; SAVE DMA, NEC PARAMETERS
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	CALL	SETUP_STATE		; INITIALIZE START AND END RATE
	POP	AX			; RESTORE READ/WRITE/VERIFY

DO_AGAIN:
	PUSH	AX			; SAVE READ/WRITE/VERIFY PARAMETER
	CALL	MED_CHANGE		; MEDIA CHANGE AND RESET IF CHANGED
	POP	AX			; RESTORE READ/WRITE/VERIFY
;	JC	RWV_END 		; MEDIA CHANGE ERROR OR TIME-OUT
	JNC	RWV
	JMP	RWV_END
RWV:
	PUSH	AX			; SAVE READ/WAIT/VERIFY PARAMETER

	MOV	DH,@DSK_STATE[DI]	; GET RATE STATE OF THIS DRIVE
	AND	DH,RATE_MSK		; KEEP ONLY RATE
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN (AL)
	JC	RWV_ASSUME		; ERROR IN CMOS
	CMP	AL,1			; 40 TRACK DRIVE
	JNE	RWV_1			; NO, BYPASS CMOS VALIDITY CHECK
	TEST	@DSK_STATE[DI],TRK_CAPA ; CHECK FOR 40 TRACK DRIVE
	JZ	RWV_2			; YES, CMOS IS CORRECT
	MOV	AL,2			; CHANGE TO 1.2 M
	JMP	SHORT RWV_2		; CONTINUE
RWV_1:
	JB	RWV_2			; NO DRIVE SPECIFED, CONTINUE
	TEST	@DSK_STATE[DI],TRK_CAPA ; IS IT REALLY 40 TRACK?
	JNZ	RWV_2			; NO, 80 TRACK
	MOV	AL,1			; IT'S 40 TRACK, FIX CMOS VALUE
RWV_2:

	OR	AL,AL			; TEST FOR NO DRIVE
	JZ	RWV_ASSUME		; ASSUME TYPE, USE MAX TRACK
	CALL	DR_TYPE_CHECK		; RTN CS:BX = MEDIA/DRIVE PARAM TBL
	JC	RWV_ASSUME		; TYPE NOT IN TABLE (BAD CMOS)

;--- SEARCH FOR MEDIA/DRIVE PARAMETER TABLE

	PUSH	DI			; SAVE DRIVE #
	XOR	BX,BX			; BX = INDEX TO DR_TYPE TABLE
	MOV	CX,DR_CNT		; CX = LOOP COUNT
RWV_DR_SEARCH:
	MOV	AH,CS:DR_TYPE[BX]	; GET DRIVE TYPE
	AND	AH,BIT7OFF		; MASK OUT MSB
	CMP	AL,AH			; DRIVE TYPE MATCH ?
	JNE	RWV_NXT_MD		; NO, CHECK NEXT DRIVE TYPE
RWV_DR_FND:
	MOV	DI,CS:WORD PTR DR_TYPE[BX+1]	; DI = MEDIA/DRIVE PARAMETER TABLE
RWV_MD_SEARCH:
	CMP	DH,CS:[DI].MD_RATE	; MATCH ?
	JE	RWV_MD_FND		; YES, GO GET 1ST SPECIFY BYTE
RWV_NXT_MD:
	ADD	BX,3			; CHECK NEXT DRIVE TYPE
	LOOP	RWV_DR_SEARCH
	POP	DI			; RESTORE DRIVE #

;--- ASSUME PRIMARY DRIVE IS INSTALLED AS SHIPPED

RWV_ASSUME:
	MOV	BX,OFFSET MD_TBL1	; POINT TO 40 TK 250 KBS
	TEST	@DSK_STATE[DI],TRK_CAPA ; TEST FOR 80 TRACK
	JZ	RWV_MD_FND1		; MUST BE 40 TRACK
	MOV	BX,OFFSET MD_TBL3	; POINT TO 80 TK 500 KBS
	JMP	RWV_MD_FND1		; GO SET SPECIFY PARAMETERS

;--- CS:BX POINTS TO MEDIA/DRIVE PARAMETER TABLE

RWV_MD_FND:

	MOV	BX,DI			; BX = MEDIA/DRIVE PARAMETER TABLE
	POP	DI			; RESTORE DRIVE #
RWV_MD_FND1:

;--- SEND THE SPECIFY COMMAND TO THE CONTROLLER

	CALL	SEND_SPEC_MD
	CALL	CHK_LASTRATE		; ZF=1 ATTEMPT RATE IS SAME AS LAST RATE
	JZ	RWV_DBL 		; YES, SKIP SEND RATE COMMAND
	CALL	SEND_RATE		; SEND DATA RATE TO NEC


RWV_DBL:
	PUSH	BX			; SAVE MEDIA DRIVE PARAM ADDRESS
	CALL	SETUP_DBL		; CHECK FOR DOUBLE STEP
	POP	BX			; RESTORE ADDRESS
	JC	CHK_RET 		; ERROR FROM READ ID, POSSIBLE RETRY
	POP	AX			; RESTORE NEC,DMA COMMAND
	PUSH	AX			; SAVE NEC COMMAND
	PUSH	BX			; SAVE MEDIA DRIVE PARAM ADDRESS
	CALL	DMA_SETUP		; SETUP THE DMA
	POP	BX			; RESTORE ADDRESS
	POP	AX			; RESTORE NEC COMMAND
	JC	RWV_BAC 		; CHECK FOR DMA BOUNDARY ERROR
	PUSH	AX			; SAVE NEC COMMAND
	PUSH	BX			; SAVE MEDIA DRIVE PARAM ADDRESS
	CALL	NEC_INIT		; INITIALIZE NEC
	POP	BX			; RESTORE ADDRESS
	JC	CHK_RET 		; ERROR - EXIT
	CALL	RWV_COM 		; OP CODE COMMON TO READ/WRITE/VERIFY
	JC	CHK_RET 		; ERROR - EXIT
	CALL	NEC_TERM		; TERMINATE, GET STATUS, ETC.

CHK_RET:
	CALL	RETRY			; CHECK FOR, SETUP RETRY
	POP	AX			; RESTORE READ/WRITE/VERIFY PARAMETER
	JNC	RWV_END 		; CY = 0 NO RETRY
	JMP	DO_AGAIN		; CY = 1 MEANS RETRY

RWV_END:
	CALL	DSTATE			; ESTABLISH STATE IF SUCCESSFUL
	CALL	NUM_TRANS		; AL = NUMBER TRANSFERRED

RWV_BAC:				; BAD DMA ERROR ENTRY
	PUSH	AX			; SAVE NUMBER TRANSFERRED
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	POP	AX			; RESTORE NUMBER TRANSFERRED
	CALL	SETUP_END		; VARIOUS CLEANUPS
	RET
RD_WR_VF	ENDP
;----------------------------------------------------------------
; SETUP_STATE:	INITIALIZES START AND END RATES.		:
;----------------------------------------------------------------
SETUP_STATE	PROC	NEAR
	TEST	@DSK_STATE[DI],MED_DET	; MEDIA DETERMINED ?
	JNZ	J1C			; NO STATES IF DETERMINED
	MOV	AX,RATE_500*H+RATE_300	; AH = START RATE, AL = END RATE
	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE ?
	JZ	AX_SET			; DO NOT KNOW DRIVE
	TEST	@DSK_STATE[DI],FMT_CAPA ; MULTI-RATE ?
	JNZ	AX_SET			; JUMP WITH FIXED END RATE
	MOV	AX,RATE_250*X		; START A END RATE 250 FOR 360 DRIVE

AX_SET:
	AND	@DSK_STATE[DI],NOT RATE_MSK+DBL_STEP ; TURN OFF THE RATE
	OR	@DSK_STATE[DI],AH	; RATE FIRST TO TRY
	AND	@LASTRATE,NOT STRT_MSK	; ERASE LAST TO TRY RATE BITS
	ROR	AL,4			; TO OPERATION LAST RATE LOCATION
	OR	@LASTRATE,AL		; LAST RATE
J1C:
	RET
SETUP_STATE	ENDP
;----------------------------------------------------------------
;  FMT_INIT: ESTABLISH STATE IF UNESTABLISHED AT FORMAT TIME.	:
;----------------------------------------------------------------
FMT_INIT	PROC	NEAR
	TEST	@DSK_STATE[DI],MED_DET	; IS MEDIA ESTABLISHED
	JNZ	F1_OUT			; IF SO RETURN
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN AL
	JC	CL_DRV			; ERROR IN CMOS ASSUME NO DRIVE
	DEC	AL			; MAKE ZERO ORIGIN
	JS	CL_DRV			; NO DRIVE IF AL 0
	MOV	AH,@DSK_STATE[DI]	; AH = CURRENT STATE
	AND	AH,NOT MED_DET+DBL_STEP+RATE_MSK	; CLEAR
	OR	AL,AL			; CHECK FOR 360
	JNZ	N_360			; IF 360 WILL BE 0
	OR	AH,MED_DET+RATE_250	; ESTABLISH MEDIA
	JMP	SHORT SKP_STATE 	; SKIP OTHER STATE PROCESSING

N_360:
	DEC	AL			; 1.2 M DRIVE
	JNZ	N_12			; JUMP IF NOT
F1_RATE:OR	AH,MED_DET+RATE_500	; SET FORMAT RATE
	JMP	SHORT SKP_STATE 	; SKIP OTHER STATE PROCESSING

N_12:
	DEC	AL			; CHECK FOR TYPE 3
	JNZ	N_720			; JUMP IF NOT
	TEST	AH,DRV_DET		; IS DRIVE DETERMINED
	JZ	ISNT_12 		; TREAT AS NON 1.2 DRIVE
	TEST	AH,FMT_CAPA		; IS 1.2M
	JZ	ISNT_12 		; JUMP IF NOT
	OR	AH,MED_DET+RATE_300	; RATE 300
	JMP	SHORT SKP_STATE 	; CONTINUE

N_720:
	DEC	AL			; CHECK FOR TYPE 4
	JNZ	CL_DRV			; NO DRIVE, CMOS BAD
	JMP	SHORT F1_RATE

ISNT_12:
	OR	AH,MED_DET+RATE_250	; MUST BE RATE 250

SKP_STATE:
	MOV	@DSK_STATE[DI],AH	; STORE AWAY

F1_OUT:
	RET

CL_DRV:
	XOR	AH,AH			; CLEAR STATE
	JMP	SHORT SKP_STATE 	; SAVE IT
FMT_INIT	ENDP
;----------------------------------------------------------------
; MED_CHANGE							:
;	CHECKS FOR MEDIA CHANGE. RESETS MEDIA CHANGE,		:
;	CHECKS MEDIA CHANGE AGAIN.				:
;								:
; ON EXIT:	CY = 1 MEANS MEDIA CHANGE OR TIMEOUT		:
;		@DSKETTE_STATUS = ERROR CODE			:
;----------------------------------------------------------------
MED_CHANGE	PROC	NEAR
	CALL	READ_DSKCHNG		; READ DISK CHANCE LINE STATE
	JZ	MC_OUT			; BYPASS HANDLING DISK CHANGE LINE
	AND	@DSK_STATE[DI],NOT MED_DET	; CLEAR STATE FOR THIS DRIVE

;	THIS SEQUENCE ENSURES WHENEVER A DISKETTE IS CHANGED THAT
;	ON THE NEXT OPERATION THE REQUIRED MOTOR START UP TIME WILL
;	BE WAITED. (DRIVE MOTOR MAY GO OFF UPON DOOR OPENING).

	MOV	CX,DI			; CL = DRIVE 0
	MOV	AL,1			; MOTOR ON BIT MASK
	SHL	AL,CL			; TO APPROPRIATE POSITION
	NOT	AL			; KEEP ALL BUT MOTOR ON
	CLI				; NO INTERRUPTS
	AND	@MOTOR_STATUS,AL	; TURN MOTOR OFF INDICATOR
	STI				; INTERRUPTS ENABLED
	CALL	MOTOR_ON		; TURN MOTOR ON

;-----	THIS SEQUENCE OF SEEKS IS USED TO RESET DISKETTE CHANGE SIGNAL

	CALL	DISK_RESET		; RESET NEC
	MOV	CH,01H			; MOVE TO CYLINDER 1
	CALL	SEEK			; ISSUE SEEK
	XOR	CH,CH			; MOVE TO CYLINDER 0
	CALL	SEEK			; ISSUE SEEK
	MOV	@DSKETTE_STATUS,MEDIA_CHANGE	; STORE IN STATUS

OK_1:	CALL	READ_DSKCHNG		; CHECK MEDIA CHANGED AGAIN
	JZ	OK_2			; IF ACTIVE, NO DISKETTE, TIMEOUT

OK_4:	MOV	@DSKETTE_STATUS,TIME_OUT; TIMEOUT IF DRIVE EMPTY

OK_2:	STC				; MEDIA CHANGED, SET CY
	RET
MC_OUT:
	CLC				; NO MEDIA CHANGED, CLEAR CY
	RET
MED_CHANGE	ENDP
;----------------------------------------------------------------
; SEND_RATE							:
;	SENDS DATA RATE COMMAND TO NEC				:
; ON ENTRY:	DI = DRIVE #					:
; ON EXIT:	NONE						:
; REGISTERS ALTERED: DX 					:
;----------------------------------------------------------------
SEND_RATE	PROC	NEAR

	PUSH	AX			; SAVE REG.
	AND	@LASTRATE,NOT SEND_MSK	; ELSE CLEAR LAST RATE ATTEMPTED
	MOV	AL,@DSK_STATE[DI]	; GET RATE STATE OF THIS DRIVE
	AND	AL,SEND_MSK		; KEEP ONLY RATE BITS
	OR	@LASTRATE,AL		; SAVE NEW RATE FOR NEXT CHECK
	ROL	AL,2			; MOVE TO BIT OUTPUT POSITIONS
	MOV	DX,03F7H		; OUTPUT NEW DATA RATE
	OUT	DX,AL

	POP	AX			; RESTORE REG.
	RET
SEND_RATE	ENDP

;----------------------------------------------------------------
; CHK_LASTRATE							:
;	CHECK PREVIOUS DATA RATE SENT TO CONTROLLER.		:
; ON ENTRY:							:
;	DI = DRIVE #						:
; ON EXIT:							:
;	ZF = 1	 DATA RATE IS THE SAME AS LAST RATE SENT TO NEC :
;	ZF = 0	 DATA RATE IS DIFFERENT FROM LAST RATE		:
; REGISTERS ALTERED: NONE					:
;----------------------------------------------------------------
CHK_LASTRATE	PROC	NEAR
	PUSH	AX			; SAVE REG.
	MOV	AH,@LASTRATE		; GET LAST DATA RATE SELECTED
	MOV	AL,@DSK_STATE[DI]	; GET RATE STATE OF THIS DRIVE
	AND	AX,SEND_MSK*X		; KEEP ONLY RATE BITS OF BOTH
	CMP	AL,AH			; COMPARE TO PREVIOUSLY TRIED
					; ZF = 1  RATE IS THE SAME
	POP	AX			; RESTORE REG.
	RET
CHK_LASTRATE	ENDP

SUBTTL (DSK3.ASM)
PAGE
;----------------------------------------------------------------
; DMA_SETUP							:
;	THIS ROUTINE SETS UP THE DMA FOR READ/WRITE/VERIFY	:
;	OPERATIONS.						:
;								:
; ON ENTRY:	AL = DMA COMMAND				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
DMA_SETUP	PROC	NEAR
	CLI				; DISABLE INTERRUPTS DURING DMA SET-UP
	OUT	DMA+12,AL		; SET THE FIRST/LAST F/F
	JMP	$+2			; WAIT FOR I/O
	OUT	DMA+11,AL		; OUTPUT THE MODE BYTE
	CMP	AL,42H			; DMA VERIFY COMMAND
	JNE	NOT_VERF		; NO
	XOR	AX,AX			; START ADDRESS
	JMP	SHORT J33
NOT_VERF:
	MOV	AX,ES			; GET THE ES VALUE
	ROL	AX,4			; ROTATE LEFT
	MOV	CH,AL			; GET HIGHEST NIBBLE OF ES TO CH
	AND	AL,11110000B		; ZERO THE LOW NIBBLE FROM SEGMENT
	ADD	AX,[BP+2]		; TEST FOR CARRY FROM ADDITION
	JNC	J33
	INC	CH			; CARRY MEANS HIGH 4 BITS MUST BE INC
J33:
	PUSH	AX			; SAVE START ADDRESS
	OUT	DMA+4,AL		; OUTPUT LOW ADDRESS
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+4,AL		; OUTPUT HIGH ADDRESS
	MOV	AL,CH			; GET HIGH 4 BITS
	JMP	$+2			; I/O WAIT STATE
	AND	AL,00001111B
	OUT	081H,AL 		; OUTPUT HIGH 4 BITS TO PAGE REGISTER

;-----	DETERMINE COUNT

	MOV	AX,SI			; AL = # OF SECTORS
	XCHG	AL,AH			; AH = # OF SECTORS
	SUB	AL,AL			; AL = 0, AX = # OF SECTORS * 256
	SHR	AX,1			; AX = # SECTORS * 128
	PUSH	AX			; SAVE # OF SECTORS * 128
	MOV	DL,3			; GET BYTES/SECTOR PARAMETER
	CALL	GET_PARM		; "
	MOV	CL,AH			; SHIFT COUNT (0=128, 1=256 ETC)
	POP	AX			; AX = # OF SECTORS * 128
	SHL	AX,CL			; SHIFT BY PARAMETER VALUE
	DEC	AX			; -1 FOR DMA VALUE
	PUSH	AX			; SAVE COUNT VALUE
	OUT	DMA+5,AL		; LOW BYTE OF COUNT
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+5,AL		; HIGH BYTE OF COUNT
	STI				; RE-ENABLE INTERRUPTS
	POP	CX			; RECOVER COUNT VALUE
	POP	AX			; RECOVER ADDRESS VALUE
	ADD	AX,CX			; ADD, TEST FOR 64K OVERFLOW
	MOV	AL,2			; MODE FOR 8237
	JMP	$+2			; WAIT FOR I/O
	OUT	DMA+10,AL		; INITIALIZE THE DISKETTE CHANNEL

	JNC	NO_BAD			; CHECK FOR ERROR
	MOV	@DSKETTE_STATUS,DMA_BOUNDARY	; SET ERROR
NO_BAD:
	RET				; CY SET BY ABOVE IF ERROR
DMA_SETUP	ENDP
PAGE
;----------------------------------------------------------------
; FMTDMA_SET							:
;	THIS ROUTINE SETS UP THE DMA CONTROLLER FOR A FORMAT	:
;	OPERATION.						:
;								:
; ON ENTRY:	NOTHING REQUIRED				:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
FMTDMA_SET	PROC	NEAR
	MOV	AL,04AH 		; WILL WRITE TO THE DISKETTE
	CLI				; DISABLE INTERRUPTS DURING DMA SET-UP
	OUT	DMA+12,AL		; SET THE FIRST/LAST F/F
	JMP	$+2			; WAIT FOR I/O
	OUT	DMA+11,AL		; OUTPUT THE MODE BYTE

	MOV	AX,ES			; GET THE ES VALUE
	ROL	AX,4			; ROTATE LEFT
	MOV	CH,AL			; GET HIGHEST NIBBLE OF ES TO CH
	AND	AL,11110000B		; ZERO THE LOW NIBBLE FROM SEGMENT
	ADD	AX,[BP+2]		; TEST FOR CARRY FROM ADDITION
	JNC	J33A
	INC	CH			; CARRY MEANS HIGH 4 BITS MUST BE INC
J33A:
	PUSH	AX			; SAVE START ADDRESS
	OUT	DMA+4,AL		; OUTPUT LOW ADDRESS
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+4,AL		; OUTPUT HIGH ADDRESS
	MOV	AL,CH			; GET HIGH 4 BITS
	JMP	$+2			; I/O WAIT STATE
	AND	AL,00001111B
	OUT	081H,AL 		; OUTPUT HIGH 4 BITS TO PAGE REGISTER

;-----	DETERMINE COUNT

	MOV	DL,4			; SECTORS/TRACK VALUE IN PARM TABLE
	CALL	GET_PARM		; "
	XCHG	AL,AH			; AL = SECTORS/TRACK VALUE
	SUB	AH,AH			; AX = SECTORS/TRACK VALUE
	SHL	AX,2			; AX = SEC/TRK * 4 (OFFSET FOR C,H,R,N)
	DEC	AX			;-1 FOR DMA VALUE
	PUSH	AX			; SAVE # OF BYTES TO BE TRANSFERED
	OUT	DMA+5,AL		; LOW BYTE OF COUNT
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+5,AL		; HIGH BYTE OF COUNT
	STI				; RE-ENABLE INTERRUPTS
	POP	CX			; RECOVER COUNT VALUE
	POP	AX			; RECOVER ADDRESS VALUE
	ADD	AX,CX			; ADD, TEST FOR 64K OVERFLOW
	MOV	AL,2			; MODE FOR 8237
	JMP	$+2			; WAIT FOR I/O
	OUT	DMA+10,AL		; INITIALIZE THE DISKETTE CHANNEL

	JNC	FMTDMA_OK		; CHECK FOR ERROR
	MOV	@DSKETTE_STATUS,DMA_BOUNDARY	; SET ERROR

FMTDMA_OK:
	RET				; CY SET BY ABOVE IF ERROR
FMTDMA_SET	ENDP
;----------------------------------------------------------------
; NEC_INIT							:
;	THIS ROUTINE SEEKS TO THE REQUESTED TRACK AND		:
;	INITIALIZES THE NEC FOR THE READ/WRITE/VERIFY/FORMAT	:
;	OPERATION.						:
;								:
; ON ENTRY:	AH : NEC COMMAND TO BE PERFORMED		:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
NEC_INIT	PROC	NEAR
	PUSH	AX			; SAVE NEC COMMAND
	CALL	MOTOR_ON		; TURN MOTOR ON FOR SPECIFIC DRIVE

;-----	DO THE SEEK OPERATION

	MOV	CH,[BP+1]		; CH = TRACK #
	CALL	SEEK			; MOVE TO CORRECT TRACK
	POP	AX			; RECOVER COMMAND
	JC	ER_1			; ERROR ON SEEK
	MOV	BX,OFFSET ER_1		; LOAD ERROR ADDRESS
	PUSH	BX			; PUSH NEC_OUT ERROR RETURN

;-----	SEND OUT THE PARAMETERS TO THE CONTROLLER

	CALL	NEC_OUTPUT		; OUTPUT THE OPERATION COMMAND
	MOV	AX,SI			; AH = HEAD #
	MOV	BX,DI			; BL = DRIVE #
	SAL	AH,2			; MOVE IT TO BIT 2
	AND	AH,00000100B		; ISOLATE THAT BIT
	OR	AH,BL			; OR IN THE DRIVE NUMBER
	CALL	NEC_OUTPUT		; FALL THRU CY SET IF ERROR
	POP	BX			; THROW AWAY ERROR RETURN
ER_1:
	RET
NEC_INIT		ENDP
;----------------------------------------------------------------
; RWV_COM							:
;	THIS ROUTINE SENDS PARAMETERS TO THE NEC SPECIFIC	:
;	TO THE READ/WRITE/VERIFY OPERATIONS.			:
;								:
; ON ENTRY:	CS:BX = ADDRESS OF MEDIA/DRIVE PARAMETER TABLE	:
; ON EXIT :	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
RWV_COM PROC	NEAR
	MOV	AX,OFFSET ER_2		; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	AH,[BP+1]		; OUTPUT TRACK #
	CALL	NEC_OUTPUT
	MOV	AX,SI			; OUTPUT HEAD #
	CALL	NEC_OUTPUT
	MOV	AH,[BP] 		; OUTPUT SECTOR #
	CALL	NEC_OUTPUT
	MOV	DL,3			; BYTES/SECTOR PARAMETER FROM BLOCK
	CALL	GET_PARM		; . TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER
	MOV	DL,4			; EOT PARAMETER FROM BLOCK
	CALL	GET_PARM		; . TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER

	MOV	AH,CS:[BX].MD_GAP	 ; GET GAP LENGTH
R15:	CALL	NEC_OUTPUT
	MOV	DL,6			; DTL PARAMETER PROM BLOCK
	CALL	GET_PARM		;  TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER
	POP	AX			; THROW AWAY ERROR EXIT
ER_2:
	RET
RWV_COM 	ENDP
;----------------------------------------------------------------
; NEC_TERM							:
;	THIS ROUTINE WAITS FOR THE OPERATION THEN ACCEPTS	:
;	THE STATUS FROM THE NEC FOR THE READ/WRITE/VERIFY/	:
;	FORMAT OPERATION.					:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
NEC_TERM	PROC	NEAR

;-----	LET THE OPERATION HAPPEN

	PUSH	SI			; SAVE HEAD #, # OF SECTORS
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	PUSHF
	CALL	RESULTS 		; GET THE NEC STATUS
	JC	SET_END_POP
	POPF
	JC	SET_END 		; LOOK FOR ERROR

;-----	CHECK THE RESULTS RETURNED BY THE CONTROLLER

	CLD				; SET THE CORRECT DIRECTION
	MOV	SI,OFFSET @NEC_STATUS	; POINT TO STATUS FIELD
	LODS	@NEC_STATUS		; GET ST0
	AND	AL,11000000B		; TEST FOR NORMAL TERMINATION
	JZ	SET_END 		;
	CMP	AL,01000000B		; TEST FOR ABNORMAL TERMINATION
	JNZ	J18			; NOT ABNORMAL, BAD NEC

;-----	ABNORMAL TERMINATION, FIND OUT WHY

	LODS	@NEC_STATUS		; GET STI
	SAL	AL,1			; TEST FOR EOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19
	SAL	AL,2
	MOV	AH,BAD_CRC
	JC	J19
	SAL	AL,1			; TEST FOR DMA OVERRUN
	MOV	AH,BAD_DMA
	JC	J19
	SAL	AL,2			; TEST FOR RECORD NOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19
	SAL	AL,1
	MOV	AH,WRITE_PROTECT	; TEST FOR WRITE_PROTECT
	JC	J19
	SAL	AL,1			; TEST MISSING ADDRESS MARK
	MOV	AH,BAD_ADDR_MARK
	JC	J19

;-----	NEC MUST HAVE FAILED
J18:
	MOV	AH,BAD_NEC
J19:
	OR	@DSKETTE_STATUS,AH
SET_END:
	CMP	@DSKETTE_STATUS,1	; SET ERROR CONDITION
	CMC				;
	POP	SI			; RESTORE HEAD #, # OF SECTORS
	RET

SET_END_POP:
	POPF
	JMP	SHORT SET_END
NEC_TERM	ENDP
;----------------------------------------------------------------
; DSTATE:	ESTABLISH STATE UPON SUCCESSFUL OPERATION.	:
;----------------------------------------------------------------
DSTATE	PROC	NEAR
	CMP	@DSKETTE_STATUS,0	; CHECK FOR ERROR
	JNZ	SETBAC			; IF ERROR JUMP
	OR	@DSK_STATE[DI],MED_DET	; NO ERROR, MARK MEDIA AS DETERMINED
	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE DETERMINED ?
	JNZ	SETBAC			; IF DETERMINED NO TRY TO DETERMINE
	MOV	AL,@DSK_STATE[DI]	; LOAD STATE
	AND	AL,RATE_MSK		; KEEP ONLY RATE
	CMP	AL,RATE_250		; RATE 250 ?
	JNE	M_12			; NO, MUST BE 1.2M OR 1.44M DRV

;--- CHECK IF IT IS 1.44M

	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN (AL)
	JC	M_12			; CMOS BAD
	CMP	AL,04			; 1.44MB DRIVE ?
	JE	M_12			; YES
M_720:
	AND	@DSK_STATE[DI],NOT FMT_CAPA ; TURN OFF FORMAT CAPA
	OR	@DSK_STATE[DI],DRV_DET	; MARK DRIVE DETERMINED
	JMP	SHORT SETBAC		; BACK

M_12:	OR	@DSK_STATE[DI],DRV_DET+FMT_CAPA ; TURN ON DETERMINED & FMT CAPA

SETBAC:
	RET
DSTATE	ENDP
;----------------------------------------------------------------
; RETRY 							:
;	DETERMINES WHETHER A RETRY IS NECESSARY. IF RETRY IS	:
;	REQUIRED THEN STATE INFORMATION IS UPDATED FOR RETRY.	:
;								:
; ON EXIT:	CY = 1 FOR RETRY, CY = 0 FOR NO RETRY		:
;----------------------------------------------------------------
RETRY	PROC	NEAR
	CMP	@DSKETTE_STATUS,0	; GET STATUS OF OPERATION
	JZ	NO_RETRY		; SUCCESSFUL OPERATION
	CMP	@DSKETTE_STATUS,TIME_OUT	; IF TIME OUT NO RETRY
	JZ	NO_RETRY
	MOV	AH,@DSK_STATE[DI]	; GET MEDIA STATE OF DRIVE
	TEST	AH,MED_DET		; ESTABLISHED/DETERMINED ?
	JNZ	NO_RETRY		; IF ESTABLISHED STATE THEN TRUE ERROR
	AND	AH,RATE_MSK		; ISOLATE RATE
	MOV	CH,@LASTRATE		; GET START OPERATION STATE
	ROL	CH,4			; TO CORRESPONDING BITS
	AND	CH,RATE_MSK		; ISOLATE RATE BITS
	CMP	CH,AH			; ALL RATES TRIED
	JE	NO_RETRY		; IF YES, THEN TRUE ERROR

;	SETUP STATE INDICATOR FOR RETRY ATTEMPT TO NEXT RATE
;	  00000000B (500) -> 10000000B (250)
;	  10000000B (250) -> 01000000B (300)
;	  01000000B (300) -> 00000000B (500)

	CMP	AH,RATE_500+1		; SET CY FOR RATE 500
	RCR	AH,1			; TO NEXT STATE
	AND	AH,RATE_MSK		; KEEP ONLY RATE BITS
	AND	@DSK_STATE[DI],NOT RATE_MSK+DBL_STEP	; RATE, DBL STEP OFF
	OR	@DSK_STATE[DI],AH	; TURN ON NEW RATE
	MOV	@DSKETTE_STATUS,0	; RESET STATUS FOR RETRY
	STC				; SET CARRY FOR RETRY
	RET				; RETRY RETURN

NO_RETRY:
	CLC				; CLEAR CARRY NO RETRY
	RET				; NO RETRY RETURN
RETRY	ENDP
;----------------------------------------------------------------
; NUM_TRANS							:
;	THIS ROUTINE CALCULATES THE NUMBER OF SECTORS THAT	:
;	WERE ACTUALLY TRANSFERRED TO/FROM THE DISKETTE. 	:
;								:
; ON ENTRY:	[BP+1] = TRACK					:
;		SI-HI  = HEAD					:
;		[BP]   = START SECTOR				:
;								:
; ON EXIT:	AL = NUMBER ACTUALLY TRANSFERRED		:
;----------------------------------------------------------------
NUM_TRANS	PROC	NEAR
	XOR	AL,AL			; CLEAR FOR ERROR
	CMP	@DSKETTE_STATUS,0	; CHECK FOR ERROR
	JNZ	NT_OUT			; IF ERROR 0 TRANSFERRED
	MOV	DL,4			; SECTORS/TRACK OFFSET TO DL
	CALL	GET_PARM		; AH = SECTORS/TRACK
	MOV	BL,@NEC_STATUS+5	; GET ENDING SECTOR
	MOV	CX,SI			; CH = HEAD # STARTED
	CMP	CH,@NEC_STATUS+4	; GET HEAD ENDED UP ON
	JNZ	DIF_HD			; IF ON SAME HEAD, THEN NO ADJUST

	MOV	CH,@NEC_STATUS+3	; GET TRACK ENDED UP ON
	CMP	CH,[BP+1]		; IS IT ASKED FOR TRACK
	JZ	SAME_TRK		; IF SAME TRACK NO INCREASE

	ADD	BL,AH			; ADD SECTORS/TRACK
DIF_HD:
	ADD	BL,AH			; ADD SECTORS/TRACK
SAME_TRK:
	SUB	BL,[BP] 		; SUBTRACT START FROM END
	MOV	AL,BL			; TO AL

NT_OUT:
	RET
NUM_TRANS	ENDP
;----------------------------------------------------------------
; SETUP_END							:
;	RESTORES @MOTOR_COUNT TO PARAMETER PROVIDED IN TABLE	:
;	AND LOADS @DSKETTE_STATUS TO AH, AND SETS CY.		:
; ON EXIT:							:
;	AH, @DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION	:
;----------------------------------------------------------------
SETUP_END	PROC	NEAR
	MOV	DL,2			; GET THE MOTOR WAIT PARAMETER
	PUSH	AX			; SAVE NUMBER TRANSFERRED
	CALL	GET_PARM
	MOV	@MOTOR_COUNT,AH 	; STORE UPON RETURN
	POP	AX			; RESTORE NUMBER TRANSFERRED
	MOV	AH,@DSKETTE_STATUS	; GET STATUS OF OPERATION
	OR	AH,AH			; CHECK FOR ERROR
	JZ	NUN_ERR 		; NO ERROR
	XOR	AL,AL			; CLEAR NUMBER RETURNED

NUN_ERR:
	CMP	AH,1			; SET THE CARRY FLAG TO INDICATE
	CMC				; SUCCESS OR FAILURE
	RET
SETUP_END	ENDP
PAGE
;----------------------------------------------------------------
; SETUP_DBL							:
;	CHECK DOUBLE STEP.					:
; ON ENTRY:							:
;		DI = DRIVE					:
; ON EXIT :	CY = 1 MEANS ERROR				:
;----------------------------------------------------------------
SETUP_DBL	PROC	NEAR
	MOV	AH,@DSK_STATE[DI]	; ACCESS STATE
	TEST	AH,MED_DET		; ESTABLISHED STATE ?
	JNZ	NO_DBL			; IF ESTABLISHED THEN DOUBLE DONE

;-----	CHECK FOR TRACK 0 TO SPEED UP ACKNOWLEDGE OF UNFORMATTED DISKETTE

	MOV	@SEEK_STATUS,0		; SET RECALIBRATE REQUIRED ON ALL DRIVES
	CALL	MOTOR_ON		; ENSURE MOTOR STAY ON
	MOV	CH,0			; LOAD TRACK 0
	CALL	SEEK			; SEEK TO TRACK 0
	CALL	READ_ID 		; READ ID FUNCTION
	JC	SD_ERR			; IF ERROR NO TRACK 0

;-----	INITIALIZE START AND MAX TRACKS (TIMES 2 FOR BOTH HEADS)

	MOV	CX,0450H		; START, MAX TRACKS
	TEST	@DSK_STATE[DI],TRK_CAPA ; TEST FOR 80 TRACK CAPABILITY
	JZ	CNT_OK			; IF NOT COUNT IS SETUP
	MOV	CL,0A0H 		; MAXIMUM TRACK 1.2 MB

;	ATTEMPT READ ID OF ALL TRACKS, ALL HEADS UNTIL SUCCESS; UPON SUCCESS,
;	MUST SEE IF ASKED FOR TRACK IN SINGLE STEP MODE = TRACK ID READ; IF NOT
;	THEN SET DOUBLE STEP ON.

CNT_OK: MOV	@MOTOR_COUNT,0FFH	; ENSURE MOTOR STAYS ON FOR OPERATION
	PUSH	CX			; SAVE TRACK, COUNT
	MOV	@DSKETTE_STATUS,0	; CLEAR STATUS, EXPECT ERRORS
	XOR	AX,AX			; CLEAR AX
	SHR	CH,1			; HALVE TRACK, CY = HEAD
	RCL	AL,3			; AX = HEAD IN CORRECT BIT
	PUSH	AX			; SAVE HEAD
	CALL	SEEK			; SEEK TO TRACK
	POP	AX			; RESTORE HEAD
	OR	DI,AX			; DI = HEAD OR'ED DRIVE
	CALL	READ_ID 		; READ ID HEAD 0
	PUSHF				; SAVE RETURN FROM READ_ID
	AND	DI,11111011B		; TURN OFF HEAD 1 BIT
	POPF				; RESTORE ERROR RETURN
	POP	CX			; RESTORE COUNT
	JNC	DO_CHK			; IF OK, ASKED = RETURNED TRACK ?
	INC	CH			; INC FOR NEXT TRACK
	CMP	CH,CL			; REACHED MAXIMUM YET
	JNZ	CNT_OK			; CONTINUE TILL ALL TRIED

;-----	FALL THRU, READ ID FAILED FOR ALL TRACKS

SD_ERR: STC				; SET CARRY FOR ERROR
	RET				; SETUP_DBL ERROR EXIT

DO_CHK: MOV	CL,@NEC_STATUS+3	; LOAD RETURNED TRACK
	MOV	@DSK_TRK[DI],CL 	; STORE TRACK NUMBER
	SHR	CH,1			; HALVE TRACK
	CMP	CH,CL			; IS IT THE SAME AS ASKED FOR TRACK
	JZ	NO_DBL			; IF SAME THEN NO DOUBLE STEP
	OR	@DSK_STATE[DI],DBL_STEP ; TURN ON DOUBLE STEP REQUIRED

NO_DBL: CLC				; CLEAR ERROR FLAG
	RET
SETUP_DBL	ENDP
;----------------------------------------------------------------
; READ_ID							:
;	READ ID FUNCTION.					:
; ON ENTRY:	DI : BIT 2 = HEAD; BITS 1,0 = DRIVE		:
;								:
; ON EXIT:	DI : BIT 2 IS RESET, BITS 1,0 = DRIVE		:
;		@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION :
;----------------------------------------------------------------
READ_ID PROC	NEAR
	MOV	AX,OFFSET ER_3		; MOVE NEC OUTPUT ERROR ADDRESS
	PUSH	AX
	MOV	AH,4AH			; READ ID COMMAND
	CALL	NEC_OUTPUT		; TO CONTROLLER
	MOV	AX,DI			; DRIVE # TO AH, HEAD 0
	MOV	AH,AL
	CALL	NEC_OUTPUT		; TO CONTROLLER
	CALL	NEC_TERM		; WAIT FOR OPERATION, GET STATUS
	POP	AX			; THROW AWAY ERROR ADDRESS
ER_3:
	RET
READ_ID ENDP
;----------------------------------------------------------------
; CMOS_TYPE							:
;	RETURNS DISKETTE TYPE FROM CMOS 			:
;								:
; ON ENTRY:	DI : DRIVE #					:
;								:
; ON EXIT:	AL = TYPE; CY REFLECTS STATUS			:
;----------------------------------------------------------------
CMOS_TYPE	PROC	NEAR
	MOV	AL,CMOS_DIAG		; CMOS DIAGNOSTIC STATUS BYTE ADDRESS
	CALL	CMOS_READ		; GET CMOS STATUS
	TEST	AL,BAD_BAT+BAD_CKSUM	; BATTERY GOOD AND CHECKSUM VALID ?
	STC				; SET CY = 1 INDICATING ERROR FOR RETURN
	JNZ	BAD_CM			; ERROR IF EITHER BIT ON

	MOV	AL,CMOS_DISKETTE	; ADDRESS OF DISKETTE BYTE IN CMOS
	CALL	CMOS_READ		; GET DISKETTE BYTE
	OR	DI,DI			; SEE WHICH DRIVE IN QUESTION
	JNZ	TB			; IF DRIVE 1, DATA IN LOW NIBBLE
	ROR	AL,4			; EXCHANGE NIBBLES IF SECOND DRIVE
TB:
	AND	AL,00FH 		; KEEP ONLY DRIVE DATA, RESET CY = 0
BAD_CM:
	RET				; CY = STATUS OF READ
CMOS_TYPE	ENDP
;----------------------------------------------------------------
; GET_PARM							:
;	THIS ROUTINE FETCHES THE INDEXED POINTER FROM THE	:
;	DISK_BASE BLOCK POINTED TO BY THE DATA VARIABLE 	:
;	@DISK_POINTER, A BYTE FROM THAT TABLE IS THEN MOVED	:
;	INTO AH, THE INDEX OF THAT BYTE BEING THE PARAMETER	:
;	IN DL.							:
;								:
; ON ENTRY:	DL = INDEX OF BYTE TO BE FETCHED		:
;								:
; ON EXIT:	AH = THAT BYTE FROM BLOCK			:
;		AL,DH DESTROYED 				:
;----------------------------------------------------------------
GET_PARM	PROC	NEAR
	PUSH	DS
	PUSH	SI
	SUB	AX,AX			; DS = 0 , BIOS DATA AREA
	MOV	DS,AX
	XCHG	DX,BX			; BL = INDEX
	SUB	BH,BH			; BX = INDEX
	ASSUME	DS:ABS0
	LDS	SI,@DISK_POINTER	; POINT TO BLOCK
	MOV	AH,[SI+BX]		; GET THE WORD
	XCHG	DX,BX			; RESTORE BX
	POP	SI
	POP	DS
	RET
	ASSUME	DS:DATA
GET_PARM	ENDP
;------------------------------------------------------------------------
; MOTOR_ON								:
;	TURN MOTOR ON AND WAIT FOR MOTOR START UP TIME. THE @MOTOR_COUNT:
;	IS REPLACED WITH A SUFFICIENTLY HIGH NUMBER (0FFH) TO ENSURE	:
;	THAT THE MOTOR DOES NOT GO OFF DURING THE OPERATION. IF THE	:
;	MOTOR NEEDED TO BE TURNED ON, THE MULTI-TASKING HOOK FUNCTION	:
;	(AX=90FDH, INT 15)  IS CALLED TELLING THE OPERATING SYSTEM	:
;	THAT THE BIOS IS ABOUT TO WAIT FOR MOTOR START UP. IF THIS	:
;	FUNCTION RETURNS WITH CY = 1, IT MEANS THAT THE MINIMUM WAIT	:
;	HAS BEEN COMPLETED. AT THIS POINT A CHECK IS MADE TO ENSURE	:
;	THAT THE MOTOR WASN'T TURNED OFF BY THE TIMER. IF THE HOOK DID  :
;	NOT WAIT, THE WAIT FUNCTION (AH=086H) IS CALLED TO WAIT THE	:
;	PRESCRIBED AMOUNT OF TIME. IF THE CARRY FLAG IS SET ON RETURN,	:
;	IT MEANS THAT THE FUNCTION IS IN USE AND DID NOT PERFORM THE	:
;	WAIT. A TIMER 1 WAIT LOOP WILL THEN DO THE WAIT.		:
;									:
; ON ENTRY:	DI = DRIVE #						:
;									:
; ON EXIT:	AX,BX,CX,DX DESTROYED					:
;------------------------------------------------------------------------
MOTOR_ON	PROC	NEAR
	PUSH	BX			; SAVE REG.
	CALL	TURN_ON 		; TURN ON MOTOR
	JC	MOT_IS_ON		; IF CY=1 NO WAIT
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	MOV	AX,090FDH		; LOAD WAIT CODE & TYPE
	INT	15H			; TELL OPERATING SYSTEM ABOUT TO DO WAIT
	PUSHF				; SAVE CY FOR TEST
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH,
	POPF				; RESTORE CY FOR TEST
	JNC	M_WAIT			; BYPASS LOOP IF OP SYSTEM HANDLED WAIT
	CALL	TURN_ON 		; CHECK AGAIN IF MOTOR ON
	JC	MOT_IS_ON		; IF NO WAIT MEANS IT IS ON

M_WAIT:
	MOV	DL,10			; GET THE MOTOR WAIT PARAMETER
	CALL	GET_PARM
	MOV	AL,AH			; AL = MOTOR WAIT PARAMETER
	XOR	AH,AH			; AX = MOTOR WAIT PARAMETER
	CMP	AL,8			; SEE IF AT LEAST A SECOND IS SPECIFIED
	JAE	GP2			; IF YES, CONTINUE
	MOV	AL,8			; ONE SECOND WAIT FOR MOTOR START UP

;-----	AS CONTAINS NUMBER OF 1/8 SECONDS (125000 MICROSECONDS) TO WAIT

GP2:	PUSH	AX			; SAVE WAIT PARAMETER
	MOV	DX,62500		; LOAD LARGEST POSSIBLE MULTIPLIER
	MUL	DX			; MULTIPLY BY HALF OF WHAT'S NECESSARY
	MOV	CX,DX			; CX = HIGH WORD
	MOV	DX,AX			; CX,DX = 1/2 * (# OF MICROSECONDS)
	CLC				; CLEAR CARRY FOR ROTATE
	RCL	DX,1			; DOUBLE LOW WORD, CY CONTAINS OVERFLOW
	RCL	CX,1			; DOUBLE HI, INCLUDING LOW WORD OVERFLOW
	MOV	AH,86H			; LOAD WAIT CODE
	INT	15H			; PERFORM WAIT
	POP	AX			; RESTORE WAIT PARAMETER
	JNC	MOT_IS_ON		; CY MEANS WAIT COULD NOT BE DONE

;-----	FOLLOWING LOOPS REQUIRED WHEN RTC WAIT FUNCTION IS ALREADY IN USE

J13:					;	WAIT FOR 1/8 SECOND PER (AL)
	MOV	CX,8286 		; COUNT FOR 1/8 SECOND AT 15.085737 US
	CALL	WAITF			; GO TO FIXED WAIT ROUTINE
	DEC	AL			; DECREMENT TIME VALUE
	JNZ	J13			; ARE WE DONE YET

MOT_IS_ON:
	POP	BX			; RESTORE REG.
	RET
MOTOR_ON       ENDP
;----------------------------------------------------------------
; TURN_ON							:
;	TURN MOTOR ON AND RETURN WAIT STATE.			:
;								:
; ON ENTRY:	 DI = DRIVE #					:
;								:
; ON EXIT:	 CY = 0 MEANS WAIT REQUIRED			:
;		 CY = 1 MEANS NO WAIT REQUIRED			:
;		 AX,BX,CX,DX DESTROYED				:
;----------------------------------------------------------------
TURN_ON PROC	NEAR
	MOV	BX,DI			; BX = DRIVE #
	MOV	CL,BL			; CL = DRIVE #
	ROL	BL,4			; BL = DRIVE SELECT
	CLI				; NO INTERRUPTS WHILE DETERMINING STATUS
	MOV	@MOTOR_COUNT,0FFH	; ENSURE MOTOR STAYS ON FOR OPERATION
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,00110000B		; KEEP ONLY DRIVE SELECT BITS
	MOV	AH,1			; MASK FOR DETERMINING MOTOR BIT
	SHL	AH,CL			; AH = MOTOR ON, A=00000001, B=00000010

;  AL = DRIVE SELECT FROM @MOTOR_STATUS
;  BL = DRIVE SELECT DESIRED
;  AH = MOTOR ON MASK DESIRED

	CMP	AL,BL			; REQUESTED DRIVE ALREADY SELECTED ?
	JNZ	TURN_IT_ON		; IF NOT SELECTED JUMP
	TEST	AH,@MOTOR_STATUS	; TEST MOTOR ON BIT
	JNZ	NO_MOT_WAIT		; JUMP IF MOTOR ON AND SELECTED

TURN_IT_ON:
	OR	AH,BL			; AH = DRIVE SELECT AND MOTOR ON
	MOV	BH,@MOTOR_STATUS	; SAVE COPY OF @MOTOR_STATUS BEFORE
	AND	BH,00001111B		; KEEP ONLY MOTOR BITS
	AND	@MOTOR_STATUS,11001111B ; CLEAR OUT DRIVE SELECT
	OR	@MOTOR_STATUS,AH	; OR IN DRIVE SELECTED AND MOTOR ON
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	MOV	BL,AL			; BL=@MOTOR_STATUS AFTER, BH=BEFORE
	AND	BL,00001111B		; KEEP ONLY MOTOR BITS
	STI				; ENABLE INTERRUPTS AGAIN
	AND	AL,00111111B		; STRIP AWAY UNWANTED BITS
	ROL	AL,4			; PUT BITS IN DESIRED POSITIONS
	OR	AL,00001100B		; NO RESET, ENABLE DMA/INTERRUPT
	MOV	DX,03F2H		; SELECT DRIVE AND TURN ON MOTOR
	OUT	DX,AL			; "
	CMP	BL,BH			; NEW MOTOR TURNED ON
	JZ	NO_MOT_WAIT		; NO WAIT REQUIRED IF JUST SELECT
	CLC				; SET CARRY MEANING WAIT
	RET

NO_MOT_WAIT:
	STC				; SET NO WAIT REQUIRED
	STI				; INTERRUPTS BACK ON
	RET
TURN_ON ENDP
;----------------------------------------------------------------
; HD_WAIT							:
;	WAIT FOR HEAD SETTLE TIME.				:
;								:
; ON ENTRY:	DI : DRIVE #					:
;								:
; ON EXIT:	AX,BX,CX,DX DESTROYED				:
;----------------------------------------------------------------
HD_WAIT 	PROC	NEAR
	MOV	DL,9			; GET HEAD SETTLE PARAMETER
	CALL	GET_PARM		; "
	TEST	@MOTOR_STATUS,10000000B ; SEE IF A WRITE OPERATION
	JZ	ISNT_WRITE		; IF NOT, DO NOT ENFORCE ANY VALUES
	OR	AH,AH			; CHECK FOR ANY WAIT?
	JNZ	DO_WAT			; IF THERE DO NOT ENFORCE
	MOV	AH,HD12_SETTLE		; LOAD 1.2M HEAD SETTLE MINIMUM
	MOV	AL,@DSK_STATE[DI]	; LOAD STATE
	AND	AL,RATE_MSK		; KEEP ONLY RATE
	CMP	AL,RATE_250		; 1.2 M DRIVE ?
	JNZ	DO_WAT			; DEFAULT HEAD SETTLE LOADED

GP3:	MOV	AH,HD320_SETTLE 	; USE 320/360 HEAD SETTLE
	JMP	SHORT DO_WAT		; "

ISNT_WRITE:
	OR	AH,AH			; CHECK FOR NO WAIT
	JZ	HW_DONE 		; IF NOT WRITE AND 0 ITS OK

;-----	AH CONTAINS NUMBER OF MILLISECONDS TO WAIT

DO_WAT: MOV	AL,AH			; AL = # MILLISECONDS
	XOR	AH,AH			; AX = # MILLISECONDS
	PUSH	AX			; SAVE HEAD SETTLE PARAMETER
	MOV	DX,1000 		; SET UP FOR MULTIPLY TO MICROSECONDS
	MUL	DX			; DX,AX = # MICROSECONDS
	MOV	CX,DX			; CX,AX = # MICROSECONDS
	MOV	DX,AX			; CX,DX = # MICROSECONDS
	MOV	AH,86H			; LOAD WAIT CODE
	INT	15H			; PERFORM WAIT
	POP	AX			; RESTORE HEAD SETTLE PARAMETER
	JNC	HW_DONE 		; CHECK FOR EVENT WAIT ACTIVE

J29:					;	1 MILLISECOND LOOP
	MOV	CX,66			; COUNT AT 15.085737 US PER COUNT
	CALL	WAITF			; DELAY FOR 1 MILLISECOND
	DEC	AL			; DECREMENT THE COUNT
	JNZ	J29			; DO AL MILLISECOND # OF TIMES
HW_DONE:
	RET
HD_WAIT 	ENDP
;----------------------------------------------------------------
; NEC_OUTPUT							:
;	THIS ROUTINE SENDS A BYTE TO THE NEC CONTROLLER AFTER	:
;	TESTING FOR CORRECT DIRECTION AND CONTROLLER READY THIS :
;	ROUTINE WILL TIME OUT IF THE BYTE IS NOT ACCEPTED WITHIN:
;	A REASONABLE AMOUNT OF TIME, SETTING THE DISKETTE STATUS:
;	ON COMPLETION.						:
;								:
; ON ENTRY:							:
;	AH = BYTE TO BE OUTPUT					:
; ON EXIT:							:
;	CY = 0	SUCCESS 					:
;	CY = 1	FAILURE -- DISKETTE STATUS UPDATED		:
;		IF A FAILURE HAS OCCURRED, THE RETURN IS MADE	:
;		ONE LEVEL HIGHER THAN THE CALLER OF NEC OUTPUT. :
;		THIS REMOVES THE REQUIREMENT OF TESTING AFTER	:
;		EVERY CALL OF NEC_OUTPUT.			:
;	AX,CX,DX DESTROYED					:
;----------------------------------------------------------------
NEC_OUTPUT	PROC	NEAR
	PUSH	BX			; SAVE REG.
	MOV	DX,03F4H		; STATUS PORT
	MOV	BL,2			; HIGH ORDER COUNTER
	XOR	CX,CX			; COUNT FOR TIME OUT

J23:	IN	AL,DX			; GET STATUS
	AND	AL,11000000B		; KEEP STATUS AND DIRECTION
	CMP	AL,10000000B		; STATUS 1 AND DIRECTION 0 ?
	JZ	J27			; STATUS AND DIRECTION OK
	LOOP	J23			; CONTINUE TILL CX EXHAUSTED

	DEC	BL			; DECREMENT COUNTER
	JNZ	J23			; REPEAT TILL DELAY FINISHED, CX = 0

;-----	FALL THRU TO ERROR RETURN

	OR	@DSKETTE_STATUS,TIME_OUT
	POP	BX			; RESTORE REG.
	POP	AX			; DISCARD THE RETURN ADDRESS
	STC				; INDICATE ERROR TO CALLER
	RET

;-----	DIRECTION AND STATUS OK: OUTPUT BYTE

J27:
	MOV	AL,AH			; GET BYTE TO OUTPUT
	INC	DX			; DATA PORT = STATUS PORT + 1
	OUT	DX,AL			; OUTPUT THE BYTE

	PUSHF				; SAVE FLAGS
	MOV	CX,3			; 30 TO 45 MICROSECOND WAIT FOR
	CALL	WAITF			; NEC FLAGS UPDATE CYCLE
	POPF				; RESTORE FLAGS FOR EXIT
	POP	BX			; RESTORE REG.
	RET				; CY = 0 FROM TEST INSTRUCTION
NEC_OUTPUT	ENDP
;----------------------------------------------------------------
; SEEK								:
;	THIS ROUTINE WILL MOVE THE HEAD ON THE NAMED DRIVE	:
;	TO THE NAMED TRACK.  IF THE DRIVE HAS NOT BEEN ACCESSED :
;	SINCE THE DRIVE RESET COMMAND WAS ISSUED, THE DRIVE	:
;	WILL BE RECALIBRATED.					:
;								:
; ON ENTRY:	DI : DRIVE #					:
;		CH : TRACK #					:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.:
;		AX,BX,CX DX DESTROYED				:
;----------------------------------------------------------------
SEEK	PROC	NEAR
	MOV	BX,DI			; BX = DRIVE #
	MOV	AL,1			; ESTABLISH MASK FOR RECALIBRATE TEST
	XCHG	CL,BL			; SET DRIVE VALULE INTO CL
	ROL	AL,CL			; SHIFT MASK BY THE DRIVE VALUE
	XCHG	CL,BL			; RECOVER TRACK VALUE
	TEST	AL,@SEEK_STATUS 	; TEST FOR RECALIBRATE REQUIRED
	JNZ	J28A			; JUMP IF RECALIBRATE NOT REQUIRED

	OR	@SEEK_STATUS,AL 	; TURN ON THE NO RECALIBRATE BIT IN FLAG
	CALL	RECAL			; RECALIBRATE DRIVE
	JNC	AFT_RECAL		; RECALIBRATE DONE

;-----	ISSUE RECALIBRATE FOR 80 TRACK DISKETTES

	MOV	@DSKETTE_STATUS,0	; CLEAR OUT INVALID STATUS
	CALL	RECAL			; RECALIBRATE DRIVE
	JC	RB			; IF RECALIBRATE FAILS TWICE THEN ERROR
;
AFT_RECAL:
	MOV	@DSK_TRK[DI],0		; SAVE NEW CYLINDER AS PRESENT POSITION
	OR	CH,CH			; CHECK FOR SEEK TO TRACK 0
	JZ	DO_WAIT 		; HEAD SETTLE, CY = 0 IF JUMP

;-----	DRIVE IS IN SYNCHRONIZATION WITH CONTROLLER, SEEK TO TRACK

J28A:	TEST	@DSK_STATE[DI],DBL_STEP ; CHECK FOR DOUBLE STEP REQUIRED
	JZ	R7			; SINGLE STEP REQUIRED BYPASS DOUBLE
	SHL	CH,1			; DOUBLE NUMBER OF STEP TO TAKE

R7:	CMP	CH,@DSK_TRK[DI] 	; SEE IF ALREADY AT THE DESIRED TRACK
	JE	RB			; IF YES, DO NOT NEED TO SEEK

	MOV	DX,OFFSET NEC_ERR	; LOAD RETURN ADDRESS
	PUSH	DX			; ON STACK FOR NEC_OUTPUT ERROR
	MOV	@DSK_TRK[DI],CH 	; SAVE NEW CYLINDER AS PRESENT POSITION
	MOV	AH,0FH			; SEEK COMMAND TO NEC
	CALL	NEC_OUTPUT
	MOV	BX,DI			; BX = DRIVE #
	MOV	AH,BL			; OUTPUT DRIVE NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,@DSK_TRK[DI] 	; GET CYLINDER NUMBER
	CALL	NEC_OUTPUT
	CALL	CHK_STAT_2		; ENDING INTERRUPT AND SENSE STATUS

;-----	WAIT FOR HEAD SETTLE

DO_WAIT:
	PUSHF				; SAVE STATUS
	CALL	HD_WAIT 		; WAIT FOR HEAD SETTLE TIME
	POPF				; RESTORE STATUS
RB:
NEC_ERR:
	RET				; RETURN TO CALLER
SEEK	ENDP
;----------------------------------------------------------------
; RECAL 							:
;	RECALIBRATE DRIVE					:
;								:
; ON ENTRY	DI = DRIVE #					:
;								:
; ON EXIT:	CY  REFLECTS STATUS OF OPERATION.		:
;----------------------------------------------------------------
RECAL	PROC	NEAR
	PUSH	CX
	MOV	AX,OFFSET RC_BACK	; LOAD NEC_OUTPUT ERROR
	PUSH	AX
	MOV	AH,07H			; RECALIBRATE COMMAND
	CALL	NEC_OUTPUT
	MOV	BX,DI			; DX = DRIVE #
	MOV	AH,BL
	CALL	NEC_OUTPUT		; OUTPUT THE DRIVE NUMBER
	CALL	CHK_STAT_2		; GET THE INTERRUPT AND SENSE INT STATUS
	POP	AX			; THROW AWAY ERROR
RC_BACK:
	POP	CX
	RET
RECAL	ENDP
;----------------------------------------------------------------
; CHK_STAT_2							:
;	THIS ROUTINE HANDLES THE INTERRUPT RECEIVED AFTER	:
;	RECALIBRATE OR SEEK TO THE ADAPTER. THE 		:
;	INTERRUPT IS WAITED FOR, THE INTERRUPT STATUS SENSED,	:
;	AND THE RESULT RETURNED TO THE CALLER.			:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.:
;----------------------------------------------------------------
CHK_STAT_2	PROC	NEAR
	MOV	AX,OFFSET CS_BACK	; LOAD NEC_OUTPUT ERROR ADDRESS
	PUSH	AX
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	J34			; IF ERROR, RETURN IT
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	CALL	RESULTS 		; READ IN THE RESULTS
	JC	J34
	MOV	AL,@NEC_STATUS		; GET THE FIRST STATUS BYTE
	AND	AL,01100000B		; ISOLATE THE BITS
	CMP	AL,01100000B		; TEST FOR CORRECT VALUE
	JZ	J35			; IF ERROR, GO MARK IT
	CLC				; GOOD RETURN
J34:
	POP	AX			; THROW AWAY ERROR RETURN
CS_BACK:
	RET

J35:
	OR	@DSKETTE_STATUS,BAD_SEEK
	STC				; ERROR RETURN CODE
	JMP	SHORT J34
CHK_STAT_2	ENDP
;----------------------------------------------------------------
; WAIT_INT							:
;	THIS ROUTINE WAITS FOR AN INTERRUPT TO OCCUR A TIME OUT :
;	ROUTINE TAKES PLACE DURING THE WAIT, SO THAT AN ERROR	:
;	MAY BE RETURNED IF THE DRIVE IS NOT READY.		:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.:
;----------------------------------------------------------------
WAIT_INT	PROC	NEAR
	STI				; TURN ON INTERRUPTS, JUST IN CASE
	CLC				; CLEAR TIMEOUT INDICATOR
	MOV	AX,09001H		; LOAD WAIT CODE AND TYPE
	INT	15H			; PERFORM OTHER PUNCTION
	JC	J36A			; BYPASS TIMING LOOP IF TIMEOUT DONE
	MOV	BL,10			; CLEAR THE COUNTERS
	XOR	CX,CX			; FOR 2 SECOND WAIT
J36:
	TEST	@SEEK_STATUS,INT_FLAG	; TEST FOR INTERRUPT OCCURRING
	JNZ	J37
	LOOP	J36			; COUNT DOWN WHILE WAITING
	DEC	BL			; SECOND LEVEL COUNTER
	JNZ	J36

J36A:	OR	@DSKETTE_STATUS,TIME_OUT	; NOTHING HAPPENED
	STC				; ERROR RETURN
J37:
	PUSHF				; SAVE CURRENT CARRY
	AND	@SEEK_STATUS,NOT INT_FLAG	; TURN OFF INTERRUPT FLAG
	POPF				; RECOVER CARRY
	RET				; GOOD RETURN CODE
WAIT_INT	ENDP
;----------------------------------------------------------------
; RESULTS							:
;	THIS ROUTINE WILL READ ANYTHING THAT THE NEC CONTROLLER :
;	RETURNS FOLLOWING AN INTERRUPT. 			:
;								:
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.:
;		AX,BX,CX,DX DESTROYED				:
;----------------------------------------------------------------
RESULTS PROC	NEAR
	PUSH	DI
	MOV	DI,OFFSET @NEC_STATUS	; POINTER TO DATA AREA
	MOV	BL,7			; MAX STATUS BYTES
	MOV	DX,03F4H		; STATUS PORT

;-----	WAIT FOR REQUEST FOR MASTER

R10:	MOV	BH,2			; HIGH ORDER COUNTER
	XOR	CX,CX			; COUNTER
J39:					; WAIT FOR MASTER
	IN	AL,DX			; GET STATUS
	AND	AL,11000000B		; KEEP ONLY STATUS AND DIRECTION
	CMP	AL,11000000B		; STATUS 1 AND DIRECTION 0 ?
	JZ	J42			; STATUS AND DIRECTION OK
	LOOP	J39			; LOOP TILL TIMEOUT

	DEC	BH			; DECREMENT HIGH ORDER COUNTER
	JNZ	J39			; REPEAT TILL DELAY DONE

	OR	@DSKETTE_STATUS,TIME_OUT
	STC				; SET ERROR RETURN
	JMP	SHORT POPRES		; POP REGISTERS AND RETURN

;-----	READ IN THE STATUS

J42:
;	JMP	$+2			; I/O DELAY
	INC	DX			; POINT AT DATA PORT
	IN	AL,DX			; GET THE DATA
	MOV	[DI],AL 		; STORE THE BYTE
	INC	DI			; INCREMENT THE POINTER

	MOV	CX,3			; MINIMUM 24 MICROSECONDS FOR NEC
	CALL	WAITF			; WAIT 30 TO 45 MICROSECONDS
	DEC	DX			; POINT AT STATUS PORT
	IN	AL,DX			; GET STATUS
	TEST	AL,00010000B		; TEST FOR NEC STILL BUSY
	JZ	POPRES			; RESULTS DONE ?

	DEC	BL			; DECREMENT THE STATUS COUNTER
	JNZ	R10			; GO BACK FOR MORE
	OR	@DSKETTE_STATUS,BAD_NEC ; TOO MANY STATUS BYTES
	STC				; SET ERROR FLAG

;-----	RESULT OPERATION IS DONE

POPRES:
	POP	DI
	RET				; RETURN WITH CARRY SET
RESULTS 	ENDP
;----------------------------------------------------------------
; READ_DSKCHNG							:
;	READS THE STATE OF THE DISK CHANGE LINE.		:
;								:
; ON ENTRY:	DI = DRIVE #					:
;								:
; ON EXIT:	DI = DRIVE #					:
;		ZF = 0 : DISK CHANGE LINE INACTIVE		:
;		ZF = 1 : DISK CHANGE LINE ACTIVE		:
;		AX,CX,DX DESTROYED				:
;----------------------------------------------------------------
READ_DSKCHNG	PROC	NEAR
	CALL	MOTOR_ON		; TURN ON THE MOTOR IF OFF
	MOV	DX,03F7H		; ADDRESS DIGITAL INPUT REGISTER
	IN	AL,DX			; INPUT DIGITAL INPUT REGISTER
	TEST	AL,DSK_CHG		; CHECK FOR DISK CHANGE LINE ACTIVE
	RET				; RETURN TO CALLER WITH ZERO FLAG SET
READ_DSKCHNG	ENDP
;----------------------------------------------------------------
; DRIVE_DET							:
;	DETERMINES WHETHER DRIVE IS 80 OR 40 TRACKS AND 	:
;	UPDATES STATE INFORMATION ACCORDINGLY.			:
;								:
; ON ENTRY:	DI = DRIVE #					:
;----------------------------------------------------------------
DRIVE_DET	PROC	NEAR
	CALL	MOTOR_ON		; TURN ON MOTOR IF NOT ALREADY ON
	CALL	RECAL			; RECALIBRATE DRIVE
	JC	DD_BAC			; ASSUME NO DRIVE PRESENT
	MOV	CH,TRK_SLAP		; SEEK TO TRACK 48
	CALL	SEEK			; "
	JC	DD_BAC			; ERROR NO DRIVE
	MOV	CH,QUIET_SEEK+1 	; SEEK TO TRACK 10
SK_GIN:
	DEC	CH			; DECREMENT TO NEXT TRACK
	PUSH	CX			; SAVE TRACK
	CALL	SEEK			; "
	JC	POP_BAC 		; POP AND RETURN
	MOV	AX,OFFSET POP_BAC	; LOAD NEC OUTPUT ERROR ADDRESS
	PUSH	AX			; "
	MOV	AH,SENSE_DRV_ST 	; SENSE DRIVE STATUS COMMAND BYTE
	CALL	NEC_OUTPUT		; OUTPUT TO NEC
	MOV	AX,DI			; AL = DRIVE
	MOV	AH,AL			; AH = DRIVE
	CALL	NEC_OUTPUT		; OUTPUT TO NEC
	CALL	RESULTS 		; GO GET STATUS
	POP	AX			; THROW AWAY ERROR ADDRESS
	POP	CX			; RESTORE TRACK
	TEST	@NEC_STATUS,HOME	; TRACK 0 ?
	JZ	SK_GIN			; GO TILL TRACK 0
	OR	CH,CH			; IS HOME AT TRACK 0
	JZ	IS_80			; MUST BE 80 TRACK DRIVE

;	DRIVE IS A 360; SET DRIVE TO DETERMINED;
;	SET MEDIA TO DETERMINED AT RATE 250.

	OR	@DSK_STATE[DI],DRV_DET+MED_DET+RATE_250
	RET				; ALL INFORMATION SET

IS_80:
	OR	@DSK_STATE[DI],TRK_CAPA ; SETUP 80 TRACK CAPABILITY
DD_BAC:
	RET

POP_BAC:
	POP	CX			; THROW AWAY
	RET

DRIVE_DET	ENDP
;----------------------------------------------------------------
; DISK_INT							:
;	THIS ROUTINE HANDLES THE DISKETTE INTERRUPT.		:
;								:
; ON EXIT:	THE INTERRUPT FLAG IS SET IN @SEEK_STATUS.	:
;								:
;----------------------------------------------------------------
DISK_INT_1	PROC	FAR		; ENTRY POINT FOR ORG 0EF57H
	PUSH	AX			; SAVE WORK REGISTER
	PUSH	DS			; SAVE REGISTERS
	CALL	DDS			; SETUP DATA ADDRESSING
	OR	@SEEK_STATUS,INT_FLAG	; TURN ON INTERRUPT OCCURRED
	POP	DS			; RESTORE USER (DS)
	MOV	AL,EOI			; END OF INTERRUPT MARKER
	OUT	INTA00,AL		; INTERRUPT CONTROL PORT
	STI				; RE-ENABLE INTERRUPTS
	MOV	AX,09101H		; INTERRUPT POST CODE AND TYPE
	INT	15H			; GO PERFORM OTHER TASK
	POP	AX			; RECOVER REGISTER
	IRET				; RETURN FROM INTERRUPT
DISK_INT_1	ENDP
PAGE
;----------------------------------------------------------------
; DSKETTE_SETUP 						:
;	THIS ROUTINE DOES A PRELIMINARY CHECK TO SEE WHAT TYPE	:
;	OF DISKETTE DRIVES ARE ATTACH TO THE SYSTEM.		:
;----------------------------------------------------------------
DSKETTE_SETUP PROC	NEAR
	PUSH	AX			; SAVE REGISTERS
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	PUSH	DS
	CALL	DDS			; POINT DATA SEGMENT TO BIOS DATA AREA
	OR	@RTC_WAIT_FLAG,01	; NO RTC WAIT, FORCE USE OF LOOP
	XOR	DI,DI			; INITIALIZE DRIVE POINTER
	MOV	WORD PTR @DSK_STATE,0	; INITIALIZE STATES
	AND	@LASTRATE,NOT STRT_MSK+SEND_MSK ; CLEAR START & SEND
	OR	@LASTRATE,SEND_MSK	; INITIALIZE SENT TO IMPOSSIBLE
	MOV	@SEEK_STATUS,0		; INDICATE RECALIBRATE NEEDED
	MOV	@MOTOR_COUNT,0		; INITIALIZE MOTOR COUNT
	MOV	@MOTOR_STATUS,0 	; INITIALIZE DRIVES TO OFF STATE
	MOV	@DSKETTE_STATUS,0	; NO ERRORS

SUP0:
	CALL	DRIVE_DET		; DETERMINE DRIVE
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	INC	DI			; POINT TO NEXT DRIVE
	CMP	DI,MAX_DRV		; SEE IF DONE
	JNZ	SUP0			; REPEAT FOR EACH DRIVE
	MOV	@SEEK_STATUS,0		; FORCE RECALIBRATE
	AND	@RTC_WAIT_FLAG,0FEH	; ALLOW FOR RTC WAIT
	CALL	SETUP_END		; VARIOUS CLEANUPS
	POP	DS			; RESTORE CALLERS REGISTERS
	POP	DI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
DSKETTE_SETUP	ENDP
CODE	ENDS
	END
