home *** CD-ROM | disk | FTP | other *** search
/ Current Shareware 1994 January / SHAR194.ISO / dos_util / v12n19.zip / DRVASM.ZIP / DRVMAIN.ASM < prev    next >
Assembly Source File  |  1993-10-11  |  39KB  |  865 lines

  1. ;---------------------------------------------------------------    
  2. ;drvmain.asm - Main module for DRVLOAD Utility                 |
  3. ;--------------------------------------------------------------|
  4. ;DRVLOAD Copyright (c) 1993                                    |
  5. ;                                                              |
  6. ;Rick Knoblaugh All rights reserved.                           |
  7. ;First Appeared in PC MAGAZINE, US Edition,                    |
  8. ;November 9, 1993                                              |
  9. ;--------------------------------------------------------------|
  10. ; 7/04/93                      Rick Knoblaugh                  |
  11. ;--------------------------------------------------------------|
  12. ;include files                                                 |
  13. ;---------------------------------------------------------------    
  14.                 include drvequ.inc
  15.                 include drvstruc.inc
  16.  
  17. code            segment public  'CODE'
  18.                 assume cs:code, ds:code, es:code
  19.  
  20. ;--------------------------------------------------------------
  21. ;externals                                                   |
  22. ;--------------------------------------------------------------
  23.  
  24.                 extrn   get_driver:near, relocate:near
  25.                 extrn   get_parms:near, check_driver:near
  26.                 extrn   look_win:near
  27. ;--------------------------------------------------------------
  28. ;publics                                                     |
  29. ;--------------------------------------------------------------
  30.                 public  driver_file, dos_ver, driver_p_off
  31.                 public  drv_stack_ptr
  32.  
  33.                 org     100h                    
  34. startup:
  35.                 jmp     start_util
  36.     
  37. ;--------------------------------------------------------------
  38. ;Variables                                                    |
  39. ;--------------------------------------------------------------
  40.                 include drvdata.inc
  41.  
  42. ;--------------------------------------------------------------
  43.  
  44.  
  45. start_util      proc    near
  46.                 cld                             ;all strings forward
  47.                 mov     ah, DOS_GET_VER         ;check unlikely event
  48.                 int     21h                     ;that user has really
  49.                 mov     dx, offset invalid_dos_msg
  50.  
  51. ;
  52. ;DRVLOAD requires DOS version 3.0 or better becuase some of the    
  53. ;DOS data structures used by the program (e.g. the CDS),
  54. ;didn't exist prior that version (some of this 
  55. ;information could be extracted from other areas, but since 
  56. ;undocumented DOS is used, it might not be 100% reliable). 
  57.  
  58.  
  59.                 cmp     al, 3                   ;need DOS 3.0 or better
  60.                 jnb     start_u050
  61. start_u040:
  62.                 jmp     start_u900
  63. start_u050:
  64.                 mov     cs:dos_ver, ax
  65.                 push    cs
  66.                 pop     word ptr cs:psp_seg
  67.  
  68.                 call    look_win                ;look for Windows
  69.                 mov     dx, offset no_win_msg 
  70.                 jc      start_u040              ;if so, can't run
  71.  
  72.                 xor     si, si                  ;offset zero from psp
  73.                 sub     ch, ch
  74.                 mov     bp, [si].psp_nextpar    ;hold end of mem
  75.                 mov     cl, [si].psp_cmd_len
  76.                 lea     si, [si].psp_cmd_tail
  77.                 call    get_driver              ;retrieve driver name
  78.                 mov     dx, offset nodriver_msg
  79.                 jc      start_u070              ;if no driver
  80.                 call    get_parms               ;retrieve driver parms
  81.  
  82.  
  83.                 call    check_driver            ;get size and ensure valid
  84.                 jnc     start_u100              ;continue if found
  85.  
  86.                 mov     dx, offset cant_open_msg 
  87. start_u070:
  88.                 jmp     start_u900              ;if can't open
  89.  
  90. ;
  91. ;size returned in dx:ax
  92. ;
  93. start_u100:
  94.                 mov     driver_size, ax         ;save driver size
  95.                 mov     si, ds                  ;psp segment
  96.                 mov     di, offset drv_stack_ptr ;end of loader
  97.                 add     di, 0fh                 ;round up to nearest para
  98.                 mov     cl, 4
  99.                 shr     di, cl                  ;size in paragraphs
  100. ;
  101. ;Calculate segment to which loader will be relocated.  This is the
  102. ;maximum ceiling to which driver could climb.
  103. ;
  104. ;
  105.                 sub     bp, di                  ;new "top of mem"   
  106.                 mov     bx, bp
  107.                 sub     bx, si                  ;max available paragraphs
  108.                 sub     bx, 10h                 ;count psp itself
  109.  
  110. ;bp has segment to which loader will be relocated.
  111. ;bx has maximum paragraphs available for driver being loaded.  Next,
  112. ;test to determine if driver will fit.  Driver should always fit.  Just
  113. ;about the only case where it possibly couldn't, is if DRVLOAD is
  114. ;being executed under LOADHIGH and there is insufficient upper memory.
  115.  
  116.  
  117.                 add     ax, 0fh                 ;round up
  118.                 adc     dx, 0
  119.  
  120.                 mov     si, dx                  ;save high size
  121.                 shr     dx, cl                  ;convert to paragraphs
  122.                 shr     ax, cl
  123.                 mov     cl, 0ch
  124.                 shl     si, cl                  
  125.                 or      ax, si
  126.  
  127.  
  128.                 cmp     bx, ax                  ;compare with max
  129.                 jae     start_u200              ;if it's >, can't
  130.                 mov     dx, offset too_big_msg
  131.                 jmp     start_u900       ;driver >= 1 Meg
  132.  
  133. start_u200:
  134.                 call    relocate                ;move loader up
  135. start_u220:
  136.                 call    load_driver
  137.                 mov     dx, offset err_load_msg
  138.                 jc      start_u900              ;if error loading driver
  139.  
  140. ;
  141. ;Do a little sanity checking on driver header.  At least ensure that
  142. ;the strategy and interrupt routines offsets are realistic values. 
  143. ;
  144.                 xor     bx, bx
  145.                 mov     ax, driver_size
  146.                 cmp     es:[bx].dev_stratr, size dev_header 
  147.                 jb      start_u230             ;offset must be past header
  148.                 cmp     es:[bx].dev_stratr, ax ;and < total driver size
  149.                 jb      start_u240
  150.                 
  151. start_u230:
  152.                 mov     dx, offset bad_drv_msg
  153.                 jmp     short start_u900       ;if driver not right
  154. start_u240:
  155.                 cmp     es:[bx].dev_intr, size dev_header
  156.                 jbe     start_u230
  157.                 cmp     es:[bx].dev_intr, ax
  158.                 jae     start_u230
  159.  
  160.                 mov     dx, offset sign_on_msg  ;say hello to user
  161.                 mov     ah, DOS_PRT_STRING       
  162.                 int     21h
  163.  
  164.                 push    es                      ;save ptr to header
  165.                 push    bx
  166.                 call    get_list_ptr
  167.                 call    get_num_blk             ;get # blk devices
  168.                 mov     num_blk_devices, al     ;save
  169.                 pop     bx
  170.                 pop     es
  171.                 test    es:[bx].dev_attrib, (mask char_dev) ;char drv?
  172.                 jnz     start_u300
  173.                 mov     dx, offset no_drives_msg
  174.                 jcxz    start_u900              ;exit if no drives
  175.  
  176. start_u300:
  177.                 call    do_drv_init             ;do driver INIT cmd
  178.                 mov     dx, offset init_fail_msg
  179.                 jnz     start_u900              ;exit if INIT failed
  180.                 mov     mem_to_keep, ax         ;save # mem paragraphs
  181. ;
  182. ;Also, if driver returned with no error, but is keeping no memory,
  183. ;treat as initialization failure (very few drivers should ever
  184. ;do this).
  185. ;
  186.                 or      ax, ax                  
  187.                 jz      start_u900
  188.  
  189. ;
  190. ;Next, if driver is a character device, simply add it to the driver
  191. ;chain and go TSR.  For block devices, must create DPB and CDS entries
  192. ;first.
  193. ;
  194. ;
  195.                 test    es:[bx].dev_attrib, (mask char_dev)
  196.                 jnz     start_u800              ;if so, go add it
  197.  
  198. start_u500:
  199.  
  200. ;
  201. ;Before generating DPBs for the block device, inspect the BPB returned
  202. ;by the driver and at least see it makes some sense.  If a bad BPB is
  203. ;passed to the undocumented function used to translate from BPB to
  204. ;DPB, the system may hang.  For example, when RAMDRIVE.SYS is
  205. ;executed with unrealistic parameters (e.g. RAMDRIVE.SYS 32767 128), 
  206. ;it won't return an error from the INIT call, and the BPB returned
  207. ;has zero for the total number of sectors and sectors per cluster.  If
  208. ;anything like this is seen, fail the install (DOS itself would).
  209. ;
  210. ;
  211.                 call    check_bpb               ;return nz if ok
  212.                 mov     dx, offset init_fail_msg 
  213.                 jz      start_u900
  214.  
  215.                 call    make_drives             ;create DPBs, CDSs
  216. ;
  217. ;The previous call should always succeed.  The only way it would return
  218. ;with carry set is if there is something very wrong with DOS (i.e. DPB
  219. ;chain corrupted, etc.)
  220. ;
  221.                 jnc     start_u800
  222.                 mov     dx, offset corrupt_msg   
  223.                 mov     ah, DOS_PRT_STRING       
  224.                 int     21h
  225.                 jmp     short start_u880
  226.  
  227. start_u800:
  228.                 call    add_to_chain
  229.                 call    go_tsr                  ;should never return   
  230. start_u880:
  231.                 mov     dx, offset warning_msg
  232. start_u900:
  233.                 push    cs
  234.                 pop     ds                      ;get data segment
  235. start_u950:
  236.                 mov     ah, DOS_PRT_STRING       
  237.                 int     21h
  238. start_u999:
  239.                 mov     ax, (DOS_TERM SHL 8)    ;terminate
  240.                 int     21h
  241.                 ret
  242. start_util      endp        
  243.  
  244. ;--------------------------------------------------------------
  245. ;load_driver - Use DOS load overlay function to load device   |
  246. ;              driver just after PSP.                         |
  247. ;                                                             |
  248. ;             Enter:                                          |
  249. ;                 psp_seg=original PSP for loader             |
  250. ;             driver_file=file spec for driver file           |
  251. ;               over_args=structure to pass for overlay call  |
  252. ;                                                             |
  253. ;                  es, ds=local data segment                  |
  254. ;                                                             |
  255. ;                                                             |
  256. ;             Exit:                                           |
  257. ;                      es=seg where loaded                    |
  258. ;                      CY=set if error loading (ax=error)     |
  259. ;                                                             |
  260. ;--------------------------------------------------------------
  261. load_driver     proc    near
  262.                 mov     dx, offset driver_file
  263.                 mov     ax, psp_seg
  264.                 add     ax, 10h                         ;load past PSP
  265.                 push    ax                              ;save load seg
  266.                 mov     over_args.over_seg, ax
  267.                 mov     over_args.over_relo_fac, ax
  268.                 mov     bx, offset over_args
  269.                 mov     ax, DOS_LOAD_OVER 
  270.                 int     21h
  271.                 pop     es                              ;ret load seg
  272.                 ret
  273. load_driver     endp
  274.  
  275. ;--------------------------------------------------------------
  276. ;get_list_ptr - Use Undocumented DOS funcntion 52h to         |
  277. ;               retrieve "list of lists" ptr.  Based on DOS   |
  278. ;               version, load variables with pointers to      |
  279. ;               DPB, CDS, and NUL driver header.              |
  280. ;                                                             |
  281. ;             Enter:                                          |
  282. ;                      ds=local data segment                  |
  283. ;                 dos_ver=DOS version from DOS version check  |
  284. ;             Exit:                                           |
  285. ;                   es:bx=list of lists ptr                   |
  286. ;                 cds_ptr=ptr to first CDS entry              |
  287. ;                cds_size=size of each CDS entry              |
  288. ;                 dpb_ptr=ptr to first DPB entry              |
  289. ;                dpb_size=size of each DPB entry              |
  290. ;             dpb_drv_off=offset of address of device driver  |
  291. ;                         in DPB entry                        |
  292. ;                                                             |
  293. ;            dpb_link_off=offset of link go next DPB          |
  294. ;                         in DPB entry                        |
  295. ;                                                             |
  296. ;             num_blk_off=offset in list of lists for number  |
  297. ;                         of block devices.                   |
  298. ;                                                             |
  299. ;--------------------------------------------------------------
  300. get_list_ptr    proc    near
  301.                 mov     ah, DOS_LIST_LISTS 
  302.                 int     21h                     ;get list of lists
  303.                 mov     cds_size, (size cds_4_0)
  304.                 mov     dpb_size, (size dpb_format_4)
  305.                 mov     di, offset dos31_ver_off ;default to 3.1 >
  306.                 mov     dpb_link_off, dpb_ptr_nxt_4 
  307.                 mov     dpb_drv_off, dpb_driver_4  
  308.                 mov     num_blk_off, num_blk31  
  309.                 cmp     byte ptr dos_ver, 4   ;maj ver 4 >?
  310.                 jae     get_list100
  311.                 mov     cds_size, (size cds_3_0)
  312.                 mov     dpb_size, (size dpb_format_3)
  313.  
  314.                 mov     dpb_link_off, dpb_ptr_nxt_3 ;DOS 3 dpb ptr
  315.                 mov     dpb_drv_off, dpb_driver_3
  316.  
  317.                 cmp     dos_ver, 300h           ;is it 3.0?
  318.                 jne     get_list100
  319.                 mov     di, offset dos30_ver_off ;offsets for 3.0
  320.                 mov     num_blk_off, num_blk30  
  321. get_list100:
  322.                 mov     si, [di].vcds_ptr       ;offset to CDS ptr
  323.                 mov     ax, es:[bx + si].d_segment ;get seg CDS ptr
  324.                 mov     dx, es:[bx + si].d_offset  ;get offset CDS ptr
  325.                 mov     cds_ptr.d_segment, ax
  326.                 mov     cds_ptr.d_offset, dx
  327.                 
  328.                 mov     si, [di].vdpb_ptr       ;offset to DPB ptr
  329.                 mov     ax, es:[bx + si].d_segment ;get seg DPB ptr
  330.                 mov     dx, es:[bx + si].d_offset  ;get offset DPB ptr
  331.                 mov     dpb_ptr.d_segment, ax
  332.                 mov     dpb_ptr.d_offset, dx
  333.  
  334.                 mov     si, [di].vnul_dev_ptr   ;offset to NUL ptr
  335.                 mov     ax, es                  ;get seg NUL dev
  336.                 mov     nul_dev_ptr.d_segment, ax
  337.                 add     si, bx
  338.                 mov     nul_dev_ptr.d_offset, si ;offset of NUL dev
  339.  
  340.                 mov     si, [di].vlast_drive    ;offset to LASTDRIVE
  341.                 mov     al, es:[bx + si]        ;get LASTDRIVE  
  342.                 mov     last_drive, al
  343.  
  344. get_list999:
  345.                 ret
  346. get_list_ptr    endp
  347.  
  348. ;--------------------------------------------------------------
  349. ;get_num_blk - Using lists of lists ptr.  Use ptr to Current  |
  350. ;              Directory Structures, CDSs, to determine the   |
  351. ;              next available driver letter.                  |
  352. ;                                                             |
  353. ;             Enter:                                          |
  354. ;                      ds=local data segment                  |
  355. ;                 cds_ptr=ptr to first CDS entry              |
  356. ;                cds_size=size of each CDS entry              |
  357. ;              last_drive=max number of CDS entries           |
  358. ;                                                             |
  359. ;             Exit:                                           |
  360. ;                      al=unit number                         |
  361. ;                      cx=0 if no drives available            |
  362. ;                 cds_ptr=ptr to first available CDS entry    |
  363. ;         max_avail_units=maximum units that can be added     |
  364. ;                         given the number of free CDSs       |
  365. ;                                                             |
  366. ;  es, bx destroyed                                           |
  367. ;--------------------------------------------------------------
  368. get_num_blk     proc    near
  369.                 les     bx, cds_ptr
  370.                 sub     ch, ch
  371.                 mov     cl, last_drive          ;max to search
  372.                 xor     al, al                  ;unit counter
  373. get_num_b100:
  374.                 mov     cds_ptr.d_offset, bx    ;point 1st available
  375.                 cmp     es:[bx].cds_drv_stat4, 0  ;is it free?
  376.                 je      get_num_b500
  377.                 inc     al                      ;advance unit count
  378.                 add     bx, cds_size            ;get to next entry
  379.                 loop    get_num_b100
  380. get_num_b500:
  381.                 mov     max_avail_units, cl     ;# available units
  382.                 ret
  383. get_num_blk     endp
  384.  
  385. ;--------------------------------------------------------------
  386. ;do_drv_init - Format an INIT cmd blk and call the driver     |
  387. ;              strategy and interrupt routines.               |
  388. ;                                                             |
  389. ;             Enter:                                          |
  390. ;                      ds=local data segment                  |
  391. ;                   es:bx=ptr to driver header                |
  392. ;            driver_p_off=offset of any cmd line parms        |
  393. ;                         (these are preceeded by driver      |
  394. ;                          file name in driver_file)          |
  395. ;                      al=number of block devices on system   |
  396. ;                                                             |
  397. ;             Exit:                                           |
  398. ;                      ax=number of paragraphs to keep        |
  399. ;                   es:bx=ptr to driver header                |
  400. ;                      NZ=if INIT failed.                     |
  401. ;                                                             |
  402. ;    ds saved                                                 |
  403. ;--------------------------------------------------------------
  404. do_drv_init     proc    near
  405.                 push    ds
  406.                 push    es                      ;save driver header ptr
  407.                 push    bx
  408.                 mov     si, bx                  ;offset of driver 
  409.                 mov     bx, driver_p_off
  410.                 mov     byte ptr [bx - 1], ' '  ;file spec was ASCIIZ
  411.                 mov     dx, offset driver_file  ;offset of cmd line
  412.                 mov     bx, offset init_cmd_blk ;point to cmd block
  413.                 mov     [bx].rh_len, (size init_req) ;len cmd blk
  414.                 mov     [bx].init_bpb_tbo, dx   ;offset cmd line
  415.                 mov     [bx].init_drv_first, al ;next available drive
  416.                 mov     [bx].init_bpb_tbs, cs   ;seg for cmd line
  417.                 mov     [bx].init_brk_seg, cs   ;end of avail mem
  418.  
  419.                 mov     ax, ds
  420.                 mov     dx, es
  421.                 assume  ds:nothing
  422.                 mov     ds, dx                  ;ds=driver header
  423.                 mov     es, ax                  ;es=local data
  424.  
  425. ;es:bx point to driver cmd request block
  426.                 push    cs                      ;far
  427.                 mov     ax, offset do_drv_i100  ;return address
  428.                 push    ax
  429.  
  430.                 push    ds                       ;far "call"
  431.                 push    word ptr [si].dev_stratr ;address 
  432.  
  433.                 retf                            ;make strategy call
  434.  
  435.  
  436.  
  437. do_drv_i100:
  438.                 push    cs                      ;far
  439.                 mov     ax, offset do_drv_i200  ;return address
  440.                 push    ax
  441.  
  442.                 push    ds                       ;far "call"
  443.                 push    word ptr [si].dev_intr   ;address
  444.  
  445. ;
  446. ;One poorly designed driver we tested relied on ah being equal
  447. ;to zero.  Set it to zero just to make drivers of that nature
  448. ;happy.
  449. ;                
  450.                 xor     ax, ax
  451.                 retf                            ;make int call
  452. do_drv_i200:
  453.  
  454.                 test    es:[bx].rh_status, (mask err_bit) ;error?
  455.                 jnz     do_drv_i900
  456.                 mov     dx, ds                  ;start of driver
  457.                 mov     ax, es:[bx].init_brk_seg
  458.                 sub     ax, dx                  ;difference if any
  459.                 mov     dx, es:[bx].init_brk_ofs
  460.                 add     dx, 0fh                 ;paragraph round
  461.                 mov     cl, 4
  462.                 shr     dx, cl                  ;convert to paragraphs
  463.                 add     ax, dx                  ;total paragraphs
  464.                 sub     cl, cl                  ;zero flag set
  465.                 assume  ds:code    
  466.  
  467. do_drv_i900:
  468.                 pop     bx
  469.                 pop     es                      ;driver header seg
  470.                 pop     ds
  471.                 ret
  472. do_drv_init     endp
  473.  
  474.  
  475. ;--------------------------------------------------------------
  476. ;check_bpb - Inspect the BPB returned from the driver to      |
  477. ;            see if it at least has a correct value for       |
  478. ;            total number of sectors.                         |
  479. ;                                                             |
  480. ;             Enter:                                          |
  481. ;                      ds=local data segment                  |
  482. ;                                                             |
  483. ;             Exit:                                           |
  484. ;                      ZR=if values are bad.                  |
  485. ;                                                             |
  486. ;    ds saved                                                 |
  487. ;--------------------------------------------------------------
  488. check_bpb       proc    near
  489.                 push    bx
  490.                 push    ds
  491.                 lds     bx, dword ptr cs:init_cmd_blk.init_bpb_tbo 
  492.                 mov     bx, [bx]                ;get BPB offset
  493.                 cmp     [bx].bpb_ts, 0          ;total sectors > 0?
  494.                 pop     ds
  495.                 pop     bx
  496.                 ret
  497. check_bpb       endp
  498.  
  499.  
  500.  
  501. ;--------------------------------------------------------------
  502. ;make_drives - Create the DPB entries and fill in CDS entries |
  503. ;              for each unit supported by the device driver.  |
  504. ;              Also, update number of block devices field in  |
  505. ;              the DOS list of lists.                         |
  506. ;                                                             |
  507. ;             Enter:                                          |
  508. ;                      ds=local data segment                  |
  509. ;                 cds_ptr=ptr to next available CDS entry     |
  510. ;                cds_size=size of each CDS entry              |
  511. ;                 dpb_ptr=ptr to first DPB entry              |
  512. ;                dpb_size=size of each DPB entry              |
  513. ;              last_drive=max number of CDS entries           |
  514. ;                   es:bx=ptr to driver header                |
  515. ;            dpb_link_off=offset of link to next device driver|
  516. ;             dpb_drv_off=offset of address of device driver  |
  517. ;                        in DPB entry                         |
  518. ;         max_avail_units=maximum number of driver letters    |
  519. ;                         available for drives                |
  520. ;             Exit:                                           |
  521. ;                      CY=set if for some reason, DOS data    |
  522. ;                         areas corrupted, etc. (this should  |
  523. ;                         never happen)                       |
  524. ;                                                             |
  525. ;             mem_to_keep=adjusted to add in DPBs placed      |
  526. ;                         at end of driver (break address)    |
  527. ;                                                             |
  528. ;                                                             |
  529. ;    ds, es, bx preserved                                     |
  530. ;--------------------------------------------------------------
  531. make_drives     proc    near
  532.                 push    bx
  533.                 push    ds
  534.                 push    es
  535. ;
  536. ;dpb_size contains DOS version specific size of DPB entry.  Convert
  537. ;this to paragraphs --used to determine the number of additional
  538. ;paragraphs we need to keep resident.
  539. ;
  540. ;                
  541.                 mov     ax, dpb_size            ;size each DPB
  542.                 add     ax, 0fh                 ;round up
  543.                 mov     cl, 4
  544.                 shr     ax, cl
  545.                 mov     dpb_para, ax
  546.  
  547.  
  548.                 sub     ch, ch
  549.                 mov     cl, es:[bx].dev_num_units   ;number of units
  550.                 cmp     cl, max_avail_units     ;enough drive letters?
  551.                 jbe     make_dr050
  552.                 mov     ah, 9
  553.                 mov     dx, offset units_msg    ;let user know that
  554.                 int     21h                     ;we can't do all of them
  555.                 mov     cl, max_avail_units     ;only do what we can
  556.  
  557. make_dr050:        
  558.                 call    get_last_dpb            ;find last DPB
  559.                 jnc     make_dr070
  560.                 jmp     make_dr999              ;exit if error 
  561. make_dr070:        
  562. ;
  563. ;dx:si holds ptr to DPB for previous drive.  Save the pointer.
  564. ;
  565.                 mov     prev_dpb_ptr.d_segment, dx
  566.                 mov     prev_dpb_ptr.d_offset, si      
  567.  
  568. ;
  569. ;Save device driver header address
  570. ;
  571.                 mov     cs:drv_head_offset, bx  ;save drv header
  572.                 mov     dx, es                  
  573.  
  574. ;get pointer to BPB table
  575.  
  576.                 lds     bx, dword ptr cs:init_cmd_blk.init_bpb_tbo 
  577.  
  578.                 mov     al, cs:init_cmd_blk.init_drv_first ;drive
  579.                 sub     ah, ah                  ;unit counter
  580.                 
  581. make_dr100:
  582.                 push    ax                ;save unit count/drive #
  583.                 mov     ax, dx                  ;base of driver
  584.                 add     ax, cs:mem_to_keep      ;set for DPB
  585.                 mov     es, ax
  586.  
  587. ;
  588. ;Clear the area where DPB will reside.  BPB to DPB function doesn't
  589. ;initialize all of the fields.  This ensures that the drive access
  590. ;field is clear initially.
  591. ;
  592. ;
  593.                 push    cx                      ;save unit count
  594.                 mov     cx, cs:dpb_size         ;clear the DPB area
  595.                 xor     al, al
  596.                 xor     di, di
  597.                 rep     stosb
  598.                 pop     cx
  599.  
  600.                 xor     bp, bp                  ;es:bp ptr to new DPB
  601.                 mov     si, [bx]                ;get BPB offset
  602. ;
  603. ;Use undocumented DOS call to translate BPB to DPB
  604. ;
  605.                 mov     ah, DOS_XLAT_BPB 
  606.                 int     21h
  607.                 xor     di, di
  608.                 pop     ax                      ;count/drive #
  609.                 push    ax                      ;save again
  610.                 mov     es:[di], ax             ;put into DPB
  611. ;
  612. ;Put address of device driver into DPB.  Offset for this DPB entry
  613. ;is DOS version specific.
  614. ;
  615.                 push    bx
  616.                 mov     bx, cs:dpb_drv_off
  617.                 mov     es:[di + bx].d_segment, dx ;&device driver
  618.                 mov     ax, cs:drv_head_offset
  619.                 mov     es:[di + bx].d_offset, ax
  620.  
  621.                 mov     bx, cs:dpb_link_off
  622.                 mov     bp, bx                  ;save link offset
  623.                 
  624. ;
  625. ;Mark this as last DPB
  626. ;
  627.                 mov     ax, 0ffffh              ;no link to next DPB
  628.                 mov     es:[di + bx].d_segment, ax
  629.                 mov     es:[di + bx].d_offset, ax
  630.  
  631.                 pop     bx
  632.  
  633.                 inc     bx                      ;next BPB table entry
  634.                 inc     bx                      
  635.  
  636.                 call    fill_cds
  637.  
  638. ;
  639. ;Link previous DPB to this one
  640. ;
  641.                 mov     ax, es                  ;DPB segment
  642.                 mov     si, di                  ;DPB offset
  643.  
  644.                 les     di, cs:prev_dpb_ptr     ;previous DPB
  645.                 add     di, bp                  ;link offset in DPB
  646.  
  647.                 mov     es:[di].d_segment, ax
  648.                 mov     es:[di].d_offset, si
  649.  
  650. ;
  651. ;Save pointer to DPB just created (in case more than one unit supported, 
  652. ;it can be linked to the next DPB created).
  653. ;
  654.                 mov     cs:prev_dpb_ptr.d_segment, ax
  655.                 mov     cs:prev_dpb_ptr.d_offset, si
  656.  
  657.                 mov     ax, cs:dpb_para         ;# paragraphs DPB uses    
  658.                 add     cs:mem_to_keep, ax      ;add to total to keep 
  659.  
  660.                 pop     ax                      ;save unit count/drive #
  661.                 inc     ah                      ;unit counter
  662.                 inc     al                      ;drive counter
  663.  
  664.                 loop    make_dr100
  665.  
  666.                 push    ax                      ;save unit count
  667.                 mov     ah, DOS_LIST_LISTS 
  668.                 int     21h                     ;get list of lists ptr
  669.                 pop     ax                      ;restore it
  670. ;
  671. ;Increase the number of block devices variable kept in the DOS
  672. ;list of lists.  The number will now reflect all block devices on
  673. ;the system (including any drive for which a CDS was found  --which
  674. ;may have not been previously reflected in the variable).  It was found
  675. ;to be necessary to include all block devices in this value.  For 
  676. ;example, if a CD-ROM drive existed on the system as drive e:, this
  677. ;variable would be a 4 (doesn't reflect the "network" drive).  Simply
  678. ;adding the number of units installed by driver being loaded, was not
  679. ;sufficient to make DOS happy  --the value needed to be the total
  680. ;number of block devices (including the CD-ROM drive).
  681. ;
  682.                 mov     si, cs:num_blk_off      ;offset for # drives
  683.                 mov     al, cs:num_blk_devices  ;original number
  684.                 add     al, ah
  685.                 mov     es:[bx + si], al
  686.  
  687.                 clc                             ;success
  688. make_dr999:
  689.  
  690.                 pop     es
  691.                 pop     ds
  692.                 pop     bx
  693.                 ret
  694. make_drives     endp
  695.  
  696.  
  697. ;--------------------------------------------------------------
  698. ;fill_cds  -  fill in a CDS entry.                            |
  699. ;                                                             |
  700. ;             Enter:                                          |
  701. ;                      ds=driver BPB segment                  |
  702. ;                   es:di=ptr to DPB                          |
  703. ;              cs:cds_ptr=ptr to first available CDS entry    |
  704. ;             cs:cds_size=size of each CDS entry              |
  705. ;             Exit:                                           |
  706. ;                                                             |
  707. ;              cs:cds_ptr=ptr to next CDS                     |
  708. ;                                                             |
  709. ;   ds, di preserved   (cx, dx not used)                      |
  710. ;--------------------------------------------------------------
  711. fill_cds        proc    near
  712.                 push    ds
  713.                 push    di
  714.                 mov     ax, di                  ;offset of DPB 
  715.                 lds     di, cs:cds_ptr          ;get ptr to CDS
  716.                 mov     [di].cds_dbp_ptr4.d_offset, ax ;ptr to DPB
  717.                 mov     [di].cds_dbp_ptr4.d_segment, es
  718.  
  719. ;indicate it's a physical drive 
  720.                 mov     [di].cds_drv_stat4, (mask physical) 
  721.                 mov     [di].cds_slash4, 2      ;slash in path
  722.                 mov     ax, 0ffffh
  723.                 mov     [di].cds_start_clus4, ax ;starting cluster
  724.                 mov     [di].cds_dk14.d_offset, ax ;ffs in this field
  725.                 mov     [di].cds_dk14.d_segment, ax 
  726.                 
  727.                 cmp     byte ptr cs:dos_ver, 4     ;below DOS 4?
  728.                 jb      fill_cds500
  729.                 inc     ax                      ;zero
  730.                 mov     [di].cds_ifs.d_segment, ax ;no install file sys
  731.                 mov     [di].cds_ifs.d_offset, ax ;no install file sys
  732.  
  733.                 mov     [di].cds_dk24, al       ;zero unknown fields
  734.                 mov     [di].cds_dk34, ax   
  735.  
  736. fill_cds500:
  737.                 mov     ax, cs:cds_size
  738.                 add     cs:cds_ptr.d_offset, ax ;next CDS entry
  739.  
  740.                 pop     di
  741.                 pop     ds
  742.                 ret
  743. fill_cds        endp
  744.  
  745.  
  746. ;--------------------------------------------------------------
  747. ;get_last_dpb - Search DPB chain and find last DPB entry.     |
  748. ;               It's possible to use DOS function 32h to      |
  749. ;               return a ptr to a DPB, but the call requires  |
  750. ;               an access to the drive and don't want         |
  751. ;               critical error if drive isn't ready (could    |
  752. ;               be removable media, etc.).                    |
  753. ;                                                             |
  754. ;             Enter:                                          |
  755. ;                      ds=local data segment                  |
  756. ;                 dpb_ptr=ptr to first DPB entry              |
  757. ;              last_drive=max number of CDS entries           |
  758. ;                                                             |
  759. ;             Exit:                                           |
  760. ;                                                             |
  761. ;                   dx:si=ptr to last used DPB                |
  762. ;                      CY=set if can't find ending entry      |
  763. ;                         (this should never occur, since     |
  764. ;                          we've already confirmed there      |
  765. ;                          are enough CDS entries, etc.)      |
  766. ;                                                             |
  767. ;    ds, bx,  cx, saved.                                      |
  768. ;--------------------------------------------------------------
  769. get_last_dpb    proc    near
  770.                 push    ds
  771.                 push    bx
  772.                 push    cx
  773.                 sub     ch, ch
  774.                 mov     cl, last_drive
  775.                 mov     bx, dpb_link_off        ;ver specific offset        
  776.                 lds     si, dpb_ptr
  777.                 mov     ax, 0ffffh              ;end of list flag
  778. get_last_100:
  779.                 cmp     [si + bx], ax           ;at the end?
  780.                 je      get_last_900
  781.                 lds     si, [si + bx]
  782.                 loop    get_last_100
  783.                 stc
  784. get_last_900:
  785.                 mov     dx, ds                  ;save seg of DPB
  786.                 pop     cx
  787.                 pop     bx
  788.                 pop     ds
  789.                 ret
  790. get_last_dpb    endp
  791. ;--------------------------------------------------------------
  792. ;add_to_chain - Add driver to driver chain.                   |
  793. ;                                                             |
  794. ;             Enter:                                          |
  795. ;                      ds=local data segment                  |
  796. ;                   es:bx=ptr to driver header                |
  797. ;             nul_dev_ptr=ptr to NUL device header            |
  798. ;                                                             |
  799. ;             Exit:                                           |
  800. ;    ds saved                                                 |
  801. ;--------------------------------------------------------------
  802. add_to_chain    proc    near
  803.                 push    ds
  804.                 lds     si, nul_dev_ptr         ;point to NUL header
  805.                 mov     ax, [si].dev_chain.d_offset ;ptr to next drvr
  806.                 mov     dx, [si].dev_chain.d_segment
  807.                 
  808.                 cli          
  809.                 mov     [si].dev_chain.d_offset, bx  ;put ours in list
  810.                 mov     [si].dev_chain.d_segment, es
  811.                 mov     es:[bx].dev_chain.d_offset, ax  ;link to 
  812.                 mov     es:[bx].dev_chain.d_segment, dx ;old 1st drvr
  813.                 sti
  814.                 pop     ds                
  815.                 ret
  816. add_to_chain    endp
  817.  
  818.  
  819. ;--------------------------------------------------------------
  820. ;go_tsr - Free our environment and then terminate and stay    |
  821. ;         resident, keeping the PSP and all paragraphs        |
  822. ;         of memory requested by the device driver.           |
  823. ;                                                             |
  824. ;             Enter:                                          |
  825. ;                      ds=local data segment                  |
  826. ;             mem_to_keep=paragraphs needed by driver         |
  827. ;                                                             |
  828. ;             Exit:                                           |
  829. ;                      SHOULD NEVER RETURN                    |
  830. ;                                                             |
  831. ;                      if for some reason the TSR function    |
  832. ;                      fails, return with CY set              |
  833. ;                                                             |
  834. ;--------------------------------------------------------------
  835. go_tsr          proc    near 
  836.                 mov     dx, offset tsr_ok_msg ;report installed
  837.                 mov     ah, DOS_PRT_STRING       
  838.                 int     21h
  839.        
  840.                 mov     ax, es          ;driver segment
  841.                 sub     ax, 10h         ;back to the PSP
  842.                 mov     es, ax
  843.                 mov     bx, psp_environ_seg
  844.                 mov     ax, es:[bx]             ;get environment segment
  845.                 mov     es, ax
  846.                 mov     ah, DOS_RELEASE_MEM        
  847.                 int     21h
  848.  
  849.                 mov     dx, mem_to_keep
  850.                 add     dx, 10h                 ;add in PSP length
  851.                 mov     ax, (DOS_TSR_FUNC SHL 8) ;exit code 0      
  852.                 int     21h                     ;Go TSR
  853.                 ret
  854. go_tsr          endp        
  855.  
  856.  
  857.                 even
  858. drv_stack       dw      DRV_STACK_SIZE dup('SS')
  859. drv_stack_ptr   label   word
  860.  
  861.  
  862. code            ends
  863.                 end     startup
  864.  
  865.