home *** CD-ROM | disk | FTP | other *** search
- ; UDMAJR.ASM -- UDMA "Junior" Written 28-Jan-2004 by Jack R. Ellis
- ;
- ; (UDMA for the ROM-disk of 80x86, DOS-based embedded systems)
- ;
- ; UDMAJR 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. UDMAJR 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!
- ;
- ; NOTE: UDMAJR is a "short" variant of the full UDMA driver. It is
- ; intended for "RAM disk" and other space-limited systems. UDMAJR has
- ; all UDMA run-time functions, i.e. it supports up to 4 disks, supports
- ; LBA-48 and any earlier address mode, provides all return codes (shown
- ; below), and permits "DMA only" use when an XMS driver is not present.
- ; To hold UDMAJR.SYS at 2048 bytes, the following items are omitted:
- ; A) Initialization "diagnostic" messages for driver return codes.
- ; B) All initialization read tests and the read-rate display.
- ; C) Hard-disk names and controller "bus" data displays.
- ; D) The check for an 80386 or better CPU.
- ; Users who REQUIRE these items should employ the full UDMA driver.
- ;
- ; 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
- ; 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" messages (UDMA only)
- ; V2.1 24-Dec-03 JE Use XMS for read tests, to reduce UDMA size
- ; V2.0 21-Dec-03 JE UDMA controller names, multi-sector tests
- ; V1.9 6-Dec-03 JE Fixed VDS init bug
- ; V1.8 3-Dec-03 JE Fixed "STI" bug, "DMA only" now 528 bytes
- ; V1.7 25-Nov-03 JE Initial release of "S" (short) variant
- ;
- ;
- ; General Program Equations
- ;
- %define VER 'V6.8, 28-Jan-04.'
- 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
- 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
- EDDBuff dd 30 ; Start of 30-byte EDD input buffer
-
- ; "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
- pushad
- push ds
- push es
- push cs ; Set our DS-reg
- pop ds
- les bx,[Packet] ; Point to DOS request packet
- cmp byte [es:bx+RPOp],0 ; Is this an "Init" packet?
- jne near I_BadP ; No? Go post errors and exit quick!
- I_Title mov dx,TTLMsg ; Display driver "title" message
- call I_Dsply
- 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
- I_HexC 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 I_HexC1 ; Yes, convert to ASCII
- add al,7 ; Add A-F offset
- I_HexC1 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 I_HexC ; If more digits to convert, loop back
- mov dx,PCMsg ; Display controller-address message
- 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 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_Scan ; No, search for disks without EDD
- cmp bx,0AA55h ; Did BIOS "reverse" our entry code?
- jne I_Scan ; No, search for disks without EDD
- test cl,4 ; Does BIOS support the EDD subset?
- jz I_Scan ; No, search for disks without EDD
- inc byte [EDDFlag] ; Set "EDD BIOS present" flag
- jmp short I_Scan ; Go scan for UltraDMA disks to use
- I_RScan mov al,0 ; Rescan - 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)
- 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
- 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
- les bx,[Packet] ; Post driver size & success code
- mov ax,[IVDSLen]
- mov [es:bx+RPSize],ax
- mov [es:bx+RPSize+2],cs
- mov ax,RPDON
- jmp short I_Exit ; Go reload all CPU registers and exit
- 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
- 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
- I_Exit mov [es:bx+RPStat],ax
- pop es ; Reload all CPU registers and exit
- pop ds
- popad
- 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,52 ; Skip I.D. bytes 2-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 dx,MSMsg ; Display "Set to mode" message
- I_Dsply mov ah,9
- int 21h
- clc ; Clear carry (no errors!) and exit
- 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
-
- ; Initialization Messages
-
- TTLMsg db CR,LF,'UDMAJR Disk Driver ',VER,CR,LF,'$'
- 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$'
- PCMsg db 'UDMA controller at PCI address '
- DspAddr db '0000h'
- CRMsg db '.',CR,LF,'$'
- EBMsg db 'Bad EDD BIOS$'
- NDMsg db 'No UDMA disk found$'
- PMMsg db 'Primary-master $'
- PSMsg db 'Primary-slave $'
- SMMsg db 'Secondary-master $'
- SSMsg db 'Secondary-slave $'
- MSMsg db 'disk set to UDMA mode '
- CurMode db '0, ATA-'
- DspMode db '16. ',CR,LF,'$'
- AEMsg db 'absent or non-ATA$'
- DEMsg db 'is not UDMA$'
- LEMsg db 'not in LBA mode$'
- SEMsg db 'set-mode error$'
- Suffix db '; driver NOT loaded!',CR,LF,'$'
-