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