home *** CD-ROM | disk | FTP | other *** search
- ; UDMA.ASM Written 28-Jan-2004 by Jack R. Ellis
- ;
- ; UDMA is free software. You can redistribute and/or modify it, under
- ; the terms of the GNU General Public License (hereafter called GPL) as
- ; published by the Free Software Foundation, either version 2 of GPL or
- ; any later versions, at your option. UDMA is distributed in the hope
- ; that it will be useful, but WITHOUT ANY WARRANTY and without even the
- ; implied warranties of MERCHANTABILITY nor of FITNESS FOR A PARTICULAR
- ; PURPOSE! See the GPL for details. You ought to have received a copy
- ; of the GPL with these UDMA files. If not, write to the Free Software
- ; Foundation Inc., 59 Temple Place Ste. 330, Boston, MA 02111-1307 USA.
- ; http://www.gnu.org/licenses
- ;
- ; Special thanks to Luchezar I. Georgiev for his INESTIMABLE advice and
- ; help in research, revisions, enhancement and testing of this driver!!
- ;
- ; This is a DOS driver designed to handle 1 to 4 UltraDMA hard-disks on
- ; PC motherboards having a VIA 8235 or equivalent chipset. The driver
- ; determines which of the IDE units are actually UltraDMA hard-disks at
- ; initialization and will run all such disks. All UltraDMA disks from
- ; mode 0 ATA-16 thru mode 7 ATA-166 may be used. An UltraDMA disk is
- ; assumed to handle full LBA mode (63 sectors, 255 heads and a designed
- ; cylinder count). "LBA mode" I-O requests are supported for FreeDOS,
- ; MS-DOS V7.xx, and other systems that allow them. LBA values over 28
- ; bits shall cause the driver to use "Read/Write Extended" DMA commands
- ; and need an ATA-6 or newer hard-disk. LBA values of 28 bits or less
- ; shall use regular DMA commands. 24-bit "CHS mode" is also supported
- ; for MS-DOS V6.xx and earlier. Data accessed using CHS calls must be
- ; located in the initial 8-GB of the disk.
- ;
- ; The driver intercepts BIOS INT13 read or write requests only. Other
- ; INT13 requests (including seeks) and read/write requests with invalid
- ; parameters will be "passed" back to the BIOS or some other driver for
- ; handling. If a user I-O buffer is not DWORD aligned, crosses a 64K-
- ; boundary or fails a VDS "lock", the I-O request will use a 64K buffer
- ; in XMS memory with UltraDMA to or from the buffer, to avoid "passing"
- ; these requests to the BIOS for execution in slow PIO mode! Although
- ; UltraDMA specifies word-aligned buffers, ERRATA in some chipsets does
- ; require DWORD alignment and avoiding a 64K DMA address boundary!
- ;
- ; Beginning with version 1.6 of this driver, the following return codes
- ; have been added to help in diagnosing "problem" systems and chipsets.
- ; On exit from successful I-O requests, the AH-register is zero and the
- ; carry flag is reset. If an error occurs, the carry flag is SET, and
- ; the AH-register contains one of the following codes:
- ;
- ; Code 08h - DMA timed out
- ; 0Fh - DMA error
- ; 20h - Controller busy before I-O
- ; 21h - Controller busy after I-O
- ; 80h - First DRQ timed out
- ; AAh - Disk not ready before I-O
- ; ABh - Disk not ready after I-O
- ; CCh - Write FAULT before I-O
- ; CDh - Write FAULT after I-O
- ; E0h - Hard error at I-O end
- ; FEh - BIOS/driver read MISMATCH (init only)
- ; FFh - XMS memory error
- ;
- ;
- ; Revision History:
- ; ----------------
- ; V6.8 28-Jan-04 JE If no EDD/DPTE, 4 disks at 80h-83h units only
- ; V6.7 16-Jan-04 JE Renumbered to replace UDMA, init code reduced
- ; V2.2 25-Dec-03 JE Corrected "read test" diagnostic messages
- ; V2.1 24-Dec-03 JE Use XMS for read tests, to reduce UDMA size
- ; V2.0 21-Dec-03 JE Controller-name displays, multi-sector tests
- ; V1.9 6-Dec-03 JE Fixed VDS init bug, buffer/diagnostic "swap"
- ; V1.8 3-Dec-03 JE Fixed "STI" bug, "DMA only" now 528 bytes
- ; V1.7 25-Nov-03 JE If no XMS driver, allow "DMA only" usage
- ; V1.6 22-Nov-03 JE Fixed init reads, added full error codes
- ; V1.5 15-Nov-03 JE Added all UDMA init functions but ctlr. name
- ; V1.4 14-Nov-03 JE Corrected DMA-status reset
- ; V1.3 13-Nov-03 JE "DoIO" does ALL I-O, "XMS error" now 0FFh
- ; V1.2 12-Nov-03 JE No "timeout error", other size reductions
- ; V1.1 7-Nov-03 JE Used 80386 test from V5.9 UDMA
- ; V1.0 6-Nov-03 JE Initial release (had been named UDMA-E)
- ;
- ;
- ; General Program Equations
- ;
- %define VER 'V6.8, 28-Jan-2004.'
- SECSCYL equ 255*63 ; LBA sectors per cylinder
- HEADS equ 255 ; LBA heads
- SECSHD equ 63 ; LBA sectors per head
- RDYTO equ 8 ; 384-msec minimum I-O timeout
- RBYTES equ (2*1024*65536*12/14318180+1)*512 ; Read-test byte count
- ; ( = microseconds per tick, but adjusted for binary megabytes)
- ; Number of reads this length per tick = transfer rate in MB/s
- RS_SC equ RBYTES/512 ; "Read speed" input sector count
- RC_SC equ 58 ; "Read compare" total sector count
- RC_CYL equ 3 ; "Read compare" starting disk address
- RC_HD equ 15
- RC_SEC equ 5
- BIOSTMR equ 46Ch ; BIOS "tick" timer address
- HDISKS equ 475h ; BIOS hard-disk count address
- VDSFLAG equ 47Bh ; BIOS "Virtual DMA" flag address
- CR equ 0Dh ; ASCII carriage-return
- LF equ 0Ah ; ASCII line-feed
-
- ; IDE Controller Register Definitions
-
- CDATA equ 1F0h ; Data port
- CSUBCM equ CDATA+1 ; Subcommand register
- CSECCT equ CDATA+2 ; I-O sector count
- CDSEL equ CDATA+6 ; Disk-select and upper LBA
- CCMD equ CDATA+7 ; Command register
- CSTAT equ CDATA+7 ; Primary status register
- CSTAT2 equ CDATA+206h ; Alternate status register
-
- ; Controller Status and Command Definitions
-
- BSY equ 80h ; IDE controller is busy
- RDY equ 40h ; IDE disk is "ready"
- FLT equ 20h ; IDE disk has a "fault"
- DRQ equ 8 ; IDE data request
- ERR equ 1 ; IDE general error flag
- DMI equ 4 ; DMA interrupt has occured
- DME equ 2 ; DMA error has occurred
- DRCMD equ 0C8h ; DMA read command (write is 0CAh,
- ; LBA48 commands are 25h/35h)
- SETM equ 3 ; Set Mode subcommand
- SETF equ 0EFh ; Set Features command
- LBABITS equ 0E0h ; Fixed high-order LBA commands
-
- ; Driver Return Codes
-
- DMATIMO equ 008h ; DMA timeout code
- DMAERR equ 00Fh ; DMA error code
- CTLRERR equ 020h-FLT ; Ctlr. busy code (020h/021h at exit)
- DRQTIMO equ 080h ; DRQ timeout code
- DISKERR equ 0AAh-FLT ; Disk-busy code (0AAh/0ABh at exit)
- WFLTERR equ 0CCh-FLT ; Write-fault code (0CCh/0CDh at exit)
- HARDERR equ 0DFh-FLT ; Hard-error code (0E0H at exit)
- ; (XMS-error code is 0FFh)
-
- ; LBA "Device Address Packet" Layout
-
- struc DAP
- DapPL resb 1 ; Packet length
- resb 1 ; (Reserved)
- DapSC resb 1 ; I-O sector count
- resb 1 ; (Reserved)
- DapBuf resd 1 ; I-O buffer address (roffset & segment)
- DapLBA resd 2 ; Disk logical block address
- endstruc
-
- ; DOS "Request Packet" layout
-
- struc RP
- resb 2 ; (Unused by us)
- RPOp resb 1 ; Opcode
- RPStat resw 1 ; Status word
- resb 9 ; (Unused by us)
- RPSize resd 1 ; Resident driver size
- endstruc
- RPERR equ 8003h ; "Strategy" packet error flags
- RPDON equ 100h ; "Strategy" packet done flag
-
- ; DOS Driver Device Header
-
- @ dd 0FFFFFFFFh ; Link to next device-header block
- dw 8000h ; Driver "device attributes"
- dw Strat ; "Strategy" routine offset
- VLF equ $-2 ; (VDS "lock" flag after initialization)
- IDEAdr equ $-1 ; (Lower IDE status address, after init)
- dw DevInt ; "Device-Interrupt" routine offset
- PCIAdr equ $-2 ; (PCI UDMA command address, after init)
- db 16,16,'UDMA$',0 ; Driver name (arrows avoid user errors)
-
- ; Resident Driver Variables
-
- XVI dw 16 ; Constant 16, for 20-bit segment "math"
- Units dd 0FFFFFFFFh ; IDE "active units" table (set by init)
- PRDAd dd IOAdr ; PRD command-list address (set by init)
- db 0 ; IDE "upper" sector count (always zero)
- LBAHi db 0, 0, 0 ; IDE "upper" LBA bits 24-47
- SecCt db 0 ; IDE "lower" sector count (always used)
- LBA db 0, 0, 0 ; IDE "lower" LBA bits 0-23
- DSCmd db 0 ; IDE disk-select and LBA commands
- IOCmd db 0 ; IDE command byte
- XMSHdl dw 0 ; XMS buffer handle number (set by init)
- XMSOffs dd 0 ; XMS 32-bit buffer offset (set by init)
- IOLen dd 0 ; XMS and VDS I-O byte count
- XMSSH dw 0 ; XMS source block handle (00h if DS:SI)
- XMSSA dd 0 ; XMS 32-bit source address (may be DS:SI)
- XMSDH dw 0 ; XMS dest. block handle (00h if ES:DI)
- IOAdr dd 0 ; XMS dest. & VDS/DMA addr. (may be ES:DI)
- VDSOf equ XMSSH ; VDS parameters all SHARE the XMS block!
- VDSSg equ XMSSA+2
- DMALn dd 80000000h ; DMA byte count and "end" flag
-
- ; Driver Main Routine. For CHS requests, at entry the registers contain:
- ;
- ; AH Request code. We handle only 2 read and 3 write
- ; AL I-O sector count
- ; CH Lower 8 bits of starting cylinder number
- ; CL Starting sector number and upper 2 bits of cylinder number
- ; DH Starting head number
- ; DL Unit number. We handle UltraDMA hard-disks of 80h and up
- ; ES:BX I-O buffer address
- ;
- ; For LBA requests, at entry the registers contain:
- ;
- ; AH Request code. We handle only 42h read and 43h write
- ; DL Unit number. We handle UltraDMA hard-disks of 80h and up
- ; DS:SI Pointer to Device Address Packet ("DAP"), described above
-
- Entry pushf ; Driver entry - save CPU flags
- pusha ; Save all CPU registers
- mov bp,4 ; Reset active-units table index
- NxtUnit dec bp ; Any more active units to check?
- js QuickEx ; No, request is not ours - exit quick!
- cmp dl,[cs:bp+Units-@] ; Does request unit match our table?
- jne NxtUnit ; No, see if more table entries remain
- mov dl,0BEh ; Mask out LBA and write request bits
- and dl,ah
- cmp dl,2 ; Is this a CHS or LBA read or write?
- jne QuickEx ; No, exit quick!
- push ds ; Save CPU segment registers
- push es
- shl ah,1 ; Is this an LBA read or write request?
- jns CalcCHS ; No, go calculate CHS disk address
- cmp dword [si+DapBuf],byte -1 ; 64-bit I-O buffer address?
- jne GetDAP ; No, get all "DAP" parameters
- NotUs pop es ; Request not for us - reload registers
- pop ds
- QuickEx popa
- popf ; Reload CPU flags
- jmp 0000:0000 ; "Pass" request back to INT13 chain
- @PrvI13 equ $-4 ; (Previous INT13 vector, set by Init)
- GetDAP mov al,[si+DapSC] ; Get "DAP" sector count
- les cx,[si+DapBuf] ; Get "DAP" I-O buffer address
- mov di,[si+DapLBA+4]; Get "DAP" logical-block address
- mov dx,[si+DapLBA+2]
- mov si,[si+DapLBA]
- jmp short CheckSC ; Go check sector count
- CalcCHS xchg ax,cx ; CHS - save request code and sectors
- mov si,SECSHD ; Get starting sector in SI-register
- and si,ax
- dec si
- mov di,dx ; Get starting head in DI-reg
- shr al,6 ; Get cylinder number in AX-reg
- xchg al,ah
- mov dx,SECSCYL ; Convert cylinder to sectors
- mul dx
- xchg ax,di ; Swap low-order and head number
- mov al,SECSHD ; Convert head to sectors
- mul ah
- add si,ax ; Add to starting sector
- add si,di ; Add in cylinder sectors
- adc dl,dh
- xchg ax,bx ; Get buffer offset in AX-register
- xchg ax,cx ; Swap offset with command/sectors
- xor di,di ; Reset upper LBA address bits
- CheckSC dec al ; Is sector count from 1 to 128?
- js NotUs ; No? Let BIOS handle this request!
- sti ; Valid request - enable interrupts
- push cs ; Set our DS-register
- pop ds
- xor bx,bx ; Zero BX-reg. for relative commands
- mov [bx+LBA+2-@],dl ; Set disk LBA bits 16-47
- mov [bx+LBAHi-@],dh ; (Bits 0-15 set below for alignment)
- mov [bx+LBAHi+1-@],di
- shr dx,12 ; Shift out LBA bits 16-27
- or di,dx ; Anything in LBA bits 28-47?
- jz DoLBA28 ; No, use LBA28 read/write command
- shl ah,3 ; LBA48 - get request as 20h/30h
- jmp short GetAddr ; Go get device-address bytes
- DoLBA28 xchg dh,[bx+LBAHi-@] ; Reload and reset LBA bits 24-27
- or ah,(DRCMD+1) ; Get LBA28 read/write command + 5
- GetAddr shr bp,1 ; Get slave-select bit in carry
- mov bp,(CDSEL-100h) ; Get primary device-address bytes
- @PCILo1 equ $-1 ; (PCI command address, set by init)
- jz DevAddr ; Secondary channel I-O request?
- mov bp,(CDSEL+680h) ; Yes, get secondary address bytes
- @PCILo2 equ $-1 ; (PCI command address, set by init)
- DevAddr mov [bx+IDEAdr-@],bp; Set IDE & PCI device-address bytes
- mov [bx+LBA-@],si ; Set disk LBA bits 0-15
- mov dl,(LBABITS/32) ; Initialize LBA command byte
- rcl dl,5
- or dl,dh ; Put LBA bits 24-27 in LBA command
- mov dh,5 ; Get final IDE read/write command
- xor dh,ah
- mov [bx+DSCmd-@],dx ; Set LBA and IDE command bytes
- cbw ; Restore sector count to 16 bits
- inc ax
- mov [SecCt],al ; Set I-O sector count
- shl ax,1 ; Set I-O and DMA byte counts
- mov [IOLen+1],ax
- mov [DMALn+1],ax
- mov [bx+VDSOf-@],cx ; Set 32-bit VDS offset
- mov [bx+VDSOf+2-@],bx
- mov [bx+VDSSg-@],es ; Set 16-bit VDS segment
- mov bp,sp ; Point BP-reg. to our stack data
- mov ax,es ; Get 20-bit buffer segment value
- mul word [bx+XVI-@]
- add ax,cx ; Add in buffer offset value
- adc dx,bx
- test al,3 ; Is user's I-O buffer DWORD aligned?
- jnz GoToBuf ; No, use buffered I-O logic below
- inc ax ; Set "no VDS" buffer-address flag
- mov [IOAdr],ax ; Preset 20-bit user buffer address
- mov [bx+IOAdr+2-@],dx
- mov ax,8103h ; Do VDS "lock" of user I-O buffer
- mov dx,0Ch
- call VDSLock
- jc GoToBuf ; VDS error - use buffered logic
- btr [bx+IOAdr-@],bx ; Set address bit 0 in VDS "lock" flag
- rcl byte [bx+VLF-@],1
- mov ax,[IOLen] ; Get low-order ending DMA address
- dec ax ; (IOLen - 1 + IOAdr)
- add ax,[bx+IOAdr-@] ; Will this I-O cross a 64K boundary?
- jc NoLock ; Yes, use buffered I-O logic below
- call DoIO ; Do direct DMA I-O with user's buffer
- Done mov sp,bp ; Done - discard "leftover" stack data
- mov [bp+19],al ; Set error code in exiting AH-register
- rcr byte [bp+26],1 ; Set error flag in exiting carry bit
- rol byte [bp+26],1
- call VDSUnlk ; If needed, "unlock" user I-O buffer
- pop es ; Reload all CPU registers and exit
- pop ds
- popa
- popf
- iret
- NoLock call VDSUnlk ; Buffered I-O - "unlock" user buffer
- GoToBuf jmp UseBuf ; Go to buffered I-O routines below
-
- ; Subroutine to do VDS "lock" and "unlock" functions
-
- VDSUnlk sti ; Ensure CPU interrupts are enabled!
- sar byte [bx+VLF-@],1 ; Was user buffer "locked" by VDS?
- jc VDSExit ; No, go exit
- mov ax,8104h ; Do VDS "unlock" of user I-O buffer
- xor dx,dx
- VDSLock push ds ; Point to VDS parameter block
- pop es
- mov di,IOLen
- int 4Bh ; Execute desired VDS function
- VDSExit ret ; Exit
-
- ; Subroutine to execute an I-O request
-
- BufIO mov dword [bx+IOAdr-@],0 ; Buffered - point to XMS memory
- @XBufAd equ $-4 ; (XMS buffer address, set by init)
- DoIO sti ; Ensure CPU interrupts are enabled!
- cld ; Ensure FORWARD "string" commands!
- mov dx,[bx+PCIAdr-@]; Get DMA command-register address
- in al,dx ; Ensure any previous DMA is stopped!
- and al,0FEh ; (See comments below in driver-init)
- out dx,al
- push dx ; Save command-register address
- mov al,[DSCmd] ; Select our desired disk
- and al,0F0h
- mov dl,[bx+IDEAdr-@]
- mov dh,1
- out dx,al
- mov di,dx ; Save IDE drive-select address
- mov es,bx ; Point to BIOS timer in low-memory
- mov si,BIOSTMR
- mov ah,RDYTO ; Set AH-reg. with I-O timeout limit
- add ah,[es:si]
- mov ch,FLT ; Check only disk fault after ready
- call WaitRdy ; Await controller- and disk-ready
- shr byte [bp+19],1 ; Get write request bit in carry
- cmc ; Invert carry so 1 = read, 0 = write
- rcl al,4 ; Set DMA "read/write" byte
- pop dx ; Reload DMA command-register address
- out dx,al ; Reset command register and set mode
- push dx ; Save DMA command-register address
- inc dx ; Point to DMA status register
- inc dx
- in al,dx ; Reset DMA status register
- or al,6 ; (Done this way so we do NOT alter
- out dx,al ; the "DMA capable" status bits!)
- push si ; Save BIOS timer pointer
- inc dx ; Set PRD pointer to our DMA address
- inc dx
- mov si,PRDAd
- outsd
- mov cx,1F7h ; Set IDE parameter-output flags
- NxtPar lea dx,[di+CSECCT-CDSEL-1] ; Point to IDE sector count -1
- IDEPar inc dx ; Output LBA48 IDE parameter bytes
- outsb ; (If LBA28, 1st 4 get overwritten!)
- shr cx,1 ; More parameters to go in this group?
- jc IDEPar ; Yes, loop back and output next one
- jnz NxtPar ; If first 4 output, go do last 6
- pop si ; Reload BIOS timer pointer
- mov dh,3 ; Get IDE alternate-status address
- dec dx ; (Primary-status address | 300h - 1)
- ChkDRQ mov al,DRQTIMO ; Get DRQ-timeout return code
- cmp ah,[es:si] ; Too long without 1st data-request?
- je Kaput ; Yes? Return carry and DRQ timeout!
- in al,dx ; Read IDE alternate status
- and al,DRQ ; Has 1st data-request arrived?
- jz ChkDRQ ; No, loop back and check again
- pop dx ; Reload DMA command-register address
- in al,dx ; Set DMA Start/Stop bit (starts DMA)
- inc ax
- out dx,al
- ChkDMA inc dx ; Read DMA controller status
- inc dx
- in al,dx
- dec dx
- dec dx
- and al,DMI+DME ; DMA interrupt or DMA error?
- jnz StopDMA ; Yes, stop DMA and check results
- cmp ah,[es:si] ; Has our DMA transfer timed out?
- jne ChkDMA ; No, loop back and check again
- StopDMA push ax ; Save ending DMA status
- in al,dx ; Reset DMA Start/Stop bit
- and al,0FEh
- out dx,al
- pop ax ; Reload ending DMA status
- cmp al,DMI ; Did DMA end with only an interrupt?
- jne DMAFail ; No? Go check what went wrong
- inc dx ; Reread DMA controller status
- inc dx
- in al,dx
- test al,DME ; Any "late" DMA error after DMA end?
- jnz PostDMA ; Yes? Set DMA-error code and exit
- mov ch,FLT+ERR ; Check fault and error after I-O end
- WaitRdy lea dx,[di+CSTAT-CDSEL] ; Point to IDE primary status
- ChkRdy in al,dx ; Read IDE primary status
- cmp ah,[es:si] ; Too long without becoming ready?
- je RdyFail ; Yes? Go check what went wrong
- test al,BSY+RDY ; Controller or disk still busy?
- jle ChkRdy ; Yes, loop back and check again
- and al,ch ; Disk-fault or hard-error?
- jnz HdwFail ; Yes? Go check what went wrong
- ret ; All is well - exit
- HdwFail test al,FLT ; Does the disk show a write-fault?
- mov ax,(256*WFLTERR)+HARDERR ; Get status-error codes
- jmp short WhichRC ; Go see which return code to use
- DMAFail test al,DME ; Did DMA end with an error?
- PostDMA mov ax,(256*DMAERR)+DMATIMO ; Get DMA-failure codes
- jmp short WhichRC ; Go see which return code to use
- RdyFail test al,BSY ; Did controller ever become ready?
- mov ax,(256*CTLRERR)+DISKERR ; Get not-ready return codes
- WhichRC jz ErAtEnd ; If "zero", use AL-reg. return code
- mov al,ah ; Use AH-reg. return code of this pair
- ErAtEnd add al,ch ; Add 1 if error was at I-O end
- Kaput stc ; Set carry flag to denote "error"
- DoneJmp jmp Done ; Go set stack return codes and exit
-
- ; Buffered I-O routines, put here so they and the XMSMove subroutine
- ; can be "dismissed" during driver-init if no XMS driver is found!
-
- BufOut call XMSMove ; Move user output data to XMS buffer
- call BufIO ; Output all data from XMS buffer
- jmp short DoneJmp ; Done - go post "success" and exit
- UseBuf shl dword [bx+VDSOf-@],16 ; Convert to XMS handle/offset
- test byte [bx+IOCmd-@],12h ; Is this a write request?
- jnz BufOut ; Yes, use output routine above
- call BufIO ; Input all data to our XMS buffer
- call XMSMove ; Move XMS data to user input buffer
- jmp short DoneJmp ; Done - go post "success" and exit
-
- ; Subroutine to move data to and from the driver's XMS buffer
- ; NOTE: Before entering here, the main routine has converted
- ; our user-buffer offset (VDSOf) to a "null" handle and hi-
- ; order offset, and so the XMS source field ALREADY has the
- ; user-buffer address needed by XMS moves, which simplifies
- ; this routine! Also, the XMS driver is allowed to control
- ; the A20 line, which "HIMEM.SYS" and other drivers all do!
-
- XMSMove sti ; Ensure CPU interrupts are enabled!
- cld ; Ensure FORWARD "string" commands!
- push ds ; Point ES-reg. to our data
- pop es
- mov di,XMSDH ; Point to XMS destination field
- jnz XMSOut ; If output, just set XMS destination!
- mov si,XMSSH ; Point to user-buffer address
- movsw ; Move user-buffer address from
- movsw ; XMS source to XMS destination
- movsw
- mov di,XMSSH ; Point to XMS source field
- XMSOut mov si,XMSHdl ; Set XMS handle and buffer offset as
- movsw ; input source or output destination
- movsw
- movsw
- mov ah,0Bh ; Move data to or from our XMS buffer
- call 0000:0000 ; (SI-reg. points to IOLen after move)
- @XEntry equ $-4 ; (XMS "entry" address, set by init)
- xor bx,bx ; Zero BX-reg. for relative commands
- dec ax ; Any errors during XMS move?
- jnz Kaput ; Yes? Return carry and XMS error!
- ret ; All is well - exit
- align 16
- ResEnd equ $ ; End of resident driver
-
- ; Initialization Variables
-
- IVDSLen dd ResEnd ; Initialization VDS parameters
- IVDSOfs dd 0
- IVDSSeg dd 0
- IVDSAdr dd 0
- Packet dd 0 ; "Init" request packet address
- Bucket dd 0 ; Working 32-bit "bucket"
- HDNames dw PMMsg ; Table of hard-disk "name" pointers
- dw PSMsg
- dw SMMsg
- dw SSMsg
- Modes db '16. ' ; Mode 0 = ATA-16 UltraDMA mode table
- db '25. ' ; Mode 1 = ATA-25
- db '33. ' ; Mode 2 = ATA-33
- db '44. ' ; Mode 3 = ATA-44 (Rare but possible)
- db '66. ' ; Mode 4 = ATA-66
- db '100.' ; Mode 5 = ATA-100
- db '133.' ; Mode 6 = ATA-133
- db '166.' ; Mode 7 = ATA-166
- ErrMsgs db 008h ; Driver error-message codes/addresses
- dw EMsg1
- db 00Fh
- dw EMsg2
- db 020h
- dw EMsg3
- db 021h
- dw EMsg4
- db 080h
- dw EMsg5
- db 0AAh
- dw EMsg6
- db 0ABh
- dw EMsg7
- db 0CCh
- dw EMsg8
- db 0CDh
- dw EMsg9
- db 0E0h
- dw EMsg10
- db 0FEh
- dw EMsg11
- EMsgEnd db 0FFh
- dw EMsg12
- ITbl db 0FAh, 0F0h, 08Ah, 080h ; Interface byte table
- ITEnd equ $
- HDCount db 0 ; Remaining hard-disk count
- EDDFlag db 0 ; "EDD BIOS present" flag
- HDUnit db 0 ; Current BIOS unit number
- HDIndex db 0 ; IDE "index" number
- HDOffs db 0 ; IDE channel "offset"
- HDNibbl db 0 ; IDE drive-select "nibble"
- RCSecNo db 0 ; "Read compare" sector address
- RCSects db 0 ; "Read compare" remaining sectors
-
- ; Main driver-initialization routine, entered from the DOS "device
- ; interrupt" logic below, after it does one-time-only functions
-
- I_RScan mov al,0 ; Load & reset EDD BIOS flag
- xchg al,[EDDFlag]
- cmp al,0 ; Were we scanning v.s. DPTE data?
- jne I_Scan ; Yes, try hardware-only disk scan
- I_Kaput mov dx,NDMsg ; Display "No UltraDMA disk" and exit
- jmp short I_Fail
- I_Scan mov ax,80h ; Reset hard-disk unit number & index
- mov [HDUnit],ax
- mov byte [HDCount],0; Reset remaining hard-disk count
- @BIOSHD equ $-1 ; (BIOS hard-disk count, set below)
- cmp byte [EDDFlag],0; Will disk scan use the EDD BIOS?
- jne I_Next ; Yes, go start with BIOS unit 80h.
- mov dx,HOMsg ; Display "hardware-only" message
- call I_Dsply
- I_Next movzx bx,[HDIndex] ; Get disk unit-number index
- cmp bh,[EDDFlag] ; Are we using DPTE data from BIOS?
- je I_ChnMS ; No, check disk at "fixed" addresses
- mov ah,48h ; Get next BIOS disk's EDD parameters
- mov dl,[HDUnit]
- mov si,EDDBuff
- int 13h
- jc I_NoGud ; Error? Display message and exit!
- cmp dword [si+26],byte -1 ; Valid DPTE pointer?
- je near I_More ; No, ignore unit & check for more
- les si,[si+26] ; Get this disk's DPTE pointer
- mov bx,15 ; Calculate DPTE checksum
- mov al,0
- I_CkSum add al,[es:bx+si]
- dec bx
- jns I_CkSum
- cmp al,0 ; Is DPTE valid (checksum = 0)?
- je I_EDDOK ; Yes, use this disk's parameters
- I_NoGud mov dx,EBMsg ; Display "Invalid EDD BIOS" and exit
- I_Fail jmp I_Err
- I_EDDOK movzx bx,[es:si+4] ; Get disk's device-select "nibble"
- shr bl,4 ; Initialize IDE unit number index
- and bl,1
- mov ax,[es:si] ; Get disk's IDE base address
- cmp ax,CDATA ; Is this a primary-channel disk?
- je I_Index ; Yes, set disk unit-number index
- cmp ax,(CDATA-80h) ; Is this a secondary-channel disk?
- jne I_More ; No, ignore unit & check for more
- add bl,byte 2 ; Adjust for secondary channel
- I_Index mov [HDIndex],bl ; Set disk's unit number index
- I_ChnMS mov ax,bx ; Separate channel and master/slave
- shr al,1
- mov ah,(LBABITS/32) ; Get drive-select "nibble"
- rcl ah,5
- ror al,1 ; Get channel offset (secondary = 80h)
- mov [HDOffs],ax ; Set select "nibble" & channel offset
- push bx ; Save 16-bit unit number index
- shl bx,1 ; Get "channel name" message index
- mov dx,[bx+HDNames] ; Display disk's IDE "channel name"
- call I_Dsply ; ("Primary master", etc.)
- mov ah,8 ; Get BIOS parameters for this disk
- mov dl,[HDUnit]
- int 13h
- xchg ax,dx ; Set AX-reg. with head-number value
- pop bx ; Reload unit number index
- mov dx,LEMsg ; Point to "not in LBA mode" message
- jc I_NotU ; Error - display msg. & ignore disk
- and cl,SECSHD ; Clear cylinder bits
- cmp cl,SECSHD ; Sectors per cylinder = 63?
- jne I_NotU ; No, display message & ignore disk
- cmp ah,HEADS-1 ; Heads = 255 (max. head = 254)?
- jne I_NotU ; No, display message & ignore disk
- mov al,[HDUnit] ; Activate this disk in main driver
- mov [bx+Units-@],al
- push bx ; Test for a valid UltraDMA disk
- call I_TestD
- pop bx
- jnc I_More ; Any errors during disk tests?
- mov byte [bx+Units-@],0FFh ; Yes? DELETE disk in driver!
- I_NotU call I_Dsply ; Display error for this disk
- mov dx,CRMsg ; Display error-message suffix
- call I_Dsply
- I_More add word [HDUnit],101h ; Bump BIOS unit and disk index
- cmp word [EDDFlag],8400h ; No EDD and all 4 units tested?
- je I_AnyHD ; Yes, see if we found any disks
- dec byte [HDCount] ; More BIOS disks to check?
- jnz near I_Next ; Yes, loop back and do next one
- I_AnyHD cmp dword [Units],byte -1 ; Any active UltraDMA disks?
- je near I_RScan ; No, see if we should do a re-scan
- call I_Hook ; "Hook" this driver into Int 13h
- les bx,[Packet] ; Post driver size & success code
- mov ax,[IVDSLen]
- mov [es:bx+RPSize],ax
- mov [es:bx+RPSize+2],cs
- mov word [es:bx+RPStat],RPDON
- popad ; Reload all CPU registers and exit
- pop es
- pop ds
- popf
- retf
- I_VErr sti ; VDS "lock" error! Enable interrupts
- mov word [XMSSH],VEMsg ; Point to VDS "lock" error message
- jmp I_XUnlk ; Go get rid of our XMS memory
- I_Err mov [XMSSH],dx ; Save error message pointer
- shr byte [IVDSOfs],1; Was driver "locked" by VDS?
- jnc I_XUnlk ; No, see if we reserved XMS memory
- push cs ; Point to VDS parameter block
- pop es
- mov di,IVDSLen
- mov ax,8104h ; Do VDS "unlock" of this driver
- xor dx,dx
- int 4Bh
- I_XUnlk mov dx,[XMSHdl] ; Get XMS buffer handle
- or dx,dx ; Did we reserve XMS memory?
- jz I_DoErr ; No, reload message pointer
- mov ah,0Dh ; Unlock our XMS memory buffer
- call far [@XEntry]
- mov ah,0Ah ; Free our XMS memory buffer
- mov dx,[XMSHdl]
- call far [@XEntry]
- I_DoErr mov dx,[XMSSH] ; Reload error message pointer
- I_ErOut call I_Dsply ; Display error message
- popad ; Reload all 32-bit registers
- push ax ; Save all 16-bit registers
- push bx
- push cx
- push dx
- push si
- push di
- I_Quit mov dx,Suffix ; Display message suffix
- call I_Dsply
- les bx,[Packet] ; Post "null" driver size
- xor ax,ax
- mov [es:bx+RPSize],ax
- mov [es:bx+RPSize+2],cs
- I_BadP mov ax,RPDON+RPERR ; Post "error" in init packet
- mov [es:bx+RPStat],ax
- pop di ; Reload all CPU registers and exit
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- pop es
- pop ds
- popf
- retf
-
- ; Subroutine to do all "validation" tests for an UltraDMA hard-disk
-
- I_TestD mov al,[HDNibbl] ; Select master or slave disk
- mov dx,CDSEL
- xor dl,[HDOffs]
- out dx,al
- mov al,0ECh ; Issue "Identify Device" command
- call I_Cmd
- jnc I_PIO ; If no error, get "identify" data
- I_AErr mov dx,AEMsg ; Absent or non-ATA! Point to msg
- stc ; Set carry flag (error!) and exit
- ret
- I_PIO mov dx,CDATA ; Point to controller PIO data reg
- xor dl,[HDOffs]
- in ax,dx ; Read I.D. bytes 0 and 1
- xchg ax,si ; Save "ATA/ATAPI" flag word
- mov cx,26 ; Skip I.D. bytes 2-53
- I_Skip0 in ax,dx
- loop I_Skip0
- cld ; Ensure FORWARD "string" commands!
- push ds ; Point to disk-name message
- pop es
- mov di,DiskNam
- mov cl,20 ; Read & swap disk name into message
- I_RdNam in ax,dx ; (I.D. bytes 54-93)
- xchg ah,al
- stosw
- loop I_RdNam
- mov cl,6 ; Skip I.D. bytes 94-105
- I_Skip1 in ax,dx
- loop I_Skip1
- in ax,dx ; Read I.D. bytes 106 and 107
- mov bh,al ; Save "DMA valid" flag byte
- mov cl,34 ; Skip I.D. bytes 108-175
- I_Skip2 in ax,dx
- loop I_Skip2
- in ax,dx ; Read I.D. bytes 176 and 177
- mov bl,ah ; Save "UDMA selected" flag byte
- mov cl,167 ; Skip remaining I.D. data
- I_Skip3 in ax,dx
- loop I_Skip3
- shl si,1 ; Is this an "ATA" hard-disk?
- jc I_AErr ; No? Exit & display message!
- test bh,4 ; Are UltraDMA flag bits valid?
- jz I_DErr ; No? Exit & display message!
- mov di,Modes ; Point to UDMA mode table
- mov al,'0' ; Initialize "current mode" value
- mov cl,2 ; Set rotating mode-check bit
- cmp bl,1 ; Will disk do UDMA mode 0?
- jae I_NxtM ; Yes, find its best UDMA mode
- I_DErr mov dx,DEMsg ; Not a UDMA disk! Point to message
- I_SErr stc ; Set carry flag (error!) and exit
- ret
- I_NxtM cmp bl,cl ; Will disk do next UDMA mode?
- jb I_GotM ; No, use previous mode
- inc ax ; Set up for next UDMA mode
- add di,byte 4
- shl cl,1 ; More UDMA modes to check?
- jnz I_NxtM ; Yes, loop back
- I_GotM mov [CurMode],al ; Update "current mode" value
- mov eax,[di] ; Post UDMA mode in set-mode message
- mov [DspMode],eax
- mov dx,CSUBCM ; Set mode-select subcode
- xor dl,[HDOffs]
- mov al,SETM
- out dx,al
- inc dx
- mov al,[CurMode] ; Set desired UltraDMA mode value
- add al,10h
- out dx,al
- mov al,SETF ; Issue set-features command to disk
- call I_Cmd
- mov dx,SEMsg ; Point to "Set-mode" error message
- jc I_SErr ; If error, set carry flag and exit
- mov di,DNamEnd ; Point to end of disk name
- I_NextN cmp di,DiskNam ; Are we at the disk-name start?
- je I_NullN ; Yes, disk name is all spaces!
- cmp byte [di-1],' ' ; Is preceding byte a space?
- jne I_TermN ; No, terminate disk name message
- dec di ; Decrement disk name pointer
- jmp short I_NextN ; Go see if next byte is a space
- I_NullN mov dword [di],"unna" ; Set "unnamed" as disk name
- mov dword [di+4],"med "
- add di,byte 7
- I_TermN mov word [di],".$" ; Set message terminators after name
- cmp byte [DspMEnd+1],CR ; Do we have XMS, for read tests?
- je near I_DispN ; No, just display disk name and mode
- mov al,0C3h ; Disable XMS moves with "ret" command,
- mov [cs:XMSMove],al ; "dirty-nasty" but QUICK code change!
- call I_Hook ; "Hook" this driver into Int 13h
- call I_Read ; Do initial read for synchronization
- jnc I_RSetC ; If O.K., set up 4-pass read test
- I_RFail push ax ; Read error! Save driver return code
- call I_RVect ; Restore original INT 13h vector
- pop ax ; Reload driver return code
- I_RdErr push ax ; Display "FAILED read test" message
- mov dx,TEMsg
- call I_Dsply
- pop ax
- mov di,(ErrMsgs-3) ; Point to our error-message table
- I_EScan add di,byte 3 ; Bump to next message code/address
- mov dx,[di+1] ; Set message address in DX-register
- cmp ah,[di] ; Driver return code = table code?
- je I_REMsg ; Yes, this is the message we want!
- cmp di,EMsgEnd ; Is driver return code unrecognized?
- jb I_EScan ; No, loop back and check next entry
- I_RCMsg mov cx,2 ; Set return code in error message
- mov si,RCode
- call HexConv
- mov dx,RCMsg ; Point to "Return code" message
- I_REMsg stc ; Set carry flag (error!) and exit
- ret
- I_RSetC xor dx,dx ; Clear read counter
- xor si,si ; Point to BIOS timer in low-memory
- mov es,si
- mov si,BIOSTMR
- mov cl,[es:si] ; Load current timer tick count LSB
- I_RWait cmp cl,[es:si] ; Next tick arrived?
- je I_RWait ; No, keep waiting
- add cl,1+4 ; Yes, update and prepare for 4 passes
- I_RNext inc dx ; Yes, count reads up
- push es ; Save timer/counter registers
- push si
- push dx
- push cx
- call I_Read ; Read RBYTES into XMS buffer
- pop cx ; Reload timer/counter registers
- pop dx
- pop si
- pop es
- jc I_RFail ; Read error? Display message & exit!
- cmp cl,[es:si] ; Next timer interrupt arrived?
- jne I_RNext ; No, read once more
- shr dx,2 ; Save average rate for 4 passes
- mov [Bucket],dx
- mov al,RC_SC ; Set "read comparison" parameters
- mov [RCSects],al
- mov cx,((RC_CYL*256)+RC_SEC)
- mov [RCSecNo],cl
- mov dh,RC_HD
- call I_Read1 ; Input "read comparison" data to XMS
- jc I_RFail ; Read error? Display message & exit!
- call I_RVect ; Restore original INT 13h vector
- cld ; Set up to initialize XMS block
- push ds
- pop es
- mov di,IOLen
- mov eax,512 ; Set 1-sector XMS block length
- stosd
- mov ax,[XMSHdl] ; Reset XMS "source address"
- stosw
- mov eax,[XMSOffs]
- stosd
- xor ax,ax ; Reset XMS "destination address"
- stosw
- mov ax,RBuffer+512
- stosw
- mov ax,ds
- stosw
- I_RCSec mov al,1 ; Set BIOS input sector count of 1
- mov bx,RBuffer ; Set BIOS input buffer address
- mov ch,RC_CYL ; Set BIOS input disk address
- mov cl,[RCSecNo]
- mov dh,RC_HD
- call I_Read2 ; Have BIOS read next data sector
- jnc I_RCXMS ; Any error during BIOS read?
- push ax ; Save BIOS return code
- mov dx,BEMsg ; Display "failed BIOS read" message
- call I_Dsply
- pop ax ; Reload BIOS return code
- jmp I_RCMsg ; Go set up return-code msg. and exit
- I_RCXMS mov ah,0Bh ; Get next data sector from XMS memory
- mov si,IOLen
- call far [@XEntry]
- dec ax ; Any errors during XMS move?
- I_RCErr jnz near I_RdErr ; Yes? Display DRIVER error and exit!
- cld ; Set up 256-word data comparison
- mov cx,256 ; (Done as 16-bit words so interrupts
- mov si,RBuffer ; can remain enabled on older CPUs)
- mov di,RBuffer+512
- push ds
- pop es
- rep cmpsw ; Mismatch between BIOS and driver input?
- mov ah,0FEh ; (Load "mismatch" return code if so)
- jne I_RCErr ; Yes? Display DRIVER error and exit!
- add dword [XMSSA],512 ; Advance XMS source to next sector
- inc byte [RCSecNo] ; Update BIOS input sector number
- dec byte [RCSects] ; More BIOS data to input?
- jnz I_RCSec ; Yes, go read next sector
- I_DispN mov dx,DNamMsg ; Display disk "name" message
- call I_Dsply
- mov dx,MSMsg ; Display "Set to mode" message
- call I_Dsply
- cmp byte [DspMEnd+1],CR ; Was no read-test done (no XMS)?
- je I_TDEnd ; Yes, just clear carry and exit
- mov ax,[Bucket] ; Reload average read rate
- mov di,DspRate+4 ; Point to read-rate digits message
- mov byte [di],'0' ; Initialize read rate digits to 0
- or ax,ax ; Did the disk read NOTHING?
- jz I_Dsply ; Yes, display read rate = 0
- mov cx,10 ; CX = divisor
- I_ItoA xor dx,dx ; DX:AX = dividend
- div cx
- xchg dx,ax ; DX = quotient, AX = remainder
- add al,'0' ; convert to ASCII
- mov [di],al
- dec di
- xchg dx,ax ; AX = quotient
- or ax,ax ; zero?
- jnz I_ItoA ; no, continue
- lea dx,[di+1] ; Display read-rate message
- I_Dsply mov ah,9
- int 21h
- I_TDEnd clc ; Clear carry (no errors!) and exit
- ret ; Exit
-
- ; Subroutine to do all "read test" inputs
-
- I_Read mov al,RS_SC ; "Read speed" - set sector count
- mov cx,1 ; Set cylinder 0, sector 1
- xor dh,dh ; Set head 0
- I_Read1 mov bx,RBuffer+3 ; Use "odd" offset to avoid VDS use
- I_Read2 push ds ; Point ES-register to our data
- pop es
- mov ah,2 ; Set "CHS read" request code
- mov dl,[HDUnit] ; Set desired unit number
- int 13h ; Do desired input from current disk
- ret ; Exit
-
- ; Subroutine to issue initialization commands to our disks
-
- I_Cmd mov dx,CCMD ; Issue desired init command
- xor dl,[HDOffs]
- out dx,al
- xor si,si ; Point to BIOS timer in low-memory
- mov es,si
- mov si,BIOSTMR
- mov cl,RDYTO ; Set I-O timeout limit in CL-reg
- add cl,[es:si]
- I_CmdW cmp cl,[es:si] ; Has our command timed out?
- je I_CmdE ; Yes, set CPU carry flag & exit
- mov dx,CSTAT ; Get IDE controller status
- xor dl,[HDOffs]
- in al,dx
- test al,BSY+RDY ; Controller or disk still busy?
- jle I_CmdW ; Yes, loop back and check again
- test al,ERR ; Did command cause any errors?
- jz I_CmdX ; No, leave carry flag off & exit
- I_CmdE stc ; Error! Set CPU carry flag
- I_CmdX ret ; Exit
-
- ; Subroutine to "hook" this driver into the Int 13h chain
-
- I_Hook mov ax,3513h ; Get current Int 13h vector
- int 21h
- mov [@PrvI13],bx ; Save vector for passed requests
- mov [@PrvI13+2],es
- mov ax,2513h ; "Hook" this driver into Int 13h
- mov dx,Entry
- int 21h
- ret ; Exit
-
- ; Subroutine to restore the Int 13h vector after initialization reads
-
- I_RVect mov ax,2513h ; Set back the old Int 13h vector
- lds dx,[@PrvI13]
- int 21h
- push cs ; Reload our DS-register
- pop ds
- mov al,0FBh ; Enable XMS moves with "sti" command
- mov [cs:XMSMove],al
- ret ; Exit
-
- ; Subroutine to convert a number from hex to ASCII for messages
- ; At entry, the message address is in the SI-reg. A 2-digit
- ; value is in the AH-reg. and the CX-reg. is equal to 2, or a
- ; 4-digit value is in the AX-reg. and the CX-reg. is set to 4
-
- HexConv rol ax,4 ; Rotate next hex digit to low-order
- push ax ; Save hex address
- and al,0Fh ; Mask off next hex digit
- cmp al,9 ; Is digit 0-9?
- jbe HexCnv1 ; Yes, convert to ASCII
- add al,7 ; Add A-F offset
- HexCnv1 add al,30h ; Convert digit to ASCII
- mov [si],al ; Set next ASCII hex digit in message
- inc si ; Bump message address
- pop ax ; Reload hex address
- loop HexConv ; If more digits to convert, loop back
- ret ; Exit
-
- ; Initialization Buffer Definitions. The next 1024 bytes beginning at
- ; label "RBuffer", Strategy and Device-Interrupt logic AND the start
- ; of the PCI device tables, are "disposable" and are used once-only.
- ; Then, these 1024 bytes become our two "read-test" input buffers.
-
- align 4
- EDDBuff dd 30 ; Start of 30-byte EDD input buffer
- RBuffer equ $ ; Start of "read-test" input buffers
-
- ; "Strategy" routine - At entry, ES:BX points to the DOS initialization
- ; request packet, which is saved for processing below
-
- Strat mov [cs:Packet],bx ; Save DOS request-packet address
- mov [cs:Packet+2],es
- retf ; Exit - await DOS "Device Interrupt"
-
- ; "Device-Interrupt" routine - This routine does one-time-only init
- ; functions, then jumps to the main initialization routine, above
-
- DevInt pushf ; Entry - save all registers
- push ds
- push es
- push ax
- push bx
- push cx
- push dx
- push si
- push di
- push cs ; Set our DS-reg
- pop ds
- les bx,[Packet] ; Point to DOS request packet
- xor ax,ax ; Get a zero for below
- cmp [es:bx+RPOp],al ; Is this an "Init" packet?
- je I_Title ; Yes, display our title message
- jmp I_BadP ; Go post errors and exit quick!
- I_Title mov dx,TTLMsg ; Display driver "title" message
- call I_Dsply
- pushf ; 80386 test - save CPU flags
- push sp ; See if CPU is an 80286 or newer
- pop ax
- cmp ax,sp ; 80286+ push SP, then decrement it
- jne I_Junk ; CPU is below 80286 - cannot use it!
- push word 7000h ; 80286 or newer - try to set NT|IOPL
- popf
- pushf
- pop ax
- test ah,70h ; Did any NT|IOPL bits get set?
- jnz I_80386 ; Yes, CPU is at least an 80386
- I_Junk popf ; Reload starting CPU flags
- mov dx,PRMsg ; Display "Not an 80386" message
- call I_Dsply
- jmp I_Quit ; Go display suffix and exit quick!
- I_80386 popf ; Reload starting CPU flags
- pop di ; Reload all 16-bit registers
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- pushad ; Save all 32-bit registers
- xor edi,edi ; Get PCI BIOS "I.D." code
- mov ax,0B101h
- int 1Ah
- cmp edx,'PCI ' ; Is PCI BIOS V2.0C or later?
- mov dx,PEMsg ; (Get error message pointer if not)
- jne I_PCErr ; No? Go display message and exit!
- mov si,ITbl ; Point to interface byte table
- cld ; Ensure we do "forward" string commands!
- I_GetDv mov ecx,10100h ; Look for class 1 (storage) subclass 1 (IDE)
- lodsb ; Get next interface byte
- mov cl,al
- push si ; Find PCI class code
- mov ax,0B103h ; (Returns bus/device/function in BX-reg.)
- xor si,si
- int 1Ah
- pop si
- jnc I_GotDv ; Found our boy! Go process it
- cmp si,ITEnd ; More interface bytes to try?
- jb I_GetDv ; Yes, try next one
- mov dx,NEMsg ; Baaad news! Point to error message
- jmp short I_PCErr ; Go display error message and exit
- I_GotDv push bx ; Save bus/device/function
- mov ax,0B108h ; Get low-order command byte
- mov di,4
- int 1Ah
- pop bx ; Reload bus/device/function
- and cl,5 ; Mask Bus-Master and I-O Space bits
- cmp cl,5 ; Are these bits what we found?
- je I_BaseA ; Yes, get our PCI base address
- mov dx,MEMsg ; Baaad news! Point to error message
- I_PCErr jmp I_ErOut ; Go display error message and exit
- I_BaseA push bx ; Get PCI base address (register 4)
- mov ax,0B109h
- mov di,32
- int 1Ah
- pop bx
- xchg ax,cx ; Post run-time PCI UDMA address
- and al,0FCh
- mov [PCIAdr],ax
- mov [@PCILo1],al ; Set lower PCI device-address bytes
- add [@PCILo2],al
- mov cx,4 ; Set hex address in display message
- mov si,DspAddr
- call HexConv
- push bx ; Get Vendor and Device I.D.
- mov di,0
- mov ax,0B10Ah
- int 1Ah
- pop bx
- xchg eax,ecx ; Save Vendor and Device I.D.
- mov [Bucket],eax
- mov cx,4 ; Set vendor I.D. in display message
- mov si,DspVID
- call HexConv
- mov eax,[Bucket] ; Reload Vendor and Device I.D.
- shr eax,16 ; Set Device I.D. in display message
- mov cl,4
- mov si,DspDID
- call HexConv
- mov ah,bh ; Set PCI bus number in message
- mov cl,2
- mov si,DspBus
- call HexConv
- mov ah,bl ; Set PCI device number in message
- shr ah,3
- mov cl,2
- mov si,DspDev
- call HexConv
- and bl,7 ; Set PCI function number in message
- or bl,30h
- mov [DspFnc],bl
- mov dx,PCMsg ; Display initial controller data
- call I_Dsply
- cld ; Set up controller-name scan
- push ds
- pop es
- mov eax,[Bucket] ; Reload Vendor and Device I.D.
- ror eax,16 ; "Swap" Vendor I.D. to high-order
- mov cx,(dt_end-devtab)/4 ; See if I.D.s are in our table
- mov di,devtab
- repne scasd ; Do we know what our controller is?
- jnz I_UnknC ; No, display Vendor/Device numbers
- mov dx,di ; Display the name of our controller
- call I_Dsply
- jmp short I_TermC ; Go display controller bus data
- I_UnknC mov dx,DspUnkn ; Display Vendor/Device I.D. numbers
- call I_Dsply
- I_TermC mov dx,PCMsg2 ; Display all controller bus data
- call I_Dsply
- mov ax,4300h ; Inquire about an XMS driver
- int 2Fh
- mov dx,NXMsg ; Point to "No XMS driver" message
- cmp al,80h ; Is an XMS driver installed?
- jne I_XErr ; No, display msg. and disable XMS
- mov ax,4310h ; Save XMS driver "entry" address
- int 2Fh
- mov [@XEntry],bx
- mov [@XEntry+2],es
- mov ah,9 ; Request 128K of XMS memory
- mov dx,128
- call far [@XEntry]
- dec ax ; Did we get our buffer memory?
- jnz I_XMErr ; No, display msg. and disable XMS
- mov [XMSHdl],dx ; Save our buffer handle
- mov ah,0Ch ; Lock our XMS memory buffer
- call far [@XEntry]
- dec ax ; Did buffer get locked?
- jz I_XMSOK ; Yes, save buffer address/offset
- xor dx,dx ; Load and reset our buffer handle
- xchg dx,[XMSHdl]
- mov ah,0Ah ; Free our XMS memory buffer
- call far [@XEntry]
- I_XMErr mov dx,XEMsg ; Point to "XMS memory" message
- I_XErr call I_Dsply ; Display XMS error message
- mov dx,NBMsg ; Display "no buffered I-O" message
- call I_Dsply
- mov eax,[CRMsg] ; Disable "read test" msg. display
- mov [DspMEnd],eax
- mov ax,(NotUs-(GoToBuf+3)) ; Reject buffered I-O with a
- mov [GoToBuf+1],ax ; dirty-nasty code change!
- mov ax,(ResEnd-BufOut) ; Dismiss all buffered logic
- sub [IVDSLen],ax ; by cutting driver length
- jmp short I_StopD ; Go stop any previous DMA
- I_XMSOK mov [Bucket],bx ; Save 32-bit XMS buffer address
- mov [Bucket+2],dx
- mov eax,[Bucket] ; Get XMS buffer address
- add eax,65536 ; Find 1st 64K boundary after start
- xor ax,ax
- mov [@XBufAd],eax ; Set XMS buffer address and offset
- sub eax,[Bucket]
- mov [XMSOffs],eax
- I_StopD mov dx,[PCIAdr] ; Ensure any previous DMA is stopped
- in al,dx ; (On some older chipsets, if DMA is
- and al,0FEh ; running, reading an IDE register
- out dx,al ; causes the controller to HANG!!)
- add dx,byte 8 ; Stop secondary-channel DMA, also!
- in al,dx
- and al,0FEh
- out dx,al
- mov byte [VLF],0FFh ; Set run-time VDS "lock" flag
- xor eax,eax ; Point ES-reg. to low memory
- mov es,ax
- mov al,[es:HDISKS] ; Set BIOS hard-disk count above
- mov [@BIOSHD],al
- cmp al,0 ; Did BIOS find any hard-disks?
- je near I_Kaput ; No? Display message and exit!
- mov ax,cs ; Set our code segment in VDS block
- mov [IVDSSeg],ax
- shl eax,4 ; Get 20-bit driver virtual address
- cli ; Avoid interrupts during VDS tests
- test byte [es:VDSFLAG],20h ; Are "VDS services" active?
- jz I_SetA ; No, set 20-bit virtual addresses
- push cs ; Point to VDS parameter block
- pop es
- mov di,IVDSLen
- mov ax,8103h ; "Lock" this driver into memory
- mov dx,0Ch
- int 4Bh
- jc near I_VErr ; Error? Display error msg. & exit!
- inc byte [IVDSOfs] ; Set initialization VDS "lock" flag
- mov eax,[IVDSAdr] ; Get 32-bit starting driver address
- I_SetA sti ; Re-enable CPU interrupts
- add [PRDAd],eax ; Set relocated 32-bit PRD address
- mov ah,41h ; See if this system has an EDD BIOS
- mov bx,55AAh
- mov dl,80h
- int 13h
- jc I_Part2 ; No, search for disks without EDD
- cmp bx,0AA55h ; Did BIOS "reverse" our entry code?
- jne I_Part2 ; No, search for disks without EDD
- test cl,4 ; Does BIOS support the EDD subset?
- jz I_Part2 ; No, search for disks without EDD
- inc byte [EDDFlag] ; Set "EDD BIOS present" flag
- I_Part2 jmp I_Scan ; Go scan for UltraDMA disks to use
-
- ; PCI vendor and device table. Format: dd <id>, db <dword-aligned name>
-
- align 4
- devtab:
- %include "udma.pci"
- dt_end equ $
-
- ; Initialization Messages
-
- DNamMsg db 'is ' ; Disk-name message (overlays title)
- DiskNam equ $
- DNamEnd equ DiskNam+40
- TTLMsg db CR,LF,'UDMA Disk Driver ',VER,CR,LF,'$'
- PRMsg db 'No 80386 CPU$'
- MEMsg db 'Bus-Master ERROR$'
- NEMsg db 'No controller found$'
- PEMsg db 'PCI BIOS too old$'
- NXMsg db 'No XMS driver$'
- XEMsg db 'XMS init error$'
- NBMsg db '; using only DMA I-O.',CR,LF,'$'
- VEMsg db 'VDS lock error$'
- DspUnkn db 'Vendor ID '
- DspVID db '0000h, device ID '
- DspDID db '0000h$'
- PCMsg db 'UltraDMA controller found at PCI address '
- DspAddr db '0000h.',CR,LF,' $'
- PCMsg2 db ', bus '
- DspBus db '00h, device '
- DspDev db '00h, function '
- DspFnc db '0.',CR,LF,'$'
- EBMsg db 'Bad EDD BIOS$'
- HOMsg db 'Doing hardware-only disk scan.',CR,LF,'$'
- NDMsg db 'No UltraDMA disk to use$'
- PMMsg db 'Primary-master disk $'
- PSMsg db 'Primary-slave disk $'
- SMMsg db 'Secondary-master disk $'
- SSMsg db 'Secondary-slave disk $'
- MSMsg db CR,LF,' Set to UltraDMA mode '
- CurMode db '0, ATA-'
- DspMode db '16.'
- DspMEnd db ' Read test = $'
- DspRate db ' 0 MB/sec'
- CRMsg db '.',CR,LF,'$'
- AEMsg db 'absent or non-ATA$'
- DEMsg db 'is not UltraDMA$'
- LEMsg db 'not in LBA mode$'
- SEMsg db 'set-mode error$'
- BEMsg db 'failed BIOS read! $'
- TEMsg db 'FAILED read test! $'
- EMsg1 db 'DMA timed out$'
- EMsg2 db 'DMA error$'
- EMsg3 db 'Controller busy before I-O$'
- EMsg4 db 'Controller busy after I-O$'
- EMsg5 db 'First DRQ timed out$'
- EMsg6 db 'Disk not ready before I-O$'
- EMsg7 db 'Disk not ready after I-O$'
- EMsg8 db 'Write FAULT before I-O$'
- EMsg9 db 'Write FAULT after I-O$'
- EMsg10 db 'Hard error at I-O end$'
- EMsg11 db 'BIOS/driver read MISMATCH$'
- EMsg12 db 'XMS memory error$'
- RCMsg db 'Return code = '
- RCode db '00h$'
- Suffix db '; driver NOT loaded!',CR,LF,'$'
-