www.pudn.com > hpbios.rar > ADISK.ASM


;	[]===========================================================[] 
; 
;	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 
;---------------------------------------------------------------------------- 
;R39	01/27/00 KVN	Remove all code of 'align' to reduce code size 
;R38	01/14/00 STV	Added Support USB-ZIP. 
;R37	01/10/00 KVN	Fixed system hang for access Floppy in windows 98 when 
;			enabled "Support_FDD_Write_Protect" function and set 
;			'Read Only' and not inserted diskette into device. 
;R36	01/03/00 STV	Added Support USB Floppy when Define "USB_STORAGE_SUPPORT". 
;R35	12/29/99 STV	Added a new device structure for Int13h access control 
;			easily. 
;R34	10/20/99 RCH	Remove Floppy INT 13H code for legacy free platform 
;R33	09/23/99 KVN	Added a Floppy read/write encrypt funtion 
;R32	07/01/99 KVN	Fixed floppy seek/verify random test fail in Qaplus v5.51 
;R31	05/06/99 STV	1.Fixed if seek no floppy but STDFEATURE have floppy. 
;			  Must DCDPABILITY(multirate) SET. 
;			2."Check floopy seek or not" Must for Set drive capacity 
;			  after. 
;990222		 RAY	Use ML 6.x to compile 
;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 
; 
;			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) 
; 
 
ifdef	MASM611				;990222 
.MODEL  SMALL, BASIC			;990222 
OPTION	PROC: PRIVATE			;990222 
endif	;MASM611			;990222 
 
		PAGE	63,132 
		TITLE	ADISK  -- FLOPPY DISK AT ROM BIOS 
.386 
 
;Always support 2.88Mb floppy drive 
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 
 
 
.XLIST 
		INCLUDE BIOS.CFG 
 
		INCLUDE COMMON.MAC 
		INCLUDE CMOS.EQU 
		INCLUDE 82077.EQU 
		INCLUDE	ADISK.EQU 
		INCLUDE COMMON.EQU				 
;R35 start 
		INCLUDE ATORGS.EQU 
 
		extrn	Drive_Letter_Table:near 
if ATAPI_COMMAND_SUPPORT	eq	1 
		extrn	Call_ATAPI:near 
endif ;ATAPI_COMMAND_SUPPORT	eq	1 
;R35 end 
;R36 start 
ifdef	USB_STORAGE_SUPPORT 
		extrn	Call_USB_STORAGE_SERVICE:near 
endif	;USB_STORAGE_SUPPORT 
;R36 end 
		EXTRN	GET_CMOS:NEAR 
		EXTRN	WAIT_FOR_PORT:NEAR 
		EXTRN	WAIT_FOR_MEM:NEAR       
		EXTRN	WAIT_REFRESH:NEAR       
		EXTRN	WAIT_FDU_8THS:WORD	 
		EXTRN	WAIT_FDU_HEAD_SETTLE:BYTE  
		EXTRN	WAIT_FDU_INT_LO:WORD    
		EXTRN	WAIT_FDU_INT_HI:BYTE    
		EXTRN	WAIT_FDU_RESULTS_LO:WORD  
		EXTRN	WAIT_FDU_RESULTS_HI:BYTE  
		EXTRN	WAIT_FDU_SEND_LO:WORD  
		EXTRN	WAIT_FDU_SEND_HI:BYTE  
 
.LIST 
 
DGROUP		GROUP	FCODE 
FCODE		SEGMENT	USE16 PARA PUBLIC 'CODE' 
		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. 
; 
;[]------------------------------------------------------------------------- 
 
;R35 start 
;[]=======================================[] 
;Function Name : Dispatch_drive_work 
;Input : DL = drive NO 
;      : AL = Device type for verify 
;[]=======================================[] 
FDD_Work_Table: 
if ATAPI_COMMAND_SUPPORT	eq	1 
		db	IDE_CDROM 
		dw	offset Call_ATAPI 
		db	IDE_LS120 
		dw	offset Call_ATAPI 
		db	IDE_ZIP_FDD 
		dw	offset Call_ATAPI 
endif ;ATAPI_COMMAND_SUPPORT 
;R36 start 
ifdef USB_STORAGE_SUPPORT 
		db	USB_FDD 
		dw	offset Call_USB_STORAGE_SERVICE 
		db	USB_ZIP_FDD				;R38 
		dw	offset Call_USB_STORAGE_SERVICE		;R38 
