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 >
Assembly Source File  |  2004-01-29  |  47KB  |  1,282 lines

  1. ; UDMA.ASM    Written 28-Jan-2004 by Jack R. Ellis
  2. ;
  3. ; UDMA is free software.   You can redistribute and/or modify it, under
  4. ; the terms of the GNU General Public License (hereafter called GPL) as
  5. ; published by the Free Software Foundation, either version 2 of GPL or
  6. ; any later versions, at your option.    UDMA is distributed in the hope
  7. ; that it will be useful, but WITHOUT ANY WARRANTY and without even the
  8. ; implied warranties of MERCHANTABILITY nor of FITNESS FOR A PARTICULAR
  9. ; PURPOSE!  See the GPL for details.  You ought to have received a copy
  10. ; of the GPL with these UDMA files.  If not, write to the Free Software
  11. ; Foundation Inc., 59 Temple Place Ste. 330, Boston, MA 02111-1307 USA.
  12. ; http://www.gnu.org/licenses
  13. ;
  14. ; Special thanks to Luchezar I. Georgiev for his INESTIMABLE advice and
  15. ; help in research, revisions, enhancement and testing of this driver!!
  16. ;
  17. ; This is a DOS driver designed to handle 1 to 4 UltraDMA hard-disks on
  18. ; PC motherboards having a VIA 8235 or equivalent chipset.   The driver
  19. ; determines which of the IDE units are actually UltraDMA hard-disks at
  20. ; initialization and will run all such disks.    All UltraDMA disks from
  21. ; mode 0 ATA-16 thru mode 7 ATA-166 may be used.    An UltraDMA disk is
  22. ; assumed to handle full LBA mode (63 sectors, 255 heads and a designed
  23. ; cylinder count).   "LBA mode" I-O requests are supported for FreeDOS,
  24. ; MS-DOS V7.xx, and other systems that allow them.   LBA values over 28
  25. ; bits shall cause the driver to use "Read/Write Extended" DMA commands
  26. ; and need an ATA-6 or newer hard-disk.      LBA values of 28 bits or less
  27. ; shall use regular DMA commands.   24-bit "CHS mode" is also supported
  28. ; for MS-DOS V6.xx and earlier.      Data accessed using CHS calls must be
  29. ; located in the initial 8-GB of the disk.
  30. ;
  31. ; The driver intercepts BIOS INT13 read or write requests only.      Other
  32. ; INT13 requests (including seeks) and read/write requests with invalid
  33. ; parameters will be "passed" back to the BIOS or some other driver for
  34. ; handling.   If a user I-O buffer is not DWORD aligned, crosses a 64K-
  35. ; boundary or fails a VDS "lock", the I-O request will use a 64K buffer
  36. ; in XMS memory with UltraDMA to or from the buffer, to avoid "passing"
  37. ; these requests to the BIOS for execution in slow PIO mode!   Although
  38. ; UltraDMA specifies word-aligned buffers, ERRATA in some chipsets does
  39. ; require DWORD alignment and avoiding a 64K DMA address boundary!
  40. ;
  41. ; Beginning with version 1.6 of this driver, the following return codes
  42. ; have been added to help in diagnosing "problem" systems and chipsets.
  43. ; On exit from successful I-O requests, the AH-register is zero and the
  44. ; carry flag is reset.     If an error occurs, the carry flag is SET, and
  45. ; the AH-register contains one of the following codes:
  46. ;
  47. ;  Code 08h - DMA timed out
  48. ;    0Fh - DMA error
  49. ;    20h - Controller busy before I-O
  50. ;    21h - Controller busy after I-O
  51. ;    80h - First DRQ timed out
  52. ;    AAh - Disk not ready before I-O
  53. ;    ABh - Disk not ready after I-O
  54. ;    CCh - Write FAULT before I-O
  55. ;    CDh - Write FAULT after I-O
  56. ;    E0h - Hard error at I-O end
  57. ;    FEh - BIOS/driver read MISMATCH (init only)
  58. ;    FFh - XMS memory error
  59. ;
  60. ;
  61. ; Revision History:
  62. ; ----------------
  63. ;  V6.8     28-Jan-04   JE       If no EDD/DPTE, 4 disks at 80h-83h units only
  64. ;  V6.7     16-Jan-04   JE       Renumbered to replace UDMA, init code reduced
  65. ;  V2.2     25-Dec-03   JE       Corrected "read test" diagnostic messages
  66. ;  V2.1     24-Dec-03   JE       Use XMS for read tests, to reduce UDMA size
  67. ;  V2.0     21-Dec-03   JE       Controller-name displays, multi-sector tests
  68. ;  V1.9      6-Dec-03   JE       Fixed VDS init bug, buffer/diagnostic "swap"
  69. ;  V1.8      3-Dec-03   JE       Fixed "STI" bug, "DMA only" now 528 bytes
  70. ;  V1.7     25-Nov-03   JE       If no XMS driver, allow "DMA only" usage
  71. ;  V1.6     22-Nov-03   JE       Fixed init reads, added full error codes
  72. ;  V1.5     15-Nov-03   JE       Added all UDMA init functions but ctlr. name
  73. ;  V1.4     14-Nov-03   JE       Corrected DMA-status reset
  74. ;  V1.3     13-Nov-03   JE       "DoIO" does ALL I-O, "XMS error" now 0FFh
  75. ;  V1.2     12-Nov-03   JE       No "timeout error", other size reductions
  76. ;  V1.1      7-Nov-03   JE       Used 80386 test from V5.9 UDMA
  77. ;  V1.0      6-Nov-03   JE       Initial release (had been named UDMA-E)
  78. ;
  79. ;
  80. ; General Program Equations
  81. ;
  82. %define VER 'V6.8, 28-Jan-2004.'
  83. SECSCYL equ    255*63        ; LBA sectors per cylinder
  84. HEADS    equ    255        ; LBA heads
  85. SECSHD    equ    63        ; LBA sectors per head
  86. RDYTO    equ    8        ; 384-msec minimum I-O timeout
  87. RBYTES    equ    (2*1024*65536*12/14318180+1)*512 ; Read-test byte count
  88.     ; ( = microseconds per tick, but adjusted for binary megabytes)
  89.     ; Number of reads this length per tick = transfer rate in MB/s
  90. RS_SC    equ    RBYTES/512    ; "Read speed" input sector count
  91. RC_SC    equ    58        ; "Read compare" total sector count
  92. RC_CYL    equ    3        ; "Read compare" starting disk address
  93. RC_HD    equ    15
  94. RC_SEC    equ    5
  95. BIOSTMR equ    46Ch        ; BIOS "tick" timer address
  96. HDISKS    equ    475h        ; BIOS hard-disk count address
  97. VDSFLAG equ    47Bh        ; BIOS "Virtual DMA" flag address
  98. CR    equ    0Dh        ; ASCII carriage-return
  99. LF    equ    0Ah        ; ASCII line-feed
  100.  
  101. ; IDE Controller Register Definitions
  102.  
  103. CDATA    equ    1F0h        ; Data port
  104. CSUBCM    equ    CDATA+1        ; Subcommand register
  105. CSECCT    equ    CDATA+2        ; I-O sector count
  106. CDSEL    equ    CDATA+6        ; Disk-select and upper LBA
  107. CCMD    equ    CDATA+7        ; Command register
  108. CSTAT    equ    CDATA+7        ; Primary status register
  109. CSTAT2    equ    CDATA+206h    ; Alternate status register
  110.  
  111. ; Controller Status and Command Definitions
  112.  
  113. BSY    equ    80h        ; IDE controller is busy
  114. RDY    equ    40h        ; IDE disk is "ready"
  115. FLT    equ    20h        ; IDE disk has a "fault"
  116. DRQ    equ    8        ; IDE data request
  117. ERR    equ    1        ; IDE general error flag
  118. DMI    equ    4        ; DMA interrupt has occured
  119. DME    equ    2        ; DMA error has occurred
  120. DRCMD    equ    0C8h        ; DMA read command (write is 0CAh,
  121.                 ;     LBA48 commands are 25h/35h)
  122. SETM    equ    3        ; Set Mode subcommand
  123. SETF    equ    0EFh        ; Set Features command
  124. LBABITS equ    0E0h        ; Fixed high-order LBA commands
  125.  
  126. ; Driver Return Codes
  127.  
  128. DMATIMO equ    008h        ; DMA timeout code
  129. DMAERR    equ    00Fh        ; DMA error   code
  130. CTLRERR equ    020h-FLT    ; Ctlr. busy  code (020h/021h at exit)
  131. DRQTIMO equ    080h        ; DRQ timeout code
  132. DISKERR equ    0AAh-FLT    ; Disk-busy   code (0AAh/0ABh at exit)
  133. WFLTERR equ    0CCh-FLT    ; Write-fault code (0CCh/0CDh at exit)
  134. HARDERR equ    0DFh-FLT    ; Hard-error  code (0E0H at exit)
  135.                 ; (XMS-error  code is 0FFh)
  136.  
  137. ; LBA "Device Address Packet" Layout
  138.  
  139. struc    DAP
  140. DapPL    resb    1        ; Packet length
  141.     resb    1        ; (Reserved)
  142. DapSC    resb    1        ; I-O sector count
  143.     resb    1        ; (Reserved)
  144. DapBuf    resd    1        ; I-O buffer address (roffset & segment)
  145. DapLBA    resd    2        ; Disk logical block address
  146. endstruc
  147.  
  148. ; DOS "Request Packet" layout
  149.  
  150. struc    RP
  151.     resb    2        ; (Unused by us)
  152. RPOp    resb    1        ; Opcode
  153. RPStat    resw    1        ; Status word
  154.     resb    9        ; (Unused by us)
  155. RPSize    resd    1        ; Resident driver size
  156. endstruc
  157. RPERR    equ    8003h        ; "Strategy" packet error flags
  158. RPDON    equ    100h        ; "Strategy" packet done flag
  159.  
  160. ; DOS Driver Device Header
  161.  
  162. @    dd    0FFFFFFFFh    ; Link to next device-header block
  163.     dw    8000h        ; Driver "device attributes"
  164.     dw    Strat        ; "Strategy" routine offset
  165. VLF    equ    $-2        ; (VDS "lock" flag after initialization)
  166. IDEAdr    equ    $-1        ; (Lower IDE status address, after init)
  167.     dw    DevInt        ; "Device-Interrupt" routine offset
  168. PCIAdr    equ    $-2        ; (PCI UDMA command address, after init)
  169.     db    16,16,'UDMA$',0 ; Driver name (arrows avoid user errors)
  170.  
  171. ; Resident Driver Variables
  172.  
  173. XVI    dw    16        ; Constant 16, for 20-bit segment "math"
  174. Units    dd    0FFFFFFFFh    ; IDE "active units" table  (set by init)
  175. PRDAd    dd    IOAdr        ; PRD command-list address  (set by init)
  176.     db    0        ; IDE "upper" sector count  (always zero)
  177. LBAHi    db    0, 0, 0        ; IDE "upper" LBA bits 24-47
  178. SecCt    db    0        ; IDE "lower" sector count  (always used)
  179. LBA    db    0, 0, 0        ; IDE "lower" LBA bits 0-23
  180. DSCmd    db    0        ; IDE disk-select and LBA commands
  181. IOCmd    db    0        ; IDE command byte
  182. XMSHdl    dw    0        ; XMS buffer handle number  (set by init)
  183. XMSOffs dd    0        ; XMS 32-bit buffer offset  (set by init)
  184. IOLen    dd    0        ; XMS and VDS I-O byte count
  185. XMSSH    dw    0        ; XMS source block handle   (00h if DS:SI)
  186. XMSSA    dd    0        ; XMS 32-bit source address (may be DS:SI)
  187. XMSDH    dw    0        ; XMS dest. block handle    (00h if ES:DI)
  188. IOAdr    dd    0        ; XMS dest. & VDS/DMA addr. (may be ES:DI)
  189. VDSOf    equ    XMSSH        ; VDS parameters all SHARE the XMS block!
  190. VDSSg    equ    XMSSA+2
  191. DMALn    dd    80000000h    ; DMA byte count and "end" flag
  192.  
  193. ; Driver Main Routine.     For CHS requests, at entry the registers contain:
  194. ;
  195. ;   AH        Request code.  We handle only 2 read and 3 write
  196. ;   AL        I-O sector count
  197. ;   CH        Lower 8 bits of starting cylinder number
  198. ;   CL        Starting sector number and upper 2 bits of cylinder number
  199. ;   DH        Starting head number
  200. ;   DL        Unit number.   We handle UltraDMA hard-disks of 80h and up
  201. ;   ES:BX   I-O buffer address
  202. ;
  203. ; For LBA requests, at entry the registers contain:
  204. ;
  205. ;   AH        Request code.  We handle only 42h read and 43h write
  206. ;   DL        Unit number.   We handle UltraDMA hard-disks of 80h and up
  207. ;   DS:SI   Pointer to Device Address Packet ("DAP"), described above
  208.  
  209. Entry    pushf            ; Driver entry - save CPU flags
  210.     pusha            ; Save all CPU registers
  211.     mov    bp,4        ; Reset active-units table index
  212. NxtUnit dec    bp        ; Any more active units to check?
  213.     js    QuickEx        ; No, request is not ours - exit quick!
  214.     cmp    dl,[cs:bp+Units-@] ; Does request unit match our table?
  215.     jne    NxtUnit        ; No, see if more table entries remain
  216.     mov    dl,0BEh        ; Mask out LBA and write request bits
  217.     and    dl,ah
  218.     cmp    dl,2        ; Is this a CHS or LBA read or write?
  219.     jne    QuickEx        ; No, exit quick!
  220.     push    ds        ; Save CPU segment registers
  221.     push    es
  222.     shl    ah,1        ; Is this an LBA read or write request?
  223.     jns    CalcCHS        ; No, go calculate CHS disk address
  224.     cmp    dword [si+DapBuf],byte -1 ; 64-bit I-O buffer address?
  225.     jne    GetDAP        ; No, get all "DAP" parameters
  226. NotUs    pop    es        ; Request not for us - reload registers
  227.     pop    ds
  228. QuickEx popa
  229.     popf            ; Reload CPU flags
  230.     jmp    0000:0000    ; "Pass" request back to INT13 chain
  231. @PrvI13 equ    $-4        ; (Previous INT13 vector, set by Init)
  232. GetDAP    mov    al,[si+DapSC]    ; Get "DAP" sector count
  233.     les    cx,[si+DapBuf]    ; Get "DAP" I-O buffer address
  234.     mov    di,[si+DapLBA+4]; Get "DAP" logical-block address
  235.     mov    dx,[si+DapLBA+2]
  236.     mov    si,[si+DapLBA]
  237.     jmp    short CheckSC    ; Go check sector count
  238. CalcCHS xchg    ax,cx        ; CHS - save request code and sectors
  239.     mov    si,SECSHD    ; Get starting sector in SI-register
  240.     and    si,ax
  241.     dec    si
  242.     mov    di,dx        ; Get starting head in DI-reg
  243.     shr    al,6        ; Get cylinder number in AX-reg
  244.     xchg    al,ah
  245.     mov    dx,SECSCYL    ; Convert cylinder to sectors
  246.     mul    dx
  247.     xchg    ax,di        ; Swap low-order and head number
  248.     mov    al,SECSHD    ; Convert head to sectors
  249.     mul    ah
  250.     add    si,ax        ; Add to starting sector
  251.     add    si,di        ; Add in cylinder sectors
  252.     adc    dl,dh
  253.     xchg    ax,bx        ; Get buffer offset in AX-register
  254.     xchg    ax,cx        ; Swap offset with command/sectors
  255.     xor    di,di        ; Reset upper LBA address bits
  256. CheckSC dec    al        ; Is sector count from 1 to 128?
  257.     js    NotUs        ; No?  Let BIOS handle this request!
  258.     sti            ; Valid request - enable interrupts
  259.     push    cs        ; Set our DS-register
  260.     pop    ds
  261.     xor    bx,bx        ; Zero BX-reg. for relative commands
  262.     mov    [bx+LBA+2-@],dl ; Set disk LBA bits 16-47
  263.     mov    [bx+LBAHi-@],dh ; (Bits 0-15 set below for alignment)
  264.     mov    [bx+LBAHi+1-@],di
  265.     shr    dx,12        ; Shift out LBA bits 16-27
  266.     or    di,dx        ; Anything in LBA bits 28-47?
  267.     jz    DoLBA28        ; No, use LBA28 read/write command
  268.     shl    ah,3        ; LBA48 - get request as 20h/30h
  269.     jmp    short GetAddr    ; Go get device-address bytes
  270. DoLBA28 xchg    dh,[bx+LBAHi-@] ; Reload and reset LBA bits 24-27
  271.     or    ah,(DRCMD+1)    ; Get LBA28 read/write command + 5
  272. GetAddr shr    bp,1        ; Get slave-select bit in carry
  273.     mov    bp,(CDSEL-100h) ; Get primary device-address bytes
  274. @PCILo1 equ    $-1        ; (PCI command address, set by init)
  275.     jz    DevAddr        ; Secondary channel I-O request?
  276.     mov    bp,(CDSEL+680h) ; Yes, get secondary address bytes
  277. @PCILo2 equ    $-1        ; (PCI command address, set by init)
  278. DevAddr mov    [bx+IDEAdr-@],bp; Set IDE & PCI device-address bytes
  279.     mov    [bx+LBA-@],si    ; Set disk LBA bits 0-15
  280.     mov    dl,(LBABITS/32) ; Initialize LBA command byte
  281.     rcl    dl,5
  282.     or    dl,dh        ; Put LBA bits 24-27 in LBA command
  283.     mov    dh,5        ; Get final IDE read/write command
  284.     xor    dh,ah
  285.     mov    [bx+DSCmd-@],dx ; Set LBA and IDE command bytes
  286.     cbw            ; Restore sector count to 16 bits
  287.     inc    ax
  288.     mov    [SecCt],al    ; Set I-O sector count
  289.     shl    ax,1        ; Set I-O and DMA byte counts
  290.     mov    [IOLen+1],ax
  291.     mov    [DMALn+1],ax
  292.     mov    [bx+VDSOf-@],cx ; Set 32-bit VDS offset
  293.     mov    [bx+VDSOf+2-@],bx
  294.     mov    [bx+VDSSg-@],es ; Set 16-bit VDS segment
  295.     mov    bp,sp        ; Point BP-reg. to our stack data
  296.     mov    ax,es        ; Get 20-bit buffer segment value
  297.     mul    word [bx+XVI-@]
  298.     add    ax,cx        ; Add in buffer offset value
  299.     adc    dx,bx
  300.     test    al,3        ; Is user's I-O buffer DWORD aligned?
  301.     jnz    GoToBuf        ; No, use buffered I-O logic below
  302.     inc    ax        ; Set "no VDS" buffer-address flag
  303.     mov    [IOAdr],ax    ; Preset 20-bit user buffer address
  304.     mov    [bx+IOAdr+2-@],dx
  305.     mov    ax,8103h    ; Do VDS "lock" of user I-O buffer
  306.     mov    dx,0Ch
  307.     call    VDSLock
  308.     jc    GoToBuf        ; VDS error - use buffered logic
  309.     btr    [bx+IOAdr-@],bx ; Set address bit 0 in VDS "lock" flag
  310.     rcl    byte [bx+VLF-@],1
  311.     mov    ax,[IOLen]    ; Get low-order ending DMA address
  312.     dec    ax        ; (IOLen - 1 + IOAdr)
  313.     add    ax,[bx+IOAdr-@] ; Will this I-O cross a 64K boundary?
  314.     jc    NoLock        ; Yes, use buffered I-O logic below
  315.     call    DoIO        ; Do direct DMA I-O with user's buffer
  316. Done    mov    sp,bp        ; Done - discard "leftover" stack data
  317.     mov    [bp+19],al    ; Set error code in exiting AH-register
  318.     rcr    byte [bp+26],1    ; Set error flag in exiting carry bit
  319.     rol    byte [bp+26],1
  320.     call    VDSUnlk        ; If needed, "unlock" user I-O buffer
  321.     pop    es        ; Reload all CPU registers and exit
  322.     pop    ds
  323.     popa
  324.     popf
  325.     iret
  326. NoLock    call    VDSUnlk        ; Buffered I-O - "unlock" user buffer
  327. GoToBuf jmp    UseBuf        ; Go to buffered I-O routines below
  328.  
  329. ; Subroutine to do VDS "lock" and "unlock" functions
  330.  
  331. VDSUnlk sti            ; Ensure CPU interrupts are enabled!
  332.     sar    byte [bx+VLF-@],1 ; Was user buffer "locked" by VDS?
  333.     jc    VDSExit        ; No, go exit
  334.     mov    ax,8104h    ; Do VDS "unlock" of user I-O buffer
  335.     xor    dx,dx
  336. VDSLock push    ds        ; Point to VDS parameter block
  337.     pop    es
  338.     mov    di,IOLen
  339.     int    4Bh        ; Execute desired VDS function
  340. VDSExit ret            ; Exit
  341.  
  342. ; Subroutine to execute an I-O request
  343.  
  344. BufIO    mov    dword [bx+IOAdr-@],0 ; Buffered - point to XMS memory
  345. @XBufAd equ    $-4        ; (XMS buffer address, set by init)
  346. DoIO    sti            ; Ensure CPU interrupts are enabled!
  347.     cld            ; Ensure FORWARD "string" commands!
  348.     mov    dx,[bx+PCIAdr-@]; Get DMA command-register address
  349.     in    al,dx        ; Ensure any previous DMA is stopped!
  350.     and    al,0FEh        ; (See comments below in driver-init)
  351.     out    dx,al
  352.     push    dx        ; Save command-register address
  353.     mov    al,[DSCmd]    ; Select our desired disk
  354.     and    al,0F0h
  355.     mov    dl,[bx+IDEAdr-@]
  356.     mov    dh,1
  357.     out    dx,al
  358.     mov    di,dx        ; Save IDE drive-select address
  359.     mov    es,bx        ; Point to BIOS timer in low-memory
  360.     mov    si,BIOSTMR
  361.     mov    ah,RDYTO    ; Set AH-reg. with I-O timeout limit
  362.     add    ah,[es:si]
  363.     mov    ch,FLT        ; Check only disk fault after ready
  364.     call    WaitRdy        ; Await controller- and disk-ready
  365.     shr    byte [bp+19],1    ; Get write request bit in carry
  366.     cmc            ; Invert carry so 1 = read, 0 = write
  367.     rcl    al,4        ; Set DMA "read/write" byte
  368.     pop    dx        ; Reload DMA command-register address
  369.     out    dx,al        ; Reset command register and set mode
  370.     push    dx        ; Save DMA command-register address
  371.     inc    dx        ; Point to DMA status register
  372.     inc    dx
  373.     in    al,dx        ; Reset DMA status register
  374.     or    al,6        ; (Done this way so we do NOT alter
  375.     out    dx,al        ;   the "DMA capable" status bits!)
  376.     push    si        ; Save BIOS timer pointer
  377.     inc    dx        ; Set PRD pointer to our DMA address
  378.     inc    dx
  379.     mov    si,PRDAd
  380.     outsd
  381.     mov    cx,1F7h        ; Set IDE parameter-output flags
  382. NxtPar    lea    dx,[di+CSECCT-CDSEL-1] ; Point to IDE sector count -1
  383. IDEPar    inc    dx        ; Output LBA48 IDE parameter bytes
  384.     outsb            ; (If LBA28, 1st 4 get overwritten!)
  385.     shr    cx,1        ; More parameters to go in this group?
  386.     jc    IDEPar        ; Yes, loop back and output next one
  387.     jnz    NxtPar        ; If first 4 output, go do last 6
  388.     pop    si        ; Reload BIOS timer pointer
  389.     mov    dh,3        ; Get IDE alternate-status address
  390.     dec    dx        ; (Primary-status address | 300h - 1)
  391. ChkDRQ    mov    al,DRQTIMO    ; Get DRQ-timeout return code
  392.     cmp    ah,[es:si]    ; Too long without 1st data-request?
  393.     je    Kaput        ; Yes?    Return carry and DRQ timeout!
  394.     in    al,dx        ; Read IDE alternate status
  395.     and    al,DRQ        ; Has 1st data-request arrived?
  396.     jz    ChkDRQ        ; No, loop back and check again
  397.     pop    dx        ; Reload DMA command-register address
  398.     in    al,dx        ; Set DMA Start/Stop bit (starts DMA)
  399.     inc    ax
  400.     out    dx,al
  401. ChkDMA    inc    dx        ; Read DMA controller status
  402.     inc    dx
  403.     in    al,dx
  404.     dec    dx
  405.     dec    dx
  406.     and    al,DMI+DME    ; DMA interrupt or DMA error?
  407.     jnz    StopDMA        ; Yes, stop DMA and check results
  408.     cmp    ah,[es:si]    ; Has our DMA transfer timed out?
  409.     jne    ChkDMA        ; No, loop back and check again
  410. StopDMA push    ax        ; Save ending DMA status
  411.     in    al,dx        ; Reset DMA Start/Stop bit
  412.     and    al,0FEh
  413.     out    dx,al
  414.     pop    ax        ; Reload ending DMA status
  415.     cmp    al,DMI        ; Did DMA end with only an interrupt?
  416.     jne    DMAFail        ; No?  Go check what went wrong
  417.     inc    dx        ; Reread DMA controller status
  418.     inc    dx
  419.     in    al,dx
  420.     test    al,DME        ; Any "late" DMA error after DMA end?
  421.     jnz    PostDMA        ; Yes?    Set DMA-error code and exit
  422.     mov    ch,FLT+ERR    ; Check fault and error after I-O end
  423. WaitRdy lea    dx,[di+CSTAT-CDSEL] ; Point to IDE primary status
  424. ChkRdy    in    al,dx        ; Read IDE primary status
  425.     cmp    ah,[es:si]    ; Too long without becoming ready?
  426.     je    RdyFail        ; Yes?    Go check what went wrong
  427.     test    al,BSY+RDY    ; Controller or disk still busy?
  428.     jle    ChkRdy        ; Yes, loop back and check again
  429.     and    al,ch        ; Disk-fault or hard-error?
  430.     jnz    HdwFail        ; Yes?    Go check what went wrong
  431.     ret            ; All is well - exit
  432. HdwFail test    al,FLT        ; Does the disk show a write-fault?
  433.     mov    ax,(256*WFLTERR)+HARDERR ; Get status-error codes
  434.     jmp    short WhichRC    ; Go see which return code to use
  435. DMAFail test    al,DME        ; Did DMA end with an error?
  436. PostDMA mov    ax,(256*DMAERR)+DMATIMO     ; Get DMA-failure codes
  437.     jmp    short WhichRC    ; Go see which return code to use
  438. RdyFail test    al,BSY        ; Did controller ever become ready?
  439.     mov    ax,(256*CTLRERR)+DISKERR ; Get not-ready return codes
  440. WhichRC jz    ErAtEnd        ; If "zero", use AL-reg. return code
  441.     mov    al,ah        ; Use AH-reg. return code of this pair
  442. ErAtEnd add    al,ch        ; Add 1 if error was at I-O end
  443. Kaput    stc            ; Set carry flag to denote "error"
  444. DoneJmp jmp    Done        ; Go set stack return codes and exit
  445.  
  446. ; Buffered I-O routines, put here so they and the XMSMove subroutine
  447. ;   can be "dismissed" during driver-init if no XMS driver is found!
  448.  
  449. BufOut    call    XMSMove        ; Move user output data to XMS buffer
  450.     call    BufIO        ; Output all data from XMS buffer
  451.     jmp    short DoneJmp    ; Done - go post "success" and exit
  452. UseBuf    shl    dword [bx+VDSOf-@],16 ; Convert to XMS handle/offset
  453.     test    byte [bx+IOCmd-@],12h ; Is this a write request?
  454.     jnz    BufOut        ; Yes, use output routine above
  455.     call    BufIO        ; Input all data to our XMS buffer
  456.     call    XMSMove        ; Move XMS data to user input buffer
  457.     jmp    short DoneJmp    ; Done - go post "success" and exit
  458.  
  459. ; Subroutine to move data to and from the driver's XMS buffer
  460. ; NOTE:     Before entering here, the main routine has converted
  461. ;   our user-buffer offset (VDSOf) to a "null" handle and hi-
  462. ;   order offset, and so the XMS source field ALREADY has the
  463. ;   user-buffer address needed by XMS moves, which simplifies
  464. ;   this routine!  Also, the XMS driver is allowed to control
  465. ;   the A20 line, which "HIMEM.SYS" and other drivers all do!
  466.  
  467. XMSMove sti            ; Ensure CPU interrupts are enabled!
  468.     cld            ; Ensure FORWARD "string" commands!
  469.     push    ds        ; Point ES-reg. to our data
  470.     pop    es
  471.     mov    di,XMSDH    ; Point to XMS destination field
  472.     jnz    XMSOut        ; If output, just set XMS destination!
  473.     mov    si,XMSSH    ; Point to user-buffer address
  474.     movsw            ; Move user-buffer address from
  475.     movsw            ;   XMS source to XMS destination
  476.     movsw
  477.     mov    di,XMSSH    ; Point to XMS source field
  478. XMSOut    mov    si,XMSHdl    ; Set XMS handle and buffer offset as
  479.     movsw            ;   input source or output destination
  480.     movsw
  481.     movsw
  482.     mov    ah,0Bh        ; Move data to or from our XMS buffer
  483.     call    0000:0000    ; (SI-reg. points to IOLen after move)
  484. @XEntry equ    $-4        ; (XMS "entry" address, set by init)
  485.     xor    bx,bx        ; Zero BX-reg. for relative commands
  486.     dec    ax        ; Any errors during XMS move?
  487.     jnz    Kaput        ; Yes?    Return carry and XMS error!
  488.     ret            ; All is well - exit
  489.     align    16
  490. ResEnd    equ    $        ; End of resident driver
  491.  
  492. ; Initialization Variables
  493.  
  494. IVDSLen dd    ResEnd        ; Initialization VDS parameters
  495. IVDSOfs dd    0
  496. IVDSSeg dd    0
  497. IVDSAdr dd    0
  498. Packet    dd    0        ; "Init" request packet address
  499. Bucket    dd    0        ; Working 32-bit "bucket"
  500. HDNames dw    PMMsg        ; Table of hard-disk "name" pointers
  501.     dw    PSMsg
  502.     dw    SMMsg
  503.     dw    SSMsg
  504. Modes    db    '16. '        ; Mode 0 = ATA-16  UltraDMA mode table
  505.     db    '25. '        ; Mode 1 = ATA-25
  506.     db    '33. '        ; Mode 2 = ATA-33
  507.     db    '44. '        ; Mode 3 = ATA-44  (Rare but possible)
  508.     db    '66. '        ; Mode 4 = ATA-66
  509.     db    '100.'        ; Mode 5 = ATA-100
  510.     db    '133.'        ; Mode 6 = ATA-133
  511.     db    '166.'        ; Mode 7 = ATA-166
  512. ErrMsgs db    008h        ; Driver error-message codes/addresses
  513.     dw        EMsg1
  514.     db    00Fh
  515.     dw        EMsg2
  516.     db    020h
  517.     dw        EMsg3
  518.     db    021h
  519.     dw        EMsg4
  520.     db    080h
  521.     dw        EMsg5
  522.     db    0AAh
  523.     dw        EMsg6
  524.     db    0ABh
  525.     dw        EMsg7
  526.     db    0CCh
  527.     dw        EMsg8
  528.     db    0CDh
  529.     dw        EMsg9
  530.     db    0E0h
  531.     dw        EMsg10
  532.     db    0FEh
  533.     dw        EMsg11
  534. EMsgEnd db    0FFh
  535.     dw        EMsg12
  536. ITbl    db    0FAh, 0F0h, 08Ah, 080h ; Interface byte table
  537. ITEnd    equ    $
  538. HDCount db    0        ; Remaining hard-disk count
  539. EDDFlag db    0        ; "EDD BIOS present" flag
  540. HDUnit    db    0        ; Current BIOS unit number
  541. HDIndex db    0        ; IDE "index" number
  542. HDOffs    db    0        ; IDE channel "offset"
  543. HDNibbl db    0        ; IDE drive-select "nibble"
  544. RCSecNo db    0        ; "Read compare" sector address
  545. RCSects db    0        ; "Read compare" remaining sectors
  546.  
  547. ; Main driver-initialization routine, entered from the DOS "device
  548. ;   interrupt" logic below, after it does one-time-only functions
  549.  
  550. I_RScan mov    al,0        ; Load & reset EDD BIOS flag
  551.     xchg    al,[EDDFlag]
  552.     cmp    al,0        ; Were we scanning v.s. DPTE data?
  553.     jne    I_Scan        ; Yes, try hardware-only disk scan
  554. I_Kaput mov    dx,NDMsg    ; Display "No UltraDMA disk" and exit
  555.     jmp    short I_Fail
  556. I_Scan    mov    ax,80h        ; Reset hard-disk unit number & index
  557.     mov    [HDUnit],ax
  558.     mov    byte [HDCount],0; Reset remaining hard-disk count
  559. @BIOSHD equ    $-1        ; (BIOS hard-disk count, set below)
  560.     cmp    byte [EDDFlag],0; Will disk scan use the EDD BIOS?
  561.     jne    I_Next        ; Yes, go start with BIOS unit 80h.
  562.     mov    dx,HOMsg    ; Display "hardware-only" message
  563.     call    I_Dsply
  564. I_Next    movzx    bx,[HDIndex]    ; Get disk unit-number index
  565.     cmp    bh,[EDDFlag]    ; Are we using DPTE data from BIOS?
  566.     je    I_ChnMS        ; No, check disk at "fixed" addresses
  567.     mov    ah,48h        ; Get next BIOS disk's EDD parameters
  568.     mov    dl,[HDUnit]
  569.     mov    si,EDDBuff
  570.     int    13h
  571.     jc    I_NoGud        ; Error?  Display message and exit!
  572.     cmp    dword [si+26],byte -1 ; Valid DPTE pointer?
  573.     je    near I_More    ; No, ignore unit & check for more
  574.     les    si,[si+26]    ; Get this disk's DPTE pointer
  575.     mov    bx,15        ; Calculate DPTE checksum
  576.     mov    al,0
  577. I_CkSum add    al,[es:bx+si]
  578.     dec    bx
  579.     jns    I_CkSum
  580.     cmp    al,0        ; Is DPTE valid (checksum = 0)?
  581.     je    I_EDDOK        ; Yes, use this disk's parameters
  582. I_NoGud mov    dx,EBMsg    ; Display "Invalid EDD BIOS" and exit
  583. I_Fail    jmp    I_Err
  584. I_EDDOK movzx    bx,[es:si+4]    ; Get disk's device-select "nibble"
  585.     shr    bl,4        ; Initialize IDE unit number index
  586.     and    bl,1
  587.     mov    ax,[es:si]    ; Get disk's IDE base address
  588.     cmp    ax,CDATA    ; Is this a primary-channel disk?
  589.     je    I_Index        ; Yes, set disk unit-number index
  590.     cmp    ax,(CDATA-80h)    ; Is this a secondary-channel disk?
  591.     jne    I_More        ; No, ignore unit & check for more
  592.     add    bl,byte 2    ; Adjust for secondary channel
  593. I_Index mov    [HDIndex],bl    ; Set disk's unit number index
  594. I_ChnMS mov    ax,bx        ; Separate channel and master/slave
  595.     shr    al,1
  596.     mov    ah,(LBABITS/32) ; Get drive-select "nibble"
  597.     rcl    ah,5
  598.     ror    al,1        ; Get channel offset (secondary = 80h)
  599.     mov    [HDOffs],ax    ; Set select "nibble" & channel offset
  600.     push    bx        ; Save 16-bit unit number index
  601.     shl    bx,1        ; Get "channel name" message index
  602.     mov    dx,[bx+HDNames] ; Display disk's IDE "channel name"
  603.     call    I_Dsply        ; ("Primary master", etc.)
  604.     mov    ah,8        ; Get BIOS parameters for this disk
  605.     mov    dl,[HDUnit]
  606.     int    13h
  607.     xchg    ax,dx        ; Set AX-reg. with head-number value
  608.     pop    bx        ; Reload unit number index
  609.     mov    dx,LEMsg    ; Point to "not in LBA mode" message
  610.     jc    I_NotU        ; Error - display msg. & ignore disk
  611.     and    cl,SECSHD    ; Clear cylinder bits
  612.     cmp    cl,SECSHD    ; Sectors per cylinder = 63?
  613.     jne    I_NotU        ; No, display message & ignore disk
  614.     cmp    ah,HEADS-1    ; Heads = 255 (max. head = 254)?
  615.     jne    I_NotU        ; No, display message & ignore disk
  616.     mov    al,[HDUnit]    ; Activate this disk in main driver
  617.     mov    [bx+Units-@],al
  618.     push    bx        ; Test for a valid UltraDMA disk
  619.     call    I_TestD
  620.     pop    bx
  621.     jnc    I_More        ; Any errors during disk tests?
  622.     mov    byte [bx+Units-@],0FFh ; Yes?  DELETE disk in driver!
  623. I_NotU    call    I_Dsply        ; Display error for this disk
  624.     mov    dx,CRMsg    ; Display error-message suffix
  625.     call    I_Dsply
  626. I_More    add    word [HDUnit],101h   ; Bump BIOS unit and disk index
  627.     cmp    word [EDDFlag],8400h ; No EDD and all 4 units tested?
  628.     je    I_AnyHD             ; Yes, see if we found any disks
  629.     dec    byte [HDCount]    ; More BIOS disks to check?
  630.     jnz    near I_Next    ; Yes, loop back and do next one
  631. I_AnyHD cmp    dword [Units],byte -1 ; Any active UltraDMA disks?
  632.     je    near I_RScan    ; No, see if we should do a re-scan
  633.     call    I_Hook        ; "Hook" this driver into Int 13h
  634.     les    bx,[Packet]    ; Post driver size & success code
  635.     mov    ax,[IVDSLen]
  636.     mov    [es:bx+RPSize],ax
  637.     mov    [es:bx+RPSize+2],cs
  638.     mov    word [es:bx+RPStat],RPDON
  639.     popad            ; Reload all CPU registers and exit
  640.     pop    es
  641.     pop    ds
  642.     popf
  643.     retf
  644. I_VErr    sti            ; VDS "lock" error!  Enable interrupts
  645.     mov    word [XMSSH],VEMsg ; Point to VDS "lock" error message
  646.     jmp    I_XUnlk        ; Go get rid of our XMS memory
  647. I_Err    mov    [XMSSH],dx    ; Save error message pointer
  648.     shr    byte [IVDSOfs],1; Was driver "locked" by VDS?
  649.     jnc    I_XUnlk        ; No, see if we reserved XMS memory
  650.     push    cs        ; Point to VDS parameter block
  651.     pop    es
  652.     mov    di,IVDSLen
  653.     mov    ax,8104h    ; Do VDS "unlock" of this driver
  654.     xor    dx,dx
  655.     int    4Bh
  656. I_XUnlk mov    dx,[XMSHdl]    ; Get XMS buffer handle
  657.     or    dx,dx        ; Did we reserve XMS memory?
  658.     jz    I_DoErr        ; No, reload message pointer
  659.     mov    ah,0Dh        ; Unlock our XMS memory buffer
  660.     call    far [@XEntry]
  661.     mov    ah,0Ah        ; Free our XMS memory buffer
  662.     mov    dx,[XMSHdl]
  663.     call    far [@XEntry]
  664. I_DoErr mov    dx,[XMSSH]    ; Reload error message pointer
  665. I_ErOut call    I_Dsply        ; Display error message
  666.     popad            ; Reload all 32-bit registers
  667.     push    ax        ; Save all 16-bit registers
  668.     push    bx
  669.     push    cx
  670.     push    dx
  671.     push    si
  672.     push    di
  673. I_Quit    mov    dx,Suffix    ; Display message suffix
  674.     call    I_Dsply
  675.     les    bx,[Packet]    ; Post "null" driver size
  676.     xor    ax,ax
  677.     mov    [es:bx+RPSize],ax
  678.     mov    [es:bx+RPSize+2],cs
  679. I_BadP    mov    ax,RPDON+RPERR    ; Post "error" in init packet
  680.     mov    [es:bx+RPStat],ax
  681.     pop    di        ; Reload all CPU registers and exit
  682.     pop    si
  683.     pop    dx
  684.     pop    cx
  685.     pop    bx
  686.     pop    ax
  687.     pop    es
  688.     pop    ds
  689.     popf
  690.     retf
  691.  
  692. ; Subroutine to do all "validation" tests for an UltraDMA hard-disk
  693.  
  694. I_TestD mov    al,[HDNibbl]    ; Select master or slave disk
  695.     mov    dx,CDSEL
  696.     xor    dl,[HDOffs]
  697.     out    dx,al
  698.     mov    al,0ECh        ; Issue "Identify Device" command
  699.     call    I_Cmd
  700.     jnc    I_PIO        ; If no error, get "identify" data
  701. I_AErr    mov    dx,AEMsg    ; Absent or non-ATA!   Point to msg
  702.     stc            ; Set carry flag (error!) and exit
  703.     ret
  704. I_PIO    mov    dx,CDATA    ; Point to controller PIO data reg
  705.     xor    dl,[HDOffs]
  706.     in    ax,dx        ; Read I.D. bytes 0 and 1
  707.     xchg    ax,si        ; Save "ATA/ATAPI" flag word
  708.     mov    cx,26        ; Skip I.D. bytes 2-53
  709. I_Skip0 in    ax,dx
  710.     loop    I_Skip0
  711.     cld            ; Ensure FORWARD "string" commands!
  712.     push    ds        ; Point to disk-name message
  713.     pop    es
  714.     mov    di,DiskNam
  715.     mov    cl,20        ; Read & swap disk name into message
  716. I_RdNam in    ax,dx        ; (I.D. bytes 54-93)
  717.     xchg    ah,al
  718.     stosw
  719.     loop    I_RdNam
  720.     mov    cl,6        ; Skip I.D. bytes 94-105
  721. I_Skip1 in    ax,dx
  722.     loop    I_Skip1
  723.     in    ax,dx        ; Read I.D. bytes 106 and 107
  724.     mov    bh,al        ; Save "DMA valid" flag byte
  725.     mov    cl,34        ; Skip I.D. bytes 108-175
  726. I_Skip2 in    ax,dx
  727.     loop    I_Skip2
  728.     in    ax,dx        ; Read I.D. bytes 176 and 177
  729.     mov    bl,ah        ; Save "UDMA selected" flag byte
  730.     mov    cl,167        ; Skip remaining I.D. data
  731. I_Skip3 in    ax,dx
  732.     loop    I_Skip3
  733.     shl    si,1        ; Is this an "ATA" hard-disk?
  734.     jc    I_AErr        ; No?  Exit & display message!
  735.     test    bh,4        ; Are UltraDMA flag bits valid?
  736.     jz    I_DErr        ; No?  Exit & display message!
  737.     mov    di,Modes    ; Point to UDMA mode table
  738.     mov    al,'0'        ; Initialize "current mode" value
  739.     mov    cl,2        ; Set rotating mode-check bit
  740.     cmp    bl,1        ; Will disk do UDMA mode 0?
  741.     jae    I_NxtM        ; Yes, find its best UDMA mode
  742. I_DErr    mov    dx,DEMsg    ; Not a UDMA disk!   Point to message
  743. I_SErr    stc            ; Set carry flag (error!) and exit
  744.     ret
  745. I_NxtM    cmp    bl,cl        ; Will disk do next UDMA mode?
  746.     jb    I_GotM        ; No, use previous mode
  747.     inc    ax        ; Set up for next UDMA mode
  748.     add    di,byte 4
  749.     shl    cl,1        ; More UDMA modes to check?
  750.     jnz    I_NxtM        ; Yes, loop back
  751. I_GotM    mov    [CurMode],al    ; Update "current mode" value
  752.     mov    eax,[di]    ; Post UDMA mode in set-mode message
  753.     mov    [DspMode],eax
  754.     mov    dx,CSUBCM    ; Set mode-select subcode
  755.     xor    dl,[HDOffs]
  756.     mov    al,SETM
  757.     out    dx,al
  758.     inc    dx
  759.     mov    al,[CurMode]    ; Set desired UltraDMA mode value
  760.     add    al,10h
  761.     out    dx,al
  762.     mov    al,SETF        ; Issue set-features command to disk
  763.     call    I_Cmd
  764.     mov    dx,SEMsg    ; Point to "Set-mode" error message
  765.     jc    I_SErr        ; If error, set carry flag and exit
  766.     mov    di,DNamEnd    ; Point to end of disk name
  767. I_NextN cmp    di,DiskNam    ; Are we at the disk-name start?
  768.     je    I_NullN        ; Yes, disk name is all spaces!
  769.     cmp    byte [di-1],' ' ; Is preceding byte a space?
  770.     jne    I_TermN        ; No, terminate disk name message
  771.     dec    di        ; Decrement disk name pointer
  772.     jmp    short I_NextN    ; Go see if next byte is a space
  773. I_NullN mov    dword [di],"unna" ; Set "unnamed" as disk name
  774.     mov    dword [di+4],"med "
  775.     add    di,byte 7
  776. I_TermN mov    word [di],".$"    ; Set message terminators after name
  777.     cmp    byte [DspMEnd+1],CR ; Do we have XMS, for read tests?
  778.     je    near I_DispN    ; No, just display disk name and mode
  779.     mov    al,0C3h        ; Disable XMS moves with "ret" command,
  780.     mov    [cs:XMSMove],al ;   "dirty-nasty" but QUICK code change!
  781.     call    I_Hook        ; "Hook" this driver into Int 13h
  782.     call    I_Read        ; Do initial read for synchronization
  783.     jnc    I_RSetC        ; If O.K., set up 4-pass read test
  784. I_RFail push    ax        ; Read error!  Save driver return code
  785.     call    I_RVect        ; Restore original INT 13h vector
  786.     pop    ax        ; Reload driver return code
  787. I_RdErr push    ax        ; Display "FAILED read test" message
  788.     mov    dx,TEMsg
  789.     call    I_Dsply
  790.     pop    ax
  791.     mov    di,(ErrMsgs-3)    ; Point to our error-message table
  792. I_EScan add    di,byte 3    ; Bump to next message code/address
  793.     mov    dx,[di+1]    ; Set message address in DX-register
  794.     cmp    ah,[di]        ; Driver return code = table code?
  795.     je    I_REMsg        ; Yes, this is the message we want!
  796.     cmp    di,EMsgEnd    ; Is driver return code unrecognized?
  797.     jb    I_EScan        ; No, loop back and check next entry
  798. I_RCMsg mov    cx,2        ; Set return code in error message
  799.     mov    si,RCode
  800.     call    HexConv
  801.     mov    dx,RCMsg    ; Point to "Return code" message
  802. I_REMsg stc            ; Set carry flag (error!) and exit
  803.     ret
  804. I_RSetC xor    dx,dx        ; Clear read counter
  805.     xor    si,si        ; Point to BIOS timer in low-memory
  806.     mov    es,si
  807.     mov    si,BIOSTMR
  808.     mov    cl,[es:si]    ; Load current timer tick count LSB
  809. I_RWait cmp    cl,[es:si]    ; Next tick arrived?
  810.     je    I_RWait        ; No, keep waiting
  811.     add    cl,1+4        ; Yes, update and prepare for 4 passes
  812. I_RNext inc    dx        ; Yes, count reads up
  813.     push    es        ; Save timer/counter registers
  814.     push    si
  815.     push    dx
  816.     push    cx
  817.     call    I_Read        ; Read RBYTES into XMS buffer
  818.     pop    cx        ; Reload timer/counter registers
  819.     pop    dx
  820.     pop    si
  821.     pop    es
  822.     jc    I_RFail        ; Read error?  Display message & exit!
  823.     cmp    cl,[es:si]    ; Next timer interrupt arrived?
  824.     jne    I_RNext        ; No, read once more
  825.     shr    dx,2        ; Save average rate for 4 passes
  826.     mov    [Bucket],dx
  827.     mov    al,RC_SC    ; Set "read comparison" parameters
  828.     mov    [RCSects],al
  829.     mov    cx,((RC_CYL*256)+RC_SEC)
  830.     mov    [RCSecNo],cl
  831.     mov    dh,RC_HD
  832.     call    I_Read1        ; Input "read comparison" data to XMS
  833.     jc    I_RFail        ; Read error?  Display message & exit!
  834.     call    I_RVect        ; Restore original INT 13h vector
  835.     cld            ; Set up to initialize XMS block
  836.     push    ds
  837.     pop    es
  838.     mov    di,IOLen
  839.     mov    eax,512        ; Set 1-sector XMS block length
  840.     stosd
  841.     mov    ax,[XMSHdl]    ; Reset XMS "source address"
  842.     stosw
  843.     mov    eax,[XMSOffs]
  844.     stosd
  845.     xor    ax,ax        ; Reset XMS "destination address"
  846.     stosw
  847.     mov    ax,RBuffer+512
  848.     stosw
  849.     mov    ax,ds
  850.     stosw
  851. I_RCSec mov    al,1        ; Set BIOS input sector count of 1
  852.     mov    bx,RBuffer    ; Set BIOS input buffer address
  853.     mov    ch,RC_CYL    ; Set BIOS input disk address
  854.     mov    cl,[RCSecNo]
  855.     mov    dh,RC_HD
  856.     call    I_Read2        ; Have BIOS read next data sector
  857.     jnc    I_RCXMS        ; Any error during BIOS read?
  858.     push    ax        ; Save BIOS return code
  859.     mov    dx,BEMsg    ; Display "failed BIOS read" message
  860.     call    I_Dsply
  861.     pop    ax        ; Reload BIOS return code
  862.     jmp    I_RCMsg        ; Go set up return-code msg. and exit
  863. I_RCXMS mov    ah,0Bh        ; Get next data sector from XMS memory
  864.     mov    si,IOLen
  865.     call    far [@XEntry]
  866.     dec    ax        ; Any errors during XMS move?
  867. I_RCErr jnz    near I_RdErr    ; Yes?    Display DRIVER error and exit!
  868.     cld            ; Set up 256-word data comparison
  869.     mov    cx,256        ; (Done as 16-bit words so interrupts
  870.     mov    si,RBuffer    ;    can remain enabled on older CPUs)
  871.     mov    di,RBuffer+512
  872.     push    ds
  873.     pop    es
  874.     rep    cmpsw        ; Mismatch between BIOS and driver input?
  875.     mov    ah,0FEh        ; (Load "mismatch" return code if so)
  876.     jne    I_RCErr        ; Yes?    Display DRIVER error and exit!
  877.     add    dword [XMSSA],512 ; Advance XMS source to next sector
  878.     inc    byte [RCSecNo]    ; Update BIOS input sector number
  879.     dec    byte [RCSects]    ; More BIOS data to input?
  880.     jnz    I_RCSec        ; Yes, go read next sector
  881. I_DispN mov    dx,DNamMsg    ; Display disk "name" message
  882.     call    I_Dsply
  883.     mov    dx,MSMsg    ; Display "Set to mode" message
  884.     call    I_Dsply
  885.     cmp    byte [DspMEnd+1],CR ; Was no read-test done (no XMS)?
  886.     je    I_TDEnd        ; Yes, just clear carry and exit
  887.     mov    ax,[Bucket]    ; Reload average read rate
  888.     mov    di,DspRate+4    ; Point to read-rate digits message
  889.     mov    byte [di],'0'    ; Initialize read rate digits to 0
  890.     or    ax,ax        ; Did the disk read NOTHING?
  891.     jz    I_Dsply        ; Yes, display read rate = 0
  892.     mov    cx,10        ; CX = divisor
  893. I_ItoA    xor    dx,dx        ; DX:AX = dividend
  894.     div    cx
  895.     xchg    dx,ax        ; DX = quotient, AX = remainder
  896.     add    al,'0'        ; convert to ASCII
  897.     mov    [di],al
  898.     dec    di
  899.     xchg    dx,ax        ; AX = quotient
  900.     or    ax,ax        ; zero?
  901.     jnz    I_ItoA        ; no, continue
  902.     lea    dx,[di+1]    ; Display read-rate message
  903. I_Dsply mov    ah,9
  904.     int    21h
  905. I_TDEnd clc            ; Clear carry (no errors!) and exit
  906.     ret            ; Exit
  907.  
  908. ; Subroutine to do all "read test" inputs
  909.  
  910. I_Read    mov    al,RS_SC    ; "Read speed" - set sector count
  911.     mov    cx,1        ; Set cylinder 0, sector 1
  912.     xor    dh,dh        ; Set head 0
  913. I_Read1 mov    bx,RBuffer+3    ; Use "odd" offset to avoid VDS use
  914. I_Read2 push    ds        ; Point ES-register to our data
  915.     pop    es
  916.     mov    ah,2        ; Set "CHS read" request code
  917.     mov    dl,[HDUnit]    ; Set desired unit number
  918.     int    13h        ; Do desired input from current disk
  919.     ret            ; Exit
  920.  
  921. ; Subroutine to issue initialization commands to our disks
  922.  
  923. I_Cmd    mov    dx,CCMD        ; Issue desired init command
  924.     xor    dl,[HDOffs]
  925.     out    dx,al
  926.     xor    si,si        ; Point to BIOS timer in low-memory
  927.     mov    es,si
  928.     mov    si,BIOSTMR
  929.     mov    cl,RDYTO    ; Set I-O timeout limit in CL-reg
  930.     add    cl,[es:si]
  931. I_CmdW    cmp    cl,[es:si]    ; Has our command timed out?
  932.     je    I_CmdE        ; Yes, set CPU carry flag & exit
  933.     mov    dx,CSTAT    ; Get IDE controller status
  934.     xor    dl,[HDOffs]
  935.     in    al,dx
  936.     test    al,BSY+RDY    ; Controller or disk still busy?
  937.     jle    I_CmdW        ; Yes, loop back and check again
  938.     test    al,ERR        ; Did command cause any errors?
  939.     jz    I_CmdX        ; No, leave carry flag off & exit
  940. I_CmdE    stc            ; Error!  Set CPU carry flag
  941. I_CmdX    ret            ; Exit
  942.  
  943. ; Subroutine to "hook" this driver into the Int 13h chain
  944.  
  945. I_Hook    mov    ax,3513h    ; Get current Int 13h vector
  946.     int    21h
  947.     mov    [@PrvI13],bx    ; Save vector for passed requests
  948.     mov    [@PrvI13+2],es
  949.     mov    ax,2513h    ; "Hook" this driver into Int 13h
  950.     mov    dx,Entry
  951.     int    21h
  952.     ret            ; Exit
  953.  
  954. ; Subroutine to restore the Int 13h vector after initialization reads
  955.  
  956. I_RVect mov    ax,2513h    ; Set back the old Int 13h vector
  957.     lds    dx,[@PrvI13]
  958.     int    21h
  959.     push    cs        ; Reload our DS-register
  960.     pop    ds
  961.     mov    al,0FBh        ; Enable XMS moves with "sti" command
  962.     mov    [cs:XMSMove],al
  963.     ret            ; Exit
  964.  
  965. ; Subroutine to convert a number from hex to ASCII for messages
  966. ;   At entry, the message address is in the SI-reg.   A 2-digit
  967. ;   value is in the AH-reg. and the CX-reg. is equal to 2, or a
  968. ;   4-digit value is in the AX-reg. and the CX-reg. is set to 4
  969.  
  970. HexConv rol    ax,4        ; Rotate next hex digit to low-order
  971.     push    ax        ; Save hex address
  972.     and    al,0Fh        ; Mask off next hex digit
  973.     cmp    al,9        ; Is digit 0-9?
  974.     jbe    HexCnv1        ; Yes, convert to ASCII
  975.     add    al,7        ; Add A-F offset
  976. HexCnv1 add    al,30h        ; Convert digit to ASCII
  977.     mov    [si],al        ; Set next ASCII hex digit in message
  978.     inc    si        ; Bump message address
  979.     pop    ax        ; Reload hex address
  980.     loop    HexConv        ; If more digits to convert, loop back
  981.     ret            ; Exit
  982.  
  983. ; Initialization Buffer Definitions.  The next 1024 bytes beginning at
  984. ;   label "RBuffer", Strategy and Device-Interrupt logic AND the start
  985. ;   of the PCI device tables, are "disposable" and are used once-only.
  986. ;   Then, these 1024 bytes become our two "read-test" input buffers.
  987.  
  988.     align    4
  989. EDDBuff dd    30        ; Start of 30-byte EDD input buffer
  990. RBuffer equ    $        ; Start of "read-test" input buffers
  991.  
  992. ; "Strategy" routine - At entry, ES:BX points to the DOS initialization
  993. ;   request packet, which is saved for processing below
  994.  
  995. Strat    mov    [cs:Packet],bx    ; Save DOS request-packet address
  996.     mov    [cs:Packet+2],es
  997.     retf            ; Exit - await DOS "Device Interrupt"
  998.  
  999. ; "Device-Interrupt" routine - This routine does one-time-only init
  1000. ;   functions, then jumps to the main initialization routine, above
  1001.  
  1002. DevInt    pushf            ; Entry - save all registers
  1003.     push    ds
  1004.     push    es
  1005.     push    ax
  1006.     push    bx
  1007.     push    cx
  1008.     push    dx
  1009.     push    si
  1010.     push    di
  1011.     push    cs        ; Set our DS-reg
  1012.     pop    ds
  1013.     les    bx,[Packet]    ; Point to DOS request packet
  1014.     xor    ax,ax        ; Get a zero for below
  1015.     cmp    [es:bx+RPOp],al ; Is this an "Init" packet?
  1016.     je    I_Title        ; Yes, display our title message
  1017.     jmp    I_BadP        ; Go post errors and exit quick!
  1018. I_Title mov    dx,TTLMsg    ; Display driver "title" message
  1019.     call    I_Dsply
  1020.     pushf            ; 80386 test - save CPU flags
  1021.     push    sp        ; See if CPU is an 80286 or newer
  1022.     pop    ax
  1023.     cmp    ax,sp        ; 80286+ push SP, then decrement it
  1024.     jne    I_Junk        ; CPU is below 80286 - cannot use it!
  1025.     push    word 7000h    ; 80286 or newer - try to set NT|IOPL
  1026.     popf
  1027.     pushf
  1028.     pop    ax
  1029.     test    ah,70h        ; Did any NT|IOPL bits get set?
  1030.     jnz    I_80386        ; Yes, CPU is at least an 80386
  1031. I_Junk    popf            ; Reload starting CPU flags
  1032.     mov    dx,PRMsg    ; Display "Not an 80386" message
  1033.     call    I_Dsply
  1034.     jmp    I_Quit        ; Go display suffix and exit quick!
  1035. I_80386 popf            ; Reload starting CPU flags
  1036.     pop    di        ; Reload all 16-bit registers
  1037.     pop    si
  1038.     pop    dx
  1039.     pop    cx
  1040.     pop    bx
  1041.     pop    ax
  1042.     pushad            ; Save all 32-bit registers
  1043.     xor    edi,edi        ; Get PCI BIOS "I.D." code
  1044.     mov    ax,0B101h
  1045.     int    1Ah
  1046.     cmp    edx,'PCI '    ; Is PCI BIOS V2.0C or later?
  1047.     mov    dx,PEMsg    ; (Get error message pointer if not)
  1048.     jne    I_PCErr        ; No?  Go display message and exit!
  1049.     mov    si,ITbl        ; Point to interface byte table
  1050.     cld            ; Ensure we do "forward" string commands!
  1051. I_GetDv mov    ecx,10100h    ; Look for class 1 (storage) subclass 1 (IDE)
  1052.     lodsb            ; Get next interface byte
  1053.     mov    cl,al
  1054.     push    si        ; Find PCI class code
  1055.     mov    ax,0B103h    ; (Returns bus/device/function in BX-reg.)
  1056.     xor    si,si
  1057.     int    1Ah
  1058.     pop    si
  1059.     jnc    I_GotDv        ; Found our boy!  Go process it
  1060.     cmp    si,ITEnd    ; More interface bytes to try?
  1061.     jb    I_GetDv        ; Yes, try next one
  1062.     mov    dx,NEMsg    ; Baaad news!  Point to error message
  1063.     jmp    short I_PCErr    ; Go display error message and exit
  1064. I_GotDv push    bx        ; Save bus/device/function
  1065.     mov    ax,0B108h    ; Get low-order command byte
  1066.     mov    di,4
  1067.     int    1Ah
  1068.     pop    bx        ; Reload bus/device/function
  1069.     and    cl,5        ; Mask Bus-Master and I-O Space bits
  1070.     cmp    cl,5        ; Are these bits what we found?
  1071.     je    I_BaseA        ; Yes, get our PCI base address
  1072.     mov    dx,MEMsg    ; Baaad news!  Point to error message
  1073. I_PCErr jmp    I_ErOut        ; Go display error message and exit
  1074. I_BaseA push    bx        ; Get PCI base address (register 4)
  1075.     mov    ax,0B109h
  1076.     mov    di,32
  1077.     int    1Ah
  1078.     pop    bx
  1079.     xchg    ax,cx        ; Post run-time PCI UDMA address
  1080.     and    al,0FCh
  1081.     mov    [PCIAdr],ax
  1082.     mov    [@PCILo1],al    ; Set lower PCI device-address bytes
  1083.     add    [@PCILo2],al
  1084.     mov    cx,4        ; Set hex address in display message
  1085.     mov    si,DspAddr
  1086.     call    HexConv
  1087.     push    bx        ; Get Vendor and Device I.D.
  1088.     mov    di,0
  1089.     mov    ax,0B10Ah
  1090.     int    1Ah
  1091.     pop    bx
  1092.     xchg    eax,ecx        ; Save Vendor and Device I.D.
  1093.     mov    [Bucket],eax
  1094.     mov    cx,4        ; Set vendor I.D. in display message
  1095.     mov    si,DspVID
  1096.     call    HexConv
  1097.     mov    eax,[Bucket]    ; Reload Vendor and Device I.D.
  1098.     shr    eax,16        ; Set Device I.D. in display message
  1099.     mov    cl,4
  1100.     mov    si,DspDID
  1101.     call    HexConv
  1102.     mov    ah,bh        ; Set PCI bus number in message
  1103.     mov    cl,2
  1104.     mov    si,DspBus
  1105.     call    HexConv
  1106.     mov    ah,bl        ; Set PCI device number in message
  1107.     shr    ah,3
  1108.     mov    cl,2
  1109.     mov    si,DspDev
  1110.     call    HexConv
  1111.     and    bl,7        ; Set PCI function number in message
  1112.     or    bl,30h
  1113.     mov    [DspFnc],bl
  1114.     mov    dx,PCMsg    ; Display initial controller data
  1115.     call    I_Dsply
  1116.     cld            ; Set up controller-name scan
  1117.     push    ds
  1118.     pop    es
  1119.     mov    eax,[Bucket]    ; Reload Vendor and Device I.D.
  1120.     ror    eax,16        ; "Swap" Vendor I.D. to high-order
  1121.     mov    cx,(dt_end-devtab)/4 ; See if I.D.s are in our table
  1122.     mov    di,devtab
  1123.     repne    scasd        ; Do we know what our controller is?
  1124.     jnz    I_UnknC        ; No, display Vendor/Device numbers
  1125.     mov    dx,di        ; Display the name of our controller
  1126.     call    I_Dsply
  1127.     jmp    short I_TermC    ; Go display controller bus data
  1128. I_UnknC mov    dx,DspUnkn    ; Display Vendor/Device I.D. numbers
  1129.     call    I_Dsply
  1130. I_TermC mov    dx,PCMsg2    ; Display all controller bus data
  1131.     call    I_Dsply
  1132.     mov    ax,4300h    ; Inquire about an XMS driver
  1133.     int    2Fh
  1134.     mov    dx,NXMsg    ; Point to "No XMS driver" message
  1135.     cmp    al,80h        ; Is an XMS driver installed?
  1136.     jne    I_XErr        ; No, display msg. and disable XMS
  1137.     mov    ax,4310h    ; Save XMS driver "entry" address
  1138.     int    2Fh
  1139.     mov    [@XEntry],bx
  1140.     mov    [@XEntry+2],es
  1141.     mov    ah,9        ; Request 128K of XMS memory
  1142.     mov    dx,128
  1143.     call    far [@XEntry]
  1144.     dec    ax        ; Did we get our buffer memory?
  1145.     jnz    I_XMErr        ; No, display msg. and disable XMS
  1146.     mov    [XMSHdl],dx    ; Save our buffer handle
  1147.     mov    ah,0Ch        ; Lock our XMS memory buffer
  1148.     call    far [@XEntry]
  1149.     dec    ax        ; Did buffer get locked?
  1150.     jz    I_XMSOK        ; Yes, save buffer address/offset
  1151.     xor    dx,dx        ; Load and reset our buffer handle
  1152.     xchg    dx,[XMSHdl]
  1153.     mov    ah,0Ah        ; Free our XMS memory buffer
  1154.     call    far [@XEntry]
  1155. I_XMErr mov    dx,XEMsg    ; Point to "XMS memory" message
  1156. I_XErr    call    I_Dsply        ; Display XMS error message
  1157.     mov    dx,NBMsg    ; Display "no buffered I-O" message
  1158.     call    I_Dsply
  1159.     mov    eax,[CRMsg]    ; Disable "read test" msg. display
  1160.     mov    [DspMEnd],eax
  1161.     mov    ax,(NotUs-(GoToBuf+3)) ; Reject buffered I-O with a
  1162.     mov    [GoToBuf+1],ax           ;   dirty-nasty code change!
  1163.     mov    ax,(ResEnd-BufOut)     ; Dismiss all buffered logic
  1164.     sub    [IVDSLen],ax           ;   by cutting driver length
  1165.     jmp    short I_StopD    ; Go stop any previous DMA
  1166. I_XMSOK mov    [Bucket],bx    ; Save 32-bit XMS buffer address
  1167.     mov    [Bucket+2],dx
  1168.     mov    eax,[Bucket]    ; Get XMS buffer address
  1169.     add    eax,65536    ; Find 1st 64K boundary after start
  1170.     xor    ax,ax
  1171.     mov    [@XBufAd],eax    ; Set XMS buffer address and offset
  1172.     sub    eax,[Bucket]
  1173.     mov    [XMSOffs],eax
  1174. I_StopD mov    dx,[PCIAdr]    ; Ensure any previous DMA is stopped
  1175.     in    al,dx        ; (On some older chipsets, if DMA is
  1176.     and    al,0FEh        ;   running, reading an IDE register
  1177.     out    dx,al        ;   causes the controller to HANG!!)
  1178.     add    dx,byte 8    ; Stop secondary-channel DMA, also!
  1179.     in    al,dx
  1180.     and    al,0FEh
  1181.     out    dx,al
  1182.     mov    byte [VLF],0FFh ; Set run-time VDS "lock" flag
  1183.     xor    eax,eax        ; Point ES-reg. to low memory
  1184.     mov    es,ax
  1185.     mov    al,[es:HDISKS]    ; Set BIOS hard-disk count above
  1186.     mov    [@BIOSHD],al
  1187.     cmp    al,0        ; Did BIOS find any hard-disks?
  1188.     je    near I_Kaput    ; No?  Display message and exit!
  1189.     mov    ax,cs        ; Set our code segment in VDS block
  1190.     mov    [IVDSSeg],ax
  1191.     shl    eax,4        ; Get 20-bit driver virtual address
  1192.     cli            ; Avoid interrupts during VDS tests
  1193.     test    byte [es:VDSFLAG],20h ; Are "VDS services" active?
  1194.     jz    I_SetA        ; No, set 20-bit virtual addresses
  1195.     push    cs        ; Point to VDS parameter block
  1196.     pop    es
  1197.     mov    di,IVDSLen
  1198.     mov    ax,8103h    ; "Lock" this driver into memory
  1199.     mov    dx,0Ch
  1200.     int    4Bh
  1201.     jc    near I_VErr    ; Error?  Display error msg. & exit!
  1202.     inc    byte [IVDSOfs]    ; Set initialization VDS "lock" flag
  1203.     mov    eax,[IVDSAdr]    ; Get 32-bit starting driver address
  1204. I_SetA    sti            ; Re-enable CPU interrupts
  1205.     add    [PRDAd],eax    ; Set relocated 32-bit PRD address
  1206.     mov    ah,41h        ; See if this system has an EDD BIOS
  1207.     mov    bx,55AAh
  1208.     mov    dl,80h
  1209.     int    13h
  1210.     jc    I_Part2        ; No, search for disks without EDD
  1211.     cmp    bx,0AA55h    ; Did BIOS "reverse" our entry code?
  1212.     jne    I_Part2        ; No, search for disks without EDD
  1213.     test    cl,4        ; Does BIOS support the EDD subset?
  1214.     jz    I_Part2        ; No, search for disks without EDD
  1215.     inc    byte [EDDFlag]    ; Set "EDD BIOS present" flag
  1216. I_Part2 jmp    I_Scan        ; Go scan for UltraDMA disks to use
  1217.  
  1218. ; PCI vendor and device table. Format: dd <id>, db <dword-aligned name>
  1219.  
  1220.     align    4
  1221. devtab:
  1222. %include "udma.pci"
  1223. dt_end    equ    $
  1224.  
  1225. ; Initialization Messages
  1226.  
  1227. DNamMsg db    'is '        ; Disk-name message (overlays title)
  1228. DiskNam equ    $
  1229. DNamEnd equ    DiskNam+40
  1230. TTLMsg    db    CR,LF,'UDMA Disk Driver ',VER,CR,LF,'$'
  1231. PRMsg    db    'No 80386 CPU$'
  1232. MEMsg    db    'Bus-Master ERROR$'
  1233. NEMsg    db    'No controller found$'
  1234. PEMsg    db    'PCI BIOS too old$'
  1235. NXMsg    db    'No XMS driver$'
  1236. XEMsg    db    'XMS init error$'
  1237. NBMsg    db    '; using only DMA I-O.',CR,LF,'$'
  1238. VEMsg    db    'VDS lock error$'
  1239. DspUnkn db    'Vendor ID '
  1240. DspVID    db    '0000h, device ID '
  1241. DspDID    db    '0000h$'
  1242. PCMsg    db    'UltraDMA controller found at PCI address '
  1243. DspAddr db    '0000h.',CR,LF,'    $'
  1244. PCMsg2    db    ', bus '
  1245. DspBus    db    '00h, device '
  1246. DspDev    db    '00h, function '
  1247. DspFnc    db    '0.',CR,LF,'$'
  1248. EBMsg    db    'Bad EDD BIOS$'
  1249. HOMsg    db    'Doing hardware-only disk scan.',CR,LF,'$'
  1250. NDMsg    db    'No UltraDMA disk to use$'
  1251. PMMsg    db    'Primary-master disk $'
  1252. PSMsg    db    'Primary-slave disk $'
  1253. SMMsg    db    'Secondary-master disk $'
  1254. SSMsg    db    'Secondary-slave disk $'
  1255. MSMsg    db    CR,LF,'    Set to UltraDMA mode '
  1256. CurMode db    '0, ATA-'
  1257. DspMode db    '16.'
  1258. DspMEnd db    '    Read test = $'
  1259. DspRate db    '    0 MB/sec'
  1260. CRMsg    db    '.',CR,LF,'$'
  1261. AEMsg    db    'absent or non-ATA$'
  1262. DEMsg    db    'is not UltraDMA$'
  1263. LEMsg    db    'not in LBA mode$'
  1264. SEMsg    db    'set-mode error$'
  1265. BEMsg    db    'failed BIOS read!  $'
  1266. TEMsg    db    'FAILED read test!  $'
  1267. EMsg1    db    'DMA timed out$'
  1268. EMsg2    db    'DMA error$'
  1269. EMsg3    db    'Controller busy before I-O$'
  1270. EMsg4    db    'Controller busy after I-O$'
  1271. EMsg5    db    'First DRQ timed out$'
  1272. EMsg6    db    'Disk not ready before I-O$'
  1273. EMsg7    db    'Disk not ready after I-O$'
  1274. EMsg8    db    'Write FAULT before I-O$'
  1275. EMsg9    db    'Write FAULT after I-O$'
  1276. EMsg10    db    'Hard error at I-O end$'
  1277. EMsg11    db    'BIOS/driver read MISMATCH$'
  1278. EMsg12    db    'XMS memory error$'
  1279. RCMsg    db    'Return code = '
  1280. RCode    db    '00h$'
  1281. Suffix    db    '; driver NOT loaded!',CR,LF,'$'
  1282.