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 >
Assembly Source File  |  2004-01-29  |  37KB  |  935 lines

  1. ; UDMAJR.ASM -- UDMA "Junior"       Written 28-Jan-2004 by Jack R. Ellis
  2. ;
  3. ; (UDMA for the ROM-disk of 80x86, DOS-based embedded systems)
  4. ;
  5. ; UDMAJR is free software.  You can redistribute and/or modify it under
  6. ; the terms of the GNU General Public License (hereafter called GPL) as
  7. ; published by the Free Software Foundation, either version 2 of GPL or
  8. ; any later versions at your option.  UDMAJR is distributed in the hope
  9. ; that it will be useful, but WITHOUT ANY WARRANTY and without even the
  10. ; implied warranties of MERCHANTABILITY nor of FITNESS FOR A PARTICULAR
  11. ; PURPOSE!  See the GPL for details.  You ought to have received a copy
  12. ; of the GPL with these UDMA files.  If not, write to the Free Software
  13. ; Foundation Inc., 59 Temple Place Ste. 330, Boston, MA 02111-1307 USA.
  14. ; (http://www.gnu.org/licenses/)
  15. ;
  16. ; Special thanks to Luchezar I. Georgiev for his INESTIMABLE advice and
  17. ; help in research, revisions, enhancement and testing of this driver!!
  18. ;
  19. ; This is a DOS driver designed to handle 1 to 4 UltraDMA hard-disks on
  20. ; PC motherboards having a VIA 8235 or equivalent chipset.   The driver
  21. ; determines which of the IDE units are actually UltraDMA hard-disks at
  22. ; initialization and will run all such disks.    All UltraDMA disks from
  23. ; mode 0 ATA-16 thru mode 7 ATA-166 may be used.    An UltraDMA disk is
  24. ; assumed to handle full LBA mode (63 sectors, 255 heads and a designed
  25. ; cylinder count).   "LBA mode" I-O requests are supported for FreeDOS,
  26. ; MS-DOS V7.xx, and other systems that allow them.   LBA values over 28
  27. ; bits shall cause the driver to use "Read/Write Extended" DMA commands
  28. ; and need an ATA-6 or newer hard-disk.      LBA values of 28 bits or less
  29. ; shall use regular DMA commands.   24-bit "CHS mode" is also supported
  30. ; for MS-DOS V6.xx and earlier.      Data accessed using CHS calls must be
  31. ; located in the initial 8-GB of the disk.
  32. ;
  33. ; The driver intercepts BIOS INT13 read or write requests only.      Other
  34. ; INT13 requests (including seeks) and read/write requests with invalid
  35. ; parameters will be "passed" back to the BIOS or some other driver for
  36. ; handling.   If a user I-O buffer is not DWORD aligned, crosses a 64K-
  37. ; boundary or fails a VDS "lock", the I-O request will use a 64K buffer
  38. ; in XMS memory with UltraDMA to or from the buffer, to avoid "passing"
  39. ; these requests to the BIOS for execution in slow PIO mode!   Although
  40. ; UltraDMA specifies word-aligned buffers, ERRATA in some chipsets does
  41. ; require DWORD alignment and avoiding a 64K DMA address boundary!
  42. ;
  43. ; NOTE:      UDMAJR is a "short" variant of the full UDMA driver.      It is
  44. ; intended for "RAM disk" and other space-limited systems.   UDMAJR has
  45. ; all UDMA run-time functions, i.e. it supports up to 4 disks, supports
  46. ; LBA-48 and any earlier address mode, provides all return codes (shown
  47. ; below), and permits "DMA only" use when an XMS driver is not present.
  48. ; To hold UDMAJR.SYS at 2048 bytes, the following items are omitted:
  49. ;   A) Initialization "diagnostic" messages for driver return codes.
  50. ;   B) All initialization read tests and the read-rate display.
  51. ;   C) Hard-disk names and controller "bus" data displays.
  52. ;   D) The check for an 80386 or better CPU.
  53. ; Users who REQUIRE these items should employ the full UDMA driver.
  54. ;
  55. ; Beginning with version 1.6 of this driver, the following return codes
  56. ; have been added to help in diagnosing "problem" systems and chipsets.
  57. ; On exit from successful I-O requests, the AH-register is zero and the
  58. ; carry flag is reset.     If an error occurs, the carry flag is SET, and
  59. ; the AH-register contains one of the following codes:
  60. ;
  61. ;  Code 08h - DMA timed out
  62. ;    0Fh - DMA error
  63. ;    20h - Controller busy before I-O
  64. ;    21h - Controller busy after I-O
  65. ;    80h - First DRQ timed out
  66. ;    AAh - Disk not ready before I-O
  67. ;    ABh - Disk not ready after I-O
  68. ;    CCh - Write FAULT before I-O
  69. ;    CDh - Write FAULT after I-O
  70. ;    E0h - Hard error at I-O end
  71. ;    FFh - XMS memory error
  72. ;
  73. ;
  74. ; Revision History:
  75. ; ----------------
  76. ;  V6.8     28-Jan-04   JE       If no EDD/DPTE, 4 disks at 80h-83h units only
  77. ;  V6.7     16-Jan-04   JE       Renumbered to replace UDMA, init code reduced
  78. ;  V2.2     25-Dec-03   JE       Corrected "read test" messages (UDMA only)
  79. ;  V2.1     24-Dec-03   JE       Use XMS for read tests, to reduce UDMA size
  80. ;  V2.0     21-Dec-03   JE       UDMA controller names, multi-sector tests
  81. ;  V1.9      6-Dec-03   JE       Fixed VDS init bug
  82. ;  V1.8      3-Dec-03   JE       Fixed "STI" bug, "DMA only" now 528 bytes
  83. ;  V1.7     25-Nov-03   JE       Initial release of "S" (short) variant
  84. ;
  85. ;
  86. ; General Program Equations
  87. ;
  88. %define VER 'V6.8, 28-Jan-04.'
  89. SECSCYL equ    255*63        ; LBA sectors per cylinder
  90. HEADS    equ    255        ; LBA heads
  91. SECSHD    equ    63        ; LBA sectors per head
  92. RDYTO    equ    8        ; 384-msec minimum I-O timeout
  93. RBYTES    equ    (2*1024*65536*12/14318180+1)*512 ; Read-test byte count
  94.     ; ( = microseconds per tick, but adjusted for binary megabytes)
  95.     ; Number of reads this length per tick = transfer rate in MB/s
  96. RS_SC    equ    RBYTES/512    ; "Read speed" input sector count
  97. RC_SC    equ    58        ; "Read compare" total sector count
  98. RC_CYL    equ    3        ; "Read compare" starting disk address
  99. RC_HD    equ    15
  100. RC_SEC    equ    5
  101. BIOSTMR equ    46Ch        ; BIOS "tick" timer address
  102. HDISKS    equ    475h        ; BIOS hard-disk count address
  103. VDSFLAG equ    47Bh        ; BIOS "Virtual DMA" flag address
  104. CR    equ    0Dh        ; ASCII carriage-return
  105. LF    equ    0Ah        ; ASCII line-feed
  106.  
  107. ; IDE Controller Register Definitions
  108.  
  109. CDATA    equ    1F0h        ; Data port
  110. CSUBCM    equ    CDATA+1        ; Subcommand register
  111. CSECCT    equ    CDATA+2        ; I-O sector count
  112. CDSEL    equ    CDATA+6        ; Disk-select and upper LBA
  113. CCMD    equ    CDATA+7        ; Command register
  114. CSTAT    equ    CDATA+7        ; Primary status register
  115. CSTAT2    equ    CDATA+206h    ; Alternate status register
  116.  
  117. ; Controller Status and Command Definitions
  118.  
  119. BSY    equ    80h        ; IDE controller is busy
  120. RDY    equ    40h        ; IDE disk is "ready"
  121. FLT    equ    20h        ; IDE disk has a "fault"
  122. DRQ    equ    8        ; IDE data request
  123. ERR    equ    1        ; IDE general error flag
  124. DMI    equ    4        ; DMA interrupt has occured
  125. DME    equ    2        ; DMA error has occurred
  126. DRCMD    equ    0C8h        ; DMA read command (write is 0CAh,
  127.                 ;     LBA48 commands are 25h/35h)
  128. SETM    equ    3        ; Set Mode subcommand
  129. SETF    equ    0EFh        ; Set Features command
  130. LBABITS equ    0E0h        ; Fixed high-order LBA commands
  131.  
  132. ; Driver Return Codes
  133.  
  134. DMATIMO equ    008h        ; DMA timeout code
  135. DMAERR    equ    00Fh        ; DMA error   code
  136. CTLRERR equ    020h-FLT    ; Ctlr. busy  code (020h/021h at exit)
  137. DRQTIMO equ    080h        ; DRQ timeout code
  138. DISKERR equ    0AAh-FLT    ; Disk-busy   code (0AAh/0ABh at exit)
  139. WFLTERR equ    0CCh-FLT    ; Write-fault code (0CCh/0CDh at exit)
  140. HARDERR equ    0DFh-FLT    ; Hard-error  code (0E0H at exit)
  141.                 ; (XMS-error  code is 0FFh)
  142.  
  143. ; LBA "Device Address Packet" Layout
  144.  
  145. struc    DAP
  146. DapPL    resb    1        ; Packet length
  147.     resb    1        ; (Reserved)
  148. DapSC    resb    1        ; I-O sector count
  149.     resb    1        ; (Reserved)
  150. DapBuf    resd    1        ; I-O buffer address (roffset & segment)
  151. DapLBA    resd    2        ; Disk logical block address
  152. endstruc
  153.  
  154. ; DOS "Request Packet" layout
  155.  
  156. struc    RP
  157.     resb    2        ; (Unused by us)
  158. RPOp    resb    1        ; Opcode
  159. RPStat    resw    1        ; Status word
  160.     resb    9        ; (Unused by us)
  161. RPSize    resd    1        ; Resident driver size
  162. endstruc
  163. RPERR    equ    8003h        ; "Strategy" packet error flags
  164. RPDON    equ    100h        ; "Strategy" packet done flag
  165.  
  166. ; DOS Driver Device Header
  167.  
  168. @    dd    0FFFFFFFFh    ; Link to next device-header block
  169.     dw    8000h        ; Driver "device attributes"
  170.     dw    Strat        ; "Strategy" routine offset
  171. VLF    equ    $-2        ; (VDS "lock" flag after initialization)
  172. IDEAdr    equ    $-1        ; (Lower IDE status address, after init)
  173.     dw    DevInt        ; "Device-Interrupt" routine offset
  174. PCIAdr    equ    $-2        ; (PCI UDMA command address, after init)
  175.     db    16,16,'UDMA$',0 ; Driver name (arrows avoid user errors)
  176.  
  177. ; Resident Driver Variables
  178.  
  179. XVI    dw    16        ; Constant 16, for 20-bit segment "math"
  180. Units    dd    0FFFFFFFFh    ; IDE "active units" table  (set by init)
  181. PRDAd    dd    IOAdr        ; PRD command-list address  (set by init)
  182.     db    0        ; IDE "upper" sector count  (always zero)
  183. LBAHi    db    0, 0, 0        ; IDE "upper" LBA bits 24-47
  184. SecCt    db    0        ; IDE "lower" sector count  (always used)
  185. LBA    db    0, 0, 0        ; IDE "lower" LBA bits 0-23
  186. DSCmd    db    0        ; IDE disk-select and LBA commands
  187. IOCmd    db    0        ; IDE command byte
  188. XMSHdl    dw    0        ; XMS buffer handle number  (set by init)
  189. XMSOffs dd    0        ; XMS 32-bit buffer offset  (set by init)
  190. IOLen    dd    0        ; XMS and VDS I-O byte count
  191. XMSSH    dw    0        ; XMS source block handle   (00h if DS:SI)
  192. XMSSA    dd    0        ; XMS 32-bit source address (may be DS:SI)
  193. XMSDH    dw    0        ; XMS dest. block handle    (00h if ES:DI)
  194. IOAdr    dd    0        ; XMS dest. & VDS/DMA addr. (may be ES:DI)
  195. VDSOf    equ    XMSSH        ; VDS parameters all SHARE the XMS block!
  196. VDSSg    equ    XMSSA+2
  197. DMALn    dd    80000000h    ; DMA byte count and "end" flag
  198.  
  199. ; Driver Main Routine.     For CHS requests, at entry the registers contain:
  200. ;
  201. ;   AH        Request code.  We handle only 2 read and 3 write
  202. ;   AL        I-O sector count
  203. ;   CH        Lower 8 bits of starting cylinder number
  204. ;   CL        Starting sector number and upper 2 bits of cylinder number
  205. ;   DH        Starting head number
  206. ;   DL        Unit number.   We handle UltraDMA hard-disks of 80h and up
  207. ;   ES:BX   I-O buffer address
  208. ;
  209. ; For LBA requests, at entry the registers contain:
  210. ;
  211. ;   AH        Request code.  We handle only 42h read and 43h write
  212. ;   DL        Unit number.   We handle UltraDMA hard-disks of 80h and up
  213. ;   DS:SI   Pointer to Device Address Packet ("DAP"), described above
  214.  
  215. Entry    pushf            ; Driver entry - save CPU flags
  216.     pusha            ; Save all CPU registers
  217.     mov    bp,4        ; Reset active-units table index
  218. NxtUnit dec    bp        ; Any more active units to check?
  219.     js    QuickEx        ; No, request is not ours - exit quick!
  220.     cmp    dl,[cs:bp+Units-@] ; Does request unit match our table?
  221.     jne    NxtUnit        ; No, see if more table entries remain
  222.     mov    dl,0BEh        ; Mask out LBA and write request bits
  223.     and    dl,ah
  224.     cmp    dl,2        ; Is this a CHS or LBA read or write?
  225.     jne    QuickEx        ; No, exit quick!
  226.     push    ds        ; Save CPU segment registers
  227.     push    es
  228.     shl    ah,1        ; Is this an LBA read or write request?
  229.     jns    CalcCHS        ; No, go calculate CHS disk address
  230.     cmp    dword [si+DapBuf],byte -1 ; 64-bit I-O buffer address?
  231.     jne    GetDAP        ; No, get all "DAP" parameters
  232. NotUs    pop    es        ; Request not for us - reload registers
  233.     pop    ds
  234. QuickEx popa
  235.     popf            ; Reload CPU flags
  236.     jmp    0000:0000    ; "Pass" request back to INT13 chain
  237. @PrvI13 equ    $-4        ; (Previous INT13 vector, set by Init)
  238. GetDAP    mov    al,[si+DapSC]    ; Get "DAP" sector count
  239.     les    cx,[si+DapBuf]    ; Get "DAP" I-O buffer address
  240.     mov    di,[si+DapLBA+4]; Get "DAP" logical-block address
  241.     mov    dx,[si+DapLBA+2]
  242.     mov    si,[si+DapLBA]
  243.     jmp    short CheckSC    ; Go check sector count
  244. CalcCHS xchg    ax,cx        ; CHS - save request code and sectors
  245.     mov    si,SECSHD    ; Get starting sector in SI-register
  246.     and    si,ax
  247.     dec    si
  248.     mov    di,dx        ; Get starting head in DI-reg
  249.     shr    al,6        ; Get cylinder number in AX-reg
  250.     xchg    al,ah
  251.     mov    dx,SECSCYL    ; Convert cylinder to sectors
  252.     mul    dx
  253.     xchg    ax,di        ; Swap low-order and head number
  254.     mov    al,SECSHD    ; Convert head to sectors
  255.     mul    ah
  256.     add    si,ax        ; Add to starting sector
  257.     add    si,di        ; Add in cylinder sectors
  258.     adc    dl,dh
  259.     xchg    ax,bx        ; Get buffer offset in AX-register
  260.     xchg    ax,cx        ; Swap offset with command/sectors
  261.     xor    di,di        ; Reset upper LBA address bits
  262. CheckSC dec    al        ; Is sector count from 1 to 128?
  263.     js    NotUs        ; No?  Let BIOS handle this request!
  264.     sti            ; Valid request - enable interrupts
  265.     push    cs        ; Set our DS-register
  266.     pop    ds
  267.     xor    bx,bx        ; Zero BX-reg. for relative commands
  268.     mov    [bx+LBA+2-@],dl ; Set disk LBA bits 16-47
  269.     mov    [bx+LBAHi-@],dh ; (Bits 0-15 set below for alignment)
  270.     mov    [bx+LBAHi+1-@],di
  271.     shr    dx,12        ; Shift out LBA bits 16-27
  272.     or    di,dx        ; Anything in LBA bits 28-47?
  273.     jz    DoLBA28        ; No, use LBA28 read/write command
  274.     shl    ah,3        ; LBA48 - get request as 20h/30h
  275.     jmp    short GetAddr    ; Go get device-address bytes
  276. DoLBA28 xchg    dh,[bx+LBAHi-@] ; Reload and reset LBA bits 24-27
  277.     or    ah,(DRCMD+1)    ; Get LBA28 read/write command + 5
  278. GetAddr shr    bp,1        ; Get slave-select bit in carry
  279.     mov    bp,(CDSEL-100h) ; Get primary device-address bytes
  280. @PCILo1 equ    $-1        ; (PCI command address, set by init)
  281.     jz    DevAddr        ; Secondary channel I-O request?
  282.     mov    bp,(CDSEL+680h) ; Yes, get secondary address bytes
  283. @PCILo2 equ    $-1        ; (PCI command address, set by init)
  284. DevAddr mov    [bx+IDEAdr-@],bp; Set IDE & PCI device-address bytes
  285.     mov    [bx+LBA-@],si    ; Set disk LBA bits 0-15
  286.     mov    dl,(LBABITS/32) ; Initialize LBA command byte
  287.     rcl    dl,5
  288.     or    dl,dh        ; Put LBA bits 24-27 in LBA command
  289.     mov    dh,5        ; Get final IDE read/write command
  290.     xor    dh,ah
  291.     mov    [bx+DSCmd-@],dx ; Set LBA and IDE command bytes
  292.     cbw            ; Restore sector count to 16 bits
  293.     inc    ax
  294.     mov    [SecCt],al    ; Set I-O sector count
  295.     shl    ax,1        ; Set I-O and DMA byte counts
  296.     mov    [IOLen+1],ax
  297.     mov    [DMALn+1],ax
  298.     mov    [bx+VDSOf-@],cx ; Set 32-bit VDS offset
  299.     mov    [bx+VDSOf+2-@],bx
  300.     mov    [bx+VDSSg-@],es ; Set 16-bit VDS segment
  301.     mov    bp,sp        ; Point BP-reg. to our stack data
  302.     mov    ax,es        ; Get 20-bit buffer segment value
  303.     mul    word [bx+XVI-@]
  304.     add    ax,cx        ; Add in buffer offset value
  305.     adc    dx,bx
  306.     test    al,3        ; Is user's I-O buffer DWORD aligned?
  307.     jnz    GoToBuf        ; No, use buffered I-O logic below
  308.     inc    ax        ; Set "no VDS" buffer-address flag
  309.     mov    [IOAdr],ax    ; Preset 20-bit user buffer address
  310.     mov    [bx+IOAdr+2-@],dx
  311.     mov    ax,8103h    ; Do VDS "lock" of user I-O buffer
  312.     mov    dx,0Ch
  313.     call    VDSLock
  314.     jc    GoToBuf        ; VDS error - use buffered logic
  315.     btr    [bx+IOAdr-@],bx ; Set address bit 0 in VDS "lock" flag
  316.     rcl    byte [bx+VLF-@],1
  317.     mov    ax,[IOLen]    ; Get low-order ending DMA address
  318.     dec    ax        ; (IOLen - 1 + IOAdr)
  319.     add    ax,[bx+IOAdr-@] ; Will this I-O cross a 64K boundary?
  320.     jc    NoLock        ; Yes, use buffered I-O logic below
  321.     call    DoIO        ; Do direct DMA I-O with user's buffer
  322. Done    mov    sp,bp        ; Done - discard "leftover" stack data
  323.     mov    [bp+19],al    ; Set error code in exiting AH-register
  324.     rcr    byte [bp+26],1    ; Set error flag in exiting carry bit
  325.     rol    byte [bp+26],1
  326.     call    VDSUnlk        ; If needed, "unlock" user I-O buffer
  327.     pop    es        ; Reload all CPU registers and exit
  328.     pop    ds
  329.     popa
  330.     popf
  331.     iret
  332. NoLock    call    VDSUnlk        ; Buffered I-O - "unlock" user buffer
  333. GoToBuf jmp    UseBuf        ; Go to buffered I-O routines below
  334.  
  335. ; Subroutine to do VDS "lock" and "unlock" functions
  336.  
  337. VDSUnlk sti            ; Ensure CPU interrupts are enabled!
  338.     sar    byte [bx+VLF-@],1 ; Was user buffer "locked" by VDS?
  339.     jc    VDSExit        ; No, go exit
  340.     mov    ax,8104h    ; Do VDS "unlock" of user I-O buffer
  341.     xor    dx,dx
  342. VDSLock push    ds        ; Point to VDS parameter block
  343.     pop    es
  344.     mov    di,IOLen
  345.     int    4Bh        ; Execute desired VDS function
  346. VDSExit ret            ; Exit
  347.  
  348. ; Subroutine to execute an I-O request
  349.  
  350. BufIO    mov    dword [bx+IOAdr-@],0 ; Buffered - point to XMS memory
  351. @XBufAd equ    $-4        ; (XMS buffer address, set by init)
  352. DoIO    sti            ; Ensure CPU interrupts are enabled!
  353.     cld            ; Ensure FORWARD "string" commands!
  354.     mov    dx,[bx+PCIAdr-@]; Get DMA command-register address
  355.     in    al,dx        ; Ensure any previous DMA is stopped!
  356.     and    al,0FEh        ; (See comments below in driver-init)
  357.     out    dx,al
  358.     push    dx        ; Save command-register address
  359.     mov    al,[DSCmd]    ; Select our desired disk
  360.     and    al,0F0h
  361.     mov    dl,[bx+IDEAdr-@]
  362.     mov    dh,1
  363.     out    dx,al
  364.     mov    di,dx        ; Save IDE drive-select address
  365.     mov    es,bx        ; Point to BIOS timer in low-memory
  366.     mov    si,BIOSTMR
  367.     mov    ah,RDYTO    ; Set AH-reg. with I-O timeout limit
  368.     add    ah,[es:si]
  369.     mov    ch,FLT        ; Check only disk fault after ready
  370.     call    WaitRdy        ; Await controller- and disk-ready
  371.     shr    byte [bp+19],1    ; Get write request bit in carry
  372.     cmc            ; Invert carry so 1 = read, 0 = write
  373.     rcl    al,4        ; Set DMA "read/write" byte
  374.     pop    dx        ; Reload DMA command-register address
  375.     out    dx,al        ; Reset command register and set mode
  376.     push    dx        ; Save DMA command-register address
  377.     inc    dx        ; Point to DMA status register
  378.     inc    dx
  379.     in    al,dx        ; Reset DMA status register
  380.     or    al,6        ; (Done this way so we do NOT alter
  381.     out    dx,al        ;   the "DMA capable" status bits!)
  382.     push    si        ; Save BIOS timer pointer
  383.     inc    dx        ; Set PRD pointer to our DMA address
  384.     inc    dx
  385.     mov    si,PRDAd
  386.     outsd
  387.     mov    cx,1F7h        ; Set IDE parameter-output flags
  388. NxtPar    lea    dx,[di+CSECCT-CDSEL-1] ; Point to IDE sector count -1
  389. IDEPar    inc    dx        ; Output LBA48 IDE parameter bytes
  390.     outsb            ; (If LBA28, 1st 4 get overwritten!)
  391.     shr    cx,1        ; More parameters to go in this group?
  392.     jc    IDEPar        ; Yes, loop back and output next one
  393.     jnz    NxtPar        ; If first 4 output, go do last 6
  394.     pop    si        ; Reload BIOS timer pointer
  395.     mov    dh,3        ; Get IDE alternate-status address
  396.     dec    dx        ; (Primary-status address | 300h - 1)
  397. ChkDRQ    mov    al,DRQTIMO    ; Get DRQ-timeout return code
  398.     cmp    ah,[es:si]    ; Too long without 1st data-request?
  399.     je    Kaput        ; Yes?    Return carry and DRQ timeout!
  400.     in    al,dx        ; Read IDE alternate status
  401.     and    al,DRQ        ; Has 1st data-request arrived?
  402.     jz    ChkDRQ        ; No, loop back and check again
  403.     pop    dx        ; Reload DMA command-register address
  404.     in    al,dx        ; Set DMA Start/Stop bit (starts DMA)
  405.     inc    ax
  406.     out    dx,al
  407. ChkDMA    inc    dx        ; Read DMA controller status
  408.     inc    dx
  409.     in    al,dx
  410.     dec    dx
  411.     dec    dx
  412.     and    al,DMI+DME    ; DMA interrupt or DMA error?
  413.     jnz    StopDMA        ; Yes, stop DMA and check results
  414.     cmp    ah,[es:si]    ; Has our DMA transfer timed out?
  415.     jne    ChkDMA        ; No, loop back and check again
  416. StopDMA push    ax        ; Save ending DMA status
  417.     in    al,dx        ; Reset DMA Start/Stop bit
  418.     and    al,0FEh
  419.     out    dx,al
  420.     pop    ax        ; Reload ending DMA status
  421.     cmp    al,DMI        ; Did DMA end with only an interrupt?
  422.     jne    DMAFail        ; No?  Go check what went wrong
  423.     inc    dx        ; Reread DMA controller status
  424.     inc    dx
  425.     in    al,dx
  426.     test    al,DME        ; Any "late" DMA error after DMA end?
  427.     jnz    PostDMA        ; Yes?    Set DMA-error code and exit
  428.     mov    ch,FLT+ERR    ; Check fault and error after I-O end
  429. WaitRdy lea    dx,[di+CSTAT-CDSEL] ; Point to IDE primary status
  430. ChkRdy    in    al,dx        ; Read IDE primary status
  431.     cmp    ah,[es:si]    ; Too long without becoming ready?
  432.     je    RdyFail        ; Yes?    Go check what went wrong
  433.     test    al,BSY+RDY    ; Controller or disk still busy?
  434.     jle    ChkRdy        ; Yes, loop back and check again
  435.     and    al,ch        ; Disk-fault or hard-error?
  436.     jnz    HdwFail        ; Yes?    Go check what went wrong
  437.     ret            ; All is well - exit
  438. HdwFail test    al,FLT        ; Does the disk show a write-fault?
  439.     mov    ax,(256*WFLTERR)+HARDERR ; Get status-error codes
  440.     jmp    short WhichRC    ; Go see which return code to use
  441. DMAFail test    al,DME        ; Did DMA end with an error?
  442. PostDMA mov    ax,(256*DMAERR)+DMATIMO     ; Get DMA-failure codes
  443.     jmp    short WhichRC    ; Go see which return code to use
  444. RdyFail test    al,BSY        ; Did controller ever become ready?
  445.     mov    ax,(256*CTLRERR)+DISKERR ; Get not-ready return codes
  446. WhichRC jz    ErAtEnd        ; If "zero", use AL-reg. return code
  447.     mov    al,ah        ; Use AH-reg. return code of this pair
  448. ErAtEnd add    al,ch        ; Add 1 if error was at I-O end
  449. Kaput    stc            ; Set carry flag to denote "error"
  450. DoneJmp jmp    Done        ; Go set stack return codes and exit
  451.  
  452. ; Buffered I-O routines, put here so they and the XMSMove subroutine
  453. ;   can be "dismissed" during driver-init if no XMS driver is found!
  454.  
  455. BufOut    call    XMSMove        ; Move user output data to XMS buffer
  456.     call    BufIO        ; Output all data from XMS buffer
  457.     jmp    short DoneJmp    ; Done - go post "success" and exit
  458. UseBuf    shl    dword [bx+VDSOf-@],16 ; Convert to XMS handle/offset
  459.     test    byte [bx+IOCmd-@],12h ; Is this a write request?
  460.     jnz    BufOut        ; Yes, use output routine above
  461.     call    BufIO        ; Input all data to our XMS buffer
  462.     call    XMSMove        ; Move XMS data to user input buffer
  463.     jmp    short DoneJmp    ; Done - go post "success" and exit
  464.  
  465. ; Subroutine to move data to and from the driver's XMS buffer
  466. ; NOTE:     Before entering here, the main routine has converted
  467. ;   our user-buffer offset (VDSOf) to a "null" handle and hi-
  468. ;   order offset, and so the XMS source field ALREADY has the
  469. ;   user-buffer address needed by XMS moves, which simplifies
  470. ;   this routine!  Also, the XMS driver is allowed to control
  471. ;   the A20 line, which "HIMEM.SYS" and other drivers all do!
  472.  
  473. XMSMove sti            ; Ensure CPU interrupts are enabled!
  474.     cld            ; Ensure FORWARD "string" commands!
  475.     push    ds        ; Point ES-reg. to our data
  476.     pop    es
  477.     mov    di,XMSDH    ; Point to XMS destination field
  478.     jnz    XMSOut        ; If output, just set XMS destination!
  479.     mov    si,XMSSH    ; Point to user-buffer address
  480.     movsw            ; Move user-buffer address from
  481.     movsw            ;   XMS source to XMS destination
  482.     movsw
  483.     mov    di,XMSSH    ; Point to XMS source field
  484. XMSOut    mov    si,XMSHdl    ; Set XMS handle and buffer offset as
  485.     movsw            ;   input source or output destination
  486.     movsw
  487.     movsw
  488.     mov    ah,0Bh        ; Move data to or from our XMS buffer
  489.     call    0000:0000    ; (SI-reg. points to IOLen after move)
  490. @XEntry equ    $-4        ; (XMS "entry" address, set by init)
  491.     xor    bx,bx        ; Zero BX-reg. for relative commands
  492.     dec    ax        ; Any errors during XMS move?
  493.     jnz    Kaput        ; Yes?    Return carry and XMS error!
  494.     ret            ; All is well - exit
  495.     align    16
  496. ResEnd    equ    $        ; End of resident driver
  497.  
  498. ; Initialization Variables
  499.  
  500. IVDSLen dd    ResEnd        ; Initialization VDS parameters
  501. IVDSOfs dd    0
  502. IVDSSeg dd    0
  503. IVDSAdr dd    0
  504. Packet    dd    0        ; "Init" request packet address
  505. Bucket    dd    0        ; Working 32-bit "bucket"
  506. HDNames dw    PMMsg        ; Table of hard-disk "name" pointers
  507.     dw    PSMsg
  508.     dw    SMMsg
  509.     dw    SSMsg
  510. Modes    db    '16. '        ; Mode 0 = ATA-16  UltraDMA mode table
  511.     db    '25. '        ; Mode 1 = ATA-25
  512.     db    '33. '        ; Mode 2 = ATA-33
  513.     db    '44. '        ; Mode 3 = ATA-44  (Rare but possible)
  514.     db    '66. '        ; Mode 4 = ATA-66
  515.     db    '100.'        ; Mode 5 = ATA-100
  516.     db    '133.'        ; Mode 6 = ATA-133
  517.     db    '166.'        ; Mode 7 = ATA-166
  518. ITbl    db    0FAh, 0F0h, 08Ah, 080h ; Interface byte table
  519. ITEnd    equ    $
  520. HDCount db    0        ; Remaining hard-disk count
  521. EDDFlag db    0        ; "EDD BIOS present" flag
  522. HDUnit    db    0        ; Current BIOS unit number
  523. HDIndex db    0        ; IDE "index" number
  524. HDOffs    db    0        ; IDE channel "offset"
  525. HDNibbl db    0        ; IDE drive-select "nibble"
  526. RCSecNo db    0        ; "Read compare" sector address
  527. RCSects db    0        ; "Read compare" remaining sectors
  528. EDDBuff dd    30        ; Start of 30-byte EDD input buffer
  529.  
  530. ; "Strategy" routine - At entry, ES:BX points to the DOS initialization
  531. ;   request packet, which is saved for processing below
  532.  
  533. Strat    mov    [cs:Packet],bx    ; Save DOS request-packet address
  534.     mov    [cs:Packet+2],es
  535.     retf            ; Exit - await DOS "Device Interrupt"
  536.  
  537. ; "Device-Interrupt" routine - This routine does one-time-only init
  538. ;   functions, then jumps to the main initialization routine, above
  539.  
  540. DevInt    pushf            ; Entry - save all registers
  541.     pushad
  542.     push    ds
  543.     push    es
  544.     push    cs        ; Set our DS-reg
  545.     pop    ds
  546.     les    bx,[Packet]    ; Point to DOS request packet
  547.     cmp    byte [es:bx+RPOp],0 ; Is this an "Init" packet?
  548.     jne    near I_BadP    ; No? Go post errors and exit quick!
  549. I_Title mov    dx,TTLMsg    ; Display driver "title" message
  550.     call    I_Dsply
  551.     xor    edi,edi        ; Get PCI BIOS "I.D." code
  552.     mov    ax,0B101h
  553.     int    1Ah
  554.     cmp    edx,'PCI '    ; Is PCI BIOS V2.0C or later?
  555.     mov    dx,PEMsg    ; (Get error message pointer if not)
  556.     jne    I_PCErr        ; No?  Go display message and exit!
  557.     mov    si,ITbl        ; Point to interface byte table
  558.     cld            ; Ensure we do "forward" string commands!
  559. I_GetDv mov    ecx,10100h    ; Look for class 1 (storage) subclass 1 (IDE)
  560.     lodsb            ; Get next interface byte
  561.     mov    cl,al
  562.     push    si        ; Find PCI class code
  563.     mov    ax,0B103h    ; (Returns bus/device/function in BX-reg.)
  564.     xor    si,si
  565.     int    1Ah
  566.     pop    si
  567.     jnc    I_GotDv        ; Found our boy!  Go process it
  568.     cmp    si,ITEnd    ; More interface bytes to try?
  569.     jb    I_GetDv        ; Yes, try next one
  570.     mov    dx,NEMsg    ; Baaad news!  Point to error message
  571.     jmp    short I_PCErr    ; Go display error message and exit
  572. I_GotDv push    bx        ; Save bus/device/function
  573.     mov    ax,0B108h    ; Get low-order command byte
  574.     mov    di,4
  575.     int    1Ah
  576.     pop    bx        ; Reload bus/device/function
  577.     and    cl,5        ; Mask Bus-Master and I-O Space bits
  578.     cmp    cl,5        ; Are these bits what we found?
  579.     je    I_BaseA        ; Yes, get our PCI base address
  580.     mov    dx,MEMsg    ; Baaad news!  Point to error message
  581. I_PCErr jmp    I_ErOut        ; Go display error message and exit
  582. I_BaseA push    bx        ; Get PCI base address (register 4)
  583.     mov    ax,0B109h
  584.     mov    di,32
  585.     int    1Ah
  586.     pop    bx
  587.     xchg    ax,cx        ; Post run-time PCI UDMA address
  588.     and    al,0FCh
  589.     mov    [PCIAdr],ax
  590.     mov    [@PCILo1],al    ; Set lower PCI device-address bytes
  591.     add    [@PCILo2],al
  592.     mov    cx,4        ; Set hex address in display message
  593.     mov    si,DspAddr
  594. I_HexC    rol    ax,4        ; Rotate next hex digit to low-order
  595.     push    ax        ; Save hex address
  596.     and    al,0Fh        ; Mask off next hex digit
  597.     cmp    al,9        ; Is digit 0-9?
  598.     jbe    I_HexC1        ; Yes, convert to ASCII
  599.     add    al,7        ; Add A-F offset
  600. I_HexC1 add    al,30h        ; Convert digit to ASCII
  601.     mov    [si],al        ; Set next ASCII hex digit in message
  602.     inc    si        ; Bump message address
  603.     pop    ax        ; Reload hex address
  604.     loop    I_HexC        ; If more digits to convert, loop back
  605.     mov    dx,PCMsg    ; Display controller-address message
  606.     call    I_Dsply
  607.     mov    ax,4300h    ; Inquire about an XMS driver
  608.     int    2Fh
  609.     mov    dx,NXMsg    ; Point to "No XMS driver" message
  610.     cmp    al,80h        ; Is an XMS driver installed?
  611.     jne    I_XErr        ; No, display msg. and disable XMS
  612.     mov    ax,4310h    ; Save XMS driver "entry" address
  613.     int    2Fh
  614.     mov    [@XEntry],bx
  615.     mov    [@XEntry+2],es
  616.     mov    ah,9        ; Request 128K of XMS memory
  617.     mov    dx,128
  618.     call    far [@XEntry]
  619.     dec    ax        ; Did we get our buffer memory?
  620.     jnz    I_XMErr        ; No, display msg. and disable XMS
  621.     mov    [XMSHdl],dx    ; Save our buffer handle
  622.     mov    ah,0Ch        ; Lock our XMS memory buffer
  623.     call    far [@XEntry]
  624.     dec    ax        ; Did buffer get locked?
  625.     jz    I_XMSOK        ; Yes, save buffer address/offset
  626.     xor    dx,dx        ; Load and reset our buffer handle
  627.     xchg    dx,[XMSHdl]
  628.     mov    ah,0Ah        ; Free our XMS memory buffer
  629.     call    far [@XEntry]
  630. I_XMErr mov    dx,XEMsg    ; Point to "XMS memory" message
  631. I_XErr    call    I_Dsply        ; Display XMS error message
  632.     mov    dx,NBMsg    ; Display "no buffered I-O" message
  633.     call    I_Dsply
  634.     mov    ax,(NotUs-(GoToBuf+3)) ; Reject buffered I-O with a
  635.     mov    [GoToBuf+1],ax           ;   dirty-nasty code change!
  636.     mov    ax,(ResEnd-BufOut)     ; Dismiss all buffered logic
  637.     sub    [IVDSLen],ax           ;   by cutting driver length
  638.     jmp    short I_StopD    ; Go stop any previous DMA
  639. I_XMSOK mov    [Bucket],bx    ; Save 32-bit XMS buffer address
  640.     mov    [Bucket+2],dx
  641.     mov    eax,[Bucket]    ; Get XMS buffer address
  642.     add    eax,65536    ; Find 1st 64K boundary after start
  643.     xor    ax,ax
  644.     mov    [@XBufAd],eax    ; Set XMS buffer address and offset
  645.     sub    eax,[Bucket]
  646.     mov    [XMSOffs],eax
  647. I_StopD mov    dx,[PCIAdr]    ; Ensure any previous DMA is stopped
  648.     in    al,dx        ; (On some older chipsets, if DMA is
  649.     and    al,0FEh        ;   running, reading an IDE register
  650.     out    dx,al        ;   causes the controller to HANG!!)
  651.     add    dx,byte 8    ; Stop secondary-channel DMA, also!
  652.     in    al,dx
  653.     and    al,0FEh
  654.     out    dx,al
  655.     mov    byte [VLF],0FFh ; Set run-time VDS "lock" flag
  656.     xor    eax,eax        ; Point ES-reg. to low memory
  657.     mov    es,ax
  658.     mov    al,[es:HDISKS]    ; Set BIOS hard-disk count above
  659.     mov    [@BIOSHD],al
  660.     cmp    al,0        ; Did BIOS find any hard-disks?
  661.     je    near I_Kaput    ; No?  Display message and exit!
  662.     mov    ax,cs        ; Set our code segment in VDS block
  663.     mov    [IVDSSeg],ax
  664.     shl    eax,4        ; Get 20-bit driver virtual address
  665.     cli            ; Avoid interrupts during VDS tests
  666.     test    byte [es:VDSFLAG],20h ; Are "VDS services" active?
  667.     jz    I_SetA        ; No, set 20-bit virtual addresses
  668.     push    cs        ; Point to VDS parameter block
  669.     pop    es
  670.     mov    di,IVDSLen
  671.     mov    ax,8103h    ; "Lock" this driver into memory
  672.     mov    dx,0Ch
  673.     int    4Bh
  674.     jc    near I_VErr    ; Error?  Display error msg. & exit!
  675.     inc    byte [IVDSOfs]    ; Set initialization VDS "lock" flag
  676.     mov    eax,[IVDSAdr]    ; Get 32-bit starting driver address
  677. I_SetA    sti            ; Re-enable CPU interrupts
  678.     add    [PRDAd],eax    ; Set relocated 32-bit PRD address
  679.     mov    ah,41h        ; See if this system has an EDD BIOS
  680.     mov    bx,55AAh
  681.     mov    dl,80h
  682.     int    13h
  683.     jc    I_Scan        ; No, search for disks without EDD
  684.     cmp    bx,0AA55h    ; Did BIOS "reverse" our entry code?
  685.     jne    I_Scan        ; No, search for disks without EDD
  686.     test    cl,4        ; Does BIOS support the EDD subset?
  687.     jz    I_Scan        ; No, search for disks without EDD
  688.     inc    byte [EDDFlag]    ; Set "EDD BIOS present" flag
  689.     jmp    short I_Scan    ; Go scan for UltraDMA disks to use
  690. I_RScan mov    al,0        ; Rescan - load & reset EDD BIOS flag
  691.     xchg    al,[EDDFlag]
  692.     cmp    al,0        ; Were we scanning v.s. DPTE data?
  693.     jne    I_Scan        ; Yes, try hardware-only disk scan
  694. I_Kaput mov    dx,NDMsg    ; Display "No UltraDMA disk" and exit
  695.     jmp    short I_Fail
  696. I_Scan    mov    ax,80h        ; Reset hard-disk unit number & index
  697.     mov    [HDUnit],ax
  698.     mov    byte [HDCount],0; Reset remaining hard-disk count
  699. @BIOSHD equ    $-1        ; (BIOS hard-disk count, set below)
  700. I_Next    movzx    bx,[HDIndex]    ; Get disk unit-number index
  701.     cmp    bh,[EDDFlag]    ; Are we using DPTE data from BIOS?
  702.     je    I_ChnMS        ; No, check disk at "fixed" addresses
  703.     mov    ah,48h        ; Get next BIOS disk's EDD parameters
  704.     mov    dl,[HDUnit]
  705.     mov    si,EDDBuff
  706.     int    13h
  707.     jc    I_NoGud        ; Error?  Display message and exit!
  708.     cmp    dword [si+26],byte -1 ; Valid DPTE pointer?
  709.     je    near I_More    ; No, ignore unit & check for more
  710.     les    si,[si+26]    ; Get this disk's DPTE pointer
  711.     mov    bx,15        ; Calculate DPTE checksum
  712.     mov    al,0
  713. I_CkSum add    al,[es:bx+si]
  714.     dec    bx
  715.     jns    I_CkSum
  716.     cmp    al,0        ; Is DPTE valid (checksum = 0)?
  717.     je    I_EDDOK        ; Yes, use this disk's parameters
  718. I_NoGud mov    dx,EBMsg    ; Display "Invalid EDD BIOS" and exit
  719. I_Fail    jmp    I_Err
  720. I_EDDOK movzx    bx,[es:si+4]    ; Get disk's device-select "nibble"
  721.     shr    bl,4        ; Initialize IDE unit number index
  722.     and    bl,1
  723.     mov    ax,[es:si]    ; Get disk's IDE base address
  724.     cmp    ax,CDATA    ; Is this a primary-channel disk?
  725.     je    I_Index        ; Yes, set disk unit-number index
  726.     cmp    ax,(CDATA-80h)    ; Is this a secondary-channel disk?
  727.     jne    I_More        ; No, ignore unit & check for more
  728.     add    bl,byte 2    ; Adjust for secondary channel
  729. I_Index mov    [HDIndex],bl    ; Set disk's unit number index
  730. I_ChnMS mov    ax,bx        ; Separate channel and master/slave
  731.     shr    al,1
  732.     mov    ah,(LBABITS/32) ; Get drive-select "nibble"
  733.     rcl    ah,5
  734.     ror    al,1        ; Get channel offset (secondary = 80h)
  735.     mov    [HDOffs],ax    ; Set select "nibble" & channel offset
  736.     push    bx        ; Save 16-bit unit number index
  737.     shl    bx,1        ; Get "channel name" message index
  738.     mov    dx,[bx+HDNames] ; Display disk's IDE "channel name"
  739.     call    I_Dsply        ; ("Primary master", etc.)
  740.     mov    ah,8        ; Get BIOS parameters for this disk
  741.     mov    dl,[HDUnit]
  742.     int    13h
  743.     xchg    ax,dx        ; Set AX-reg. with head-number value
  744.     pop    bx        ; Reload unit number index
  745.     mov    dx,LEMsg    ; Point to "not in LBA mode" message
  746.     jc    I_NotU        ; Error - display msg. & ignore disk
  747.     and    cl,SECSHD    ; Clear cylinder bits
  748.     cmp    cl,SECSHD    ; Sectors per cylinder = 63?
  749.     jne    I_NotU        ; No, display message & ignore disk
  750.     cmp    ah,HEADS-1    ; Heads = 255 (max. head = 254)?
  751.     jne    I_NotU        ; No, display message & ignore disk
  752.     mov    al,[HDUnit]    ; Activate this disk in main driver
  753.     mov    [bx+Units-@],al
  754.     push    bx        ; Test for a valid UltraDMA disk
  755.     call    I_TestD
  756.     pop    bx
  757.     jnc    I_More        ; Any errors during disk tests?
  758.     mov    byte [bx+Units-@],0FFh ; Yes?  DELETE disk in driver!
  759. I_NotU    call    I_Dsply        ; Display error for this disk
  760.     mov    dx,CRMsg    ; Display error-message suffix
  761.     call    I_Dsply
  762. I_More    add    word [HDUnit],101h   ; Bump BIOS unit and disk index
  763.     cmp    word [EDDFlag],8400h ; No EDD and all 4 units tested?
  764.     je    I_AnyHD             ; Yes, see if we found any disks
  765.     dec    byte [HDCount]    ; More BIOS disks to check?
  766.     jnz    near I_Next    ; Yes, loop back and do next one
  767. I_AnyHD cmp    dword [Units],byte -1 ; Any active UltraDMA disks?
  768.     je    near I_RScan    ; No, see if we should do a re-scan
  769.     mov    ax,3513h    ; Get current Int 13h vector
  770.     int    21h
  771.     mov    [@PrvI13],bx    ; Save vector for passed requests
  772.     mov    [@PrvI13+2],es
  773.     mov    ax,2513h    ; "Hook" this driver into Int 13h
  774.     mov    dx,Entry
  775.     int    21h
  776.     les    bx,[Packet]    ; Post driver size & success code
  777.     mov    ax,[IVDSLen]
  778.     mov    [es:bx+RPSize],ax
  779.     mov    [es:bx+RPSize+2],cs
  780.     mov    ax,RPDON
  781.     jmp    short I_Exit    ; Go reload all CPU registers and exit
  782. I_VErr    sti            ; VDS "lock" error!  Enable interrupts
  783.     mov    word [XMSSH],VEMsg ; Point to VDS "lock" error message
  784.     jmp    I_XUnlk        ; Go get rid of our XMS memory
  785. I_Err    mov    [XMSSH],dx    ; Save error message pointer
  786.     shr    byte [IVDSOfs],1; Was driver "locked" by VDS?
  787.     jnc    I_XUnlk        ; No, see if we reserved XMS memory
  788.     push    cs        ; Point to VDS parameter block
  789.     pop    es
  790.     mov    di,IVDSLen
  791.     mov    ax,8104h    ; Do VDS "unlock" of this driver
  792.     xor    dx,dx
  793.     int    4Bh
  794. I_XUnlk mov    dx,[XMSHdl]    ; Get XMS buffer handle
  795.     or    dx,dx        ; Did we reserve XMS memory?
  796.     jz    I_DoErr        ; No, reload message pointer
  797.     mov    ah,0Dh        ; Unlock our XMS memory buffer
  798.     call    far [@XEntry]
  799.     mov    ah,0Ah        ; Free our XMS memory buffer
  800.     mov    dx,[XMSHdl]
  801.     call    far [@XEntry]
  802. I_DoErr mov    dx,[XMSSH]    ; Reload error message pointer
  803. I_ErOut call    I_Dsply        ; Display error message
  804.     mov    dx,Suffix    ; Display message suffix
  805.     call    I_Dsply
  806.     les    bx,[Packet]    ; Post "null" driver size
  807.     xor    ax,ax
  808.     mov    [es:bx+RPSize],ax
  809.     mov    [es:bx+RPSize+2],cs
  810. I_BadP    mov    ax,RPDON+RPERR    ; Post "error" in init packet
  811. I_Exit    mov    [es:bx+RPStat],ax
  812.     pop    es        ; Reload all CPU registers and exit
  813.     pop    ds
  814.     popad
  815.     popf
  816.     retf
  817.  
  818. ; Subroutine to do all "validation" tests for an UltraDMA hard-disk
  819.  
  820. I_TestD mov    al,[HDNibbl]    ; Select master or slave disk
  821.     mov    dx,CDSEL
  822.     xor    dl,[HDOffs]
  823.     out    dx,al
  824.     mov    al,0ECh        ; Issue "Identify Device" command
  825.     call    I_Cmd
  826.     jnc    I_PIO        ; If no error, get "identify" data
  827. I_AErr    mov    dx,AEMsg    ; Absent or non-ATA!   Point to msg
  828.     stc            ; Set carry flag (error!) and exit
  829.     ret
  830. I_PIO    mov    dx,CDATA    ; Point to controller PIO data reg
  831.     xor    dl,[HDOffs]
  832.     in    ax,dx        ; Read I.D. bytes 0 and 1
  833.     xchg    ax,si        ; Save "ATA/ATAPI" flag word
  834.     mov    cx,52        ; Skip I.D. bytes 2-105
  835. I_Skip1 in    ax,dx
  836.     loop    I_Skip1
  837.     in    ax,dx        ; Read I.D. bytes 106 and 107
  838.     mov    bh,al        ; Save "DMA valid" flag byte
  839.     mov    cl,34        ; Skip I.D. bytes 108-175
  840. I_Skip2 in    ax,dx
  841.     loop    I_Skip2
  842.     in    ax,dx        ; Read I.D. bytes 176 and 177
  843.     mov    bl,ah        ; Save "UDMA selected" flag byte
  844.     mov    cl,167        ; Skip remaining I.D. data
  845. I_Skip3 in    ax,dx
  846.     loop    I_Skip3
  847.     shl    si,1        ; Is this an "ATA" hard-disk?
  848.     jc    I_AErr        ; No?  Exit & display message!
  849.     test    bh,4        ; Are UltraDMA flag bits valid?
  850.     jz    I_DErr        ; No?  Exit & display message!
  851.     mov    di,Modes    ; Point to UDMA mode table
  852.     mov    al,'0'        ; Initialize "current mode" value
  853.     mov    cl,2        ; Set rotating mode-check bit
  854.     cmp    bl,1        ; Will disk do UDMA mode 0?
  855.     jae    I_NxtM        ; Yes, find its best UDMA mode
  856. I_DErr    mov    dx,DEMsg    ; Not a UDMA disk!   Point to message
  857. I_SErr    stc            ; Set carry flag (error!) and exit
  858.     ret
  859. I_NxtM    cmp    bl,cl        ; Will disk do next UDMA mode?
  860.     jb    I_GotM        ; No, use previous mode
  861.     inc    ax        ; Set up for next UDMA mode
  862.     add    di,byte 4
  863.     shl    cl,1        ; More UDMA modes to check?
  864.     jnz    I_NxtM        ; Yes, loop back
  865. I_GotM    mov    [CurMode],al    ; Update "current mode" value
  866.     mov    eax,[di]    ; Post UDMA mode in set-mode message
  867.     mov    [DspMode],eax
  868.     mov    dx,CSUBCM    ; Set mode-select subcode
  869.     xor    dl,[HDOffs]
  870.     mov    al,SETM
  871.     out    dx,al
  872.     inc    dx
  873.     mov    al,[CurMode]    ; Set desired UltraDMA mode value
  874.     add    al,10h
  875.     out    dx,al
  876.     mov    al,SETF        ; Issue set-features command to disk
  877.     call    I_Cmd
  878.     mov    dx,SEMsg    ; Point to "Set-mode" error message
  879.     jc    I_SErr        ; If error, set carry flag and exit
  880.     mov    dx,MSMsg    ; Display "Set to mode" message
  881. I_Dsply mov    ah,9
  882.     int    21h
  883.     clc            ; Clear carry (no errors!) and exit
  884.     ret            ; Exit
  885.  
  886. ; Subroutine to issue initialization commands to our disks
  887.  
  888. I_Cmd    mov    dx,CCMD        ; Issue desired init command
  889.     xor    dl,[HDOffs]
  890.     out    dx,al
  891.     xor    si,si        ; Point to BIOS timer in low-memory
  892.     mov    es,si
  893.     mov    si,BIOSTMR
  894.     mov    cl,RDYTO    ; Set I-O timeout limit in CL-reg
  895.     add    cl,[es:si]
  896. I_CmdW    cmp    cl,[es:si]    ; Has our command timed out?
  897.     je    I_CmdE        ; Yes, set CPU carry flag & exit
  898.     mov    dx,CSTAT    ; Get IDE controller status
  899.     xor    dl,[HDOffs]
  900.     in    al,dx
  901.     test    al,BSY+RDY    ; Controller or disk still busy?
  902.     jle    I_CmdW        ; Yes, loop back and check again
  903.     test    al,ERR        ; Did command cause any errors?
  904.     jz    I_CmdX        ; No, leave carry flag off & exit
  905. I_CmdE    stc            ; Error!  Set CPU carry flag
  906. I_CmdX    ret            ; Exit
  907.  
  908. ; Initialization Messages
  909.  
  910. TTLMsg    db    CR,LF,'UDMAJR Disk Driver ',VER,CR,LF,'$'
  911. MEMsg    db    'Bus-Master ERROR$'
  912. NEMsg    db    'No controller found$'
  913. PEMsg    db    'PCI BIOS too old$'
  914. NXMsg    db    'No XMS driver$'
  915. XEMsg    db    'XMS init error$'
  916. NBMsg    db    '; using only DMA I-O.',CR,LF,'$'
  917. VEMsg    db    'VDS lock error$'
  918. PCMsg    db    'UDMA controller at PCI address '
  919. DspAddr db    '0000h'
  920. CRMsg    db    '.',CR,LF,'$'
  921. EBMsg    db    'Bad EDD BIOS$'
  922. NDMsg    db    'No UDMA disk found$'
  923. PMMsg    db    'Primary-master $'
  924. PSMsg    db    'Primary-slave $'
  925. SMMsg    db    'Secondary-master $'
  926. SSMsg    db    'Secondary-slave $'
  927. MSMsg    db    'disk set to UDMA mode '
  928. CurMode db    '0, ATA-'
  929. DspMode db    '16. ',CR,LF,'$'
  930. AEMsg    db    'absent or non-ATA$'
  931. DEMsg    db    'is not UDMA$'
  932. LEMsg    db    'not in LBA mode$'
  933. SEMsg    db    'set-mode error$'
  934. Suffix    db    '; driver NOT loaded!',CR,LF,'$'
  935.