endif	;USB_STORAGE_SUPPORT 
;R36 end 
		db	None_Device	;end sign 
 
Dispatch_FDD_work	Proc	near 
		push	si 
		lea	si,FDD_Work_Table 
		extrn	Dispatch_work_Start:near 
		jmp	Dispatch_work_Start 
Dispatch_FDD_work	endp 
;R35 end 
 
		PAGE 
		PUBLIC	DISKIO			; * ATORGS.ASM 
;R39		ALIGN	4		        
DISKIO		PROC	NEAR 
 
		call	Dispatch_FDD_work	;R35 
 
		STI 
 
;R35 ;;;ifdef ATAPI_COMMAND_SUPPORT	 
;R35 if	ATAPI_COMMAND_SUPPORT	eq	1	;;; 
;R35 ;----------- check if CD-ROM,ATAPI devices emulation as A: ---- 
;R35 	push	ds 
;R35 	push	ax 
;R35 	mov	ax,G_RAM 
;R35 	mov	ds,ax	      	;DS = BIOS data area(40:0) 
;R35 	assume	ds:G_RAM 
;R35 	and	byte ptr G_RAM:[CDROM_ALLOCATE],not Change_Floppy_Number	 
;R35  
;R35 	mov	al,byte ptr G_RAM:[ATAPI_Byte] 
;R35 	test	al,4	      	;ZIP or LS120 drive installed ? 
;R35 	jz	short @F      	;no , check CD-ROM 
;R35 	mov	ah,al	      	;AH = ATAPI_Byte 
;R35 	shr	al,3	      	;check ATAPI emulation drive No. 
;R35 	and	al,1	      	;only drive No. A: or B: 
;R35 	cmp	al,dl	      	;drive No. match ? 
;R35 	je	short Go_Std_Atapi;yes, access ATAPI drive 
;R35 				;no, it may be FDD or CD-ROM access 
;R35  
;R35 	test	ah,8		;ATAPI emulate drive B ? 
;R35 	jnz	short @F	;yes,check CD-ROM   	 
;R35 	test	ah,30h	  	;check ATAPI drive 1 exist? 
;R35 	jnz	short Go_Std_Atapi; Yes,go ATAPI subroutine 
;R35  
;R35 	or	dl,dl	       	;access drive A? 
;R35 	jnz	short Go_Std_Floppy;No,skip 
;R35 @@: 
;R35  
;R35 	;Check if CD-ROM existed and emulated as drive A 
;R35 	mov	al,byte ptr G_RAM:[CDROM_ALLOCATE] 
;R35 	and	al,0fh 
;R35 	shr	al,2   		;bit3-2 = CD-ROM emulation No. 
;R35 	cmp	al,CD_DRVA 	;emulating drive A? 
;R35 	jne	short no_cd   	;not emulate drive A 
;R35 				;yes, emulate drive A 
;R35 	cmp	dl,0		;access drive A? 
;R35 	jne	short @F	;not for CD-ROM access 
;R35 ; calling drive 0 (the CD) 
;R35 	extrn	std_atapi:near 
;R35 Go_Std_Atapi:					 
;R35 	pop	ax				 
;R35 	pop	ds				 
;R35 	call	std_atapi	;ATAPI service routine 
;R35 	retf	2				; discard flags 
;R35  
;R35 ; calling drive 1 (the real floppy) 
;R35 @@: 
;R35 Go_Std_Floppy:				 
;R35 	;mark flag for FDD access due to ATAPI device existed 
;R35 	or	byte ptr G_RAM:[CDROM_ALLOCATE],Change_Floppy_Number 
;R35  
;R35 no_cd: 
;R35  
;R35 	pop	ax				 
;R35 	pop	ds				 
;R35 endif ;ATAPI_COMMAND_SUPPORT			 
 
ifndef	LEGACY_FREE_SUPPORT						;R34 
		PUSH	SI 
		PUSH	DS 
		PUSH	ES 
		PUSH	DI 
		CLD 
 
	;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 
 
;R35 start 
		or	dl,dl 
		jz	short @F 
		cmp	byte ptr cs:[Drive_Letter_Table],STD_FDD 
		je	short @F 
		or	byte ptr G_RAM:[CDROM_ALLOCATE],Change_Floppy_Number 
