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