;	[]===========================================================[]
;
;	NOTICE: THIS PROGRAM BELONGS TO AWARD SOFTWARE INTERNATIONAL(R)
;	        INC. IT IS CONSIDERED A TRADE SECRET AND IS NOT TO BE 	
;	        DIVULGED OR USED BY PARTIES WHO HAVE NOT RECEIVED	
;	        WRITTEN AUTHORIZATION FROM THE OWNER.
;
; 	[]===========================================================[]
;

;----------------------------------------------------------------------------
;Rev	Date	 Name	Description
;----------------------------------------------------------------------------
;R29A	03/17/98 KVN	Don't process it if DL is over 80h that will cause some
;			M/B (2A432I59) boot error from LS120.this time are none
;			any HDD
;R14D	01/12/98 KVN	Modify R14B coding mistake for same bug fixed correctly
;R14C	01/06/98 KVN	Fixed qaplus disk change test fail by R14B
;R30	12/17/97 KVN	Dont access floppy if controller be disabled by user
;			password
;R14B	11/05/97 KVN	Dont report error code 06h(media changed) if FDD 3 mode
;			be disable to fix a INT13.EXE test program
;R29	05/01/97 KVN	Add LS120 bootable even floppy A exist
;R28	02/01/97 KVN	Support IOMEGA ZIP-100 drive active
;R27A	01/24/97 KVN	Fixed R27 coding mistake
;R27	01/22/97 KVN	Support dual LS120 drive active
;R26	11/01/96 KVN	Added LS-120 support
;R25	09/05/96 LAW	Added "FORCE_XSPEED_IN_ADISK" define for fixed run 
;			OS2 "Boot other DOS from A: disk" error
;R24A	08/20/96 LAW	Fixed format disk error in QA5xx utility format disk
;R24	07/23/96 LAW	Fixed set FDD 3 mode to do format 1.44MB disk error
;R23	07/19/96 KVN	Fixed test disk write protect fail in QA529
;R04A	07/08/96 KVN	Fixed software swap A and B drive format error
;R21B	06/07/96 KVN	Fixed can't format 1024 bytes/sector use 1024fmt.exe
;			utility because this program will call reset floppy
;			but DL above 80h (0A3h) then BIOS must return no error
;R22	05/24/96 RCH	Move non run-time code to new file MISC.ASM to get
;			more space in F8000-FFFFF area
;R21A	05/14/96 KVN	Fixed Triton mother board with WDC-AC2850F HDD then HDD
;			access error (maybe hang at EMM386.EXE or can't boot)
;R21	04/22/96 KVN	Fixed excute [AIC ASPI manager v-3.63] driver in config.sys
;			when HDD is none and boot from floppy then system hang
;R20	03/20/96 KVN	Fixed format wrong when enable swap floppy function
;R19	03/14/96 KVN	Fixed read floppy disk spent a long time when this disk
;			is bad
;R18	01/23/95 RAY	To prevent some FDD verify error under system over 133Mhz
;R17	01/18/96 RCH	The IBM 486 class CPU already gone, so kill that codes
;R16	11/03/95 KVN	Added floppy 3 mode 1.2Mb format
;R14A	09/11/95 KVN	Added floppy 3 mode standard code FDD3MODE.SIO
;R15	09/06/95 KVN	Added floppy write protect function
;R14	09/04/95 KVN	Added floppy 3 mode auto switch
;R13	08/09/95 RCH	Don't call "Wait_For_Pie" for FDD motor spin up, 
;			because OS2 failed with FDD access.
;R12	06/20/95 KVN	Fixed code mistake
;R11	06/13/95 KVN	Reduce Post_func_call and F000_call code size
;R10	05/08/95 KVN	Add CD-ROM bootable feature
;R09	12/16/94 RCH	Don't hardcode for IODELAY , because some chipset
;			use port 0e1H.
;R08	10/18/94 RCH	Added alignment for all procedures
;R07	04/26/94 KVN	Fixed HT-2000 IDE VL-bus+HT-2000 driver and run ET
;			system then floppy can't access.
;R06	01/26/94 KVN	Added FDD performance.
;R04	12/05/93 KVN	Add swap A and B drive function.
;R03	09/01/93 RAY	Add PM hooks as standard features
;R02	08/27/93 KVN	When media is 2.88MB must set 3f7h port to high density.
;			Set FDC_FIFO_THRES is '0dh'.
;R01	05/28/93 DNL	Added IBM 486DLC3 CPU support
;R100	04/22/93 RAY	Flush CPU cache if it is IBM CPU
;R00	02/10/93 KVN	Recover command "VERIFY" to command "READ".
;
;			NOTE: The following items should be defined
;			 in BIOS.CFG for 2.88 support.
;
;	ENABLE_288_SUPPORT	EQU	1
;	ENABLE_FDC_FIFO		EQU	1	; (2.88 needs FIFO to avoid slow DMA errors)
;	FDC_FIFO_THRES		EQU	0dh	; (FIFO threshold set to mid value)
;
;	CMOS_FD_288		EQU	5	; (CMOS value for 2.88, can be changed)
;	(FORCE_1MB_CMOS		EQU	1)	; (to force first drive to 2.88 for debug)
;
;	(FORCE_1MB_PARMS	EQU	1)	; (to force 2.88 parms for 1 mbs for debug)
;


		PAGE	63,132
		TITLE	ADISK  -- FLOPPY DISK AT ROM BIOS
.386
ENABLE_288_SUPPORT	EQU	1
ENABLE_FDC_FIFO		EQU	1	; (2.88 needs FIFO to avoid slow DMA errors)
FDC_FIFO_THRES		EQU	0dh	; (FIFO threshold set to mid value)

CMOS_FD_288		EQU	5	; (CMOS value for 2.88, can be changed)

;		+---------------+
;		|		|
;		|  FLOPPY DISK	|
;		|		|
;		+---------------+


;PORT DEFINITIONS
;
;     o Diskette interface consists of NEC 765 and a digital output register,
;       digital input register and diskette control register.
;       I/O Ports 3F4-3F5 for NEC 765
;       I/O Port 3F2 for DOR
;       I/O Port 3F7 for DCR and DIR
;       o Digital output register signals
;          Bit 5 Drive 1 motor enable (true)
;          Bit 4 Drive 0 motor enable (true)
;          Bit 3 NEC 765 DMA and interrupt enable (true)
;          Bit 2 T/C to NEC 765
;          Bit 0 Drive 0 select (false)
;        o Digital input register signals
;          Bit 7 Selected drive has diskette change (true)
;          Bit 6 Hard Disk Write Gate enable (true)
;          Bit 5 Hard Disk Head 3 Select/Reduced Write Current (true)
;          Bit 4 Hard Disk Head 2 Select (true)
;          Bit 3 Hard Disk Head 1 Select (true)
;          Bit 2 Hard Disk Head 0 Select (true)
;          Bit 1 Hard Disk Drive 1 Select (true)
;          Bit 0 Hard Disk Drive 0 Select(true)
;        o Diskette control register
;          Bit 0-1 Data transfer rate for drive (0 is 500K, 1 is 300K,
;               2 is 250K).

;DATA LOCATIONS AT 0:

;Byte 0:43EH is the diskette seek status. Low 4 bits indicate drive is
;to be recalibrated on next seek operation when 0 (bit 0 is drive 0,
;bit 1 is drive 1 etc.). When bit 7 set, interrupt is complete. When
;bit 7 not set, interrupt pending.
;
;Byte 0:43FH is motor status. If bit 0-1 on then drive motor 0-1 is running.
;Bits 4-5 on indicate drive select for drives 0-1.
;
;Byte 0:440H is time out value to turn drive off. Counted down by timer
;tick routine (See 1AH interrupt).
;
;Byte 0:441H is last diskette status code.
;
;Byte 0:442-448H is the NEC 765 result codes from last operation. See NEC 765
;specs for details.
;
;Byte 0:48BH is the data transfer byte.  Bits 6-7 contains the last
;data transfer rate sent to the controller.  Bits 2-3 contain
;the first data transfer rate we attempt an operation in.  This is
;used to keep track of how many times we retry an operation when the
;state is not determined.
;
;Byte 0:48FH is the physically determined drive capability byte.  It
;is divided into two nibbles, with the lower nibble representing drive 0
;and the upper nibble representing drive 1.  The lowest bit of the nibble
;says whether this drive has 40 or 80 track capability (0=40 track,
;1=80 track).  The next bit tells us whether the drive is capable of
;performing operations at more than one data rate.  If set, then this
;means we have either a 1.2 Meg drive or a 1.44 Meg drive.  The next
;bit tells us whether the previous bit is valid or not.
;
;
;Byte 0:490H is diskette drive 0 media state. Bits 6-7 indicate data transfer
;rate (0 is 500K, 1 is 300K, 2 is 250K, 3 is reserved). Bit 5 set means
;double stepping on tracks. Bit 4 set means media/drive is known. Bit 3 is
;reserved. Bits 2-0 indicate present drive state (0 is 360K media in 360K
;drive unestablished, 1 is 360K media in 1.2M drive unestablished, 2 is
;1.2M media in 1.2M drive unestablished, 3 is 360K media in 360K drive
;established, 4 is 360K media in 1.2M drive established, 5 is 1.2M media
;in 1.2M drive established, 7 indicates none of the above or cannot
;be determined.)  Bits 2-0 are really only here to maintain compatiblity
;with the old AT.  The values in them should not be relied on by the bios.
;
;Byte 0:491H is diskette drive 1 media state. See above for details.
;
;Byte 0:492H is original media state tried in transfer for drive 0.
;
;Byte 0:493H is original media state tried in transfer for drive 1.
;
;Word 0:494H is current cylinder number for drive 0.
;
;Word 0:495H is current cylinder number for drive 1.


;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Reset
;Entry:  	INT 13H
;Input:  	ah=0
;                dl=0-1 for all diskettes
;                dl=80-81H for all hard disks
;Output: 	none
;Description:
;
;Disk_IO_Reset will:
;1. 	save environment
;2. 	Indicate drives need recalibration. Reset controller. Update
;        memory values. For hard disk reset drive parameters and recalibrate
;        drive.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Status
;Entry:  	INT 13H
;Input:  	ah=1
;                dl=0-1 for diskette
;                dl=80-81H for hard disk
;Output: 	al=status
;                    Values are:
;                    CCH write fault on drive
;                    BBH unknown error occured
;                    AAH drive not ready
;                    80H time out
;                    40H bad seek
;                    20H bad controller
;                    11H data corrected on transfer
;                    10H bad ECC or CRC
;                     BH bad track found
;                     AH bad sector flag found
;                     9H dma boundary error (transfer across 64K bank)
;                     8H bad dma transfer occured
;                     7H drive failed
;                     6H media removed
;                     5H reset failed
;                     4H sector not found
;                     3H write attempted on write protected media
;                     2H data address mark not found
;                     1H bad command
;                     0H no error found
;Description:
;
;Disk_IO_Status will:
;1. 	save environment
;2. 	Return indicated status byte.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

;;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Read
;Entry:  	INT 13H
;Input:  	ah=2
;                al=number of sectors
;                dl=drive number (0-1 for diskette, 80-81H for hard disk)
;                dh=head number
;                ch=cylinder number - low 8 bits
;                cl=sector number - bits 0-5
;                   bits 6-7 are high 2 cylinder bits
;                es:bx=transfer address
;
;Output: 	ah=status (see above)
;                cy=1 if error, cy=0 if ok.
;Description:
;
;Disk_IO_Read will:
;1. 	save environment
;2. 	Check DMA boundary conditions, setup DMA chip for transfer. Read in
;        requested number of sectors starting at ES:BX. Update data values at
;        0:4??H as needed.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Write
;Entry:  	INT 13H
;Input:  	ah=3
;                al=number of sectors
;                dl=drive number (0-1 for diskette, 80-81H for hard disk)
;                dh=head number
;                ch=cylinder number - low 8 bits
;                cl=sector number - bits 0-5
;                   bits 6-7 are high 2 cylinder bits
;                es:bx=transfer address
;
;Output: 	ah=status (see above)
;                cy=1 if error, cy=0 if ok.
;Description:
;
;Disk_IO_Write will:
;1. 	save environment
;2.  	Check DMA boundary conditions,setup DMA chip for transfer. Write out
;        requested number of sectors starting at ES:BX. Update data values at
;        0:4??H as needed.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Verify
;Entry:  	INT 13H
;Input:  	ah=4
;                al=number of sectors
;                dl=drive number (0-1 for diskette, 80-81H for hard disk)
;                dh=head number
;                ch=cylinder number - low 8 bits
;                cl=sector number - bits 0-5
;                   bits 6-7 are high 2 cylinder bits
;
;Output: 	ah=status (see above)
;                cy=1 if error, cy=0 if ok.
;Description:
;
;Disk_IO_Verify will:
;1. 	save environment
;2. 	see hard disk spec.
;3. 	Verify the requested number of sectors. Update data values 0:4??H as
;        needed.
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Format
;Entry:  	INT 13H
;Input:  	ah=5
;                al=number of sectors
;                dl=drive number (0-1 for diskette, 80-81H for hard disk)
;                dh=head number
;                ch=cylinder number - low 8 bits
;                cl=sector number - bits 0-5
;                   bits 6-7 are high 2 cylinder bits
;                es:bx=transfer address
;                    if hard drive then es:bx points to 512 byte buffer with
;                     n pairs of {f,s}.
;                     where n is number of sectors
;                           f is flag 0 is good sector, 80H is bad sector
;                           s is sector number
;                    if diskette drive then es:bx points to 512 byte buffer
;                     n pairs of (c,h,s,l)
;                     where n is number of sectors
;                           c is cylinder number
;                           h is head number
;                           s is sector number
;                           l is sector length (l = 0 is 128, l = 1 is 256
;                                l = 2 is 512, l = 3 is 1024).
;Output: 	ah=status (see above)
;                cy=1 if error, cy=0 if ok.
;Description:
;
;Disk_IO_Format will
;1. 	save environment
;2.  	Check DMA boundary conditions,setup DMA chip for transfer. Format
;        track given sector pattern starting at ES:BX. Update data values at
;        0:4??H as needed.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_BADC
;Entry:  	INT 13H
;Input:  	ah=6,7,E,F,12,13H
;Output: 	al=1, cy=1
;Description:
;
;Disk_IO_BADC will:
;1. 	save environment
;2. 	see hard disk spec.
;3. 	return bad command status and update bytes at 0:4??H as needed.
;4. 	return to POI
;[]------------------------------------------------------------------------

;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Change_Line
;Entry:  	INT 13H
;Input:  	ah=16H
;                dl=drive (0-1)
;Output: 	ah=0,cy=0 disk change line not active
;                  =6,cy=1 disk change line active
;                ah=1,cy=1 drive out of bounds
;Description:
;Determine if media has been removed since last operation on specified
;drive.
;
;Disk_IO_Change_Line will
;1. 	save environment
;2. 	If drive in bounds then set ah as indicated above else set error.
;        Set data values at 0:4??H as needed.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------


		PAGE

; minimize segment 0 access in protected mode (exception 5)

SEG_0		SEGMENT	USE16 AT 1
		INCLUDE	SEG_0.INC
SEG_0		ENDS

G_RAM		SEGMENT	USE16 AT 40H
		INCLUDE	G_RAM.INC
G_RAM		ENDS

ifdef	PM_SUPPORT				;128K
		extrn	PM_After_FDD:near	;128K
		extrn	PM_Before_FDD:near	;128K
endif;	PM_SUPPORT				;128K
.XLIST
		INCLUDE BIOS.CFG

		INCLUDE CMOS.EQU
		INCLUDE 82077.EQU
		INCLUDE	ADISK.EQU
		INCLUDE COMMON.EQU				


		INCLUDE SETSPEED.EXT
		INCLUDE	ATORGS.EXT

		INCLUDE COMMON.MAC

IFDEF	POWER_MANAGEMENT
		INCLUDE PM_GEN.EXT
ENDIF	;POWER_MANAGEMENT
.LIST

DGROUP		GROUP	FCODE
;R08 FCODE		SEGMENT	USE16 DWORD PUBLIC 'CODE'      
FCODE		SEGMENT	USE16 PARA PUBLIC 'CODE'      	;R08
		ASSUME	CS:DGROUP

		INCLUDE ADISK.INC

		PAGE
;[]----------------------------------------------------------------------
;
;DISKIO:  Main entry point for disk io.
;	The values in the registers will be put onto the stack and
;	made addressable via the BP register.  This structure
;	will be called the BP_REGISTERS.
;
;	BP_REGISTERS are put on the stack in the following order:
;
;	FLAGS				[bp+22]
;	CS RETURN VALUE			[bp+20]
;	IP RETURN VALUE  		[bp+18]
;	SI				[bp+16]
;	DS				[bp+14]
;	ES 			        [bp+12]
;	DI				[bp+10]
;	DX				[bp+8]
;	CX				[bp+6]
;	BX				[bp+4]
;	AX				[bp+2]
;	BP				[bp+0]
;
;  SEGMENT REGISTERS:
;	CS and DS point to the ROM, 0F000h
;	ES will be set to 40h, to access the data area.
;	DS INTERNAL POINT TO 0 FOR vectors 303c
;	SS points to the stack.
;
;
;  Maintenance guidelines:
;  REGISTER AND STACK USAGE:
;  There are 2 things to be concerned with:
;	1.  Register protection
;	2.  Access to BP_REGISTERS
;
;  There are 4 levels of procedures:
;          Level 1:  Entry procedure, DISKIO
;          Level 2:  Function procedures: DISKIO_XFER, etc.
;	   Level 3:  High level Service procedures:  NEC_SEEK, for example
;	   Level 4:  Low level service procedures:  GET_CURRENT_MEDIA, etc.
;
;  Different rules apply to each level:
;		Level 1 is the entry point to the system. It knows
;		about the BP_REGISTERS structure, and may access
;		and change it.  DISKIO is the only procedure
;		that has permission to change the BP_AH register and
;		BP_FLAGS on BP_REGISTERS.
;
;		Level 2 procedures are the major functions.  They
;		are only called once per INT 40h.  They also know
;		about BP_REGISTERS and may access any of them. They
;		do not have permission, however, to change BP_AH
;		and BP_FLAGS.  This should only be done by DISKIO.
;		These routines do not protect any registers except
;		for the SEGMENT and BP registers.
;
;		Level 3 procedures are large general purpose procedures.
;		These routines do not know about BP_REGISTERS because
;		May be called from places outside INT 40h, so
;		the BP_REGISTERS structure may not be there, and they
;		also may be called multiple times, sometimes after
;		values in BP_REGISTERS have been changed by Level 1 and
;		Level 2 procedures.
;
;		Because Level 3 procedures are large, and may
;		use many registers, they do not attempt to protect
;		any registers except the SEGMENT and BP registers.
;		The calling procedure has the responsibility of protecting
;		any values in registers it needs to preserve.  Calling
;		procedures should protect registers if the level
;		3 procedure they are calling does not currently destroy
;		that register.  This way level 3 procedures can be
;		rewritten without affecting the calling procedures.
;
;		Level 4 procedures are small, frequently called procedures
;		desgined to save code space.  Arguments should be passed
;		through registers for the same reasons as in level 3.
;		All registers that are not part of the interface however,
;		will be protected by these procedures.  Therefore it is not
;		necessary to push registers in the calling procedures,
;		unless they are also used by the called procedure to return
;		values.
;
;[]-------------------------------------------------------------------------

		PAGE
		PUBLIC	DISKIO			; * ATORGS.ASM
		ALIGN	4		       
DISKIO		PROC	NEAR


;R03 IFDEF	POWER_MANAGEMENT			
;R03 		call	PM_Set_Critical		; inclrment critical setction
;R03 ENDIF	;POWER_MANAGEMENT			; semaphore

;R03 IFDEF	PM_DISK_TRIGGER
;R03 		EXTRN	SET_DISK_TRIGGER:NEAR
;R03 		CALL	SET_DISK_TRIGGER
;R03 ENDIF	;PM_DISK_TRIGGER

;R03 - Start
ifdef	PM_SUPPORT
;128K		extrn	PM_Before_FDD:near	;routine in PMU.ASM
		call	PM_Before_FDD
endif;	PM_SUPPORT
;R03 - End

		STI
;R10 start
;R26IFDEF CD_ROM
;;;ifdef ATAPI_COMMAND_SUPPORT		;R26
if	ATAPI_COMMAND_SUPPORT	eq	1	;;;
;------------------- check for CD emulation of A --------------
	push	ds
	push	ax
	mov	ax,G_RAM
	mov	ds,ax	      	;DS = BIOS data area(40:0)
	assume	ds:G_RAM
	and	byte ptr G_RAM:[CDROM_ALLOCATE],not Change_Floppy_Number	;R29
;R26 start
	mov	al,byte ptr G_RAM:[ATAPI_Byte]
	test	al,4	      	;ZIP or LS120 drive installed ?
	jz	short @F      	;no , check CD-ROM
	mov	ah,al	      	;AH = ATAPI_Byte	;R27
	shr	al,3	      	;check ATAPI emulation drive No.
	and	al,1	      	;only drive No. A: or B:
	cmp	al,dl	      	;drive No. match ?
	je	short Go_Std_Atapi;yes, access ATAPI drive
				;no, it may be FDD or CD-ROM access
;R27 start
	test	ah,8		;ATAPI emulate drive B ?;R27A
	jnz	short @F	;yes,check CD-ROM   	;R27A
;R28	shr	ah,6
;R28	and	ah,1
;R28	cmp	ah,dl
;R28	je	short Go_Std_Atapi
	test	ah,30h	  	;R28 check ATAPI drive 1 exist?
	jnz	short Go_Std_Atapi;R28 Yes,go ATAPI subroutine
;R27 end
	or	dl,dl	       	;R29 access drive A?
	jnz	short Go_Std_Floppy;R29 No,skip
@@:
;R26 end
	;Check if CD-ROM existed and emulated as drive A
	mov	al,byte ptr G_RAM:[CDROM_ALLOCATE]
	and	al,0fh
	shr	al,2   		;bit3-2 = CD-ROM emulation No.
	cmp	al,CD_DRVA 	;emulating drive A?
;R26	pop	ax
;R26	pop	ds
	jne	short no_cd   	;not emulate drive A
				;yes, emulate drive A
	cmp	dl,0		;access drive A?
	jne	short @F	;not for CD-ROM access
; calling drive 0 (the CD)
	extrn	std_atapi:near
Go_Std_Atapi:					;R26
	pop	ax				;R26
	pop	ds				;R26
	call	std_atapi	;ATAPI service routine
	retf	2				; discard flags

; calling drive 1 (the real floppy)
@@:
Go_Std_Floppy:					;R29
	;mark flag for FDD access due to ATAPI device existed
	or	byte ptr G_RAM:[CDROM_ALLOCATE],Change_Floppy_Number	;R29
;R29	dec	dl
no_cd:
;R26ENDIF ; CD_ROM
	pop	ax				;R26
	pop	ds				;R26
endif ;ATAPI_COMMAND_SUPPORT			;R26
;R10 end
		PUSH	SI
		PUSH	DS
		PUSH	ES
		PUSH	DI
		CLD

;R04 start
	;Logically swap floppy drive A & B if user enable this feature
	;in CMOS setup
		push	ax			;save function No.
		mov	ax,G_RAM
		mov	ds,ax			;DS = BIOS data area (40:0)
		ASSUME	ds:G_RAM
;R29 start
		cmp	dl,80h			;for hard drive access ?;R29A
		jae	short Not_Floppy	;yes, don't swap FDD	;R29A

	;Change logical FDD drive number if the system was boot from ATAPI
	;device , like LS120 , ZIP/100 or CD-ROM emluated as drive A:

		test	byte ptr G_RAM:[CDROM_ALLOCATE],Change_Floppy_Number
		jz	short @F		;boot from ATAPI ?
						;yes, input DL = 1 for FDD 0
						;              = 2 for FDD 1
		dec	dl			;adjust drive No. for FDD
						;INT 13H access.
@@:
;R29 end
	;Check if FDD swap is enabled or not
		test	byte ptr FDD_VERIFY_CMD_FLAG,SWAP_DRIVE
		jz	short @F	    	;no swap
		xor	dl,1			;swap drive No. for FDD
@@:
Not_Floppy:				;R29A
		pop	ax			;restore function No.
;R04 end
		PUSH	DX
		PUSH	CX
		PUSH	BX
		PUSH	AX

		MOV	AX,CS
		MOV	DS,AX
		ASSUME	DS:DGROUP		; dgroup must be here

		MOV	AX,G_RAM		; variables.
		MOV	ES,AX			; ES for addressing data area
		ASSUME	ES:G_RAM		; STAYS IN EFFECT THROUGH CODE
						; SEG_0
ifndef	FORCE_XSPEED_IN_ADISK				;R25
		test	CS:[SYSTEM_BYTE],02H

		JNZ	SHORT OPTION_NFS	; 1 IS NO SWITCH
endif	;FORCE_XSPEED_IN_ADISK				;R25


		XOR	AL,AL			; default to low speed
;R12		POST_FUNC_CALL	XSPEED			; do it
		CALL	XSPEED			;R12

OPTION_NFS:
;R17 ;R100 Starts
;R17 		push	di
;R17 		mov	di,offset @F
;R17 		jmp	Invalidate_IBM_Cache
;R17 	@@:
;R17 		pop	di
;R17 ;R100 Ends

		PUSH	BP
		MOV	BP,SP
		OR	BP_FLAGS,200H		; SET INT FLAG ON BECAUSE OF
						; DOS CODE AT 70:XXXX
;R30 start
		mov	ah,ST_OK
		test	byte ptr G_RAM:[HARDWARE],1	;have any floppy drive?
		jz	D_LEGAL_FUNCTION		;None any drive then skip
;R30 end
;R21A		cmp	BP_DRIVE,80h		;R21
;R21A		jae	D_ILLEGAL_FUNCTION	;R21 jump, its illegal
		CMP	BP_OPCODE,1
		JA	SHORT D_NOT_FUNC_0_OR_1	; function > 2, then jump
		JE	SHORT D_FUNC_1		; function == 1, then jump
						; else fall thru to zero.
;FUNCTION 0:  Reset drives.

		MOV	G_RAM:[FD_OP_RESULT],0	; clear previous errors.
		CALL	RESETDRIVES		; reset the drives
		JMP	short D_COPY_STATUS	; exit to caller.

D_FUNC_1:					; FUNCTION 1:  Get status of last operation.
		MOV	AH,G_RAM:[FD_OP_RESULT]	; previous status to ah
		JMP	short D_COPY_STATUS   	; exit to caller.

D_NOT_FUNC_0_OR_1:
		MOV	G_RAM:[FD_OP_RESULT],0	; functions >2 all
						; clear previous errors
		CMP	BP_OPCODE,5		; if function >5
		JA	SHORT D_NOT_0_TO_5	; jump

;FUNCTIONS 2-4: Read/write/verify

		MOV	BX,OFFSET DGROUP:DISK_IO_XFER	; assume 2,3,4
		JB	SHORT D_NORMAL_CASE

;FUNCTION 5, format.

		MOV	BX,OFFSET DGROUP:DISK_IO_FORMAT	; its format.
		JMP	SHORT D_NORMAL_CASE

D_NOT_0_TO_5:	CMP	BP_OPCODE,8		; function 8?
		JNE	SHORT D_NOT_8		; jump if not

;FUNCTION 8:  requires special handling because it
;still returns information if drive is
;out of range.  Also wipes out BP_DRIVE parameter on stack.

		MOV	AL,BP_DRIVE
		PUSH	AX			; save drive #
		CALL	DISK_IO_GET_PARMS
		POP	BX			; recover drive #
		PUSH	AX			; save results
		MOV	AL,BL			; al=drive #
		CALL	FAKE_UP_MEDIA_BYTE
		POP	AX			; recover results
		JMP	SHORT D_CHECK_CARRY	; doesn't copy error code
						; to FD_OP_RESULTS
D_NOT_8:
		MOV	BL,BP_OPCODE
		XOR	BH,BH
		CMP	BL,18H			; function >18h
;R55		JA	SHORT D_ILLEGAL_FUNCTION	; jump, its illegal
		JA	D_ILLEGAL_FUNCTION	; jump, its illegal	;R55
		SUB	BX,15H			; reduce function for table
		JB	D_ILLEGAL_FUNCTION		;128k
;128k		JB	SHORT D_ILLEGAL_FUNCTION	; illegal if 6,7,9-14h
		SHL	BX,1			; word index
		MOV	BX,FUNC_15_TO_18[BX]	; get proc address

D_NORMAL_CASE:	CMP	BP_DRIVE,1		; routines 2-5 and 15-18 all
		JA	D_ILLEGAL_FUNCTION	; range check drive.
		JMP	BX			; "virtual" call to save stack

		ALIGN	4
D_NORMAL_RET:					; space.  All procedures "ret"
						; by jmping to d_normal_ret

		PUSH	AX			; save RESULTS
		MOV	AL,BP_DRIVE
		CALL	FAKE_UP_MEDIA_BYTE	; 0:490,491, low bits for comp.
		POP	AX			; recover RESULTS
		CMP	BP_OPCODE,015H		; function 15h's value in ah
		JE	SHORT D_CLEAR_CARRY	; is NOT an error code.

D_COPY_STATUS:
;R21A start
		cmp	BP_OPCODE,0		;R21B
		je	D_LEGAL_FUNCTION	;R21B
		test	BP_DRIVE,80h
		jz	D_LEGAL_FUNCTION
		mov	ah,ST_BADCMD
D_LEGAL_FUNCTION:
;R21A end
		MOV	G_RAM:[FD_OP_RESULT],AH	; record results in case
						; next call is function 1.
D_CHECK_CARRY:	OR	BP_FLAGS,+1		; assume error.MASM5 REMOVED
		OR	AH,AH			; if AH is zero, indicate
		JNZ	SHORT D_FLAGS_SET	; okay by clearing carry flag
D_CLEAR_CARRY:	AND	BP_FLAGS,0FFFEH		; else clear flags.MASM5 REMOVED
D_FLAGS_SET:
DISKIO_EXIT:	MOV	BP_RET_CODE,AH		; put Ah on stack, so it will
						; be popped off.
		MOV	AL,2			; shorten motor count so
		CALL	GET_PARM		; so motor will shutoff in 2
		MOV	G_RAM:[MOTOR_OFF_WAIT],AL	; rather than 14 seconds.

ifndef	FORCE_XSPEED_IN_ADISK				;R25
		test	CS:[SYSTEM_BYTE],02H	
		JNZ	SHORT OPTION_NFS1
endif	;FORCE_XSPEED_IN_ADISK				;R25

		MOV	AX,G_RAM		; retrieve speed G_RAM
		MOV	DS,AX
		ASSUME	DS:G_RAM
		XOR	AL,AL			; assume slow speed
		TEST	INIT_ERR_FLG,80H	; speed switch
		JZ	SHORT SET_SP
		MOV	AL,1			; set hi speed
SET_SP:
;R12		POST_FUNC_CALL	XSPEED
		CALL	XSPEED			;R12
OPTION_NFS1:
;R17 ;R100 Starts
;R17 		mov	di,offset @F
;R17 		jmp	Invalidate_IBM_Cache
;R17 	@@:
;R17 ;R100 Ends
		MOV	SP,BP			; not necessary, but leave in
						; in case we want to add local
						; variables in future.
		POP	BP			; recover bp
		POP	AX
		POP	BX
		POP	CX
		POP	DX

;R04 start
		push	ax
		mov	ax,G_RAM
		mov	ds,ax			;DS = BIOS data area(40:0)
;R29;R10 start
;R29;R26IFDEF CD_ROM
;R29ifdef ATAPI_COMMAND_SUPPORT		;R26
;R29	mov	al,byte ptr G_RAM:[CDROM_ALLOCATE]
;R29	and	al,0fh
;R29	shr	al,2
;R29	cmp	al,CD_DRVA			; emulating drive A?
;R29	jne	short @F
;R29	inc	dl
;R29@@:
;R29;R26ENDIF ;CD_ROM
;R29endif ;ATAPI_COMMAND_SUPPORT		;R26
;R29;R10 end
	;restore drive No.(DL) if FDD swap is enabled
		test	byte ptr FDD_VERIFY_CMD_FLAG,SWAP_DRIVE
		jz	short @F
		xor	dl,1			;restore drive No.
@@:
;R29 start
;;;ifdef ATAPI_COMMAND_SUPPORT
if	ATAPI_COMMAND_SUPPORT	eq	1	;;;
	;restore drive No.(DL) if any ATAPI device emulate drive A:
		test	byte ptr G_RAM:[CDROM_ALLOCATE],Change_Floppy_Number
		jz	short @F
		inc	dl			;restore drive No.
@@:
endif ;ATAPI_COMMAND_SUPPORT
;R29 end
		ASSUME	ds:NOTHING
		pop	ax
;R04 end
		POP	DI
		POP	ES
		POP	DS
		POP	SI

;R03 - Start
ifdef	PM_SUPPORT
;128k		extrn	PM_After_FDD:near
		call	PM_After_FDD
endif;	PM_SUPPORT
;R03 - End

;R03 IFDEF	POWER_MANAGEMENT
;R03 		call	PM_Clr_Critical		; Decrement critical setction
;R03 ENDIF	;POWER_MANAGEMENT			; semaphore

;R03 IFDEF	PM_DISK_TRIGGER
;R03 		EXTRN	CLR_DISK_TRIGGER:NEAR
;R03 		CALL	CLR_DISK_TRIGGER
;R03 ENDIF	;PM_DISK_TRIGGER

		IRET
		ASSUME	DS:DGROUP		; BUG FROM XSPEED

D_ILLEGAL_FUNCTION:
		MOV	AH,1			; error code
;R29		JMP	short D_COPY_STATUS
		JMP	D_COPY_STATUS		;R29

DISKIO		ENDP

		PAGE

;!!!!!!!!!!!!!!  LEVEL 2 PROCEDURES:  The Function calls  !!!!!!!!!!!!!!!!

;[]---------------------------------------------------------------------------
;These procedures have permission to read any of BP_REGISTERS.  They
;can also change any BP_REGISTERS except BP_OPCODE,BP_RET_CODE and BP_FLAGS
;These procedures can destroy any registers except BP and Segment registers.
;[]---------------------------------------------------------------------------

		ALIGN	4
DISK_IO_XFER	PROC	NEAR
						; SEG_0
		MOV	AL,BP_DRIVE		; get drive
		CALL	GET_DCAPABILITY		; is there a drive?
		OR	AH,AH
		JNZ	SHORT DIX_MARK_OPERATION	; jump if drive present,
		MOV	AH,ST_TIMEOUT		; if no drive, return timeout.
		JMP	DIX_FAILURE

DIX_MARK_OPERATION:
		AND	G_RAM:[MOTOR_ON_IND],NOT 80H	; assume read
		CMP	BP_OPCODE,3		; is it actually a write?
		JNE	SHORT DIX_START_ENGINES
;R15 start
ifdef	Support_FDD_Write_Protect
		test	G_RAM:[POST_FLAG],Fdd_Wt_Protect_Status
		jz	short @F
		mov	AH,ST_WRITEPROT		; was in place.
		JMP	DIX_FAILURE
@@:
endif	;Support_FDD_Write_Protect
;R15 end
		OR	G_RAM:[MOTOR_ON_IND],80H	; mark as write
						; for TURNONMOTOR routine.
DIX_START_ENGINES:
		PUSH	AX			; save drive
		CALL	TURNONMOTOR		; turn on motor and wait
						; if not already on.
		POP	AX			; recover drive.
		CALL	GET_CURRENT_MEDIA
		TEST	AH,010H			; determined?
		JNZ	SHORT DIX_CHECK_DCL	; no retries if determined
		CALL	SET_UP_RETRIES		; prepare xfer rates for
						; retries
DIX_CHECK_DCL:	MOV	G_RAM:[MOTOR_OFF_WAIT],0FFH	; keep motor spinning if this
						; is a retry.
		CALL	GET_DCAPABILITY		; if forty track drive,
		TEST	AH,01H			; don't check DCL, in
		JZ	SHORT DIX_DCL_OK	; case it's an old controller.
		CALL	READ_AND_CLEAR_DCL	; check the dcl
						; note: destroys al
		OR	AH,AH			; exit if active
		JZ	SHORT DIX_DCL_OK
		JMP	DIX_FAILURE		; abort operation if disk
						; change line was active,
						; if we could clear it.
DIX_DCL_OK:

;----Prepare and send a SPECIFY command to controller----

		XOR	AL,AL
		CALL	GET_PARM
		MOV	BL,AL			; FIRST SPECIFY BYTE - DF
		MOV	AL,1
		CALL	GET_PARM
		MOV	BH,AL			; SECOND SPECIFY BYTE - 02

		MOV	AL,BP_DRIVE
		CALL	GET_CURRENT_MEDIA
		AND	AH,0C0H			; isolate xfer rate.

IFDEF	ENABLE_288_SUPPORT
		CMP	AH,0C0H			; check 1MB
		JE	short DIX_SPEC_1M 	; skip if so
		OR	AH,AH			; else set flags on xfer rate
ENDIF	;ENABLE_288_SUPPORT
		JNZ	SHORT DIX_SPECIFY	; if not 500 KBS, use default.

DIX_SPEC_1M:
		CALL	GET_CMOS_DRIVE		; else if cmos says drive 4
		CMP	AH,4
		JNE	SHORT DIX_SPECIFY
		MOV	BL,0AFH			; use 0AFh as 1st specify
DIX_SPECIFY:	PUSH	BX			; parameters on stack
		MOV	SI,SP			; pointer to parameters
		MOV	AH,NC_SPECIFY		; specify command
		MOV	BL,NC_LEN_SPECIFY	; 3 bytes in specify command.
		CALL	SEND_COMMAND		; output specify command
		POP	BX			; clear parameters off stack.
		OR	AH,AH			; error?
		JZ	SHORT DIX_SET_XFER_RATE	; if no error, continue
		JMP	DIX_CHECK_ERROR		; see if error should abort
						; operation, or move us to
						; next transfer rate.
DIX_SET_XFER_RATE:
		MOV	AL,BP_DRIVE		; drive to al
		CALL	RESET_XFER_RATE		; choose appropriate xfer rate

		CALL	CHECK_DSTEP		; see if we need double
		OR	AH,AH			; stepping. directly updates
		JZ	SHORT DIX_SEEK
		JMP	DIX_CHECK_ERROR		; current_media.

DIX_SEEK:	MOV	AL,BP_DRIVE		; get drive
		MOV	AH,BP_CYLINDER		; logical cylinder
		CALL	NEC_SEEK		; position head over cylinder
		OR	AH,AH
		JZ	SHORT DIX_SET_UP_DMA
		JMP	DIX_CHECK_ERROR
DIX_SET_UP_DMA:
;xfer size calculation:
;size of xfer = SECTORS * 128 * 2^N, where N comes from
;user parameter table.
;	128 = 2^7
;	so size of xfer = SECTORS * 2^7 * 2^N.
;			= SECTORS * 2^(7+N)
;			= SECTORS left-shifted 7+N

		MOV	AL,3			; get "N", as in 128*2^N
		CALL	GET_PARM
		MOV	CL,AL			; move to shift count reg.
		ADD	CL,7			; add 7 shifts for 128
		MOV	AL,BP_AL		; get # of sectors.
		XOR	AH,AH			; in  ax.
		SHL	AX,CL			; every shift = times 2
		DEC	AX			; DMA controller wants
						; count - 1
		PUSH	AX			; store xfer size on stack.
		PUSH	BP_ES			; store segment
		PUSH	BP_OFFSET		; store offset.

		MOV	BL,BP_OPCODE
		XOR	BH,BH
		SUB	BX,2
		MOV	AL,DGROUP:DIX_DMA_TABLE[BX]
		MOV	SI,SP
		CALL	DMA_SETUP
		ADD	SP,6			; clear the stack.
		OR	AH,AH
		JZ	SHORT DIX_PREPARE_OPERATION
		JMP	short DIX_CHECK_ERROR

DIX_PREPARE_OPERATION:
		MOV	AL,6			; get DTL, byte 9 of
		CALL	GET_PARM		; command.
		MOV	BH,AL
						; get gap length, byte 8
		MOV	BL,01BH			; assume 500 kbs xfer rate
		MOV	AL,BP_DRIVE
		CALL	GET_CURRENT_MEDIA
		AND	AH,0C0H
		JZ	SHORT DIX_GOT_GAP_LEN
IFDEF	ENABLE_288_SUPPORT
		CMP	AH,0C0H			; check 1 mbs xfer rate
		JE	SHORT DIX_GOT_GAP_LEN	;skip if so
ENDIF	;ENABLE_288_SUPPORT
		MOV	BL,02AH			; gap length for 300, 250 kbs
DIX_GOT_GAP_LEN:
		PUSH	BX			; bytes 9 and 8 on stack.

		MOV	AL,4			; get "EOT", last track, from
		CALL	GET_PARM		; user parameter table.

IFDEF	FORCE_1MB_EOT
		CALL	GET_CURRENT_EOT		; modify EOT in al as necessary
ENDIF	;FORCE_1MB_EOT

		MOV	BH,AL
		MOV	AL,3			; get "N", sector size, from
		CALL	GET_PARM		; user parameter table.
		MOV	BL,AL
		PUSH	BX			; push bytes 7 and 6 on stack.

		MOV	BH,BP_SECTOR		; get "R", starting track.
		MOV	BL,BP_HEAD		; get "H", the head:  DO NOT
						; range check these-- copy protect
						; schemes.
		PUSH	BX			; push bytes 5 and 4 of command

		MOV	BH,BP_CYLINDER		; get "C",cylinder, don't range check.
		MOV	BL,BP_HEAD		; get head
		AND	BL,01H			; range check it.
		SHL	BL,2
		OR	BL,BP_DRIVE		; or in drive, it is already
						; range checked.
		PUSH	BX			; push bytes 3 and 2 of command.
		MOV	AH,NC_READ		;R00 ; assume a read or verify command
		CMP	BP_OPCODE,03H		;R00 ; is it a write
		JNE	SHORT DIX_SEND_COMMAND	;R00 
		MOV	AH,NC_WRITE		;R00 ; change to a write command

DIX_SEND_COMMAND:
		MOV	BL,NC_LEN_RW
		MOV	SI,SP			; pointer to rest of parameters.
Send_Verify_para:			  		;R00
		CALL	SEND_COMMAND
		ADD	SP,8			; clear stack
		OR	AH,AH
		JNZ	SHORT DIX_CHECK_ERROR
DIX_GET_RESULTS:
		CALL	WAIT_FOR_RESULTS	; wait for interrupt, read status
DIX_CHECK_ERROR:
		OR	AH,AH			; see if error
		JZ	SHORT DIX_SUCCESS
		CMP	AH,80H			; if timeout, abort operation.
		JE	SHORT DIX_FAILURE
		CMP	AH,09H			; if DMA	boundary error, abort
		JE	SHORT DIX_FAILURE	; operation
		CMP	AH,3			;R23 if write protect then dont retry
		JE	SHORT DIX_FAILURE	;R23
		MOV	BH,AH			; save error
		MOV	AL,BP_DRIVE
		CALL	GET_CURRENT_MEDIA
		TEST	AH,010H			; determined
		MOV	AH,BH			; recover error
		JNZ	SHORT DIX_FAILURE

		CALL	PREPARE_RETRY		; ah unchanged if no more retries
		OR	AH,AH			; else ah=0 if more retries.
		JNZ	SHORT DIX_FAILURE
		JMP	DIX_CHECK_DCL
DIX_FAILURE:	MOV	BP_AL,0			; no sectors xferred.
		JMP	SHORT DIX_EXIT		;

DIX_SUCCESS:	MOV	AL,BP_DRIVE
		CALL	UPDATE_MEDIA_INFO	; see if we learned anything
		MOV	CX,BP_SEC_CYL		; cx=starting sector, cylinder
		MOV	DH,BP_HEAD		; dh=starting head
		CALL	CALC_XFER_SIZE		; new about drive capabilities
		MOV	BP_AL,AL		; or current media.
		XOR	AH,AH			; successful operation.
DIX_EXIT:	JMP	D_NORMAL_RET

DISK_IO_XFER	ENDP

IFDEF	ENABLE_288_SUPPORT
		align	4			;R08
GET_CURRENT_EOT	PROC	NEAR

		PUSHA				; save regs
		MOV	AL,BP_DRIVE
		CALL	GET_CURRENT_MEDIA
		AND	AH,0C0H			; isolate data rate field.
		CMP	AH,0C0H			; check 1 mbs
		JE	SHORT GET_EOT_FORCE	; skip if so to force EOT
		POPA				; else restore entry registers
		CLC				; signal not 1 mbs
		RET				; and return no action

GET_EOT_FORCE:
		POPA				; restore entry registers
		MOV	AL,24H			; and force 36 sectors for 1 mbs trnasfer rate
		STC				; signal 1 mbs
		RET				; and return new value
GET_CURRENT_EOT	ENDP
ENDIF	;ENABLE_288_SUPPORT

		PAGE
;[]-----------------------------------------------------------------------
;[]-----------------------------------------------------------------------

		align	4			;R08
DISK_IO_FORMAT	PROC	NEAR
		MOV	AL,BP_DRIVE		; see if a drive is there
		CALL	GET_DCAPABILITY
		OR	AH,AH
		JNZ	SHORT DIF_CHECK_IF_SET
		MOV	AH,ST_TIMEOUT		; if not, return timeout
		JMP	DIF_EXIT		; error.
DIF_CHECK_IF_SET:
		CALL	GET_CURRENT_MEDIA
		TEST	AH,010H
		JNZ	SHORT DIF_MEDIA_SET
		PUSH	AX			; save drive
		CALL	GUESS_AT_MEDIA
		POP	AX			; recover drive.
DIF_MEDIA_SET:	OR	G_RAM:[MOTOR_ON_IND],80H
		PUSH	AX
		CALL	TURNONMOTOR		; turn on motor and wait
		POP	AX			; if not already on.
		CALL	GET_DCAPABILITY		; If 40 track drive,
		TEST	AH,1			; then don't test DCL.
		JZ	SHORT DIF_SEND_SPECIFY
		CALL	READ_AND_CLEAR_DCL
		OR	AH,AH
		JZ	SHORT DIF_SEND_SPECIFY
		JMP	DIF_EXIT		; return with error in ah.

DIF_SEND_SPECIFY:
;R24 - start
ifdef	FDD_3_Mode
		pusha					;R24A
		mov	al,FloppyB_3_mode	
		cmp	byte ptr BP_DRIVE,1
		je	short @F
		mov	al,FloppyA_3_mode
@@:
		test	G_RAM:FDD_VERIFY_CMD_FLAG,al	;detect set 3 mode
		jz	short @F			;No,jmp disable 3 mode
		mov	al,BP_DRIVE			;detect 1.2Mb fdd set
		call	get_cmos_drive
		cmp	ah,2
		mov	al,1				;enable FDD_3_Mode
		je	short do_set_3_mode
@@:
		xor	al,al				;Disable 3 mode
do_set_3_mode:
		call	Set_FDD_3_mode
		popa					;R24A
endif;	FDD_3_Mode
;R24 - end
		; send a specify command, using external parameter table.

		MOV	BX,DS			; save ds
		MOV	CX,SEG_0
		MOV	DS,CX			; CX DESTROYED IN SEND_COMMAND
		ASSUME	DS:SEG_0
		LDS	SI,SEG_0:[DISK_PARM_PTR]	; DS NOW REPLACED
		PUSH	DS:[SI]			; push 1st and 2nd specify
		MOV	DS,BX			; recover ds.
		ASSUME	DS:DGROUP
		MOV	SI,SP			; pointer to args
		MOV	AH,NC_SPECIFY		; specify command
		MOV	BL,NC_LEN_SPECIFY	; 3 byte command
		CALL	SEND_COMMAND		; send it, DESTROYS CX
		POP	BX			; clear stack.
		OR	AH,AH
		JZ	SHORT DIF_SPECIFY_COMPLETE
		JMP	DIF_EXIT
DIF_SPECIFY_COMPLETE:
		MOV	AL,BP_DRIVE
		CALL	RESET_XFER_RATE

		; SET UP DMA
		; xfer size calculation:
		; size of xfer = SECTORS * 4, since each sector has a
		; 4 byte field.

		MOV	AL,4			; get EOT parameter.
		CALL	GET_PARM
		SHL	AX,2			; multiply by 4.
		DEC	AX			; minus 1 for DMA
		PUSH	AX			; store xfer size on stack.
		PUSH	BP_ES			; store segment on stack.
		PUSH	BP_OFFSET		; store offset on stack.
		MOV	SI,SP			; point to parameters.
		MOV	AL,04AH			; write command for DMA
		CALL	DMA_SETUP
		ADD	SP,6			; clear the stack.
		OR	AH,AH			; exit if boundary error.
		JZ	SHORT DIF_DMA_PREPARED
		JMP	DIX_EXIT
DIF_DMA_PREPARED:

		; SEEK TO TRACK
		MOV	AL,BP_DRIVE
		MOV	AH,BP_CYLINDER
		CALL	NEC_SEEK
		OR	AH,AH
		JZ	SHORT DIF_ON_CYLINDER
		JMP	DIF_EXIT


DIF_ON_CYLINDER:
		MOV	BX,DS			; SEND COMMAND
		MOV	CX,SEG_0
		MOV	DS,CX
		ASSUME	DS:SEG_0
		LDS	SI,SEG_0:[DISK_PARM_PTR] ; REPLACES DS, SEG0 NO MORE
		ASSUME	DS:NOTHING
		PUSH	DS:[SI+8]		; store filler byte on stack.
						; its byte 6 of command, the last.
		MOV	AH,DS:[SI+7]		; get format gap length, byte 5
		MOV	AL,DS:[SI+4]		; get "EOT", byte 4.
		PUSH	AX			; push bytes 5 and 4 on stack.
		MOV	AH,DS:[SI+3]		; get "N", sector size, byte 3.
		MOV	DS,BX			; recover ds
		ASSUME	DS:DGROUP

		MOV	AL,BP_HEAD		; get head/drive select
		AND	AL,01H			; range check head
		SHL	AL,2
		OR	AL,BP_DRIVE		; byte 2
		PUSH	AX			; push bytes 3 and 2 on stack

		MOV	SI,SP			; pointer to parameters.
		MOV	AH,NC_FORMAT		; format command.
		MOV	BL,NC_LEN_FORMAT	; 6 parameters
		CALL	SEND_COMMAND		; DESTROYS CX
		ADD	SP,6			; clear stack
		OR	AH,AH
		JNZ	SHORT DIF_EXIT

DIF_GET_RESULTS:
		CALL	WAIT_FOR_RESULTS	; wait for interrupt, read status
DIF_EXIT:	JMP	D_NORMAL_RET		; ah=0 or error code.

DISK_IO_FORMAT	ENDP

		PAGE
;[]---------------------------------------------------------------------------
;[]---------------------------------------------------------------------------

		align	4			;R08
DISK_IO_GET_PARMS	PROC	NEAR
; SEG_0
		MOV	AL,BYTE PTR G_RAM:HARDWARE
		TEST	AL,1
		JNZ	SHORT DIGP_SOME_DRIVES
		XOR	AL,AL			; no drives
		JMP	SHORT DIGP_CHECK_DRIVE
DIGP_SOME_DRIVES:
		ROL	AL,2			; bit 6 to bit 0.
		AND	AL,01H			; isolate
		INC	AL			; 0->1, 1->2
DIGP_CHECK_DRIVE:
		XCHG	BP_DRIVE,AL		; # of drives on stack,
		CMP	AL,80H			; al=drive requested
		JB	SHORT DIGP_IN_RANGE	; HDU call?
		MOV	AH,1			; Error.
		MOV	BP_OFFSET,0		; Drive type = 0
		JMP	DIGP_EXIT
DIGP_IN_RANGE:	CMP	BP_DRIVE,0		; if # of drives = 0, zero out
		JE	SHORT DIGP_ZERO_OUT	; parameters.
		CMP	AL,01H			; if drive >1, zero out.
		JA	SHORT DIGP_ZERO_OUT

		CALL	GET_CMOS_VALUE
		OR	AH,AH			;;;XXXX if zero, out of range or bad
		JZ	SHORT DIGP_GUESS_DRIVE	; bad CMOS, try to guess drive type.

IFDEF	ENABLE_288_SUPPORT
		CMP	AH,CMOS_FD_288		; check 2.88 drive
		JNE	SHORT DIGP_NOT_288	; skip if not
		MOV	AH,CMOS_FD_144		; get start table for 1.44 drive
		CALL	CALC_PTABLE_OFFSET
		ADD	SI,2*(SIZE FDPARMS)	; use largest capacity.
		MOV	BP_OFFSET,DOS_FD_288	; drive type returned thru bx.
		JMP	SHORT DIGP_SET_VALUES	; get to common code

DIGP_NOT_288:
ENDIF	;ENABLE_288_SUPPORT
		CMP	AH,CMOS_FD_144
		JA	SHORT DIGP_GUESS_DRIVE


;R05		CALL	GET_CMOS_DRIVE
;R05		OR	AH,AH			; if zero, out of range or bad
;R05		JZ	SHORT DIGP_GUESS_DRIVE	; bad CMOS, try to guess drive type.
;R05		CMP	AH,4
;R05		JA	SHORT DIGP_GUESS_DRIVE
		MOV	BL,AH
		XOR	BH,BH
		MOV	BP_OFFSET,BX		; drive type returned thru bx.
		JMP	SHORT DIGP_DRIVE_FOUND
DIGP_GUESS_DRIVE:
		CALL	GET_CURRENT_MEDIA
		TEST	AH,010H			; see if established.
		JZ	SHORT DIGP_ZERO_OUT	; if not established, return zero.

		XOR	BX,BX			; signal to caller that we guessed
		MOV	BP_OFFSET,BX		; drive by returning BX = 0

		MOV	BL,AH			; make index out of bx
		ROL	BL,3			; xfer rate to bits 0 and 1
		AND	BL,0110B		; isolate xfer rate
		CALL	GET_DCAPABILITY
		AND	AH,01H			; isolate 80/40 track flag
		OR	BL,AH			; add to bl for index
		MOV	AH,DGROUP:DIGP_DRIVE_TABLE[BX]
		JMP	SHORT DIGP_DRIVE_FOUND

DIGP_ZERO_OUT:	MOV	BP_AL,0			; input:  dl=#of drives
		MOV	BP_SEC_CYL,0
		MOV	BP_HEAD,0
		MOV	BP_DI,0
		MOV	BP_ES,0
		MOV	BP_OFFSET,0
		JMP	SHORT DIGP_CLEAR_AH

DIGP_DRIVE_FOUND:
						; input: ah=drive type
		CALL	CALC_PTABLE_OFFSET

IFDEF	ENABLE_288_SUPPORT
		CMP	AH,LOCAL_FD_288		; check 2.88 drive
		JE	SHORT DIGP_ADJ_PARMS	; skip if so to use highest capacity
ENDIF	;ENABLE_288_SUPPORT

		TEST	AH,1			; if a dual-capacity drive
		JNZ	SHORT DIGP_SET_VALUES
DIGP_ADJ_PARMS:
		ADD	SI,SIZE FDPARMS		; then use larger capacity.
DIGP_SET_VALUES:
		MOV	BP_AL,0			; AL=0
		MOV	AL,[SI].FD_EOT
		MOV	AH,[SI].FD_LAST_CYL
		MOV	BP_SEC_CYL,AX
		MOV	BP_HEAD,1		; always 2 heads on a floppy
		MOV	BP_DI,SI
		MOV	BP_ES,CS
DIGP_CLEAR_AH:	XOR	AH,AH			; indicate no error.
DIGP_EXIT:	RET				; real RET, not a jump back to
						; d_normal_ret since 8 is special.

DISK_IO_GET_PARMS	ENDP

		PAGE
;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Read_DASD
;Entry:  	INT 13H
;Input:  	ah=15H
;                dl=drive (0-1 diskette, 80H-81H hard)
;Output: 	ah=0 not present
;                  =1 diskette - no change line available
;                  =2 diskette - change line available
;                  =3 hard
;                cy=0 unless drive specified out of bounds. Then cy=1, al=1
;Description:
;
;Disk_IO_Read_DASD will
;1. 	save environment
;2. 	If drive in bounds then set ah as indicated above else set error.
;        Set data values at 0:4??H as needed.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

		align	4			;R08
DISK_IO_READ_DASD	PROC	NEAR
		MOV	AL,BP_DRIVE		; get current drive
		CALL	GET_DCAPABILITY		;
		OR	AH,AH			; if no drive, exit with
		JZ	SHORT DIRD_EXIT		; ah=0
		AND	AH,01H			; 40 track drive = 1
		INC	AH			; 80 track drive = 2
DIRD_EXIT:	JMP	D_NORMAL_RET
DISK_IO_READ_DASD	ENDP

		PAGE
		align	4			;R08
DISK_IO_CHANGE_LINE	PROC	NEAR
		MOV	BH,ST_TIMEOUT		; assume a timeout
		MOV	AL,BP_DRIVE		; get drive
		CALL	GET_DCAPABILITY
		OR	AH,AH			; if no drive
		JZ	SHORT DICL_EXIT

		MOV	BH,ST_MEDIACHANGE	; assume media change error.
		TEST	AH,01H			; a 40 track drive  always
		JZ	SHORT DICL_EXIT		; returns media change.

		PUSH	BX			; save ST_MEDIACHANGE in bh.
		PUSH	AX
		CALL	TURNONMOTOR		; must turn motor on to
		POP	AX			; recover drive #.
		CALL	READ_DCL		; read line
		POP	BX			; recover ST_MEDIACHANGE
		JNZ	SHORT DICL_EXIT		; if active, return ST_MEDIA
		XOR	BH,BH			; else return no error.
DICL_EXIT:	MOV	AH,BH			; move code to ah.
		JMP	D_NORMAL_RET
DISK_IO_CHANGE_LINE	ENDP

		PAGE
;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Write_DASD
;Entry:  	INT 13H
;Input:  	ah=17H
;                dl=drive (0-1)
;                al=0 reserved
;                  =1 360K media in 360K drive
;                  =2 360K media in 1.2M drive
;                  =3 12.M media in 1.2M drive
;Output: 	ah=status, if error cy=1, else cy=0
;Description:
;
;Disk_IO_Write_DASD will
;1. 	save environment
;2. 	If drive in bounds then set data values at 0:4??H as indicated by al
;        above else set error.
;3. 	restore environment
;4. 	return to POI
;[]------------------------------------------------------------------------

		align	4			;R08
DISK_IO_WRITE_DASD	PROC	NEAR
		MOV	AL,BP_DRIVE
		CMP	BP_AL,1
		JNE	SHORT DIWD_READ_DCL
		MOV	CX,00090H		; cl= 250 kbs, no double stepping
						; ch=0, no error.
		JMP	SHORT DIWD_EXIT

DIWD_READ_DCL:	PUSH	AX			; save drive #
		CALL	TURNONMOTOR		; spin up motor for change line.
		POP	AX			; recover drive #
		PUSH	AX			; resave drive #
		CALL	READ_AND_CLEAR_DCL	; check disk change line
		MOV	CH,AH			; copy return code to ch
		POP	AX			; recover drive #
		CMP	CH,80H			; if unable to clear dcl, exit
		JNE	SHORT DIWD_DCL_CLEAR	; with error code 80h
		XOR	CL,CL			; clear current media
		JMP	SHORT DIWD_EXIT
DIWD_DCL_CLEAR:	XOR	BX,BX			; bx=0
		OR	BL,BP_AL		; bx=index, check for bl=0
		JZ	SHORT DIWD_BAD_CODE	; bl=0 is invalid.
		CMP	BL,4			; bx must be below 5
		JBE	SHORT DIWD_IN_RANGE
DIWD_BAD_CODE:	MOV	CX,0100H
		JMP	SHORT DIWD_EXIT
DIWD_IN_RANGE:	JNE	SHORT DIWD_LOOKUP	; if 2 or 3, go look up xfer rate
		CALL	GET_DCAPABILITY		; get capabilities for drive
		AND	AH,06H			; if dual capability not known
		CMP	AH,06H			; or no dual capability, use
		JNE	SHORT DIWD_LOOKUP	; 4 as index
		INC	BL			; else use 5 if known to have
						; dual capability
DIWD_LOOKUP:	MOV	CL,DIWD_LUT[BX]		; look up new setting
DIWD_EXIT:	CALL	GET_CURRENT_MEDIA	; get current media
		AND	AH,0FH			; clear top nibble.
		OR	AH,CL			; add top nibble
		CALL	SET_CURRENT_MEDIA	; store in memory.
		MOV	AH,CH			; return code to ah
		JMP	D_NORMAL_RET

DISK_IO_WRITE_DASD	ENDP

		PAGE
;[]------------------------------------------------------------------------
;Name:   	Disk_IO_Set_Media
;Entry:  	INT 13H
;Input:  	ah=18H
;                dl=drive (0-1)
;		 cl=last sector on cylinder
;		 ch=last cylinder on diskette.
;Output: 	ah=status, if error cy=1, else cy=0
;		If ah=0, the ES:DI will point to a parameter table,
;		unless no drive exists.
;		0:490[drive], will be update to reflect new drive type.
;Description:
;
;Disk_IO_Set_Media will
;1. 	save environment
;2. 	Read CMOS to find drive type.
;3.     Check to see if BIOS has a parameter table for the CMOS drive type
;	which also matches the last sector and last cylinder as passed
;	in cl and ch.
;4.     Return AH=0Ch if no match, else return AH=0h, ES:DI pointing to
;	parameter table if a match.
;5. 	restore environment
;6. 	return to POI
;[]------------------------------------------------------------------------

		align	4			;R08
DISK_IO_SET_MEDIA	PROC	NEAR

		MOV	AL,BP_DRIVE

		CALL	GET_DCAPABILITY
		OR	AH,AH			; if no drive
;R14		JZ	SHORT DISM_OK_EXIT	; return 0, ok.
		JZ	DISM_OK_EXIT		;R14
		TEST	AH,01H			; a 40 track drive doesn't
		JZ	SHORT DISM_READ_CMOS	; need to check change line

		PUSH	AX			; save drive #
		CALL	TURNONMOTOR		; spin up motor for change line.
		POP	AX			; recover drive #
		PUSH	AX			; resave drive #
		CALL	READ_AND_CLEAR_DCL	; check disk change line
		MOV	CH,AH			; copy return code to ch
		POP	AX			; recover drive #

IFNDEF		SIEMENS
;*****************************************************************************
;* Kie 04/25/88 If we cannot clear change line we should not return error 80H
;		because IBMBIO.COM and FORMAT from MS-DOS 3.2x interpret every
;		error as combination or funktion not supported
;*****************************************************************************
		CMP	CH,80H			; if unable to clear dcl, exit
		JNE	SHORT DISM_READ_CMOS	; with error code 80h
		MOV	AH,CH			; CH IS 80H
		JMP	SHORT DISM_RET		; exit.
ENDIF						; SIEMENS

DISM_READ_CMOS:	MOV	AL,BP_DRIVE
		CALL	GET_CMOS_VALUE
		OR	AH,AH			; if there is no drive
		JZ	SHORT DISM_OK_EXIT	; return OK

IFDEF	ENABLE_288_SUPPORT
		CMP	AH,CMOS_FD_288		; check 2.88 drive
		JNE	SHORT DISM_NOT_288	; skip if not
		MOV	AH,CMOS_FD_144		; index tables as 1.44
		CALL	CALC_PTABLE_OFFSET	; si = pointer to 1st table
		MOV	AH,BYTE PTR NUM_DRIVE_TABLES[LOCAL_FD_288] ; get number of drive tables for 2.88
		JMP	SHORT DISM_CHECK_SEC_CYL ; get to common code
DISM_NOT_288:
ENDIF	;ENABLE_288_SUPPORT

		CMP	AH,CMOS_FD_144		; out of range, or bad cmos
		JBE	SHORT DISM_CMOS_OK	; try to find drive type
		MOV	AH,1			; if bad CMOS, try type 1.

DISM_CMOS_OK:	CALL	CALC_PTABLE_OFFSET	; si = pointer to 1st table
		PUSH	BX			; save reg
		MOV	BL,AH			; CMOS type as byte index
		MOV	BH,0
		MOV	AH,BYTE PTR NUM_DRIVE_TABLES[BX] ; get number of drive tables to check for this type
		POP	BX			; restore reg

DISM_CHECK_SEC_CYL:
		MOV	CL,[SI].FD_EOT		; get last track
		MOV	CH,[SI].FD_LAST_CYL	; get last cylinder
		CMP	CX,BP_SEC_CYL		; compare to caller's
		JE	SHORT DISM_SET_MEDIA_EXIT

		DEC	AH			; update counter
;R14		JZ	SHORT DISM_ERROR_EXIT	; skip if all tables tried
;R14 start
		jnz	short @F
		cmp	si,offset FD_1024_PARMS
		je	short DISM_ERROR_EXIT
		MOV	AL,BP_DRIVE
		CALL	GET_CMOS_VALUE
		CMP	AH,CMOS_FD_144		; Is 1.44 FDD?
		jne	short DISM_ERROR_EXIT	;No,jump
		lea	si,FD_1024_PARMS
		mov	ah,1
		jmp	short DISM_CHECK_SEC_CYL
@@:
;R14 end
		ADD	SI,SIZE FDPARMS		; else get to next table
		JMP	SHORT DISM_CHECK_SEC_CYL ; and go again

DISM_SET_MEDIA_EXIT:
		MOV	AH,[SI].FD_XFER_RATE
		CMP	AH,040H			; 300 kbs?
		JNE	SHORT DISM_UPDATE
		OR	AH,020H			; add double stepping
DISM_UPDATE:	OR	AH,010H			; determined
		CALL	SET_CURRENT_MEDIA	; drive # still in al
		MOV	BP_DI,SI		; es:di will point to table
		MOV	BP_ES,CS		; on return.
DISM_OK_EXIT:	XOR	AX,AX			; no error
DISM_RET:	JMP	D_NORMAL_RET
DISM_ERROR_EXIT:
		MOV	AH,0CH
		JMP	SHORT DISM_RET
DISK_IO_SET_MEDIA	ENDP

		PAGE
;!!!!!!!!!!!!!  LEVEL 3 PROCEDURES: General purpose procedures. !!!!!!!!!!!


		PAGE

;[]-----------------------------------------------------------------------
; FAKE_UP_MEDIA_BYTE:  Changes low 3 bits of CURRENT_MEDIA to match
; older ROMs definitions of these 3 bits.
;	INPUT:  AL=drive to update, DCAPABILITY, CURRENT_MEDIA
;	OUTPUT:  CURRENT_MEDIA, low 3 bits.
;		   	= 2 if 500 Kbs, type 2 drive, undetermined
;			= 5 if 500 Kbs, type 2 drive, determined
;			= 1 if 300 Kbs, double stepping, undetermined
;			= 4 if 300 Kbs, double stepping, determined.
;			= 0 if 250 Kbs, 40 track drive, undetermined.
;			= 3 if 250 Kbs, 40 track drive, determined.
;			= 7 if none of above.
;[]------------------------------------------------------------------------

		align	4			;R08
FAKE_UP_MEDIA_BYTE	PROC	NEAR
		MOV	DL,7			; assume new drive type
		CALL	GET_DCAPABILITY		; get 40/80 track info.
		MOV	BX,1
		AND	BL,AH			; bx has 40/80 track info.

		CALL	GET_CURRENT_MEDIA
		MOV	DH,AH			; dh=copy of bits 3-7
		MOV	CH,AH			; ch=copy for double step/determined.
		AND	AH,0C0H			; ah=xfer rate
		JZ	SHORT FUMB_500_KBS	; if 500 kbs, handle special.
		CMP	AH,80H			; compare to middle xfer rate
		JA	SHORT FUMB_UPDATE	; if invalid, low 3 bits = 7.
		JE	SHORT FUMB_MAKE_INDEX	; if 250, top bit of index = 0
		OR	BL,08H			; if 300, top bit of index = 1
FUMB_MAKE_INDEX:
		SHR	CH,3			; move double step and determined
		AND	CH,0110B		; bits to bits 1 and 2 and add
		OR	BL,CH			; to index.
		MOV	DL,FUMB_BIT_TABLE[BX]	; get low 3 bits
		JMP	SHORT FUMB_UPDATE	; add to media.

FUMB_500_KBS:	CALL	GET_CMOS_DRIVE		; if 500 kbs, but not
		CMP	AH,2			; type 2, then low 3 bits = 7
		JNE	SHORT FUMB_UPDATE
		MOV	DL,2			; assume undetermined
		TEST	DH,010H			; test determined.
		JZ	SHORT FUMB_UPDATE	; jump if undetermined
		MOV	DL,5			; low 3 bits = 5 if determined
FUMB_UPDATE:	MOV	AH,DH			; recover CURRENT_MEDIA
		AND	AH,NOT 07H		; clear low 3 bits
		OR	AH,DL			; combine with new low 3 bits
		CALL	SET_CURRENT_MEDIA	; change CURRENT_MEDIA
		RET
FAKE_UP_MEDIA_BYTE	ENDP

		PAGE
;[]------------------------------------------------------------------------
;READ_AND_CLEAR_DCL:  Reads disk change line, if active, attempts to clear
;		      it.
;INPUT:  AL = drive #, MOTOR MUST ALREADY BE RUNNING FOR VALID RESULTS.
;OUTPUT:  AH=0 if disk change line not active
;	     6 if was active, but we successfully clear it.
;	     80h if active, and unable to clear it.
;	     CURRENT_MEDIA is set to unestablished.
;	May destroy any other register.
;[]------------------------------------------------------------------------

		align	4			;R08
READ_AND_CLEAR_DCL	PROC	NEAR
		CALL	READ_DCL		; read the change line
						; ah=code, al preserved.
		MOV	AH,0			; assume ok, SAVE FLAGS
;R14		JZ	SHORT RACD_EXIT		; exit if okay
;R14 start
		jnz	SHORT DCL_enable
ifdef	FDD_3_Mode
		mov	cl,FloppyA_3_mode		;R14D
		mov	al,FDD_A_3_mode_detect
		cmp	byte ptr BP_DRIVE,0
		je	short @F
		mov	cl,FloppyB_3_mode		;R14D
		mov	al,FDD_B_3_mode_detect
@@:
		test	G_RAM:FDD_VERIFY_CMD_FLAG,cl	;R14D
		jz	RACD_EXIT			;R14D
		test	G_RAM:FDD_VERIFY_CMD_FLAG,al
;R16		jnz	SHORT RACD_EXIT
		jnz	RACD_EXIT		;R16
		MOV	AH,ST_MEDIACHANGE
;R24 - start
;R24A		cmp	BP_OPCODE,5		;if format disk then don't
;R24A		je	RACD_EXIT		;detect FDD 3 mode
;R24 - end
		cmp	BP_OPCODE,17h
		je	RACD_EXIT
		cmp	BP_OPCODE,18h
;R16		je	short RACD_EXIT
		je	RACD_EXIT		;R16
		or	G_RAM:FDD_VERIFY_CMD_FLAG,al
endif	;FDD_3_Mode
		jmp	short Detect_3_mode_media
DCL_enable:
;R14 end

		CALL	GET_CURRENT_MEDIA	; User may have changed media.
		AND	AH,NOT 010H		; so we can't rely on rate.
		CALL	SET_CURRENT_MEDIA
		PUSH	AX			; save drive
		CALL	CLEAR_DCL
		POP	AX			; recover drive
		OR	G_RAM:[XFER_INFO],0C0h	;set invalid "last rate"   
		CALL	READ_DCL
		MOV	AH,ST_MEDIACHANGE	; assume we cleared it.
;R14		JZ	SHORT RACD_EXIT
;R14 start
		jnz	short Some_error
Detect_3_mode_media:
ifdef	FDD_3_Mode
		cmp	BP_OPCODE,17h
		je	RACD_EXIT
		cmp	BP_OPCODE,18h
		je	short RACD_EXIT
		push	ax
;R16		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,Floppy_3_mode
;R16		jz	short SF3MD_exit

		mov	al,FDD_A_3_mode_detect
		mov	cl,FloppyB_3_mode	;R16
		cmp	byte ptr BP_DRIVE,1
		je	short @F
		mov	al,FDD_B_3_mode_detect
		mov	cl,FloppyA_3_mode	;R16
@@:
		not	al
		and	G_RAM:FDD_VERIFY_CMD_FLAG,al
;R16 start
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,cl
		jnz	short @F
		xor	al,al			;Disable 3 mode
		call	Set_FDD_3_mode
;R14D;R14C		pop	ax			;R14B get error code (in AH)
;R14D;R14C		xor	ah,ah			;R14B clear error code
;R14D;R14C		push	ax			;R14B restore error code
		jmp	short SF3MD_exit
@@:
		mov	al,BP_DRIVE
		CALL	GET_CMOS_DRIVE
		cmp	ah,2
		je	short Force_3mode
;R16 end

		xor	al,al			;Disable 3 mode
		call	Set_FDD_3_mode
 		mov	cx,1			;0 cylinder 1 sector
		mov	dl,BP_DRIVE
		mov	dh,BP_HEAD
@@:
		mov	ax,401h
		int	13h			;check is 720K?
		jnc	short SF3MD_exit	;Yes or disk OK jump
		cmp	ah,ST_MEDIACHANGE
		je	short @B
Force_3mode:					;R16
		mov	al,1			;Enable 3 mode
		call	Set_FDD_3_mode
SF3MD_exit:
		pop	ax
endif	;FDD_3_Mode
		jmp	SHORT RACD_EXIT
Some_error:
;R14 end
		MOV	AH,ST_TIMEOUT		; can't clear it.
RACD_EXIT:	RET

READ_AND_CLEAR_DCL	ENDP

;R14A start
ifdef	FDD_3_Mode
		include	fdd3mode.sio
endif	;FDD_3_Mode
;R14A end

		PAGE
;[]----------------------------------------------------------------------
;NEC_SEEK:	Moves read head to the requested cylinder.  Recalibrates
;		drive if drive has not yet been recalibrated.  Will take
;		care of double stepping if necessary.
;	INPUT:	AL=Drive #
;		AH=Logical Track #  (not the double-stepped number)
;		CURRENT_MEDIA, bit 5 = +double step bit.
;		NEED_RECAL, bit 0-1 to indicate if this drive needs
;			recalibration.
;		CURCYL to tell us if we are already on the requested drive.
;	OUTPUT:
;	 	AH=error code if unable to complete seek.
;		AH=0 if successfuly seek operation.
;		CURCYL = New physical clinder
;		NEED_RECAL, recalibrate bit for this drive set.
;	May destroy any general register.
;[]----------------------------------------------------------------------

		ALIGN	4     
NEC_SEEK	PROC	NEAR

		MOV	BL,1			; create mask for testing
		MOV	CL,AL			; seek status recalibrate bit
		SHL	BL,CL			; for this drive.

		TEST	G_RAM:[NEED_RECAL],BL	; fall thru if this
		JNZ	SHORT NS_DO_SEEK	; drive needs recal.

		OR	G_RAM:[NEED_RECAL],BL	; set bit, so next seek
						; will not recalibrate.
		MOV	DX,AX			; track, drive to dx.
		XOR	AH,AH			; cylinder will be 0 after
		CALL	SET_CURCYL		; recalibrate.
		MOV	CX,2			; NEC returns error on
						; recal after 77 step pulses,
						; so we made need to recal
						; twice.
NS_RECAL_LOOP:	PUSH	CX			; save loop counter
		PUSH	DX			; save track, pass drive
		MOV	SI,SP			; to SEND_COMMAND
		MOV	AH,NC_RECAL		; recalibrate command.
		MOV	BL,NC_LEN_RECAL		; 2 byte command.
		CALL	SEND_COMMAND
		POP	DX			; recover track, drive.
		POP	CX			; recover loop counter
		OR	AH,AH
		JNZ	SHORT NS_EXIT		; can't send command.
		PUSH	CX			; save loop counter
		PUSH	DX			; save drive & track
		CALL	SENSESTATINT		; check status
		POP	DX			; recover drive, track.
		POP	CX			; recover loop counter
		OR	AH,AH			; check for error.
		LOOPNZ	SHORT NS_RECAL_LOOP	; try again if error.

		JNZ	SHORT NS_EXIT		; if 2 errors, its a genuine
						; error.
		MOV	AX,DX			; ax=cylinder,drive.
		OR	AH,AH			; if cylinder = 0, impose
		JNZ	SHORT NS_DO_SEEK	; head settle time now,
		CALL	HEAD_SETTLE_TIME	; since seek will skip this.
		JMP	SHORT NS_SUCCESS

NS_DO_SEEK:	MOV	BX,AX			; save track.
		CALL	GET_CURRENT_MEDIA
		TEST	AH,020H			; double stepping?
		JZ	SHORT NS_GOT_CYL
		SHL	BH,1			; then double cylinder.

NS_GOT_CYL:	CALL	GET_CURCYL
		CMP	AH,BH
		JE	SHORT NS_SUCCESS	; no need to seek.
		MOV	AH,BH
		CALL	SET_CURCYL

		PUSH	BX			; drive and track on stack
		MOV	SI,SP			; ss:[si] points to args.
		MOV	AH,NC_SEEK		; ah=seek command
		MOV	BL,NC_LEN_SEEK		; 3 byte command
		CALL	SEND_COMMAND
		POP	BX			; recover drive and track
		OR	AH,AH
		JNZ	SHORT NS_EXIT		; unable to send seek.
		PUSH	BX			; save drive, cylinder
		CALL	SENSESTATINT		; get RESULTS of operation.
		POP	BX			; recover drive track.
		OR	AH,AH
		JNZ	SHORT NS_EXIT		; exit if error.
		MOV	AX,BX			; drive to al
		CALL	HEAD_SETTLE_TIME
NS_SUCCESS:	XOR	AH,AH			; indicate no error.
NS_EXIT:	RET
NEC_SEEK	ENDP

		PAGE
;[]----------------------------------------------------------------------
;HEAD_SETTLE_TIME:	imposes head settle time for drive.
;	INPUT:	AL=drive, ES=0
;	OUTPUT: none
;	may destroy any general register.
;[]----------------------------------------------------------------------

		align	4			;R08
HEAD_SETTLE_TIME	PROC	NEAR

		MOV	BX,AX			; save drive #
		MOV	AL,9			; get head settle time
		CALL	GET_PARM
		OR	AL,AL
		JNE	SHORT HST_DO_WAIT	; if not zero, do the wait.

		TEST	G_RAM:[MOTOR_ON_IND],80H	; if zero and a read
		JZ	SHORT HST_EXIT		; skip the wait.

		MOV	AL,BL
		CALL	GET_CURRENT_MEDIA
		MOV	AL,20			; assume longer wait
		OR	AH,AH			; if 250 kbs or invalid
		JS	SHORT HST_DO_WAIT	; use longer wait.
		MOV	AL,15			; use shorter wait.

HST_DO_WAIT:
;Don't use PIE to implement waiting function, because OS2 failure
;R13		test	cs:[SYSTEM2_BYTE],80h	;R08
;R13		jnz	short HST_No_Pie	;R08
;R13		PUSH	AX			; save head settle wait time.
;R13		XOR	AH,AH			; increment, as CMOS chip's
;R13		INC	AX			; resolution is only 1 ms.
;R13		MOV	DX,1000			; 1000 micros per ms.
;R13		MUL	DX
;R13		MOV	CX,AX			; dx:cx=dx:ax
;R13		XCHG	DX,CX			; msw to cx, lsw to dx
;R13		MOV	AH,86H			; wait call.
;R13		INT	15H			; ALLOW OTHER DRIVERS
;R13		sti				;R07
;R13		POP	AX			; recover head settle time
;R13		JNC	SHORT HST_EXIT		; if no carry, all done
;R13HST_No_Pie:		    			;R08


		MOV	DL,CS:WAIT_FDU_HEAD_SETTLE ; CS: override here!    
						; units in a millisecond
		MUL	DL			; calculate # of 30 micro
		MOV	CX,AX			; units in AX milliseconds
		XOR	BX,BX			; bx:cx=wait time.
		CALL	WAIT_REFRESH		; perform wait.

HST_EXIT:	RET

HEAD_SETTLE_TIME	ENDP

		PAGE
;[]----------------------------------------------------------------------
;ERROR_CHECK:  Checks the return codes for the NEC to see if any errors
;	     occurred.  If it detects any, then it translates them
;	     into the BIOS error code.
;	INPUT:  FDC_RET_CODES
;	OUTPUT: AH=0 if no errors,
;		AH=error code if error occurred.

		align	4			;R08
ERROR_CHECK	PROC	NEAR
; SEG_0

		MOV	BL,G_RAM:FDC_RET_CODES[0]	; get ST0
		AND	BL,0C0H			; bits 7 and 6 = 0
		MOV	AH,ST_OK		; if normal termination
		JE	SHORT EC_EXIT		; occurred.

		CMP	BL,40H			; Invalid command
		MOV	AH,ST_BADNEC		; or FDD ready line changed.
		JNZ	SHORT EC_EXIT		; blame it on controller.

		MOV	BL,G_RAM:FDC_RET_CODES[1]	; get ST1
		TEST	BL,84H			; user asked for sector > EOT
		MOV	AH,ST_RNF		; or NEC couldn't find ID
		JNZ	SHORT EC_EXIT		; field that matched.

		TEST	BL,20H			; read an ID or data
		MOV	AH,ST_BADCRC		; field, but the CRC
		JNZ	SHORT EC_EXIT		; didn't add up.

		TEST	BL,10H			; DMA was too slow
		MOV	AH,ST_BADDMA		; too keep up with NEC
		JNZ	SHORT EC_EXIT

		TEST	BL,2			; write protect tab
		MOV	AH,ST_WRITEPROT		; was in place.
		JNZ	SHORT EC_EXIT

		TEST	BL,1			; wasn't able to find
		MOV	AH,ST_BAM		; any address mark at all.
		JNZ	SHORT EC_EXIT

		MOV	AH,ST_BADNEC		; everything else, blame
						; on the controller.
EC_EXIT:	RET
ERROR_CHECK	ENDP

		PAGE
;RESETDRIVES:	Performs a reset of the controller, returning it to a
;		known state.
;	INPUT: none
;	OUTPUT: AH=0 if no error, 20h if controller error, 80h if
;		timeout while issuing specify command.
; called by hrdskio too...
;
;[]----------------------------------------------------------------------
		PUBLIC	RESETDRIVES		; * AHDSK.ASM
		ALIGN	4
RESETDRIVES	PROC	NEAR
		PUSH	DS
		CLI				; stop interrupts while changing
						; port 3F2h, so MOTOR_ON_IND
						; stays in sync.

		MOV	AX,G_RAM
		MOV	DS,AX
		ASSUME	DS:G_RAM

		MOV	AL,G_RAM:[MOTOR_ON_IND]	; don't shut off any motors
		ROL	AL,4			; or change selected bits.
		AND	AL,NOT 04H		; -RESET bit, active low
		OR	AL,8			; +DMA

		MOV	DX,FDC_CTRL_OUT
		OUT	DX,AL
		MOV	G_RAM:[NEED_RECAL],0	; recalibrate on next seek
		MOV	CX,WAITCPU_RESET_ON
		ALIGN	4			; N3.03

@wdw1:
		NEWIODELAY			;R09
;R09		out	0e1h,ax
		loop	short @wdw1						   

		OR	AL,4			; refresh off.
		OUT	DX,AL			; dx = FDC_CTRL_OUT
		IODELAY
		STI				; reset over,

		CALL	SENSESTATINT		; SENSESTATINT();
		MOV	AH,ST_BADNEC		; assume bad controller
		CMP	G_RAM:[FDC_RET_CODES],0C0H	; check if controller
		JNE	SHORT RD_EXIT		; detected change in ready

		XOR	AL,AL			; get 1st specify byte
		CALL	GET_PARM
		MOV	BL,AL
		MOV	AL,1			; get 2nd specify byte
		CALL	GET_PARM
		MOV	BH,AL
		PUSH	BX			; push args on stack.
		MOV	SI,SP			; pointer to args.
		MOV	AH,NC_SPECIFY		; 3=specify command.
		MOV	BL,NC_LEN_SPECIFY	; 3 byte command.
		CALL	SEND_COMMAND
		POP	BX			; clear stack
RD_EXIT:	XOR	AL,AL			; clear al, error code in ah.
		POP	DS
		ASSUME	DS:NOTHING		; ASS-U-ME NEEDS TO BE CLEAR
		RET
RESETDRIVES	ENDP

		PAGE

;[]----------------------------------------------------------------------
;SENSESTATINT:	Called after a SEEK, RECALIBRATE or RESET of the controller.
;		It:
;			1.  Waits for interrupt from controller
;			2.  Sends a Sense interrupt status command to
;			    get ST0, which it stores in FDC_RET_CODES.
;			3.  Evaluates ST0 for SEEK error.
;		INPUT: none
;		OUTPUT: AH=0 if success
;			   80h if no interrupt or problem sending or
;			    receiving sense interrupt status.
;			   40h if bits 5 and 6 of ST0 are set, indicating
;			   an abnormal termination.
;			FDC_RET_CODES = ST0, (the RESET command wants
;			to check ST0 for itself).
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
SENSESTATINT	PROC	NEAR
; SEG_0
		CALL	WAITNECINT		; wait for interrupt to occur
		OR	AH,AH			; if timeout, exit.
		JNZ	SHORT S_EXIT

		MOV	AH,NC_SIS		; sense interrupt status code
		MOV	BL,NC_LEN_SIS		; 1 byte command
		CALL	SEND_COMMAND
		OR	AH,AH
		JNZ	SHORT S_EXIT

		CALL	RESULTS			; read ST0 and Present
						; cylinder number
		OR	AH,AH			; if reading error
		JNZ	SHORT S_EXIT		; return with timeout code.

		MOV	AL,G_RAM:FDC_RET_CODES[0]	; get ST0. Check for
		AND	AL,060H			; abnormal termination of
		CMP	AL,060H			; seek command.
		MOV	AH,ST_BADSEEK
		JE	SHORT S_EXIT

		XOR	AH,AH			; return success

S_EXIT:		RET

SENSESTATINT	ENDP

		PAGE
;[]----------------------------------------------------------------------
;WAITNECINT: 	Waits for the controller to generate an IRQ6, which will
;		cause INT 0Dh to set bit 7 of NEED_RECAL.
;
;		INPUT:  NEED_RECAL, bit 7.
;		OUTPUT: AH=0 if interrupt occurred.
;			AH=80h if interrupt didn't occur, or if INT 15h
;			function 9001h returned with carry flag set.
;			NEED_RECAL, bit 7 = 0.
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
WAITNECINT	PROC	NEAR

		MOV	DI,OFFSET G_RAM:NEED_RECAL	; set now, in case int 15
						; sets carry. CODE WILL BREAK
						; WITHOUT THIS
		MOV	AX,9001H
		CLC
		INT	15H
		sti				;R07
		MOV	AH,ST_TIMEOUT		; assume timeout.
		JC	SHORT W_EXIT

		MOV	AH,80H			; check top bit
		MOV	BH,DGROUP:[WAIT_FDU_INT_HI]	; outer loop counter
		MOV	CX,DGROUP:[WAIT_FDU_INT_LO]	; inner loop counter
		CALL	WAIT_FOR_MEM		; returns ah=80h or 0h

W_EXIT:		AND	BYTE PTR G_RAM:[DI],NOT 80H	; di = NEED_RECAL
		TEST	AH,80H
		JZ	W_RET
		STC
W_RET:		RET
WAITNECINT	ENDP

		PAGE
;[]----------------------------------------------------------------------
;RESULTS:	Reads status bytes back from NEC controller and
;		stores them into FDC_RET_CODES array.
;	INPUT: none
;	OUTPUT: FDC_RET_CODES
;		AH=80h if timeout while waiting for Status port
;		    to give us RQM or direction bit is wrong.
;		AH=ST_BADNEC if more than 7 bytes.
;		Other general registers may be destroyed.
;[]----------------------------------------------------------------------

		public	RESULTS					;R22
		ALIGN	4			; 303A
RESULTS		PROC	NEAR

		MOV	DI,OFFSET G_RAM:FDC_RET_CODES

		ALIGN	4			; N3.03
R_NEXT_BYTE:	MOV	CX,DGROUP:[WAIT_FDU_RESULTS_LO]	; length of time for
		MOV	BH,DGROUP:[WAIT_FDU_RESULTS_HI]	; RQM and direction set.
		MOV	AX,0C0C0H		; RQM is bit 7, dir bit 6.
		MOV	DX,NEC_STAT_PORT	; of port 3F4h.
		CALL	WAIT_FOR_PORT		; ah=80h if timeout, 0 otherwise.
		OR	AH,AH
		JNZ	SHORT R_EXIT

		INC	DX			; dx is now NEC_DATA_PORT
;R18IFDEF		COMPUADD
;R18		IN	AL,DX			; vain attempt to avoid
;R18		SIODELAY			; bus posting timing problem
;R18		STOSB
;R18ELSE
		IN	AL,DX			; es:[di] = status port.
		NEWIODELAY			;R18
		STOSB				; inc di
;R18ENDIF						; COMPUADD
		XOR	BX,BX

		push	cx
;R18		mov	cx, 20
		mov	cx, 30			;R18
@wdw2:
		NEWIODELAY			;R09
;R09		out	0e1h,ax
		loop	short @wdw2
		pop	cx

		MOV	DX,NEC_STAT_PORT	; get back dx
		IN	AL,DX			; read the status port.
		NEWIODELAY			;R18
		XOR	AH,AH			; assume no error, save flag
		TEST	AL,10H			; more?
		JE	SHORT R_EXIT		; quit if no more.
		MOV	AH,ST_BADNEC		; assume error.
		CMP	DI,OFFSET FDC_RET_END	; if we still have room for
		JBE	SHORT R_NEXT_BYTE	; more codes, go get them.

R_EXIT:		RET

RESULTS		ENDP

		PAGE
;[]----------------------------------------------------------------------
;TURNONMOTOR:	Turns on motor and SELECTS drive.  In addition,
;		it also will impose a motor start up delay if
;		the motor was not already spinning.
;	INPUT:	AL=drive number.
;
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
TURNONMOTOR	PROC	NEAR

		XOR	CH,CH			; ch = 0, assume drive
						; is already running
		MOV	BH,1			; bh=mask for drive.
		MOV	CL,AL
		SHL	BH,CL

		CLI				; interrupts off so that
						; MOTOR_ON_IND stays in synch
						; with port 3f2h

		MOV	BL,G_RAM:[MOTOR_ON_IND]	; get current port 3f2h
		TEST	BL,BH			; is motor already running?
		JNZ	SHORT M_UP_COUNT
		INC	CH			; flag that motor is just
						; now being turned on.
M_UP_COUNT:	MOV	G_RAM:[MOTOR_OFF_WAIT],0FFH	; putd(MOTOR_OFF_WAIT,0xff,0)

		MOV	AH,BL			; ah=old motor status
		AND	AH,3			; save currently running drvs.
		MOV	CL,4			; shift to upper nibble for
		SHL	AH,CL			; port 3f2,
		MOV	DL,AH			; save in dl for now.
		AND	BL,0CFH			; Clear drive select
		MOV	AH,AL			; drive to ah
		SHL	AH,CL			; shift drive to upper nibble
		OR	BL,BH			; or in the new motor
		OR	BL,AH			; OR in the new drive select
		MOV	G_RAM:[MOTOR_ON_IND],BL	; update motor status

		MOV	AH,DL			; assemble port 3f2h in
		MOV	DL,10H			; ah register.
		MOV	CL,AL
		OR	AH,AL			; or in drive select
		SHL	DL,CL
		OR	AH,DL			; or in the new motor
		OR	AH,0CH			; or no reset, use DMA
		XCHG	AH,AL
		MOV	DX,FDC_CTRL_OUT
		OUT	DX,AL			; out to controller
		STI				; allow interrupts, now that
						; port 3F2h and MOTOR_ON_IND
						; are the same again.

		OR	CH,CH			; if motor already running
		JE	SHORT M_NO_WAIT		; don't wait.

		MOV	AX,090FDH		; give multitasker a chance
		CLC				; to bypass motor spin up.
		INT	15H
		sti				;R07
		JNC	SHORT M_DO_WAIT		; if no multi-tasker, do wait.

M_NO_WAIT:	RET

M_DO_WAIT:	MOV	AX,10			; J = getparm(10);
		CALL	GET_PARM
		TEST	G_RAM:[MOTOR_ON_IND],80H	; read operation
		JE	SHORT M_READ_OP		; use shorter minimum

		CMP	AL,7			; write or format, ensure
		JA	SHORT M_CALC_WAIT	; 1 second minimum

		MOV	AX,8
		JMP	SHORT M_CALC_WAIT

M_READ_OP:					; read, read verify
		OR	AL,AL			; no motor spin up ok on
		JZ	SHORT WAIT_OVER		; reads if DOS does retries.

		CMP	AL,4			; ensure 5/8 second spin up.
		JA	SHORT M_CALC_WAIT

		MOV	AX,5

M_CALC_WAIT:	XOR	AH,AH			; ax=1/8 sec. units.
;Don't use PIE to implement waiting function, because OS2 failure
;R13		test	cs:[SYSTEM2_BYTE],80h	;R08
;R13		jnz	short Mortor_No_Pie	;R08
;R13		MOV	BX,AX			; copy to bx, if int 15 busy
;R13ifdef	NO_SHORT_MOTOR_ON			; R04
;R13		mov	cx,62500		; R04
;R13else;	NO_SHORT_MOTOR_ON			; R04
;R13		MOV	CX,62500/4		; cx=number of 2 microsecond
;R13						; units in 1/8 of a second.
;R13endif;	NO_SHORT_MOTOR_ON			; R04
;R13		MUL	CX			; dx:ax=# of 2 microsecond
;R13						; units to wait
;R13		MOV	CX,DX			; MSW to cx
;R13		MOV	DX,AX			; LSW to dx
;R13		SHL	DX,1			; mul cx:dx * 2.
;R13		RCL	CX,1			; top bit of dx to low bit of
;R13						; cx.
;R13		MOV	AH,86H			; CMOS chip wait call.
;R13		INT	15H
;R13		sti				;R07
;R13		JNC	SHORT WAIT_OVER		; exit if wait occurred
;R13						; else use refresh to time
;R13						; wait.
;R13		MOV	AX,BX			; recover 1/8 sec units
;R13Mortor_No_Pie:					;R08

		MOV	CX,CS:WAIT_FDU_8THS	; located in the CS!!	   
						; in 1/8 of a second
		MUL	CX			; dx:ax=wait_refresh micro units to wait
		MOV	CX,AX			; bx:cx = dx:ax
		MOV	BX,DX
		CALL	WAIT_REFRESH
WAIT_OVER:	RET

TURNONMOTOR	ENDP

		PAGE
;[]----------------------------------------------------------------------
;CALC_XFER_SIZE:  Calculates the number of sectors that were transferred.
;	INPUT: CH=starting cylinder
;	       CL=starting sector
;	       DH=starting head
;		FDC_RET_CODES[3] = ending cylinder
;		FDC_RET_CODES[4] = ending head
;		FDC_RET_CODES[5] = ending sector.
;	OUTPUT:  AL=sectors xferred.
;
;[]----------------------------------------------------------------------

		ALIGN	4		       
CALC_XFER_SIZE	PROC	NEAR

; The return codes supplied by the FDC point to the sector AFTER
; the last one transferred.  If the last sector transferred was
; the last sector on the track, the head and possibly the cylinder
; will be moved up, and sector set to 1.

		MOV	AL,4
		CALL	GET_PARM
		MOV	AH,AL			; ah=EOT
		MOV	AL,G_RAM:FDC_RET_CODES[5]	; al=ending sector.
		SUB	AL,CL			; al=end-start, may be
						; negative number.
		CMP	DH,G_RAM:FDC_RET_CODES[4]	; did head advance?
		JE	SHORT CXS_HEAD_SAME	; check cylinder if not
		ADD	AL,AH			; al=end-start + EOT
		JMP	SHORT CXS_EXIT		; if head different, cylinder
						; can't have advanced.
CXS_HEAD_SAME:	CMP	CH,G_RAM:FDC_RET_CODES[3]	; did cylinder advance?
		JE	SHORT CXS_EXIT		; exit if not
		SHL	AH,1			; ah=EOT*2
		ADD	AL,AH			; al=end-start + (EOT*2)
CXS_EXIT:	RET				;
CALC_XFER_SIZE	ENDP

		PAGE
;[]----------------------------------------------------------------------
;DMA_SETUP:	set up 8237 for disk transfer
;	input -	AL=command.
;		SS:SI = offset
;		SS:SI+2 = segment
;		SS:SI+4 = size of xfer, in bytes - 1 for DMA
;	output:
;		AH=0 if no error
;		AH=09 if DMA boundary error.
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
DMA_SETUP	PROC	NEAR

; --- QEMM/WINDOWS FIX ---
; Occasionally, complete sectors were being written with wrong data.
; The problem occurs when (1) we DMA from paged memory, and (2) the
; ES:BX value plus sectors would overlap the "linear" 64K boundary
; (detected by BIOS here), and (3) the transfer also overlaps the 64K
; "physical" boundary in the target (EMS) paged RAM.  We note that the
; DMAC writes to "physical" RAM, so when BIOS programs the DMAC with a
; "linear" value, QEMM must reprogram a "physical" address into the
; DMAC.  Furthermore, if a 64K "physical" boundary would be crossed,
; then QEMM needs to supply a temporary buffer, then trap the FDC
; completion interrupt and copy the data to the final target (EMS) RAM.
; QEMM has a state-machine bug here:
; (a) DOS asks BIOS to DMA several sectors across a 64K boundary,
;     (As this RAM is paged, this is a "linear 64K boundary", and
;      does not necessarily correspond to a "physical" 64K boundary).
; (b) BIOS unconditionally programs DMAC according to BIOS request.
; (c) QEMM traps then reprograms DMAC for the actual "physical" address,
;     noting the special case here that it also (coincidental to #a)
;     will cross a 64K "physical" boundary.
; (d) BIOS then checks and determines the "linear" boundary problem,
;     and aborts with error "9" (DMA BOUNDARY FAULT).
; (e) DOS then changes its request to read only the 1st sector. (The
;     2nd sector would actually cross the 64K boundary, and DOS reads
;     that one to an internal 70: buffer, then writes it to the user's
;     RAM location).
; (f) BIOS honors DOS's request for 1 sector, programs the DMAC.
; (e) QEMM reprograms the DMAC for the "physical" address.
; (g) BIOS programs the FDC and the (correct) data transfer occurs.
; (h) QEMM (incorrectly) remembers that it was double-buffering (to
;     solve #c, which is no longer an issue), and overwrites the
;     freshly DMAd data with old data from a temporary buffer.
; -----------------------
; There is a work-around for QEMM's problem, which should be harmless
; to the BIOS.  Since writing to the DMAC triggers QEMM, we should only
; do so when we actually intend to do DMA.  In other words, we should
; check for the DMA boundary situation, and abort prior to (needlessly)
; programming the DMAC.
; 
;
;---------------------------------
;
; Prior to setting up DMA, confirm that segment overflow won't occur.
;
; The DMA controller only has a 16-bit address register, so will wrap
; to the bottom of the 64K segment if too many bytes are transferred.
; Calculation: translate original ES:BX to a value with format X000:MNPQ
; Then figure out if the desired sectors (512 bytes per) would require
; a final address at X001:RSPQ (ie, incremented segment #).
;
 
		PUSH	DX		      

		MOV	DX, SS:[SI+2]		; Expand SEG to 20 bits
		MOV	BL, DH			;
		SHL	DX, 4			;
		SHR	BL, 4			; SEG SHL(4) ---> BL:AX

		ADD	DX, SS:[SI]		; Get SEG SHL(4)+OFFSET
		ADC	BL, 0			;

		ADD	DX, SS:[SI+4]		; Plus #bytes to xfer-1

		MOV	AH, 9			; Assume seg overflow
		JC	SHORT DS_EXIT		; Jump if wrap (bad)

		SUB	DX, SS:[SI+4]		; Restore to start addr

	; All is ok

		CLI
		OUT	DMA_PORT+12,AL		; set the first/last f/f
		IODELAY
		OUT	DMA_PORT+11,AL		; output the mode byte

		SIODELAY			; COMPUADD

		XCHG	AX, DX			; (saved byte)

		OUT	DMA_PORT+4,AL		; output low 2 nibbles of
		XCHG	AL,AH			; address
		IODELAY
		OUT	DMA_PORT+4,AL		; out high 2 nibbles of
		IODELAY				; address.

		XCHG	AX,BX			; save ax, send highest
		OUT	PAGE_PORT,AL		; nibble to page reg
		SIODELAY			; PCMOS

		MOV	AX,SS:[SI+4]		; and amount to transfer

		SIODELAY			; PCMOS
		OUT	DMA_PORT+5,AL		; send LSB of xfer to DMA
		MOV	AL,AH
		IODELAY
		OUT	DMA_PORT+5,AL		; send MSB of xfer to DMA
		IODELAY
		STI

		MOV	AL,2			; initialize diskette channel
		OUT	DMA_PORT+10,AL

		XOR	AX,AX			; no error
DS_EXIT:
		POP	DX			; 
		RET

DMA_SETUP	ENDP

		PAGE
;[]----------------------------------------------------------------------
;clear_dcl:	Attempts to clear disk change line by doing:
;		1. reset
;		2. seek to track 1
;		3. seek to track 0
;
;	INPUT:  Motor must be running, with appropriate drive selected.
;		AL=drive #
;	OUTPUT: none
;	May destory any general registers
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
CLEAR_DCL	PROC	NEAR

		PUSH	AX			; save drive # in al
		CALL	RESETDRIVES
		POP	AX			; recover drive #.
;		MOV	AH,1			; seek to track 1
		MOV	AH,4			; jsz 
		PUSH	AX			; save drive # in al
		CALL	NEC_SEEK
		POP	AX			; recover drive #
		XOR	AH,AH
		CALL	NEC_SEEK
		RET
CLEAR_DCL	ENDP

		PAGE
;[]----------------------------------------------------------------------
;SEND_COMMAND:  Sends a complete command sequence to the NEC controller.
;	INPUT:
;		AH=Command
;		SS:SI=parameters for command
;		BL=number of bytes (maximum of 7)
;	OUTPUT:
;		AH=0 if all bytes sent successfully
;		   80h if timeout error.
;		May destroy any general register.
; DESTROYS CX
;[]----------------------------------------------------------------------

		public	SEND_COMMAND				;R22
		ALIGN	4			; 303A
SEND_COMMAND	PROC	NEAR
SC_NEXT_BYTE:	PUSH	AX			; save value in ah
		MOV	DX,NEC_STAT_PORT
		MOV	BH,DGROUP:[WAIT_FDU_SEND_HI]	; amount of tme to allow
		MOV	CX,DGROUP:[WAIT_FDU_SEND_LO]	; for RQM and direction .
		MOV	AX,0C080H		; check bits 6,7 = 10b.
		CALL	WAIT_FOR_PORT		; wait til RQM is on.
		POP	CX			; recover value from stack.
		OR	AH,AH			; if TIMEOUT
		JNZ	SHORT SC_EXIT		; abort operation.

		MOV	AL,CH			; al=output data.
		MOV	CX,WAITCPU_RQM_LOW

;R06		push	cx						   
;R06		mov	cx, 10						   
;R06@wdw3:
;R06		out	0e1h,ax
;R06		loop	short @wdw3						   
;R06		pop	cx						   

						; prepare post-write wait.
		INC	DX			; point to data register
		OUT	DX,AL			; send the data
		IODELAY
		DEC	DX			; point back to status


;IFNDEF SLOW_DOWN_DISK_FOR_FAST_CPU					   
;		ALIGN	4			; N3.03
;SC_RQM_LOW_LP:	IN	AL,DX			; wait for RQM to go low.
;		TEST	AL,80H			; RQM might remain incorrectly set
;		LOOPNZ	SHORT SC_RQM_LOW_LP
;ENDIF ;SLOW_DOWN_DISK_FOR_FAST_CPU					   

		DEC	BL			; any more bytes to send?
		JZ	SHORT SC_SUCCESS	; exit if done.

		MOV	AH,SS:[SI]		; get next parameter to send
		INC	SI			; point to parameter after this one
		JMP	SHORT SC_NEXT_BYTE

SC_SUCCESS:	XOR	AX,AX			; ah=0, operation a success

SC_EXIT:	RET

SEND_COMMAND	ENDP

		PAGE
;[]----------------------------------------------------------------------
;CHECK_DSTEP	check if double stepping is necessary
;	INPUT:   AL=drive
;	OUTPUT:  CURRENT_MEDIA is updated to reflect whether we
;		 have to double step or not.
;[]----------------------------------------------------------------------
		ALIGN	4			; N3.03
CHECK_DSTEP	PROC	NEAR
		CALL	GET_CURRENT_MEDIA
		TEST	AH,010H			; established?
		JNZ	SHORT CD_SUCCESS
		AND	AH,NOT 020H		; turn off double stepping, for
		CALL	SET_CURRENT_MEDIA	; NECSEEK routine.
		CALL	GET_DCAPABILITY
		TEST	AH,01H			; if 40 track drive, leave with
		JZ	SHORT CD_SUCCESS	; leave with double stepping off.

		MOV	BL,0FEH
		MOV	CL,AL
		ROL	BL,CL
		AND	G_RAM:[NEED_RECAL],BL	; force recalibrate
		XOR	AH,AH			; start at track 0
		MOV	DX,AX			; track,drive and head to dx
		ALIGN	4
CD_READ_ID_LOOP:
		PUSH	DX			; save track, drive # and head
		MOV	AX,DX			; ax=track,drive and head.
		AND	AL,01H			; eliminate head for seek operation
		CALL	NEC_SEEK
		POP	DX			; recover track, drive,head
		OR	AH,AH			; any errors
		JNZ	SHORT CD_EXIT		; failure if errors.

		PUSH	DX			; argument for read id on stack
		MOV	SI,SP			; pointer to argument
		MOV	AH,NC_READ_ID		; read id command
		MOV	BL,NC_LEN_READ_ID	; 2 byte command
		CALL	SEND_COMMAND		; perform read id.
		POP	DX			; recover track,drive head.
		OR	AH,AH
		JNZ	SHORT CD_EXIT		; error if we can't send a command

		PUSH	DX
		CALL	WAIT_FOR_RESULTS	; wait for interrupt, read status
CD_TL_READ_ID:					; testing label
		MOV	G_RAM:[MOTOR_OFF_WAIT],0FFH	; keep motor spinning.
		POP	DX			; bytes and check for errors.
		OR	DH,DH			; handle track zero special
		JZ	SHORT CD_CYLINDER_0
		OR	AH,AH
		JZ	SHORT CD_GOT_ID
		jmp	short CD_EXIT		;R19

;R19		INC	DH			; move to next cylinder
;R19		CMP	DH,80			; last cylinder
;R19		JB	SHORT CD_READ_ID_LOOP	; go back if not
;R19
;R19		TEST	DL,04H			; on 2nd head already?
;R19		JNZ	SHORT CD_EXIT		; exit with error code in ah
;R19		MOV	DH,4			; move to side 2
;R19		OR	DL,04H			; second head.
;R19		JMP	CD_READ_ID_LOOP

CD_CYLINDER_0:	OR	AH,AH			; check for error.
		JNZ	SHORT CD_EXIT		; if we can't read id on track 0,
						; then we probably have wrong xfer
						; rate or unformatted diskette
		MOV	DH,4			; start looking on track 4
		JMP	CD_READ_ID_LOOP

CD_GOT_ID:	CMP	G_RAM:FDC_RET_CODES[3],DH	; if equal, leave
		JE	SHORT CD_SUCCESS	; as single step.
		MOV	AL,DL			; get drive
		AND	AL,01H			; destroy head
		CALL	GET_CURRENT_MEDIA
		OR	AH,020H			; set double stepping
		CALL	SET_CURRENT_MEDIA
CD_SUCCESS:	XOR	AH,AH
CD_EXIT:	RET
CHECK_DSTEP	ENDP

		PAGE

;[]----------------------------------------------------------------------
;wait_for_results:  This routine called after a read/write/verify or
;		    format command has been issued.  IT:
;			1.  Waits for completion interrupt from controller.
;			2.  Reads status bytes to FDC_RET_CODES
;			3.  Checks FDC_RET_CODES for any errors.
;			4.  Translates NEC errors into BIOS error interface.
;		INPUT:  none.
;		OUTPUT:	FDC_RET_CODES
;			AH=error code if some sort of failure
;			AH=0 if no error.
;[]----------------------------------------------------------------------

		ALIGN	4			; N3.03
WAIT_FOR_RESULTS	PROC	NEAR

		CALL	WAITNECINT
		PUSH	AX			; save RESULTS
		CALL	RESULTS			; always read RESULTS to clean up
		MOV	BH,AH			; save RESULTS
		POP	AX			; recover RESULTS from WAITNECINT
		OR	AH,AH			; see if timeout from RESULTS.
		JNZ	SHORT WFR_EXIT

		MOV	AH,BH			; get return code from RESULTS
		OR	AH,AH			; if non-zero, exit.
		JNZ	SHORT WFR_EXIT
		CALL	ERROR_CHECK		; check for errors, convert NEC

WFR_EXIT:	RET				; errors to BIOS errors.

WAIT_FOR_RESULTS	ENDP

;[]----------------------------------------------------------------------
;GUESS_AT_MEDIA:	if format is called with CURRENT_MEDIA
;			unestablished, this routine looks at CURRENT_MEDIA
;			and DCAPABILITY and tries to guess what the
;			CURRENT_MEDIA should be, then establishes it.
;	INPUT:  AL=drive
;	OUTPUT: CURRENT_MEDIA updated and established.
;	May destroy any general register.
;[]----------------------------------------------------------------------

		ALIGN	4			; N3.03
GUESS_AT_MEDIA	PROC	NEAR

		CALL	GET_CMOS_VALUE
IFDEF	ENABLE_288_SUPPORT
		CMP	AH,CMOS_FD_288		; check for 2.88 drive
		JNE	GAM_NOT_288
		MOV	AH,LOCAL_FD_288		; if 2.88 then
		JMP	SHORT GAM_GOT_DRIVE	; set index and get to common code
GAM_NOT_288:
ENDIF	;ENABLE_288_SUPPORT

;R05		CALL	GET_CMOS_DRIVE
		CMP	AH,4
		JBE	SHORT GAM_GOT_DRIVE
		XOR	BL,BL			; change to 0
		JMP	SHORT GAM_UPDATE
GAM_GOT_DRIVE:	MOV	BL,AH			; save drive type
		XOR	BH,BH			; make into index
		MOV	BL,DGROUP:GAM_TABLE[BX]	; get new current media
		CMP	AH,3			; make exception for type 3
		JNE	SHORT GAM_UPDATE
		CALL	GET_DCAPABILITY
		AND	AH,06H			; if determined dual capability
		CMP	AH,06H
		JNE	SHORT GAM_UPDATE
		MOV	BL,050H			; change to 300 kbs NO DOUBLE STEPPING
						; determined
GAM_UPDATE:	CALL	GET_CURRENT_MEDIA
		MOV	AH,BL
		CALL	SET_CURRENT_MEDIA
		RET

GUESS_AT_MEDIA	ENDP


		PAGE
;!!!!!!!!!!!! LEVEL 4 PROCEDURES:  these procedure protect registers !!!!!

;[]----------------------------------------------------------------------
; CALC_PTABLE_OFFSET:  calculates offset, from 0F000h, of where
; the parameter table in ah is located, and returns in SI.
; INPUT:  AH = drive type, valid values: 1 to 4.
; OUTPUT: SI = offset to start of table.
; SI is only register changed.
;
;[]----------------------------------------------------------------------
		ALIGN	4			; N3.03
CALC_PTABLE_OFFSET	PROC	NEAR
		PUSH	AX			; save ax.
		MOV	AL,SIZE FDPARMS		; length of a table
		MUL	AH			; multiply by drive type
		MOV	SI,AX			; save result of multiply
		POP	AX			; recover drive type, restore al.
		CMP	AH,2			; if type 1 or 2, subtract one table
		JA	SHORT CPO_GOT_OFFSET
		SUB	SI,SIZE FDPARMS
CPO_GOT_OFFSET:	ADD	SI,OFFSET DGROUP:FD_BIOS_PARMS
		RET
CALC_PTABLE_OFFSET	ENDP

;[]----------------------------------------------------------------------
;READ_DCL:  Reads the disk change line status
;	INPUT:  AL=drive #.
;		Motor is running, drive is already selected.
;	OUTPUT: AH=80h if drive active, 0 if inactive
;		ZFLAG = ZR if inactive
;			NZ if active
;	All other registers preserved.
;[]----------------------------------------------------------------------

		ALIGN	4			; N3.03
READ_DCL	PROC	NEAR
		PUSH	DX
		XCHG	AH,AL			; save drive # in ah
		MOV	DX,FDC_CTRL_IN		; CHECK STATUS
		IN	AL,DX
		XCHG	AH,AL			; drive # back to al
		AND	AH,80H			; SET ZERO FLAG
		POP	DX
		RET
READ_DCL	ENDP

		PAGE
;[]----------------------------------------------------------------------
;SET_UP_RETRIES:  Sets the next xfer rate to try, and also sets the
;		  last xfer rate to try.
;	INPUT: AL=drive.
;	       AH=current media
;		CURRENT_MEDIA, DCAPABILITY, XFER_INFO
;	OUTPUT: AH=new current media, after change.
;	preserves all registers
;	Note: this routine should only be called if media is not
;	known.
;[]----------------------------------------------------------------------
		align	4			;R08
SET_UP_RETRIES	PROC	NEAR
		PUSH	BX			; save bx
		AND	G_RAM:[XFER_INFO],NOT 1100B	; clear stop xfer rate
		OR	G_RAM:[XFER_INFO],1000B	; stop xfer rate is always 250 Kbs

		MOV	BH,AH			; save current media
		AND	BH,NOT 0C0H		; clear starting xfer rate
		OR	BH,040H			; assume starting will be 300 kbs

		PUSH	AX			; save reg
		CALL	GET_CMOS_VALUE		; get CMOS drive type
		CMP	AH,CMOS_FD_144		; check 1.44 drive
		JNE	SHORT SUR_START_1	; if NOT 1.44 (or bad cmos) start at 300 kbs
		AND	BH,NOT 0C0H		; if 1.44 start at 500 kbs
		JMP	SHORT SUR_START_2	; get to common code

SUR_START_1:
IFDEF	ENABLE_288_SUPPORT
		CMP	AH,CMOS_FD_288		; check 2.88 drive
		JNE	SUR_START_2		; if NOT 2.88 (or bad cmos) start at 500 kbs
		OR	BH,0C0H			; if 2.88 start at 1 mbs xfer rate
ENDIF	;ENABLE_288_SUPPORT

SUR_START_2:
		POP	AX			; restore reg


		CALL	GET_DCAPABILITY
		AND	AH,0110B		; isolate dual media capability
		CMP	AH,0100B		; if known to NOT be dual media
		JNE	SHORT SUR_EXIT
		AND	BH,NOT 0C0H		; then change to 250 KBS
		OR	BH,080H
SUR_EXIT:	MOV	AH,BH			; ah=new current media
		CALL	SET_CURRENT_MEDIA
		POP	BX			; recover bx
		RET
SET_UP_RETRIES	ENDP

		PAGE
;[]----------------------------------------------------------------------
;RESET_XFER_RATE:	Checks to see if current xfer rate is the one
;			we want, and updates it if it isn't.
;	INPUT:	AL=drive
;		CURRENT_MEDIA, XFER_INFO
;	OUTPUT:
;		All registers preserved.
;[]----------------------------------------------------------------------

		ALIGN	4			; N3.03
RESET_XFER_RATE	PROC	NEAR
		PUSH	AX
		PUSH	BX

		CALL	GET_CURRENT_MEDIA	; get xfer rate for this drive
		AND	AH,0C0H			; isolate it.
;R02		MOV	BH,G_RAM:[XFER_INFO]
;R02		AND	BH,0C0H
;R02		CMP	AH,BH
;R02		JE	SHORT RXR_EXIT

		CLI				; suspend interrupts, so
						; xfer rate and reflection
						; stay in sync.
		AND	G_RAM:[XFER_INFO],NOT 0C0H	; clear old xfer rate
		OR	G_RAM:[XFER_INFO],AH	; or in new one.
		ROL	AX,2			; shift to position for port.
		AND	AL,03H			; all other bits = 0
		MOV	BX,DX			; save dx
		MOV	DX,FDC_CTRL_IN		; point to xfer controller.
		OUT	DX,AL			; out new xfer rate.
		MOV	DX,BX			; recover dx
		STI
;R02 RXR_EXIT:
		POP	BX
		POP	AX
		RET
RESET_XFER_RATE	ENDP

		PAGE
;[]----------------------------------------------------------------------
;PREPARE_RETRY:	After a read/write or verify has been tried and failed
;		at one xfer rate, this routine advances to next xfer rate,
;		if any.
;	INPUT:  AL=drive
;		AH=error code from last operation.
;	OUTPUT: AH=0 if operation should be retried at new xfer rate.
;		AH=unchanged if we have done all our retries.
;
;[]----------------------------------------------------------------------
		ALIGN	4	 
PREPARE_RETRY	PROC	NEAR
		PUSH	BX			; save bx

		MOV	BH,AH			; save error code
		CALL	GET_CURRENT_MEDIA
		MOV	BL,AH			; bl=xfer rate
		AND	BL,0C0H
		CMP	BL,080H			; is it 250 kbs?
		JNE	SHORT PR_MORE_RETRIES	; if not, try next state.
		MOV	AH,BH			; restore error code
		JMP	SHORT PR_EXIT
PR_MORE_RETRIES:
		AND	AH,NOT 0C0H		; assume next xfer rate = 500 KBS
		CMP	BL,40H			; if current is 300, next is 500.
		JE	SHORT PR_UPDATE_MEDIA

IFDEF	ENABLE_288_SUPPORT
		CMP	BL,0C0H			; if current is 1MB next is 500.
		JE	SHORT PR_UPDATE_MEDIA
ENDIF	;ENABLE_288_SUPPORT

		OR	AH,80H			; must be 500 or invalid, make into
						; 250.
PR_UPDATE_MEDIA:
		CALL	SET_CURRENT_MEDIA
		XOR	AH,AH			; indicate we should perform a retry.
PR_EXIT:	POP	BX
		RET
PREPARE_RETRY	ENDP

		PAGE
;[]----------------------------------------------------------------------
;UPDATE_MEDIA_INFO:	checks to see if we've learned anything new about
;			current media or the capabilities of the drive.
;			If we have, it updates the appropriate variable.
;	INPUT: AL=drive
;	OUTPUT:  CURRENT_MEDIA is determined
;		 DCAPABILITY's dual capability is determined.
;
;[]----------------------------------------------------------------------

		ALIGN	4			; 303A
UPDATE_MEDIA_INFO	PROC	NEAR
		PUSH	AX
		PUSH	BX

		CALL	GET_CURRENT_MEDIA
		OR	AH,010H			; make it determined
		CALL	SET_CURRENT_MEDIA

		MOV	BH,AH			; save media info
		CALL	GET_DCAPABILITY
		TEST	AH,04H			; is dual capability already known?
		JNZ	SHORT UMI_EXIT		; exit if it is.
		OR	AH,06H			; assume this is a dual drive.
		AND	BH,0C0H			; isolate xfer rate
		CMP	BH,080H			; is it 250?
		JNE	SHORT UMI_UPDATE_CAP	; if 300 or 500, its a dual drive.
		MOV	BL,AH			; save current capability
		CALL	GET_CMOS_DRIVE		;
		XCHG	BL,AH			; recover capability
		JNZ	SHORT UMI_UPDATE_CAP	; if cmos bad, assume dual
		CMP	BL,4			; if 4, its dual
		JE	SHORT UMI_UPDATE_CAP	;
		AND	AH,NOT 02H		; otherwise assume non-dual
UMI_UPDATE_CAP:	CALL	SET_DCAPABILITY		;
UMI_EXIT:	POP	BX
		POP	AX
		RET
UPDATE_MEDIA_INFO	ENDP

		PAGE
;[]----------------------------------------------------------------------
;GET_CURRENT_MEDIA:  gets CURRENT_MEDIA variable for drive in al
;	INPUT:  AL= drive #,ES=0
;	OUTPUT: If drive 0, AH=0:490
;		else if drive 1, AH=0:491
;	All other registers preserved.
;[]----------------------------------------------------------------------

		ALIGN	4			; N3.03
GET_CURRENT_MEDIA	PROC	NEAR
		MOV	AH,G_RAM:[CURRENT_MEDIA]
		OR	AL,AL
		JZ	SHORT GCM_EXIT
		MOV	AH,G_RAM:CURRENT_MEDIA[1]
GCM_EXIT:	RET
GET_CURRENT_MEDIA	ENDP

;[]----------------------------------------------------------------------
;SET_CURRENT_MEDIA:  sets CURRENT_MEDIA variable for drive in al
;	INPUT:  AL= drive #,ES=0
;	OUTPUT: If drive 0, 0:490=AH
;		else if drive 1, 0:491=AH
;	All other registers preserved.
;[]----------------------------------------------------------------------

		ALIGN	4 
SET_CURRENT_MEDIA	PROC	NEAR
		OR	AL,AL			; is it drive 1
		JNZ	SHORT SCM_DRIVE_1	; jump if it is
		MOV	CURRENT_MEDIA,AH	; update drive 0
		JMP	SHORT SCM_EXIT		; return
SCM_DRIVE_1:	MOV	G_RAM:CURRENT_MEDIA[1],AH	; update drive 1
SCM_EXIT:	RET
SET_CURRENT_MEDIA	ENDP

		PAGE
;[]----------------------------------------------------------------------
;GET_CURCYL:  gets CURCYL variable for drive in al
;	INPUT:  AL= drive #,ES=0
;	OUTPUT: If drive 0, AH=0:494
;		else if drive 1, AH=0:495
;	All other registers preserved.
;[]----------------------------------------------------------------------
		ALIGN	4
GET_CURCYL	PROC	NEAR
		MOV	AH,G_RAM:[CURCYL]
		OR	AL,AL
		JZ	SHORT GC_EXIT
		MOV	AH,G_RAM:CURCYL[1]
GC_EXIT:
		RET
GET_CURCYL	ENDP

;[]----------------------------------------------------------------------
;SET_CURCYL:  sets CURCYL variable for drive in al
;	INPUT:  AL= drive #,ES=0
;	OUTPUT: If drive 0, 0:494=AH
;		else if drive 1, 0:495=AH
;	All other registers preserved.
;[]----------------------------------------------------------------------

		ALIGN	4
SET_CURCYL	PROC	NEAR
		OR	AL,AL			; is it drive 1
		JNZ	SHORT SC_DRIVE_1	; jump if it is
		MOV	G_RAM:[CURCYL],AH	; update drive 0
		JMP	SHORT SCC_EXIT		; return
SC_DRIVE_1:	MOV	G_RAM:CURCYL[1],AH	; update drive 1
SCC_EXIT:	RET
SET_CURCYL	ENDP

		PAGE

		ASSUME	DS:NOTHING,ES:G_RAM

;[]----------------------------------------------------------------------
;GET_DCAPABILITY:  gets DCAPABILITY variable for drive in al,
;		   rotates bits to bottom 3.
;	INPUT:  AL= drive #,ES=G_RAM
;	OUTPUT: If drive 0, AH = 0:48F, bits 0-3.
;		else if drive 1, AH=0:48f, bits 4-6, rotated to position 0-3.
;		all other bits in AH=0
;	All other registers preserved.
;[]----------------------------------------------------------------------
		ALIGN	4
GET_DCAPABILITY	PROC	NEAR
		MOV	AH,G_RAM:[DCAPABILITY]
		OR	AL,AL			; is it drive 0
		JZ	SHORT GD_AND_OFF	; jump if it is
		SHR	AH,4			; shift to low nibble.
GD_AND_OFF:	AND	AH,07H
		RET
GET_DCAPABILITY	ENDP

;[]----------------------------------------------------------------------
;SET_DCAPABILITY:  sets DCAPABILITY variable for drive in al,
;		   from the three low bits in ah.
;	INPUT:  AL= drive #,AH=3 bits to put in DCAPABILITY,ES=G_RAM
;		all other bits in AH = 0
;	OUTPUT: If drive 0, 0:48F, bits 0-3 = AH, bits 0-3.
;		else if drive 1, 0:48f, bits 4-6 = AH bits 0-3
;	All registers preserved.
;[]----------------------------------------------------------------------
		ALIGN	4
SET_DCAPABILITY	PROC	NEAR
		PUSH	AX
		AND	AH,07H			; just to be sure no other bits.
		OR	AL,AL			; is it drive 0
		JZ	SHORT SD_AND_OFF	; jump if it is
		SHL	AH,4			; mov bits 0-2 to 4-6
		AND	G_RAM:[DCAPABILITY],NOT 70H	; erase bits 4-6
		JMP	SHORT SD_OR_ON

SD_AND_OFF:	AND	G_RAM:[DCAPABILITY],NOT 07H	; erase bits 0-2
SD_OR_ON:	OR	G_RAM:[DCAPABILITY],AH
		POP	AX
		RET
SET_DCAPABILITY	ENDP

		PAGE
comment %	;R05 - start
;[]-----------------------------------------------------------------------
;GET_CMOS_DRIVE:  gets drive type from CMOS.
;	INPUT:  AL= drive #
;	OUTPUT: If CMOS good:
;			AH=drive type from CMOL register 10h
;			ZFLAG=ZR
;		If CMOS bad:
;			ZFLAG=NZ
;			AH=0FFh.
;	Interrupts enabled.
;	All other registers preserved.
;[]------------------------------------------------------------------------

		ALIGN	4
GET_CMOS_DRIVE	PROC	NEAR
		MOV	AH,AL			; save drive
		CLI				; no interrupts while
						; accessing CMOS
		MOV	AL,STATB NMI_ON		; read status register
		CALL	GET_CMOS
		TEST	AL,0C0H			; bad battery or checksum
		JNZ	SHORT GCD_ERROR_EXIT	; jump with zero flag NZ

		MOV	AL,DSKB NMI_ON
		CALL	GET_CMOS
		OR	AH,AH			; drive 0?
		JNZ	SHORT GCD_DRIVE_IN_LOW	; jump if not
		SHR	AL,4
GCD_DRIVE_IN_LOW:
		AND	AL,0FH
		CMP	AH,AH			; set zero flag to zero.
GCD_EXIT:	XCHG	AH,AL			; recover al, the drive #
		STI				; allow ints again.
		RET				; put drive type in AH
GCD_ERROR_EXIT:	MOV	AL,0FFH			; illegal drive type
		JMP	SHORT GCD_EXIT		; zero flag is already NZ
GET_CMOS_DRIVE	ENDP
%		;R05 - start
;R05 - Begin
;[]-----------------------------------------------------------------------
;GET_CMOS_DRIVE:  gets drive type from CMOS.
;	INPUT:  AL= drive #
;	OUTPUT: If CMOS good:
;			AH=drive type from CMOS register 10h
;			ZFLAG=ZR
;		If CMOS bad:
;			ZFLAG=NZ
;			AH=0FFh.
;	Interrupts enabled.
;	All other registers preserved.
;[]------------------------------------------------------------------------

		ALIGN	4

GET_CMOS_DRIVE	PROC	NEAR
IFDEF	ENABLE_288_SUPPORT
		CALL	GET_CMOS_VALUE		; ah <- value from CMOS
		PUSHF				; save flags from GET_CMOS_VALUE
		JNZ	GCD_DONE		; skip if CMOS bad
		CMP	AH,CMOS_FD_288		; check 2.88 drive value
		JNE	GCD_DONE		; skip if not CMOS 2.88 value
		MOV	AH,CMOS_FD_144		; else force 1.44 value for compatibility
GCD_DONE:
		POPF				; restore flags from GET_CMOS_VALUE
		RET				; return ah and flags set
ENDIF	;ENABLE_288_SUPPORT			; if not 2.88 fall through to GET_CMOS_VALUE
GET_CMOS_DRIVE	ENDP



		public	GET_CMOS_VALUE				;R22
		ALIGN	4
GET_CMOS_VALUE	PROC	NEAR
;R04A start
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,SWAP_DRIVE
		jz	short @F
		xor	al,1
@@:
;R04A end
IFDEF	FORCE_1MB_CMOS
		CMP	AL,0			; check drive 0
		JNE	GCD_CMOS		; skip if not
		MOV	AH,CMOS_FD_288		; else force 2.88 drive type
		CMP	AH,AH			; set zero flag for good CMOS
		RET		

GCD_CMOS:		
ENDIF	;FORCE_1MB_CMOS
		MOV	AH,AL			; save drive
		CLI				; no interrupts while
						; accessing CMOS
		MOV	AL,STATB NMI_ON		; read status register
		CALL	GET_CMOS
		TEST	AL,0C0H			; bad battery or checksum
		JNZ	SHORT GCD_ERROR_EXIT	; jump with zero flag NZ

		MOV	AL,DSKB NMI_ON
		CALL	GET_CMOS
		OR	AH,AH			; drive 0?
		JNZ	SHORT GCD_DRIVE_IN_LOW	; jump if not
		SHR	AL,4
GCD_DRIVE_IN_LOW:
		AND	AL,0FH
		CMP	AH,AH			; set zero flag to zero.
GCD_EXIT:	XCHG	AH,AL			; recover al, the drive #
		STI				; allow ints again.
		RET				; put drive type in AH
GCD_ERROR_EXIT:	MOV	AL,0FFH			; illegal drive type
		JMP	SHORT GCD_EXIT		; zero flag is already NZ
GET_CMOS_VALUE	ENDP


		PAGE

;[]==================================================================[]
;
;  GET_TPI:  
;
;	Determines how many tracks a drive supports.
;
; Input: AL=drive
;Output: NONE
;
;Author: Award
;Date:   10/02/90
;
; Name | Date	    | Description
; ---------------------------------------------------------------
; TIM  | 02-Oct-90  | Update for 4.0
;
;[]==================================================================[]

		PUBLIC	GET_TPI						
		align	4			;R08
GET_TPI		PROC	NEAR
;R20 start
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,SWAP_DRIVE
		jz	short @F
		xor	al,1
@@:
;R20 end

		PUSH	AX			; save drive.
		CALL	TURNONMOTOR		; make sure motor on.
		POP	AX			; recover drive.

		CALL	GET_CURRENT_MEDIA
		AND	AH,NOT 020H		; turn off double stepping
		CALL	SET_CURRENT_MEDIA

		MOV	CL,AL
		MOV	BL,0FEH
		ROL	BL,CL
		AND	G_RAM:[NEED_RECAL],BL	; force recalibrate

		; seek to track large enough to engage braking
		; mechanism on a 40 track drive, but not to much larger.
		; This will cause head position to be out of sync
		; with NEC head position register if 40 track,
		; but not 80 track.  Determine if head and register
		; are out of sync by seeking back to track 0 and checking
		; for drive 0 signal.  If it doesn't come true when we
		; expect it, it must be out of sync, and therefore 40 track.

		MOV	AH,50			; seek to track > 40
		PUSH	AX			; save drive.
		CALL	NEC_SEEK
		OR	AH,AH
		POP	DX			; recover drive in DL
		JNZ	SHORT GT_NO_DRIVE	; unable to seek out.

		; seek to track 10  This will cause controller to issue
		; 40 seek pulses, which on a 40 track drive with braking
		; mechanism set exactly for 40 tracks will put us on
		; track 0.  Drive manufacturers may set braking mechanism
		; anywhere between track 41-49, so we do 10 extra seeks in
		; case. 80 track drives must give drive 0 signal on
		; exactly the 10th seek inward.

		MOV	DH,10			; seek to track 10
GT_RESEEK:	MOV	AX,DX			; track, drive to ax.
		PUSH	DX			; save drive, track.
		CALL	NEC_SEEK		;
		POP	DX
		OR	AH,AH
		JNZ	SHORT GT_NO_DRIVE

		PUSH	DX			; drive on stack.
		MOV	SI,SP			; pointer to drive parameter.
		MOV	AH,NC_SDS		; sense drive status command
		MOV	BL,NC_LEN_SDS		; two byte command
		CALL	SEND_COMMAND
		POP	DX
		OR	AH,AH
		JNZ	SHORT GT_NO_DRIVE

		PUSH	DX			; no interrupt from status
		CALL	RESULTS			; read ST3
		POP	DX
		OR	AH,AH
		JNZ	SHORT GT_NO_DRIVE

		TEST	G_RAM:[FDC_RET_CODES],010H	; track 0?
		JNZ	SHORT GT_GOT_CYL_0
		DEC	DH
		CMP	DH,0FFH
		JNE	SHORT GT_RESEEK
		JMP	SHORT GT_NO_DRIVE	; no track 0, unknown.

GT_GOT_CYL_0:	MOV	BL,1			; assume 80 track.
		MOV	BH,02H			; unestablished 500 kbs.
		OR	DH,DH			; did we find 0 on track 0
		JZ	SHORT GT_UPDATE
		MOV	BL,04H			; this is a 40 track drive, and
		MOV	BH,93H			; 40 track drives don't have
						; dual capability.
		MOV	DH,0FEH			; force recalibrate, in case 40 track
		MOV	CL,DL
		ROL	DH,CL
		AND	G_RAM:[NEED_RECAL],BL	; force recalibrate
GT_UPDATE:	MOV	AL,DL
		CALL	GET_CURRENT_MEDIA
		MOV	AH,BH
		CALL	SET_CURRENT_MEDIA
		CALL	GET_DCAPABILITY
		MOV	AH,BL
		CALL	SET_DCAPABILITY
		RET

GT_NO_DRIVE:					; dl=drive when jumped to
		; unable to determine drive by testing

		XOR	BX,BX			; assume no drive.
		MOV	AL,DL
		JMP	GT_UPDATE
		CALL	GET_CMOS_DRIVE		; find out what CMOS says.
		OR	AH,AH			; if cmos = 0
		JZ	SHORT GT_UPDATE
		CMP	AH,4			; or cmos invalid
		JA	SHORT GT_UPDATE		; set to no drive.
		MOV	BX,9304H		; assume type 1
		CMP	AH,1
		JE	SHORT GT_UPDATE		; jump if type 1.
		MOV	BX,0201H		; else assume 80 track drive
		JMP	GT_UPDATE

GET_TPI		ENDP

;[]-----------------------------------------------------------------------
;GET_PARM - ROUTINE RETRIEVES THE DISK PARAMETER BLOCK PTR AT INT 1EH
;	  - AND USES IT TO RETURN PARAMETER WHOSE INDEX IS PASSED
;	INPUT:
;		AL = INDEX
;	OUTPUT:
;		AL=VALUE
;		AH=0, in case we want to use value as a word operand
;[]-----------------------------------------------------------------------

		ALIGN	4	     
		PUBLIC	GET_PARM						
GET_PARM	PROC	NEAR
		PUSH	DS
		PUSH	SI
		PUSH	BX
		MOV	BX,SEG_0
		MOV	DS,BX
		ASSUME	DS:SEG_0
		LDS	SI,SEG_0:[DISK_PARM_PTR]
		XOR	AH,AH
		ADD	SI,AX
		LODSB
		POP	BX
		POP	SI
		POP	DS
		ASSUME	DS:NOTHING
		RET

GET_PARM	ENDP


;R22IFDEF	ENABLE_288_SUPPORT
;R22
;R22 		PUBLIC	FD_PERP_MODE
;R22		align	4			;R08
;R22FD_PERP_MODE	PROC	NEAR
;R22
;R22		PUSHA				; save regs
;R22		PUSH	DS
;R22		PUSH	ES
;R22
;R22		PUSH	CS			; set addressibility
;R22		POP	DS
;R22		ASSUME	DS:DGROUP
;R22		MOV	AX,G_RAM
;R22		MOV	ES,AX
;R22		ASSUME	ES:G_RAM
;R22
;R22
;R22IFDEF	FORCE_PERP_MODE
;R22		MOV	AL,10000100B		; set perp mode for first drive
;R22		JMP	SHORT FPM_FINISH	; get to common finish mode
;R22
;R22ELSE	;not FORCE_PERP_MODE
;R22		MOV	BL,080H			; set for all drives non perp mode
;R22
;R22		MOV	AL,0			; check drive zero for 2.88
;R22		CALL	GET_CMOS_VALUE		; get CMOS drive type
;R22		CMP	AH,CMOS_FD_288		; check 2.88 drive
;R22		JNE	short PERP_1		; skip if NOT 2.88 (or bad cmos)
;R22		OR	BL,004H			; else drive 0 is setup as perp
;R22
;R22PERP_1:
;R22		MOV	AL,1			; check drive one for 2.88
;R22		CALL	GET_CMOS_VALUE		; get CMOS drive type
;R22		CMP	AH,CMOS_FD_288		; check 2.88 drive
;R22		JNE	short PERP_2	 	; skip if NOT 2.88 (or bad cmos)
;R22		OR	BL,008H			; else drive 1 is setup as perp
;R22
;R22PERP_2:
;R22		XCHG	AX,BX			; parm into al
;R22ENDIF	;FORCE_PERP_MODE
;R22
;R22FPM_FINISH:
;R22		PUSH	AX			; parameters on stack
;R22		MOV	SI,SP			; pointer to parameters
;R22		MOV	AH,012H			; perpendicular command
;R22		MOV	BL,2			; 2 bytes in perp command
;R22		CALL	SEND_COMMAND		; output perp command
;R22		ADD	SP,2			; clear parameters off stack.
;R22		OR	AH,AH			; error?
;R22		JNZ	SHORT FPM_BAD		; skip if error
;R22
;R22		CLC				; signal no error
;R22		JMP	SHORT FPM_RET		; get to common return
;R22
;R22FPM_BAD:
;R22		STC				; signal error
;R22
;R22FPM_RET:
;R22		POP	ES
;R22		POP	DS
;R22		POPA
;R22		RET
;R22
;R22FD_PERP_MODE	ENDP
;R22
;R22ENDIF	;ENABLE_288_SUPPORT
;R22
;R22
;R22
;R22IFDEF	ENABLE_FDC_FIFO
;R22
;R22		PUBLIC	FD_FIFO_MODE
;R22		align	4			;R08
;R22FD_FIFO_MODE	PROC	NEAR
;R22
;R22		PUSHA				; save regs
;R22		PUSH	DS
;R22		PUSH	ES
;R22
;R22		PUSH	CS			; set addressibility
;R22		POP	DS
;R22		ASSUME	DS:DGROUP
;R22		MOV	AX,G_RAM
;R22		MOV	ES,AX
;R22		ASSUME	ES:G_RAM
;R22
;R22		MOV	AL,00h			; set PRETRK for configure command
;R22		PUSH	AX			; parameters on stack
;R22		MOV	AL,00h			; set first parm byte for configure command
;R22		MOV	AH,00h+FDC_FIFO_THRES	; set FIFO on and set threshold
;R22		PUSH	AX			; parameters on stack
;R22		MOV	SI,SP			; pointer to parameters
;R22		MOV	AH,013H			; configure command
;R22		MOV	BL,4			; 4 bytes in configure command
;R22		CALL	SEND_COMMAND		; output configure command
;R22		ADD	SP,4			; clear parameters off stack.
;R22		OR	AH,AH			; error?
;R22		JNZ	SHORT FFM_BAD		; skip if error to quit
;R22
;R22		MOV	AH,094H			;;;XXXX lock command
;R22		MOV	BL,1			; 1 byte in lock command
;R22		CALL	SEND_COMMAND		; output lock command
;R22		OR	AH,AH			; error?
;R22		JNZ	SHORT FFM_BAD		; skip if error to quit
;R22		CALL	RESULTS			; read results
;R22		OR	AH,AH			; see if error
;R22		JNZ	SHORT FFM_BAD		; skip if error to quit
;R22
;R22		CLC				; signal no error
;R22		JMP	SHORT FFM_RET		; get to common return
;R22
;R22FFM_BAD:
;R22		STC				; signal error
;R22
;R22FFM_RET:
;R22		POP	ES
;R22		POP	DS
;R22		POPA
;R22		RET
;R22
;R22FD_FIFO_MODE	ENDP
;R22
;R22ENDIF	;ENABLE_FDC_FIFO


;R17 ;R100 Starts
;R17 ;[]============================================[]
;R17 ;Function :	Flush IBM CPU cache if IBM CPU
;R17 ;Input    :	ES = G_RAM
;R17 ;		DI = return address
;R17 ;Destroy  :	AL
;R17 ;[]============================================[]
;R17 Invalidate_IBM_Cache:
;R17 
;R17 		mov	al,es:[CPU_TYPE_FLAG]
;R17 ;R01		and	al,not CPUCACHE+DOUBLECLOCK+COPROCESSOR
;R17 		and	al,not CPUCACHE+COPROCESSOR		;R01
;R17 
;R17 		cmp	al,TYPE_IBM386SLC
;R17 		je	short Flush_CPU_Cache
;R17 		cmp	al,TYPE_IBM486SLC2
;R17 ;R01		jne	short Not_IBM_CPU
;R17 		je	short Flush_CPU_Cache			;R01
;R17 		cmp	al,TYPE_IBM486DLC3
;R17 		jne	short Not_IBM_CPU			;R01
;R17 
;R17 	Flush_CPU_Cache:
;R17 
;R17 		INVD
;R17 		WBINVD
;R17 
;R17 	Not_IBM_CPU:
;R17 
;R17 		jmp	di
;R17 		ret
;R17 ;R100 Ends

FCODE		ENDS
		END
