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