home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1998 February / PCOnline_02_1998.iso / filesbbs / os2 / ihpfs126.arj / IHPFS126.ZIP / ihpfs.asm < prev    next >
Encoding:
Assembly Source File  |  1997-11-18  |  120.1 KB  |  4,744 lines

  1. ;
  2. ; iHPFS - HPFS driver for DOS
  3. ; Copyright (C) 1993-1997 Marcus Better
  4. ;
  5. ; This program is free software; you can redistribute it and/or modify
  6. ; it under the terms of the GNU General Public License as published by
  7. ; the Free Software Foundation; either version 2 of the License, or
  8. ; (at your option) any later version.
  9. ;
  10. ; This program is distributed in the hope that it will be useful,
  11. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ; GNU General Public License for more details.
  14. ;
  15. ; You should have received a copy of the GNU General Public License
  16. ; along with this program; if not, write to the Free Software
  17. ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. ;
  19.  
  20. ;==================================================================
  21. ; This program is written for Turbo Assembler 4.1, and uses 
  22. ; ideal mode syntax. To assemble, use the /m option for multiple
  23. ; passes.
  24. ;==================================================================
  25.  
  26. IDEAL
  27. P386
  28. JUMPS
  29. LOCALS
  30.  
  31. ;------------------------------------------------- Constants
  32. ; Version
  33. VerMajor    EQU    1
  34. VerMinor    EQU    26
  35.  
  36. ; Errors
  37. errFileNotFound EQU     02h
  38. errPathNotFound EQU     03h
  39. errTooManyFiles EQU     04h
  40. errAccessDenied EQU     05h
  41. errInvalidHandle EQU    06h
  42. errNoMoreFiles  EQU     12h
  43. errWriteProt    EQU     13h
  44. errSectorNotFound EQU   1Bh
  45.  
  46. TimeZone        EQU     -3600
  47. MinCacheSize    EQU     32      ; Minimum cache size in KB
  48. MaxCacheSize    EQU     32768   ; Maximum cache size in KB
  49. LoadFactor      EQU     5       ; Elements/chain in hash.
  50. CacheEntrySize    EQU    14    ; Cache entry size
  51. MaxPathLength    EQU    57    ; Maximum length of pathname in DOS
  52.  
  53. ; Disk access methods
  54. Method_CHS    EQU    0    ; CHS addressing
  55. Method_CHSExt    EQU    1    ; Extended CHS addressing
  56. Method_Ext    EQU    2    ; IBM/MS Extensions
  57. ;------------------------------------------------- Macros
  58. MACRO   Abort   Code
  59.     mov     bx, Code
  60.     call    AbortMsg
  61. ENDM
  62.  
  63. MACRO    NOASSUME
  64.     ASSUME    cs:NOTHING,ds:NOTHING,es:NOTHING,fs:NOTHING,gs:NOTHING,ss:NOTHING
  65. ENDM
  66.  
  67. MACRO    DEFASSUME
  68.     ASSUME    cs:ResCode,ds:ResCode,es:NOTHING,fs:NOTHING,gs:ResData,ss:NOTHING
  69. ENDM
  70.  
  71. ;------------------------------------------------- Structures
  72. STRUC   XMSMoveStruct
  73.   Length        DD      0       ; Number of bytes to transfer
  74.   SourceHandle  DW      0       ; Handle of source block
  75.   SourceOffset  DD      0       ; Offset into source block
  76.   DestHandle    DW      0       ; Handle of dest block
  77.   DestOffset    DD      0       ; Offset into dest block
  78. ENDS    XMSMoveStruct
  79.  
  80. STRUC    DiskAddrPacketStruct    ; IBM/MS Extensions disk address packet
  81.   Length    DB    10h    ; Length of packet
  82.         DB    0
  83.   Count        DW    0    ; Number of blocks to transfer
  84.   Buffer    DD    0    ; Address of transfer buffer
  85.   Sector    DQ    0    ; Starting absolute sector number
  86. ENDS    DiskAddrPacketStruct
  87.  
  88. ;------------------------------------------------- Procedures
  89. PROCDESC DiskRead PASCAL NEAR :DWORD,:WORD,:DWORD
  90. PROCDESC ReadSector PASCAL NEAR :DWORD,:WORD,:DWORD
  91. PROCDESC ReadFileSector PASCAL NEAR :DWORD,:WORD,:DWORD,:DWORD
  92. PROCDESC ChkPathLength PASCAL NEAR :DWORD
  93. PROCDESC ScanPartTbl PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
  94. PROCDESC CheckHPFSPart PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
  95. PROCDESC ReadSectorCHS PASCAL NEAR :DWORD,:WORD,:WORD,:WORD,:DWORD
  96. PROCDESC ReadSectorExtCHS PASCAL NEAR :DWORD,:WORD,:WORD,:WORD,:DWORD
  97. PROCDESC CheckCylNumber PASCAL NEAR :DWORD,:WORD,:WORD
  98. PROCDESC IsInstalledPart PASCAL NEAR :WORD
  99. PROCDESC QueryPart PASCAL NEAR :WORD,:WORD
  100. PROCDESC RemoveDrv PASCAL NEAR :WORD,:WORD
  101. PROCDESC QueryDrive PASCAL NEAR :WORD,:WORD
  102. PROCDESC UninstallDriver PASCAL NEAR :WORD
  103. ;======================================================= Resident section
  104. SEGMENT ResCode
  105.     ASSUME  cs:ResCode,ds:NOTHING,es:NOTHING,ss:NOTHING,fs:NOTHING,gs:NOTHING
  106. ;-------------------------------------------------- INT 2D entry
  107. ; Follows the Alternate Multiplex Interrupt Specification (AMIS) [v3.5.1]
  108. ; This is an IBM interrupt sharing protocol entry point.
  109. ; The entry point is located in the beginning of the segment,
  110. ; so that the rest of the segment may be released on uninstall.
  111. Int2DEntry:
  112.     jmp    SHORT JmpToInt2DHandler
  113. OldInt2D DD    0    ; Saved vector for next handler in chain
  114.     DW    424Bh    ; Protocol signature
  115.     DB    00h    ; EOI flag - software interrupt
  116.     jmp    SHORT HardwareReset2D    ; Hardware reset routine
  117.     DB    7 DUP (0) ; Reserved
  118. JmpToInt2DHandler:
  119.     jmp    Int2DHandler
  120. HardwareReset2D:
  121.     retf
  122.  
  123. ;-------------------------------------------------- INT 2F entry        
  124. ; This is an IBM interrupt sharing protocol entry point.
  125. Int2FEntry:
  126.     jmp     SHORT JmpToInt2FHandler
  127. OldInt2F DD     0       ; Saved vector for next handler in chain
  128.     DW    424Bh    ; Protocol signature
  129.     DB    00h    ; EOI flag - software interrupt.
  130.     jmp    SHORT HardwareReset2F ; Hardware reset routine
  131.     DB    7 DUP (0) ; Reserved
  132. JmpToInt2FHandler:
  133.     jmp     Int2FHandler
  134. HardwareReset2F:
  135.     retf
  136. EndUninstalledCode:    ; Last byte to keep when uninstalled
  137.  
  138. ;-------------------------------------------------- Common resident data
  139. DataSegs DW    26 DUP(0)    ; Data segments for the drives
  140. ; AMIS information
  141. AMISSign DB    "M Better"    ; Manufacturer name
  142.     DB    "iHPFS   "    ; Product name
  143.     DB    "HPFS Driver for DOS", 0 ; Description, ASCIIZ.
  144. HookList DB    2Fh
  145.     DW    OFFSET Int2FEntry
  146.     DB    2Dh
  147.     DW    OFFSET Int2DEntry
  148. ApiFunc DB     0                ; INT 2D function # for API.
  149. ; Saved registers and vectors
  150. FuncAddr DW    0        ; Pointer to current redirector function
  151. ResDataSeg DW    0        ; Current resident data segment
  152. DriveNo DB      0               ; Current drive number
  153. SaveSP  DW      0        ; SP on entry to interrupt handler
  154. SaveSS  DW      0        ; SS on entry to interrupt handler
  155. DosVersion DB    0        ; Major DOS version, zero if Novell/Dr DOS
  156. Novell    DB    0        ; Set if Novell DOS
  157. Win95    DB    0        ; Set if running under Windows95
  158. ; Pointers to SDA fields. Layout:
  159. ;                DOS4+    DOS 3, DR-DOS
  160. ;  DTA ptr            0Ch    0Ch
  161. ;  First filename buffer    9Eh    92h
  162. ;  Search data block        19Eh    192h
  163. ;  Dir entry for found file    1B3h    1A7h
  164. ;  Search attributes        24Dh    23Ah
  165. ;  File access/sharing mode    24Eh    23Bh
  166. ;  Ptr to current CDS        282h    26Ch
  167. ;  Extended open mode        2E1h    Not supported
  168. SDA    DD    0               ; Address of DOS Swappable Data Area
  169. PSP    DW      0           ; Program Segment Prefix
  170. pCurrCDS DD    282h        ; Pointer to current CDS
  171. pDTA    DD    0Ch        ; Pointer to current DTA
  172. FN1    DD    9Eh        ; Address of first filename field
  173. AccMode    DD    24Eh        ; Address of file access/sharing mode field
  174. SrchAttr DD    24Dh        ; Address of search attributes
  175. ExtOpenMode DD    2E1h        ; Address of extended open mode
  176. ; Buffers.
  177. Buf1    DB      512 DUP(0)
  178. Buf2    DB      512 DUP(0)
  179. Buf3    DB      512 DUP(0)
  180. Buf4    DB      512 DUP(0)
  181. FNameBuf DB     128 DUP(0)
  182. FNameBuf2 DB    128 DUP(0)
  183. BufUsed    DB    0    ; Flag for FindNext, set if buffers untouched.
  184. ; Some flags
  185. Multitrack DB   1       ; Allow multitrack reads and writes.
  186. ; Characters permitted in filenames, etc
  187. MinPerm DB      0       ; Lowest permissible character
  188. MaxPerm DB      255     ; Highest permissible character
  189. MinExcl DB      0       ; Lowest excluded character
  190. MaxExcl DB      0       ; Highest excluded character
  191. NumTerm DB      0       ; Number of illegal (terminator) characters
  192. TermChars DB    32 DUP (0) ; Array of illegal characters
  193. UpCaseTbl DB    128 DUP (0) ; File Character Upper-Case Table
  194. DaysInMonth DB  31,28,31,30,31,30,31,31,30,31,30,31
  195. ConvertLong DB    0    ; Convert long filenames flag
  196. ConvTable DB    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_!#%"
  197. ; Cache info
  198. CacheOn DB      0       ; Flag: Caching active?
  199. XMSEntry DD     0       ; XMS driver entry point
  200. hHashTable DW   0       ; XMS handle to the hash table
  201. hCacheLists DW  0       ; XMS handle to the hash chains
  202. hCacheSectors DW 0      ; XMS handle to the cache sectors
  203. CacheEntries DW 0       ; Entries in the cache
  204. HashSize DW     0       ; Slots in the hash table
  205. FreeEntry DW    0       ; Next free entry pointer
  206. XMoveStruc XMSMoveStruct <>
  207. XMSError DB     0       ; Set if XMS call failed
  208. EntryBuf DB     20 DUP (0)
  209. ; IBM/MS Extensions data
  210. DiskAddrPkt DiskAddrPacketStruct <>
  211. ; Jump Table, 0 means unsupported
  212. JmpTbl  DW      0, RmDir, 0, MkDir
  213.     DW      0, ChDir, Close, Commit
  214.     DW      Read, Write, LockRegion, UnlockRegion
  215.     DW      DiskFree, 0, SetAttrib, GetAttrib
  216.     DW      0, Rename, 0, Delete
  217.     DW      0, 0, Open, Create
  218.     DW      0, 0, 0, FindFirst
  219.     DW      FindNext, 0, 0, 0
  220.     DW      0, Seek, 0, 0
  221.     DW      0, 0, 0, 0
  222.     DW      0, 0, 0, 0
  223.     DW      0, 0, ExtOpen
  224.  
  225. ;------------------------------------------------------------------------
  226. ; Int 2D (Alternate Multiplex) handler.
  227. PROC    Int2DHandler FAR
  228.     NOASSUME
  229.     ASSUME    cs:ResCode
  230.     cmp    ah, [ApiFunc]
  231.     je    apiApiCall
  232.     jmp    [OldInt2D]
  233. apiApiCall:
  234.     or      al, al          ; Installation check
  235.     je      apiInstallChk
  236.     cmp     al, 2           ; Uninstall
  237.     je      apiUnInstall
  238.     cmp    al, 4        ; Determine chained interrupts
  239.     je    apiGetHookList
  240.     cmp    al, 10h        ; Query drive installed
  241.     je    apiQueryDrive
  242.         xor    al, al        ; Not implemented
  243.     iret
  244.  
  245. ; iHPFS installation check.
  246. apiInstallChk:        
  247.     mov     al, 0FFh        ; Multiplex number in use
  248.     mov    ch, VerMajor
  249.     mov    cl, VerMinor
  250.     mov    dx, cs
  251.     mov    di, OFFSET AMISSign ; DX:DI -> AMIS signature
  252.     iret
  253.  
  254. apiGetHookList:
  255.     mov    dx, cs
  256.     mov    bx, OFFSET HookList
  257.     mov    al, 04h        ; Hook list returned.
  258.     iret
  259.  
  260. apiUnInstall:
  261.     mov    bx, ResCode
  262.     mov    al, 03h
  263.     iret
  264.  
  265. ; Query drive installed. Drive number in BX (will be destroyed). Returns
  266. ; AH=1 if installed, AH=0 otherwise.
  267. apiQueryDrive:
  268.     shl    bx, 1
  269.     cmp    [DataSegs+bx], 0
  270.     setnz    ah
  271.     iret
  272.  
  273. ENDP    Int2DHandler
  274.  
  275. ;------------------------------------------------------------------------
  276. ; Int 2F (Multiplex) Interrupt handler.
  277. ; Processes calls for function 11h.
  278. PROC    Int2FHandler FAR
  279.     NOASSUME
  280.     ASSUME  cs:ResCode
  281.     sti
  282.     cmp     ah, 11h
  283.     je      Function11
  284.     jmp     [OldInt2F]
  285.  
  286. Function11:
  287. ; Decide which method to use for determining if the call is for us.
  288.     or      al, al
  289.     jz      NetInstallChk   ; Redirector installation check
  290.     call    DetectWin95
  291.     cmp     al, 21h         ; Seek
  292.     je      CheckSFT
  293.         cmp     al, 1Ch         ; Find next
  294.         je      CheckFindNext
  295.     cmp     al, 2Eh
  296.     je      CheckCDS
  297.     cmp     al, 1Dh
  298.     jbe     Function11_1
  299.     jmp     [OldInt2F]
  300. Function11_1:
  301.     cmp     al, 06h
  302.     jb      CheckCDS
  303.     cmp     al, 0Bh
  304.     jna     CheckSFT
  305.  
  306. ; CDS method: Check path field of CDS.
  307. CheckCDS:
  308.     push    ds bx
  309.     lds    bx, [pCurrCDS]
  310.     lds    bx, [bx]        ; CDS for current file
  311.     movzx    bx, [BYTE bx]
  312.     cmp    bl, '\'
  313.     je    CheckCDS1        ; Not for us - ZF set
  314.     sub    bl, 'A'
  315.     mov    [DriveNo], bl
  316.     shl    bx, 1
  317.     mov    bx, [DataSegs+bx]
  318.     mov    [ResDataSeg], bx
  319.     or    bx, bx            ; ZF set if unsupported drive
  320. CheckCDS1:
  321.     pop     bx ds
  322.     jnz     CallForUs
  323.     jmp     [DWORD OldInt2F] ; Call was not for us
  324.  
  325. ; SFT method: Check drive number in SFT entry.
  326. ; ES:DI -> SFT entry for file
  327. CheckSFT:
  328.     push    bx
  329.     movzx   bx, [BYTE es:di+5]
  330.     and     bl, 3Fh                 ; Bits 5-0 contain drive number
  331.     mov    [DriveNo], bl
  332.     shl    bx, 1
  333.     mov    bx, [DataSegs+bx]
  334.     mov    [ResDataSeg], bx
  335.     or    bx, bx
  336.     pop     bx
  337.     jnz     CallForUs
  338.     jmp     [DWORD OldInt2F]
  339.  
  340. ; Special check for Find Next function - drive number in SDB.
  341. ; Under Windows95 we do not put the drive number in the SDB, so this
  342. ; cannot be used.
  343. CheckFindNext:
  344.     cmp    [Win95], 0
  345.     jnz    CheckCDS        ; CDS check under Win95 instead
  346.     cmp    [DosVersion], 7
  347.     jae    CheckCDS        ; CDS check under DOS 7
  348.         push    ds bx
  349.         lds     bx, [pDTA]
  350.         lds     bx, [bx]                ; DS:BX -> DTA
  351.         mov     bl, [BYTE es:di]        ; SDB drive number
  352.         and     bx, 3Fh                 ; Turn off network bit
  353.         mov     [DriveNo], bl
  354.         shl     bx, 1
  355.         mov     bx, [DataSegs+bx]
  356.         mov     [ResDataSeg], bx
  357.         or      bx, bx
  358.         pop     bx ds
  359.         jnz     CallForUs
  360.         jmp     [DWORD OldInt2F]
  361.     
  362. ; Call is for our drive
  363. CallForUs:
  364. ; Switch stack
  365.     mov     [cs:SaveSP], sp
  366.     mov     [cs:SaveSS], ss
  367.     push    cs
  368.     pop     ss
  369.     mov     sp, OFFSET ResStack
  370.     ASSUME    ss:SEG ResStack
  371. ; Transfer to the subfunction handler
  372.     push    bx
  373.     mov     bl, al
  374.     xor     bh, bh
  375.     shl     bl, 1
  376.     mov    bx, [JmpTbl+bx]
  377.     mov    [FuncAddr], bx
  378.     pop    bx
  379.     cmp    [FuncAddr], 0
  380.     jz      Unsupported
  381.     push    ds gs
  382.     push    cs
  383.     pop    ds
  384.     mov    gs, [ResDataSeg]
  385.     DEFASSUME
  386.     call    [FuncAddr]
  387.     pop    gs ds
  388.     NOASSUME
  389.     ASSUME    cs:ResCode
  390.     lss     sp, [DWORD SaveSP]
  391.     ret     2
  392. Unsupported:
  393.     lss     sp, [DWORD SaveSP]
  394.     jmp     [DWORD OldInt2F] ; Function not supported, ignore it.
  395.  
  396. ; Redirector installed check
  397. NetInstallChk:
  398.     mov     ax, 00FFh       ; Redirector installed.
  399.     iret                    ; Return immediately
  400. ENDP    Int2FHandler
  401.  
  402. ;---------------------------------------------------------------------
  403. ; Detect presence of Windows95.
  404. PROC    DetectWin95
  405.     NOASSUME
  406.     ASSUME    cs:ResCode
  407.     pushad
  408.     mov    [Win95], 0
  409.     mov    ax, 1600h
  410.     int    2fh
  411.     cmp    al, 1        ; 0=nothing, 1=Windows/386
  412.     jbe    @@Done
  413.     cmp    al, 80h        ; XMS version 1 driver, no Windows
  414.     je    @@Done
  415.     cmp    al, 0FFh    ; Windows/386
  416.     je    @@Done
  417.     cmp    al, 4        ; Major version
  418.     jb    @@Done
  419.     mov    [Win95], 1
  420. @@Done:
  421.     popad
  422.     ret
  423. ENDP    DetectWin95
  424.  
  425. ;---------------------------------------------------------------------
  426. ; Remove Directory
  427. PROC    RmDir
  428.     stc
  429.     mov     ax, errPathNotFound
  430.     ret
  431. ENDP    RmDir
  432.  
  433. ;---------------------------------------------------------------------
  434. ; Make Directory
  435. PROC    MkDir
  436.     stc
  437.     mov     ax, errWriteProt
  438.     ret
  439. ENDP    MkDir
  440.  
  441. ;---------------------------------------------------------------------
  442. ; Change Directory
  443. PROC    ChDir   STDCALL
  444.     LOCAL   @@CDSPointer:DWORD, @@Result
  445.     DEFASSUME
  446.     pushad
  447.     push    es
  448.     mov    [BufUsed], 0
  449.     les    di, [pCurrCDS]
  450.     mov    eax, [es:di]
  451.     mov     [@@CDSPointer], eax
  452.     les     di, [FN1]
  453.     cmp     [BYTE es:di+3], 0    ; See if root directory
  454.     je      @@Root
  455.     cmp    [BYTE es:di+2], 0    ; See if root in DR-DOS
  456.     je    @@Root
  457.     call    FindFile
  458.     jc      @@PathNotFound
  459. ; See if the entry is a subdirectory
  460.     test    [Buf1+bx+03h], 10h    ; Mask out subdirectory attribute
  461.     jz      @@PathNotFound
  462.     call    NEAR ChkPathLength PASCAL, [FN1]
  463.     jc    @@PathNotFound
  464. ; Extract the FNode pointer
  465.     mov     ecx, [DWORD Buf1+bx+04h]
  466.     mov     [CDFNode], ecx
  467. ; Set the CDS directory name to current directory.
  468.     lds     si, [FN1]
  469.     ASSUME    ds:NOTHING
  470.     les     di, [@@CDSPointer]
  471.     mov     cx, 67            ; Length of CDS path string
  472.     cld
  473. @@MovePath:
  474.     lodsb
  475.     stosb
  476.     or      al, al
  477.     loopnz  @@MovePath
  478.     clc
  479.     jmp     @@Done
  480.  
  481.     DEFASSUME
  482. @@Root: mov     eax, [RootFNode]
  483.     mov     [CDFNode], eax
  484.     lds     bx, [@@CDSPointer]
  485.     ASSUME    ds:NOTHING
  486.     mov     [BYTE bx+3], 0      ; Puts 0 after X:\ in CDS Filename
  487.     clc
  488.     jmp     @@Done
  489. @@PathNotFound:
  490.     stc
  491.     mov     [@@Result], errPathNotFound
  492. @@Done:
  493.     pop     es
  494.     popad
  495.     jnc     @@Exit
  496.     mov     ax, [@@Result]
  497. @@Exit:
  498.     ret
  499. ENDP    ChDir
  500.  
  501. ;---------------------------------------------------------------------
  502. ; Close File
  503. PROC    Close
  504.     DEFASSUME
  505.     push    ax bx
  506.     mov     ax, 1208h
  507.     int     2Fh             ; Decrease handle count in SFT
  508.     cmp     ax, 01h         ; Last handle closed?
  509.     jne     @@1
  510. ; Clear the SFT
  511.     mov     [WORD es:di], 0     ; Number of references
  512. @@1:
  513.     pop     bx ax
  514.     ret
  515. ENDP    Close
  516.  
  517. ;---------------------------------------------------------------------
  518. ; Commit File
  519. PROC    Commit
  520.     stc
  521.     mov     ax, errWriteProt
  522.     ret
  523. ENDP    Commit
  524.  
  525. ;---------------------------------------------------------------------
  526. ; Read from File
  527. PROC    Read    STDCALL
  528.     DEFASSUME
  529.     LOCAL   @@OrigBytes, @@Bytes, @@RelSect:DWORD, @@SecOfs, @@DTABuf:DWORD
  530.         LOCAL   @@Result, @@SFTOfs, @@FNode:DWORD
  531.     pushad
  532.     push    es fs
  533.     push    es
  534.     pop     fs
  535.     ASSUME    fs:NOTHING
  536.     mov     [@@SFTOfs], di
  537.     mov    [BufUsed], 0
  538.     mov     [@@Bytes], cx
  539.     mov    [@@OrigBytes], 0
  540.     les     bx, [SDA]
  541.     les     bx, [DWORD es:bx+0Ch]       ; DTA Pointer
  542.     mov     [WORD LOW @@DTABuf], bx
  543.     mov     [WORD HIGH @@DTABuf], es
  544.         mov     eax, [fs:di+19h]
  545.         mov     [@@FNode], eax
  546.     push    cs
  547.         pop    es
  548.     ASSUME    es:ResCode
  549. ; Adjust bytes to read if necessary.
  550.     movzx   eax, [@@Bytes]           ; Bytes to read
  551.     mov    edx, [fs:di+15h]    ; File pos
  552.     mov    ecx, [fs:di+11h]    ; File size
  553.     cmp    edx, ecx
  554.     jae    @@Succeed        ; Beyond EOF
  555.     add     eax, edx
  556.     cmp     eax, ecx        ; Compare w file size
  557.     jna     @@1
  558.     sub     eax, ecx
  559.     sub     [@@Bytes], ax           ; Actual bytes to read
  560. @@1:    mov     ax, [@@Bytes]
  561.     mov     [@@OrigBytes], ax
  562.     mov     eax, edx        ; File pos
  563.     and     dx, 511
  564.     mov     [@@SecOfs], dx
  565.     shr     eax, 9
  566.     mov     [@@RelSect], eax
  567.     cmp    [@@SecOfs], 0
  568.     je    @@WholeSectors
  569. ; Read sector into Buf2 and copy bytes to DTA
  570.         call    NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
  571.     jc    @@ReadError
  572.     inc     [@@RelSect]
  573.     mov     cx, 512
  574.     sub     cx, [@@SecOfs]            ; Bytes to read from this sector
  575.     cmp     [@@Bytes], cx
  576.     ja      @@2
  577.     mov     cx, [@@Bytes]
  578. @@2:
  579.     mov     si, OFFSET Buf2
  580.     add     si, [@@SecOfs]
  581.     les     di, [@@DTABuf]
  582.     ASSUME    es:NOTHING
  583.     push    cx
  584.     rep movsb                       ; Transfer bytes to DTA Buffer
  585.     pop     cx
  586.     add     [WORD @@DTABuf], cx     ; Increase user buffer offset
  587.     sub     [@@Bytes], cx           ; Decrease bytes left to read
  588. ; Read sectors into DTA
  589. @@WholeSectors:    
  590.     cmp     [@@Bytes], 512
  591.     jb      @@LastSector            ; Done if no more bytes
  592.     movzx    eax, [@@Bytes]
  593.     shr    ax, 9            ; Sectors to read
  594.         call    NEAR ReadFileSector, [@@RelSect], ax, [@@FNode], [@@DTABuf]
  595.     jc      @@ReadError
  596.     add    [@@RelSect], eax
  597.     shl    ax, 9            ; Bytes read
  598.     add    [WORD @@DTABuf], ax
  599.     sub    [@@Bytes], ax
  600. ; Read the last sector into Buf2 and copy part of it to DTA
  601. @@LastSector:
  602.     cmp    [@@Bytes], 0
  603.     jz    @@Succeed        ; No bytes left to read
  604.         call    NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
  605.     jc      @@ReadError
  606.     mov     cx, [@@Bytes]
  607.     mov     si, OFFSET Buf2
  608.     les     di, [@@DTABuf]
  609.     ASSUME    es:NOTHING
  610.     rep movsb                       ; Transfer bytes to user buffer
  611. ; Done!    
  612. @@Succeed:
  613.     movzx   ecx, [@@OrigBytes]
  614.     mov     bx, [@@SFTOfs]
  615.     add     [fs:bx+15h], ecx
  616.     mov     [@@Result], cx
  617.     clc
  618.     jmp     @@Done
  619. @@SectorNotFound:
  620. @@ReadError:
  621.     mov     [@@Result], errSectorNotFound
  622.     stc
  623. @@Done:
  624.     pop     fs es
  625.     ASSUME    es:NOTHING,fs:NOTHING
  626.     popad
  627.     jc      @@Fail
  628.     mov     cx, [@@Result]
  629.     jmp     @@Exit
  630. @@Fail:
  631.     mov     ax, [@@Result]
  632. @@Exit:
  633.     ret
  634. ENDP    Read
  635.  
  636. ;---------------------------------------------------------------------
  637. ; Write to File
  638. PROC    Write    STDCALL
  639.     stc
  640.     mov     ax, errWriteProt
  641.     ret
  642. ENDP    Write
  643.  
  644. ;---------------------------------------------------------------------
  645. ; Lock Region of File
  646. PROC    LockRegion
  647.     clc
  648.     ret
  649. ENDP    LockRegion
  650.  
  651. ;---------------------------------------------------------------------
  652. ; Unlock Region of File
  653. PROC    UnlockRegion
  654.     clc
  655.     ret
  656. ENDP    UnlockRegion
  657.  
  658. ;---------------------------------------------------------------------
  659. ; Get Disk Space
  660. PROC    DiskFree
  661.     DEFASSUME
  662.     mov    ah, [MediaID]    
  663.     mov    al, 1        ; Sectors per cluster
  664.     mov    ebx, [TotalSectors]    ; Number of clusters
  665.     xor    cl, cl
  666. ; Adjust clusters so that value is 16-bit
  667. @@1:
  668.     cmp    ebx, 0FFFFh    ; Cluster count fits in BX?
  669.     jbe    @@2
  670.     shr    ebx, 1        ; Divide cluster count by 2
  671.     shl    al, 1        ; Multiply sectors per cluster by 2
  672.     inc    cl        ; CL=shift value for sector numbers
  673.     jmp    @@1
  674. @@2:
  675.     mov     edx, [FreeSectors]
  676.     shr    edx, cl        ; Available clusters
  677.     mov     cx, 512         ; Bytes/sector
  678.     clc
  679.     ret
  680. ENDP    DiskFree
  681.  
  682. ;---------------------------------------------------------------------
  683. ; Set File Attributes
  684. PROC    SetAttrib STDCALL
  685.     stc
  686.     mov     ax, errWriteProt
  687.     ret
  688. ENDP    SetAttrib
  689.  
  690. ;---------------------------------------------------------------------
  691. ; Get File Attributes
  692. PROC    GetAttrib STDCALL
  693.     DEFASSUME
  694.     LOCAL   @@Result
  695.     pushad
  696.     push    es
  697.     mov    [BufUsed], 0
  698.     les     di, [FN1]
  699.     call    FindFile
  700.     jc    @@FileNotFound
  701.     mov     al, [Buf1+bx+03h]       ; Extract attributes
  702.     and    al, 10111111b        ; Mask out 8.3 filename bit
  703.     xor     ah, ah
  704.     test    al, 10h            ; Test if subdir
  705.     jz    @@SubDirOK
  706.     call    NEAR ChkPathLength PASCAL, [FN1]
  707.     jnc    @@SubDirOk
  708.     and    al, NOT 10h        ; Turn off subdir attribute
  709. @@SubDirOk:
  710.     mov     [@@Result], ax
  711.     clc
  712.     jmp    @@Done
  713.  
  714. @@FileNotFound:
  715.     mov     [@@Result], errFileNotFound
  716.     stc
  717. @@Done:
  718.     pop     es
  719.     popad
  720.     mov     ax, [@@Result]
  721.     ret
  722. ENDP    GetAttrib
  723.  
  724. ;---------------------------------------------------------------------
  725. ; Rename File
  726. PROC    Rename
  727.     stc
  728.     mov     ax, errWriteProt
  729.     ret
  730. ENDP    Rename
  731.  
  732. ;---------------------------------------------------------------------
  733. ; Delete File
  734. PROC    Delete
  735.     stc
  736.     mov     ax, errWriteProt
  737.     ret
  738. ENDP    Delete
  739.  
  740. ;---------------------------------------------------------------------
  741. ; Open File
  742. PROC    Open    STDCALL
  743.     DEFASSUME
  744.     LOCAL   @@OpenMode:BYTE, @@Result:WORD
  745.     pushad
  746.     push    es fs
  747.     mov     ax, es
  748.     mov     fs, ax
  749.     mov    [BufUsed], 0
  750.     mov     si, di
  751.     les     di, [AccMode]
  752.     mov     al, [es:di]        ; File access mode/sharing
  753.     cmp    [Novell], 0
  754.     jnz    @@OpenModeOk        ; Ignore access mode under Novell DOS
  755.     test    al, 7
  756.     jnz    @@errAccessDenied
  757. @@OpenModeOk:
  758.     mov     [@@OpenMode], al
  759. @@FindFile:
  760.     mov     di, [WORD LOW FN1]      ; First filename buffer
  761.     call    FindFile
  762.     jnc     @@Found
  763.     mov     [@@Result], errFileNotFound
  764.     jmp     @@Fail
  765. @@Found:
  766.     mov     al, [Buf1+bx+03h]       ; Attributes
  767.     and     al, 10h            ; Test subdir attribute
  768.     jnz     @@errAccessDenied
  769.         
  770. ; Set SFT fields
  771.     mov     al, [@@OpenMode]
  772.     and     al, 7Fh
  773.     xor     ah, ah
  774.     mov     [fs:si+02h], ax
  775.     mov     al, [Buf1+bx+03h]       ; Attributes
  776.     and    al, 10111111b        ; Mask out 8.3 filename bit
  777.     mov     [fs:si+04h], al
  778.     mov     ax, 8040h               ; Device info word
  779.     or      al, [DriveNo]           ; Drive number
  780.     mov     [fs:si+05h], ax
  781.     mov     [DWORD fs:si+07h], 0     ; Device driver pointer
  782.     mov     [WORD fs:si+0Bh], 0     ; Cluster #, local files only
  783.     mov     eax, [DWORD Buf1+bx+08h]; Timestamp
  784.     call    Unix2DosTime
  785.     mov     [fs:si+0Dh], eax
  786.     mov     eax, [DWORD Buf1+bx+0Ch] ; File size
  787.     mov     [fs:si+11h], eax
  788.     mov     [DWORD fs:si+15h], 0    ; Current offset in file
  789.     mov     eax, [DWORD Buf1+bx+04h]; FNode sector #
  790.     mov     [fs:si+19h], eax     ; Save in REDIRIFS field
  791. ; Convert filename to FCB format (11 bytes, no dot, blank-padded)
  792. ; Scan to the terminating 0.
  793.     cld
  794.     xor     al, al
  795.     mov     cx, -1
  796.     repne scasb
  797. ; Scan back to the last \
  798.     std
  799.     mov     al, '\'
  800.     mov     cx, -1
  801.     repne   scasb
  802.     add     di, 2
  803.  
  804.     xchg    si, di
  805.     push    es
  806.     push    fs
  807.     pop     es
  808.     pop     ds
  809.     ASSUME    ds:NOTHING
  810.     add     di, 20h
  811.  
  812.     mov     cx, 11
  813.     mov     al, ' '
  814.     cld
  815.     rep stosb                       ; Clear the field
  816.     sub     di, 11
  817.     lea     dx, [di+8]              ; Extension part
  818. @@Fn1:  lodsb
  819.     or      al, al
  820.     je      @@Succeed
  821.     cmp     al, '.'
  822.     jne     @@Fn2
  823.     mov     di, dx                  ; Move on to extension field if '.'
  824.     jmp     @@Fn1
  825. @@Fn2:
  826.     stosb
  827.     jmp     @@Fn1
  828.  
  829. @@errAccessDenied:
  830.     mov     [@@Result], errAccessDenied
  831.     jmp     @@Fail
  832. @@Succeed:      
  833.     clc
  834.     jmp     @@Done
  835. @@Fail: stc
  836. @@Done: pop     fs es
  837.     popad
  838.     jnc     @@Exit
  839.     mov     ax, [@@Result]
  840. @@Exit:
  841.     ret
  842. ENDP    Open
  843.  
  844. ;---------------------------------------------------------------------
  845. ; Create File
  846. PROC    Create
  847.     stc
  848.     mov     ax, errWriteProt
  849.     ret
  850. ENDP    Create
  851.  
  852. ;---------------------------------------------------------------------
  853. ; Find First Matching File
  854. PROC    FindFirst STDCALL
  855.     DEFASSUME
  856.     LOCAL   @@Result, @@SrchTmplPos, @@DirFNode:DWORD, @@Attr:BYTE
  857.     LOCAL    @@LongPath:BYTE, @@DTA:DWORD
  858.     pushad
  859.     push    es
  860.     mov    ax, cs
  861. ; Move the filename from SDA First Filename buffer to FNameBuf2
  862.     mov    [BufUsed], 0
  863.     mov    [@@LongPath], 0        ; Set if pathname is long
  864.     lds    si, [pDTA]
  865.     ASSUME    ds:NOTHING
  866.     mov    ecx, [si]
  867.     mov    [@@DTA], ecx        ; Address of DTA
  868.     lds     si, [FN1]
  869.     mov    es, ax
  870.     ASSUME    es:ResCode
  871.     mov     di, OFFSET FNameBuf2
  872.     mov     cx, 32
  873.     cld
  874.     rep movsd
  875.     mov     ds, ax
  876.     ASSUME    ds:ResCode
  877. ; Find the last \ and replace it with 0
  878.     xor     al, al
  879.     mov     cx, 128
  880.     mov     di, OFFSET FNameBuf2
  881.     repne scasb
  882.     jne     @@PathNotFound
  883.     dec     di
  884.     mov     al, '\'
  885.     sub     cx, 128
  886.     neg     cx
  887.     std
  888.     repne scasb
  889.     jne     @@PathNotFound
  890.     inc     di
  891.     mov     [BYTE di], 0
  892.     inc     di
  893.     mov     [@@SrchTmplPos], di       ; Where the search template starts
  894.     mov    ax, di
  895.     sub    ax, OFFSET FNameBuf2
  896.     cmp    ax, MaxPathLength-8    ; Long path?
  897.     seta    [@@LongPath]
  898.     ror    [@@LongPath], 1        ; Move flag to high bit
  899. ; Find the directory
  900.     cmp     di, OFFSET FNameBuf2+3  ; See if it's in the root dir.
  901.     jne     @@FindDir
  902.     mov     ecx, [RootFNode]
  903.     mov     [@@DirFNode], ecx
  904.     jmp     @@DirOk
  905. @@FindDir:
  906.     mov     di, OFFSET FNameBuf2
  907.     call    FindFile
  908.     jc      @@PathNotFound
  909. ; Check that it's really a directory.
  910.     test    [BYTE Buf1+bx+03], 10h
  911.     jz      @@PathNotFound
  912.     mov     ecx, [DWORD Buf1+bx+04h]    ; FNode of directory
  913.     mov     [@@DirFNode], ecx
  914.  
  915. @@DirOk:
  916.     les     di, [SrchAttr]
  917.     ASSUME    es:NOTHING
  918.     mov     al, [es:di]            ; Search attribute
  919.     mov     [@@Attr], al
  920. ; Initialize the search data block in the DTA.
  921. ; The "drive number" byte does not seem to work as specified in Windows 95.
  922. ; We later overwrite this with a magic value that seems to work. The reason
  923. ; for this behaviour is unknown.
  924.     cld
  925.     les    di, [@@DTA]
  926.     mov     al, [DriveNo]           ; Drive number
  927.     or      al, 80h                 ; Set bit 7 for remote drive
  928.     stosb
  929. ; Search template - 11 bytes, padded with spaces.
  930.     mov     cx, 11
  931.     mov     al, ' '
  932.     rep stosb                       ; Clear the field
  933.     sub     di, 11
  934.     lea     dx, [di+8]              ; Extension part
  935.     mov     si, [@@SrchTmplPos]
  936. @@1:    lodsb
  937.     or      al, al
  938.     je      @@TemplateDone
  939.     cmp     al, '.'
  940.     jne     @@2
  941.     mov     di, dx                  ; Move on to extension field if '.'
  942.     jmp     @@1
  943. @@2:
  944.     stosb
  945.     jmp     @@1
  946.  
  947. @@TemplateDone:
  948.     mov     di, dx
  949.     add     di, 3
  950.     mov     al, [@@Attr]
  951.     stosb
  952.  
  953. ; Read the directory FNode
  954.     call    NEAR ReadSector, [@@DirFNode], 1, ds (OFFSET Buf1)
  955.     jc      @@NoMoreFiles
  956.     mov     eax, [DWORD Buf1+48h] ; Starting sector
  957.     les     bx, [@@DTA]
  958.     mov     [es:bx+0Dh], eax     ; Save in SDB
  959.     xor    al, al
  960.     mov    ah, [@@LongPath]
  961.     mov     [WORD es:bx+11h], ax ; Offset of last entry + long path flag
  962.  
  963.     test    [@@Attr], 08h                 ; Volume label?
  964.     jz      @@RegularFile
  965. ; Volume label
  966.     push    ds
  967.     pop     es
  968.     ASSUME    es:ResCode
  969.     mov    ds, [ResDataSeg]
  970.     ASSUME    ds:ResData
  971. ; Move volume label to FNameBuf
  972.     mov     di, OFFSET FNameBuf
  973.     mov     cx, 8
  974.     mov     si, OFFSET Volabel
  975.     rep movsb
  976.     mov     al, '.'
  977.     stosb
  978.     mov     cx, 3
  979.     rep movsb
  980.     push    cs
  981.         pop    ds
  982.     ASSUME    ds:ResCode
  983.  
  984.     mov     dx, OFFSET FNameBuf
  985.     mov     bx, [@@SrchTmplPos]
  986.     call    Match
  987.     jnc    @@DoVolLabel
  988.     cmp    [@@Attr], 08h        ; Only volume label?
  989.     je    @@NoMoreFiles
  990.     jmp    @@RegularFile
  991. @@DoVolLabel:
  992. ; Set the directory entry for found vol. label.
  993.     les     di, [@@DTA]
  994.     ASSUME    es:NOTHING
  995.     add     di, 15h            ; Directory entry for found file
  996.     mov     cx, 11
  997.     mov     al, ' '
  998.     cld
  999.     rep stosb
  1000.     sub     di, 11
  1001.     lea     dx, [di+11]
  1002.     mov     cx, 11
  1003.     mov     si, OFFSET Volabel
  1004.     mov    ds, [ResDataSeg]
  1005.     ASSUME    ds:ResData
  1006. @@MoveVolabel:
  1007.     lodsb
  1008.     or      al, al
  1009.     jz      @@VolabelDone
  1010.     stosb
  1011.     loop    @@MoveVolabel
  1012. @@VolabelDone:
  1013.     push    cs
  1014.         pop    ds
  1015.     ASSUME    ds:ResCode
  1016.     mov     di, dx
  1017.     mov     al, 08h
  1018.     stosb                           ; Attributes
  1019.     add     di, 10
  1020.     xor     eax, eax
  1021.     stosd                           ; Time and date
  1022.     stosw                           ; Starting cluster
  1023.     stosd                           ; File size     
  1024.     clc
  1025.     jmp     @@Done
  1026.  
  1027. @@RegularFile:
  1028. ; Call FindNext to do the actual directory search
  1029.     les    di, [@@DTA]
  1030.     call    FindNext
  1031.     mov     [@@Result], ax
  1032.     jmp     @@Done
  1033.  
  1034. @@PathNotFound:
  1035.     stc
  1036.     mov     [@@Result], errPathNotFound
  1037.     jmp     @@Done
  1038. @@NoMoreFiles:
  1039.     stc
  1040.     mov     [@@Result], errNoMoreFiles
  1041.     jmp     @@Done
  1042. @@Done:
  1043.     pop     es
  1044.     popad
  1045.     jnc     @@Exit
  1046.     mov     ax, [@@Result]
  1047. @@Exit:
  1048.     ret
  1049. ENDP    FindFirst
  1050.  
  1051. ;---------------------------------------------------------------------
  1052. ; Find Next Matching File
  1053. ; Use of reserved or unused SDB fields:
  1054. ; Offset Size    Function
  1055. ; 0Dh     DWORD    First sector of the directory block last searched.
  1056. ; 11h     WORD    Bit 15   : Set if subdirs are not to be returned (long paths)
  1057. ;        Bits 0-14: Offset into directory block of the last entry 
  1058. ;        examined, or 0=no last entry, 1=only "." entry returned.
  1059. PROC    FindNext STDCALL
  1060.     DEFASSUME
  1061.     LOCAL   @@Result, @@DirBlock:DWORD, @@LastEntry, @@Root:BYTE, @@NoSubDirs:BYTE
  1062.     LOCAL    @@FileAttr:BYTE, @@FileSize:DWORD, @@DTA:DWORD
  1063.     pushad
  1064.     push    es
  1065.     cld
  1066.     mov    [@@Root], 0        ; Flag is set if root dir.
  1067.     les    bx, [pDTA]
  1068.     mov    eax, [es:bx]
  1069.     mov    [@@DTA], eax        ; Address of DTA
  1070.     les     bx, [es:bx]        ; ES:BX -> DTA
  1071.     mov     eax, [es:bx+0Dh]    ; SDB, directory block.
  1072.     mov     [@@DirBlock], eax
  1073.     mov     ax, [es:bx+11h]        ; SDB, offset of last entry
  1074.     mov    [@@NoSubDirs], ah    ; Copy to subdirectory flag
  1075.     and    [@@NoSubDirs], 80h
  1076.     and    ah, 7Fh
  1077.     mov     [@@LastEntry], ax
  1078. @@Search:
  1079.     cmp    [BufUsed], 0
  1080.     jnz    @@GotDirBlock
  1081.     call    NEAR ReadSector, [@@DirBlock], 4, ds (OFFSET Buf1)
  1082.     jc      @@NoMoreFiles
  1083. @@GotDirBlock:
  1084.     mov    [BufUsed], 0
  1085. ; See if we're in the root dir.
  1086.     mov    eax, [DWORD Buf1+0Ch]
  1087.     cmp    eax, [RootFNode]
  1088.     jne    @@TransferTemplate
  1089.     mov    [@@Root], 1        ; Set root dir flag.
  1090. ; Transfer search template to ASCIIZ format in FNameBuf2
  1091. @@TransferTemplate:
  1092.     push    ds
  1093.     pop     es
  1094.     lds     si, [@@DTA]
  1095.     ASSUME    ds:NOTHING, es:ResCode
  1096.     inc    si                ; SDB Search template
  1097.     mov     di, OFFSET FNameBuf2
  1098.     mov     cx, 8
  1099. @@MoveTmpl1:
  1100.     lodsb
  1101.     cmp     al, ' '                         ; Go to extension if blank
  1102.     je      @@MoveTmplExt
  1103.     stosb
  1104.     loop    @@MoveTmpl1
  1105.     inc     si
  1106. @@MoveTmplExt:
  1107.     add     si, cx                          ; Extension field in template
  1108.     dec     si
  1109.     mov     cx, 3
  1110.     mov     al, '.'
  1111.     stosb
  1112. @@MoveTmpl2:
  1113.     lodsb
  1114.     cmp     al, ' '
  1115.     je      @@MoveTmplDone
  1116.     stosb
  1117.     loop    @@MoveTmpl2
  1118. @@MoveTmplDone:
  1119.     xor     al, al                          ; Zero terminate
  1120.     stosb
  1121.     mov    ax, cs
  1122.     mov    ds, ax
  1123.     mov    es, ax
  1124.     ASSUME    ds:ResCode, es:NOTHING
  1125.  
  1126.     mov     bx, [@@LastEntry]
  1127.     cmp    bx, 1
  1128.     ja    @@NextEntry
  1129.     je    @@DotDot
  1130.     mov     bx, 14h                 ; Offset of first dir. entry
  1131.     jmp     @@DoEntry
  1132.  
  1133. @@NextEntry:    
  1134.     test    [BYTE Buf1+bx+02h], 08h ; Last entry in block?
  1135.     jz      @@MoveToNextEntry
  1136. ; Read higher level directory block.
  1137.     call    NEAR ReadSector, [DWORD Buf1+0Ch], 4, ds (OFFSET Buf1)
  1138.     jc    @@NoMoreFiles
  1139. ; Check that it's a directory sector, not the FNode.
  1140.     cmp     [BYTE Buf1+03h], 0F7h      ; FNode signature
  1141.     je      @@NoMoreFiles
  1142. ; Go through this directory block to find the entry that we came from.
  1143.     mov     edx, [@@DirBlock]
  1144.     mov     bx, 14h
  1145. @@SrchParent:
  1146.     test    [Buf1+bx+02h], 04h      ; Entry has B tree pointer?
  1147.     jz      @@NotParent
  1148.     mov     si, bx
  1149.     add     si, [WORD Buf1+bx]
  1150.     cmp     [DWORD Buf1+si-4], edx
  1151.     je      @@FoundParent
  1152. @@NotParent:    
  1153.     test    [Buf1+bx+02h], 08h      ; Last entry in block?
  1154.     jnz     @@NoMoreFiles
  1155.     add     bx, [WORD Buf1+bx]
  1156.     jmp     @@SrchParent
  1157.  
  1158. @@FoundParent:
  1159.     mov     eax, [DWORD Buf1+10h]               ; Sector number
  1160.     mov     [@@DirBlock], eax
  1161.     mov     [@@LastEntry], bx 
  1162.     jmp     @@NoBTree
  1163.  
  1164. @@MoveToNextEntry:
  1165.     add     bx, [WORD Buf1+bx]  ; Move to next entry
  1166. @@DoEntry:
  1167. ; Check if entry has a B Tree pointer
  1168.     mov     [@@LastEntry], bx 
  1169.     test    [BYTE Buf1+bx+02h], 04h
  1170.     jz      @@NoBTree
  1171. ; Go down the branch
  1172.     add     bx, [WORD Buf1+bx]
  1173.     mov     eax, [DWORD Buf1+bx-04h]    ; B Tree pointer
  1174.     mov     [@@DirBlock], eax
  1175.     mov     [@@LastEntry], 0
  1176.     jmp     @@Search
  1177.  
  1178. ; No B Tree, check this entry for a match.
  1179. @@NoBTree:
  1180.     test    [BYTE Buf1+bx+02h], 08h     ; Last entry in block?
  1181.     jnz     @@NextEntry
  1182.     les     di, [@@DTA]
  1183.     ASSUME    es:NOTHING
  1184. ; Match file attributes with AT MOST the specified combination of srch attrib.
  1185.     mov    ah, [Buf1+bx+03h]        ; File attributes
  1186.     cmp    [@@NoSubDirs], 0        ; Allowed to return subdirs?
  1187.     jz    @@SubDirOk
  1188.     and    ah, NOT 10h            ; Subdirectory returned as file
  1189. @@SubDirOk:
  1190.     mov     al, [es:di+0Ch]                 ; Search attributes
  1191.     not     al
  1192.     and     al, ah                ; Compare attributes
  1193.     and     al, 10011110b                   ; Ignore bits 0, 5 and 6
  1194.     jnz     @@NextEntry
  1195.     push    cs
  1196.     pop    es
  1197.     ASSUME    es:ResCode
  1198. ; Check if "." entry.
  1199.     test    [BYTE Buf1+bx+02h], 01h
  1200.     jz    @@NoDot
  1201.     cmp    [@@Root], 0            ; No "." in root.
  1202.     jnz    @@NoDot
  1203. ; Return . file if it matches filespec. 
  1204.     mov    si, OFFSET FNameBuf2
  1205.     call    MatchDot
  1206.     jc    @@NoDot
  1207.     les     di, [@@DTA]
  1208.     ASSUME    es:NOTHING
  1209. ; Write directory entry for . file
  1210.     mov     eax, [@@DirBlock]
  1211.     cmp    [Win95], 0
  1212.     jz    @@Win1
  1213.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1214. @@Win1:
  1215.     mov     [es:di+0Dh], eax               ; Save current dir block
  1216.     mov     ax, 01h                ; Flag "." file returned
  1217.     or    ah, [@@NoSubDirs]        ; Keep subdir flag bit
  1218.     mov     [es:di+11h], ax                ; Save offset of found entry
  1219.     add     di, 15h                      ; Point to found file field
  1220. ; Transfer the filename
  1221.     mov    eax, '   .'
  1222.     stosd
  1223.     mov    eax, '    '
  1224.     stosd
  1225.     stosw
  1226.     stosb
  1227.     mov     al, [Buf1+14h+03h]               ; Attributes
  1228.     stosb
  1229.     add     di, 10                           ; Reserved field
  1230.     lea     si, [Buf1+14h+08h]               ; Timestamp field
  1231.     lodsd
  1232.     call    Unix2DosTime
  1233.     stosd
  1234.     xor     ax, ax
  1235.     stosw                                   ; Cluster #
  1236.     movsd                                   ; File size
  1237.     mov    [BufUsed], 1
  1238.     clc
  1239.     jmp     @@Done
  1240.  
  1241. ; Return ".." if attributes match
  1242. @@DotDot:
  1243.     mov    bx, 14h
  1244.     mov    si, OFFSET FNameBuf2
  1245.     call    MatchDotDot
  1246.     jc    @@NextEntry
  1247.     les     di, [@@DTA]
  1248.     ASSUME    es:NOTHING
  1249. ; Match file attributes with AT MOST the specified combination of srch attrib.
  1250.     mov     al, [es:di+0Ch]                 ; Search attributes
  1251.     not     al
  1252.     and     al, [Buf1+14h+03h]              ; File attributes
  1253.     and     al, 10011110b                   ; Ignore bits 0, 5 and 6
  1254.     jnz     @@NextEntry
  1255. ; Write directory entry for .. file
  1256.     mov     eax, [@@DirBlock]
  1257.     cmp    [Win95], 0
  1258.     jz    @@Win2
  1259.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1260. @@Win2:
  1261.     mov     [es:di+0Dh], eax               ; Save current dir block
  1262.     mov     ax, 14h                ; First entry completed
  1263.     or    ah, [@@NoSubDirs]
  1264.     mov     [es:di+11h], ax                ; Save offset of found entry
  1265.     add     di, 15h                      ; Point to found file field
  1266. ; Transfer the filename
  1267.     mov    eax, '  ..'
  1268.     stosd
  1269.     mov    eax, '    '
  1270.     stosd
  1271.     stosw
  1272.     stosb
  1273.     mov     al, [Buf1+14h+03h]               ; Attributes
  1274.     stosb
  1275.     add     di, 10                           ; Reserved field
  1276.     lea     si, [Buf1+14h+08h]               ; Timestamp field
  1277.     lodsd
  1278.     call    Unix2DosTime
  1279.     stosd
  1280.     xor     ax, ax
  1281.     stosw                                   ; Cluster #
  1282.     movsd                                   ; File size
  1283.     mov    [BufUsed], 1
  1284.     clc
  1285.     jmp     @@Done
  1286.  
  1287.     ASSUME    es:ResCode
  1288. @@NoDot:
  1289. ; See if filename is valid in DOS.
  1290.     test    [BYTE Buf1+bx+03h], 40h
  1291.     jz    @@MoveFileName
  1292. ; Convert long filenames?
  1293.     cmp    [ConvertLong], 0
  1294.     je    @@NextEntry
  1295. ; Convert long filename to valid name in FNameBuf
  1296.     lea    si, [Buf1+bx+1Fh]    
  1297.     mov    di, OFFSET FNameBuf
  1298.     movzx    cx, [BYTE si-1]
  1299.     call    ConvertFilename
  1300.     jmp    @@MatchFilename    
  1301.  
  1302. ; Move filename to FNameBuf, converting it to upper case
  1303. @@MoveFilename:
  1304.     lea     si, [Buf1+bx+1Fh]
  1305.     mov     di, OFFSET FNameBuf
  1306.     mov     cl, [Buf1+bx+1Eh]
  1307.     xor     ch, ch
  1308. @@MoveChar:
  1309.     lodsb
  1310.     call    UpCase
  1311.     stosb
  1312.     loop    @@MoveChar
  1313.     xor     al, al
  1314.     stosb                                   ; Zero terminate
  1315. ; Check if filename matches search template
  1316. @@MatchFilename:
  1317.     mov     dx, OFFSET FNameBuf             ; Filename
  1318.     push    bx
  1319.     mov     bx, OFFSET FNameBuf2            ; Search template, ASCIIZ
  1320.     call    Match
  1321.     pop     bx
  1322.     jc      @@NextEntry
  1323. ; Match found - set directory entry
  1324.     les     di, [@@DTA]
  1325.     ASSUME    es:NOTHING
  1326.     cmp    [Win95], 0
  1327.     jz    @@Win3
  1328.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1329. @@Win3:
  1330.     mov     eax, [@@DirBlock]
  1331.     mov     [es:di+0Dh], eax        ; Save current dir block
  1332.     mov     ax, [@@LastEntry]
  1333.     or    ah, [@@NoSubDirs]
  1334.     mov     [es:di+11h], ax            ; Save offset of found entry
  1335.     add     di, 15h                ; Point to found file field
  1336. ; Pad with spaces.
  1337.     push    di
  1338.     mov     cx, 11
  1339.     mov     al, ' '
  1340.     rep stosb
  1341.     pop     di
  1342. ; Transfer the filename
  1343.     mov     si, OFFSET FNameBuf
  1344.     mov     cx, 9
  1345.     lea     dx, [di+8]
  1346. @@Tf1:
  1347.     lodsb
  1348.     or      al, al
  1349.     je      @@TfDone
  1350.     cmp     al, '.'
  1351.     je      @@TfExt
  1352.     stosb
  1353.     loop    @@Tf1
  1354.  
  1355. ; Transfer the extension
  1356. @@TfExt:
  1357.     mov     cx, 3
  1358.     mov     di, dx
  1359. @@TfExt1:
  1360.     lodsb
  1361.     call    UpCase
  1362.     or      al, al
  1363.     je      @@TfDone
  1364.     stosb
  1365.     loop    @@TfExt1
  1366. @@TfDone:
  1367.     mov     di, dx
  1368.     add     di, 3
  1369.     mov     al, [Buf1+bx+03h]               ; Attributes
  1370.     and    al, 10111111b            ; Mask out 8.3 filename bit
  1371.     cmp    [@@NoSubDirs], 0        ; Allowed to return subdir?
  1372.     jz    @@AttrOk
  1373.     and    al, NOT 10h            ; Return subdir as file
  1374. @@AttrOk:
  1375.     stosb
  1376.     add     di, 10                          ; Reserved field
  1377.     lea     si, [Buf1+bx+08h]               ; Timestamp field
  1378.     lodsd
  1379.     call    Unix2DosTime
  1380.     stosd
  1381.     xor     ax, ax
  1382.     stosw                                   ; Cluster #
  1383.     movsd                    ; File size
  1384.     mov    [BufUsed], 1
  1385.     clc
  1386.     jmp     @@Done
  1387.  
  1388. @@NoMoreFiles:
  1389.     stc
  1390.     mov     [@@Result], errNoMoreFiles
  1391. @@Done:
  1392.     pop     es
  1393.     popad
  1394.     jnc     @@Exit
  1395.     mov     ax, [@@Result]
  1396. @@Exit:
  1397.     ret
  1398. ENDP    FindNext
  1399.  
  1400. ;---------------------------------------------------------------------
  1401. ; Seek from End of File
  1402. ; NOTE Seems like this function never gets called! DOS changes the
  1403. ; file pointers itself.
  1404. PROC    Seek    STDCALL
  1405.     DEFASSUME
  1406.     LOCAL   @@RetValue:DWORD
  1407.     pushad
  1408.     cmp     [WORD es:di], 0     ; Open files count
  1409.     jne     @@SFTOk
  1410.     mov     [WORD @@RetValue], errInvalidHandle
  1411.     stc
  1412.     jmp     @@Done
  1413. @@SFTOk:
  1414.     shl     ecx, 16
  1415.     mov     cx, dx          ; Offset from end of file in ECX
  1416.     mov     eax, [es:di+11h] ; File size
  1417.  
  1418.     sub     eax, ecx
  1419.     jnc     @@1
  1420.     xor     eax, eax
  1421. @@1:
  1422.     mov     [es:di+15h], eax ; New file pos.
  1423.     mov     [@@RetValue], eax
  1424.     clc
  1425. @@Done:
  1426.     popad
  1427.     jnc     @@2
  1428.     mov     ax, [WORD @@RetValue]
  1429.     jmp     @@Exit
  1430. @@2:    mov     ax, [WORD @@RetValue]
  1431.     mov     dx, [WORD @@RetValue+2]
  1432. @@Exit:
  1433.     ret
  1434. ENDP    Seek
  1435.  
  1436. ;---------------------------------------------------------------------
  1437. ; Extended Open File
  1438. ; Never gets called under DR-DOS 6.0 which doesn't support extended open.
  1439. PROC    ExtOpen STDCALL
  1440.     DEFASSUME
  1441.     LOCAL   @@OpenMode, @@RetValue
  1442.     pushad
  1443.     push    fs
  1444.     lfs     si, [ExtOpenMode]
  1445.     mov     ax, [fs:si]                    ; Open mode
  1446.     mov    bx, [WORD LOW AccMode]        ; Exchange with normal open mode
  1447.     xchg    [fs:bx], ax
  1448.     mov     [@@OpenMode], ax
  1449.     call    Open
  1450.     mov     [@@RetValue], ax
  1451.     mov     ax, [@@OpenMode]
  1452.     mov     [fs:bx], ax
  1453.         jc      @@Fail
  1454.     mov     [@@RetValue], 01h                 ; File opened
  1455.     jmp     @@Done
  1456.  
  1457. @@Fail:
  1458. @@Done:
  1459.     pop     fs
  1460.     popad
  1461.     jnc     @@Succeeded
  1462.     mov     ax, [@@RetValue]
  1463.     jmp     @@Exit
  1464. @@Succeeded:
  1465.     mov     cx, [@@RetValue]
  1466. @@Exit:
  1467.     ret
  1468. ENDP    ExtOpen
  1469.  
  1470. ;------------------------------------------------------------------------
  1471. ; Convert logical sector number to Cyl/Head/Sect in CX, DX, and AL
  1472. ; as passed to INT 13h. Logical sector passed in ECX.
  1473. PROC    LogToCHS
  1474.     add    ecx, [LBAStart]
  1475.     movzx   ax, [nSecs]
  1476.     mul    [nHeads]
  1477.     mov     bx, ax          ; Sectors/track * heads
  1478.     mov     ax, cx
  1479.     mov     edx, ecx
  1480.     shr     edx, 16         ; DX:AX = logical sector #
  1481.     div     bx
  1482.     push    ax              ; Cylinder
  1483.     mov     ax, dx
  1484.     div     [nSecs]
  1485.     movzx   dx, al          ; Head
  1486.     mov     cl, ah          ; Sector
  1487.     inc    cl
  1488.     pop     ax              ; Cylinder
  1489.     mov    dh, dl        ; Head
  1490.     mov     ch, al
  1491.     xor     al, al
  1492.     shr     ax, 2
  1493.     or      cl, al          ; bits 8 and 9 of cyl. number go here
  1494.         xor     al, al
  1495.         shr     ax, 2
  1496.         or      dh, al          ; bits 10 and 11 of cyl (BIOS extension)
  1497.     ret
  1498. ENDP    LogToCHS
  1499.  
  1500. ;------------------------------------------------------------------------
  1501. ; Read sectors from disk.
  1502. PROC    DiskRead PASCAL
  1503.     DEFASSUME
  1504.     ARG    @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
  1505.     LOCAL    @@CurrSector:DWORD, @@SectorsToRead:WORD, @@CurrDest:DWORD
  1506.     pushad
  1507.     push    es
  1508.     mov    eax, [@@Sector]
  1509.     mov    [@@CurrSector], eax
  1510.     add    eax, [LBAstart]
  1511.     mov    [DWORD LOW DiskAddrPkt.Sector], eax
  1512.     mov    ax, [@@NumSectors]
  1513.     mov    [@@SectorsToRead], ax
  1514.     mov    [DiskAddrPkt.Count], ax
  1515.     mov    eax, [@@Dest]
  1516.     mov    [@@CurrDest], eax
  1517.     mov    [DiskAddrPkt.Buffer], eax
  1518.     cmp    [AccessMethod], Method_Ext
  1519.     jne    @@ReadCHS
  1520. ; Read using LBA addressing
  1521.     mov    dl, [PhysDrv]
  1522.     mov    si, OFFSET DiskAddrPkt
  1523.     mov    ah, 42h
  1524.     int    13h
  1525.     jc    @@Done
  1526.     jmp    @@CopyToCache
  1527.  
  1528. ; Read using CHS addressing
  1529. @@ReadCHS:
  1530.     mov     ecx, [@@CurrSector]
  1531.     call    LogToCHS
  1532.         mov     al, [BYTE @@NumSectors]
  1533.         cmp     [Multitrack], 0
  1534.         jnz     @@DoInt13
  1535. ; Multitrack operations not supported
  1536.     push    cx
  1537.     and    cl, 3Fh
  1538.     mov    al, [nSecs]
  1539.     sub    al, cl
  1540.     inc    al        ; Sectors left on this track
  1541.     xor    ah, ah
  1542.     cmp    ax, [@@SectorsToRead]
  1543.     jbe    @@3
  1544.     mov    al, [BYTE @@SectorsToRead] ; Read SectorsToRead sectors
  1545. @@3:    pop    cx
  1546. @@DoInt13:
  1547.         mov     dl, [PhysDrv]
  1548.     mov     ah, 02h
  1549.     les     bx, [@@CurrDest]
  1550.     push    ax
  1551.     int     13h
  1552.     pop    ax
  1553.     jc      @@Done
  1554.     and    eax, 0FFh
  1555.     les    di, [@@CurrDest]    ; Buffer
  1556.     movzx    cx, al            ; Number of sectors
  1557.     mov    edx, [@@CurrSector]    ; First sector
  1558.     add    [@@CurrSector], eax
  1559.     sub    [@@SectorsToRead], ax
  1560.     push    dx
  1561.     mov    dx, 200h
  1562.     mul    dx
  1563.     pop    dx
  1564.     add    [WORD @@CurrDest], ax
  1565. ; Check if any more sectors to read
  1566. @@SectorsLeft:
  1567.     cmp    [@@SectorsToRead], 0
  1568.     jnz    @@ReadCHS
  1569. ; Copy read sectors to cache.
  1570. @@CopyToCache:
  1571.     cmp    [CacheOn], 0
  1572.     jz    @@Done
  1573.     cmp    [XMSError], 0
  1574.     jnz    @@Done
  1575.     mov    edx, [@@Sector]
  1576.     les    di, [@@Dest]
  1577.     mov    cx, [@@NumSectors]
  1578. @@CacheLoop:
  1579.     call    GetFreeCacheEntry    ; Get free cache entry in BX.
  1580.     or    bx, bx
  1581.     sete    [XMSError]
  1582.     jz    @@Done
  1583.     push    cx
  1584.     mov    ecx, edx
  1585.     call    CacheInsert        ; Insert sector ECX at ES:DI at entry BX.
  1586.     pop    cx
  1587.     add    edx, 1
  1588.     add    di, 200h
  1589.     loop    @@CacheLoop
  1590.     clc
  1591. @@Done:
  1592.     pop    es
  1593.     popad
  1594.     ret
  1595. ENDP    DiskRead
  1596.  
  1597. ;------------------------------------------------------------------------
  1598. ; Read logical sectors into memory.
  1599. PROC    ReadSector PASCAL
  1600.     DEFASSUME
  1601.     ARG    @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
  1602.     LOCAL   @@SectorsToRead
  1603.     pushad
  1604.     push    es
  1605. @@Start:
  1606.     mov    dx, [@@NumSectors]
  1607.     mov    [@@SectorsToRead], dx
  1608.     cmp     [CacheOn], 0
  1609.     jz      @@DiskRead
  1610.     cmp     [XMSError], 0
  1611.     jnz     @@DiskRead
  1612. @@SearchCache:
  1613.     mov    ecx, [@@Sector]
  1614.     call    SearchCache        ; Search for sector ECX in cache
  1615.     or    bx, bx
  1616.     jz    @@CacheMiss
  1617. ; Cache hit. Move the sector to the destination.
  1618. @@CacheHit:
  1619.     les    di, [@@Dest]
  1620.     call    ReadCacheSector        ; Read sector in entry BX
  1621.     or    ax, ax
  1622.     sete    [XMSError]
  1623.     jz    @@DiskRead
  1624.     inc    [@@Sector]
  1625.     add    [WORD @@Dest], 200h
  1626.     sub    [@@NumSectors], 1        ; Sectors left to read?
  1627.     jnz    @@SearchCache
  1628.     jmp     @@Done
  1629.  
  1630. @@CacheMiss:
  1631. ; Determine number of consecutive sectors to read from disk.
  1632.     mov    ecx, [@@Sector]
  1633.     mov    [@@SectorsToRead], 0
  1634. @@ConsecutiveSectors:
  1635.     inc    ecx
  1636.     inc    [@@SectorsToRead]
  1637.     mov    ax, [@@NumSectors]
  1638.     cmp    ax, [@@SectorsToRead]
  1639.     jbe    @@DiskRead
  1640.     push    ecx
  1641.     call    SearchCache
  1642.     pop    ecx
  1643.     or    bx, bx
  1644.     jz    @@ConsecutiveSectors
  1645.  
  1646. ; Read sectors from disk.
  1647. @@DiskRead:
  1648.     movzx    eax, [@@SectorsToRead]
  1649.     call    NEAR DiskRead, [@@Sector], ax, [@@Dest]
  1650.     jc    @@Done
  1651.     add    [@@Sector], eax
  1652.     sub    [@@NumSectors], ax
  1653.     jz    @@Done        ; No sectors left to read
  1654.     mov    dx, 200h
  1655.     mul    dx
  1656.     add    [WORD @@Dest], ax    
  1657.     jmp     @@Start
  1658.  
  1659. @@Done:
  1660.     pop     es
  1661.     popad
  1662.     ret
  1663. ENDP    ReadSector
  1664.  
  1665. ;---------------------------------------------------------------------
  1666. ; Inserts a sector into the cache at a free entry. Sector number ECX,
  1667. ; entry number BX, memory address ES:DI. Returns CF=1 on XMS error.
  1668. PROC    CacheInsert STDCALL
  1669.     LOCAL    @@EntryNum, @@SectorAddr:DWORD, @@Sector:DWORD
  1670.     DEFASSUME
  1671.         pushad
  1672.     mov     si, OFFSET XMoveStruc
  1673.     mov    [WORD @@SectorAddr], di
  1674.     mov    [WORD @@SectorAddr+2], es
  1675.     mov    [@@Sector], ecx
  1676.     mov    [@@EntryNum], bx
  1677. ; Store the sector number
  1678.     mov     eax, [@@Sector]
  1679.     mov     [DWORD EntryBuf], eax
  1680.     mov    [WORD EntryBuf+0Ch], gs        ; Data segment
  1681. ; Insert the new sector at the head of the priority list.
  1682. ; next[x] <- next[sentinel]
  1683.     mov     [XMoveStruc.Length], 2
  1684.     mov     ax, [hCacheLists]
  1685.     mov     [XMoveStruc.SourceHandle], ax
  1686.     mov     [XMoveStruc.DestHandle], 0
  1687.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+0Ah
  1688.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1689.     mov     [XMoveStruc.SourceOffset], 0Ah  ; Next field of sentinel
  1690.     mov     ah, 0Bh
  1691.     call    [XMSEntry]
  1692.     or      ax, ax
  1693.     jz      @@XMSError
  1694. ; prev[next[sentinel]] <- x
  1695.     mov     ax, [WORD EntryBuf+0Ah]
  1696.     mov     dx, CacheEntrySize
  1697.     mul     dx
  1698.     add     ax, 08h
  1699.     adc     dx, 0
  1700.     mov     [WORD XMoveStruc.DestOffset], ax
  1701.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1702.     mov     ax, [hCacheLists]
  1703.     mov     [XMoveStruc.DestHandle], ax
  1704.     mov     [XMoveStruc.SourceHandle], 0
  1705.     mov    bx, [@@EntryNum]
  1706.     mov     [WORD EntryBuf+CacheEntrySize], bx
  1707.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1708.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1709.     mov     ah, 0Bh
  1710.     call    [XMSEntry]
  1711.     or      ax, ax
  1712.     jz      @@XMSError
  1713. ; next[sentinel] <- x
  1714.     mov     [XMoveStruc.DestOffset], 0Ah
  1715.     mov     ah, 0Bh
  1716.     call    [XMSEntry]
  1717.     or      ax, ax
  1718.     jz      @@XMSError
  1719. ; prev[x] <- sentinel
  1720.     mov     [EntryBuf+08h], 0
  1721. ; Compute hash function
  1722.     mov     eax, [@@Sector]
  1723.     mov     edx, eax
  1724.     shr     edx, 16         ; Sector in DX:AX
  1725.     div     [HashSize]      ; Hash slot in DX
  1726.     shl     edx, 1          ; Offset into hash table
  1727.     mov     edi, edx        ; Save offset
  1728. ; Insert the entry at the head of the hash table
  1729. ; next[x] <- head
  1730.     mov     ax, [hHashTable]
  1731.     mov     [XMoveStruc.SourceHandle], ax
  1732.     mov     [XMoveStruc.SourceOffset], edx
  1733.     mov     [XMoveStruc.DestHandle], 0
  1734.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+06h
  1735.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1736.     mov     ah, 0Bh
  1737.     call    [XMSEntry]
  1738.     or      ax, ax
  1739.     jz      @@XMSError
  1740. ; if head <> NIL then prev[head] <- x
  1741.     mov     ax, [WORD EntryBuf+06h]
  1742.     or      ax, ax
  1743.     jz      @@HashInsert2
  1744.     mov     dx, CacheEntrySize
  1745.     mul     dx
  1746.     add     ax, 04h         ; Prev
  1747.     adc     dx, 0
  1748.     mov     [WORD XMoveStruc.DestOffset], ax
  1749.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1750.     mov     ax, [hCacheLists]
  1751.     mov     [XMoveStruc.DestHandle], ax
  1752.     mov     [XMoveStruc.SourceHandle], 0
  1753.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1754.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1755.     mov     ah, 0Bh
  1756.     call    [XMSEntry]
  1757.     or      ax, ax
  1758.     jz      @@XMSError
  1759. @@HashInsert2:
  1760. ; head <- x
  1761.     mov     [XMoveStruc.SourceHandle], 0
  1762.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1763.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1764.     mov     ax, [hHashTable]
  1765.     mov     [XMoveStruc.DestHandle], ax
  1766.     mov     [XMoveStruc.DestOffset], edi    ; Saved offset into hash table
  1767.     mov     ah, 0Bh
  1768.     call    [XMSEntry]
  1769.     or      ax, ax
  1770.     jz      @@XMSError
  1771. ; prev[x] <- NIL
  1772.     mov     [WORD EntryBuf+04h], 0
  1773. ; Write the entry
  1774.     mov     ax, [hCacheLists]
  1775.     mov     [XMoveStruc.DestHandle], ax
  1776.     mov    ax, [@@EntryNum]
  1777.     mov     dx, CacheEntrySize
  1778.     mul     dx
  1779.     mov     [WORD XMoveStruc.DestOffset], ax
  1780.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1781.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf
  1782.     mov     [XMoveStruc.Length], CacheEntrySize
  1783.     mov     ah, 0Bh
  1784.     call    [XMSEntry]
  1785.     or      ax, ax
  1786.     jz      @@XMSError
  1787.  
  1788. ; Copy the sector to the cache
  1789.     mov     [XMoveStruc.Length], 512
  1790.     mov     eax, [@@SectorAddr]
  1791.     mov     [XMoveStruc.SourceOffset], eax
  1792.     mov     [XMoveStruc.SourceHandle], 0
  1793.     mov     ax, [hCacheSectors]
  1794.     mov     [XMoveStruc.DestHandle], ax
  1795.     movzx   eax, [@@EntryNum]
  1796.     shl     eax, 9
  1797.     mov     [XMoveStruc.DestOffset], eax
  1798.     mov     si, OFFSET XMoveStruc
  1799.     mov     ah, 0Bh
  1800.     call    [XMSEntry]
  1801.     or      ax, ax
  1802.     clc
  1803.     jnz     @@Done
  1804.  
  1805. @@XMSError:
  1806.     mov     [XMSError], 1   ; Flag XMS error
  1807.     stc
  1808. @@Done: popad
  1809.     ret
  1810. ENDP    CacheInsert
  1811.  
  1812. ;---------------------------------------------------------------------
  1813. ; Searches for a sector in the cache. Sector number in ECX. Returns
  1814. ; cache entry in BX. If sector is not in cache, BX=0000.
  1815. PROC    SearchCache STDCALL
  1816.     DEFASSUME
  1817.     LOCAL    @@Sector:DWORD
  1818. ; Compute the hash function (sector number modulo slots in hash table)
  1819.     cmp    [XMSError], 0
  1820.     jnz    @@XMSError
  1821.     mov    [@@Sector], ecx
  1822.     mov     ax, cx
  1823.     mov    edx, ecx
  1824.     shr    edx, 16        ; Sector in DX:AX
  1825.     div     [HashSize]      ; Hash slot in DX
  1826. ; Extract the linked list pointer from the hash table
  1827.     mov     [XMoveStruc.Length], 2
  1828.     mov     ax, [hHashTable]
  1829.     mov     [XMoveStruc.SourceHandle], ax
  1830.     shl    edx, 1
  1831.     mov     [XMoveStruc.SourceOffset], edx
  1832.     mov     [XMoveStruc.DestHandle], 0
  1833.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
  1834.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1835.     mov     si, OFFSET XMoveStruc
  1836.     mov     ah, 0Bh
  1837.     call    [XMSEntry]
  1838.     or      ax, ax
  1839.     jz      @@XMSError
  1840.     mov     bx, [WORD EntryBuf]
  1841. ; Walk the linked list and search for the sector
  1842. ; Entry format: Offset  Size    Descr
  1843. ;               00h     DWORD   Sector number
  1844. ;               04h     WORD    Prev entry in hash chain
  1845. ;               06h     WORD    Next entry in hash chain
  1846. ;               08h     WORD    Prev entry in priority list
  1847. ;               0Ah     WORD    Next entry in priority list
  1848. ;        0Ch    WORD    ResData segment of owner drive
  1849.     mov     [XMoveStruc.Length], CacheEntrySize
  1850.     mov     ax, [hCacheLists]
  1851.     mov     [XMoveStruc.SourceHandle], ax
  1852. @@SearchList:
  1853.     or      bx, bx
  1854.     je      @@Done        ; Sector not in cache, exit.
  1855. ; Copy the entry into first bytes of buffer
  1856.     mov     ax, bx
  1857.     mov     dx, CacheEntrySize
  1858.     mul     dx        ; Offset of entry in DX:AX
  1859.     mov    [WORD XMoveStruc.SourceOffset], ax
  1860.     mov    [(WORD XMoveStruc.SourceOffset)+2], dx
  1861.     mov     si, OFFSET XMoveStruc
  1862.     mov     ah, 0Bh
  1863.     push    bx
  1864.     call    [XMSEntry]
  1865.     pop    bx
  1866.     or      ax, ax
  1867.     jz      @@XMSError
  1868. ; Examine the entry in buffer
  1869.     mov     eax, [@@Sector]
  1870.     cmp     [DWORD EntryBuf], eax
  1871.     jne    @@Search1
  1872.     mov    ax, gs
  1873.         cmp    [WORD EntryBuf+0Ch], ax    ; Check if it's the right drive.
  1874.     je      @@Done        ; Sector found, exit with entry in BX.
  1875. @@Search1:
  1876.     mov     bx, [WORD EntryBuf+06h]       ; Next entry in chain
  1877.     jmp     @@SearchList
  1878.  
  1879. @@XMSError:
  1880.     mov     [XMSError], 1   ; Flag XMS error
  1881.     xor    bx, bx
  1882. @@Done:    ret
  1883. ENDP    SearchCache
  1884.  
  1885. ;---------------------------------------------------------------------
  1886. ; Delete a cache entry. Entry number in BX. Entry must be in use.
  1887. ; Returns carry set on XMS error.
  1888. PROC    DeleteCacheEntry
  1889.         pushad
  1890. ; Read the entry into buffer
  1891.     mov     [XMoveStruc.Length], CacheEntrySize
  1892.     mov     ax, bx
  1893.     mov     dx, CacheEntrySize
  1894.     mul     dx
  1895.     mov     [WORD XMoveStruc.SourceOffset], ax
  1896.     mov     [WORD (XMoveStruc.SourceOffset)+2], dx
  1897.     mov     ah, 0Bh
  1898.     call    [XMSEntry]
  1899.     or      ax, ax
  1900.     jz      @@XMSError
  1901. ; Delete the entry from the hash chain
  1902. ; if prev[x] <> NIL        
  1903. ;   then next[prev[x]] <- next[x]
  1904. ;   else head <- next[x]
  1905. ; if next[x] <> NIL
  1906. ;   then prev[next[x]] <- prev[x]
  1907.     mov     ax, [WORD EntryBuf+04h] ; Prev field. 
  1908.     or      ax, ax                      ; See if entry is head
  1909.     je      @@NewHead
  1910.     mov     dx, CacheEntrySize
  1911.     mul     dx
  1912.     add     ax, 06h                     ; Next field
  1913.     adc     dx, 0
  1914.     mov     [WORD XMoveStruc.DestOffset], ax
  1915.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1916.     mov     ax, [hCacheLists]
  1917.     mov     [XMoveStruc.DestHandle], ax
  1918.     mov     [XMoveStruc.SourceHandle], 0
  1919.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
  1920.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1921.     mov     [XMoveStruc.Length], 2
  1922.     mov     si, OFFSET XMoveStruc
  1923.     mov     ah, 0Bh
  1924.     call    [XMSEntry]
  1925.     or      ax, ax
  1926.     jz      @@XMSError
  1927.     jmp     @@UpdateNextChainEntry
  1928. @@NewHead:
  1929.     xor     edx, edx
  1930.     mov     ax, [WORD EntryBuf]
  1931.     mov     dx, [WORD EntryBuf+2]   ; Sector in DX:AX
  1932.     div     [HashSize]              ; Hash slot in DX
  1933.     mov     ax, [hHashTable]
  1934.     mov     [XMoveStruc.DestHandle], ax
  1935.     shl     edx, 1
  1936.     mov     [XMoveStruc.DestOffset], edx
  1937.     mov     [XMoveStruc.SourceHandle], 0
  1938.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
  1939.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1940.     mov     [XMoveStruc.Length], 2
  1941.     mov     si, OFFSET XMoveStruc
  1942.     mov     ah, 0Bh
  1943.     call    [XMSEntry]
  1944.     or      ax, ax
  1945.     jz      @@XMSError
  1946. @@UpdateNextChainEntry:
  1947.     mov     ax, [WORD EntryBuf+06h]
  1948.     or      ax, ax
  1949.     je      @@DeleteEntry2
  1950.     mov     dx, CacheEntrySize
  1951.     mul     dx
  1952.     add     ax, 04h
  1953.     adc     dx, 0
  1954.     mov     [WORD XMoveStruc.DestOffset], ax
  1955.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1956.     mov     ax, [hCacheLists]
  1957.     mov     [XMoveStruc.DestHandle], ax
  1958.     mov     [XMoveStruc.SourceHandle], 0
  1959.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+04h
  1960.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1961.     mov     [XMoveStruc.Length], 2
  1962.     mov     ah, 0Bh
  1963.     call    [XMSEntry]
  1964.     or      ax, ax
  1965.     jz      @@XMSError
  1966. @@DeleteEntry2:
  1967. ; Delete the entry from the priority list.
  1968. ; next[prev[x]] <- next[x]
  1969. ; prev[next[x]] <- prev[x]
  1970.     mov     ax, [WORD EntryBuf+08h]         ; Prev
  1971.     mov     dx, CacheEntrySize
  1972.     mul     dx
  1973.     add     ax, 0Ah                         ; Next field
  1974.     adc     dx, 0
  1975.     mov     [WORD XMoveStruc.DestOffset], ax
  1976.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1977.     mov     ax, [hCacheLists]
  1978.     mov     [XMoveStruc.DestHandle], ax
  1979.     mov     [XMoveStruc.SourceHandle], 0
  1980.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah
  1981.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1982.     mov     [XMoveStruc.Length], 2
  1983.     mov     ah, 0Bh
  1984.     call    [XMSEntry]
  1985.     or      ax, ax
  1986.     jz      @@XMSError
  1987.  
  1988.     mov     ax, [WORD EntryBuf+0Ah]         ; Next
  1989.     mov     dx, CacheEntrySize
  1990.     mul     dx
  1991.     add     ax, 08h                         ; Prev field
  1992.     adc     dx, 0
  1993.     mov     [WORD XMoveStruc.DestOffset], ax
  1994.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1995.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h
  1996.     mov     ah, 0Bh
  1997.     call    [XMSEntry]
  1998.     or      ax, ax
  1999.     jz      @@XMSError
  2000.     clc
  2001.     jmp    @@Done
  2002. @@XMSError:
  2003.     stc
  2004. @@Done: popad
  2005.     ret
  2006. ENDP    DeleteCacheEntry
  2007.  
  2008. ;---------------------------------------------------------------------
  2009. ; Return a free cache entry. If cache is full, then oldest entry is
  2010. ; deleted. Free entry returned in BX. BX=0000 if an XMS error occurs.
  2011. PROC    GetFreeCacheEntry STDCALL
  2012.     LOCAL    Entry
  2013.         pushad
  2014.     mov     bx, [FreeEntry]
  2015.     or      bx, bx
  2016.     jz    @@GetOldestEntry
  2017.     mov    [Entry], bx
  2018.     dec     [FreeEntry]
  2019.     jmp    @@Done
  2020. ; Get the oldest entry.        
  2021. @@GetOldestEntry:
  2022.     mov     [XMoveStruc.Length], 2
  2023.     mov     ax, [hCacheLists]
  2024.     mov     [XMoveStruc.SourceHandle], ax
  2025.     mov     [XMoveStruc.SourceOffset], 08h  ; Tail of priority list
  2026.     mov     [XMoveStruc.DestHandle], 0
  2027.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
  2028.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  2029.     mov     si, OFFSET XMoveStruc
  2030.     mov     ah, 0Bh
  2031.     call    [XMSEntry]
  2032.     or      ax, ax
  2033.     jz      @@XMSError
  2034.     mov     bx, [WORD EntryBuf]     ; Entry to be deleted
  2035.     mov     [Entry], bx
  2036.     call    DeleteCacheEntry
  2037.     jc    @@XMSError
  2038.     jmp    @@Done
  2039. @@XMSError:
  2040.     mov     [XMSError], 1   ; Flag XMS error
  2041.     mov    [Entry], 0
  2042. @@Done: popad
  2043.     mov    bx, [Entry]
  2044.     ret
  2045. ENDP    GetFreeCacheEntry
  2046.  
  2047. ;---------------------------------------------------------------------
  2048. ; Read a sector from the cache. Cache entry in BX. The entry is moved
  2049. ; to the head of the priority list. Sector is copied to ES:DI.
  2050. ; Returns AX=0 if XMS error.
  2051. PROC    ReadCacheSector STDCALL
  2052.     LOCAL    @@Entry
  2053.     mov    [@@Entry], bx
  2054.     mov     [XMoveStruc.Length], 512
  2055.     mov     ax, [hCacheSectors]
  2056.     mov     [XMoveStruc.SourceHandle], ax
  2057.     movzx   eax, bx
  2058.     shl     eax, 9                  ; Multiply by 512
  2059.     mov     [XMoveStruc.SourceOffset], eax
  2060.     mov     [XMoveStruc.DestHandle], 0
  2061.     mov    [WORD XMoveStruc.DestOffset], di
  2062.     mov    [(WORD XMoveStruc.DestOffset)+2], es
  2063.     mov     si, OFFSET XMoveStruc
  2064.     mov     ah, 0Bh
  2065.     call    [XMSEntry]
  2066.     or      ax, ax
  2067.     jz      @@XMSError
  2068. ; Remove the entry from the priority list.
  2069. ; Entry 0 in priority list is a sentinel.        
  2070. ; next[prev[x]] <- next[x]
  2071.     mov     [XMoveStruc.Length], 2
  2072.     mov     [XMoveStruc.SourceHandle], 0
  2073.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  2074.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah ; Next
  2075.     mov     ax, [hCacheLists]
  2076.     mov     [XMoveStruc.DestHandle], ax
  2077.     mov     ax, [WORD EntryBuf+08h] ; Prev
  2078.     mov     dx, CacheEntrySize
  2079.     mul     dx                      ; Offset
  2080.     add     ax, 0Ah                 ; Next field of previous entry
  2081.     adc     dx, 0
  2082.     mov     [WORD XMoveStruc.DestOffset], ax
  2083.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2084.     mov     ah, 0Bh
  2085.     call    [XMSEntry]        
  2086.     or      ax, ax
  2087.     jz      @@XMSError
  2088. ; prev[next[x]] <- prev[x]
  2089.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h ; Prev
  2090.     mov     ax, [WORD EntryBuf+0Ah] ; Next
  2091.     mov     dx, CacheEntrySize
  2092.     mul     dx                      ; Offset
  2093.     add     ax, 08h                 ; Prev field
  2094.     adc     dx, 0
  2095.     mov     [WORD XMoveStruc.DestOffset], ax
  2096.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2097.     mov     ah, 0Bh
  2098.     call    [XMSEntry]        
  2099.     or      ax, ax
  2100.     jz      @@XMSError
  2101. ; Insert the entry at the head of the priority list
  2102. ; next[x] <- next[sentinel]
  2103.     mov     ax, [hCacheLists]
  2104.     mov     [XMoveStruc.SourceHandle], ax
  2105.     mov     [XMoveStruc.SourceOffset], 0Ah  ; Next field of sentinel
  2106.  
  2107.     mov     ax, [@@Entry]                   ; Entry number
  2108.     mov     dx, CacheEntrySize
  2109.     mul     dx
  2110.     mov     di, dx
  2111.     shl     edi, 16
  2112.     mov     di, ax
  2113.     add     edi, 0Ah                        ; Next field
  2114.     mov     [XMoveStruc.DestOffset], edi
  2115.     mov     ah, 0Bh
  2116.     call    [XMSEntry]
  2117.     or      ax, ax
  2118.     jz      @@XMSError
  2119. ; prev[next[sentinel]] <- x
  2120.     mov     [XMoveStruc.DestHandle], 0        
  2121.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+CacheEntrySize
  2122.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  2123.     mov     ah, 0Bh
  2124.     call    [XMSEntry]
  2125.     or      ax, ax
  2126.     jz      @@XMSError
  2127.     mov     ax, [WORD EntryBuf+CacheEntrySize]     ; next[sentinel]
  2128.     mov     dx, CacheEntrySize
  2129.     mul     dx
  2130.     add     ax, 08h                 ; prev field
  2131.     adc     dx, 0
  2132.     mov     [WORD XMoveStruc.DestOffset], ax
  2133.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2134.     mov     ax, [hCacheLists]
  2135.     mov     [XMoveStruc.DestHandle], ax
  2136.     mov    ax, [@@Entry]
  2137.     mov     [WORD EntryBuf+CacheEntrySize], ax     ; Current entry
  2138.     mov     [XMoveStruc.SourceHandle], 0
  2139.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  2140.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  2141.     mov     ah, 0Bh
  2142.     call    [XMSEntry]
  2143.     or      ax, ax
  2144.     jz      @@XMSError
  2145. ; next[sentinel] <- x
  2146.     mov     [XMoveStruc.DestOffset], 0Ah
  2147.     mov     ah, 0Bh
  2148.     call    [XMSEntry]
  2149.     or      ax, ax
  2150.     jz      @@XMSError
  2151. ; prev[x] <- sentinel
  2152.     mov     [WORD EntryBuf+CacheEntrySize], 0
  2153.     mov    ax, [@@Entry]
  2154.     mov     dx, CacheEntrySize
  2155.     mul     dx
  2156.     add     ax, 08h                 ; prev field
  2157.     adc     dx, 0
  2158.     mov     [WORD XMoveStruc.DestOffset], ax
  2159.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2160.     mov     ah, 0Bh
  2161.     call    [XMSEntry]
  2162.     or      ax, ax
  2163.     jz      @@XMSError
  2164.     jmp    @@Done
  2165.  
  2166. @@XMSError:
  2167.     mov     [XMSError], 1   ; Flag XMS error
  2168. @@Done:    ret
  2169. ENDP    ReadCacheSector
  2170.  
  2171. ;---------------------------------------------------------------------
  2172. ; Read specified file sectors. Arguments:
  2173. ; @@Sector    - starting file sector
  2174. ; @@NumSectors    - number of sectors to read
  2175. ; @@FNode       - FNode sector number of file
  2176. ; @@Dest    - destination buffer
  2177. ; Uses Buf4. Return CF on error.
  2178. PROC    ReadFileSector PASCAL
  2179.     DEFASSUME
  2180.         ARG     @@Sector:DWORD, @@NumSectors, @@FNode:DWORD, @@Dest:DWORD
  2181.         LOCAL   @@Entries:WORD
  2182.         pushad
  2183. ; Read the FNode
  2184.         call    NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
  2185.         mov     bx, OFFSET Buf4+38h
  2186. @@Start:
  2187.     mov    eax, [@@Sector]
  2188.     movzx    cx, [BYTE bx+05h]    ; number of entries in sector
  2189.     mov    [@@Entries], cx
  2190.     test    [BYTE bx], 80h        ; Internal or external node?
  2191.     jnz    @@Internal
  2192. ; Search external node - extents.
  2193.     sub    bx, 4            ; Point to 12 bytes before first entry
  2194.     xor    dx, dx            ; Entry counter
  2195. @@NextExtent:    
  2196.     add    bx, 12            ; Point to next entry
  2197.     inc    dx
  2198.     cmp    dx, [@@Entries]        ; Any entries left?
  2199.     ja    @@Error
  2200.     mov    ecx, [DWORD bx]        ; First file sector in this extent
  2201.     cmp    ecx, eax
  2202.     ja    @@Error            ; Too high - something's wrong
  2203.     add    ecx, [DWORD bx+04h]    ; Add number of sectors in extent
  2204.     sub    ecx, eax        ; Sector in this extent?
  2205.     jna    @@NextExtent
  2206. ; Extent found - calculate disk sector and read.
  2207.     mov    esi, ecx        ; Number of sectors left in extent
  2208.     mov    ecx, eax
  2209.     sub    ecx, [DWORD bx]        ; Offset into extent
  2210.     add    ecx, [DWORD bx+08h]    ; First logical sector number
  2211. ; Calculate number of sectors to read.
  2212.     movzx    edi, [@@NumSectors]
  2213.     cmp    esi, edi
  2214.     jbe    @@1
  2215.     mov    si, di
  2216. @@1:
  2217.     call    NEAR ReadSector, ecx, si, [@@Dest]
  2218.     jc    @@Error
  2219.     sub    [@@NumSectors], si
  2220.     clc
  2221.     jz    @@Done
  2222.     and    esi, 0FFFFh
  2223.     add    [@@Sector], esi
  2224. ; Reread FNode
  2225.         call    NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
  2226.         mov     bx, OFFSET Buf4+38h
  2227.     mov    ax, 200h
  2228.     mul    si
  2229.     add    [WORD LOW @@Dest], ax    ; High word in DX not used, should be 0.
  2230.     jmp    @@Start
  2231.  
  2232. ; Search internal node
  2233. @@Internal:
  2234.     xor    dx, dx            ; Entry counter
  2235. @@NextSubtree:    
  2236.     add    bx, 8            ; Point to next entry
  2237.     inc    dx
  2238.     cmp    dx, [@@Entries]        ; Any entries left?
  2239.     ja    @@Error
  2240.     cmp    eax, [DWORD bx]    ; If below, then sector is in this tree.
  2241.     jae    @@NextSubtree
  2242. ; Subtree found - read sector and recurse.
  2243.     call    NEAR ReadSector, [DWORD bx+04h], 1, ds (OFFSET Buf4)
  2244.     jc    @@Error
  2245.     mov    bx, OFFSET Buf4+0Ch    ; Point to allocation structure
  2246.     jmp    @@Start
  2247.  
  2248. @@Error:
  2249.     stc
  2250. @@Done: popad
  2251.     ret
  2252. ENDP    ReadFileSector
  2253.  
  2254. ;---------------------------------------------------------------------
  2255. ; Convert a character in AL to upper case.
  2256. PROC    UpCase
  2257.     DEFASSUME
  2258.     cmp     al, 128
  2259.     jae     @@Extended
  2260.     cmp     al, 'a'
  2261.     jb      @@Done
  2262.     cmp     al, 'z'
  2263.     ja      @@Done
  2264.     sub     al, 'a'-'A'
  2265. @@Done: ret
  2266. @@Extended:
  2267.     push    bx ds
  2268.     push    cs
  2269.     pop     ds
  2270.     mov     bx, OFFSET UpCaseTbl-128
  2271.     xlatb
  2272.     pop     ds bx
  2273.     ret
  2274. ENDP    UpCase
  2275.  
  2276. ;---------------------------------------------------------------------
  2277. ; Checks if the character in AL is a valid filename character.
  2278. ; CF=1 if invalid. Registers preserved.
  2279. PROC    FileChar
  2280.     DEFASSUME
  2281.     push    ax ds
  2282.     push    cs
  2283.     pop     ds
  2284.     cmp     al, [MinPerm]
  2285.     jb      @@Fail
  2286.     cmp     al, [MaxPerm]
  2287.     ja      @@Fail
  2288.     cmp     al, [MinExcl]
  2289.     jb      @@1
  2290.     cmp     al, [MaxExcl]
  2291.     jna     @@Fail
  2292. @@1:
  2293.     push    cx di es
  2294.     push    ds
  2295.     pop     es
  2296.     ASSUME    es:ResCode
  2297.     mov     cl, [NumTerm]
  2298.     xor     ch, ch
  2299.     mov     di, OFFSET TermChars
  2300.     cld
  2301.     repne scasb
  2302.     pop     es di cx
  2303.     je      @@Fail
  2304.     clc     
  2305.     jmp     @@Done
  2306. @@Fail:
  2307.     stc
  2308. @@Done:
  2309.     pop     ds ax
  2310.     ret
  2311. ENDP    FileChar
  2312.  
  2313. ;---------------------------------------------------------------------
  2314. ; Checks if a filename matches a spec.
  2315. ; Filename at DS:DX. Filespec at ES:BX. 
  2316. ; Returns CF=0 if match.
  2317. PROC    Match
  2318.     DEFASSUME
  2319.     ASSUME    ds:NOTHING
  2320.     push    bx cx dx si di
  2321.     cld
  2322.     mov     si, dx
  2323. @@Next:
  2324.     mov     ah, [es:bx]
  2325.     inc     bx
  2326.     lodsb
  2327. ; Check if the character is valid in filenames
  2328.     or      al, al
  2329.     je      @@Valid
  2330.     cmp     al, '.'
  2331.     je      @@Valid
  2332.     call    FileChar
  2333.     jc      @@Fail
  2334. @@Valid:
  2335.     or      ah, ah          ; End of filespec?
  2336.     jne     @@1
  2337.     or      al, al
  2338.     jne     @@Fail
  2339.     jmp     @@Succeed
  2340. @@1:    cmp     ah, '?'
  2341.     jne     @@3
  2342.     cmp     al, '.'
  2343.     je      @@2
  2344.     or      al, al
  2345.     je      @@2
  2346.     jmp     @@Next
  2347. @@2:    dec     si
  2348.     jmp     @@Next
  2349. @@3:    cmp     ah, '.'
  2350.     jne     @@4
  2351.     or      al, al
  2352.     jne     @@4
  2353.     dec     si
  2354.     jmp     @@Next
  2355. @@4:    cmp     ah, al
  2356.     je      @@Next
  2357. @@Fail:
  2358.     stc
  2359.     jmp     @@Done
  2360. @@Succeed:
  2361.     clc
  2362. @@Done: pop     di si dx cx bx
  2363.     ret
  2364. ENDP    Match
  2365.  
  2366. ;---------------------------------------------------------------------
  2367. ; Check if filespec at DS:SI matches . file. Returns CF=0 if match.
  2368. ; Filespec matched: At least one <?>, optional <dot>, optional more <?>.
  2369. PROC    MatchDot
  2370.     NOASSUME
  2371.     ASSUME    cs:ResCode
  2372.         push    ax si
  2373. ; Match one <?>
  2374.     lodsb
  2375.     cmp    al, '?'
  2376.     jne    @@Fail
  2377. ; Eat all consecutive <?>
  2378. @@1:    lodsb
  2379.     cmp    al, '?'
  2380.     je    @@1
  2381.     or    al, al        ; End of filespec?
  2382.     je    @@Ok
  2383.     cmp    al, '.'        ; Match dot (separator)
  2384.     jne    @@Fail
  2385. ; Eat all consecutive <?>
  2386. @@2:    lodsb
  2387.     cmp    al, '?'
  2388.     je    @@2
  2389.     or    al, al        ; End of filespec?
  2390.     jne    @@Fail
  2391.  
  2392. @@Ok:    clc
  2393.     jmp    @@Done
  2394. @@Fail:    stc
  2395. @@Done:    pop    si ax
  2396.     ret
  2397. ENDP    MatchDot
  2398.     
  2399. ;---------------------------------------------------------------------
  2400. ; Check if filespec at DS:SI matches .. file. Returns CF=0 if match.
  2401. ; Filespec matched: At least two <?>, optional <dot>, optional more <?>.
  2402. PROC    MatchDotDot
  2403.     NOASSUME
  2404.     ASSUME    cs:ResCode
  2405.         push    ax si
  2406. ; Match two <?>
  2407.     lodsw
  2408.     cmp    ax, '??'
  2409.     jne    @@Fail
  2410. ; Eat all consecutive <?>
  2411. @@1:    lodsb
  2412.     cmp    al, '?'
  2413.     je    @@1
  2414.     or    al, al        ; End of filespec?
  2415.     je    @@Ok
  2416.     cmp    al, '.'        ; Match dot (separator)
  2417.     jne    @@Fail
  2418. ; Eat all consecutive <?>
  2419. @@2:    lodsb
  2420.     cmp    al, '?'
  2421.     je    @@2
  2422.     or    al, al        ; End of filespec?
  2423.     jne    @@Fail
  2424.  
  2425. @@Ok:    clc
  2426.     jmp    @@Done
  2427. @@Fail:    stc
  2428. @@Done:    pop    si ax
  2429.     ret
  2430. ENDP    MatchDotDot
  2431.     
  2432. ;---------------------------------------------------------------------
  2433. ; Convert an HPFS filename to a valid DOS filename. Only to be called
  2434. ; from FindNext.
  2435. ; DS:SI - filename to convert. ES:DI - buffer to place converted name.
  2436. ; CX - length of filename.
  2437. PROC    ConvertFilename STDCALL
  2438.     DEFASSUME
  2439.     LOCAL    @@Len:WORD
  2440.     pusha
  2441.     cld
  2442.     mov    [@@Len], cx
  2443.     xor    ah, ah
  2444. @@ConvName:
  2445.     jcxz    @@ConvExt
  2446.     lodsb
  2447.     dec    cx
  2448.     cmp    al, '.'
  2449.     je    @@ConvExt
  2450.     call    FileChar    ; Character valid in filenames?    
  2451.     jc    @@ConvName
  2452.     call    UpCase
  2453.     stosb
  2454.     inc    ah
  2455.     cmp    ah, 8
  2456.     jb    @@ConvName
  2457. @@ConvExt:
  2458.     or    ah, ah        ; Empty name?
  2459.     jnz    @@Conv1
  2460.     mov    eax, 'SFPH'    ; Dummy name
  2461.     stosd
  2462. @@Conv1:
  2463.     mov    al, '.'
  2464.     stosb
  2465.     lea    si, [Buf1+bx+1Fh]    ; Point to filename
  2466.     mov    cx, [@@Len]        ; Length of filename
  2467.     call    CRC16            ; CRC16 in DX
  2468.     mov    ax, dx
  2469.     xor    dx, dx
  2470. ; Convert to base-41 
  2471.     mov    bx, OFFSET ConvTable    ; Translation table
  2472.     mov    cx, 3
  2473. @@ConvBase:
  2474.     mov    si, 41
  2475.     div    si            ; Remainder in DX (DL)
  2476.     xchg    al, dl
  2477.     xlatb
  2478.     stosb
  2479.     mov    al, dl
  2480.     xor    dx, dx
  2481.     loop    @@ConvBase
  2482.     xor    al, al            ; Zero terminate
  2483.     stosb
  2484.     popa
  2485.     ret
  2486. ENDP    ConvertFilename
  2487.  
  2488. ;---------------------------------------------------------------------
  2489. ; Find a directory entry. FNode sector of directory passed in ECX. 
  2490. ; Filename at DS:DX. Assumes DS=CS.
  2491. ; CF=1 if failed.
  2492. ; The sector is read into Buf1, and BX contains the offset to the
  2493. ; specified entry on return. Uses last half of FNameBuf
  2494. PROC    FindDirEntry STDCALL
  2495.     DEFASSUME
  2496.     ASSUME    es:ResCode
  2497.     LOCAL   @@Len:BYTE, @@BytesRead:WORD, @@Sector:DWORD
  2498.     push    es
  2499.     push    cs
  2500.     pop    es
  2501.     mov    [@@Sector], ecx
  2502. ; Count characters in filename.
  2503.     mov     di, dx
  2504.     xor     al, al
  2505.     mov     cx, 0FFh
  2506.     cld
  2507.     repne scasb
  2508.     neg     cl
  2509.     sub     cl, 2
  2510.     or      cl, cl
  2511.     jz      @@Fail
  2512.     mov     [@@Len], cl
  2513.     mov     [@@BytesRead], 200h       ; Bytes of directory block in memory
  2514. ; Read the FNode of the directory
  2515.     call    NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
  2516.     jc    @@Fail
  2517.     mov     ecx, [DWORD Buf1+48h]
  2518.     mov     [@@Sector], ecx
  2519.     call    NEAR ReadSector, ecx, 4, es (OFFSET Buf1)
  2520.     jc    @@Fail
  2521.  
  2522.     mov     bx, 14h         ; Offset of first dir. entry
  2523.     cld
  2524.     cmp    [ConvertLong], 0
  2525.     je    @@TestEntry
  2526. ; Traverse the directory B Tree to find a match. Convert long filenames.
  2527. ; All entries must be searched.
  2528. @@ChkValidName:
  2529.     mov    di, OFFSET FNameBuf+64    ; Load regs for call or move later
  2530.     lea    si, [Buf1+bx+1Fh]
  2531.     movzx    cx, [BYTE si-1]
  2532.     test    [Buf1+bx+03h], 40h
  2533.     jz    @@DOSName
  2534. ; Convert filename
  2535.     call    ConvertFilename
  2536.     jmp    @@CompareName
  2537. ; Copy filename to buffer
  2538. @@DosName:
  2539.     test    [Buf1+bx+02h], 01h    ; Skip '.' file
  2540.     jnz    @@NextLong
  2541.     push    cx di
  2542. @@CopyName:
  2543.     lodsb
  2544.     call    UpCase
  2545.     stosb
  2546.     loop    @@CopyName
  2547.     xor    al, al
  2548.     stosb
  2549.     pop    di cx
  2550. @@CompareName:
  2551.     mov    si, dx
  2552. @@Cmp1:
  2553.     lodsb
  2554.     or    al, al
  2555.     jz    @@EndOfTmpl        ; End of search filename
  2556.     scasb
  2557.     je    @@Cmp1
  2558.     jmp    @@NextLong
  2559. @@EndOfTmpl:
  2560.     cmp    [BYTE es:di], 0        ; Check that filename in entry also ends
  2561.     je    @@Succeed
  2562. @@NextLong:
  2563. ; See if there's a B Tree pointer
  2564.     test    [BYTE Buf1+bx+02h], 04h
  2565.     jz      @@NoBTree
  2566. ; Go down the tree
  2567.     add     bx, [WORD Buf1+bx]
  2568.     mov     eax, [DWORD Buf1+bx-04h]    ; B Tree pointer
  2569.     mov    [@@Sector], eax
  2570.     call    NEAR ReadSector, eax, 4, es (OFFSET Buf1)
  2571.     jc    @@Fail
  2572.     mov    bx, 14h
  2573.     jmp    @@ChkValidName            ; Search this block
  2574. @@NoBTree:
  2575. ; Move to next entry
  2576. @@NextEntry:
  2577.     test    [BYTE Buf1+bx+02h], 08h     ; See if last entry
  2578.     jnz     @@Up
  2579.     add     bx, [WORD Buf1+bx]          ; Point to next entry
  2580.     jmp    @@ChkValidName            ; Continue search    
  2581. ; Go up the tree
  2582. @@Up:
  2583.     mov    eax, [DWORD Buf1+0Ch]    ; Parent node
  2584.     call    NEAR ReadSector, eax, 4, es (OFFSET Buf1)
  2585.     jc    @@Fail
  2586.     cmp     [BYTE Buf1+03h], 0F7h      ; FNode signature
  2587.     je    @@Fail
  2588. ; Go through this directory block to find the entry that we came from.
  2589.     mov     bx, 14h
  2590. @@SrchParent:
  2591.     test    [Buf1+bx+02h], 04h      ; Entry has B tree pointer?
  2592.     jz      @@NotParent
  2593.     mov     si, bx
  2594.     add     si, [WORD Buf1+bx]
  2595.     mov    ecx, [@@Sector]
  2596.     cmp     [DWORD Buf1+si-4], ecx    ; Is parent?
  2597.     jne     @@NotParent
  2598.     mov    [@@Sector], eax
  2599.     jmp    @@NextEntry
  2600. @@NotParent:    
  2601.     test    [Buf1+bx+02h], 08h      ; Last entry in block?
  2602.     jnz     @@Fail
  2603.     add     bx, [WORD Buf1+bx]
  2604.     jmp     @@SrchParent
  2605.     
  2606. ; Traverse the directory B Tree to find a match. No long filenames.
  2607. @@TestEntry:
  2608.     cld
  2609. ; Compare the filename length byte
  2610.     mov     cl, [@@Len]
  2611.     cmp     cl, [Buf1+bx+1Eh]
  2612.     jna     @@1
  2613.     mov     cl, [Buf1+bx+1Eh]       ; Entry shorter than filename
  2614. ; Compare the filenames
  2615. @@1:
  2616.     xor     ch, ch
  2617.     mov     di, dx
  2618.     lea     si, [Buf1+bx+1Fh]
  2619. @@Compare:
  2620.     lodsb                   ; Load one character
  2621.     call    UpCase          ; Convert to upper case
  2622.     scasb                   ; Compare
  2623.     loope   @@Compare
  2624.     jb      @@Next
  2625.     ja      @@ChkBTree
  2626.     mov     cl, [@@Len]       ; See which of the names is the longest
  2627.     cmp     cl, [Buf1+bx+1Eh]
  2628.     je      @@Succeed
  2629.     ja      @@Next
  2630. ; See if there's a B Tree pointer
  2631. @@ChkBTree:
  2632.     test    [BYTE Buf1+bx+02h], 04h
  2633.     jz      @@Fail
  2634.     add     bx, [WORD Buf1+bx]
  2635.     mov     ecx, [DWORD Buf1+bx-4]      ; Extract the B Tree pointer
  2636.     mov    [@@Sector], ecx
  2637.     mov     [@@BytesRead], 200h
  2638.     call    NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
  2639.     mov     bx, 14h
  2640.     jmp     @@TestEntry
  2641.  
  2642. @@Next:
  2643.     test    [BYTE Buf1+bx+02h], 08h     ; See if last entry
  2644.     jnz     @@Fail
  2645.     add     bx, [WORD Buf1+bx]          ; Point to next entry
  2646.     mov     ax, [WORD Buf1+bx]
  2647.     add     ax, bx
  2648.     cmp     ax, [@@BytesRead]                 ; See if enough sectors read.
  2649.     jb      @@TestEntry
  2650.     cmp     [@@BytesRead], 800h
  2651.     jnb     @@Fail                          ; Whole dir. block already read.
  2652.     inc     [@@Sector]
  2653.     push    bx
  2654.     mov     bx, [@@BytesRead]
  2655.     add     bx, OFFSET Buf1
  2656.     call    NEAR ReadSector, [@@Sector], 1, es bx
  2657.     pop     bx
  2658.     add     [@@BytesRead], 200h
  2659.     jmp     @@TestEntry     
  2660.  
  2661. @@Fail:
  2662.     stc
  2663.     jmp     @@Done
  2664. @@Succeed:
  2665.     clc
  2666. @@Done: 
  2667.     pop     es
  2668.     ret
  2669. ENDP    FindDirEntry
  2670.  
  2671. ;---------------------------------------------------------------------
  2672. ; Find a file. Fully qualified filename at ES:DI. Offset into
  2673. ; Buf1 to directory entry returned in BX.
  2674.  
  2675. PROC    FindFile STDCALL
  2676.     DEFASSUME
  2677.     LOCAL   @@DirFlag:BYTE, @@CurFNode:DWORD, @@RetValue, @@LongPath:BYTE
  2678.     LOCAL    @@BufStart
  2679.     pushad
  2680.     push    ds
  2681.     push    cs
  2682.     pop     ds
  2683.     mov     [@@DirFlag], 0            ; Set if current level is also the last.
  2684.     mov    [@@LongPath], 0        ; Set if path is too long for any more subdirs.
  2685.     mov    [@@BufStart], di
  2686.     mov     ecx, [RootFNode]
  2687.     mov     [@@CurFNode], ecx
  2688. ; Scan past the first backslash.
  2689.     mov     al, '\'
  2690.     mov     cx, 80h
  2691.     cld
  2692.     repne scasb
  2693.     dec     di
  2694. @@NextLevel:
  2695.     cmp    [@@LongPath], 0
  2696.     jne    @@Fail            ; Path too long - can't access directory.
  2697. ; See if path is too long
  2698.     mov    ax, di
  2699.     sub    ax, [@@BufStart]
  2700.     cmp    ax, MaxPathLength-9
  2701.     seta    [@@LongPath]
  2702.  
  2703.     mov     bx, OFFSET FNameBuf
  2704.     mov     dx, bx
  2705. ; Move the name of the next subdirectory to FNameBuf
  2706. @@MoveName:
  2707.     inc     di
  2708.     mov     al, [es:di]
  2709.     or      al, al
  2710.     je      @@SetFlag
  2711.     cmp     al, '\'
  2712.     je      @@FindEntry
  2713.     mov     [bx], al
  2714.     inc     bx
  2715.     jmp     @@MoveName
  2716. @@SetFlag:
  2717.     mov     [@@DirFlag], 1            ; This is the last level
  2718. @@FindEntry:
  2719.     mov     [BYTE bx], 0
  2720. ; Find the directory entry
  2721.     mov     ecx, [@@CurFNode]
  2722.     push    di
  2723.     call    FindDirEntry
  2724.     mov     [@@RetValue], bx
  2725.     pop     di
  2726.     jc      @@Fail
  2727.     mov     ecx, [DWORD Buf1+bx+04h]    ; Get FNode pointer
  2728.     mov     [@@CurFNode], ecx
  2729.     cmp     [@@DirFlag], 0
  2730.     je      @@NextLevel
  2731.     clc
  2732.     jmp     @@Done
  2733.  
  2734. @@Fail: stc
  2735. @@Done:
  2736.     pop     ds
  2737.     popad
  2738.     jc      @@Exit
  2739.     mov     bx, [@@RetValue]
  2740. @@Exit:
  2741.     ret
  2742. ENDP    FindFile
  2743.  
  2744. ;---------------------------------------------------------------------
  2745. ; Check if a fully qualified pathname is too long to be the name of a
  2746. ; directory, and should be handled as a zero-length file. CF=1 if path
  2747. ; is too long.
  2748. PROC    ChkPathLength PASCAL
  2749.     DEFASSUME
  2750.     ARG    @@FileName:DWORD
  2751.     pushad
  2752.     push    es
  2753.     les    di, [@@FileName]
  2754.     xor    al, al
  2755.     mov    cx, 128
  2756.     cld
  2757.     repne scasb
  2758.     mov    al, '\'
  2759.     std
  2760.     mov    cx, 128
  2761.     repne scasb
  2762.     cld
  2763.     sub    di, [WORD LOW @@FileName]
  2764.     cmp    di, MaxPathLength-9
  2765.     cmc                ; CF indicates result
  2766.     pop    es
  2767.     popad
  2768.     ret
  2769. ENDP    ChkPathLength
  2770.  
  2771. ;---------------------------------------------------------------------
  2772. ; Convert UNIX (and HPFS) timestamp to DOS timestamp (local time).
  2773. ; UNIX timestamp passed in EAX and DOS timestamp returned in EAX -
  2774. ; time in low word, date in high word.
  2775. PROC    Unix2DosTime STDCALL
  2776.     DEFASSUME
  2777.     LOCAL   @@Result:DWORD,@@Year,@@Month,@@Day,@@Hour,@@Min,@@Secs
  2778.     pushad
  2779.     push    ds
  2780.     push    cs
  2781.     pop     ds
  2782.     sub     eax, 24*60*60*3652+TimeZone+60*60
  2783.     xor     edx, edx
  2784.     mov     ebx, 60
  2785.     div     ebx
  2786.     mov     [@@Secs], dx
  2787.  
  2788.     xor     edx, edx
  2789.     div     ebx
  2790.     mov     [@@Min], dx               ; Time in minutes in EAX
  2791.  
  2792.     mov     ebx, 1461*24
  2793.     xor     edx, edx
  2794.     div     ebx
  2795.     shl     eax, 2
  2796.     add     eax, 1980
  2797.     mov     [@@Year], ax
  2798.     
  2799.     mov     eax, edx
  2800.     cmp     eax, 366*24
  2801.     jb      @@1
  2802.     sub     eax, 366*24
  2803.     inc     [@@Year]
  2804.     mov     ebx, 365*24
  2805.     xor     edx, edx
  2806.     div     ebx
  2807.     add     [@@Year], ax
  2808.     mov     ax, dx
  2809.  
  2810. @@1:                                    ; Now in AX
  2811.     xor     dx, dx
  2812.     mov     bx, 24
  2813.     div     bx
  2814.     mov     [@@Hour], dx
  2815.     inc     ax
  2816.     
  2817.     test    [@@Year], 3
  2818.     jnz     @@2
  2819.     cmp     ax, 60
  2820.     jna     @@3
  2821.     dec     ax
  2822.     jmp     @@2
  2823. @@3:    jne     @@2
  2824.     mov     [@@Month], 2
  2825.     mov     [@@Day], 29
  2826.     jmp     @@DecodeDone
  2827.  
  2828. @@2:    
  2829.     xor     bx, bx
  2830. @@2_1:
  2831.     mov     dl, [DaysInMonth+bx]
  2832.     xor     dh, dh
  2833.     cmp     dx, ax
  2834.     jnb     @@4
  2835.     sub     ax, dx
  2836.     inc     bx
  2837.     jmp     @@2_1
  2838. @@4:    inc     bx
  2839.     mov     [@@Month], bx
  2840.     mov     [@@Day], ax
  2841.         
  2842. @@DecodeDone:
  2843. ; Pack the time in DOS format.
  2844.     xor     eax, eax
  2845.     mov     ax, [@@Day]
  2846.     mov     bx, [@@Month]
  2847.     shl     bx, 5
  2848.     or      ax, bx
  2849.     mov     bx, [@@Year]
  2850.     sub     bx, 1980
  2851.     shl     bx, 9
  2852.     or      ax, bx
  2853.     
  2854.     shl     eax, 16
  2855.     mov     ax, [@@Secs]
  2856.     shr     ax, 1
  2857.     mov     bx, [@@Min]
  2858.     shl     bx, 5
  2859.     or      ax, bx
  2860.     mov     bx, [@@Hour]
  2861.     shl     bx, 11
  2862.     or      ax, bx
  2863.     mov     [@@Result], eax
  2864.  
  2865.     pop     ds
  2866.     popad
  2867.     mov     eax, [@@Result]
  2868.     ret
  2869. ENDP    Unix2DosTime
  2870.  
  2871. ;---------------------------------------------------------------------
  2872. ; Compute CRC16 of a string. DS:SI points at the buffer, CX is the
  2873. ; length of the buffer. CRC16 returned in DX.
  2874.  
  2875. PROC    CRC16
  2876.     NOASSUME
  2877.     ASSUME    cs:ResCode
  2878.         push    ax bx
  2879.     pushf
  2880.     cld
  2881.     xor    dx, dx
  2882. @@1:    lodsb
  2883.     xor    ah, ah
  2884.     xchg    ah, al
  2885.     xor    dx, ax
  2886.     push    cx
  2887.     mov    cx, 8
  2888. @@2:    mov    bx, dx
  2889.     shl    dx, 1
  2890.     and    bx, 8000h
  2891.     jz    @@3
  2892.     xor    dx, 1021h
  2893. @@3:    loop    @@2
  2894.     pop    cx
  2895.     loop    @@1
  2896.     popf
  2897.     pop    bx ax
  2898.     ret
  2899. ENDP    CRC16
  2900.  
  2901. PROC    ThunkReadSector PASCAL FAR
  2902.     ARG    @@Sector:DWORD,@@NumSectors:WORD,@@Dest:DWORD
  2903.     call    NEAR ReadSector, [@@Sector], [@@NumSectors], [@@Dest]
  2904.     ret
  2905. ENDP    ThunkReadSector
  2906.  
  2907.     DB      64 DUP ('RESSTACK')
  2908. LABEL   ResStack
  2909. ENDS    ResCode
  2910.  
  2911. ;================================================== Resident data
  2912. SEGMENT    ResData
  2913. PartitionNr DB    0        ; HPFS partition number
  2914. ; Partition info
  2915. nSecs    DB      0        ; Sectors per track
  2916. nHeads    DW      0        ; Heads
  2917. LBAstart DD    0        ; LBA starting sector
  2918. AccessMethod DB 0        ; Disk access method
  2919. PhysDrv DB      80h
  2920. ; File system info
  2921. RootFNode DD    0               ; FNode sector for root dir.
  2922. CDFNode DD      0               ; FNode sector for current dir.
  2923. TotalSectors DD    0        ; Partition size in sectors
  2924. FreeSectors DD  0               ; # of sectors marked as free in bitmaps
  2925. MediaID    DB    0        ; Media ID byte from boot block
  2926. Volabel DB      12 DUP (0)      ; Volume label + an extra null.
  2927. LABEL    EndResdata
  2928. ENDS    ResData
  2929.  
  2930. ;======================================================= Transient section
  2931.     NOASSUME
  2932. SEGMENT CSeg
  2933.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,ss:SSeg,fs:ResCode,gs:ResCode
  2934. PROC    Main
  2935.     mov     ax, DSeg
  2936.     mov     ds, ax
  2937.     mov     es, ax
  2938.     mov     ax, ResCode
  2939.     mov    fs, ax
  2940.     mov     gs, ax
  2941.  
  2942. ; Write hello message
  2943.     mov     ah, 09h
  2944.     mov     dx, OFFSET MsgHello
  2945.     int     21h
  2946.  
  2947. ; Check for DR-DOS or Novell DOS
  2948.     mov    ax, 4452h
  2949.     stc
  2950.     int    21h
  2951.     jc    @@NotDrDos
  2952. ; DR-DOS or Novell DOS found. Check version.
  2953.     cmp    ah, 10h
  2954.     jne    errBadDosVer
  2955.     cmp    al, 72h        ; Novell DOS
  2956.     je    @@NovellDOS
  2957.     mov    [DrDos], al    ; DR-DOS version code
  2958.     cmp    al, 65h        ; DR-DOS 5
  2959.     je    @@Dos3
  2960.     cmp    al, 67h        ; DR-DOS 6
  2961.     je    @@Dos3
  2962.     jmp    errBadDosVer
  2963. @@NovellDOS:
  2964.     mov    [Novell], 1
  2965.     jmp    @@DosVerOk
  2966. @@NotDrDos:
  2967. ; Check DOS version
  2968.     mov     ax, 3000h
  2969.     int     21h
  2970.     mov    [DosVersion], al
  2971.     cmp    al, 3
  2972.     je    @@Major3
  2973.     jb      errBadDOSVer
  2974.     cmp     al, 10
  2975.     jae    errBadDOSVer
  2976.     jmp    @@DosVerOk
  2977. ; DOS 3.x, check minor version is at least 10
  2978. @@Major3:
  2979.     cmp    ah, 10
  2980.     jb    errBadDOSVer
  2981. ; DOS 3.10-3.x or DR DOS 5 and 6
  2982. @@Dos3:
  2983.     mov    [CDSSize], 51h
  2984.     mov    [WORD LOW pCurrCDS], 26Ch
  2985.     mov    [WORD LOW FN1], 92h
  2986.     mov    [WORD LOW AccMode], 23Bh
  2987.     mov    [WORD LOW SrchAttr], 23Ah
  2988.  
  2989. @@DosVerOk:
  2990.  
  2991. ; Get PSP
  2992.     mov     ah, 51h
  2993.     int     21h
  2994.     mov     [PSP], bx
  2995. ; Release heap space above end of stack
  2996.     mov    bx, sp
  2997.     add    bx, 15
  2998.     shr    bx, 4
  2999.     mov    ax, ss
  3000.     add    bx, ax
  3001.     mov    ax, [PSP]
  3002.     sub    bx, ax
  3003.     push    es
  3004.     mov    es, ax
  3005.     mov    ah, 4Ah
  3006.     int    21h
  3007.     pop    es
  3008.     jc    errMemRel
  3009. ; Get SDA address
  3010.     mov     ax, 5D06h
  3011.     int     21h             ; Address returned in DS:SI
  3012.     ASSUME  ds:NOTHING
  3013.     jc      errNoSDA
  3014.  
  3015.     mov     [WORD HIGH SDA], ds
  3016.     mov     [WORD LOW SDA], si
  3017.     mov    [WORD HIGH pCurrCDS], ds
  3018.     add    [WORD LOW pCurrCDS], si
  3019.     mov    [WORD HIGH pDTA], ds
  3020.     add    [WORD LOW pDTA], si
  3021.     mov    [WORD HIGH FN1], ds
  3022.     add    [WORD LOW FN1], si
  3023.     mov    [WORD HIGH AccMode], ds
  3024.     add    [WORD LOW AccMode], si
  3025.     mov    [WORD HIGH SrchAttr], ds
  3026.     add    [WORD LOW SrchAttr], si
  3027.     mov    [WORD HIGH ExtOpenMode], ds
  3028.     add    [WORD LOW ExtOpenMode], si
  3029.     mov     ax, DSeg
  3030.     mov     ds, ax
  3031.     ASSUME  ds:DSeg
  3032. ; Get address of LoL
  3033.     mov     ah, 52h
  3034.     int     21h             ; ES:BX -> List of Lists
  3035.     ASSUME  es:NOTHING
  3036.     mov     [WORD LOW LstOfLst], bx
  3037.     mov     [WORD HIGH LstOfLst], es
  3038. ; Get Lastdrive
  3039.     mov     cl, [es:bx+21h]
  3040.     mov     [LastDrive], cl
  3041. ; Check if XMS is present, and get driver entry point
  3042.     mov     ax, 4300h
  3043.     int     2Fh
  3044.     cmp     al, 80h
  3045.     jne     NoXMS
  3046.     mov     [XMSFound], 1
  3047.     mov     ax, 4310h
  3048.     int     2Fh
  3049.     mov     [WORD LOW XMSEntry], bx
  3050.     mov     [WORD HIGH XMSEntry], es
  3051. NoXMS:
  3052.  
  3053. ; Parse command line
  3054.     call    ParseCmdLine
  3055.     cmp     [UnInstall], 1
  3056.     jne     InstallDriver
  3057.  
  3058. ; Scan through INT 2D functions 00h-0FFh in descending order.
  3059.     mov    [ApiFunc], 0FFh
  3060. CallMultiplex:
  3061.     mov    ah, [ApiFunc]
  3062.     xor    al, al        ; Installation check
  3063.     int     2Dh
  3064.     cmp    al, 0FFh
  3065.     jne    @@NextMultiplex
  3066.     mov    bx, ResCode    ; Compare signature strings
  3067.     mov    si, OFFSET AMISSign
  3068.     push    ds
  3069.     mov    ds, bx
  3070.     mov    es, dx        ; ES=driver's ResCode segment
  3071.     mov    cx, 4
  3072.     cld
  3073.     repe cmpsd
  3074.     pop    ds
  3075.     jne    @@NextMultiplex
  3076. ; Installed iHPFS found.
  3077.     mov    [DriverLoaded], 1
  3078.         cmp    [SpecUninstall], 0
  3079.     jz    @@TotalUninstall    ; Uninstall all drives
  3080. ; Loop through list of drives to uninstall.
  3081.     mov    bx, -1
  3082. @@UninstallDrives:
  3083.     inc    bx
  3084.     cmp    bx, 26
  3085.     je    @@NextMultiplex
  3086.     cmp    [UninstallDrv+bx], 0
  3087.     je    @@UninstallDrives
  3088.     movzx    ax, [ApiFunc]
  3089.     call    NEAR QueryDrive, bx, ax
  3090.         or    ah, ah
  3091.     jz    @@UninstallDrives
  3092.     mov    [UninstallDrv+bx], 0    ; Driver found
  3093.     call    NEAR RemoveDrv, bx, es
  3094.         or    ah, ah
  3095.         jnz    @@errRmDrvFail
  3096.     mov    dl, bl
  3097.         add    dl, 'A'
  3098.         mov    [MsgDrvRemovedLetter], dl
  3099.         mov    ah, 9
  3100.         mov    dx, OFFSET MsgDrvRemoved
  3101.         int    21h
  3102.     jmp    @@UninstallDrives
  3103. @@errRmDrvFail:
  3104.     mov    dl, bl
  3105.         add    dl, 'A'
  3106.         mov    [MsgCantRmDrvLetter], dl
  3107.     mov    dx, OFFSET MsgCantRmDrv
  3108.     mov    ah, 9
  3109.     int    21h
  3110.     jmp    @@UninstallDrives
  3111.         
  3112. @@TotalUninstall:
  3113.     call    NEAR UninstallDriver, es
  3114.     mov    [Installed], 1        ; Driver uninstalled
  3115.         jmp    @@NextMultiplex
  3116.        
  3117. @@NextMultiplex:
  3118.     sub    [ApiFunc], 1
  3119.     jnc    CallMultiplex
  3120.     cmp    [DriverLoaded], 0
  3121.         je    @@errNotLoaded
  3122.     cmp    [SpecUninstall], 0
  3123.         jnz    @@ChkUninstallResult
  3124.     cmp    [Installed], 0
  3125.         je    @@errCantUninstall
  3126. ; Total uninstall done.
  3127.     mov     ah, 9
  3128.     mov     dx, OFFSET MsgUnInstalled
  3129.     int     21h
  3130.     mov     ax, 4C00h
  3131.     int     21h
  3132.  
  3133. ; Check if any drives couldn't be uninstalled.
  3134. @@ChkUninstallResult:
  3135.     mov    bx, -1
  3136. @@ChkUninstall1:
  3137.     inc    bx
  3138.     cmp    bx, 26
  3139.     je    @@Exit
  3140.     cmp    [UninstallDrv+bx], 0
  3141.     je    @@ChkUninstall1
  3142.     mov    dl, bl
  3143.         add    dl, 'A'
  3144.         mov    [MsgDrvNotInstLetter], dl
  3145.     mov    dx, OFFSET MsgDrvNotInst
  3146.     mov    ah, 9
  3147.     int    21h
  3148.     jmp    @@ChkUninstall1
  3149.          
  3150. InstallDriver:
  3151.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,fs:NOTHING,ss:SSeg,gs:ResCode
  3152. ; Release environment block.
  3153.     mov     es, [PSP]
  3154.     mov     ax, [es:2Ch]
  3155.     mov     es, ax
  3156.     mov     ah, 49h
  3157.     int     21h
  3158.     jc      errEnvRel
  3159.  
  3160. ; Find an unused Multiplex function
  3161.     xor    ah, ah
  3162. FindFreeMux:
  3163.     xor     al, al                  ; Installation check
  3164.     push    ax
  3165.     int     2Dh
  3166.     or      al, al
  3167.     pop     ax
  3168.     je      FoundFreeMux
  3169.     add    ah, 1
  3170.     jz    errNoFreeMux
  3171.     jmp     FindFreeMux
  3172. FoundFreeMux:
  3173.     mov     [ApiFunc], ah          ; Save Multiplex function
  3174.  
  3175.     call    NEAR ScanDisks
  3176. ; Check if any specified partitions weren't found.
  3177.     mov    bx, -1
  3178. @@ChkPartFound:
  3179.     inc    bx
  3180.     cmp    bx, 26
  3181.     je    @@ChkPartFound1
  3182.     cmp    [Partitions+bx], 0FEh
  3183.     jae    @@ChkPartFound
  3184.     mov    ax, bx
  3185.     mov    dl, 10
  3186.     div    dl
  3187.     or    al, al
  3188.     jz    @@ChkPartFound2
  3189.     add    al, 30h
  3190. @@ChkPartFound2:
  3191.     add    ah, 30h
  3192.     mov    [WORD MsgPartNotFoundNr], ax
  3193.     mov    dx, OFFSET MsgPartNotFound
  3194.     mov    ah, 9
  3195.     int    21h
  3196.     mov    [ErrSignaled], 1
  3197.     jmp    @@ChkPartFound
  3198. @@ChkPartFound1:
  3199.     cmp    [Installed], 0
  3200.     jnz    @@Installed    ; One or more drives installed
  3201.     cmp    [ErrSignaled], 0
  3202.         jnz    @@Exit        ; Error message already displayed
  3203.     jmp    errNoHPFS    ; No HPFS partitions found
  3204.  
  3205. ; One or more drives successfully installed.
  3206. @@Installed:
  3207.     mov    ax, ResCode
  3208.         mov    es, ax
  3209.     ASSUME    es:ResCode
  3210. ; Get File Character Upcase Table
  3211.     mov     ax, 6504h
  3212.     mov     bx, 0FFFFh
  3213.     mov     cx, 5
  3214.     mov     dx, 0FFFFh
  3215.     mov     di, OFFSET TermChars
  3216.     int     21h
  3217.     jc      @@GetFileChar
  3218.     push    ds
  3219.     lds     si, [DWORD TermChars+1]
  3220.         add     si, 2
  3221.     mov     di, OFFSET UpCaseTbl
  3222.     mov     cx, 32
  3223.     cld
  3224.     rep movsd
  3225.     pop     ds
  3226.  
  3227. ; Get File-Character Table
  3228. @@GetFileChar:
  3229.     mov     ax, 6505h       ; Get File-Character Table
  3230.     mov     bx, 0FFFFh      ; Default codepage
  3231.     mov     cx, 5           ; Buffer size
  3232.     mov     dx, 0FFFFh      ; Default country ID
  3233.     mov     di, OFFSET TermChars
  3234.     int     21h
  3235.     jc      @@FileChar2
  3236.     mov     si, [WORD TermChars+1]
  3237.     mov     ax, [WORD TermChars+3]
  3238.     mov     ds, ax
  3239.     ASSUME    ds:NOTHING
  3240.         add     si, 3
  3241.     cld
  3242.     lodsw
  3243.     mov     [WORD MinPerm], ax
  3244.     inc     si
  3245.     lodsw
  3246.     mov     [WORD MinExcl], ax
  3247.     inc     si
  3248.     lodsb
  3249.     mov     cl, al
  3250.     mov     [NumTerm], al
  3251.     xor     ch, ch
  3252.     mov     di, OFFSET TermChars
  3253.     rep movsb
  3254. @@FileChar2:
  3255.                 
  3256. ; Get interrupt 2D and 2F vectors.
  3257.     ASSUME    ds:NOTHING,es:NOTHING,fs:ResCode,gs:NOTHING
  3258.     mov    ax, ResCode
  3259.     mov    fs, ax
  3260. GetIntVectors:
  3261.     mov     ax, 352Dh
  3262.     int     21h
  3263.     mov     [WORD OldInt2D], bx
  3264.     mov     [WORD HIGH OldInt2D], es
  3265.     mov     ax, 352Fh
  3266.     int     21h
  3267.     mov     [WORD OldInt2F], bx
  3268.     mov     [WORD HIGH OldInt2F], es
  3269.  
  3270. ; Set new interrupt 2D and 2F vectors.
  3271.     push    ds
  3272.     mov    ax, ResCode
  3273.     mov    ds, ax
  3274.     mov    ax, 252Dh
  3275.     mov    dx, OFFSET Int2DEntry
  3276.     int    21h
  3277.     mov     ax, 252Fh
  3278.     mov     dx, OFFSET Int2FEntry
  3279.     int     21h
  3280.     pop     ds
  3281.  
  3282. ; Terminate and Stay Resident
  3283.     mov     ax, 3100h
  3284.     mov    dx, ResData
  3285.         sub    dx, ResCode
  3286.         add    dx, 16        ; 16 paras for PSP
  3287.         mov    bx, OFFSET EndResData
  3288.         shr    bx, 4
  3289.         add    dx, bx
  3290.         inc    dx
  3291.     int     21h
  3292.  
  3293. @@Exit:
  3294.     mov    ax, 4C00h
  3295.     int    21h
  3296. ;--------
  3297. ; Errors
  3298.  
  3299. errBadDosVer:
  3300.     Abort   0
  3301. errEnvRel:
  3302. errMemRel:
  3303.     Abort    1        ; Malloc error
  3304. errNoSDA:
  3305.     Abort    3        ; Compatibility error
  3306. errNoHPFS:
  3307.     Abort   5
  3308. errNoFreeMux:
  3309.     Abort    14        ; No free multiplex function
  3310. @@errCantUninstall:
  3311.     Abort    11            ; Cannot unload
  3312. @@errNotLoaded:
  3313.     Abort    15            ; Driver not loaded
  3314. ENDP    Main
  3315.  
  3316. ;---------------------------------------------------------------------
  3317. ; Scans all hard disks in the system.
  3318. PROC    ScanDisks STDCALL
  3319.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3320.     LOCAL    @@Drive:BYTE, @@Cyls:WORD, @@Heads:WORD, @@Secs:WORD
  3321.     LOCAL    @@IBM_MS_Ext:WORD
  3322.     push    es fs gs
  3323.     mov    ax, ResCode
  3324.     mov    es, ax
  3325.         mov    fs, ax
  3326.  
  3327.     mov    [@@Drive], 80h
  3328. @@DoDrive:
  3329. ; Get drive type
  3330.     mov    ah, 15h
  3331.     mov    dl, [@@Drive]
  3332.     int    13h
  3333.     jc    @@Done
  3334.     cmp    ah, 03h
  3335.     jne    @@Done
  3336. ; Get drive parameters
  3337.     mov    ah, 08h
  3338.     mov    dl, [@@Drive]
  3339.     int    13h
  3340.     jc    @@Done
  3341.     mov    ax, cx
  3342.     xchg    ah, al
  3343.     shr    ah, 6
  3344.     inc    ax
  3345.     mov    [@@Cyls], ax
  3346.     mov    [BYTE LOW @@Heads], dh
  3347.     mov    [BYTE HIGH @@Heads], 0
  3348.     inc    [@@Heads]
  3349.     and    cx, 3Fh
  3350.     mov    [@@Secs], cx
  3351. ; Check for IBM/MS Extensions
  3352. ; Some BIOSes return major version 02h in AH, which seems to
  3353. ; be incorrect. These BIOSes also have a strange support bitmap,
  3354. ; so we ignore it.
  3355.     mov    [@@IBM_MS_Ext], 0
  3356.     cmp    [NoIBMExt], 0
  3357.     jnz    @@ExtChecked
  3358.     mov    ah, 41h
  3359.     mov    bx, 55AAh
  3360.     mov    dl, [@@Drive]
  3361.     int    13h
  3362.     jc    @@ExtChecked
  3363.     cmp    bx, 0AA55h
  3364.     jne    @@ExtChecked
  3365.         cmp     ah, 02h                 ; Buggy version
  3366.         je      @@HasIBMExt
  3367.     test    cl, 1            ; Extended functions supported
  3368.         jz      @@ExtChecked
  3369. @@HasIBMExt:
  3370.     mov    [@@IBM_MS_Ext], 1
  3371. @@ExtChecked:
  3372. ; Read the first sector just to make sure the drive exists. This is the only
  3373. ; safe way as the drive type/drive param calls may return crazy values.
  3374.     mov    ax, 0201h
  3375.     mov    bx, OFFSET Buf1
  3376.     mov    cx, 1
  3377.     movzx    dx, [@@Drive]
  3378.     int    13h
  3379.     jc    @@Done
  3380.  
  3381.     movzx    dx, [@@Drive]
  3382.     mov    [ExtPartBase], 0
  3383.     call    NEAR ScanPartTbl, Method_CHS,dx,[@@Heads],[@@Secs],[@@IBM_MS_Ext],0,0,1,0 0
  3384.     inc    [@@Drive]
  3385.     jnc    @@DoDrive
  3386.  
  3387. @@Done:
  3388.     pop    gs fs es
  3389.     ret
  3390. ENDP    ScanDisks
  3391.  
  3392. ;---------------------------------------------------------------------
  3393. ; Scans partition tables on a drive.
  3394. ; Args: @@Method    Disk access method to use
  3395. ;    @@Drive        Drive number
  3396. ;    @@nHeads    Number of heads (logical)
  3397. ;    @@nSecs        Number of sectors (logical)
  3398. ;    @@IBM_MS_Ext    IBM/MS Extensions supported
  3399. ;    @@PartCyl    Cylinder of partition table
  3400. ;    @@PartHead    Head of partition table
  3401. ;    @@PartSec    Sector of partition table
  3402. ;    @@PartLBA    LBA of partition table
  3403. ; Return CF=1 if a critical error is encountered (scan should not continue)
  3404. PROC    ScanPartTbl PASCAL
  3405.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3406.     ARG    @@Method,@@Drive,@@nHeads,@@nSecs,@@IBM_MS_Ext,@@PartCyl,@@PartHead,@@PartSec,@@PartLBA:DWORD
  3407.     LOCAL    @@PartOfs:WORD, @@HighPart:BYTE, @@sLBA:DWORD
  3408.     LOCAL    @@sCyl:WORD, @@sHead:WORD, @@sSec:WORD
  3409.     LOCAL    @@eCyl:WORD, @@eHead:WORD, @@eSec:WORD
  3410.  
  3411.     mov    [@@PartOfs], 1BEh
  3412. @@DoEntry:
  3413.     mov    [@@HighPart], 0
  3414.     cmp    [@@Method], Method_CHS
  3415.     je    @@ReadPartCHS
  3416.     cmp    [@@Method], Method_CHSExt
  3417.     je    @@ReadPartCHSExt
  3418.     jmp    @@ReadPartExt
  3419.  
  3420. ; Read partition table using CHS
  3421. @@ReadPartCHS:
  3422.     call    NEAR ReadSectorCHS PASCAL, [@@PartLBA],[@@Drive],[@@nHeads],[@@nSecs],es (OFFSET Buf1)
  3423.     jc    @@errReadError
  3424.     jmp    @@PartTableRead
  3425.  
  3426. ; Read partition table using extended CHS
  3427. @@ReadPartCHSExt:
  3428.     call    NEAR ReadSectorExtCHS PASCAL, [@@PartLBA],[@@Drive],[@@nHeads],[@@nSecs],es (OFFSET Buf1)
  3429.     jc    @@errReadError
  3430.     jmp    @@PartTableRead
  3431.  
  3432. ; Read partition table using IBM/MS Extensions
  3433. @@ReadPartExt:
  3434.     mov    [DiskAddrPkt1.Count], 1
  3435.     mov    eax, [@@PartLBA]
  3436.     mov    [DWORD LOW DiskAddrPkt1.Sector], eax
  3437.     mov    [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf1
  3438.     mov    [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf1
  3439.     mov    dl, [BYTE LOW @@Drive]
  3440.     mov    si, OFFSET DiskAddrPkt1
  3441.     mov    ah, 42h
  3442.     int    13h
  3443.     jc    @@errReadError
  3444. @@PartTableRead:
  3445.  
  3446. ; Check partition table signature
  3447.     cmp    [WORD Buf1+510], 0AA55h
  3448.     jne    @@errBadPartTable
  3449.  
  3450.     mov    bx, [@@PartOfs]
  3451.     cmp    [BYTE Buf1+bx+04h], 0        ; Unused entry
  3452.     jz    @@NextEntry
  3453. ; Extract CHS information
  3454.     movzx    ax, [Buf1+bx+01h]
  3455.     mov    [@@sHead], ax
  3456.     movzx    ax, [Buf1+bx+02h]
  3457.     mov    [@@sSec], ax
  3458.     and    [@@sSec], 3Fh
  3459.     shl    ax, 2
  3460.     mov    al, [Buf1+bx+03h]
  3461.     mov    [@@sCyl], ax
  3462.  
  3463.     movzx    ax, [Buf1+bx+05h]
  3464.     mov    [@@eHead], ax
  3465.     movzx    ax, [Buf1+bx+06h]
  3466.     mov    [@@eSec], ax
  3467.     and    [@@eSec], 3Fh
  3468.     shl    ax, 2
  3469.     mov    al, [Buf1+bx+07h]
  3470.     mov    [@@eCyl], ax
  3471. ; Calculate partition size from CHS information to determine if high
  3472.     movzx    eax, [@@eCyl]
  3473.     sub    ax, [@@sCyl]
  3474.     mul    [@@nHeads]
  3475.     shl    edx, 16
  3476.     add    eax, edx
  3477.     movzx    edx, [@@nSecs]
  3478.     mul    edx
  3479.     mov    ecx, eax
  3480.     movzx    eax, [BYTE LOW @@eHead]
  3481.     sub    al, [BYTE LOW @@sHead]
  3482.     mul    [BYTE LOW @@nSecs]
  3483.     add    al, [BYTE LOW @@eSec]
  3484.     adc    ah, 0
  3485.     sub    al, [BYTE LOW @@sSec]
  3486.     sbb    ah, 0
  3487.     add    ecx, eax
  3488.     inc    ecx            ; ECX = partition size
  3489.     cmp    ecx, [DWORD Buf1+bx+0Ch]
  3490.     je    @@PartSizeDone
  3491.     mov    [@@HighPart], 1
  3492.     cmp    ecx, 1
  3493.     je    @@PartSizeDone
  3494.     cmp    [Buf1+bx+04h], 05h
  3495.     jne    @@PartSizeDone
  3496.     mov    [@@HighPart], 0        ; Extended partition, partially high
  3497. @@PartSizeDone:
  3498.  
  3499.     mov    al, [Buf1+bx+04h]    ; Partition type
  3500.     cmp    al, 05h
  3501.     je    @@ExtPart
  3502.     cmp    al, 07h
  3503.     je    @@HPFSPart
  3504.     cmp    al, 17h
  3505.     je    @@HPFSPart
  3506.     jmp    @@NextEntry
  3507.  
  3508. @@ExtPart:
  3509.     mov    eax, [DWORD Buf1+bx+08h]
  3510.     add    eax, [ExtPartBase]    ; Relative to first extended partition
  3511.     mov    [@@sLBA], eax        ; LBA of partition
  3512.     cmp    [ExtPartBase], 0
  3513.     jnz    @@ExtBaseDone
  3514.     mov    [ExtPartBase], eax    ; This is the first ext part
  3515. @@ExtBaseDone:
  3516.  
  3517.     cmp    [@@HighPart], 0
  3518.     jnz    @@ExtPartHigh
  3519.     call    NEAR ScanPartTbl, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
  3520.     jc    @@Done
  3521.     jmp    @@NextEntry
  3522. @@ExtPartHigh:
  3523.     cmp    [@@IBM_MS_Ext], 0
  3524.     jnz    @@ExtPartIBMExt
  3525.     cmp    [UseExtCHS], 0
  3526.     jz    @@NextEntry
  3527.     test    [BYTE LOW @@nHeads], 0C0h
  3528.     jnz    @@NextEntry        ; Must have at most 63 heads
  3529.     call    NEAR CheckCylNumber, [@@sLBA], [@@nHeads], [@@nSecs]
  3530.     jnz    @@NextEntry
  3531.     call    NEAR ScanPartTbl, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
  3532.     jc    @@Done
  3533.     jmp    @@NextEntry
  3534. @@ExtPartIBMExt:
  3535.     call    NEAR ScanPartTbl, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
  3536.     jc    @@Done
  3537.     jmp    @@NextEntry
  3538.  
  3539. @@HPFSPart:
  3540.     mov    eax, [@@PartLBA]    ; Relative to current ext part
  3541.     add    eax, [DWORD Buf1+bx+08h]
  3542.     mov    [@@sLBA], eax        ; LBA of partition
  3543.  
  3544.     cmp    [@@HighPart], 0
  3545.     jnz    @@HPFSPartHigh
  3546.     call    NEAR CheckHPFSPart, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
  3547.     jc    @@Done
  3548.     jmp    @@NextEntry
  3549. @@HPFSPartHigh:
  3550.     cmp    [@@IBM_MS_Ext], 0
  3551.     jnz    @@HPFSPartIBMExt
  3552.     cmp    [UseExtCHS], 0
  3553.     jz    @@NextEntry
  3554.     test    [BYTE LOW @@nHeads], 0C0h
  3555.     jnz    @@NextEntry        ; Must have at most 63 heads
  3556.     mov    eax, [@@sLBA]        ; Check that we can read last sector
  3557.     add    eax, [DWORD Buf1+bx+0Ch]
  3558.     dec    eax
  3559.     call    NEAR CheckCylNumber, eax, [@@nHeads], [@@nSecs]
  3560.     jnz    @@NextEntry
  3561.         call    NEAR CheckHPFSPart, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
  3562.     jc    @@Done
  3563.     jmp    @@NextEntry
  3564. @@HPFSPartIBMExt:
  3565.     call    NEAR CheckHPFSPart, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
  3566.     jc    @@Done
  3567.     jmp    @@NextEntry
  3568.  
  3569. @@NextEntry:
  3570.     add    [@@PartOfs], 10h
  3571.     cmp     [@@PartOfs], 1FEh
  3572.     jb      @@DoEntry
  3573.     clc
  3574.     jmp    @@Done
  3575.  
  3576. @@errReadError:
  3577.     mov    dx, OFFSET MsgDiskError
  3578.     mov    ah, 9
  3579.     int    21h
  3580.     stc
  3581.     jmp    @@Done
  3582.  
  3583. @@errBadPartTable:
  3584.     mov    dx, OFFSET MsgBadPartTable
  3585.     mov    ah, 9
  3586.     int    21h
  3587.     stc
  3588.     ; FALL THROUGH to @@Done
  3589.  
  3590. @@Done:
  3591.     ret
  3592. ENDP    ScanPartTbl
  3593.  
  3594. ;---------------------------------------------------------------------
  3595. ; Checks and possibly installs an HPFS partition. Uses Buf2
  3596. ; Args: @@Method    Disk access method to use
  3597. ;    @@PhysDrive    Drive number
  3598. ;    @@nHeads    Number of heads (logical)
  3599. ;    @@nSecs        Number of sectors (logical)
  3600. ;    @@sCyl        Starting cylinder
  3601. ;    @@sHead        Starting head
  3602. ;    @@sSec        Starting sector
  3603. ;    @@sLBA        Starting LBA
  3604. ; Returns CF=1 if a critical error occurs.
  3605. PROC    CheckHPFSPart PASCAL
  3606.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3607.     ARG    @@Method,@@PhysDrive,@@nHeads,@@nSecs,@@sCyl,@@sHead,@@sSec,@@sLBA:DWORD
  3608.     LOCAL    @@Drive:BYTE
  3609.     push    ds es fs gs
  3610.     pushad
  3611.     cmp    [@@Method], Method_CHS
  3612.     je    @@ReadCHS
  3613.     cmp    [@@Method], Method_CHSExt
  3614.     je    @@ReadCHSExt
  3615.     jmp    @@ReadExt
  3616.  
  3617. ; Read boot sector using CHS
  3618. @@ReadCHS:
  3619.         call    NEAR ReadSectorCHS PASCAL, [@@sLBA],[@@PhysDrive],[@@nHeads],[@@nSecs],es (OFFSET Buf2)
  3620.     jc    @@errReadError
  3621.     jmp    @@BootRead
  3622.  
  3623. ; Read boot sector using extended CHS
  3624. @@ReadCHSExt:
  3625.         call    NEAR ReadSectorExtCHS PASCAL, [@@sLBA],[@@PhysDrive],[@@nHeads],[@@nSecs],es (OFFSET Buf2)
  3626.     jc    @@errReadError
  3627.     jmp    @@BootRead
  3628.  
  3629. ; Read boot sector using IBM/MS Extensions
  3630. @@ReadExt:
  3631.     mov    [DiskAddrPkt1.Count], 1
  3632.     mov    eax, [@@sLBA]
  3633.     mov    [DWORD LOW DiskAddrPkt1.Sector], eax
  3634.     mov    [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf2
  3635.     mov    [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf2
  3636.     mov    dl, [BYTE LOW @@PhysDrive]
  3637.     mov    si, OFFSET DiskAddrPkt1
  3638.     mov    ah, 42h
  3639.     int    13h
  3640.     jc    @@errReadError
  3641.  
  3642. @@BootRead:
  3643. ; Check the HPFS signature
  3644.     cmp     [WORD Buf2+36h], 'PH'
  3645.     jne     @@Done
  3646.     cmp     [WORD Buf2+38h], 'SF'
  3647.     jne     @@Done
  3648. ; HPFS partition found.
  3649.     inc    [PartCount]
  3650.     movzx    bx, [PartCount]
  3651.     mov    dl, [Partitions+bx]
  3652.     mov    [@@Drive], dl
  3653.     mov    [Partitions+bx], 0FFh        ; Partition found
  3654.     cmp    dl, 0FFh
  3655.     je    @@Done                ; Don't install this partition
  3656. ; Check that partition is not already installed
  3657.     call    NEAR IsInstalledPart, bx
  3658.     or    ah, ah
  3659.     jnz    @@errPartInstalled
  3660.  
  3661.     les     bx, [LstOfLst]
  3662.     ASSUME    es:NOTHING
  3663.     les     si, [es:bx+16h]        ; CDS array
  3664.     cmp    [@@Drive], 0FEh
  3665.     je    @@ScanCDS        ; Find first free drive letter
  3666.     mov     ah, 36h                 ; Get disk free space
  3667.     mov     dl, [@@Drive]
  3668.     inc     dl
  3669.     int     21h
  3670.     cmp     ax, 0FFFFh
  3671.     jne     @@errDrvUsed
  3672.     mov     al, [BYTE CDSSize]
  3673.     mov     cl, [@@Drive]
  3674.     mul     cl
  3675.     mov     si, ax                  ; Points to CDS for our drive
  3676.     cmp     cl, [LastDrive]
  3677.     jb    @@FoundCDS
  3678.     jmp     @@errInvDrv
  3679.  
  3680. ; Search the CDS array, look for an unused CDS.
  3681. ; ES:SI -> CDS.
  3682. @@ScanCDS:
  3683.     mov    [@@Drive], 0
  3684. @@ScanCDS1:
  3685.     cmp    [DrDos], 0
  3686.     jz    @@ScanCDS2
  3687.     cmp    [WORD es:si+43h], 0    ; DR-DOS
  3688.     jmp    @@ScanCDS3
  3689. @@ScanCDS2:
  3690.     test    [WORD es:si+43h], 0C000h  ; Mask bits 15 and 14 of drv attributes
  3691. @@ScanCDS3:
  3692.     jz      @@FoundCDS                ; If 0, then drive is invalid = free
  3693.     add     si, [CDSSize]             ; Point to next CDS
  3694.     inc     [@@Drive]
  3695.     mov    cl, [@@Drive]
  3696.     cmp     cl, [LastDrive]
  3697.     jb      @@ScanCDS1                ; Go to next CDS entry
  3698.     jmp    @@errOutOfDrv
  3699.  
  3700. @@FoundCDS:
  3701. ; Allocate memory for resident data
  3702.     mov    bx, OFFSET EndResData
  3703.     shr    bx, 4
  3704.     inc    bx
  3705.     mov    ah, 48h
  3706.     int    21h
  3707.     jc    @@errMallocErr
  3708.     movzx    bx, [@@Drive]
  3709.     shl    bx, 1
  3710.     mov    [DataSegs+bx], ax    ; Save ResData segment
  3711.     mov    gs, ax
  3712.     ASSUME    gs:ResData
  3713. ; Clear resident data.
  3714.     push    es
  3715.     mov    es, ax
  3716.     xor    al, al
  3717.     cld
  3718.     mov    cx, OFFSET EndResData
  3719.     xor    di, di
  3720.     rep stosb
  3721.     pop    es
  3722. ; Set resident data
  3723.     mov    al, [BYTE LOW @@PhysDrive]
  3724.     mov    [PhysDrv], al
  3725.     mov    ax, [@@nHeads]
  3726.     mov    [nHeads], ax
  3727.     mov    al, [BYTE LOW @@nSecs]
  3728.     mov    [nSecs], al
  3729.     mov    al, [PartCount]
  3730.     mov    [PartitionNr], al
  3731.     mov    al, [BYTE LOW @@Method]
  3732.     mov    [AccessMethod], al
  3733.     mov    eax, [@@sLBA]
  3734.     mov    [LBAstart], eax
  3735.         call    InitResData
  3736. ; Set CDS fields
  3737.     mov    cl, [@@Drive]
  3738.     add     cl, 'A'
  3739.     mov     [MsgDrvLetter], cl
  3740.     cmp    [DrDos], 0
  3741.     jz    @@SetCDS
  3742.     mov    [WORD es:si+43h], 8000h    ; DR-DOS
  3743.     jmp    @@CDSSet
  3744. @@SetCDS:
  3745.     or      [WORD es:si+43h], 0C000h ; Flags+Physical bits on = Netwrk drive
  3746. @@CDSSet:
  3747.     mov     [es:si], cl
  3748.     mov     [WORD es:si+1], '\:'
  3749.     mov     [BYTE es:si+3], 0
  3750.     mov     [WORD es:si+4Fh], 2 ; Offset of backslash
  3751.     mov    [Installed], 1        ; Drive successfully installed
  3752. ; Partition installed - print message
  3753.     mov    dx, OFFSET MsgInstalled
  3754.     mov    ah, 9
  3755.     int    21h
  3756.     jmp    @@Done
  3757.  
  3758. @@errReadError:
  3759.     mov    dx, OFFSET MsgDiskError
  3760.     mov    ah, 9
  3761.     int    21h
  3762.     jmp    @@Done
  3763. @@errDrvUsed:
  3764.     mov    dl, [@@Drive]
  3765.     add    dl, 'A'
  3766.     mov    [MsgDrvUsedLetter], dl
  3767.     mov    dx, OFFSET MsgDrvUsed
  3768.     mov    ah, 9
  3769.     int    21h
  3770.     mov    [ErrSignaled], 1
  3771.     jmp    @@Done
  3772. @@errInvDrv:
  3773.     mov    dl, [@@Drive]
  3774.     add    dl, 'A'
  3775.     mov    [MsgInvDrvLetter], dl
  3776.     mov    dx, OFFSET MsgInvDrv
  3777.     mov    ah, 9
  3778.         int    21h
  3779.     mov    [ErrSignaled], 1
  3780.         jmp    @@Done
  3781. @@errPartInstalled:
  3782.     movzx    ax, [PartCount]
  3783.     mov    dl, 10
  3784.     div    dl
  3785.     or    al, al
  3786.     jz    @@errPartInstalled1    ; Leave 00h if first digit 0.
  3787.     add    al, 30h
  3788. @@errPartInstalled1:
  3789.     add    ah, 30h
  3790.     mov    [WORD MsgPartInstalledNr], ax
  3791.     mov    dx, OFFSET MsgPartInstalled
  3792.     mov    ah, 9
  3793.     int    21h
  3794.     mov    [ErrSignaled], 1
  3795.         jmp    @@Done
  3796. @@errOutOfDrv:
  3797.     mov    dx, OFFSET MsgNoAvailDrvLetter
  3798.     mov    ah, 9
  3799.     int    21h
  3800.     mov    [ErrSignaled], 1
  3801.     jmp    @@Fail
  3802. @@errMallocErr:
  3803.     mov    dx, OFFSET MsgMallocErr
  3804.     mov    ah, 9
  3805.     int    21h
  3806.     mov    [ErrSignaled], 1
  3807.     jmp    @@Fail
  3808.  
  3809. @@Fail:    stc
  3810.     jmp    @@Exit
  3811. @@Done:
  3812.     clc
  3813. @@Exit:
  3814.     popad
  3815.     pop    gs fs es ds
  3816.     ret
  3817. ENDP    CheckHPFSPart
  3818.  
  3819. ;---------------------------------------------------------------------
  3820. ; Read a sector given by LBA using CHS addressing.
  3821. PROC    ReadSectorCHS PASCAL
  3822.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3823.     ARG    @@Sector:DWORD, @@Drive:WORD, @@nHeads:WORD, @@nSecs:WORD, @@Buf:DWORD
  3824.     pushad
  3825.     push    es
  3826.     mov    ecx, [@@Sector]
  3827.     mov    ax, [@@nSecs]
  3828.     mul    [@@nHeads]
  3829.     mov     bx, ax          ; Sectors/track * heads
  3830.     mov     ax, cx
  3831.     mov     edx, ecx
  3832.     shr     edx, 16         ; DX:AX = logical sector #
  3833.     div     bx
  3834.     push    ax              ; Cylinder
  3835.     mov     ax, dx
  3836.     div     [BYTE LOW @@nSecs]
  3837.     movzx   dx, al          ; Head
  3838.     mov     cl, ah          ; Sector
  3839.     inc    cl
  3840.     pop     ax              ; Cylinder
  3841.     mov    dh, dl        ; Head
  3842.     mov     ch, al
  3843.     xor     al, al
  3844.     shr     ax, 2
  3845.     or      cl, al          ; bits 8 and 9 of cyl. number go here
  3846.         xor     al, al
  3847.  
  3848.     mov    ax, 0201h
  3849.     les    bx, [@@Buf]
  3850.     mov    dl, [BYTE LOW @@Drive]
  3851.     int    13h
  3852.     pop    es
  3853.     popad
  3854.     ret
  3855. ENDP    ReadSectorCHS
  3856.  
  3857. ;---------------------------------------------------------------------
  3858. ; Read a sector given by LBA using extended CHS addressing.
  3859. PROC    ReadSectorExtCHS PASCAL
  3860.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3861.     ARG    @@Sector:DWORD, @@Drive:WORD, @@nHeads:WORD, @@nSecs:WORD, @@Buf:DWORD
  3862.     pushad
  3863.     push    es
  3864.     mov    ecx, [@@Sector]
  3865.     mov    ax, [@@nSecs]
  3866.     mul    [@@nHeads]
  3867.     mov     bx, ax          ; Sectors/track * heads
  3868.     mov     ax, cx
  3869.     mov     edx, ecx
  3870.     shr     edx, 16         ; DX:AX = logical sector #
  3871.     div     bx
  3872.     push    ax              ; Cylinder
  3873.     mov     ax, dx
  3874.     div     [BYTE LOW @@nSecs]
  3875.     movzx   dx, al          ; Head
  3876.     mov     cl, ah          ; Sector
  3877.     inc    cl
  3878.     pop     ax              ; Cylinder
  3879.     mov    dh, dl        ; Head
  3880.     mov     ch, al
  3881.     xor     al, al
  3882.     shr     ax, 2
  3883.     or      cl, al          ; bits 8 and 9 of cyl. number go here
  3884.         xor     al, al
  3885.         shr     ax, 2
  3886.         or      dh, al          ; bits 10 and 11 of cyl (BIOS extension)
  3887.  
  3888.     mov    ax, 0201h
  3889.     les    bx, [@@Buf]
  3890.     mov    dl, [BYTE LOW @@Drive]
  3891.     int    13h
  3892.     pop    es
  3893.     popad
  3894.     ret
  3895. ENDP    ReadSectorExtCHS
  3896.  
  3897. ;---------------------------------------------------------------------
  3898. ; Determines the cylinder number of a given logical sector and returns
  3899. ; ZF=1 if it is less than 4096, which means it can be read with 
  3900. ; extended CHS addressing.
  3901. PROC    CheckCylNumber PASCAL
  3902.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3903.     ARG    @@Sector:DWORD, @@nHeads:WORD, @@nSecs:WORD
  3904.     pushad
  3905.     mov    ecx, [@@Sector]
  3906.     mov    ax, [@@nSecs]
  3907.     mul    [@@nHeads]
  3908.     mov     bx, ax          ; Sectors/track * heads
  3909.     mov     ax, cx
  3910.     mov     edx, ecx
  3911.     shr     edx, 16         ; DX:AX = logical sector #
  3912.     div     bx
  3913.     test    ax, 0F000h
  3914.     popad
  3915.     ret
  3916. ENDP    CheckCylNumber
  3917.  
  3918. ;---------------------------------------------------------------------
  3919. ; Initialize resident data. Assumes GS=ResData
  3920. PROC    InitResData STDCALL
  3921.     ASSUME    ds:ResCode,es:ResData,fs:NOTHING,gs:ResData
  3922.     LOCAL    @@BitmapTable:DWORD
  3923.         pushad
  3924.         push    ds es fs gs
  3925.     mov    ax, ResCode
  3926.     mov    ds, ax
  3927.     push    gs
  3928.     pop    es
  3929. ; Read the boot sector
  3930.     call    FAR ThunkReadSector PASCAL, LARGE 0, 1, ds (OFFSET Buf1)
  3931.     jc      @@ReadError
  3932.     mov     cx, 11
  3933.     mov     si, OFFSET Buf1+2Bh
  3934.     mov     di, OFFSET Volabel
  3935.     rep movsb
  3936.     mov    al, [BYTE Buf1+15h]        ; Media ID byte
  3937.     mov    [MediaID], al
  3938. ; Read the SuperBlock
  3939.     call    FAR ThunkReadSector PASCAL, LARGE 16, 1, ds (OFFSET Buf1)
  3940.     jc      @@ReadError
  3941.     mov     eax, [DWORD Buf1+0Ch]    ; Root dir fnode
  3942.     mov     [CDFNode], eax
  3943.     mov     [RootFNode], eax
  3944.     mov    eax, [DWORD Buf1+10h]    ; Partition size in sectors
  3945.     mov    [TotalSectors], eax
  3946.         mov     eax, [DWORD Buf1+18h]
  3947.         mov     [@@BitmapTable], eax
  3948. ; Scan the free space bitmaps and count free sectors.
  3949.         mov     edx, [TotalSectors]
  3950.         add     edx, 3FFFh
  3951.         shr     edx, 14         ; Number of bands
  3952.         shl     dx, 2
  3953.         xor     bx, bx          ; offset into bitmap table
  3954.         xor     ecx, ecx        ; bit count
  3955. @@DoBand:
  3956. ; Read free space bitmap table
  3957.         call    FAR ThunkReadSector PASCAL, [@@BitmapTable], 4, ds (OFFSET Buf1)
  3958.         jc      @@ReadError
  3959. ; Read free space bitmaps
  3960.         call    FAR ThunkReadSector PASCAL, [DWORD Buf1+bx], 4, ds (OFFSET Buf1)
  3961.         jc      @@ReadError
  3962.     call    CountBits
  3963.         add     ecx, eax
  3964.         add     bx, 4
  3965.         cmp     bx, dx
  3966.         jne     @@DoBand
  3967.         mov     [FreeSectors], ecx
  3968.  
  3969.            pop    gs fs es ds
  3970.         popad
  3971.     ret
  3972. @@ReadError:
  3973.     Abort    4
  3974. ENDP    InitResData                
  3975.  
  3976. ;---------------------------------------------------------------------
  3977. ; Count number of set bits in Buf. Assumes DS=ResCode. Returns
  3978. ; number of set bits in EAX.
  3979. PROC    CountBits
  3980.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  3981.     push    cx dx si
  3982.     xor    dx, dx
  3983.     mov    si, OFFSET Buf1
  3984.     mov    cx, 4*512
  3985.     cld
  3986. @@Bytes:
  3987.     lodsb
  3988.     push    cx
  3989.     mov    cx, 8
  3990. @@Bits: shr    al, 1
  3991.     adc    dx, 0
  3992.     loop    @@Bits
  3993.     pop    cx
  3994.     loop    @@Bytes
  3995.     movzx    eax, dx
  3996.     pop    si dx cx
  3997.     ret
  3998. ENDP    CountBits
  3999.  
  4000. ;---------------------------------------------------------------------
  4001. ; Write error message BX, release memory and exit
  4002. PROC    AbortMsg
  4003.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,fs:ResCode,gs:NOTHING
  4004.     push    bx
  4005.     mov     ax, DSeg
  4006.     mov     ds, ax
  4007.     mov     ax, ResCode
  4008.     mov     fs, ax
  4009.     shl     bx, 1
  4010.     mov     dx, [ErrMsgTbl+bx]
  4011.     mov     ah, 9
  4012.     int     21h
  4013. ; Deallocate XMS
  4014.     cmp     [CacheOn], 0
  4015.     jz      @@Exit
  4016.     mov     cl, [XMSBlocks]
  4017.     xor     ch, ch
  4018.     jcxz    @@1
  4019.     mov     ah, 0Ah
  4020.     mov     dx, [hCacheSectors]
  4021.     call    [XMSEntry]
  4022.     dec     cx
  4023.     jcxz    @@1
  4024.     mov     ah, 0Ah
  4025.     mov     dx, [hCacheLists]
  4026.     call    [XMSEntry]
  4027.     dec     cx
  4028.     jcxz    @@1
  4029.     mov     ah, 0Ah
  4030.     mov     dx, [hHashTable]
  4031.     call    [XMSEntry]
  4032. ; Release allocated ResData blocks.
  4033. @@1:    xor    bx, bx
  4034.     mov    cx, 26
  4035. @@2:    mov    ax, [DataSegs+bx]
  4036.     or    ax, ax
  4037.     jz    @@3
  4038.     mov    es, ax
  4039.     mov    ah, 49h
  4040.     push    bx cx
  4041.     int    21h
  4042.     pop    cx bx
  4043. @@3:    add    bx, 2
  4044.     loop    @@2    
  4045. @@Exit:
  4046.     pop    ax
  4047.     mov     ah, 4Ch
  4048.     int     21h
  4049. ENDP    AbortMsg
  4050.  
  4051. ;---------------------------------------------------------------------
  4052. ; Check if iHPFS is alredy loaded for a partition. Returns
  4053. ; AH=00h if not installed, 01h if installed
  4054. ; DL=drive number if partition installed.
  4055. PROC    IsInstalledPart PASCAL
  4056.     ARG    @@Part
  4057.     LOCAL    @@Func:BYTE, @@Result, @@Drive:BYTE
  4058.         pushad
  4059. ; Scan through INT 2D functions 00h-0FFh.
  4060.     mov    [@@Func], 0
  4061. @@CallMultiplex:
  4062.     mov    ah, [@@Func]
  4063.     xor    al, al        ; Installation check
  4064.     int     2Dh
  4065.     cmp    al, 0FFh
  4066.     jne    @@NextMultiplex
  4067.     mov    bx, ResCode    ; Compare signature strings
  4068.     mov    si, OFFSET AMISSign
  4069.     push    ds
  4070.     mov    ds, bx
  4071.     mov    es, dx
  4072.     mov    cx, 4
  4073.     cld
  4074.     repe cmpsd
  4075.     pop    ds
  4076.     jne    @@NextMultiplex
  4077. ; Installed iHPFS found.
  4078.     call    NEAR QueryPart, [@@Part], es
  4079.     mov    [@@Result], ax
  4080.     mov    [@@Drive], dl
  4081.     or    ah, ah
  4082.     jnz    @@Done        ; Partition found, exit.
  4083. @@NextMultiplex:
  4084.     add    [@@Func], 1
  4085.     jnz    @@CallMultiplex
  4086.     mov    [@@Result], 0FFh ; Partition not found.
  4087.     mov    [@@Drive], 0FFh
  4088. @@Done: popad
  4089.     mov    ax, [@@Result]
  4090.     mov    dl, [@@Drive]
  4091.     ret
  4092. ENDP    IsInstalledPart
  4093.  
  4094. ;---------------------------------------------------------------------
  4095. ; Uninstall driver.
  4096. PROC    UninstallDriver PASCAL
  4097.     ARG    @@ResCode    ; ResCode of driver to uninstall
  4098.     LOCAL    @@PSP
  4099.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4100.         pushad
  4101.     push    ds es gs
  4102.     mov    ds, [@@ResCode]
  4103. ; Release XMS memory
  4104.     cmp     [CacheOn], 0
  4105.     jz      @@XMSReleased
  4106.     mov     [CacheOn], 0
  4107.     mov     ah, 0Ah
  4108.     mov     dx, [hHashTable]
  4109.     call    [XMSEntry]
  4110.     mov     ah, 0Ah
  4111.     mov     dx, [hCacheLists]
  4112.     call    [XMSEntry]
  4113.     mov     ah, 0Ah
  4114.     mov     dx, [hCacheSectors]
  4115.     call    [XMSEntry]
  4116. @@XMSReleased:
  4117.  
  4118. ; Disconnect drives
  4119.     mov    cx, -1
  4120. @@Remove:
  4121.     inc    cx
  4122.         cmp    cx, 26
  4123.         je    @@DrivesDisconnected
  4124.     mov    bx, cx
  4125.     shl    bx, 1
  4126.     cmp    [DataSegs+bx], 0
  4127.         jz    @@Remove
  4128.         call    NEAR RemoveDrv, cx, ds
  4129.         jmp    @@Remove
  4130.         
  4131. @@DrivesDisconnected:
  4132. ; Get PSP.
  4133.     mov    ah, 51h
  4134.     int    21h
  4135.     mov    [@@PSP], bx    ; Save old PSP
  4136. ; Set PSP to resident driver. This is so the memory block retains its old owner.
  4137.     mov    bx, [PSP]
  4138.     mov     ah, 50h
  4139.     int     21h
  4140.     mov     es, bx
  4141.     ASSUME  es:NOTHING
  4142. ; See if interrupt vectors have been hooked by another TSR
  4143.     xor    ax, ax
  4144.     mov    gs, ax
  4145.     mov    ax, [@@ResCode]
  4146.     cmp    [WORD LOW DWORD gs:2Dh*4], OFFSET Int2DEntry
  4147.     jne    @@Resize
  4148.     cmp    [WORD HIGH DWORD gs:2Dh*4], ax
  4149.     jne    @@Resize
  4150.     cmp    [WORD LOW DWORD gs:2Fh*4], OFFSET Int2FEntry
  4151.     jne    @@Resize
  4152.     cmp    [WORD HIGH DWORD gs:2Fh*4], ax
  4153.     jne    @@Resize
  4154. ; Restore interrupt vectors
  4155.     push    ds
  4156.     pop    gs
  4157.     ASSUME    gs:ResCode
  4158.     lds    dx, [OldInt2D]
  4159.     ASSUME    ds:NOTHING
  4160.     mov    ax, 252Dh
  4161.     int    21h
  4162.     lds    dx, [OldInt2F]
  4163.     mov    ax, 252Fh
  4164.     int    21h
  4165.     push    gs
  4166.     pop    ds
  4167.     ASSUME    ds:ResCode
  4168. ; Release memory block
  4169.     mov    ah, 49h
  4170.     int    21h    
  4171.     jmp    @@MemReleased
  4172. ; Resize memory block       
  4173. @@Resize:
  4174.     mov     bx, OFFSET EndUninstalledCode
  4175.     shr    bx, 4
  4176.     add    bx, 17        ; Paragraphs to keep (code+PSP+1)
  4177.     mov     ah, 4Ah
  4178.     int     21h
  4179. ; Patch in far jump into driver's interrupt code to chain to original handler.
  4180.     mov    [WORD Int2DEntry], 0EA90h    ; NOP and JMP FAR
  4181.     mov    [WORD Int2FEntry], 0EA90h
  4182.  
  4183. @@MemReleased:
  4184. ; Back to original PSP       
  4185.     mov    bx, [@@PSP]
  4186.     mov     ah, 50h
  4187.     int     21h
  4188.  
  4189.     pop    gs es ds
  4190.         popad
  4191.     mov    al, 0FFh
  4192.     ret
  4193. ENDP    UninstallDriver
  4194.  
  4195. ;---------------------------------------------------------------------
  4196. ; Query logical drive connected.
  4197. ; Return: AH=Install status (00h Not installed, 01h Installed)
  4198. PROC    QueryDrive PASCAL
  4199.     ARG    @@Drive, @@ApiFunc
  4200.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4201.     push    bx
  4202.     mov    ah, [BYTE LOW @@ApiFunc]
  4203.     mov    al, 10h        ; Query drive
  4204.     mov    bx, [@@Drive]
  4205.     int    2Dh
  4206.     pop    bx
  4207.         ret
  4208. ENDP    QueryDrive
  4209.  
  4210. ;---------------------------------------------------------------------
  4211. ; Query HPFS partition connected.
  4212. ; Return: AH=Install status (00h Not installed, 01h Installed)
  4213. ;         DL=Drive letter associated with partition (if installed)
  4214. PROC    QueryPart PASCAL
  4215.     ARG    @@PartNum, @@ResCodeSeg
  4216.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4217.     push    bx cx dx
  4218.     push    ds es
  4219.     mov    ds, [@@ResCodeSeg]
  4220.     mov    dx, -1
  4221. @@1:
  4222.     inc    dx
  4223.     cmp    dx, 26
  4224.     je    @@PartNotFound
  4225.     movzx    bx, dl
  4226.     shl    bx, 1
  4227.     mov    bx, [DataSegs+bx]
  4228.     or    bx, bx
  4229.     je    @@1
  4230.     mov    es, bx
  4231.     ASSUME    es:ResData
  4232.     mov    cl, [BYTE LOW @@PartNum]
  4233.     cmp    [PartitionNr], cl
  4234.     jne    @@1
  4235.     ASSUME    es:NOTHING
  4236.     mov    ax, 01FFh
  4237.     jmp    @@Done
  4238. @@PartNotFound:
  4239.     mov    ax, 0FFh
  4240. @@Done:
  4241.     pop    es ds
  4242.     pop    dx cx bx
  4243.     ret
  4244. ENDP    QueryPart
  4245.  
  4246. ;---------------------------------------------------------------------
  4247. ; Remove a drive.
  4248. ; Return: AH=result 
  4249. ;    00h = Uninstalled
  4250. ;    01h = Drv not installed
  4251. ;    02h = Failed for other reason
  4252. PROC    RemoveDrv PASCAL
  4253.     ARG    @@Drive, @@ResCodeSeg
  4254.     LOCAL    @@RetValue
  4255.     ASSUME    ds:ResCode,es:NOTHING,fs:DSeg,gs:NOTHING
  4256.         pushad
  4257.         push    ds es fs
  4258.     mov    ax, DSeg
  4259.     mov    fs, ax
  4260.     mov    ds, [@@ResCodeSeg]
  4261. ; Release resident data segment
  4262.     movzx    bx, [BYTE LOW @@Drive]
  4263.         shl    bx, 1
  4264.     mov    dx, [DataSegs+bx]
  4265.         or    dx, dx
  4266.         jz    @@NotInstalled
  4267.     mov    [DataSegs+bx], 0
  4268.         mov    es, dx
  4269.         mov    ah, 49h
  4270.         int    21h
  4271. ; Patch CDS
  4272.         mov    ah, 52h
  4273.         int    21h        ; Get List of Lists in ES:BX
  4274.         les     bx, [es:bx+16h] ; CDS array
  4275.     mov     al, [BYTE LOW @@Drive]
  4276.         mov     dl, [BYTE CDSSize]
  4277.     mul     dl
  4278.     add     bx, ax          ; CDS entry for drive
  4279.     mov    [WORD es:bx+43h], 0
  4280.     mov    [@@RetValue], 0FFh
  4281.     jmp    @@Exit
  4282.         
  4283. @@NotInstalled:
  4284.     mov    [@@RetValue], 01FFh
  4285. @@Exit:    pop    fs es ds
  4286.         popad
  4287.     mov    ax, [@@RetValue]
  4288.         ret
  4289. ENDP    RemoveDrv
  4290.  
  4291. ;---------------------------------------------------------------------
  4292. ; Parse command line.
  4293. PROC    ParseCmdLine STDCALL
  4294.     LOCAL   @@LastByte, @@CacheOpt:BYTE, @@DrvSpecd, @@DriveNo:BYTE
  4295.     LOCAL    @@PartNum:BYTE, @@UninstallOpt:BYTE
  4296.         ASSUME  ds:DSeg,es:NOTHING,fs:ResCode
  4297.     mov    [@@CacheOpt], 0        ; /C option flag
  4298.     mov    [@@UninstallOpt], 0    ; /U option flag
  4299.     mov    [@@DrvSpecd], 0
  4300.     mov     es, [PSP]
  4301.     movzx   ax, [BYTE es:80h]
  4302.     cmp     al, 2
  4303.     jb      @@Done
  4304.     add     al, 80h
  4305.     mov     [@@LastByte], ax
  4306. ; Find first non-space character
  4307.     mov     di, 81h
  4308. @@Next:
  4309.     mov     cx, [@@LastByte]
  4310.     sub     cx, di
  4311.     jc      @@Done
  4312.     inc     cx
  4313.     mov     al, ' '
  4314.     cmp     al, ' '         ; Set zero flag
  4315.     repe scasb
  4316.     je      @@Done
  4317.     dec     di
  4318. ; Get char and convert to upper case
  4319.     mov     dl, [es:di]
  4320.     mov     ax, 6520h
  4321.     int     21h
  4322. ; Check for drive spec 'A'..'Z'
  4323.     cmp     dl, 'A'
  4324.     jb      @@Parse1
  4325.     cmp     dl, 'Z'
  4326.     ja      @@Parse1
  4327. ; Drive spec.
  4328.     inc     di
  4329.     mov     al, ':'                 ; Match a colon
  4330.     scasb
  4331.     jne     @@errBadOption
  4332.     sub     dl, 'A'                 ; Drive number
  4333.     mov     [@@DriveNo], dl
  4334. ; Check for partition number '1'..'9'
  4335.     cmp    di, [@@LastByte]
  4336.     ja    @@SetDrvLetter        ; Save drive letter in list
  4337.     mov     al, [es:di]
  4338.     cmp    al, ' '
  4339.     je    @@SetDrvLetter
  4340.     cmp     al, '1'
  4341.     jb      @@errBadOption
  4342.     cmp     al, '9'
  4343.     ja      @@errBadOption
  4344. ; Partition number
  4345.     mov     [Install], 1            ; Partition number - installing
  4346.     inc     di
  4347.     sub     al, '0'
  4348.     mov     [@@PartNum], al
  4349.     cmp    di, [@@LastByte]
  4350.     ja    @@SetPart
  4351.     mov    al, [es:di]        ; Second digit
  4352.     cmp    al, ' '
  4353.     je    @@SetPart
  4354.     cmp    al, '0'
  4355.     jb    @@errBadOption
  4356.     cmp    al, '9'
  4357.     ja    @@errBadOption
  4358.     inc    di
  4359.     sub    al, '0'
  4360.     mov    bl, [@@PartNum]
  4361.     shl    bl, 3            ; Multiply by 10
  4362.     add    bl, [@@PartNum]
  4363.     add    bl, [@@PartNum]
  4364.     add    al, bl
  4365.     mov    [@@PartNum], al
  4366.     cmp    di, [@@LastByte]
  4367.     ja    @@SetPart
  4368.     mov    al, [es:di]
  4369.     cmp    al, ' '
  4370.     jne    @@errBadOption
  4371. @@SetPart:
  4372.     mov     [@@DrvSpecd], 1           ; Drive letter specified
  4373.     movzx    bx, [@@PartNum]
  4374.     mov    al, [@@DriveNo]
  4375.     cmp    [Partitions+bx], 0FEh
  4376.     jne    @@errPartUsed
  4377.     mov    [Partitions+bx], al
  4378.     jmp    @@Next
  4379. @@SetDrvLetter:
  4380.     movzx    bx, [@@DriveNo]
  4381.     mov    [UnInstallDrv+bx], 1    ; Uninstall this drive
  4382.     mov    [UnInstall], 1
  4383.     mov    [SpecUninstall], 1    ; Drive to uninstall specified
  4384.     jmp    @@Next
  4385.  
  4386. @@Parse1:
  4387. ; Check for switches
  4388.     mov     al, '/'
  4389.     scasb
  4390.     jne     @@errBadOption
  4391. ; Switch
  4392.     cmp     di, [@@LastByte]
  4393.     ja      @@errBadOption
  4394.     mov     dl, [es:di]
  4395.     inc     di
  4396.     mov     ax, 6520h        ; Upper case
  4397.     int     21h
  4398.     cmp    dl, 'B'
  4399.     je    @@BIOSExtension
  4400.     cmp    dl, 'C'
  4401.     je      @@CacheSize
  4402.     cmp     dl, 'U'
  4403.     je      @@UnInstall
  4404.     cmp    dl, 'L'
  4405.     je    @@ConvertLong
  4406.         cmp     dl, 'M'
  4407.         je      @@Multitrack
  4408.     jmp     @@errBadOption
  4409. ; /B switch
  4410. @@BIOSExtension:
  4411. ;    mov    [UseExtCHS], 1
  4412. ;    cmp     di, [@@LastByte]
  4413. ;    ja      @@Next
  4414. ;    cmp     [BYTE es:di], ' '
  4415. ;    jne     @@errBadOption
  4416. ;    jmp     @@Next
  4417. ;***
  4418.     cmp     di, [@@LastByte]
  4419.     ja      @@errBadOption
  4420.     mov    dl, [es:di]
  4421.     inc    di
  4422.     cmp     dl, '1'
  4423.     je      @@ExtCHS
  4424.     cmp    dl, '2'
  4425.     je    @@NoIBMExt
  4426.     jmp    @@errBadOption
  4427. @@ExtCHS:
  4428.     mov    [UseExtCHS], 1
  4429.     cmp     di, [@@LastByte]
  4430.     ja      @@Next
  4431.     cmp     [BYTE es:di], ' '
  4432.     jne     @@errBadOption
  4433.     jmp     @@Next
  4434. @@NoIBMExt:
  4435.     mov    [NoIBMExt], 1
  4436.     cmp     di, [@@LastByte]
  4437.     ja      @@Next
  4438.     cmp     [BYTE es:di], ' '
  4439.     jne     @@errBadOption
  4440.     jmp     @@Next
  4441.  
  4442. ; /C switch
  4443. @@CacheSize:
  4444.     cmp    [@@CacheOpt], 0
  4445.     jnz    @@errBadOption        ; Only one /C option allowed
  4446.     mov    [@@CacheOpt], 1
  4447.     mov     [Install], 1          ; /C switch - installing
  4448.     cmp     di, [@@LastByte]
  4449.     je      @@errBadOption
  4450.     mov     al, '='
  4451.     scasb
  4452.     jne     @@errBadOption
  4453.     xor     eax, eax              ; AX=Converted number
  4454. @@GetDigit:
  4455.     cmp     di, [@@LastByte]
  4456.     ja      @@ConvDone
  4457.     cmp     [BYTE es:di], ' '
  4458.     je      @@ConvDone
  4459.     mov     bx, 10
  4460.     mul     bx
  4461.     or      dx, dx
  4462.     jne     @@errBadOption
  4463.     cmp     [BYTE es:di], '0'
  4464.     jb      @@errBadOption
  4465.     cmp     [BYTE es:di], '9'
  4466.     ja      @@errBadOption
  4467.     add     al, [es:di]
  4468.     adc     ah, 0
  4469.     jc      @@errBadOption
  4470.     sub     ax, 30h
  4471.     inc     di
  4472.     jmp     @@GetDigit
  4473. @@ConvDone:
  4474.     cmp     ax, MinCacheSize        ; Check that cache size is ok.
  4475.     jb      @@errBadOption
  4476.     cmp     ax, MaxCacheSize
  4477.     ja      @@errBadOption
  4478.     mov     ecx, eax
  4479.     shl     eax, 10                 ; Bytes
  4480.     mov     edx, eax
  4481.     shr     edx, 16                 ; Cache size in bytes in DX:AX
  4482.     mov     bx, 512+CacheEntrySize+2
  4483.     div     bx                      ; Cache entries
  4484.     mov     [CacheEntries], ax
  4485.     mov     [FreeEntry], ax
  4486.     dec     [FreeEntry]
  4487.     xor     dx, dx
  4488.     mov     bx, LoadFactor
  4489.     div     bx                      ; Hash table size=entries/load factor
  4490.     mov     [HashSize], ax
  4491. ; Allocate the XMS memory blocks
  4492.     cmp     [XMSFound], 0
  4493.     je      @@errXMSFailure
  4494.     
  4495.     mov     dx, [CacheEntries]
  4496.     shr     dx, 1
  4497.     adc     dx, 0                   ; KB needed for the sectors
  4498.     mov     ah, 09h
  4499.     call    [XMSEntry]
  4500.     or      ax, ax
  4501.     jz      @@errAllocFailed
  4502.     mov     [hCacheSectors], dx
  4503.     inc     [XMSBlocks]
  4504.  
  4505.     mov     ax, [CacheEntries]
  4506.     mov     dx, CacheEntrySize
  4507.     mul     dx
  4508.     shl     edx, 16
  4509.     mov     dx, ax
  4510.     shr     edx, 10
  4511.     inc     dx
  4512.     mov     ah, 09h
  4513.     call    [XMSEntry]
  4514.     or      ax, ax
  4515.     jz      @@errAllocFailed
  4516.     mov     [hCacheLists], dx
  4517.     inc     [XMSBlocks]
  4518.  
  4519.     mov     dx, [HashSize]
  4520.     shr     dx, 9
  4521.     inc     dx                      ; KB needed for hash table
  4522.     mov     ah, 09h
  4523.     call    [XMSEntry]
  4524.     or      ax, ax
  4525.     jz      @@errAllocFailed
  4526.     mov     [hHashTable], dx
  4527.     inc     [XMSBlocks]
  4528.     mov     [CacheOn], 1
  4529. ; Clear the hash table        
  4530.     push    ds es
  4531.     push    di
  4532.     mov     ax, fs
  4533.     mov     ds, ax
  4534.     mov     es, ax
  4535.     ASSUME  ds:ResCode,es:ResCode
  4536.     mov     di, OFFSET Buf1
  4537.     mov     cx, 100h
  4538.     xor     eax, eax
  4539.     rep stosd                       ; Clear Buf1-Buf2
  4540.     
  4541.     mov     cx, [HashSize]
  4542.     shr     cx, 9
  4543.     inc     cx
  4544.     mov     [XMoveStruc.Length], 1024
  4545.     mov     [XMoveStruc.SourceHandle], 0
  4546.     mov     [WORD XMoveStruc.SourceOffset], OFFSET Buf1
  4547.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  4548.     mov     ax, [hHashTable]
  4549.     mov     [XMoveStruc.DestHandle], ax
  4550.     mov     [XMoveStruc.DestOffset], 0
  4551. @@InitCacheTbl:
  4552.     mov     ah, 0Bh
  4553.     mov     si, OFFSET XMoveStruc
  4554.     call    [XMSEntry]
  4555.     add     [XMoveStruc.DestOffset], 1024
  4556.     or      ax, ax
  4557.     loopnz  @@InitCacheTbl
  4558.     jz      @@errXMSFailure
  4559. ; Clear the sentinel, entry 0
  4560.     mov     ax, [hCacheLists]
  4561.     mov     [XMoveStruc.DestHandle], ax
  4562.     mov     [XMoveStruc.DestOffset], 0
  4563.     mov     [WORD XMoveStruc.SourceOffset], OFFSET Buf1
  4564.     mov     [XMoveStruc.Length], CacheEntrySize
  4565.     mov     ah, 0Bh
  4566.     call    [XMSEntry]
  4567.     jz      @@errXMSFailure
  4568.     pop     di
  4569.     pop     es ds
  4570.     ASSUME  ds:DSeg,es:NOTHING
  4571.     jmp     @@Next  
  4572. ; /U switch
  4573. @@UnInstall:
  4574.     mov     [UnInstall], 1          ; /U switch - uninstall
  4575.     mov    [@@UnInstallOpt], 1
  4576.     cmp     di, [@@LastByte]
  4577.     ja      @@Next
  4578.     cmp     [BYTE es:di], ' '
  4579.     jne     @@errBadOption
  4580.     jmp     @@Next
  4581. ; /L switch
  4582. @@ConvertLong:
  4583.     mov    [ConvertLong], 1
  4584.     cmp     di, [@@LastByte]
  4585.     ja      @@Next
  4586.     cmp     [BYTE es:di], ' '
  4587.     jne     @@errBadOption
  4588.     jmp     @@Next
  4589. @@Multitrack:
  4590.         mov     [Multitrack], 0
  4591.         cmp     di, [@@LastByte]
  4592.         ja      @@Next
  4593.         cmp     [BYTE es:di], ' '
  4594.         jne     @@errBadOption
  4595.         jmp     @@Next
  4596.  
  4597. @@errBadOption:
  4598.     Abort   7
  4599. @@errPartUsed:
  4600.     Abort    17        ; Partition already specified once
  4601. @@errXMSFailure:
  4602.     Abort   10
  4603. @@errAllocFailed:
  4604.     cmp     bl, 0A0h
  4605.     jne     @@errAlloc1
  4606.     Abort   9               ; Out of XMS
  4607. @@errAlloc1:
  4608.     cmp     bl, 0A1h
  4609.     jne     @@errAlloc2
  4610.     Abort   13              ; Out of XMS handles
  4611. @@errAlloc2:
  4612.     Abort   10
  4613. @@Done:
  4614.     mov    al, [UnInstall]
  4615.     xor    al, [@@UnInstallOpt]
  4616.     jnz    @@errBadOption    ; Can't specify "/U" xor "d:"
  4617.     mov     al, [Install]
  4618.     test    al, [UnInstall]
  4619.     jnz     @@errBadOption    ; Can't both install and uninstall
  4620.     or    al, al
  4621.     jz    @@Exit
  4622.     cmp    [@@DrvSpecd], 0
  4623.     jz    @@Exit
  4624. ; Mark unwanted partition numbers
  4625.     mov    bx, -1
  4626. @@MarkPart:
  4627.     inc    bx
  4628.     cmp    bx, 26
  4629.         ja    @@Exit
  4630.     cmp    [Partitions+bx], 0FEh
  4631.     jne    @@MarkPart
  4632.     mov    [Partitions+bx], 0FFh
  4633.         jmp    @@MarkPart
  4634. @@Exit:
  4635.     ret
  4636. ENDP    ParseCmdLine
  4637.  
  4638. ENDS    CSeg
  4639.  
  4640. ;--------------------------------------------- Data for transient section
  4641. SEGMENT DSeg
  4642. MsgHello DB     "iHPFS     An installable HPFS driver for DOS       "
  4643.         DB      'Version 1.26    97-11-18',10,13
  4644.         DB      "Copyright (C) 1993-1997, Marcus Better.",10,13,10,13,"$"
  4645. MsgInstalled DB "Installed as "
  4646. MsgDrvLetter DB 0, ":",10,13,"$"
  4647. MsgUnInstalled DB "Driver uninstalled.",10,13,"$"
  4648. MsgDrvRemoved DB "Removed drive "
  4649. MsgDrvRemovedLetter DB "A:",10,13,"$"
  4650. DiskNumber DB    80h    ; Hard disk number
  4651. PartCount DB    0    ; Partition counter
  4652. Partitions DB    27 DUP(0FEh) ; Maps partitions to drive letters.
  4653.             ; 0FFh=don't install, 0FEh=first free drv letter.
  4654. XMSFound DB     0       ; Flag: XMS Found?
  4655. XMSBlocks DB    0       ; XMS blocks allocated
  4656. Install DB      0       ; Install iHPFS
  4657. UnInstall DB    0       ; Uninstall iHPFS. Error if both Install AND UnInstall
  4658. UnInstallDrv DB    26 DUP(0); Uninstall drive if set.
  4659. SpecUninstall DB 0    ; Set if specific drives are to be uninstalled.
  4660. DriverLoaded DB    0    ; Set if iHPFS loaded (during uninstall)
  4661. Installed DB    0    ; Set if any drive successfully installed,
  4662.             ; or if any uninstalled.
  4663. ErrSignaled DB  0       ; Error message displayed in ScanPartTbl
  4664. ExtPartBase DD    0    ; Base of extended partition offsets
  4665. UseExtCHS DB    0    ; Use extended CHS addressing
  4666. NoIBMExt DB    0    ; Do not use IBM/MS Extensions
  4667. DiskAddrPkt1 DiskAddrPacketStruct <>
  4668.  
  4669. ; DOS info
  4670. LstOfLst DD     0       ; Address of List of Lists
  4671. LastDrive DB    0
  4672. DrDos    DB    0    ; Nonzero if DR-DOS (zero if Novell DOS)
  4673. CDSSize    DW    58h    ; Size of CDS entry
  4674.  
  4675. ; Error message table
  4676. ErrMsgTbl DW    MsgBadDOSVer            ; Error 0
  4677.     DW      MsgMallocErr            ; Error 1
  4678.     DW      MsgNoAvailDrvLetter     ; Error 2
  4679.     DW      MsgNoSDA                ; Error 3
  4680.     DW      MsgDiskError            ; Error 4
  4681.     DW      MsgNoPart               ; Error 5
  4682.     DW      MsgDrvUsed              ; Error 6
  4683.     DW      MsgBadCmdLine           ; Error 7
  4684.     DW      MsgInvDrv        ; Error 8
  4685.     DW      MsgOutOfXMS             ; Error 9
  4686.     DW      MsgXMSAllocFailed       ; Error 10
  4687.     DW      MsgCantUninstall        ; Error 11
  4688.     DW      MsgCantRmDrv            ; Error 12
  4689.     DW      MsgOutOfXMSHandles      ; Error 13
  4690.     DW    MsgNoMux        ; Error 14
  4691.     DW    MsgNotLoaded        ; Error 15
  4692.     DW    MsgDrvNotInst        ; Error 16
  4693.     DW    MsgOneDrivePerPart    ; Error 17
  4694.     DW    MsgPartNotFound        ; Error 18
  4695.     DW    MsgPartInstalled    ; Error 19
  4696.                                 
  4697. ; Error messages
  4698. MsgBadDOSVer     DB     "Wrong DOS version.",10,13,"$"
  4699. MsgMallocErr     DB     "Memory allocation error.",10,13,"$"
  4700. MsgNoAvailDrvLetter DB    "Out of drive letters.",10,13,"$"
  4701. MsgNoSDA     DB     "Compatibility error.",10,13,"$"
  4702. MsgDiskError     DB     "Error reading disk.",10,13,"$"
  4703. MsgNoPart     DB     "Cannot find HPFS partition.",10,13,"$"
  4704. MsgDrvUsed     DB     "Drive "
  4705. MsgDrvUsedLetter DB    "A: already in use.",10,13,"$"
  4706. MsgInvDrv    DB     "Invalid drive letter "
  4707. MsgInvDrvLetter    DB    "A:",10,13,"$"
  4708. MsgBadCmdLine    DB     "Invalid command line arguments.",10,13
  4709.         DB      "Syntax: IHPFS [options] [d:n d:n ...]",10,13
  4710.         DB    "        IHPFS /U [d:]",10,13
  4711.         DB      "where d is a drive letter and n is the number of the "
  4712.         DB      "HPFS partition.",10,13,10,13
  4713.         DB      "Options:",10,13
  4714.         DB    "/B1          Use more BIOS extensions to access high partitions.",10,13
  4715.         DB    "/B2          Do not use IBM/MS Extensions.",10,13
  4716.         DB      "/C=x         Allocate x KB for cache. Must be between 32 and 32768.",10,13
  4717.         DB    "/L           Convert long filenames.",10,13
  4718.                 DB      "/M           Disable multitrack operations.",10,13
  4719.         DB      "/U           Uninstall driver.",10,13
  4720.         DB    "$"
  4721. MsgOutOfXMS     DB    "Out of XMS memory.",10,13,"$"
  4722. MsgXMSAllocFailed DB    "Cannot allocate XMS memory.",10,13,"$"
  4723. MsgCantUninstall DB    "Cannot unload driver.",10,13,"$"
  4724. MsgCantRmDrv    DB    "Couldn't uninstall drive "
  4725. MsgCantRmDrvLetter DB    "A:",10,13,"$"
  4726. MsgOutOfXMSHandles DB    "Out of XMS handles.",10,13,"$"
  4727. MsgNoMux     DB    "No free multiplex function found.",10,13,"$"
  4728. MsgNotLoaded    DB    "iHPFS not loaded.",10,13,"$"
  4729. MsgDrvNotInst    DB    "iHPFS is not installed for drive "
  4730. MsgDrvNotInstLetter DB "A:",10,13,"$"
  4731. MsgOneDrivePerPart DB    "Cannot install two iHPFS drives for the same partition.",10,13,"$"
  4732. MsgPartNotFound    DB    "Partition "
  4733. MsgPartNotFoundNr DB    " 0 not found.",10,13,"$"
  4734. MsgPartInstalled DB    "iHPFS already installed for partition "
  4735. MsgPartInstalledNr DB    " 0.",10,13,"$"
  4736. MsgBadPartTable    DB    "Bad partition table signature.",10,13,"$"
  4737. ENDS    DSeg
  4738.  
  4739. SEGMENT SSeg    STACK
  4740.     DB      128 DUP ('STACK---')
  4741. ENDS    SSeg
  4742.  
  4743.     END     Main
  4744.