@@: 
;R35 end 
 
		cmp	dl,80h			;for hard drive access ? 
		jae	short Not_Floppy	;yes, don't swap FDD	 
 
	;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. 
@@: 
 
	;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:			 
		pop	ax			;restore function No. 
 
		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 
 
		PUSH	BP 
		MOV	BP,SP 
		OR	BP_FLAGS,200H		; SET INT FLAG ON BECAUSE OF 
						; DOS CODE AT 70:XXXX 
 
		mov	ah,ST_OK 
		test	byte ptr G_RAM:[HARDWARE],1	;have any floppy drive? 
		jz	D_LEGAL_FUNCTION		;No any drive then skip 
 
		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 
		JA	D_ILLEGAL_FUNCTION	; jump, its illegal	 
		SUB	BX,15H			; reduce function for table 
		JB	D_ILLEGAL_FUNCTION	 
		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 
 
;R39		ALIGN	4 
;990222 D_NORMAL_RET:					; space.  All procedures "ret" 
D_NORMAL_RET	Label	Near			; 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: 
 
		cmp	BP_OPCODE,0		 
		je	D_LEGAL_FUNCTION	 
		test	BP_DRIVE,80h 
		jz	D_LEGAL_FUNCTION 
		mov	ah,ST_BADCMD 
D_LEGAL_FUNCTION: 
 
		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. 
 
		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 
 
		push	ax 
		mov	ax,G_RAM 
		mov	ds,ax			;DS = BIOS data area(40:0) 
	;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. 
@@: 
 
;;;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 
 
		ASSUME	ds:NOTHING 
		pop	ax 
 
		POP	DI 
		POP	ES 
		POP	DS 
		POP	SI 
endif;	LEGACY_FREE_SUPPORT						;R34 
 
		IRET 
		ASSUME	DS:DGROUP		; BUG FROM XSPEED 
 
ifndef	LEGACY_FREE_SUPPORT						;R34 
D_ILLEGAL_FUNCTION: 
		MOV	AH,1			; error code 
 
		JMP	D_COPY_STATUS		 
endif;	LEGACY_FREE_SUPPORT						;R34 
 
DISKIO		ENDP 
 
ifndef	LEGACY_FREE_SUPPORT						;R34 
		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. 
;[]--------------------------------------------------------------------------- 
 
;R39		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 
 
;R37ifdef	Support_FDD_Write_Protect 
;R37		test	G_RAM:[POST_FLAG],Fdd_Wt_Protect_Status 
;R37		jz	short @F 
;R37		mov	AH,ST_WRITEPROT		; was in place. 
;R37		JMP	DIX_FAILURE 
;R37@@: 
;R37endif	;Support_FDD_Write_Protect 
 
		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: 
;R37 - starts 
ifdef	Support_FDD_Write_Protect 
		cmp	BP_OPCODE,3		; is it actually a write? 
		jne	short Skip_Report_Write_Protect 
		test	G_RAM:[POST_FLAG],Fdd_Wt_Protect_Status 
		jz	short Skip_Report_Write_Protect 
		mov	AH,ST_WRITEPROT		; was in place. 
		JMP	DIX_FAILURE 
Skip_Report_Write_Protect: 
endif	;Support_FDD_Write_Protect 
;R37 - ends 
 
;----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 
;R32		MOV	BL,0AFH			; use 0AFh as 1st specify 
		mov	bl,0bfh			;R32 use 0BFh 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		; assume a read or verify command 
		CMP	BP_OPCODE,03H		; is it a write 
		JNE	SHORT DIX_SEND_COMMAND	; 
;R33 start 
ifdef	FDD_RW_Encrypt_CMOS 
		test	byte ptr G_RAM:[Runtime_Flag],FDD_RW_Encrypt 
		jz	short FDD_RW_Encrypt_Disabled 
		call	FDD_RW_Encrypt_Algorithm 
FDD_RW_Encrypt_Disabled	label	near 
endif	;FDD_RW_Encrypt_CMOS 
;R33 end 
		MOV	AH,NC_WRITE		; change to a write command 
 
DIX_SEND_COMMAND: 
		MOV	BL,NC_LEN_RW 
		MOV	SI,SP			; pointer to rest of parameters. 
