home *** CD-ROM | disk | FTP | other *** search
/ Beijing Paradise BBS Backup / PARADISE.ISO / software / BBSDOORW / SHSUCD11.ZIP / SHSUCDX.ASM < prev    next >
Assembly Source File  |  1995-12-07  |  59KB  |  1,827 lines

  1. ; ***************************************************************************
  2. ;
  3. ;     SHSUCDX.ASM  Version 1.0
  4. ;     John H. McCoy, December 1995
  5. ;     Sam Houston St. Univ., TX 77341-2206
  6. ;
  7. ;     SHSUCDX is an un-loadable CD-ROM redirector substitute for MSCDEX.
  8. ;        Version 1.0 supports up to 10 CD drives.  Each drive is single
  9. ;        sector buffered and the last 10 directory entries are cached.
  10. ;        This version fixes an incompatability with Netware VLM client,
  11. ;        ignores associative files on MAC compatable disks and fixes a
  12. ;        directory problem involving some CDs created on Unix systems.
  13. ;
  14. ;        Approx 17K of RAM is needed to install SHSUCDX.  The resident size
  15. ;        for a single drive is less than 11K.  Each additional drive increases
  16. ;        the resident size by 2500 bytes.  Multiple drivers are supported.
  17. ;        The driver name, drive letter, drive unit and number of drives from
  18. ;        each driver can be specified on the command line.
  19. ;
  20. ;        SHSUCDX does not attempt to read the CD ROM until an access request
  21. ;        is made.  Thus, the CD drive does not have to be ready when the
  22. ;        redirector is loaded.  If more than 7 seconds elapse between access
  23. ;        requests a media check is made.  The buffers and cache are flushed
  24. ;        and the CD is re-read only if the driver reports a media change.
  25. ;
  26. ;        When SHSUCDX unloads it marks the drives it used as invalid.
  27. ;
  28. ;     SHSUCDX has been run with MS-DOS 4, 5, 6 and 7 stand-alone, under
  29. ;        Windows 3.1, and in a specific DOS window under OS2.
  30. ;
  31. ;     A CD-ROM driver which supports the CD-ROM extensions must be loaded
  32. ;        before loading SHSUCDX.  By default, SHSUCDX looks for a driver
  33. ;        named SHSU-CDN.
  34. ;
  35. ;     usage:  SHSUCDX [/D:DriverName[,[Drive][,[Unit][,[MaxDrives]]]]]
  36. ;
  37. ;        DriverName  1 to 8 characters.
  38. ;        Drive       First drive letter to assign to drives attached to
  39. ;                       this driver.
  40. ;        Unit        First drive unit on this driver to be assigned to a
  41. ;                       drive letter.  (Allowed range 0 to 99)
  42. ;        MaxDrives   Maximum number of drives on this driver that are to
  43. ;                       be assigned to drive letters.
  44. ;
  45. ;        Note:  The drive letter assigned to units of a second driver will
  46. ;          always be higher than those assigned to the first driver and
  47. ;          those assigned to a third driver will be higher than those
  48. ;          assigned to the second.
  49. ;
  50. ;        example: SHSUCDX
  51. ;
  52. ;        SHSUCDX finds the first available drive letter and assigns it
  53. ;        to device unit 0 of the default driver SHSU-CDN.  If there is a
  54. ;        second and/or third CD drive they are assigned to the next avail-
  55. ;        able letters in sequence.  Drive letters in use are skipped.  The
  56. ;        first CD supported by a driver is device unit 0 regardless of its
  57. ;        SCSI address.
  58. ;
  59. ;        example: SHSUCDX /D:CD001,F,,1  /D:SHSU-CDN,,1
  60. ;
  61. ;        SHSUCDX assigns drive F to device unit 0 of the driver CD001.
  62. ;        Units 1 and 2 of driver SHSU-CDN are then assigned to the next
  63. ;        available letters.
  64. ;
  65. ;        example: SHSUCDX /D:CD001,,1,1  /D:CD001,,4,1
  66. ;
  67. ;        SHSUCDX assigns the first available drive letter to device unit 1
  68. ;        of the driver CD001 and drive unit 4 to the next.  This allows
  69. ;        access to non-contiguous drive units without having to support
  70. ;        un-needed units.
  71. ;
  72. ;     unload: SHSUCDX [-u|/u]
  73. ;
  74. ;     The following INT 2F, 15h functions are supported:
  75. ;
  76. ;        00      Get number of CD-ROM drives
  77. ;        01      Get CD-ROM drive device list
  78. ;        02      Get Copyright File Name
  79. ;        03      Get Abstract File Name
  80. ;        05      Read VTOC (1st only)
  81. ;        08      Absolute disk read
  82. ;        0B      CDROM drive check
  83. ;        0C      MSCDEX version
  84. ;        0D      Get CD-ROM drive letters
  85. ;        0F      Get directory Entry
  86. ;        10      Send device request
  87. ;
  88. ;   SHSUCDX is a copyright reserved, free use program.
  89. ;
  90. ;   (c)John H. McCoy, 1994,1995 Sam Houston St. Univ., TX 77341-2206
  91. ;
  92. ; ***************************************************************************
  93. ;
  94. ;    Microsoft has not documented the redirector functions.  I have borrowed
  95. ;      from and am particularly indebted to the authors of:
  96. ;
  97. ;      A CD-ROM redirector for HighSierra and ISO 9660 disks.
  98. ;         Jim Harper, DDJ, March 1993
  99. ;      Inside the ISO-9660 Filesystem Format
  100. ;         William and Lynne Jolitz, DDJ, December 1992
  101. ;      Undocumented DOS, Chapter 4.
  102. ;         Andrew Schulman, et. al, Addison Wesley, 1990
  103. ;
  104. ;    Written for MASM 6.0b.  C functions compiled with MSC 5.1
  105. ; ***************************************************************************
  106. ;
  107. ; Modifications 3-4-94
  108. ;   test redir not network bit on call
  109. ;   set drive flags for physical network redir on install
  110. ;   test for physical network redir before clearing root
  111.  
  112.  
  113.  
  114. option nokeyword:<name type length >
  115. option expr16
  116.  
  117. ; make offsets group relative instead of segment relative
  118. .model small
  119.  
  120. fptr  typedef  far ptr
  121. nptr  typedef  near ptr
  122.  
  123. include undoc.inc
  124.  
  125. True              equ      1h
  126. False             equ      0h
  127. AsciiNul          equ      0
  128. AsciiA            equ      'A'
  129. cr                equ      0dh
  130. lf                equ      0ah
  131. QMark             equ      '?'
  132. UnknonwCDType     equ      0
  133.  
  134. ;  redirector equates
  135. REDIR             equ      11h
  136. InstallChk        equ      00h
  137. UnInstallCmd      equ      04h
  138. ChDir             equ      05h
  139. Close             equ      06h
  140. Flush             equ      07h
  141. Read              equ      08h
  142. Write             equ      09h
  143. GetSpace          equ      0ch
  144. SetAttr           equ      0eh
  145. GetAttr           equ      0fh
  146. Open              equ      16h
  147. FindFirst         equ      1Bh
  148. FindNext          equ      1Ch
  149. Seek              equ      21h
  150. PathName          equ      23h
  151. TOF               equ      2dh         ; truncate open file
  152. EOpen             equ      2eh
  153.  
  154. ;  CDS offsets
  155. RootSlashOff      equ      7
  156. DriveOff          equ      2
  157.  
  158. ;  MSCDEX equates
  159. MSCDEX            equ      15h
  160. MSCDEX_Q          equ      0DADAh
  161. MSCDEX_R          equ      0ADADh
  162.  
  163. ;
  164.  
  165. MAXDRIVES         equ      10
  166. CACHESIZE         equ      10
  167. SECTORSIZE        equ      2048
  168.  
  169. ; declare protos, publics and externals
  170.  
  171.    DoChDir           proto near C
  172.    DoRead            proto near C SFTp:fptr
  173.    DoGetSpace        proto near C
  174.    DoGetAttr         proto near C
  175.    DoOpen            proto near C
  176.    DoClose           proto near C SFTp:fptr
  177.    DoFindFirst       proto near C
  178.    DoFindNext        proto near C
  179.    DoSeek            proto near C SFTp:fptr
  180.    InitDrive         proto near C
  181.    ForUs             proto near C DriveLetter: byte
  182.    SetDDD            proto near C DriveLetter: byte
  183.    MsgOut            proto near C msg:near ptr char
  184.    CdReadLong        proto near C IOBuf:fptr,BlkNo:dword,NumBlks:word
  185.    CDMediaChanged    proto near C
  186.    PathLook          proto near C Dp:fptr, Pathp:fptr
  187.    ToHex             proto near C Num:word
  188.    Main2F            proto near
  189.    ParseCommandLine  proto near
  190.    ClrRoot           proto near
  191.    SetRoot           proto near  CDSx:fptr
  192.  
  193.    PUBLIC C FN1p, DTApp, PSPp, SAttrp
  194.    PUBLIC C DosDp, SDBp, SFTpp
  195.    PUBLIC C DriveNo, DriveIndex, DeviceUnit, NoDrives
  196.    PUBLIC C CDSBase, CDSLen, CDSp
  197.    PUBLIC C Drive, IODatap
  198.    PUBLIC C _FLAGS,_AX,_BX,_CX,_DX,_SI,_DI,_ES,DataSeg
  199.    PUBLIC _DirCache, _IOData
  200. DGROUP   group   _TEXT, _DATA, _BSS, C_COMMON, CONST, _INIT
  201.  
  202. ; this is the way C wants it
  203.  
  204. _TEXT    segment word PUBLIC 'CODE'
  205.          assume cs:DGROUP, ds:DGROUP
  206. _TEXT    ends
  207.  
  208. _DATA    segment word PUBLIC 'DATA'
  209.          assume ds:DGROUP
  210. _DATA    ends
  211.  
  212. _BSS     segment word PUBLIC 'BSS'
  213.          assume ds:DGROUP
  214. _BSS     ends
  215.  
  216. C_COMMON     segment word PUBLIC 'BSS'
  217.              assume ds:DGROUP
  218. C_COMMON     ends
  219.  
  220. CONST    segment word PUBLIC 'CONST'
  221.          assume ds:DGROUP
  222. CONST    ends
  223.  
  224. _INIT    segment word PUBLIC 'INIT'
  225.          assume cs:DGROUP, ds:DGROUP
  226. _INIT    ends
  227.  
  228.  
  229. EndOfCDX segment para STACK
  230.    byte "This STACK SEGMENT is here to satisfy loadhigh when using netx"
  231. EndOfCDX ends
  232.  
  233. _DATA    segment
  234.  
  235.          byte "(c)John H. McCoy, 1994, Sam Houston St. Univ., TX 77341-2206"
  236.  
  237. align 2
  238.  
  239. local_stack       word     128 dup ('ss')
  240. top_stack         word     DGROUP:$
  241.  
  242. DevHeader         fptr     ?
  243. DevStrategy       fptr     ?
  244. DevInterrupt      fptr     ?
  245. Old2F             fptr     ?
  246. DataSeg           word     ?
  247. _FLAGS            word     ?
  248. _PSP              word     ?
  249. _SP               word     ?
  250. _SS               word     ?
  251. _AX               word     ?
  252. _BX               word     ?
  253. _CX               word     ?
  254. _DX               word     ?
  255. _SI               word     ?
  256. _DI               word     ?
  257. _DS               word     ?
  258. _ES               word     ?
  259. PSPp              fptr     ?
  260. FN1p              fptr     ?
  261. SAttrp            fptr     ?
  262. DosDp             fptr     ?
  263. SDBp              fptr     ?
  264. DTApp             fptr     ?
  265. SFTpp             fptr     ?
  266. CDSBase           fptr     ?
  267. CdsLen            word     ?       ; for this DOS version
  268. CDSp              fptr     ?       ; must be recalced for current drive
  269. DrvEntLen         word     sizeof DrvEnt
  270. IODatap           word     _IOData
  271. DirCachep         word     _DirCache
  272.  
  273. align 1
  274.  
  275. Active            byte     0h
  276. FirstDriveNo      byte     0
  277. NoDrives          byte     MAXDRIVES
  278. DriveNo           byte     ?
  279. DeviceUnit        byte     ?
  280. DriveIndex        byte     ?
  281. ChainFlag         byte     False
  282.  
  283. rh_hdr             byte  27 dup(?);
  284.  
  285. ; ioctl in control blocks
  286.  
  287. IoCB_MediaChange     byte   9
  288.      MediaChange     byte   ?   ; 0    not changed
  289.                                 ; 1    dont't know
  290.                                 ; 0FFh media changed
  291.  
  292. IoCB_Status          byte   6   ; ioctl get status subcommand
  293.                      dword  ?   ; status  bit 0  0 door closed
  294.                                 ;                1 door open
  295.                                 ;         bit 1  0 door locked
  296.                                 ;                1 door unlocked
  297.                                 ;         bit 4  0 data read only
  298.                                 ;                1 data read and play audio
  299.                                 ;         bit 7  0 no prefetching
  300.                                 ;                1 supports prefetching
  301.                                 ;         bit 9  0 HSG addressing only
  302.                                 ;                1 HSG and RedBook audio addr
  303.                                 ;         bit 11 1 no CD in drive (best guess)
  304.  
  305. ; ioctl out control blocks
  306.  
  307. IoCB_EjectCD         byte  0   ; ioctl out eject subcommand
  308.   ; Note!!  It may be necessary to unlock a drive before the CD can be ejected.
  309.  
  310. IoCB_LockCD          byte  1   ; ioctl out lock/unlock subcommand
  311.                      byte  1   ; lock code
  312.  
  313. IoCB_UnLockCD        byte  1   ; ioctl out lock/unlock subcommand
  314.                      byte  0   ; unlock code
  315.  
  316.  
  317. _DATA    ends
  318.  
  319. _TEXT    segment
  320.  
  321. New2F    proc    far
  322.  
  323.    push     ax
  324.    inc      cs:Active
  325.  
  326.    ; is this call for us?
  327.  
  328.    .if (((ah != REDIR) && (ah != MSCDEX)) || (cs:Active > 1))
  329.       dec      cs:Active
  330.       pop      ax
  331.       jmp      cs:Old2F                   ; chain out
  332.    .endif
  333.  
  334.    pop      ax
  335.  
  336.    ; Handle REDIR install checks now, others after saving regs
  337.    .if ah == REDIR
  338.       .if al == InstallChk
  339.          push bp
  340.          mov  bp, sp
  341.          .if [bp].frame.fr_Parm1 == MSCDEX_Q
  342.             mov      [bp].frame.fr_Parm1, MSCDEX_R
  343.          .endif
  344.          mov      ax,0ffh
  345.          pop      bp
  346.          jmp      Fexit
  347.       .elseif al == UnInstallCmd
  348.          .if bx == MSCDEX_Q
  349.             invoke  ClrRoot
  350.             mov   es, cs:_PSP                ; tsr's psp
  351.             mov   es:[16h], cx               ; make unloader parent
  352.             mov   es:[0ah], dx               ; set terminate address
  353.             mov   dx, ds
  354.             mov   es:[0ah+2h], dx            ; in psp
  355.             mov   ah, 50h                    ; make TSR psp current psp
  356.             mov   bx, es
  357.             int   21h
  358.             mov   dx, word ptr cs:Old2F       ; restore vector
  359.             mov   ds, word ptr cs:Old2F[2]
  360.             mov   ax, 252Fh
  361.             int   21h
  362.             mov   ah,4ch                     ; normal terminate
  363.             int   21h                        ; will take us back to unloader
  364.          .else
  365.             dec      cs:Active
  366.             jmp      cs:Old2F                   ; chain out
  367.          .endif
  368.       .endif
  369.    .endif
  370.  
  371.    ; set up data addressing
  372.    mov      cs:_DS, ds
  373.    push     cs
  374.    pop      ds
  375.  
  376.    ;  save registers and switch to local stack
  377.    mov      _AX, ax
  378.    mov      _BX, bx
  379.    mov      _CX, cx
  380.    mov      _DX, dx
  381.    mov      _SI, si
  382.    mov      _DI, di
  383.    mov      _ES, es
  384.  
  385.    push      bp
  386.    mov       bp, sp
  387.    mov       ax, [bp].frame.fr_Flags
  388.    mov      _FLAGS, ax
  389.    mov      _SP, sp
  390.    mov      _SS, ss
  391.  
  392.    cli
  393.    push     cs
  394.    pop      ss
  395.    mov      sp, top_stack
  396.    sti
  397.  
  398.    INVOKE   Main2F
  399.  
  400.    ;  switch back to callers stack and restore registers
  401.  
  402.    cli
  403.    mov      ss, _SS
  404.    mov      sp, _SP
  405.    sti
  406.  
  407.    mov      bp, sp
  408.    mov      ax, _FLAGS
  409.    mov      [bp].frame.fr_Flags, ax
  410.    mov      ax, _AX
  411.    mov      bx, _BX
  412.    mov      cx, _CX
  413.    mov      dx, _DX
  414.    mov      si, _SI
  415.    mov      di, _DI
  416.    mov      es, _ES
  417.    mov      ds, _DS
  418.    pop      bp
  419.  
  420.  
  421.    .if   cs:ChainFlag
  422.       dec      cs:Active
  423.       jmp      cs:Old2F
  424.    .endif
  425.  
  426. Fexit:
  427.    dec      cs:Active
  428.    iret
  429.  
  430. New2F endp
  431.  
  432. Main2F   proc  near
  433.  
  434.  
  435.    mov      ChainFlag, False
  436.    mov      ax, _AX
  437.  
  438.    .if (ah == MSCDEX)
  439.       ; Handle the MSCDEX calls
  440.       .if al == 00h     ; get number of drive letters
  441.          mov   al,NoDrives
  442.          cbw
  443.          mov   _BX,ax
  444.          mov   al,FirstDriveNo
  445.          cbw
  446.          mov   _CX,ax
  447.          and   _FLAGS, 0FFFEh                ; A Ok
  448.          jmp   MExit
  449.       .elseif al == 01h ; get drive device list
  450.          sub      si, si
  451.          sub      cx, cx
  452.          .while (cl< NoDrives)
  453.              mov   ch, [si+Drive.Unit]
  454.              mov   byte ptr es:[bx], ch
  455.              mov   ax, word ptr [si+Drive.DevHdrp]
  456.              mov   word ptr es:[bx+1], ax
  457.              mov   ax, word ptr [si+Drive.DevHdrp+2]
  458.              mov   word ptr es:[bx+3], ax
  459.              add   si, sizeof DrvEnt
  460.              add   bx, 5
  461.              inc   cl
  462.          .endw
  463.          and   _FLAGS, 0FFFEh                ; A Ok
  464.          jmp   MExit
  465.       .elseif al == 02h ; Get Copyright File Name
  466.          .if ( cl < 26 )
  467.              add     cl, 'A'
  468.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  469.             .if   !(ax)
  470.               jmp @F
  471.             .endif
  472.          .endif
  473.          mov    _AX, 015h        ; error, invalid drive
  474.          or     _FLAGS, 01h
  475.          jmp    MExit
  476.      @@:
  477.          mov    di, _BX
  478.          mov    es, _ES
  479.          mov    ax, sizeof DrvEnt
  480.          mul    DriveIndex
  481.          lea    bx, Drive.CopyRightID
  482.          add    ax, bx
  483.          mov    si, ax
  484.          mov    cx, sizeof Drive.CopyRightID
  485.          cld
  486.          rep    movsb
  487.          mov    byte ptr es:[di], 0                   ; make sure we have a 0
  488.          and    _FLAGS, 0FFFEh                ; A Ok
  489.          jmp    MExit
  490.       .elseif al == 03h ; Get Abstract File Name
  491.          .if ( cl < 26 )
  492.              add     cl, 'A'
  493.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  494.             .if   !(ax)
  495.               jmp @F
  496.             .endif
  497.          .endif
  498.          mov    _AX, 015h        ; error, invalid drive
  499.          or     _FLAGS, 01h
  500.          jmp    MExit
  501.      @@:
  502.          mov    di, _BX
  503.          mov    es, _ES
  504.          mov    ax, sizeof DrvEnt
  505.          mul    DriveIndex
  506.          lea    bx, Drive.AbstractID
  507.          add    ax, bx
  508.          mov    si, ax
  509.          mov    cx, sizeof Drive.AbstractID
  510.          cld
  511.          rep    movsb
  512.          mov    byte ptr es:[di], 0         ; make sure we have a 0
  513.          and    _FLAGS, 0FFFEh                ; A Ok
  514.          jmp    MExit
  515.       .elseif al == 05h ; Read VTOC   not complete!   we only read first one
  516.          .if ( cl < 26 )
  517.              add     cl, 'A'
  518.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  519.             .if   !(ax)
  520.               jmp @F
  521.             .endif
  522.          .endif
  523.          mov    _AX, 015h        ; error, invalid drive
  524.          or     _FLAGS, 01h
  525.          jmp    MExit
  526.      @@:
  527.          mov   es, _ES
  528.          mov   bx, _BX
  529.          invoke CdReadLong, es::bx, 10h, 1
  530.          .if  (ax == 100h)
  531.             mov   _AX, 01
  532.             and   _FLAGS, 0FFFEh
  533.          .else
  534.             mov      _AX, 021h          ; drive not ready
  535.             or     _FLAGS, 01h
  536.          .endif
  537.          jmp      MExit
  538.       .elseif al == 08h ; Absolute disk read
  539.          .if ( cl < 26 )
  540.              add     cl, 'A'
  541.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  542.             .if   !(ax)
  543.               jmp @F
  544.             .endif
  545.          .endif
  546.          mov    _AX, 015h        ; error, invalid drive
  547.          or     _FLAGS, 01h
  548.          jmp    MExit
  549.      @@:
  550.          .if (_DX == 0)
  551.             mov ax, 100h
  552.          .else
  553.             mov   si, _SI
  554.             mov   di, _DI
  555.             mov   es, _ES
  556.             mov   bx, _BX
  557.             invoke CdReadLong, es::bx,si::di,_DX
  558.          .endif
  559.          .if  (ax == 100h)
  560.             mov   _AX, 01
  561.             and   _FLAGS, 0FFFEh
  562.          .else
  563.             mov      _AX, 021h          ; drive not ready
  564.             or     _FLAGS, 01h
  565.          .endif
  566.          jmp      MExit
  567.       .elseif al == 0Bh ; CDROM check
  568.          mov      _BX, MSCDEX_R
  569.          .if ( cl < 26 )
  570.              add     cl, 'A'
  571.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  572.             .if   !(ax)
  573.               jmp @F
  574.             .endif
  575.          .endif
  576.          mov    _AX, 0h          ; drive not supported by REDIR
  577.          jmp    MExit
  578.      @@:
  579.          mov     _AX, 0FFh
  580.          jmp      MExit
  581.       .elseif al == 0Ch ; MSCDEX version
  582.          mov   _BX, 0215h                 ; report ver 2.21
  583.          and   _FLAGS, 0FFFEh               ; A Ok
  584.          jmp   MExit
  585.       .elseif al == 0Dh ; drive letters
  586.          mov      ah, 0ffh
  587.          sub      si, si
  588.          sub      cx, cx
  589.          .while (cl< NoDrives)
  590.              mov   ch, byte ptr [si+Drive.No]
  591.              mov   byte ptr es:[bx], ch
  592.              add   si, sizeof DrvEnt
  593.              inc   bx
  594.              inc   cl
  595.          .endw
  596.          and   _FLAGS, 0FFFEh                ; A Ok
  597.          jmp   MExit
  598.       .elseif al == 0Fh ; Get Directory Entry
  599.          .if ( cl < 26 )
  600.              add     cl, 'A'
  601.              invoke  ForUs, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  602.             .if   !(ax)
  603.               jmp @F
  604.             .endif
  605.          .endif
  606.          mov    _AX, 015h        ; error, invalid drive
  607.          or     _FLAGS, 01h
  608.          jmp    MExit
  609.      @@:
  610.          mov    es, _ES
  611.          mov    bx, _BX
  612.          mov    si, _SI
  613.          mov    di, _DI
  614.          and   _FLAGS, 0FFFEh
  615.          invoke PathLook,si::di,es::bx
  616.          jmp   MExit
  617.       .elseif al == 10h ; device request
  618.          .if ( cl < 26 )
  619.              add     cl, 'A'
  620.              invoke  SetDDD, cl    ; sets DriveNo, DeviceUnit and DriveIndex
  621.             .if   !(ax)
  622.               jmp @F
  623.             .endif
  624.          .endif
  625.          mov    _AX, 015h        ; error, invalid drive
  626.          or     _FLAGS, 01h
  627.          jmp    MExit
  628.      @@:
  629.          mov    ax, sizeof DrvEnt
  630.          mul    DriveIndex
  631.          mov    bx, ax
  632.          mov    ax, word ptr [bx+Drive.Strategyp]
  633.          mov    word ptr DevStrategy, ax
  634.          mov    ax, word ptr [bx+Drive.Strategyp+2]
  635.          mov    word ptr DevStrategy+2, ax
  636.          mov    ax, word ptr [bx+Drive.Interruptp]
  637.          mov    word ptr DevInterrupt, ax
  638.          mov    ax, word ptr [bx+Drive.Interruptp+2]
  639.          mov    word ptr DevInterrupt+2, ax
  640.          push   ds
  641.          mov    es, _ES                ; restore rh ptr
  642.          mov    bx, _BX
  643.          mov    ah, DeviceUnit
  644.          mov    byte ptr es:rh.SubUnit[bx], ah
  645.          call   cs:DevStrategy
  646.          call   cs:DevInterrupt
  647.          pop    ds                   ; in case device driver wiped out ds
  648.          mov    es, _ES                ; restore rh ptr
  649.          mov    bx, _BX
  650.          .if    (es:rh.Command[bx] == rhcmdIOCTL_In)   ; on some drives we
  651.             les  bx,es:rhIOCTL.CBPtr[bx]               ; will miss media change
  652.             .if  (byte ptr es:[bx] == 09h)           ; if direct access checks
  653.                .if (byte ptr es:[bx+1] != 1)         ; so, if media has changed
  654.                   mov    ax, sizeof DrvEnt           ; set type to unknown to
  655.                   mul    DriveIndex                  ; force re-read on next
  656.                   mov    bx, ax                      ; non-direct access
  657.                   mov    word ptr [bx+Drive.Type], 0  ; 0 is type unknown
  658.                .endif
  659.             .endif
  660.          .endif
  661.          and   _FLAGS, 0FFFEh        ; A Ok from our perspective
  662.          jmp    MExit
  663.       .else                               ; we don't support it
  664.          mov   _AX,01h                    ; call it an error
  665.          or    _FLAGS, 01h
  666.          jmp   MExit
  667.       .endif
  668.    .endif
  669.  
  670.    ;  handle redirector calls
  671.  
  672.    .if (al == PathName)
  673.        mov    ChainFlag, True
  674.        jmp    MExit
  675.    .elseif  (al == FindFirst || al == Open || al == EOpen ||    \
  676.              al == ChDir || al == GetAttr ||  al == TOF)
  677.        les     di, FN1p
  678.        .if  ((byte ptr es:[di+DriveOff] < 'A') ||                        \
  679.              (byte ptr es:[di+DriveOff] > 'Z')  )
  680.            mov    ChainFlag, True
  681.            jmp    MExit
  682.        .endif
  683.        invoke  ForUs, byte ptr es:[di+DriveOff]
  684.  
  685.        .if   (ax == 0)
  686.           mov    ax, _AX                 ; restores ax
  687.        .else
  688.           .if (ax == 1)
  689.              mov    ChainFlag, True
  690.           .else
  691.              mov    _AX, 015h              ; drive not ready
  692.           .endif
  693.           jmp    MExit
  694.        .endif
  695.        .if al == FindFirst
  696.           invoke   DoFindFirst
  697.        .elseif al == Open || al == EOpen
  698.            mov   al, DriveNo
  699.            cbw
  700.            mul      CDSLen
  701.            les      bx, CDSBase
  702.            add      ax, bx
  703.            mov      word ptr CDSp, ax
  704.            mov      ax, es
  705.            mov      word ptr CDSp+2, ax
  706.            mov      bx, _BX
  707.            mov      es, _ES
  708.            invoke   DoOpen
  709.        .elseif al == ChDir
  710.            invoke   DoChDir
  711.        .elseif  al == GetAttr
  712.            invoke   DoGetAttr
  713.            jmp    MExit
  714.        .elseif  al == TOF             ; haven't got a clue about what to do
  715.            and   _FLAGS, 0FFFEh         ; just say we did it
  716.            mov    _AX, 0h             ; and flash 'em a smile
  717.            jmp    MExit
  718.        .endif
  719.    .elseif al == FindNext
  720.        les      di, SDBp                   ; get SDBp
  721.        mov      al, byte ptr es:[di]       ; SDBp->DriveLet (A - 0)
  722.        test     al, 40h                    ; redir not network when set
  723.        jnz      @F
  724.        mov      ChainFlag, True            ; not for us, chain out
  725.        jmp      MExit
  726.    @@: and      al, 01fh                   ; this part is actual drive letter
  727.        add      al, 'A'
  728.        invoke ForUs, al
  729.        .if   (ax == 0)
  730.           mov    ax, _AX
  731.        .else
  732.           .if (ax == 1)
  733.              mov    ChainFlag, True
  734.           .else
  735.              mov    _AX, 015h              ; drive not ready
  736.           .endif
  737.           jmp    MExit
  738.        .endif
  739.        invoke   DoFindNext
  740.    .elseif (al == Close || al == Read ||  al == Seek)
  741.        les      di, SFTpp                 ; ptr to cur SFTp
  742.        les      di, es:[di]               ; SFTp
  743.        mov      ax, es:[di].SFT.Flags
  744.        test     al, 40h                    ; redir not network when set
  745.        jnz      @F
  746.        mov      ChainFlag, True            ; not for us, chain out
  747.        jmp      MExit
  748.    @@: and      ax, 01fh
  749.        add      al, 'A'
  750.        invoke ForUs, al
  751.        .if   (ax == 0)
  752.           mov    ax, _AX
  753.           mov      es, _ES
  754.           mov      di, _DI
  755.        .else
  756.           .if (ax == 1)
  757.              mov    ChainFlag, True
  758.           .else
  759.              mov    _AX, 015h              ; drive not ready
  760.           .endif
  761.           jmp    MExit
  762.        .endif
  763.        .if al == Read
  764.           invoke   DoRead, es::di
  765.        .elseif al == Seek
  766.           invoke   DoSeek, es::di
  767.           jmp    MExit
  768.        .elseif al == Close
  769.           invoke   DoClose, es::di
  770.        .endif
  771.    .elseif al == GetSpace                ; es:di is curr CDSp
  772.        .if ((byte ptr es:[di+DriveOff] < 'A') ||                  \
  773.              (byte ptr es:[di+DriveOff] > 'Z')  )
  774.            mov    ChainFlag, True
  775.            jmp    MExit
  776.        .endif
  777.        invoke  ForUs, es:[di+DriveOff]
  778.        .if   (ax == 0)
  779.            invoke DoGetSpace
  780.        .else
  781.           .if (ax == 1)
  782.              mov    ChainFlag, True
  783.           .else
  784.              mov    _AX, 015h              ; drive not ready
  785.           .endif
  786.        .endif
  787.        jmp    MExit                ; this calls not for me
  788.    .else
  789.        mov    ChainFlag, True      ; just chain out.  Novell doesn't like
  790.        jmp    MExit                ; it if you call it an error
  791.    .endif
  792.    mov      _AX, ax
  793.  
  794. MExit:
  795.  
  796.    ret
  797.  
  798. Main2F  endp
  799.  
  800. CdReadLong  proc near C uses ds es bx si di, IOBufp: fptr,BlkNo: dword,NumBlks: word
  801.          mov   ax, sizeof DrvEnt
  802.          mul   DriveIndex
  803.          mov   bx, ax
  804.          mov   ax, word ptr [bx+Drive.Strategyp]
  805.          mov   word ptr DevStrategy, ax
  806.          mov   ax, word ptr [bx+Drive.Strategyp+2]
  807.          mov   word ptr DevStrategy+2, ax
  808.          mov   ax, word ptr [bx+Drive.Interruptp]
  809.          mov   word ptr DevInterrupt, ax
  810.          mov   ax, word ptr [bx+Drive.Interruptp+2]
  811.          mov   word ptr DevInterrupt+2, ax
  812.          lea   bx, rh_hdr
  813.          mov   rh.Length[bx], 27
  814.          mov   ah, DeviceUnit
  815.          mov   rh.SubUnit[bx], ah
  816.          mov   rh.Command[bx], rhcmdReadLong         ; read long
  817.          mov   ax, word ptr IOBufp
  818.          mov   word ptr rhReadLong.Bufp[bx], ax
  819.          mov   ax, word ptr IOBufp[2]
  820.          mov   word ptr rhReadLong.Bufp[bx+2], ax
  821.          mov   ax, word ptr BlkNo
  822.          mov   word ptr rhReadLong.StartBlk[bx], ax
  823.          mov   ax, word ptr BlkNo[2]
  824.          mov   word ptr rhReadLong.StartBlk[bx+2], ax
  825.          mov   ax, NumBlks
  826.          mov   word ptr rhReadLong.Count[bx], ax
  827.          xor   ax, ax                          ; zero for
  828.          mov   rhReadLong.AddrMode[bx], al  ; hsc addressing
  829.          mov   rhReadLong.ReadMode[bx], al  ; cooked mode
  830.          mov   rhReadLong.ISize[bx], al
  831.          mov   rhReadLong.ISkip[bx], al
  832.          push  cs
  833.          pop   es
  834.          call  cs:DevStrategy
  835.          call  cs:DevInterrupt
  836.          mov   ax, word ptr cs:rh_hdr+rh.status
  837.          ret
  838. CdReadLong  endp
  839.  
  840. CdMediaChanged  proc near C uses ds es bx si di
  841.          mov   ax, sizeof DrvEnt
  842.          mul   DriveIndex
  843.          mov   bx, ax
  844.          mov   ax, word ptr [bx+Drive.Strategyp]
  845.          mov   word ptr DevStrategy, ax
  846.          mov   ax, word ptr [bx+Drive.Strategyp+2]
  847.          mov   word ptr DevStrategy+2, ax
  848.          mov   ax, word ptr [bx+Drive.Interruptp]
  849.          mov   word ptr DevInterrupt, ax
  850.          mov   ax, word ptr [bx+Drive.Interruptp+2]
  851.          mov   word ptr DevInterrupt+2, ax
  852.          mov   bx, offset rh_hdr
  853.          mov   rh.Length[bx], 26
  854.          mov   ah, DeviceUnit
  855.          mov   rh.SubUnit[bx], ah
  856.          mov   rh.Command[bx], rhcmdIOCTL_In
  857.          mov   ax, OFFSET IoCB_MediaChange
  858.          mov   word ptr rhIOCTL.CBPtr[bx], ax
  859.          mov   ax, SEG IoCB_MediaChange
  860.          mov   word ptr rhIOCTL.CBPtr[bx+2], ax
  861.          mov   word ptr rhIOCTL.BytesToTransfer[bx], size IoCB_MediaChange
  862.          xor   ax, ax                         ;  zero out other fields
  863.          mov   rhIOCTL.MediaDesc[bx], al
  864.          mov   rhIOCTL.StartSector[bx], ax
  865.          mov   word ptr rhIOCTL.VolidPtr[bx], ax
  866.          mov   word ptr rhIOCTL.VolidPtr[bx+2], ax
  867.          push  cs
  868.          pop   es
  869.          call  cs:DevStrategy
  870.          call  cs:DevInterrupt
  871.          mov   al, cs:MediaChange       ; use cs in case driver wiped out ds
  872.          cbw
  873.          ret
  874. CdMediaChanged  endp
  875.  
  876. ClrRoot     proc near uses es bx ax cx dx si
  877.        sub      si, si
  878.        sub      cx, cx
  879.        .while (cl< cs:NoDrives)
  880.            mov   ch, byte ptr cs:[si+Drive.No]
  881.            mov   al, ch
  882.            cbw
  883.            mul      cs:CDSLen
  884.            les      bx, cs:CDSBase
  885.            add      bx, ax
  886.            test     es:[bx].CDS.Flags, 0C080h ; physical net redir drive ?
  887.            jz      @F                         ; couldn't be our drive
  888.            mov   ax, 0
  889.            mov   es:[bx].CDS.Flags, ax        ; clear drive flags
  890.            mov   al, ch
  891.            add   al, 'A'
  892.            mov   es:[bx].CDS.CurrPath, al
  893.            mov   al, ':'
  894.            mov   es:[bx+1].CDS.CurrPath, al
  895.            mov   al, '\'
  896.            mov   es:[bx+2].CDS.CurrPath, al
  897.            mov   al, 0
  898.            mov   es:[bx+3].CDS.CurrPath, al
  899.        @@: add   si, sizeof DrvEnt
  900.            inc   cl
  901.        .endw
  902.             ret
  903. ClrRoot     endp
  904.  
  905. _TEXT    ends
  906.  
  907. _INIT     segment
  908.  
  909.  
  910. ToHex        proc near C public uses ax bx cx dx, Num:word
  911.       mov   cl, 4
  912.       mov   ch, 4
  913.       mov   ah, 02h
  914.       mov   dx, Num
  915.       .while ch > 0
  916.           rol   dx, cl
  917.           mov   bx, dx
  918.           and   dx, 0fh
  919.           .if dl < 0Ah
  920.               add  dl, '0'
  921.           .else
  922.               add  dl, 'A' - 0Ah
  923.           .endif
  924.           int   21h
  925.           mov   dx, bx
  926.           dec   ch
  927.       .endw
  928.       ret
  929. ToHex    endp
  930.  
  931.  
  932. ; set up for one drive minimum
  933. ; drive table, cache and buffers are set up dynamically here at run time
  934.  
  935. align 2
  936. Drive             DrvEnt   1 dup (<>)         ;MAXDRIVES dup (<>)
  937. _DirCache         DirEnt   CACHESIZE dup (<>) ;MAXDRIVES*CACHESIZE dup (<>)
  938. _IOData           byte     SECTORSIZE dup('B')
  939.  
  940. ; insert filler so we don't wipe out INIT when whe init drive table and
  941. ;   link up dir cache
  942.  
  943. if (MAXDRIVES-1)*(sizeof DrvEnt+CACHESIZE*sizeof DirEnt)-SECTORSIZE GT 0
  944.  byte (MAXDRIVES-1)*(sizeof DrvEnt+CACHESIZE*sizeof DirEnt)-SECTORSIZE dup (0)
  945. endif
  946.  
  947. ;============================================================================
  948. ;  everything below this line is discarded after installing
  949. ;    the redirector
  950.  
  951. ;     MSC pacifier
  952.  
  953.      PUBLIC C  _acrtused
  954.  
  955. _acrtused     dw    1
  956.  
  957. ; Credits
  958.  
  959. CopyrightMsg   byte    cr, lf
  960.                byte    'SHSUCDX Version 1.0',cr,lf
  961.                byte    '(c)John H. McCoy, 1995, '
  962.                byte    'Sam Houston State University'
  963.                byte    cr,lf, '$'
  964.  
  965. DrivesAssigned byte    cr, lf
  966.                byte    "SHSUCDX Installed.",cr,lf
  967.                byte    "  Drives Assigned",cr,lf
  968.                byte    "Drive  Driver   Unit",cr,lf,'$'
  969.  
  970. UnInstalledMsg byte    cr, lf
  971.                byte    'SHSUCDX un-installed and memory freed.'
  972.                byte    cr, lf, '$'
  973.  
  974. CantUnInstallMsg  byte    cr, lf
  975.                   byte    'SHSUCDX can''t un-install.'
  976.                   byte    cr, lf, '$'
  977.  
  978. CantInstallMsg byte    '  SHSUCDX can''t install.',cr,lf,'$'
  979.  
  980. AlreadyInstalledMsg  byte   cr, lf, 'SHSUCDX or MSCDEX is already installed.$'
  981.  
  982. NoDrivesAvailMsg  byte  cr, lf,'Need More Drive Letters.$'
  983.  
  984. HighDriveMsg    byte    cr, lf,'Drive letter to high.$'
  985.  
  986. HighUnitMsg    byte    cr, lf,'Units specified don''t exist.$'
  987.  
  988. WrongDOSMsg    byte    cr, lf,'Must be DOS 3.3 - 7.xx.$'
  989.  
  990. CantFindCdMsg  byte    'Can''t open CD driver $'
  991.  
  992. NotEnoughMemMsg byte   'Not enough memory.  $'
  993.  
  994. CRLF           byte    cr, lf, '$'
  995.  
  996.  
  997. DrvrEnt        struct
  998.    Name        byte    'SHSU-CDN'
  999.                byte    0
  1000.    DrvrAddr    fptr    ?
  1001.    Drive       byte    0
  1002.    Unit        byte    0
  1003.    NoWanted    byte    0
  1004. DrvrEnt        ends
  1005.  
  1006. NoDrivers        byte    ?
  1007. Drivers          DrvrEnt  MAXDRIVES dup (<>)  ; one driver per drive is max
  1008. DriverIndex      byte     ?
  1009. FirstDeviceUnit  byte     ?
  1010. NoDeviceUnits    byte     ?
  1011. NoAvailUnits     byte     ?
  1012. NoUnitsWanted    byte     ?
  1013. LastDOSDrive     byte     ?      ; 1 base
  1014. KeepSize         word     ?
  1015.  
  1016. ArgumentNotFound EQU     2       ; Unrecognized argument
  1017. NoArgumentsFound EQU     1       ; No argument in command line
  1018. ArgumentFound    EQU     0       ; Ok argument in command line
  1019.  
  1020. UnInstallIt      equ     2
  1021. DontInstallIt    equ     1
  1022. InstallIt        equ     0
  1023.  
  1024. InstallFlag      byte    0
  1025. QuietFlag        word    0
  1026.  
  1027. DosVer           label    word
  1028.    osMajor       byte     ?
  1029.    osMinor       byte     ?
  1030. IoctlInBuf       byte     5 dup (0)      ; get devhdr addr
  1031. IoctlOutBuf      byte     2              ; reset CD
  1032.  
  1033. MsgOut    proc  near C public uses ax bx dx, msg:near ptr char
  1034.       mov      ah, 02h       ; display ch function
  1035.       mov      bx, msg
  1036.       mov      dl, ds:[bx]
  1037.       .while (dl != '$' && dl != 0)
  1038.          int      21h
  1039.          inc      bx
  1040.          mov      dl, ds:[bx]
  1041.       .endw
  1042.       ret
  1043. MsgOut    endp
  1044.  
  1045. DisplayDrives     proc near
  1046.       invoke MsgOut, addr DrivesAssigned
  1047.       sub      si, si
  1048.       sub      cx, cx
  1049.       .while (cl< cs:NoDrives)
  1050.          mov      ah, 02h       ; display ch function
  1051.          mov   dl, ' '
  1052.          int      21h
  1053.          int      21h
  1054.          mov   dl, byte ptr [si+Drive.Letter]
  1055.          int      21h
  1056.          mov   dl, ':'
  1057.          int      21h
  1058.          mov   dl, ' '
  1059.          int      21h
  1060.          int      21h
  1061.          int      21h
  1062.          sub   bx, bx
  1063.          .while (bx < 8)
  1064.             mov      dl, [si+bx+Drive.DriverName]
  1065.             int      21h        ; output driver name
  1066.             inc      bx
  1067.          .endw
  1068.          mov   dl, ' '
  1069.          int      21h
  1070.          int      21h
  1071.          mov   dl, byte ptr [si+Drive.Unit]
  1072.          .if (dl > 9)            ; output drive unit
  1073.             xor   dh,dh
  1074.             .while (dl > 9)
  1075.                inc  dh
  1076.                sub  dl, 10
  1077.             .endw
  1078.             xchg    dl, dh
  1079.             add     dl, 30h
  1080.             int     21h          ; tens digit
  1081.             xchg    dl, dh
  1082.           .endif
  1083.          add   dl, 30h
  1084.          int      21h            ; units digit
  1085.          add   si, sizeof DrvEnt
  1086.          inc   cl
  1087.          invoke MsgOut, addr CRLF
  1088.       .endw
  1089.             ret
  1090. DisplayDrives  endp
  1091.  
  1092. SetRoot     proc near uses es ax bx cx di si , CDSx: fptr
  1093.             les   bx, CDSx
  1094.             mov   ax, 0C080h              ; set physical network & redir bits
  1095.             mov   es:[bx].CDS.Flags, ax
  1096.        ; we don't set redirector address.  Should we???
  1097.             mov   es:[bx].CDS.RootOff,RootSlashOff ; root \ in curr_path
  1098.        ; Set to CDS to CD root form \\D.\U.
  1099.             mov   al, '\'
  1100.             mov   es:[bx].CDS.CurrPath, al
  1101.             mov   es:[bx+1].CDS.CurrPath, al
  1102.             mov   al, cs:DriveNo
  1103.             add   al, 'A'
  1104.             mov   es:[bx+2].CDS.CurrPath, al
  1105.             mov   al, '.'
  1106.             mov   es:[bx+3].CDS.CurrPath, al
  1107.             mov   al, '\'
  1108.             mov   es:[bx+4].CDS.CurrPath, al
  1109.             mov   al, cs:DeviceUnit
  1110.             add   al, 'A'
  1111.             mov   es:[bx+5].CDS.CurrPath, al
  1112.             mov   al, '.'
  1113.             mov   es:[bx+6].CDS.CurrPath, al
  1114.             mov   al, 0
  1115.             mov   es:[bx+7].CDS.CurrPath, al
  1116.             mov    ax, sizeof DrvEnt
  1117.             mul    DriveIndex
  1118.             mov    bx, ax
  1119.             mov    al, DriveNo
  1120.             mov    [bx+Drive.No], al
  1121.             add    al, 'A'
  1122.             mov    [bx+Drive.Letter], al
  1123.             mov    al, DeviceUnit
  1124.             mov    [bx+Drive.Unit], al
  1125.             mov    ax, word ptr DevHeader
  1126.             mov    word ptr [bx+Drive.DevHdrp], ax
  1127.             mov    ax, word ptr DevHeader+2
  1128.             mov    word ptr [bx+Drive.DevHdrp+2], ax
  1129.             mov    word ptr [bx+Drive.Strategyp+2], ax
  1130.             mov    word ptr [bx+Drive.Interruptp+2], ax
  1131.             mov    ax, word ptr DevStrategy
  1132.             mov    word ptr [bx+Drive.Strategyp], ax
  1133.             mov    ax, word ptr DevInterrupt
  1134.             mov    word ptr [bx+Drive.Interruptp], ax
  1135.             lea    di, [bx+Drive.DriverName]
  1136.             mov    ax, ds
  1137.             mov    es, ax
  1138.             lea    si, [si+Drivers.Name]
  1139.             mov    cx, 4
  1140.             cld
  1141.             rep movsw               ; ds:si->es:di
  1142.             ret
  1143. SetRoot     endp
  1144.  
  1145. InitDrive   proc near C uses ax bx di si es
  1146.    local    DrvEntryp:nptr
  1147.    local    DirEntryp:nptr
  1148.    local    Bufp:nptr
  1149.    local    Ticks:word
  1150.  
  1151.    ; fill in drive entry first
  1152.       mov   ax, 0040h             ; get clock ticks from 0040006Ch
  1153.       mov   es, ax
  1154.       mov   ax, es:[06Ch]
  1155.       mov   Ticks, ax
  1156.       push  ds
  1157.       pop   es
  1158.       mov   al, DriveIndex
  1159.       cbw
  1160.       mov   bx, SectorSize
  1161.       mul   bx
  1162.       add   ax, word ptr IODatap
  1163.       mov   Bufp, ax              ; addr of buffer for this drive
  1164.       mov   al, DriveIndex
  1165.       cbw
  1166.       mov   bx, CACHESIZE
  1167.       mul   bx
  1168.       mov   bx, sizeof DirEnt
  1169.       mul   bx
  1170.       add   ax, DirCachep         ; DirCache base ptr
  1171.       mov   DirEntryp, ax         ; addr of first cache entry for this drive
  1172.       mov   ax, sizeof DrvEnt     ; assume < 256
  1173.       mul   DriveIndex
  1174.       add   ax, offset Drive
  1175.       mov   bx, ax
  1176.       mov   ax, Bufp
  1177.       mov   [bx + DrvEnt.Bufp], ax
  1178.       mov   ax, UnknonwCDType
  1179.       mov   DrvEnt.Type[bx], ax
  1180.       mov   ax, 0FFFFh
  1181.       mov   word ptr DrvEnt.BufBlkNo[bx], ax
  1182.       mov   word ptr DrvEnt.BufBlkNo[bx+2], ax
  1183.       mov   ax, Ticks
  1184.       mov   DrvEnt.LastAccess[bx], ax
  1185.    ; link up cache for dir entries
  1186.       mov   ax, DirEntryp
  1187.       mov   DrvEnt.RootEnt.Forw[bx], ax
  1188.       mov   di, ax
  1189.       mov   si, ax
  1190.       add   si, sizeof DirEnt
  1191.       mov   DirEnt.Forw[di], si
  1192.       lea   ax, DrvEnt.RootEnt[bx]
  1193.       mov   DirEnt.Back[di], ax
  1194.       mov   cx, CACHESIZE
  1195.       dec   cx
  1196.       .while (cx > 0)
  1197.          mov   DirEnt.Back[si], di
  1198.          mov   di, si
  1199.          add   si, sizeof DirEnt
  1200.          mov   DirEnt.Forw[di], si
  1201.          dec   cx
  1202.       .endw
  1203.       lea   ax, DrvEnt.RootEnt[bx]
  1204.       mov   DirEnt.Forw[di], ax           ; last entry points forw to root
  1205.       mov   DrvEnt.RootEnt.Back[bx], di   ; root points back to last entry
  1206.  
  1207.       ret
  1208. InitDrive   endp
  1209.  
  1210. Init:  PUBLIC Init
  1211.  
  1212.    ;  set DS for addressing, move stack and save our psp address
  1213.       mov      ax, cs
  1214.       mov      ds, ax
  1215.       mov      ss, ax
  1216.       mov      sp, top_stack
  1217.       mov      DataSeg, ds
  1218.       mov      _PSP, es
  1219.  
  1220.    ;  get command line parameters
  1221.  
  1222.       invoke ParseCommandLine
  1223.  
  1224.       .if (InstallFlag == UnInstallIt)
  1225.          jmp   UnInstall
  1226.       .endif
  1227.  
  1228.    ;  see if we are already installed
  1229.       mov      ax, MSCDEX_Q
  1230.       push     ax
  1231.       mov      ah, REDIR
  1232.       mov      al, InstallChk
  1233.       int      2fh
  1234.       pop      bx
  1235.       .if al == 0ffh        ; redir installed, is it MSCDEX?
  1236.          .if bx == MSCDEX_R
  1237.             jmp      AlreadyInstalled
  1238.          .endif
  1239.       .endif
  1240.  
  1241.  
  1242.    ;  get DOS version
  1243.       mov      ah, 30h
  1244.       int      21h
  1245.       mov      DosVer, ax
  1246.  
  1247.    ;  get list of lists address thereby getting DOS seg
  1248.       mov      ah, 52h
  1249.       int      21h                        ; es:bx is LOLp
  1250.       mov      word ptr DTApp+2, es       ; set seg of DOS ptrs
  1251.       mov      word ptr PSPp+2, es
  1252.       mov      word ptr FN1p+2, es
  1253.       mov      word ptr SDBp+2, es
  1254.       mov      word ptr DosDp+2, es
  1255.       mov      word ptr SAttrp+2, es
  1256.       mov      word ptr SFTpp+2, es
  1257.       mov      ax, word ptr es:[bx].DOS_LOL.CDS
  1258.       mov      word ptr CDSBase, ax
  1259.       mov      ax, word ptr es:[bx].DOS_LOL.CDS+2
  1260.       mov      word ptr CDSBase+2, ax
  1261.  
  1262.    ;  set version specific DOS parameters
  1263.       .if osMajor ==3 && osMinor >= 30
  1264.          mov      CdsLen, 51h
  1265.          mov      si, 02ceh                    ; version 3.3+ offset
  1266.          mov      ax, si
  1267.          add      ax, 0ch
  1268.          mov      word ptr DTApp, ax
  1269.          mov      ax, si
  1270.          add      ax, 10h
  1271.          mov      word ptr PSPp, ax
  1272.          mov      ax, si
  1273.          add      ax, 92h
  1274.          mov      word ptr FN1p, ax
  1275.          mov      ax, si
  1276.          add      ax, 192h
  1277.          mov      word ptr SDBp, ax
  1278.          mov      ax, si
  1279.          add      ax, 1a7h
  1280.          mov      word ptr DosDp, ax
  1281.          mov      ax, si
  1282.          add      ax, 23ah
  1283.          mov      word ptr SAttrp, ax
  1284.          mov      ax, si
  1285.          add      ax, 268h
  1286.          mov      word ptr SFTpp, ax
  1287.       .elseif  osMajor >= 4 && osMajor <= 7     ; may need to check
  1288.                                                 ; DOS seg [4] ==+01
  1289.          mov      CdsLen, 58h
  1290.          mov      si, 0320h                    ; version 4, 5, 6, & 7 offset
  1291.          mov      ax, si
  1292.          add      ax, 0ch
  1293.          mov      word ptr DTApp, ax
  1294.          mov      ax, si
  1295.          add      ax, 10h
  1296.          mov      ax, si
  1297.          add      ax, 9eh
  1298.          mov      word ptr FN1p, ax
  1299.          mov      ax, si
  1300.          add      ax, 19eh
  1301.          mov      word ptr SDBp, ax
  1302.          mov      ax, si
  1303.          add      ax, 1b3h
  1304.          mov      word ptr DosDp, ax
  1305.          mov      ax, si
  1306.          add      ax, 24dh
  1307.          mov      word ptr SAttrp, ax
  1308.          mov      ax, si
  1309.          add      ax, 27eh
  1310.          mov      word ptr SFTpp, ax
  1311.       .else
  1312.          jmp      WrongDOS
  1313.       .endif
  1314.  
  1315.    ;  take advantage of the photo op
  1316.  
  1317.       invoke MsgOut, addr CopyrightMsg
  1318.  
  1319.    ; find last available drive letter
  1320.       mov      ch, es:[bx].DOS_LOL.LastDrive
  1321.       dec      ch                  ; lol use 1 for 'A'
  1322.       mov      LastDOSDrive, ch
  1323.  
  1324.     ; assign drives
  1325.       sub      ax, ax
  1326.       mov      DriveIndex, al
  1327.       mov      DriverIndex, al
  1328.       mov      DriveNo, al
  1329.       mov      ah, NoDrivers
  1330.       .while  (al < ah)                ; while DriverIndex < (NoDrivers)
  1331.       ; Open device driver and use IOCTL Input sub-command 0 to get
  1332.       ;   the device header address.
  1333.          mov      ax, sizeof DrvrEnt
  1334.          mul      DriverIndex
  1335.          add      ax, offset Drivers.Name
  1336.          mov      dx, ax
  1337.          sub      al, al                     ; read only
  1338.          mov      ah, 3Dh
  1339.          int      21h
  1340.          jnc      @f                         ; error when carry set
  1341.          jmp      CantFindCd
  1342.       @@:mov      bx, ax                     ; move handle to bx
  1343.          mov      ax, 4402h                  ; IOCTL input-get devhdr addr
  1344.          mov      cx, 5                      ; dta has cmd code plus a fptr
  1345.          lea      dx, IoctlInBuf             ;   to device header
  1346.          int      21h
  1347. ;        mov      ax, 4403h                  ; IOCTL output-reset CD
  1348. ;        mov      cx, 1                      ; dta has cmd code only (1 byte)
  1349. ;        lea      dx, IoctlOutBuf            ;   to device header
  1350. ;        int      21h
  1351.          mov      ah, 3eh                    ; close file handle in bx
  1352.          int      21h
  1353.          jnc      @f                         ; error when carry set
  1354.          jmp      CantFindCd
  1355.       @@:
  1356.          les      bx, dword ptr IoctlInBuf+1
  1357.          mov      word ptr DevHeader, bx
  1358.          mov      word ptr DevHeader+2, es
  1359.          mov      ax, es:[bx+6]
  1360.          mov      word ptr DevStrategy, ax
  1361.          mov      word ptr DevStrategy+2, es
  1362.          mov      ax, es:[bx+8]
  1363.          mov      word ptr DevInterrupt, ax
  1364.          mov      word ptr DevInterrupt+2, es
  1365.          mov      ax, sizeof DrvrEnt
  1366.          mul      DriverIndex
  1367.          mov      si, ax
  1368.          mov      al, [si + Drivers.Unit]       ; first unit wanted
  1369.          mov      ah, es:[bx+21]
  1370.          mov      NoDeviceUnits, ah
  1371.         .if  (ah <= al)
  1372.              invoke MsgOut, addr HighUnitMsg
  1373.              jmp InitErrorExit
  1374.          .endif
  1375.          sub      ah, al
  1376.          mov      NoAvailUnits, ah
  1377.          mov      DeviceUnit, al
  1378.          mov      al, [si + Drivers.NoWanted]   ; units asked for
  1379.          .if (al == 0) || ( al > NoAvailUnits)
  1380.             mov   al, NoAvailUnits
  1381.          .endif
  1382.          mov      NoUnitsWanted, al
  1383.          mov      cl,[si + Drivers.Drive]
  1384.          .if  (cl < DriveNo)
  1385.              mov  cl, DriveNo
  1386.          .endif
  1387.          mov      DriveNo, cl
  1388.          mov      ah, DriveIndex
  1389.      ; ah is DriveIndex   al is NoUnitsWanted
  1390.          .while (ah < MAXDRIVES) && (al > 0)
  1391.             mov      cl, DriveNo
  1392.             call     FindAvailDrive       ; also sets CDSp for us
  1393.             .if  (cl != ch)
  1394.                invoke MsgOut, addr NoDrivesAvailMsg
  1395.                jmp  InitErrorExit
  1396.             .endif
  1397.             mov      DriveNo, cl
  1398.             invoke SetRoot,CDSp           ; uses CDSp, DevHeader, DriveIndex,
  1399.                                           ; DriveNo and DeviceUnit
  1400.             inc     DriveIndex
  1401.             inc     DriveNo
  1402.             inc     DeviceUnit
  1403.             dec     NoUnitsWanted
  1404.             mov      ah, DriveIndex
  1405.             mov      al, NoUnitsWanted
  1406.          .endw
  1407.          inc      DriverIndex
  1408.          mov      al, DriverIndex
  1409.          mov      ah, NoDrivers
  1410.       .endw
  1411.  
  1412.       mov   al, DriveIndex
  1413.       .if (al == 0)                ; then no drives were assigned
  1414.          invoke MsgOut, addr HighDriveMsg
  1415.          jmp   InitErrorexit
  1416.       .endif
  1417.       mov      NoDrives, al
  1418.  
  1419.   ;  relocate buffers and init drive structs
  1420.       mov      cx, sizeof DrvEnt      ; find Drive Table space needed
  1421.       mov      al, NoDrives
  1422.       cbw
  1423.       mul      cx
  1424.       add      ax, offset Drive
  1425.       mov      DirCachep, ax          ; relocate dir cache
  1426.       mov      cx, CACHESIZE          ; find cache space needed
  1427.       mov      al, NoDrives
  1428.       cbw
  1429.       mul      cx
  1430.       mov      cx, ax
  1431.       mov      ax, sizeof DirEnt
  1432.       mul      cx
  1433.       add      ax, DirCachep
  1434.       mov      IODatap, ax            ; relocate IOData buffers
  1435.       mov      cx, SECTORSIZE         ; find buffer space needed
  1436.       mov      al, NoDrives
  1437.       cbw
  1438.       mul      cx
  1439.       add      ax, IODatap            ; last byte to keep now in ax
  1440.       add      ax, 10Fh               ; add 100h for psp and roundup
  1441.       mov      cl, 4
  1442.       shr      ax, cl                 ; program paragraphs to keep
  1443.       mov      KeepSize, ax
  1444.  
  1445.   ;  get more space if we need it for buffers
  1446.       mov     bx, seg EndOfCDX
  1447.       sub     bx, _PSP
  1448.       .if     ax > bx
  1449.           mov  bx, ax
  1450.           mov  es, _PSP
  1451.           mov  ah, 04Ah       ; modify block size
  1452.           int  21h
  1453.           jnc  @F
  1454.              invoke MsgOut, addr NotEnoughMemMsg
  1455.              jmp InitErrorExit
  1456.        @@:
  1457.        .endif
  1458.  
  1459.   ;  initialize drive table and link up dir cache
  1460.  
  1461.       xor      al, al
  1462.       mov      ah, NoDrives
  1463.       mov      DriveIndex, 0
  1464.       .while   al < ah
  1465.          invoke InitDrive
  1466.          inc DriveIndex
  1467.          inc al
  1468.       .endw
  1469.  
  1470.  
  1471.   ;  Display drive assignments
  1472.  
  1473.       mov      al, Drive.No[0]
  1474.       mov      FirstDriveNo, al
  1475.       invoke DisplayDrives
  1476.  
  1477.   ;  capture 2F vector
  1478.  
  1479.       mov      ax, 352Fh
  1480.       int      21h
  1481.       mov      word ptr Old2F ,bx
  1482.       mov      word ptr Old2F[2],es
  1483.       mov      dx, offset New2F
  1484.       mov      ax, 252Fh
  1485.       int      21h
  1486.  
  1487.  ;  release excess space and tsr
  1488.  
  1489.       mov      es, _PSP
  1490.       sub      ax, ax                 ;
  1491.       xchg     ax, es:[2Ch]           ; zap evironment pointer in psp
  1492.       or       ax, ax                 ; ax := environment ptr
  1493.       jz       @f                     ; no environment if zero
  1494.       mov      es, ax
  1495.       mov      ah, 49h
  1496.       int      21h                    ; and release environment
  1497.   @@:
  1498.       mov      dx, KeepSize
  1499.       mov      ax,3100h
  1500.       int      21h                    ; go TSR
  1501.       mov      ah, 4ch                ; emergency exit in case we don't TSR
  1502.       int      21h
  1503.  
  1504. UnInstall:
  1505.       push  bp
  1506.       mov   _SS, ss
  1507.       mov   _SP, sp
  1508.       mov   bx, MSCDEX_Q
  1509.       mov   cx, _PSP                ; our  psp
  1510.       mov   dx, offset UnInstalled  ; uses ds:dx for successful return
  1511.       mov   ah, REDIR
  1512.       mov   al, UnInstallCmd
  1513.       int   2fh
  1514.       jmp   CantUnInstall           ; comes back here if unsuccessful
  1515. UnInstalled:                        ; or, here if successful
  1516.       push   cs
  1517.       pop    ds
  1518.       mov   ss, _SS
  1519.       mov   sp, _SP
  1520.       pop   bp
  1521.       invoke MsgOut, addr UnInstalledMsg
  1522.       jmp   Initexit
  1523.  
  1524. CantUnInstall:
  1525.       pop    bp
  1526.       push   cs
  1527.       pop    ds
  1528.       invoke MsgOut, addr CantUnInstallMsg
  1529.       jmp   Initexit
  1530.  
  1531. AlreadyInstalled:
  1532.       invoke MsgOut, addr AlreadyInstalledMsg
  1533.       invoke MsgOut, addr CantInstallMsg
  1534.       jmp   Initexit
  1535.  
  1536. CantFindCd:
  1537.       invoke MsgOut, addr CantFindCdMsg
  1538.       mov      ax, sizeof DrvrEnt
  1539.       mul      DriverIndex
  1540.       add      ax, offset Drivers.Name
  1541.       invoke MsgOut, ax
  1542.       invoke MsgOut, addr CRLF
  1543.       jmp   InitErrorExit
  1544.  
  1545. WrongDOS:
  1546.       invoke MsgOut, addr WrongDOSMsg
  1547.       invoke MsgOut, addr CantInstallMsg
  1548.       jmp   InitExit
  1549.  
  1550. InitErrorExit:
  1551.    .if DriveIndex > 0
  1552.       mov      al, DriveIndex
  1553.       mov      NoDrives, al
  1554.       invoke ClrRoot
  1555.    .endif
  1556.    invoke MsgOut, addr CantInstallMsg
  1557.    jmp   InitExit
  1558.  
  1559. InitExit:
  1560.    mov   ah,4ch                     ; normal terminate
  1561.    int   21h
  1562.  
  1563. FindAvailDrive         proc near uses es bx
  1564.  
  1565. ;  Finds first available drive letter.
  1566. ;
  1567. ;  Entry     cl     Starting letter for search
  1568. ;
  1569. ;  Exit      cl == ch   Drive in cl is available
  1570. ;            cl != ch   No drive available starting at specified drive
  1571.  
  1572.  
  1573.       mov      al, cl
  1574.       cbw
  1575.       mul      CDSLen
  1576.       les      bx, CDSBase
  1577.       add      bx, ax             ; es:bx FirstDriveNo CDSp
  1578.       xor      ch, ch
  1579.       .while  (cl <= LastDOSDrive)
  1580.          test     es:[bx].CDS.Flags, 0C000h ; drive in use ?
  1581.          jnz      @F
  1582.          mov      ch, cl
  1583.          mov      word ptr CDSp, bx          ; set CDSp
  1584.          mov      word ptr CDSp+2, es
  1585.          .break
  1586.    @@:   inc      cl
  1587.          add      bx, CDSLen
  1588.       .endw
  1589.       ret
  1590.  
  1591. FindAvailDrive         endp
  1592.  
  1593. ParseCommandLine       proc near  uses es
  1594.    ;* If driver is loaded from config.sys using device=drivername parms
  1595.    ;* then rhINIT points to the first character following the drivername
  1596.    ;* and a CR follows the last parm.  When loaded by executing, the
  1597.    ;* command line is available in the PSP.(len +80h, 1st ch +81h, no CR)
  1598.  
  1599.       sub      ch, ch
  1600.       mov      di, 80h            ; command line length psp +80h
  1601.       mov      cl, es:[di]
  1602.       mov      al, 'U'            ; /U unInstall driver
  1603.       call     GetParm
  1604.       .if ax == ArgumentFound
  1605.           mov      InstallFlag, UnInstallIt
  1606.           jmp      ParseExit
  1607.       .endif
  1608.  
  1609.       mov      di, 80h                ; command line length at psp +80h
  1610.       sub      ch, ch
  1611.       mov      cl, es:[di]
  1612.       mov      DriverIndex, 0
  1613.       mov      ax, ArgumentFound
  1614.    .while  ax == ArgumentFound
  1615.       mov      al, 'D'            ; /D:drivername
  1616.       call     FindParm
  1617.      .if   ax == ArgumentFound
  1618.          mov      ax, sizeof DrvrEnt
  1619.          mul      DriverIndex
  1620.          mov      si, ax
  1621.          mov      dx, 8
  1622.          call     MoveName
  1623.          mov al, es:[di]
  1624.          .if   cx == 0
  1625.              jmp     @F
  1626.          .endif
  1627.          .while (al != ',' && al != ' ')
  1628.             inc    di
  1629.             mov al, es:[di]
  1630.             dec    cx
  1631.             .if   cx == 0  || al == '/'
  1632.                 jmp     @F
  1633.             .endif
  1634.          .endw
  1635.          dec    cx
  1636.          .if   cx == 0
  1637.              jmp     @F
  1638.          .endif
  1639.          inc    di
  1640.          mov al, es:[di]
  1641.          .if al == ' '
  1642.              jmp @F
  1643.          .elseif al != ','                         ; check first driveno
  1644.             .if (al >= 'a' && al <= 'z')
  1645.                 and    al, 11011111y           ; upper case it
  1646.             .endif
  1647.             .if (al >= 'A' && al <= 'Z')
  1648.                 sub    al, 'A'
  1649.             .else
  1650.                 jmp @F
  1651.             .endif
  1652.             mov  [si+Drivers.Drive], al
  1653.             dec     cx
  1654.             jcxz    @F
  1655.             inc    di
  1656.             mov al, es:[di]
  1657.             .if al != ','
  1658.                jmp @F
  1659.             .endif
  1660.          .endif
  1661.          dec    cx
  1662.          jcxz     @F
  1663.          inc    di
  1664.          mov al, es:[di]
  1665.          .if al != ','                              ; check firstdeviceunit
  1666.             .if (al >= '0' && al <= '9')
  1667.                 sub    al, '0'
  1668.                 mov ah, es:[di+1]                   ; is it two digits?
  1669.                .if (ah >= '0' && ah <= '9')         ; ignore if not a digit
  1670.                    shl    al,1                      ; mul 1st digit by 10
  1671.                    mov    ah, al
  1672.                    shl    al,1
  1673.                    shl    al,1
  1674.                    add    al, ah
  1675.                    mov    ah, es:[di+1]             ; and add in 2nd digit
  1676.                    sub    ah, '0'
  1677.                    add    al, ah
  1678.                    dec    cx
  1679.                    inc    di
  1680.                .endif
  1681.                 mov    [si+Drivers.Unit], al
  1682.             .else
  1683.                 jmp @F
  1684.             .endif
  1685.             dec     cx
  1686.             jcxz     @F
  1687.             inc    di
  1688.          .endif
  1689.          dec     cx
  1690.          jcxz     @F
  1691.          inc    di
  1692.          mov al, es:[di]
  1693.          .if al != ','
  1694.             .if (al >= '0' && al <= '9')
  1695.                 sub    al, '0'
  1696.                 mov    [si+Drivers.NoWanted], al
  1697.             .else
  1698.                 jmp @F
  1699.             .endif
  1700.          .endif
  1701.    @@:  inc    DriverIndex
  1702.         mov    ax, ArgumentFound
  1703.         dec    di                    ; find parm needs ptr -1
  1704.      .endif
  1705.    .endw
  1706.     mov   al, DriverIndex
  1707.     .if  al ==0
  1708.        inc al
  1709.     .endif
  1710.     mov   NoDrivers, al
  1711. ParseExit:
  1712.     ret
  1713.  
  1714. ParseCommandLine       endp
  1715.  
  1716. MoveName proc near
  1717.       sub   bx, bx                                 ; es:di points to 1st char
  1718.       .repeat                                      ; cx chars left on cmd line
  1719.           mov al, es:[di]                          ; dx is length of name field
  1720.           .if ((al == ',') || (cx == 0) || (al==' ') || (al == '/'))
  1721.               mov    byte ptr [si+bx+Drivers.Name], ' '
  1722.           .else
  1723.              .if (al >= 'a' && al <= 'z')
  1724.                  and    al, 11011111y           ; upper case it
  1725.               .endif
  1726.               mov    byte ptr [si+bx+Drivers.Name], al
  1727.               inc    di
  1728.               dec    cx
  1729.           .endif
  1730.           inc    bx
  1731.       .until bx == dx
  1732.       ret
  1733. MoveName endp
  1734.  
  1735. FindParm proc near
  1736.  
  1737.    ; al      parm code we are to find       /X: or -X:
  1738.    ; es:di   first char on command line -1
  1739.    ; cx      number of characters left on command line
  1740.  
  1741.  GetNext:                             ; this code allows us to handle names
  1742.       call     GetParm                ; like   -C:NET-CD
  1743.       cmp      ax, ArgumentFound
  1744.       jne      NotFound
  1745.       inc      di                     ; found /X or -X, is next char a ':' ?
  1746.       dec      cl
  1747.       mov      al, es:[di]
  1748.       cmp      al, ':'
  1749.       je       FoundIt
  1750.       loop     GetNext
  1751.       mov      ax, ArgumentNotFound
  1752.       ret
  1753.  
  1754.   FoundIt:
  1755.       inc   di                           ; /X:name  make di point @ name
  1756.       dec   cl
  1757.       mov   ax, ArgumentFound
  1758.   NotFound:
  1759.    ret
  1760.  
  1761. FindParm endp
  1762.  
  1763. ;* GetParm - Scans command line for argument of form /X or -X  where
  1764. ;* X = specified ASCII character. Presumes that argument is preceded
  1765. ;* by a '/' or a '-'. Comparisons are case insensitive.
  1766. ;*
  1767. ;* Params: ES:DI = Address of CommandLine -1
  1768. ;*         AL    = Paramater character to scan for
  1769. ;*         CX    = command line length
  1770. ;*
  1771. ;* Return: AX    = One of the following codes:
  1772. ;*                 NoArgumentsFound  if empty command line
  1773. ;*                 ArgumentFound  if argument found
  1774. ;*                 ArgumentNotFound if argument not as specified
  1775. ;*         ES:DI = Pointer to found argument
  1776. ;*         CX    = chars left on command line including arg or 0
  1777.  
  1778. GetParm PROC NEAR
  1779.  
  1780.         mov     ah, NoArgumentsFound    ; assume no /X style arguments
  1781.         jcxz    exit
  1782.         .if (al >= 'a' && al <= 'z')
  1783.             and    al, 11011111y           ; Make character upper case
  1784.         .endif
  1785.  
  1786. ; Find start of argument
  1787.  
  1788. loop1:
  1789.         inc     di                      ;
  1790.         mov     dl, es:[di]             ; Get character from argument list
  1791.         cmp     dl, '/'                 ; Find option prefix '/'
  1792.         je      analyze
  1793.         cmp     dl, '-'                 ;   or option prefix '-'
  1794.         je      analyze
  1795.  
  1796.         loop    loop1
  1797.  
  1798.         jmp     exit
  1799.  
  1800. ; '/' or '-' prefix found. Compare command-line character
  1801. ; with character specified in AL.
  1802. analyze:
  1803.         inc     di
  1804.         dec     cl
  1805.         jcxz    exit
  1806.         mov     ah, ArgumentFound         ; Assume argument is okay
  1807.         mov     dl, es:[di]
  1808.         .if (dl >= 'a' && dl <= 'z')
  1809.             and    dl, 11011111y           ; Make character upper case
  1810.         .endif
  1811.         cmp     dl, al
  1812.         je      exit                    ; specified char
  1813.         mov     ah, ArgumentNotFound    ; Else signal bad argument,
  1814.         loop    loop1             ;   continue scan
  1815.  
  1816. exit:
  1817.         mov     al, ah
  1818.         cbw                             ; AX = return code
  1819.         ret
  1820.  
  1821. GetParm ENDP
  1822.  
  1823. _INIT        ends
  1824.  
  1825.             end   Init
  1826.  
  1827.