home *** CD-ROM | disk | FTP | other *** search
/ Boston 2 / boston-2.iso / DOS / HILFEN / SYSTEM / RAMDISK6 / AMDISK.ASM < prev    next >
Assembly Source File  |  1993-12-01  |  33KB  |  1,165 lines

  1. ; ==== Adjustable Memory Disk Device Driver ====
  2. ;
  3. ; (c) Copyright 1986,1987,1988 by Gary R. Cramblitt
  4. ;
  5. ; v2.2  1 Jul 86  Initial version
  6. ; v2.3 24 Aug 86  Bug.  FAT media byte not updated properly
  7. ; v2.4 29 Aug 86  Sync version # with ADJRAM.C  No other changes.
  8. ; v2.5 30 Aug 86  Add /E (LOTUS/INTEL,Microsoft Expanded Memory) support;
  9. ;          Increase root directory to 128 entries;
  10. ;          Permit maximum size of 2043K.
  11. ;          Get rid of second FAT.
  12. ; v3.0  5 Sep 86  Sync version # with ADJRAM.C.  No other changes.
  13. ; v3.1  2 Oct 86  Sync version # with ADJRAM.C.  No other changes.
  14. ; v3.2 18 Oct 86  Add SIZE= option
  15. ; v4.0  3 Jan 87  Chg SIZE= to MINSIZE=
  16. ;          Add support for reserved conventional memory.
  17. ;          Add CLUSTER= option.
  18. ;          Allow for expanded memory blocks larger than 64K.
  19. ;          Use legal FAT ID.  CHKDSK now happy with memory disk, and
  20. ;            occasional DOS "FAT error" prevented.
  21. ;          Add volume label to memory disk.
  22. ; v4.1 15 May 88  Restrict reserved memory to A segment or above.
  23. ;                 Initialize reserved memory.
  24. ;
  25. ; ==== Constant Definition ===================
  26. ;
  27. ; ---- Customizable Definitions -----
  28. ;      If these are changed, the corresponding symbols in adjram.c
  29. ;      must be altered.
  30.  
  31. def_size_K    equ    64        ; default 64K ram disk
  32. max_isize_K    equ    512        ; maximum 512K initial ram disk size
  33. cnv_sec_per_blk    equ    64        ; 32K increments in conventional mem
  34. max_clusters    equ    4086        ; max clusters per disk
  35.                                         ;   (12-bit FAT entries)
  36. bytes_per_sec    equ    512        ; bytes per sector
  37. def_sec_per_cl    equ    1        ; default sectors per cluster
  38. par_per_sec_lg2    equ    5        ; 16-byte paragraphs per sector
  39.                     ;   log 2.  Must match bytes_per_sec
  40. fats_per_disk    equ    1        ; number of FATS
  41.  
  42. ; ---- Derived quantities ----
  43.  
  44. max_mem_blks    equ    (max_clusters/cnv_sec_per_blk)
  45.                     ; Maximum number of memory blocks
  46.                     ;   assuming cluster size of 1
  47. sec_per_K    equ    1024/bytes_per_sec ; sectors per 1024 bytes
  48. sec_per_K_lg2    equ    1        ; must match above parameter
  49. par_per_sec    equ    bytes_per_sec/16   ; 16-byte paragraphs per sector
  50. def_size_sec    equ    def_size_K*sec_per_K ; default size in sectors
  51. bytes_per_fat    equ    max_clusters*3/2
  52. sec_per_fat    equ    (bytes_per_fat+bytes_per_sec-1)/bytes_per_sec
  53.  
  54. sec_per_em_pag    equ    16*sec_per_K    ; sectors per expanded mem page
  55. sec_per_em_pag_lg2 equ    5        ; sectors per em page log 2, must
  56.                     ;   match above parameter
  57. sec_per_em_pag_msk equ    sec_per_em_pag-1
  58.  
  59. ; ---- Static Request Header Structure Definitions ----
  60.  
  61. srh_length    equ    0        ; (byte) length field
  62. srh_unit    equ    1 + srh_length    ; (byte) unit field
  63. srh_command    equ    1 + srh_unit    ; (byte) command code
  64. srh_status    equ    1 + srh_command    ; (word) status field
  65. srh_reserved    equ    2 + srh_status    ; (8 bytes)
  66. srh_size    equ    8 + srh_reserved ; request header size
  67.  
  68. ; ---- Request Header Status values ----
  69.  
  70. s_done        equ    0100h    ; done, no errors
  71. s_busy        equ    0200h    ; busy, no errors
  72.  
  73. e_err        equ    8000h
  74. e_protect    equ    e_err+0h    ; error: write protect
  75. e_unknown_unit    equ    e_err+1h    ; error: unknown unit
  76. e_not_ready    equ    e_err+2h    ; error: not ready
  77. e_command    equ    e_err+3h    ; error: unknown command
  78. e_crc        equ    e_err+4h    ; error: bad CRC
  79. e_bad_length    equ    e_err+5h    ; error: bad structure length
  80. e_seek        equ    e_err+6h    ; error: bad seek
  81. e_media        equ    e_err+7h    ; error: unknown media
  82. e_not_found    equ    e_err+8h    ; error: sector not found
  83. e_paper        equ    e_err+9h    ; error: out of paper
  84. e_write        equ    e_err+ah    ; error: write fault
  85. e_read        equ    e_err+bh    ; error: read fault
  86. e_general    equ    e_err+ch    ; error: general error not listed above
  87.  
  88. ; ---- Non-destructive read parameter block ----
  89.  
  90. rh_read_data    equ    srh_size    ; (byte) non-destructive data
  91.  
  92. ; ---- Input/output parameter block ----
  93.  
  94. rh_media    equ    0 + srh_size        ; (byte) media descriptor
  95. rh_buf_offset    equ    1 + rh_media        ; (word) transfer buffer offset
  96. rh_buf_segment    equ    2 + rh_buf_offset    ; (word) transfer buffer segment
  97. rh_buf_size    equ    2 + rh_buf_segment    ; (word) transfer buffer size
  98. rh_start    equ    2 + rh_buf_size        ; (word) transfer starting sector
  99.  
  100. m_fixed        equ    0f8h    ; media: fixed disk
  101.  
  102. m_ss9        equ    0fch    ; media: single sided, 8 sectors/track
  103. m_ds9        equ    0fdh
  104. m_ss8        equ    0feh
  105. m_ds8        equ    0ffh
  106.  
  107. ; ---- Build BPB parameter block ----
  108.                         ; preceeded by media descriptor
  109. rh_bpb        equ    1 + rh_media        ; (dword) bpb buffer address
  110. rh_tbl_offset    equ    4 + rh_bpb        ; (dword) bpb table offset/segment
  111. rh_tbl_segment    equ    2 + rh_tbl_offset
  112.  
  113. ; ---- Media Check parameter block ----
  114.  
  115. rh_check    equ    1 + rh_media        ; (byte) media check result
  116.  
  117. mc_changed    equ    -1            ; media has changed
  118. mc_maybe    equ    0            ; media may have been changed
  119. mc_same        equ    1            ; media has not changed
  120.  
  121. ; ---- Initialize parameter block ----
  122.  
  123. rh_units    equ    0 + srh_size        ; (byte) number of units supported
  124. rh_end_offset    equ    1 + rh_units        ; (word) end address of driver
  125. rh_end_segment    equ    2 + rh_end_offset
  126. rh_bpb_offset    equ    2 + rh_end_segment    ; (word) BPB array address
  127. rh_bpb_segment    equ    2 + rh_bpb_offset
  128. rh_cmd_offset    equ    rh_bpb_offset        ; (word) "device=" cmd line
  129. rh_cmd_segment    equ    rh_bpb_segment
  130. rh_cmd        equ    rh_bpb_offset
  131.  
  132. ; ---- DOS interrupts ----
  133.  
  134. dosi_dosf    equ    21h        ; DOS function dispatcher
  135.  
  136. ; ---- User Interrupts ----
  137.  
  138. usri_emm    equ    67h        ; Expanded Memory Manager
  139.  
  140. ; ---- DOS interrupt 21 functions ----
  141.  
  142. dosf_outstr    equ    9        ; display string
  143. dosf_seldisk    equ    0eh        ; select disk
  144. dosf_getdisk    equ    19h        ; get current disk
  145.  
  146. ; ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ----
  147.  
  148. emm_status    equ    40h        ; get manager status
  149. emm_get_PFseg    equ    41h        ; get page frame segment
  150. emm_get_pages    equ    42h        ; get number of pages
  151. emm_get_handle    equ    43h        ; get handle and allocate memory
  152. emm_map_memory    equ    44h        ; map memory
  153. emm_fre_handle    equ    45h        ; free handle and memory
  154. emm_get_ver    equ    46h        ; get EMM version
  155. emm_sav_map    equ    47h        ; save mapping context
  156. emm_res_map    equ    48h        ; restore mapping context
  157. emm_num_handles    equ    4bh        ; get number of EMM handles
  158. emm_hdl_pages    equ    4ch        ; get pages owned by handle
  159. emm_all_pages    equ    4dh        ; get pages for all handles
  160. emm_pag_map    equ    4eh        ; get or set page map
  161.  
  162. ; ---- Device Driver Header Attribute Definitions ----
  163.  
  164. a_input        equ    0001h        ; standard input device
  165. a_output    equ    0002h        ; standard output device
  166. a_nul        equ    0004h        ; NUL device
  167. a_clock        equ    0008h        ; CLOCK$ device
  168. a_ibm        equ    0        ; IBM block device (bit 13)
  169. a_not_ibm    equ    2000h        ; non-IBM block device (bit 13)
  170. a_ioctl        equ    4000h        ; IOCTL functions supported
  171. a_block        equ    0        ; block device (bit 15)
  172. a_character    equ    8000h        ; character device (bit 15)
  173.  
  174. ; ---- FAT IDs ----
  175.  
  176. fid_fix        equ    0f8h        ; fixed disk
  177. fid_ds15    equ    0f9h        ; double-sided, 15 sector
  178. fid_ss9        equ    0fch        ; single-sided, 9 sector
  179. fid_ds9        equ    0fdh        ; double-sided, 9 sector
  180. fid_ss8        equ    0feh        ; single-sided, 8 sector
  181. fid_ds8        equ    0ffh        ; double-sided, 8 sector
  182. fid_ss8sd    equ    0feh        ; single-sided, 8 inch, single dens
  183. fid_ss8sd_alt    equ    0fdh        ; alternate sssd 8 inch
  184. fid_ds8dd    equ    0feh        ; double-side, 8 inch, double-dens
  185.  
  186. ; ---- Character Codes ----
  187.  
  188. cc_ht        equ    9        ; TAB
  189. cc_cr        equ    13
  190. cc_lf        equ    10
  191. cc_sp        equ    ' '
  192. cc_bel        equ    7
  193.  
  194. ; ==== Device Driver Header Definition ====
  195.  
  196. cseg    segment para public 'CODE'
  197.  
  198. driver    proc far
  199.  
  200.     assume cs:cseg,ds:cseg,es:cseg
  201.  
  202.     dd    -1            ; last driver in chain
  203.     dw    a_block + a_not_ibm    ; driver attribute
  204.     dw    dev_strategy        ; offset to strategy routine
  205.     dw    dev_interrupt        ; offset to interrupt routine
  206.     db    1            ; number of devices or device name
  207.     db    7 dup ( ? )        ; filler for block device
  208.  
  209. ; ==== Device Driver Tables ====
  210. ;
  211. ; ---- BIOS Parameter Block Table and Entries ----
  212.  
  213. bpb_table    dw    bpb        ; one entry for each unit
  214.  
  215. ; ---- Request Header Address set by dev_strategy ----
  216.  
  217. rh_address    dd    1 dup ( ? )        ; request header base address
  218.  
  219. rh_offset    equ    word ptr rh_address
  220. rh_segment    equ    word ptr rh_address + 2
  221.  
  222. ; ---- Request Header Command Dispatch Table ----
  223.  
  224. cmd_table    dw    initialize    ; initialize driver
  225.         dw    media_check    ; media check
  226.         dw    build_bpb    ; build BPB
  227.         dw    ioctl_read    ; IOCTL read
  228.         dw    read        ; normal read
  229.         dw    check_input    ; non-destructive read/status
  230.         dw    input_status    ; input status
  231.         dw    input_flush    ; flush input buffers
  232.         dw    write        ; normal write
  233.         dw    write_verify    ; normal write with read verify
  234.         dw    output_status    ; output status
  235.         dw    output_flush    ; flush output buffer
  236.         dw    ioctl_write    ; IOCTL write
  237.  
  238. ; ==== Common Device Driver Routines ====
  239. ;
  240. ; ---- Device Driver Strategy Routine ----
  241. ;
  242. ; es:bs == request header address
  243.  
  244. dev_strategy    proc    far
  245.         mov    cs:rh_offset,bx
  246.         mov    cs:rh_segment,es
  247.         ret
  248. dev_strategy    endp
  249.  
  250. ; ---- Device Driver Interrupt Routine ----
  251.  
  252. dev_interrupt    proc    far
  253.         push    ax            ; save registers used
  254.         push    bx
  255.         push     cx
  256.         push    dx
  257.         push    di
  258.         push    si
  259.         push    ds
  260.         push    es
  261.          cld                ; clear direction flag
  262.          push    cs            ; setup small memory model
  263.          pop    ds            ; ds := program segment
  264.          les    bx,rh_address        ; es:bx := request header index
  265.          mov    si,es:srh_command[bx]    ; si := request command (byte)
  266.          and    si,0ffh            ; si := request command
  267.          add    si,si            ; si := word table offset
  268.          call    word ptr cmd_table[si]    ; ax := command result
  269.          lds    bx,cs:rh_address    ; ds:bx := request header index
  270.          mov    srh_status[bx],ax    ; update request status
  271.         pop    es            ; restore register
  272.         pop    ds
  273.         pop    si
  274.         pop    di
  275.         pop    dx
  276.         pop    cx
  277.         pop    bx
  278.         pop    ax
  279.         ret
  280.  
  281. dev_interrupt    endp
  282.  
  283. ; **** END OF DEVICE INDEPENDENT PORTION OF DRIVER ****
  284.  
  285. ; ---- Memory Block Table that follows BPB in boot sector ----
  286.  
  287. mem_blk_table_entry    struc
  288. typ        db    ?        ; 0 = normal  1 = EM   2 = extended
  289. par        dw    ?        ; paragraph address of block
  290. siz        dw    ?        ; number of sectors in the memory block
  291. hdl        dw    ?        ; EM handle for the block
  292. mem_blk_table_entry    ends
  293.  
  294. nor_flg        equ    0        ; Memory Block is in normal memory
  295. em_flg        equ    1        ; Memory Block is in expanded memory
  296. rm_flg        equ    2        ; Memory Block is in reserved memory
  297.  
  298. ; ---- Flag indicating whether expanded memory mapping context has been
  299. ;      saved yet or not.  If nonzero, it indicates that the context has
  300. ;      been saved.
  301.  
  302. em_context_flg    db    0        ; 0 = not saved; 1 = context saved
  303. em_context_hdl    dw    ?        ; EM handle underwhich context saved
  304.  
  305. ; ==== Memory Disk Device Driver Code ====
  306. ;
  307. ; ---- Driver support functions (near functions) ----
  308. ;
  309. ; es:bx == request header
  310. ; ds    == cs
  311. ; All other registers are usable.
  312. ;
  313. ; ax := result status
  314. ;
  315. ; ---- Initialize driver ----
  316.  
  317. initialize    proc    near        ; initialize driver
  318.  
  319.     lea    ax,word ptr mdisk_data    ; ax := end of driver
  320.     mov    cl,4            ; cl := paragraph size log2
  321.     shr    ax,cl            ; ax := paragraphs in driver
  322.     mov    dx,cs            ; dx := driver segment
  323.     add    ax,dx            ; ax := memory disk segment
  324.     mov    mem_blk_table.par,ax    ; update for subsequent transfers
  325.  
  326.     call    parse_size        ; Parse MINSIZE= clause
  327.     call    parse_cluster        ; Parse CLUSTER= clause
  328.  
  329.     mov    ax,cs
  330.     mov    es,ax
  331.     call    enter_vol_label        ; Put volume label in directory
  332.     lea    di,word ptr fat1    ; es:di := first FAT
  333.     mov    cx,sec_per_fat*bytes_per_sec ; cx = size
  334.     push    di            ; save first FAT address
  335.     push    es
  336.  
  337.      mov    al,fid_ds9        ; say "dbl-sided, 9 sector"
  338.      mov    [di],al            ; update FAT media byte
  339.      mov    word ptr 1[di],0ffffh    ; allocate initial sectors
  340.      add    di,3            ; adjust FAT index
  341.      sub    cx,3            ; adjust FAT size
  342.      xor    al,al            ; al := 0
  343.      rep    stosb            ; clear rest of FAT
  344.  
  345.     pop    ds
  346.     pop    si            ; ds:si := first FAT index
  347.  
  348.     if    fats_per_disk - 1    ; assemble if 2 FATS
  349.       lea    di,word ptr fat2    ; es:di := second FAT
  350.           mov    cx,sec_per_fat*bytes_per_sec ; cx := size
  351.       rep    movsb            ; copy first FAT to second FAT
  352.     endif
  353.  
  354.     add    di,32            ; skip over volume label
  355.  
  356.     mov    ax,cs:bpb_root        ; ax := number of directory entries
  357.     dec    ax            ; less volume label
  358.     mov    cl,5            ; cl := size of entry log2 (32 bytes)
  359.     shl    ax,cl            ; ax := directory size
  360.  
  361.     mov    cx,ax            ; cx := directory size
  362.     xor    al,al            ; al := 0
  363.     rep    stosb            ; zero directory
  364.  
  365.     lds    bx,cs:rh_address    ; ds:bx := request header address
  366.     mov    byte ptr rh_units[bx],1    ; return number of units
  367.     mov    word ptr rh_end_offset[bx],offset mdisk_data
  368.     mov    rh_end_segment[bx],cs    ; Calc end address of disk
  369.     mov    ax,cs:mem_blk_table.siz
  370.     mov    cl,par_per_sec_lg2
  371.     shl    ax,cl
  372.     add    rh_end_segment[bx],ax    ; return ending address of driver
  373.     mov    word ptr rh_bpb_offset[bx],offset bpb_table
  374.     mov    rh_bpb_segment[bx],cs    ; return BPB table address
  375.  
  376.     push    cs
  377.     pop    ds
  378.     mov    ah,dosf_getdisk        ; get current default disk
  379.     int    dosi_dosf
  380.     mov    dl,al            ; select it 
  381.     mov    ah,dosf_seldisk
  382.     int    dosi_dosf        ; DL := number of drives
  383.     add    al,'A'            ; convert to letter
  384.     mov    drive,al        ; store into message
  385.     mov    dx,offset initok
  386.     mov    ah,dosf_outstr
  387.     int    dosi_dosf
  388.     mov    ax,s_done        ; ax := done, no errors
  389.     ret
  390.  
  391. initok    db    cc_cr,cc_lf,'AMDISK v4.1 '
  392.     db    '(c) Copyright 1986, 1987, 1988 by Gary Cramblitt'
  393.         db      cc_cr,cc_lf
  394.     db    '   -- Initialized as disk '
  395. drive    db    ' '
  396.     db    ':',cc_cr,cc_lf,'$'
  397.  
  398. initialize    endp
  399.  
  400. ; ---- Media check ----
  401. ;
  402. ; Memory disk is non-removable media, however, the size of the media
  403. ; can be changed.  We check the number of memory blocks allocated,
  404. ; which is located in the boot record.  If it has changed, then the media
  405. ; has changed.
  406.  
  407. media_check    proc    near        ; media check
  408.  
  409.     lds    bx,cs:rh_address    ; ds:bx := request header address
  410.     mov    al,cs:bpb_media        ; Get our media byte
  411.     cmp    al,rh_media[bx]        ; Changed from what DOS has?
  412.     jz    media_same        ; -- Yes
  413.     mov    byte ptr rh_check[bx],mc_changed
  414.     mov    ax,s_done
  415.     ret
  416.  
  417. media_same:
  418.     mov    byte ptr rh_check[bx],mc_same
  419.     mov    ax,s_done        ; ax := function done, no errors
  420.     ret
  421.  
  422. media_check    endp
  423.  
  424. ; ---- Build BPB ----
  425. ;
  426. ; Read boot sector and copy into BPB buffer
  427. ; Adjust request header values
  428.  
  429. build_bpb    proc    near        ; build BPB
  430.  
  431.     lds    bx,cs:rh_address    ; ds:bx    := request header address
  432.     mov    word ptr rh_tbl_offset[bx],offset bpb
  433.     mov    word ptr rh_tbl_segment[bx],cs
  434.     mov    ax,s_done        ; ax := function done, no errors
  435.      ret
  436.  
  437. build_bpb    endp
  438.  
  439. ; ---- Read from device ----
  440.  
  441. read        proc near        ; normal read
  442.  
  443.     push    es
  444.     pop    ds            ; ds := request header segment
  445.     mov    di,rh_buf_offset[bx]
  446.     mov    ax,di
  447.     and    di,000fh        ; di := buffer offset within paragraph
  448.     mov    cl,4
  449.     shr    ax,cl
  450.     add    ax,rh_buf_segment[bx]    ; ax := paragraph of buffer
  451.     mov    es,ax            ; es:di := normalized buffer address
  452.     mov    dx,rh_buf_size[bx]    ; dx := sectors to read
  453.     mov    ax,rh_start[bx]        ; ax := first sector to read
  454.  
  455.     mov    bx,-(size mem_blk_table_entry)    ; find the memory block which..
  456.     or    dx,dx            ; All sectors transfered?
  457.     jnz    find_mem_blk_2_rd    ; -- No
  458.     jmp    rd_finish_2        ; -- Yes, squirrelly request!
  459. find_mem_blk_2_rd:                ;  contains the desired sector..
  460.     add    bx,size mem_blk_table        ; by subtracting the # of sectors..
  461.     sub    ax,cs:mem_blk_table.siz[bx] ; in each block.
  462.     jnc    find_mem_blk_2_rd
  463.     add    ax,cs:mem_blk_table.siz[bx] ; bx := ptr to memory block table
  464.                         ; ax := sector within the block
  465.  
  466.     cmp    cs:mem_blk_table.typ[bx],em_flg; Expanded Memory?
  467.     jnz    rd_1st_cnv_blk
  468.  
  469.     call    save_em_context        ; save expanded mem context
  470.     call    get_em_PF_seg        ; get em page frame segment
  471.     push    ax            ; save sector within block
  472.     and    ax,sec_per_em_pag_msk    ; ax := sec within em page
  473.     push    ax            ; save sec within em page
  474.     mov    cl,par_per_sec_lg2    ; paragraphs per sector log 2
  475.     shl    ax,cl            ; ax := paragraph within em page
  476.     add    ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
  477.     mov    ds,ax
  478.     xor    si,si            ; ds:si := address of 1st sector
  479.     mov    cx,sec_per_em_pag
  480.     pop    ax            ; get sector within em page
  481.     sub    cx,ax            ; cx := secs remaining in em page
  482.     pop    ax            ; get sector within block
  483.     push    cx            ; save secs remaining in em page
  484.     sub    ax,cs:mem_blk_table.siz[bx]
  485.     neg    ax            ; ax := sectors remaining in block
  486.     dec    ax            ; relative to 0
  487.     mov    cl,sec_per_em_pag_lg2    ; adjust to em pages
  488.     shr    ax,cl
  489.     inc    ax            ; ax := em pages remaining in blk
  490.     pop    cx            ; get sectors remaining in em page
  491.  
  492.     call    map_em_pag        ; map EM to log mem
  493.     jmp    rd_one_pag
  494.  
  495. rd_1st_cnv_blk:
  496.     push    ax            ; save sector within block
  497.     mov    cl,par_per_sec_lg2    ; paragraphs per sector log 2
  498.     shl    ax,cl            ; ax := paragraph within memory block
  499.     add    ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
  500.     mov    ds,ax
  501.     xor    si,si            ; ds:si := address of sector
  502.  
  503.     mov    cx,cs:mem_blk_table.siz[bx] ; cx := end sector
  504.     pop    ax            ; ax := start sector
  505.     sub    cx,ax            ; cx := secs remaining in mem blk
  506.     mov    ax,1            ; ax := pages remaining to be read
  507.  
  508. rd_one_pag:
  509.     push    ax            ; save pages remaining count
  510. rd_one_sector:
  511.     push    cx            ; save sector count for this blk
  512.     mov    cx,bytes_per_sec    ; 512 bytes per sector
  513.     rep    movsb            ; copy sector to buffer
  514.     pop    cx            ; cx := secs remaining in this blk+1
  515.     mov    ax,ds
  516.     add    ax,par_per_sec        ; 32 paragraphs per sector
  517.     mov    ds,ax
  518.     xor    si,si            ; ds:si := address of next sector
  519.         mov    ax,es            ; advance es to next sector
  520.     add    ax,par_per_sec
  521.     mov    es,ax
  522.     and    di,000fh        ; es:di := normalized transfer address
  523.     dec    dx            ; decrement sector count
  524.     jz    rd_finish        ; all done?
  525.     loop    rd_one_sector        ; if not end of mem blk, do another
  526.  
  527.     pop    ax            ; ax := number of em pages left
  528.     dec    ax            ; all pages read?
  529.     jnz    rd_next_em_pag        ; -- no, then read next em page
  530.  
  531. rd_next_mem_blk:
  532.     add    bx,size mem_blk_table_entry
  533.     cmp    cs:mem_blk_table.typ[bx],em_flg
  534.     jnz    rd_next_cnv_mem_blk
  535.     call    save_em_context
  536.     call    get_em_PF_seg
  537.     mov    ax,cs:mem_blk_table.siz[bx] ; ax := sectors in mem block
  538.     mov    cl,sec_per_em_pag_lg2    ; adjust to em pages in mem block
  539.     shr    ax,cl
  540.  
  541. rd_next_em_pag:
  542.     call    map_em_pag        ; Map EM to logical mem
  543.     mov    ds,cs:mem_blk_table.par[bx]
  544.     mov    cx,sec_per_em_pag
  545.     jmp    rd_one_pag
  546.  
  547. rd_next_cnv_mem_blk:
  548.     call    restore_em_context
  549.     mov    ax,1                ; ax := pages remaining
  550.     mov    ds,cs:mem_blk_table.par[bx] ; ds:si := start of new mem blk
  551.     mov    cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
  552.     jmp    rd_one_pag
  553.  
  554. rd_finish:
  555.     pop    ax            ; Clear page remaining count
  556.     call    restore_em_context    ; If needed, restore em context
  557. rd_finish_2:
  558.     mov    ax,s_done        ; ax := transfer done
  559.     ret
  560.  
  561. read    endp
  562.  
  563. ; ---- Write to device ----
  564.  
  565. write        proc    near        ; normal write
  566.  
  567.     push    es
  568.     pop    ds            ; ds := request header segment
  569.     mov    dx,rh_buf_size[bx]    ; dx := sectors to write
  570.     mov    si,rh_buf_offset[bx]
  571.     mov    ax,si
  572.     and    si,000fh        ; si := buffer offset within paragraph
  573.     mov    cl,4
  574.     shr    ax,cl
  575.     add    ax,rh_buf_segment[bx]    ; ax := paragraph of buffer
  576.     mov    cx,ax
  577.     mov    ax,rh_start[bx]        ; ax := first sector to write
  578.     mov    ds,cx            ; ds:si := normalized buffer address
  579.  
  580.     mov    bx,-(size mem_blk_table_entry)
  581.     or    dx,dx            ; transfer already done?
  582.     jnz    find_mem_blk_2_wrt    ; -- no, of course not
  583.     jmp    wrt_finish_2        ; -- yes, squirrelly request!
  584.                         ; find the memory block which...
  585. find_mem_blk_2_wrt:                ;   contains the desired sector..
  586.     add    bx,size mem_blk_table_entry ;   by subtracting the # of sectors..
  587.     sub    ax,cs:mem_blk_table.siz[bx] ;  in each block.
  588.     jnc    find_mem_blk_2_wrt
  589.     add    ax,cs:mem_blk_table.siz[bx]
  590.                     ; bx := ptr to memory block table
  591.                     ; ax := sector within the block
  592.  
  593.     cmp    cs:mem_blk_table.typ[bx],em_flg; Expanded Memory?
  594.     jnz    wrt_1st_cnv_blk
  595.  
  596.     call    save_em_context        ; save expanded mem context
  597.     call    get_em_PF_seg        ; get em page frame segment
  598.     push    ax            ; save sector within block
  599.     and    ax,sec_per_em_pag_msk    ; ax := sec within em page
  600.     push    ax            ; save sec within em page
  601.     mov    cl,par_per_sec_lg2    ; paragraphs per sector log 2
  602.     shl    ax,cl            ; ax := paragraph within em page
  603.     add    ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
  604.     mov    es,ax
  605.     xor    di,di            ; es:di := address of 1st sector
  606.     mov    cx,sec_per_em_pag
  607.     pop    ax            ; get sector within em page
  608.     sub    cx,ax            ; cx := secs remaining in em page
  609.     pop    ax            ; get sector within block
  610.     push    cx            ; save secs remaining in em page
  611.     sub    ax,cs:mem_blk_table.siz[bx]
  612.     neg    ax            ; ax := sectors remaining in block
  613.     dec    ax
  614.     mov    cl,sec_per_em_pag_lg2    ; adjust to em pages
  615.     shr    ax,cl
  616.     inc    ax            ; ax := em pages remaining in blk
  617.     pop    cx            ; get sectors remaining in em page
  618.     call    map_em_pag        ; map EM to log mem
  619.     jmp    wrt_one_pag
  620.  
  621. wrt_1st_cnv_blk:
  622.     push    ax            ; save sector within block
  623.     mov    cl,par_per_sec_lg2    ; paragraphs per sector log 2
  624.     shl    ax,cl            ; ax := paragraph within memory block
  625.     add    ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
  626.     mov    es,ax
  627.     xor    di,di            ; es:di := address of sector
  628.  
  629.     mov    cx,cs:mem_blk_table.siz[bx] ; cx := end sector
  630.     pop    ax            ; ax := start sector
  631.     sub    cx,ax            ; cx := secs remaining in mem blk
  632.     mov    ax,1            ; ax := # of pages remaining
  633.  
  634. wrt_one_pag:
  635.     push    ax            ; save pages remaining count
  636. wrt_one_sector:
  637.     push    cx            ; save sector count for this blk
  638.     mov    cx,bytes_per_sec    ; 512 bytes per sector
  639.     rep    movsb            ; copy buffer to sector
  640.     pop    cx            ; cx := secs remaining in this blk+1
  641.     mov    ax,es
  642.     add    ax,par_per_sec        ; 32 paragraphs per sector
  643.     mov    es,ax
  644.     xor    di,di            ; es:di := address of next sector
  645.         mov    ax,ds            ; advance ds to next sector
  646.     add    ax,par_per_sec
  647.     mov    ds,ax
  648.     and    si,000fh        ; ds:si := normalized transfer address
  649.     dec    dx            ; decrement sector count
  650.     jz    wrt_finish        ; all done?
  651.     loop    wrt_one_sector        ; if not end of mem blk, do another
  652.  
  653.     pop    ax            ; ax := number of em pages left
  654.     dec    ax            ; all memory for this em handle gone?
  655.     jnz    wrt_next_em_pag
  656.  
  657. wrt_next_mem_blk:
  658.     add    bx,size mem_blk_table_entry
  659.     cmp    cs:mem_blk_table.typ[bx],em_flg
  660.     jnz    wrt_next_cnv_mem_blk
  661.     call    save_em_context
  662.     call    get_em_PF_seg
  663.     mov    ax,cs:mem_blk_table.siz[bx] ; ax := sectors in mem block
  664.     mov    cl,sec_per_em_pag_lg2    ; adjust to em pages in mem block
  665.     shr    ax,cl
  666.  
  667. wrt_next_em_pag:
  668.     call    map_em_pag        ; Map EM to logical mem
  669.     mov    es,cs:mem_blk_table.par[bx]
  670.     mov    cx,sec_per_em_pag
  671.     jmp    wrt_one_pag
  672.  
  673. wrt_next_cnv_mem_blk:
  674.     call    restore_em_context
  675.     mov    ax,1                ; ax := pages remaining
  676.     mov    es,cs:mem_blk_table.par[bx] ; es:di := start of new mem blk
  677.     mov    cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
  678.     jmp    wrt_one_pag
  679.  
  680. wrt_finish:
  681.     pop    ax            ; Clear page remaining count
  682.     call    restore_em_context    ; If needed, restore em context
  683. wrt_finish_2:
  684.     mov    ax,s_done        ; ax := transfer done
  685.     ret
  686.  
  687. write    endp
  688.  
  689. ; ---- Write and verify ----
  690.  
  691. write_verify    proc    near        ; normal write with read verify
  692.  
  693.     jmp    write            ; assume no errors
  694.  
  695. write_verify    endp
  696.  
  697. ; ==== Support Functions ====
  698. ;
  699. ; ----    Map a handle of Expanded Memory to a 16K logical page
  700.  
  701. ;    ENTRY:    BX =     pointer to Memory Block Table entry (in CS)
  702. ;        AX =     logical page number to map to (first page is 1)
  703. ;    EXIT:    Memory is mapped and paragraph address updated in entry.
  704. ;    USES:    Flags
  705.  
  706. map_em_pag    proc    near
  707.     push    ax
  708.     push    bx
  709.     push    dx
  710.  
  711.     mov    dx,cs:mem_blk_table.hdl[bx]    ; get handle of mem blk
  712.     mov    bx,ax                ; logical page number
  713.     dec    bx                ; relative to 0
  714.     mov    ah,emm_map_memory
  715.     mov    al,0                ; physical page number
  716.     int    usri_emm
  717.  
  718.     pop    dx
  719.     pop    bx
  720.     pop    ax
  721.     ret
  722. map_em_pag    endp
  723.  
  724. ; ----    Get Expanded Memory Page Frame Segment ----
  725.  
  726. ;    ENTRY:    BX =    pointer to Memory Block Table entry into which
  727. ;            PF segment is to be stored.
  728. ;    USES:    Flags
  729.  
  730. get_em_PF_seg    proc    near
  731.     push    ax
  732.     push    bx
  733.     mov    ah,emm_get_PFseg        ; get page frame segment
  734.     int    usri_emm
  735.     mov    ax,bx
  736.     pop    bx
  737.     mov    cs:mem_blk_table.par[bx],ax    ; store page frame segment
  738.     pop    ax
  739.     ret
  740. get_em_PF_seg    endp
  741.  
  742. ; ----    Save Expanded Memory Context ----
  743.  
  744. ;    Saves the Expanded Memory context.  Context is saved under given
  745. ;    handle.  Flag at "em_context_flg" is set accordingly.
  746. ;    ENTRY:    BX =     pointer to Memory Block Table entry containing
  747. ;            the handle underwhich to save context.
  748. ;    USES:    Flags
  749.  
  750. save_em_context        proc    near
  751.     cmp    cs:em_context_flg,0        ; em context saved yet?
  752.     jnz    save_em_context_xit        ; -- yes, nothing to do
  753.     push    ax
  754.     push    dx
  755.     mov    dx,cs:mem_blk_table.hdl[bx]    ; get handle of mem blk
  756.     mov    cs:em_context_hdl,dx        ; store handle underwhich saved
  757.     mov    ah,emm_sav_map
  758.     int    usri_emm
  759.     mov    cs:em_context_flg,1        ; flag as saved
  760.     pop    dx
  761.     pop    ax
  762. save_em_context_xit:
  763.     ret
  764. save_em_context        endp
  765.  
  766. ; ----    Restore expanded memory context, if needed. ---
  767.  
  768. ;    USES:    Flags
  769.  
  770. restore_em_context    proc    near
  771.     cmp    cs:em_context_flg,0    ; was context saved?
  772.     jz    restore_em_context_xit    ; - No, then just exit
  773.     push    ax
  774.     push    dx
  775.     mov    ah,emm_res_map        ; - Yes, then restore it..
  776.     mov    dx,cs:em_context_hdl    ;   ..using this handle
  777.     int    usri_emm
  778.     mov    cs:em_context_flg,0    ; clear context flag
  779.     pop    dx
  780.     pop    ax
  781. restore_em_context_xit:
  782.     ret
  783. restore_em_context    endp
  784.  
  785. ; ---- Unimplemented functions ----
  786.  
  787. unimplemented    proc    near
  788.     mov    ax,e_command
  789.     ret
  790. unimplemented    endp
  791.  
  792. ioctl_read    equ    unimplemented    ; IOCTL read
  793. check_input    equ    unimplemented    ; non-destructive read/status
  794. input_status    equ    unimplemented    ; input status
  795. input_flush    equ    unimplemented    ; flush input buffers
  796. output_status    equ    unimplemented    ; output status
  797. output_flush    equ    unimplemented    ; flush output buffers
  798. ioctl_write    equ    unimplemented    ; IOCTL write
  799.  
  800. ; ==== Memory Disk Data Area ====
  801. ;
  802. ; Align to paragraph boundary for easy computation of the sector address
  803.  
  804. if (( $ - driver) mod 16 )
  805.     org    ( $ - driver) + (16 - (( $- driver) mod 16 ))
  806. endif
  807.  
  808. mdisk_data    equ    $        ; memory disk starts here
  809.  
  810. ; ---- Boot Sector ----
  811.  
  812. boot_record    db    3 dup ( 0 )    ; non-bootable (no jump instruction )
  813.         db    'AMDISK4 '    ; identification
  814.  
  815. bpb:                    ; BIOS Parameter Block
  816. bytes_in_sector    dw    bytes_per_sec    ; bytes/sector
  817. bpb_sec_per_cl    db    def_sec_per_cl    ; sectors/cluster
  818. bpb_reserved    dw    1        ; reserved sectors
  819. bpb_fats    db    fats_per_disk    ; number of FAT's
  820. bpb_root    dw    128        ; directory entries in root
  821. bpb_total    dw    def_size_sec    ; total number of sectors
  822. bpb_media    db    1        ; media dexcriptor byte ...
  823. mem_blk_cnt    equ    bpb_media    ; ...is used for number of mem blocks
  824. bpb_fat_size    dw    sec_per_fat    ; sectors/FAT
  825.  
  826.         dw    1        ; sectors/track
  827.         dw    1        ; number of heads
  828.         dw    0        ; hidden sectors
  829.  
  830. mem_blk_table    mem_blk_table_entry <nor_flg,,def_size_sec,0>  ; memory block table
  831. end_mem_blk_table equ    $+(size mem_blk_table)*(max_mem_blks)
  832.  
  833. ; ----    NOTE: From here on down, code is overwritten during initialization
  834.  
  835. fat1        equ    boot_record + bytes_per_sec
  836. fat2        equ    fat1 + (bytes_per_sec*sec_per_fat)
  837. root_dir    equ    fat1+(fats_per_disk*sec_per_fat*bytes_per_sec)
  838. first_data_sec    equ    root_dir + (128*32)
  839. min_cluster_siz    equ    1
  840. max_cluster_siz    equ    8
  841. init_sectors    equ    (first_data_sec - boot_record)/bytes_per_sec
  842. min_size_sec    equ    init_sectors + max_cluster_siz    ; Must be 1 data cluster
  843. min_size_K    equ    (min_size_sec + sec_per_K - 1)/sec_per_K
  844.  
  845. size_string    db    7,'MINSIZE'
  846. count        dw    ?
  847. delims        db    cc_sp,cc_ht,cc_cr
  848. num_delims    =    offset $ - offset delims
  849. size_error    db    cc_cr,cc_lf,cc_bel
  850.         db    'Illegal MINSIZE= clause in CONFIG.SYS for '
  851.         db    'device AMDISK'
  852.         db    cc_cr,cc_lf,'Defaulting to 64K',cc_cr,cc_lf,'$'
  853.  
  854. cluster_string    db    7,'CLUSTER'
  855. cluster_error    db    cc_cr,cc_lf,cc_bel
  856.         db    'Illegal CLUSTER= clause in CONGIG.SYS for '
  857.         db    'device AMDISK'
  858.         db    cc_cr,cc_lf,'Defaulting to 1',cc_cr,cc_lf,'$'
  859.  
  860.  
  861. ; ----  Skip over white space in a string.
  862. ;    ENTRY:  ES:DI ==> string
  863. ;    EXIT:   ES:DI ==> first non-white character
  864.  
  865. skip_white    proc    near
  866.     cmp    byte ptr es:[di],' '
  867.     jz    skip_white_1
  868.     cmp    byte ptr es:[di],cc_ht
  869.     jz    skip_white_1
  870.     ret
  871. skip_white_1:
  872.     inc    di
  873.     jmp    skip_white
  874. skip_white    endp
  875.  
  876. ; ----  MATCH ----
  877. ;    Scans a string to locate a second string
  878. ;    ENTRY:    DS:SI ==> string to locate.  The first byte of this string
  879. ;              is the length of the string
  880. ;        ES:DI ==> string to search
  881. ;    EXIT:    zero flag set if string found
  882. ;          ES:DI ==> char after match string
  883. ;        zero flag unset if string not found
  884. ;          ES:DI unchanged
  885. ;    USES:    none
  886.  
  887. match    proc    near
  888.     push    ax
  889.     push    es
  890.     push    di
  891.  
  892. ;    Prepare for search
  893.  
  894.     lodsb            ; Get count and save it
  895.     sub    ah,ah
  896.     mov    count,ax
  897.     mov    al,ds:[si]    ; Get first char to match
  898.  
  899. ;    Try to match the first character in the search string
  900.  
  901. match1:
  902.     cmp    byte ptr es:[di],cc_cr    ; At end of string?
  903.     jz    match4            ; -- Yes, exit
  904.     cmp    al,es:[di]        ; -- No, does first char match?
  905.     jz    match2            ;    -- Yes, try to match string
  906.     inc    di            ;    -- No, try next character
  907.     jmp    match1
  908.  
  909. ;    First character matches, try to match string
  910.  
  911. match2:
  912.     push    si            ; Save old pointers in case no match
  913.     push    di
  914.     mov    cx,count
  915.     repz    cmpsb            ; Check if strings match
  916.     jz    match3            ; -- Yes, exit
  917.     pop    di            ; -- No, restore pointers
  918.     pop    si
  919.     inc    di            ; Bump past match character
  920.     jmp    match1            ; Try to match first character again
  921.  
  922. ;    String was matched, clean up and set 'Z' flag
  923.  
  924. match3:
  925.     add    sp,8        ; Get rid of garbage on the stack
  926.     sub    ax,ax        ; Ensure Zero flag set
  927.     pop    ax
  928.     ret
  929.  
  930. ;    String was not matched, restore registers and clear 'Z' flag
  931.  
  932. match4:
  933.     sub    al,al
  934.     inc    al
  935.     pop    di
  936.     pop    es
  937.     pop    ax
  938.     ret
  939.  
  940. match    endp
  941.  
  942. ;    GET_DECIMAL - Get a decimal number
  943. ;
  944. ;    ENTRY:    ES:DI pointer to string
  945. ;
  946. ;    EXIT:    AX - Number (errors return as 0)
  947. ;
  948. get_decimal    proc    near
  949.  
  950.     push    cx
  951.     mov    cx,10
  952.  
  953. gd0:    push    bx
  954.     push    dx
  955.  
  956.     sub    bx,bx        ; BX will hold number
  957.  
  958. ;    Get character and turn it into a digit
  959.  
  960. gd1:
  961.     mov    al,es:[di]    ; Get a character
  962.     call    delim        ; Check if delimiter
  963.     jz    gd3        ; -- Yes, exit
  964.     sub    al,'0'        ; Make it a number
  965.     js    gd2        ; Error if below 0
  966.     cmp    al,cl        ; Check if it is above the maximum digit
  967.     jae    gd2        ; Error, invalid character
  968.     inc    di        ; Character good, advance pointer
  969.  
  970. ;    Update number
  971.  
  972.     xchg    ax,bx        ; Prepare for shifting number
  973.     sub    dx,dx
  974.     mul    cx
  975.     sub    bh,bh        ; Add new digit to old number
  976.     add    bx,ax
  977.     adc    dx,0        ; Check for overflow
  978.     jz    gd1        ; No, return for more digits
  979.  
  980. ;    Error, not a valid number. Return 0
  981.  
  982. gd2:
  983.     sub    bx,bx        ; Yes, return 0
  984.  
  985. gd3:
  986.     mov    ax,bx        ; Return value in AX
  987.     pop    dx
  988.     pop    bx
  989.     pop    cx
  990.     ret
  991.  
  992. get_decimal    endp
  993.  
  994. ;    DELIM - Check for delimiter character
  995. ;
  996. ;    ENTRY:    ES:DI points to character to check
  997. ;
  998. ;    EXIT:    'Z' set, char was delimiter
  999. ;        'Z' clear, char was not a delimiter
  1000. ;
  1001.  
  1002. delim    proc    near
  1003.  
  1004.     push    cx
  1005.     push    di
  1006.     push    es
  1007.     push    cs
  1008.     pop    es
  1009.  
  1010.     mov    di,offset delims
  1011.     mov    cx,num_delims
  1012.     repnz    scasb
  1013.  
  1014.     pop    es
  1015.     pop    di
  1016.     pop    cx
  1017.     ret
  1018.  
  1019. delim    endp
  1020.  
  1021. ; ----     Look for and parse MINSIZE= option from DEVICE= line in CONFIG.SYS.
  1022. ;    The resulting size in sectors is placed at locations
  1023. ;    "mem_blk_table.siz" and at "bpb_total".
  1024. ;
  1025. ;    ENTRY:  ES:BX ==> DOS request packet
  1026. ;        DS = CS
  1027. ;    USES:
  1028.  
  1029. parse_size    proc    near
  1030.     push    es
  1031.     push    bx
  1032.     cld
  1033.     les    di,es:[bx+rh_cmd]
  1034.     call    skip_white
  1035.     mov    si,offset size_string
  1036.     call     match
  1037.     jnz    parse_size_exit
  1038.     call    skip_white        ; Skip white space
  1039.     cmp    byte ptr es:[di],'='    ; Make sure an equals sign is there
  1040.     jnz    ds2            ; No, error do no size
  1041.     inc    di            ; Skip over =
  1042.     call    skip_white        ; Skip white
  1043.     call    get_decimal        ; Get a decimal number
  1044.  
  1045. ;    Ensure correct AMDISK size
  1046.  
  1047.     cmp    ax,min_size_K        ; Is disk less than minimum
  1048.     jb    ds2            ; Yes, Notify user
  1049. ds1:
  1050.     cmp    ax,max_isize_K        ; No, is disk greater than max. 
  1051.     jbe    ds3            ; No, skip
  1052.  
  1053. ;    Invalid MDISK size, notify user.
  1054.  
  1055. ds2:
  1056.     mov    dx,offset size_error
  1057.     mov    ah,dosf_outstr
  1058.     int    dosi_dosf
  1059.     jmp    parse_size_exit
  1060.  
  1061. ;    Convert size to sectors and store it.
  1062.  
  1063. ds3:
  1064.     mov    cl,sec_per_K_lg2
  1065.     shl    ax,cl
  1066.     mov    mem_blk_table.siz,ax
  1067.     mov    bpb_total,ax
  1068.  
  1069. parse_size_exit:
  1070.     pop    bx
  1071.     pop    es
  1072.     ret
  1073.     
  1074. parse_size    endp
  1075.  
  1076. ; ----     Look for and parse CLUSTER= option from DEVICE= line in CONFIG.SYS.
  1077. ;    The resulting sectors per cluster is placed at location
  1078. ;    bpb_sec_per_cl
  1079. ;
  1080. ;    ENTRY:  ES:BX ==> DOS request packet
  1081. ;        DS = CS
  1082. ;    USES:
  1083.  
  1084. parse_cluster    proc    near
  1085.     push    es
  1086.     push    bx
  1087.     cld
  1088.     les    di,es:[bx+rh_cmd]
  1089.     call    skip_white
  1090.     mov    si,offset cluster_string
  1091.     call     match
  1092.     jnz    parse_cluster_exit
  1093.     call    skip_white        ; Skip white space
  1094.     cmp    byte ptr es:[di],'='    ; Make sure an equals sign is there
  1095.     jnz    pc3            ; No, error do no cluster
  1096.     inc    di            ; Skip over =
  1097.     call    skip_white        ; Skip white
  1098.     call    get_decimal        ; Get a decimal number
  1099.  
  1100. ;    Ensure correct AMDISK cluster size
  1101.  
  1102.     cmp    al,min_cluster_siz    ; Is disk less than minimum
  1103.     jb    pc3            ; Yes, notify user
  1104. pc1:
  1105.     cmp    al,max_cluster_siz    ; No, is disk greater than max. 
  1106.     ja    pc3            ; Yes, notify user
  1107.     mov    bl,al
  1108. pc2:
  1109.     sal    bl,1            ; If power of 2, then...
  1110.     jnc    pc2            ;   when a bit is shifted out..
  1111.     and    bl,bl            ;   the rest is all 0s
  1112.     jz    pc4    
  1113.  
  1114. ;    Invalid MDISK cluster size, notify user.
  1115.  
  1116. pc3:
  1117.     mov    dx,offset cluster_error
  1118.     mov    ah,dosf_outstr
  1119.     int    dosi_dosf
  1120.     jmp    parse_cluster_exit
  1121.  
  1122. ;    Store cluster size in BPB.
  1123.  
  1124. pc4:
  1125.     mov    bpb_sec_per_cl,al
  1126.  
  1127. parse_cluster_exit:
  1128.     pop    bx
  1129.     pop    es
  1130.     ret
  1131.     
  1132. parse_cluster    endp
  1133.  
  1134. ; ----    Store volume label into directory ----
  1135. ;
  1136. ;    ENTRY:    ES = CS
  1137. ;    USES:    SI,DI,DS,CX
  1138.  
  1139. volume_label    db    'ADJRAM v4.1'
  1140.         db    00001000b    ; attr = volume label
  1141.         db    10 dup (0)
  1142.         dw    0110000000000000b ; time = 12:00:00
  1143. ;                       hhhhhmmmmmmsssss
  1144.         dw    0001000010101111b ; date = 15May88
  1145. ;                       yyyyyyymmmmddddd  yr=yyyyyyy+1980
  1146.         dw    0        ; start cluster
  1147.         db    4 dup (0)    ; size
  1148.  
  1149. enter_vol_label    proc    near
  1150.     push    es
  1151.     pop    ds
  1152.     lea    si,volume_label        ; ds:si ==> volume label
  1153.     lea    di,root_dir        ; es:di ==> directory
  1154.     mov    cx,32
  1155.     rep    movsb
  1156.     ret    
  1157. enter_vol_label    endp
  1158.  
  1159. ; ==== End of Memory Disk Device Driver ====
  1160.  
  1161. driver        endp
  1162. cseg        ends
  1163.         end
  1164.  
  1165.