Send_Verify_para:			  		 
		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			; if write protect then dont retry 
		JE	SHORT DIX_FAILURE	 
		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. 
;R33 start 
ifdef	FDD_RW_Encrypt_CMOS 
		test	byte ptr G_RAM:[Runtime_Flag],FDD_RW_Encrypt 
		jz	short FDD_RW_Encrypt_Disabled1 
		call	FDD_RW_Encrypt_Algorithm 
FDD_RW_Encrypt_Disabled1	label	near 
endif	;FDD_RW_Encrypt_CMOS 
;R33 end 
		XOR	AH,AH			; successful operation. 
;990222 DIX_EXIT: 
DIX_EXIT	Label	Near			;990222 
		JMP	D_NORMAL_RET 
 
DISK_IO_XFER	ENDP 
 
;R33 start 
FDD_RW_Encrypt_Algorithm	PROC	NEAR 
		cmp	BP_OPCODE,02H		;is it a read? 
		je	short Do_it		;Yes,do it 
		cmp	BP_OPCODE,03H		;is it a write? 
		je	short Do_it		;Yes,do it 
		ret 
Do_it: 
		push	es 
		push	di 
		push	cx 
 
		movzx	cx,BP_AL		; get # of sectors. 
		shl	cx,7			; CX = 512/4 dword count 
		mov	di,BP_ES		; store segment 
		mov	es,di 
		mov	di,BP_OFFSET		; store offset. 
 
XOR_OP_Loop: 
		xor	word ptr es:[di],0aa55h 
		add	di,4 
		loop	XOR_OP_Loop 
 
		pop	cx 
		pop	di 
		pop	es 
		ret 
FDD_RW_Encrypt_Algorithm	ENDP 
;R33 end 
 
IFDEF	ENABLE_288_SUPPORT 
;R39		align	4			 
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 
;[]----------------------------------------------------------------------- 
;[]----------------------------------------------------------------------- 
 
;R39		align	4			 
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: 
ifdef	FDD_3_Mode 
		pusha					 
		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					 
endif;	FDD_3_Mode 
 
		; 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 
;[]--------------------------------------------------------------------------- 
;[]--------------------------------------------------------------------------- 
 
;R39		align	4			 
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 
 
		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 
;[]------------------------------------------------------------------------ 
 
;R39		align	4			 
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 
;R39		align	4			 
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 
;[]------------------------------------------------------------------------ 
 
;R39		align	4			 
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 
;[]------------------------------------------------------------------------ 
 
;R39		align	4			 
DISK_IO_SET_MEDIA	PROC	NEAR 
 
		MOV	AL,BP_DRIVE 
 
		CALL	GET_DCAPABILITY 
		OR	AH,AH			; if no drive 
		JZ	DISM_OK_EXIT		 
		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 
		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 
@@: 
 
		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. 
;[]------------------------------------------------------------------------ 
 
;R39		align	4			 
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. 
;[]------------------------------------------------------------------------ 
 
;R39		align	4			 
READ_AND_CLEAR_DCL	PROC	NEAR 
		CALL	READ_DCL		; read the change line 
						; ah=code, al preserved. 
		MOV	AH,0			; assume ok, SAVE FLAGS 
 
		jnz	SHORT DCL_enable 
ifdef	FDD_3_Mode 
		mov	cl,FloppyA_3_mode		 
		mov	al,FDD_A_3_mode_detect 
		cmp	byte ptr BP_DRIVE,0 
		je	short @F 
		mov	cl,FloppyB_3_mode		 
		mov	al,FDD_B_3_mode_detect 
@@: 
		test	G_RAM:FDD_VERIFY_CMD_FLAG,cl	 
		jz	RACD_EXIT			 
		test	G_RAM:FDD_VERIFY_CMD_FLAG,al 
 
		jnz	RACD_EXIT	 
		MOV	AH,ST_MEDIACHANGE 
		cmp	BP_OPCODE,17h 
		je	RACD_EXIT 
		cmp	BP_OPCODE,18h 
		je	RACD_EXIT		 
		or	G_RAM:FDD_VERIFY_CMD_FLAG,al 
endif	;FDD_3_Mode 
		jmp	short Detect_3_mode_media 
