home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / mbug / mbug097.arc / DB512.MAC < prev    next >
Text File  |  1979-12-31  |  17KB  |  683 lines

  1. ;
  2. ;        512k ENHANCED BIOS
  3. ;        DISK CACHE ROUTINES
  4. ;
  5. ;        (c) Peter Broughton.
  6. ;
  7. ;        Sector buffering of 0k to 128k.
  8. ;
  9. ;        28th Dec., 1987
  10. ;
  11.  
  12.  
  13.  
  14. ; If the following is true then two seperate lists will be kept.
  15. ; 1 for normal sectors, 1 for dir sectors (which have a higher priority)
  16. ; At the moment (26/1/88) this code isn't debugged
  17. dir_list    equ    false
  18.  
  19. ; number of bytes per hash table entry
  20. ; #0 = backwards pointer, #1 = forewards pointer,
  21. ; #2 = { b6, b7 = disk number (0..3), b0..b4 = sector number (0..31) }
  22. ;    if #2 = 0FFh (b5) then this sector is unused,
  23. ;    if #2 = 0FEh then this sector is unusable (no corresponding page)
  24. ; #3 = track number
  25. bytes_per_entry        equ    4
  26.  
  27. db_back_ptr        equ    0
  28. db_fore_ptr        equ    1
  29. db_d_s_ptr        equ    2
  30. db_track_ptr        equ    3
  31.  
  32. ; the hash table has max. 256 entries so occupies 1024 bytes = 2 sectors
  33. ; so first available sector (entry) is actually #2
  34. ; this also means that there will be 
  35. ; 8 spare bytes at the start of 'dbuff page 1'
  36. first_used_sector    equ    2
  37.  
  38. db_unused        equ    0FFh
  39. db_unusable        equ    0FEh
  40.  
  41. ; 2 bytes / disk (initialised to 0FFh) :
  42. ;    first gives number of system tracks,
  43. ;    second gives number of system + dir tracks
  44. ;    system tracks = 0FEh for unbufferable
  45. ; This uses the 8 spare bytes in the first buffer page
  46. dir_tracks_on_a        equ    0
  47.     if    (first_used_sector * bytes_per_entry) ne 8
  48.      error    Not enough room for track table
  49.     endif
  50.  
  51. ; initialise disk buffer
  52. db_init:
  53. ; don't bother if no pages set for dbuff
  54.     ld    hl,dbuff_pages
  55.     ld    a,(hl)
  56.     dec    a
  57.     ret    m
  58.  
  59.     push    af
  60. ; select first disk buffer page ie. page with hash table
  61.     dec    hl            ; dbuff after mdrive, ndrive
  62.     ld    a,(hl)
  63.     inc    hl
  64.     inc    hl
  65.     add    a,(hl)
  66.     add    a,3            ; and after TPA, system page
  67.     ld    (db_page_num_1),a    ; First db page (number)
  68.     call    mem_page
  69.     ld    (db_page_1),a        ; store it for later use
  70.     pop    af
  71.  
  72. ; calculate number of sectors available,
  73. ; with 512 bytes / sector this gives 64 sectors / page
  74.     rrca
  75.     rrca
  76.     or    64-1
  77. ; actually pages*64-1 gives number of last used entry
  78. ; pages*64-2 is the total number of entries
  79.     dec    a
  80.     ld    (db_entries),a
  81.  
  82. ; now make all entries to 'unused' value
  83. ; Also sets track table to 'unknown' value
  84.     ld    hl,0
  85.     ld    de,1
  86.     ld    bc,+(bytes_per_entry * 256)-1
  87.     ld    (hl), db_unused
  88.     ldir
  89.  
  90. ; Now must make up linked list.
  91. ; First entry is initially assigned to dir list and points to itself.
  92. ; Remaining entries must be linked in one big long list.
  93. ; IX --> address of entry, B = loop counter,
  94. ; H = backwards pointer, L = forewards pointer
  95. ; DE = bytes_per_entry (incr. of IX)
  96. ; first and last entries and handled differently
  97.     ld    h,a
  98.     inc    h    ; first entry in normal list points back to last entry
  99.     sub    3    ; count 3 less
  100.     ld    b,a
  101.     ld    ix,first_used_sector * bytes_per_entry
  102.     ld    de,bytes_per_entry
  103.     ld    l,first_used_sector
  104. ; first dir entry
  105.     ld    (ix),l        ; points to itself
  106.     ld    (ix+1),l
  107. ; first normal entry points forward to next entry
  108.     inc    l
  109.     inc    l
  110.     add    ix,de
  111. ; first entry
  112.     ld    (ix),h        ; backwards to last entry
  113.     ld    (ix+1),l    ; forewards to next
  114.     add    ix,de
  115.     ld    h,first_used_sector+1
  116.     inc    l
  117. db_i_1:
  118.     ld    (ix),h        ; entry's backwards pointer
  119.     ld    (ix+1),l    ;  "      forewards  "
  120.     inc    h
  121.     inc    l
  122.     add    ix,de
  123.     djnz    db_i_1
  124. ; last entry
  125.     ld    (ix),h
  126.     ld    (ix+1),first_used_sector+1    ; forewards to first entry
  127. ; finished
  128.     map    bios_map
  129.     ret
  130.  
  131.  
  132.  
  133. ; disk write has just been done, so new sector must be copied into
  134. ; next available entry
  135. db_disk_write:
  136.     push    hl
  137.     push    de
  138.     push    bc
  139.     ld    a,(db_page_1)
  140.     out    (map_port),a
  141. ; first find if this sector is already there, if it is then link it in
  142. ; at most recently used position and keep the same entry as next to be used
  143. ; returns nz if not found so must get address, page of next sector
  144.     call    db_find_and_relink
  145. ; note that if this is a dir sector then this routine will steal an
  146. ; entry from the normal list and add it to the dir list
  147.     call    nz,db_get_next_entry
  148. ; both calls return with correct page selected and with hl, de and bc ready
  149. ; for write to buffer
  150. ; so put sector into buffer
  151.     ldir
  152. ; Z is not necessary but saves code
  153.     jr    db_ret_z
  154.  
  155.  
  156. ; disk read is about to be done so check if sector is already buffered
  157. ; if it is then make it most recently used and return Z to say
  158. ; that the sector does not have to be read from disk
  159. db_disk_preread:
  160.     push    hl
  161.     push    de
  162.     push    bc
  163.     ld    a,(db_page_1)
  164.     out    (map_port),a
  165. ; find if this sector is already there, if it is then link it in
  166. ; at most recently used position and keep the same entry as next to be used
  167.     call    db_find_and_relink
  168. ; returns NZ if sector not found so just return and let disk read go ahead
  169.     jr    nz,db_ret_nz
  170. ; if it is then then correct page has been selected and HL, DE and BC are ready
  171.     ex    de,hl
  172. ; copy buffer to sector
  173.     ldir
  174. ; successful so return Z
  175. db_ret_z:
  176.     pop    bc
  177.     pop    de
  178.     pop    hl
  179.     map    bios_map
  180.     xor    a
  181.     ret
  182.  
  183. ; return with NZ
  184. ; assumes already NZ
  185. db_ret_nz:
  186.     pop    bc
  187.     pop    de
  188.     pop    hl
  189.     map    bios_map
  190.     ret
  191.  
  192. ; sector was not already buffered so it has just been read
  193. ; so must now buffer it
  194. db_disk_postread:
  195.     push    hl
  196.     push    de
  197.     push    bc
  198.     ld    a,(db_page_1)
  199.     out    (map_port),a
  200. ; find next sector
  201. ; returns with page selected, HL, DE and BC loaded for write to buffer
  202.     call    db_get_next_entry
  203. ; copy sector to buffer
  204.     ldir
  205.     jr    db_ret_z
  206.  
  207.  
  208.  
  209. ; Search the list for specified sector, if not found then return NZ.
  210. ; If found then return Z ready for a write to sector,
  211. ; (if read required then EX DE,HL).
  212. ; Returns :
  213. ;    HL = hstbuf, DE = address of found sector
  214. ;    BC = length of sector (512 bytes only may be sufficient)
  215. ;    correct page selected
  216. db_find_and_relink:
  217.     push    ix
  218. ; first get disk, sector and track number in appropriate format for search
  219. ; H has disk/sector, L has track
  220.     call    db_get_sect_param
  221. ; now do search of table, numeric not link order
  222.     ld    ix,+(first_used_sector * bytes_per_entry)
  223.     ld    a,(db_entries)
  224.     ld    b,a
  225.     ld    de,bytes_per_entry
  226. db_f_1:
  227.     ld    a,(ix+2)    ; disk/sector
  228.     cp    l
  229.     jr    nz,db_f_2
  230.     ld    a,(ix+3)    ; track
  231.     cp    h
  232.     jr    z,db_f_fnd    ; matches !!
  233. ; doesn't match
  234. db_f_2:
  235.     add    ix,de
  236.     djnz    db_f_1
  237. ; reached last entry without match, return with NZ
  238.     pop    ix
  239.     or    -1
  240.     ret
  241.  
  242. ; found a match, now comes the hard part
  243. db_f_fnd:
  244.     pop    ix
  245. ; convert loop counter's value to entry number and save
  246.     ld    a,(db_entries)
  247.     add    a,first_used_sector
  248.     sub    b
  249.     call    remove_and_relink
  250. ; there done !!!
  251. db_get_sect_addr:
  252. ; This section requires that HL --> somewhere within entry for sector
  253. ; for which address and page is required
  254. ; Convert to page in A (and selected) and sector address in DE
  255. ; 64 entries (256 bytes) per page so H has relative page number
  256.     ld    a,(db_page_num_1)
  257.     add    a,h
  258. ; select it
  259.     call    mem_page
  260. ; L has number of entry within page (*4), convert this to address
  261.     ld    d,l
  262.     srl    d    ; 512 bytes / sector
  263.     res    0,d
  264.     ld    e,0
  265. ; load other values for block move
  266.     ld    hl,hstbuf
  267.     ld    bc,512
  268. ; return Z for successful
  269.     xor    a
  270.     ret
  271.  
  272.  
  273. ; Wish to buffer a new sector, therefore get next one in list and
  274. ; move 'next sector' to next entry in list.
  275. ; If this is a directory sector then try to steal an entry
  276. ; from the normal list.
  277. ; This routine will always successfully return a sector address.
  278. ; This means that even if this is a dir sector it will always leave
  279. ; at least one entry in the normal list.
  280. ; Returns :
  281. ;    HL = hstbuf, DE = address of found sector
  282. ;    BC = length of sector (512 bytes only may be sufficient)
  283. ;    correct page selected
  284. db_get_next_entry:
  285.     if    dir_list
  286. ; if dir then try to steal entry from normal list
  287.     ld    a,(db_is_dir)
  288.     or    a
  289.     jr    nz,db_get_new_dir
  290.     endif
  291. ; Is a normal sector so just get the next available entry in the normal list
  292.     ld    a,(db_next_sector)
  293. ; get address of this entry
  294.     call    db_num_to_addr
  295.     inc    hl        ; entry's forward pointer
  296. ; move next sector pointer foreward along list
  297.     ld    a,(hl)
  298.     ld    (db_next_sector),a
  299. ; Must get to this point with HL --> (address of ENTRY for cache) +1
  300. db_setup_entry:
  301. ; now make entry point to disk sector it is buffering
  302.     ex    de,hl
  303. ; get track in D, disk & sector in E
  304.     call    db_get_sect_param
  305.     ex    de,hl
  306.     inc    hl
  307.     ld    (hl),e        ; disk & sector
  308.     inc    hl
  309.     ld    (hl),d        ; track
  310. ; turn this into a sector address and page and return
  311. ; requires entry address ( + 0..3 ) in HL
  312.     jr    db_get_sect_addr
  313.  
  314.     if    dir_list
  315.  
  316. ; Attempt to steal an entry from the normal list and put it into the dir list
  317. db_get_new_dir:
  318. ; First check if first in dir list is unused
  319.     ld    a,(db_next_dir_sector)
  320.     call    db_num_to_addr
  321.     inc    hl
  322.     inc    hl
  323.     ld    a,(hl)
  324.     dec    hl        ; HL must be left --> entry address +1
  325.     cp    db_unused        ; Is unused
  326.     jr    z,db_use_this_dir    ; so use it
  327.     ex    de,hl        ; Address required later
  328. ; Get next 'available' entry in normal list
  329.     ld    a,(db_next_sector)
  330.     ld    b,a
  331.     call    db_num_to_addr    ; Get entry address
  332.     inc    hl
  333.     ld    a,(hl)        ; Forwards pointer
  334.     cp    b        ; If it points to itself then don't steal
  335.     jr    z,db_dont_steal
  336. ; Move next normal sector pointer along one
  337.     ld    (db_next_sector),a
  338.     ld    a,b
  339. ; Remove it from normal list and link into dir list before next dir entry
  340.     call    remove_and_relink
  341. ; Now have an available entry to buffer dir sector so prepare to buffer
  342.     jr    db_setup_entry
  343.  
  344. ; Normal entry wasn't available to be stolen (very unlikely)
  345. ; so just move along dir list
  346. db_dont_steal:
  347.     ex    de,hl
  348. ; Goes to here if first entry in dir list is marked as unused
  349. db_use_this_dir:
  350.     ld    a,(hl)        ; Forward pointer
  351.     ld    (db_next_dir_sector),a    ; Move to next
  352.     jr    db_setup_entry
  353.  
  354.     endif
  355.  
  356. ; remove entry (A) from present position and relink before next sector
  357. ; return HL --> entry (A) address +1
  358. ; all regs. destroyed
  359. remove_and_relink:
  360.     ld    b,a
  361. ; remove it from it's present position, ie. link before and after together
  362.     call    db_num_to_addr
  363.     ld    d,(hl)        ; entry before
  364.     inc    hl
  365.     ld    e,(hl)        ; entry after
  366. ; link entry before to one after
  367.     ld    a,d
  368.     call    db_num_to_addr
  369.     inc    hl
  370.     ld    (hl),e
  371. ; link entry after to one before
  372.     ld    a,e
  373.     call    db_num_to_addr
  374.     ld    (hl),d
  375. ; link it in before 'next sector'
  376. ; ie. foreward link to next entry, backward link to same entry
  377. ; that next entry links to
  378.     if    dir_list
  379. ; First check if it dir or normal
  380.      ld    a,(db_is_dir)
  381.      or    a
  382.      ld    a,(db_next_dir_sector)    ; Use dir list
  383.      jr    nz,db_rr_1
  384.      ld    a,(db_next_sector)    ; Use normal list
  385. db_rr_1:
  386.     else
  387.      ld    a,(db_next_sector)
  388.     endif    
  389.     ld    d,a
  390.     call    db_num_to_addr
  391.     ld    c,(hl)        ; entry before next entry
  392.     ld    (hl),b        ; now points back to this entry
  393.     ld    a,c
  394.     call    db_num_to_addr    ; previous entry
  395.     inc    hl
  396.     ld    (hl),b        ; now point to this entry
  397.     ld    a,b
  398.     call    db_num_to_addr    ; get address of this entry
  399.     ld    (hl),c        ; point back to previous entry
  400.     inc    hl
  401.     ld    (hl),d        ; point forward to next entry
  402.     ret
  403.  
  404.  
  405. ; Purge all entries for drive [c]
  406. ; This is used during seldsk to ensure that the reboot
  407. ; actually gets new data from the new disk.
  408. ; This also kills the deblocking buffer.
  409. ; All regs preserved except A.
  410. ; Must also flag number of system, dir+system tracks as 'unknown' (0FFh).
  411. purge_c:
  412.     push    hl
  413.     push    bc
  414.  
  415.     call    db_common_purge
  416.  
  417. ; Flag track numbers unknown
  418.     ld    a,0FFh
  419.     ld    (hl),a
  420.     inc    hl
  421.     ld    (hl),a
  422. ; now purge all tracks < 255 (A)
  423.     jr    db_purge_1
  424.  
  425.  
  426. ; Purge all directory entries for drive [c]
  427. ; This is used before the BDOS delete/rename function is performed
  428. ; to ensure that we don't write bad data on a changed disk
  429. ; This also kills the deblocking buffer
  430. ; All regs preserved except a
  431. purgedir_c:
  432.     push    hl
  433.     push    bc
  434.  
  435.     call    db_common_purge
  436.  
  437.     inc    hl
  438.     ld    a,(hl)        ; Get number of dir + system tracks
  439.  
  440. ; purge all tracks < (A) on disk (C)
  441. db_purge_1:
  442. ; Go through in numeric order (not list order), find sectors from
  443. ; disk (C), track < (A), mark them as 'unused' 
  444. ; and relink them as new 'next sector' in normal list
  445.     push    ix
  446.     push    de
  447.     rrc    c        ; convert disk num. to MS bits
  448.     rrc    c
  449.     ld    h,a        ; check < this track
  450.     if    dir_list
  451.      xor    a
  452.      ld    (db_is_dir),a    ; So entries are relinked into normal list
  453.     endif
  454.     ld    ix,first_used_sector * bytes_per_entry
  455.     ld    de,bytes_per_entry
  456.     ld    a,(db_entries)
  457.     ld    b,a            ; loop counter
  458.     ld    l,first_used_sector    ; entry number
  459. db_p_1:
  460.     ld    a,(ix+2)    ; disk/sector
  461.     cp    db_unused
  462.     jr    z,db_p_2
  463.     and    0C0h        ; mask out sector number
  464.     cp    c        ; is this disk ?
  465.     jr    nz,db_p_2
  466.     ld    a,(ix+3)
  467.     cp    h        ; track
  468.     jr    nc,db_p_2
  469. ; disk matches and track is within range
  470. ; so mark as unused and relink as next sector
  471.     ld    (ix+2),db_unused
  472. ; don't relink if this is the next dir sector
  473. ; ie. must keep at least one (maybe unused) entry in dir list
  474.     if    dir_list
  475.      ld    a,(db_next_dir_sector)
  476.      cp    l
  477.      jr    z,db_p_2
  478.     endif
  479.     push    hl
  480.     push    de
  481.     push    bc
  482.     ld    a,l
  483.     call    remove_and_relink
  484.     pop    bc
  485.     pop    de
  486.     pop    hl
  487.     ld    a,l
  488.     ld    (db_next_sector),a    ; make it next sector
  489. db_p_2:
  490.     inc    l
  491.     add    ix,de
  492.     djnz    db_p_1
  493.  
  494.     pop    de
  495.     pop    ix
  496.     pop    bc
  497.     pop    hl
  498.     map    bios_map
  499.     ret
  500.  
  501. ; Routines common to the start of both purge routines
  502. db_common_purge:
  503. ; Empty host buffer
  504.     call    hst_flush
  505.     xor    a
  506.     ld    (hstact),a
  507.     ld    a,(dbuff_pages)
  508.     or    a
  509.     jr    z,db_p_ret_1    ; cache is disabled anyway so leave now
  510.     ld    a,(db_page_1)
  511.     out    (map_port),a
  512. ; Point HL to number of system/dir tracks on disk (C)
  513.     if    dir_tracks_on_a eq 0
  514.      ld    h,0
  515.      ld    l,c
  516.      sla    l
  517.     else
  518.      ld    b,0
  519.      ld    hl,dir_tracks_on_a
  520.      add    hl,bc
  521.      add    hl,bc
  522.     endif
  523.     ret
  524.  
  525. db_p_ret_1:
  526.     pop    bc        ; remove call to this sub
  527.     pop    bc        ; Restore BC, HL
  528.     pop    hl
  529.     ret            ; return from call to purge
  530.  
  531.  
  532. ; Return Z if buffering permissible.
  533. ; ie. NZ if buffering disabled or wrong disk type.
  534. ; If system sector then NZ.
  535. ; If dir sector then Z and flag.
  536. ; If normal sector then Z.
  537. db_sector_type:
  538.     ld    a,(dbuff_pages)
  539.     or    a
  540.     jr    z,db_cant_buff_1
  541.     push    hl
  542.     push    de
  543. ; See if buffering disabled for this disk
  544.     ld    a,(hstdsk)
  545.     ld    hl,cache_map
  546.     ld    e,a
  547.     ld    d,0
  548.     add    hl,de
  549.     ld    a,(hl)
  550.     or    a
  551.     jr    nz,db_cant_buff    
  552. ; now see if track is system or dir track
  553.     ld    a,(db_page_1)
  554.     out    (map_port),a
  555. ; make HL --> number of system, dir+system tracks for (hstdsk)
  556.     if    dir_tracks_on_a eq 0
  557.      rlc    e
  558.      ex    de,hl
  559.     else
  560.      ld    hl,dir_tracks_on_a
  561.      rlc    e        ; 2 bytes per disk
  562.      add    hl,de
  563.     endif
  564.     ld    a,(hl)
  565. ; if 0FFh then the number of ... tracks on this disk is not known
  566.     cp    0FFh    
  567.     call    z,db_get_num_dir    ; so find it out
  568. ; if 0FEh then it is an unbufferable disk
  569.     cp    0FEh
  570.     jr    z,db_cant_buff
  571.     ld    a,(hsttrk)
  572.     cp    (hl)        ; is system ?
  573.     jr    c,db_cant_buff    ; don't buffer system
  574.     if    dir_list
  575.      inc    hl
  576.      cp    (hl)        ; is dir ?
  577.      ld    a,true        ; flag dir track (sector)
  578.      jr    c,db_s_t_1
  579.      xor    a        ; flag normal track
  580. db_s_t_1:
  581.      ld    (db_is_dir),a    ; 'is directory sector' flag
  582.     endif
  583.     pop    de
  584.     pop    hl
  585. ; can buffer so return Z
  586.     map    bios_map
  587.     xor    a
  588.     ret
  589.  
  590. ; can't buffer so return NZ
  591. db_cant_buff:
  592.     pop    de
  593.     pop    hl
  594.     map    bios_map
  595. db_cant_buff_1:
  596.     dec    a    ; NZ since A = 0 or bios_map (0Ch)
  597.     ret
  598.  
  599.  
  600. ; calculate number of dir + system tracks on hstdsk
  601. ; HL --> 2 bytes : (number of system tracks),(number of dir + system tracks)
  602. ;    correct values are loaded into these
  603. ; must restore all regs. except A and DE
  604. ; Also returns A containing (hl)
  605.  
  606. db_get_num_dir:
  607.     push    ix
  608.     ld    a,(hstdsk)
  609.     call    x_tab        ; Get dpb addr in IX
  610.     push    ix
  611.     pop    de
  612.     ld    a,low spare_dpb
  613.     cp    e
  614.     ld    a,0FEh
  615.     jr    z,db_gn_cant
  616. ; Get number of system tracks
  617.     ld    a,(ix+13)
  618. db_gn_cant:
  619.     ld    (hl),a
  620.     ld    e,a
  621.     inc    hl
  622. ; Get number of dir tracks
  623.     ld    d,(ix+7)    ; Get number of highest dir entry
  624.     xor    a
  625.     rl    d        ; High bit of num dir
  626.     rla
  627.     inc    a    ; <=127 dir = 1 track, 128..255 dir = 2 tracks
  628.     add    a,e        ; add number of system tracks
  629.     ld    (hl),a
  630.     dec    hl
  631.     ld    a,e
  632.     pop    ix
  633.     ret
  634.  
  635.     
  636. ; convert entry number in A to entry address in HL
  637. db_num_to_addr:
  638.     ld    h,0
  639.     ld    l,a
  640. ; multiply by bytes_per_entry to get address
  641.     if    bytes_per_entry eq 4
  642.      sla    l
  643.      rl    h
  644.      sla    l
  645.      rl    h
  646.     else
  647.      error    bytes_per_entry <> 4 so fix this code
  648.     endif
  649.     ret
  650.  
  651. ; load H with track number, L with disk and sector numbers
  652. db_get_sect_param:
  653.     ld    hl,(hstdsk)    ; H = hsttrk, L = hstdsk
  654.     rrc    l        ; rotate disk number to b6,b7
  655.     rrc    l
  656.     ld    a,(hstsec)    ; now OR in sector
  657.     or    l
  658.     ld    l,a
  659.     ret
  660.  
  661.  
  662. ; all data storage here
  663.  
  664. db_page_1:    ds    1
  665.  
  666. db_page_num_1:    ds    1
  667.  
  668. ; next sector in normal list
  669. db_next_sector:
  670.     db    first_used_sector+1
  671.  
  672.     if    dir_list
  673. ; next sector in dir list
  674. db_next_dir_sector:
  675.     db    first_used_sector
  676.     endif
  677.  
  678. db_entries:    ds    1    ; maximum number of entries (pages*64-2)
  679.  
  680.     if    dir_list
  681. db_is_dir:    ds    1    ; next sector read/write is dir
  682.     endif
  683.