home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2004 September
/
PCWorld_2004-09_cd.bin
/
software
/
vyzkuste
/
stehujemewin
/
stehujemewin.exe
/
udma68.exe
/
UDMA.ASM
next >
Wrap
Assembly Source File
|
2004-01-29
|
47KB
|
1,282 lines
; 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,'$'