DCL_enable: 
 
		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. 
		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 
 
		mov	al,FDD_A_3_mode_detect 
		mov	cl,FloppyB_3_mode	 
		cmp	byte ptr BP_DRIVE,1 
		je	short @F 
		mov	al,FDD_B_3_mode_detect 
		mov	cl,FloppyA_3_mode	 
@@: 
		not	al 
		and	G_RAM:FDD_VERIFY_CMD_FLAG,al 
 
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,cl 
		jnz	short @F 
		xor	al,al			;Disable 3 mode 
		call	Set_FDD_3_mode 
		jmp	short SF3MD_exit 
@@: 
		mov	al,BP_DRIVE 
		CALL	GET_CMOS_DRIVE 
		cmp	ah,2 
		je	short Force_3mode 
 
		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:					 
		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: 
 
		MOV	AH,ST_TIMEOUT		; can't clear it. 
RACD_EXIT:	RET 
 
READ_AND_CLEAR_DCL	ENDP 
 
ifdef	FDD_3_Mode 
		include	fdd3mode.sio 
endif	;FDD_3_Mode 
 
 
		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		align	4			 
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 
 
		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. 
 
;R39		align	4			 
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 
;R39		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 
;R39		ALIGN	4			; N3.03 
 
@wdw1: 
		NEWIODELAY		 
		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). 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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				 
		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					 
;R39		ALIGN	4			; 303A 
RESULTS		PROC	NEAR 
 
		MOV	DI,OFFSET G_RAM:FDC_RET_CODES 
 
;R39		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 
		IN	AL,DX			; es:[di] = status port. 
		NEWIODELAY			 
		STOSB				; inc di 
 
		XOR	BX,BX 
 
		push	cx 
		mov	cx, 30			 
@wdw2: 
		NEWIODELAY		 
		loop	short @wdw2 
		pop	cx 
 
		MOV	DX,NEC_STAT_PORT	; get back dx 
		IN	AL,DX			; read the status port. 
		NEWIODELAY			 
		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. 
; 
;[]---------------------------------------------------------------------- 
 
;R39		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				 
		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 
 
		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. 
; 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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 
;[]---------------------------------------------------------------------- 
 
;R39		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				 
;R39		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 
 
						; 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. 
;[]---------------------------------------------------------------------- 
;R39		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 
;R39		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		 
 
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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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 
 
		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. 
; 
;[]---------------------------------------------------------------------- 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
;R39		align	4			 
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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
 
		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 
 
		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. 
; 
;[]---------------------------------------------------------------------- 
;R39		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. 
; 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
;R39		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. 
;[]---------------------------------------------------------------------- 
 
;R39		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. 
;[]---------------------------------------------------------------------- 
;R39		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. 
;[]---------------------------------------------------------------------- 
;R39		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 
 
;[]----------------------------------------------------------------------- 
;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. 
;[]------------------------------------------------------------------------ 
 
;R39		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	 
;R39		ALIGN	4 
GET_CMOS_VALUE	PROC	NEAR 
 
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,SWAP_DRIVE 
		jz	short @F 
		xor	al,1 
@@: 
 
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						 
;R39		align	4	 
GET_TPI		PROC	NEAR 
 
	;Floppy drive A & B are swapped ? 
		test	byte ptr G_RAM:FDD_VERIFY_CMD_FLAG,SWAP_DRIVE 
		jz	short @F 
		xor	al,1 
@@: 
 
		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 
 
;R31		XOR	BX,BX			; assume no drive. 
		MOV	AL,DL 
		CALL	GET_DCAPABILITY			;R31 no drive don't 
							;R31 clear dcapability 
		movzx	bx,ah				;R31 restore default 
		JMP	GT_UPDATE 
;R31		CALL	GET_CMOS_DRIVE		; find out what CMOS says. 
;R31		OR	AH,AH			; if cmos = 0 
;R31		JZ	SHORT GT_UPDATE 
;R31		CMP	AH,4			; or cmos invalid 
;R31		JA	SHORT GT_UPDATE		; set to no drive. 
;R31		MOV	BX,9304H		; assume type 1 
;R31		CMP	AH,1 
;R31		JE	SHORT GT_UPDATE		; jump if type 1. 
;R31		MOV	BX,0201H		; else assume 80 track drive 
;R31		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 
;[]----------------------------------------------------------------------- 
 
;R39		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 
endif;	LEGACY_FREE_SUPPORT						;R34 
 
FCODE		ENDS 
		END