mirror of
https://github.com/elliotnunn/supermario.git
synced 2025-02-19 18:30:32 +00:00
3219 lines
122 KiB
Plaintext
3219 lines
122 KiB
Plaintext
;
|
||
; File: DiskCache.a
|
||
;
|
||
; Contains: an LRU cache of disk blocks
|
||
;
|
||
; Written by: Larry Kenyon
|
||
; Major rewrite for 7.0 by: Kenny SC. Tung
|
||
;
|
||
; Copyright: © 1985-1993 by Apple Computer, Inc., all rights reserved.
|
||
;
|
||
; Change History (most recent first):
|
||
;
|
||
; <SM4> 11/23/93 rab In the previous checkin a !forROM conditional was added to
|
||
; FlushCache to strip out a check for the Mac+ ROM. Unforutnately
|
||
; the conditional was added a few lines too soon and eliminated
|
||
; all of the logic which decided if the cache should be flushed
|
||
; based on CacheFlag and noFlushBit. The result was that the cache
|
||
; always flushed which causes a performance hit. Moved the
|
||
; conditional and added a bra fcExit so things will work like they
|
||
; used to.
|
||
; <SM3> 11/18/92 RB Made some changes forROM so that we have the disk cache code in
|
||
; ROM and not installed as a patch from the system file. This code
|
||
; is now installed as the cache from ROM, but we do not prevent
|
||
; the file system cache from being installed (yet), so we will let
|
||
; this cache do cleanup.
|
||
; <24> 12/3/91 KST Clean up the equates. Remove cbHqAge and use cbHqDevNum
|
||
; consistently.
|
||
; <23> 7/9/91 JSM Remove obsolete SysVers conditionals, turn off a label unless
|
||
; ktDebugCache is defined, remove include of GestaltEqu.a.
|
||
; <22> 6/12/91 LN added #include 'InternalOnlyEqu.a'
|
||
; <21> 2/13/91 KST dfn, #notabug: Took out _debugger trap.
|
||
; <20> 1/15/91 KST With Scott T.B: Changed "verifyCacheSize". Don't reserve 32K for
|
||
; others.
|
||
; <19> 1/2/91 KST With Scott T.B. Removed the cache limit imposed by <13>. This
|
||
; fix is a coordinated effort between Boot Mgr, VM, and Cache Mgr.
|
||
; <18> 10/31/90 KST Fixing a bug in Init700cacheQs.
|
||
; <17> 10/29/90 KST Make DiskCache.a build for Sys606.
|
||
; <16> 10/29/90 KST Fixed a bug that can't mount CD ROM drive with 32K cache. I am
|
||
; now using logical AND to calculate the hash key. (code read by
|
||
; Bill Bruffey).
|
||
; <15> 9/22/90 KST Used in BigBang build. (Didn't specify this file is for which
|
||
; build and BBS checked it in.)
|
||
; <14> 9/22/90 KST Save D0 across TrashVBlks call for MountVol. Yes D0.
|
||
; <13> 9/20/90 KST For B1, DiskCache can only have 256K bytes maximum because of
|
||
; BufPtr problem under VM and 24 bit mode.
|
||
; <12> 9/18/90 KST Fixing a bug in clearMem which should do LSR.L not LSR.W
|
||
; <11> 8/31/90 KST Put empty buffers at the end of free queue, non-empty buffers at
|
||
; the front. GetBlock will always take a buffer from the end of
|
||
; the queue.
|
||
; <10> 8/28/90 KST Make fCFreeBit override fCTrash option.
|
||
; <9> 8/27/90 KST Adding new options to RelBlock and FlushCache calls for the new
|
||
; DiskCache scheme.
|
||
; <8> 8/27/90 KST RelBlock should trash the block for bad mount.
|
||
; <7> 8/6/90 KST Some documentation changes.
|
||
; <6> 8/6/90 KST Major rewrite for 7.0. See description below.
|
||
; <5> 7/26/90 gbm dirtyBit conflicts with PaletteMgr... changing it to
|
||
; cacheDirtyBit (also in DiskCachePriv.a)
|
||
; <4> 6/29/90 gbm make 7.0 use cache size setting, no matter what cacheEnabled
|
||
; says
|
||
; <3> 2/4/90 DNF Add include of FileMgrPrivate.a
|
||
; <2> 12/28/89 dba made this a MAIN instead of a PROC so we get dead code
|
||
; stripping; made CacheDebug default to 0; got rid of OKToAdjust
|
||
; routine which always returned true
|
||
; <1.3> 11/14/89 KST Patch 1.2 change for 7.0 forgot to set up Oldbufptr. This fixes
|
||
; that problem.
|
||
; <1.2> 10/14/89 EMT For System 7: Allocate locals and buffers as per LoadPatches.a.
|
||
; Remove dynamic resizing as it is unavailable with MultiFinder.
|
||
; <1.1> 9/29/89 KST Changed "FlushCache" call to honor D1 option flag, ie. to trash
|
||
; buffers after flushing. This is compatible with RomCache and
|
||
; also fixes a problem that breaks "ExchangeFile".
|
||
; <1.0> 11/16/88 CCH Added to EASE.
|
||
; 12/14/87 JB (PMAB331) Changed setting of CurConfig so that re-sizing is
|
||
; attempted on each launch. If size change requested in
|
||
; CacheConfig is not attainable, then CurConfig is zeroed.
|
||
; 11/20/87 JB (PMAB331) Changed from using 'minApplZone' constant to
|
||
; 'CacheMinZn' variable for minimum appl zone size calculation.
|
||
; Added test of 'noCacheBit' in ioPosMode to allow file
|
||
; read/write's to bypass the cache on a per-request basis.
|
||
; Non-integral block sized data transfer will see cache activity
|
||
; for the tail of the transfer only.
|
||
; 12/11/86 LAK Fixed bug introduced in <02Dec86> fix which was trashing A0 for
|
||
; segloader. Qualify direct ROM address compares with a ROM
|
||
; version check (this code was needed to patch ROM 75 bugs). Made
|
||
; cache adjust size more robust a la CacheInstall.a code. Also do
|
||
; RestoreCaches routine after synchronization with the file
|
||
; system.
|
||
; 12/9/86 LAK Don't take over jSegStack (someone else may have already done
|
||
; this and we would be cutting them out). Use INITSegVect instead.
|
||
; 12/2/86 LAK Stop using file system stack during cache adjust. Sync with file
|
||
; system before adjusting buffer usage and do a low-level flush
|
||
; since flush at launch time might not be valid (an async write
|
||
; might have come thru afterwards).
|
||
; 7/23/86 DLD Converted source to MPW.
|
||
; 1/1/86 LAK Fixed bug in file-order queueing in GetBlock: if Lg2Phys call
|
||
; causes A5 block to be recycled for extents file, A5 is no longer
|
||
; valid. Check A5 validity by checking that element's queue ptr
|
||
; instead (this is simpler anyway). When buffers are put into the
|
||
; free queue, the file-order queue header is set to point to
|
||
; itself to avoid queueing confusions on later errors.
|
||
; 12/31/85 LAK Added check of back links in CacheCheck routine. Now call
|
||
; CacheCheck whenever we queue up a new element in GetBlock (for
|
||
; debug option).
|
||
; 12/9/85 LAK Added fix to TrashVBlks code: remember last queue element when
|
||
; calling TrashFCache since the latter routine now recycles blocks
|
||
; to the end of the queue.
|
||
; 12/8/85 LAK Fixed bug with flush-skipping: the flush should be done when
|
||
; called from FileRead (which is the read verify case) so that all
|
||
; writes are done at once rather than a flush for the in-place
|
||
; read and separate ones for any GetBlock calls for partial
|
||
; blocks. There was also an equate bug in GetBlock. When trashing
|
||
; a file queue, requeue the header at the end (TrashFCache) so
|
||
; GetBlock will recycle the empties first.
|
||
; 12/6/85 LAK Skip all flushes if CacheFlag is not set (see if this goes over
|
||
; ok). Set CacheFlag when calling FlushCache from CacheRdIP (we
|
||
; have to flush here). Minor coding speedup in FindFileCache. Set
|
||
; BufTgFNum to $CACD at getblock: this will show in tags of FEdit
|
||
; if block was not read from disk. Added version header.
|
||
; 12/4/85 LAK Added age field to each cache block (stamped into cache block at
|
||
; GetBlock exit), to be used in LRU block determination for
|
||
; recycling blocks. Increment age when new file queue is moved to
|
||
; MRU position. Changed buffer recycle routine to use this new
|
||
; field. Clear CacheCom at launch time. If noRWIPBit is set, don't
|
||
; cache ip requests. This can be set by Finder during disk copy
|
||
; I/O to prevent needless caching. Temporarily include new TFSEQU
|
||
; from cache- directory. Allocate file headers from cache space.
|
||
; Use old cache space for new cache buffers as well. Cache code is
|
||
; now located in cache space as well.
|
||
; 12/2/85 LAK Added caching of writeIP blocks. Put in patch of FlushMDB bug in
|
||
; RelBlock. Preserve D3 in CacheWrIP (fix that was lost). Moved
|
||
; RestoreCaches here to minimize size of INIT. Added file-order
|
||
; search at GetBlock for all but control files; the file position
|
||
; is remembered if the block is not found to avoid another search
|
||
; for file-order queueing the new block. Added caching of write IP
|
||
; blocks. Limit of r/w IP requests to cache is now dynamic, based
|
||
; upon size of cache (the larger the cache, the larger the request
|
||
; cached).
|
||
; 12/1/85 LAK Changed alternate queue to be in File order, instead of Disk
|
||
; order (since GetBlock, TrashBlock searches need file order).
|
||
; Added CheckCache routine for debug. Free Queue now has header
|
||
; backpointers in each block (for DQCacheBlks to use in
|
||
; determining to which queue an element belongs).
|
||
; 11/15/85 LAK New today (from TFS-Cache.Text, TFS-CacheIO.Text).
|
||
;
|
||
; Cache Install/Adjust
|
||
; Routines:
|
||
; CacheInstall/CacheInSub
|
||
; RestoreCaches/RestoreProc ------ cache remove code
|
||
; RestCVect
|
||
; CacheAdjust
|
||
; InstCVect
|
||
; EQCacheBufs
|
||
; DQCacheBufs
|
||
;
|
||
; Cache
|
||
; Routines: FlushCache - Flushes a cache queue to disk.
|
||
; GetBlock - Gets a specified disk block.
|
||
; MarkBlock - Marks a specified disk block dirty.
|
||
; RelBlock - Releases use of a specified disk block.
|
||
; TrashVBlks - Trashes all blocks belonging to a volume.
|
||
; TBStartSearch:
|
||
; TrashBlocks - Trashes a specified range of file blocks.
|
||
; TrashFBlocks- Trashes all blocks of a specified file.
|
||
; CacheRdIP - Multiblock read into user-buffer.
|
||
; CacheWrIP - Multiblock write from user-buffer.
|
||
;
|
||
; Internal
|
||
; Subroutines: TFSVCBTst - Checks A2 VCB for TFS/MFS.
|
||
; Lg2Phys - (ROM hook)
|
||
; WrBlocks - (ROM hook)
|
||
; RdBlocks - (ROM hook)
|
||
; ReadBlock
|
||
; WriteBlock
|
||
; BasicIO - (ROM hook)
|
||
;
|
||
; FindFileCache - Find cache queue for specified file.
|
||
; FlushFCache - Flush specified cache queue.
|
||
; TrashFCache - Trash specified cache queue (blks moved to free q).
|
||
; FreeCBlk - Move a cache block to free queue.
|
||
; EQCBlkDO - Enqueue a cache buffer in disk order.
|
||
; GetLPBuf - Return lowest-priority buffer.
|
||
;
|
||
; DQElement - Dequeue a queue element.
|
||
; EQElement - Enqueue a queue element at spec'd position.
|
||
; AddQToQ - Add all of a queue's blocks to another queue.
|
||
;
|
||
; MEMORY
|
||
;
|
||
; The cache code is loaded temporarily into the application heap by the cache INIT resource
|
||
; when caching has been enabled; this is done at launch time. The code is then copied
|
||
; to the top of the space allocated for caching (by moving BufPtr down).
|
||
;
|
||
; The remainder of the space under BufPtr is allocated to cache buffers. This may be
|
||
; adjusted at launch time if the user has changed the setting of the cache size.
|
||
;
|
||
; The old File System cache space in the System heap is retained; this is for two
|
||
; reasons: (1) it guarantees us space to restore the old cache if the user disables caching,
|
||
; and (2) the cache pointers have already been stored in VCBs and B-Tree control blocks.
|
||
; We might want to go patch these and move the old cache up under BufPtr (don't get rid
|
||
; of all our space when disabled) in another release. We do use the old cache space for
|
||
; additional new cache buffers, so it's not lost to the world . . .
|
||
;
|
||
; QUEUES
|
||
;
|
||
; All queues in this cache are doubly linked queues. A single queue is used for all
|
||
; 'empties': blocks when they are initially allocated and when they are freed up.
|
||
;
|
||
; There are 36 file queues, each caching blocks for a unique file fork; the headers for
|
||
; these file queues are in a File Queue, kept in MRU order:
|
||
;
|
||
; FileQHdr -> FQHead <--> FQ1 <--> FQ2 . . . . . FQ36 <--> (FQHead)
|
||
;
|
||
; Each file queue header is actually the header for two queues: a queue of its file blocks
|
||
; linked in file order, and a queue of its file blocks links in MRU order. The file
|
||
; system tries to allocate file blocks in disk order, and so the file block order approximates
|
||
; disk order; therefore, when blocks are flushed, they are flushed in the order they appear
|
||
; in this queue. Blocks are accessed by file block number, and because of the usually sequential
|
||
; accessing of a file, the file order queueing is also useful for searches.
|
||
;
|
||
; AGING OF CACHE BLOCKS
|
||
;
|
||
; Global age count is incremented whenever GetBlock file is not at MRU position; this keeps all
|
||
; r/w ip blocks at the same age, and generally all blocks of a file related to a particular
|
||
; operation (e.g., OpenResFile) at the same age. The current age is stamped on each buffer
|
||
; header when it is passed back from GetBlock. When there is a need to recycle buffers, those
|
||
; with an age less than or equal (earlier in time) than that of the LRU buffer in the LRU file
|
||
; with any buffers, are recycled. Note that the use of a global age rather than time or ticks
|
||
; prevents any screwup if someone happened to change those globals. By only incrementing age
|
||
; global when a file comes to the front of the queue, all r/w ip blocks for a particular request
|
||
; will get the same stamp and be recycled at the same time.
|
||
;
|
||
; CACHECOM
|
||
;
|
||
; The low-memory global CacheCom is cleared at launch time. The Finder can signal us there when file
|
||
; copy is being done by setting bit 7. This prevents unnecessary usage of the cache for the
|
||
; high-bandwidth copy/verify operations (I never cache r/w ip operations when the verify bit
|
||
; is set). By setting bit 6 (diskRdBit), GetBlock will be forced to go to disk for
|
||
; the block (this will cause things to go very slow).
|
||
;
|
||
; FILE TAGS
|
||
;
|
||
; File tags are supported. The extra 8 bytes for HD20 as well as the file number, file
|
||
; block number, and file attributes are kept in the file header. It may appear that tags
|
||
; are messed up when viewing blocks with FEdit or similar utilities: this is because the
|
||
; block may not actually be read from disk, but from the cache; these blocks will have
|
||
; 'CACD' stamped in the first longword of the tags so they may be distinguished. I considered
|
||
; formulating tags, but this is misleading because they may not really be on the disk.
|
||
; The CACHECOM diskRdBit may be set by these utilities to force a disk read.
|
||
;
|
||
;
|
||
;
|
||
; Cache To Do, Notes:
|
||
; * Put in some check for copy-protection: if read-data vector has been intercepted,
|
||
; read block from disk regardless (for drives 1 and 2).
|
||
; - Bump Sony timeout if on at GetBlock (extend power-off timeout) to avoid power-on
|
||
; penalty.
|
||
; - Make file-order searches smarter - figure distance to front or back block and
|
||
; search backwards when convenient.
|
||
; - Add better multiblock read/write support (need to group these blocks to avoid
|
||
; long linear searches . . .)
|
||
; - When Switcher is in, we really won't ever see any launch traps (launch is
|
||
; completely intercepted).
|
||
; - Don't call GetLRUBlk if we've max'ed out in our own cache queue. Recycle the last 8
|
||
; buffers or so. - let's wait on this one: we already have the limit on r/w ip request
|
||
; size to cache, and this may be enough of a throttle. Jazz seems to suck up about 256
|
||
; blocks or more and not suffer: it also does small r/w requests. With the new file-order
|
||
; search option and the better LRU recycling, it may not be so bad after all to keep a large
|
||
; number of blocks in a particular file cache. It might be reasonable to monitor the
|
||
; cache for a particular file and throttle it down by limiting the size of r/w IP blocks
|
||
; cached for it.
|
||
; - Add a checksum option for debug? this is fairly complicated since the file system
|
||
; will sometimes modify released blocks (only do for r/w ip blocks?).
|
||
; - Perhaps have a second queue for file headers in VCB order (and maybe file-order
|
||
; within a VCB, based on first file-order block within the queue). This would
|
||
; probably speed many flushing operations.
|
||
; - We could tighten up some loops: use word compare for preliminary check.
|
||
; - Add some other file priority than LRU; use file type? How about caching files in
|
||
; system folder with higher priority? We have access to a file's type and directory
|
||
; for TFS volumes in the FCB.
|
||
;
|
||
; type creator file flush at flushvol only
|
||
; CLIP MACS clipboard X
|
||
; ZSYS MACS system (refnum=2)
|
||
; FNDR MACS finder
|
||
; PRER LWRT laserwriter
|
||
; FNDR ERIK desktop
|
||
; APPL applications
|
||
; TEMP special for temp files? X
|
||
;
|
||
; To test:
|
||
; - test against monkey.
|
||
; - test with switcher, finder keeper, RAM Disk.
|
||
; - look at all uses of SP rather than A6 and verify that no async action can
|
||
; happen at that time.
|
||
; - try a number of copy-protected applications.
|
||
;_________________________________________________________________________________
|
||
|
||
; What's new for Sys7.0bc1: <Kenny SC. Tung>
|
||
; 1. Memory allocated by ROM is released.
|
||
; 2. Data buffers are separated from headers.
|
||
; 3. Data buffers will not cross page boundary.
|
||
; 4. Added new bit "fqDirtyBit" in file header to optimize FlushFCache and FlushVol.
|
||
; This is very essential because "FlushVol" calls "FlushCache" three times in a row
|
||
; (while 2 of them are unnecessary !!)
|
||
; and "FlushCache" calls "FlushFCache" 40 times !
|
||
; 5. translation cache to map data buffer => header
|
||
; 6. New dontInstRC bit in cachecom. (fsPrivate.a)
|
||
; 7. Adding the code to check upper bound limit against actual physical
|
||
; memory that Ramcache can allocate. Fixing bug BRC#62288.
|
||
; 8. When a file is closed, its buffers are released. They are put onto the free queue
|
||
; however, their volume level information, ie., volume refnum and disk block number,
|
||
; is preserved in the hope that the file will be used again. In this case, if the
|
||
; block is still in the free queue, we just remove it from the free queue and add it
|
||
; to the file queue. There is no disk access required. When a buffer is returned to
|
||
; the free pool, it is linked to the end of the queue. When we take a buffer from the
|
||
; free pool, the buffer is taken from the head of the free queue. To make the search
|
||
; of the free queue more efficient, a hash mechanism is used.
|
||
; 9. There is a mysterious anomaly using RamCache: If you increase the size of RamCache,
|
||
; it will degredate the performance of the file system. Myth had it that the linear
|
||
; search of GetBlock is the culprit to blame. As a matter of fact, the search algorithm
|
||
; is quite efficient. The real problem is in CacheRdIP. CacheRdIP will try to save
|
||
; blocks in the cache if the number of blocks to read falls within a limit. That limit
|
||
; is calculated based on the size of the cache. Which means the larger the cache size,
|
||
; more blocks are saved in cache. The problem happens here, CacheRdIP doesn't do multi-
|
||
; block read as it is advertized when it decides to save the blocks in cache, it does
|
||
; single block read thru GetBlock. As you can see, now a single disk access has fragamented
|
||
; into many disk accesses. Because disk access is a mechanical movement, the time it
|
||
; takes is significant. The degredation is even worse on some disks which doesn't do
|
||
; hadrware level disk cache.
|
||
; 10.Rewrote CacheRdIP to search the cache first, searching both the file queue and free queue.
|
||
; The previously said hashing scheme makes this feasible. If all blocks are already in the cache,
|
||
; data is copied into user's buffer. Note in this case, I don't remove buffers from the free queue
|
||
; and put them in the file queue.
|
||
; If during the search, once I found a block that is not in the cache, I will do multiple block
|
||
; read, starting from the missing block, directly into user's buffer and then copy the data from
|
||
; user's buffer to the cache. Of course we only need to copy iff the block is not in the cache.
|
||
; 11.So what's the big deal of rewriting these routines? You should now be able to "SEE" the difference
|
||
; between this DiskCache(7.0bc1) and DiskCache(7.0a12). A 7.0 system build with 1024K cache on
|
||
; MacII showed 30% performance improvement !!
|
||
;
|
||
; Todo:
|
||
; 1. Don't cache RAMDisk if the driver says so. Right now, there is no way to
|
||
; know the type of the disk. -- bad !!
|
||
; 2. Read ahead is a nice feature we should do, but without scatter/gather I/O
|
||
; this is impractical to implement it. (DMA will help too)
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
STRING ASIS
|
||
|
||
LOAD 'StandardEqu.d'
|
||
INCLUDE 'DiskCachePriv.a'
|
||
INCLUDE 'InternalOnlyEqu.a'
|
||
INCLUDE 'FileMgrPrivate.a'
|
||
|
||
if &type('CacheDebug') = 'UNDEFINED' then
|
||
CacheDebug: equ 0
|
||
endif
|
||
|
||
if &type('NewInit70') = 'UNDEFINED' then
|
||
NewInit70 equ 0 ; set if building '7.0 INIT'
|
||
endif
|
||
|
||
if &type('ktDebugCache') = 'UNDEFINED' then
|
||
ktDebugCache equ 0
|
||
endif
|
||
|
||
if &type('ktDebugHash') = 'UNDEFINED' then
|
||
ktDebugHash equ 0
|
||
endif
|
||
|
||
RAMCache PROC EXPORT ; <SM3> rb
|
||
|
||
; The following absolute addresses are needed to patch problems in the version 75 ROM:
|
||
|
||
FReadCall EQU $404F56 ; ROM75 FileRead return address (read-verify flush)
|
||
FlMDBCall EQU $402DD8 ; ROM75 FlushMDB return address (used for patch)
|
||
|
||
;_______________________________________;
|
||
;
|
||
; If we are compiling forROM, this code is simply part of the ROM code and not a <SM3> rb
|
||
; resource installed by a patch. <SM3> rb
|
||
;
|
||
; Otherwise: <SM3> rb
|
||
;
|
||
; The following code is made into
|
||
; a separate resource of type CACH.
|
||
; It is loaded by the CacheLoad (PTCH 0 in 7.0 world)
|
||
; routine of the cache INIT resource
|
||
; when caching is enabled.
|
||
;_______________________________________;
|
||
|
||
CacheStart
|
||
|
||
IF NOT forROM THEN ; <SM3> rb
|
||
|
||
BRA CacheInstall ; <06Dec85>
|
||
DC.L ('CACH') ; CACH resource <06Dec85>
|
||
DC.W 1 ; res is 1 <06Dec85>
|
||
DC.W 3 ; <17Apr90> version 3 <17Apr90>
|
||
ENDIF ; <SM3> rb
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: FlushCache
|
||
;
|
||
; Function: Flushes cache buffers for specified file or volume.
|
||
;
|
||
; Input: D0.W - file or volume refnum
|
||
; D1.B - option flags (default = don’t trash buffers after flush)
|
||
; fCTrash - trash buffers after flush
|
||
; fCfreeBit - trash buffers but save in the free queue hash
|
||
; A2.L - VCB ptr if D0=volume refnum
|
||
;
|
||
; Output: D0.W - result code (always zero)
|
||
;
|
||
; Called by: FileRead: (flush all blocks before read-verify) => has to be done for partial blks
|
||
; MyReadIP: (flush all blocks of a file before a read-in-place) => has to be done.
|
||
; Close, FlushFile: (flush all blocks of a file) => skippable
|
||
; FlushVol: (flush all volume blocks) => has to be done (CacheFlag=$FF for this case)
|
||
; BTFlush, BTClose: (flush control cache B-Tree blocks) => skip if possible (we flush too often)
|
||
; Revision History:
|
||
; 28Sep89 KSCT Make FlushCache to honor D1 option flag, ie. trash the cache.
|
||
; Note: 1. flush and trash can be done in one call on RamCache.
|
||
; 2. Only buffers are removed from FCQueue not the FileQHeader. Therefore,
|
||
; this call is for interim flush/trash (eg. ExchangeFile) not for FClose.
|
||
; 29Sep89 KSCT Some optimization: exit if no buffer cached.
|
||
; 28Aug90 KSCT Added fCfreeBit option.
|
||
;_________________________________________________________________________________
|
||
|
||
FlushCache
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L D1-D6/A1-A4,-(A6) ; save registers
|
||
MOVE D1,D5 ; save option flag <28Sep89>
|
||
MOVEA.L FileQHdr,A3 ; A3 = ptr(FQH)
|
||
MOVE.W D0,D1 ; D1 = refnum
|
||
BLE.S fcVolume ; br if volume flush
|
||
|
||
; Find the file cache for this file/fork (if any) and flush it.
|
||
|
||
fcFile MOVEA.L FCBSPtr,A1 ; A1 = FCB ptr
|
||
MOVEA.L FCBVPtr(A1,D1),A2 ; A2 = VCB ptr
|
||
MOVE.L FCBFlNm(A1,D1),D3 ; D3 = file number
|
||
BTST #FCBRscBit,FCBMdRByt(A1,D1) ; resource fork?
|
||
SNE D6 ; $FF=resource fork, $00=data fork
|
||
BSR FindFileCache ; get the file cache ptr in A4
|
||
BEQ fcExit ; exit if no blocks cached
|
||
|
||
TST FHBufCnt(A4) ; anything to flush? <29Sep89>
|
||
BEQ.S fcExit ; no -> (no buffer allocated) <29Sep89>
|
||
;; if cacheFlag <> 0, then force flush, (it really should be called FlushFlag)
|
||
;; if cacheFlag == 0, and this is NOT a control file then we always flush.
|
||
;; if cacheFlag == 0, and this IS a control file then we delay flushing.<24Apr90>
|
||
TST.B CacheFlag ; flushing time?
|
||
BNE.S @1 ; br if so (go flush)
|
||
BTST #noFlushBit,FHFlags(A4) ; avoid intermediate flushes? <08Dec85>
|
||
BEQ.S @1 ; br if not (go flush) <08Dec85>
|
||
bra.s fcExit ; exit if so (skip flush) <SM4>
|
||
|
||
IF NOT forROM THEN ; We don't need MacPlus ROM check <SM3> rb <SM4>
|
||
MOVE.L ROMBase,A3 ;<11Dec86> make sure we have the correct ROM
|
||
CMP.W #$0075,8(A3) ;<11Dec86>
|
||
BNE.S fcExit ;<11Dec86> exit if not
|
||
CMP.L #FReadCall,$28(A6) ; FileRead flush before verify? <08Dec85>
|
||
BNE.S fcExit ; exit if not (skip flush) <08Dec85>
|
||
ELSE ; <SM3> rb
|
||
; if FlushFlag is 0, then if this file is control file skip flushing
|
||
ENDIF ; <SM3> rb
|
||
|
||
@1 BSR FlushFCache ; flush the A4 cache
|
||
BRA.S fcExitTrash ; trash and exit <28Sep89>
|
||
|
||
; Flush all file caches belonging to this volume.
|
||
fcVolume
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
@1 MOVEA.L FHFlink(A4),A4 ; position to next file queue
|
||
CMPA.L A3,A4 ; back to top of queue ?
|
||
BEQ.S fcExit ; exit if so
|
||
CMPA.L FHVCBPtr(A4),A2 ; same volume?
|
||
BNE.S @1 ; no, continue search
|
||
BSR FlushFCache ; flush the queue
|
||
BRA.S @1
|
||
|
||
fcExitTrash ;; buffers flushed, trash them if requested so <28Sep89>
|
||
;; A1=FCB array, A2=VCB ptr, A4=FQheader, D1=refnum, D5=option <28Sep89>
|
||
;; If option = fCfree, we will free the buffers without trashing them. <28Aug90>
|
||
;; If option = fCtrash, we will give FClose a chance to keep the buffers <28Aug90>
|
||
BTST #fCfreeBit,D5 ; free and save in DHT requested? <28Aug90>
|
||
BNE.S @freeit ; yes, free the buffers <28Aug90>
|
||
BTST #FCtrash,D5 ; trash option requested? <28Sep89>
|
||
BEQ.S fcExit ; no, keep it around -> <28Sep89>
|
||
@freeit
|
||
MOVE D1,D0 ; D0=refnum,volume won’t branch to here <28Sep89>
|
||
MOVEQ #0,D1 ; D1=from block 0 <28Sep89>
|
||
MOVEQ #-1,D2 ; D2=to the end <28Sep89>
|
||
|
||
MOVE.L D7,-(SP) ; save D7 <04Jun90>
|
||
MOVEQ #0,D7 ; clear flag, really TRASH it <04Jun90>
|
||
BTST #fCfreeBit,D5 ; free and save in DHT requested? <04Jun90>
|
||
BNE.S @hashit ; yes <04Jun90>
|
||
|
||
;; fclose flushes and/or trashes cache, but we should keep them in freeq for reuse
|
||
;; Since FCtbSave bit is not implemented, I do a come from check: <22Aug90>
|
||
;; ** (This is really just a hack for the current cache interface) <22Aug90>
|
||
;; assume D1-D6/A1-A4 registers saved by this routine and <22Aug90>
|
||
;; A1(FCB)/D0.L(error code)/D1 registers saved by FClose on A6 stack. <22Aug90>
|
||
;; -- A1.L (52) FCB array <22Aug90>
|
||
;; -- D1.L (48) refnum <22Aug90>
|
||
;; -- D0.L (44) keep the buffers only if D0.L = 0 <22Aug90>
|
||
;; -- ......... <22Aug90>
|
||
;; A6 => low memory <22Aug90>
|
||
|
||
MOVE.L 52(A6),D5 ; D5 = FCBsptr <04Jun90>
|
||
CMPA.L D5,A1 ; D5 = FCBsptr? <04Jun90>
|
||
BNE.S @trashit ; no, not from FClose <04Jun90>
|
||
CMP.W 50(A6),D0 ; for this file? <22Aug90>
|
||
BNE.S @trashit ; no, <22Aug90>
|
||
TST.L 44(A6) ; error? <04Jun90>
|
||
BNE.S @trashit ; yes <04Jun90>
|
||
;; Now, we are from a successful FClose, save the buffers in the hash <22Aug90>
|
||
@hashit MOVE.L #SaveinHash,D7 ; save in DHT <04Jun90>
|
||
@trashit BSR TrashBlocks ; free all buffers but the fileQ header <28Sep89>
|
||
MOVE.L (SP)+,D7 ; restore D7 <04Jun90>
|
||
fcExit
|
||
IF CacheDebug THEN
|
||
BSR CacheCheck
|
||
ENDIF
|
||
MOVEM.L (A6)+,D1-D6/A1-A4 ; restore registers
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
MOVEQ #0,D0 ; never an error
|
||
RTS ; exit Flush Cache
|
||
IF ktDebugCache THEN
|
||
DC.B $80, 'FlushCache'
|
||
ALIGN
|
||
DC.W 0
|
||
ENDIF
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: GetBlock
|
||
;
|
||
; Function: Gets a specified disk block. The desired block may be
|
||
; specified either by file refnum and file block number, or by
|
||
; volume refnum and logical block number.
|
||
;
|
||
; Input: D0.W - file or volume refnum
|
||
; A2.L - VCB ptr if D0=volume refnum
|
||
; D1.B - option flags (default = read from disk if not found):
|
||
; GBRead - read from disk (forced read)
|
||
; GBnoRead - don’t read from disk if not found
|
||
; GBexist - get existing cache block
|
||
; GBrelease - release block immediately
|
||
; D2.L - file block number or logical block number
|
||
;
|
||
; Output: A0.L - addr(cache buffer) containing desired block
|
||
; D0.W - result code
|
||
; 0 = ok
|
||
; other = error
|
||
; Called By:
|
||
; TFSCommon:
|
||
; TFSRfn1:
|
||
; TFSRfn2:
|
||
; TFSVol:
|
||
; VSM:
|
||
; BTINTF:
|
||
; BTSVCS:
|
||
; BTALLOC:
|
||
; BTMAINT1:
|
||
; BTMAINT2:
|
||
;_________________________________________________________________________________
|
||
|
||
GetBlock
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L D1-D7/A1-A5,-(A6) ; save regs
|
||
MOVE.B D1,D7 ; D7 = option flags
|
||
MOVEA.L FileQHdr,A3 ; A3 = ptr(FQH)
|
||
MOVE.L #$CAC11ED1,BufTgFNum ; if we don’t read it, mark tag 'CAChED' <06Dec85>
|
||
|
||
MOVE.L CacheVars,A1 ; first <01Aug90>
|
||
CLR.W LcGBinCache(A1) ; clear the incache flag WORD <01Aug90>
|
||
|
||
MOVEQ #0,D3 ; use file number=0 for vol blks
|
||
MOVEQ #0,D6 ; and fork type 0
|
||
MOVE.W D0,D1 ; D1 = volume or file refnum?
|
||
BLE.S @1 ; br if vol refnum
|
||
|
||
MOVEA.L FCBSPtr,A1 ; A1 = ptr(1st FCB)
|
||
MOVEA.L FCBVPtr(A1,D1.W),A2 ; A2 = ptr(VCB)
|
||
|
||
MOVE.L FCBFlNm(A1,D1.W),D3 ; D3 = file number
|
||
BTST #FCBRscBit,FCBMdRByt(A1,D1.W) ; resource fork?
|
||
SNE D6 ; $FF=res fork, $00=data fork
|
||
|
||
; (1) Find appropriate file queue. If none, reallocate a free one
|
||
; or use LRU one. Requeue file queue header to keep MRU order.
|
||
|
||
@1 BSR FindFileCache ; get the file cache ptr in A4
|
||
BNE.S @2 ; br if we found it
|
||
MOVEA.L FHBlink(A3),A4 ; use LRU file cache
|
||
BSR FlushFCache ; flush the LRU one
|
||
BSR TrashFCache ; and put any blocks in the free queue
|
||
|
||
MOVE.L A2,FHVCBPtr(A4) ; initialize this file cache queue hdr
|
||
MOVE.L D3,FHFlNum(A4)
|
||
MOVE.B D6,FHFkType(A4)
|
||
|
||
TST.W D1 ; file?
|
||
BLE.S @2 ; br if not
|
||
MOVE.B FCBMdRByt(A1,D1),FHAttrib(A4)
|
||
|
||
BSR TFSVCBTst ; TFS volume?
|
||
BNE.S @2 ; br if not
|
||
|
||
MOVE.L FCBFType(A1,D1),FHFlType(A4) ; set Finder file type
|
||
MOVE.L FCBDirID(A1,D1),FHParDID(A4) ; ...and parent DirID
|
||
|
||
MOVEQ #FSUsrCNID,D0 ;
|
||
CMP.L D0,D3 ; control file?
|
||
BCC.S @2 ; br if not
|
||
BSET #mruSrchBit,FHFlags(A4) ; search for these in MRU order (we tend to
|
||
; cluster catalog requests for the same set of blocks)
|
||
BSET #noFlushBit,FHFlags(A4) ;<11Dec86> don’t flush until flush time
|
||
|
||
@2 CMP.L FQHFlink(A3),A4 ; already first?
|
||
BEQ.S gbSearchQ ; br if so
|
||
|
||
MOVE.L CacheVars,A0 ; since we had to requeue, time to increment
|
||
ADDQ.L #1,CacheAge(A0) ; the age of cache world . . .
|
||
|
||
BSR DQElement ; first ReQueue it to keep file
|
||
MOVEA.L A3,A0 ; queue in MRU order
|
||
BSR EQElement ;
|
||
|
||
; (2) Search queue for requested block, return it if found. We search in
|
||
; either MRU order (for control files), or in file order (from start
|
||
; or MRU block).
|
||
|
||
gbSearchQ
|
||
SUB.L A5,A5 ; set this to file-order position if not found
|
||
MOVE.B FHFlags(A4),D0 ; save file header flags
|
||
|
||
; Always look at MRU block in any case.
|
||
|
||
LEA FHMQHFlink(A4),A3 ; MRU queue header (set up for gbGetLPBuf)
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
MOVEA.L MCBFlink(A4),A4 ; position to next buffer, MRU order
|
||
CMPA.L A3,A4 ; empty queue?
|
||
BEQ.S gbMapBlk ; branch if so (have to map it)
|
||
CMP.L MCBFlBlk(A4),D2 ; same file block?
|
||
BEQ gbFoundIt ; br if so (we found it)
|
||
|
||
BTST #mruSrchBit,D0 ; search in MRU order?
|
||
BNE.S gbMRUSearch ; br if so
|
||
|
||
LEA MCBDQFlink(A3),A5 ; first queue position (for loop)
|
||
|
||
CMP.L MCBFlBlk(A4),D2 ; before MRU block?
|
||
BCC.S @1 ; br if not
|
||
|
||
MOVE.L A5,A4 ; Not the MRU block, but before it in file
|
||
BRA.S gbFOLoop ; order, so start from the beginning
|
||
;; After or equal to MRU block ...
|
||
@1 MOVE.L MCBDQBlink(A3),A4 ; last file-order buffer
|
||
CMP.L DCBFlBlk(A4),D2 ; before the last one?
|
||
BCS.S @2 ; br if so
|
||
BEQ.S gbFOFound ; br if it is the last one
|
||
;; After the last block => not in the cache. (A4 = last buffer)
|
||
MOVE.L A4,A5 ; want to queue it after the last one
|
||
BRA.S gbMapBlk ; have to map it
|
||
|
||
; Not before MRU block, or after last, so start search from MRU block
|
||
|
||
@2 MOVEA.L MCBFlink(A3),A4 ; get MRU block
|
||
LEA MCBDQFlink(A4),A4 ; get into file-order queue mode
|
||
|
||
gbFOLoop MOVE.L DCBFlink(A4),A4 ; get next buffer in file order
|
||
CMP.L A4,A5 ; reached start?
|
||
BEQ.S @1 ; br if so (we’ll have to map it)
|
||
CMP.L DCBFlBlk(A4),D2 ; after this block?
|
||
BHI.S gbFOLoop ; loop if so
|
||
|
||
BEQ.S gbFOFound ; br if we found it
|
||
|
||
@1 MOVE.L DCBBlink(A4),A5 ; queue after here
|
||
BRA.S gbMapBlk ; go map it
|
||
|
||
gbFOFound LEA -MCBDQFlink(A4),A4 ; point to buffer header
|
||
BRA gbFoundIt ; we found it!
|
||
|
||
; Search in MRU order until entire queue has been searched, or we find it.
|
||
|
||
gbMRUSearch
|
||
|
||
@1 MOVEA.L MCBFlink(A4),A4 ; position to next buffer, MRU order
|
||
CMPA.L A3,A4 ; back to top of queue?
|
||
BEQ.S gbMapBlk ; branch if so
|
||
CMP.L MCBFlBlk(A4),D2 ; same file block?
|
||
BNE.S @1 ; br if not (continue search)
|
||
BRA gbFoundIt ; br if so (we found it)
|
||
|
||
; (3) Map requested file block to disk block (since it was not in the
|
||
; cache, it will have to be read from the disk).
|
||
|
||
gbMapBlk
|
||
BTST #GBexist,D7 ; request for an existing block?
|
||
BEQ.S @1 ; br if not
|
||
MOVEQ #Chnotfound,D0 ; result = 'not found'
|
||
BRA gbFail ; exit
|
||
|
||
@1 MOVE.L D2,D3 ; MCBDBlk=MCBFlBlk for volume blocks
|
||
TST.W D1 ; mapping volume block?
|
||
BLE.S gbGetLPBuf ; br if so (clear A5 if recycle blk in this cache)
|
||
|
||
MOVE.L #BufSiz,D4 ; request 512 bytes
|
||
MOVE.L D2,D5 ; file block number
|
||
LSL.L #8,D5 ; file block number x 512
|
||
ADD.L D5,D5 ; ... = file position
|
||
|
||
; NOTE: (A1,D1)=FCB Ptr, A2=VCB ptr, D5=byte position (block boundary), D4=$200
|
||
|
||
BSR Lg2Phys ; get disk block in D3
|
||
MOVE.L CacheVars,A1 ; Lg2Phys could change the inCache flag <02Aug90>
|
||
CLR.W LcGBinCache(A1) ; clear the incache flag WORD <02Aug90>
|
||
; block has not been found yet <02Aug90>
|
||
BNE gbFail ; exit if we were unable to map block
|
||
|
||
; (4) Get lowest-priority block and recycle it.
|
||
|
||
gbGetLPBuf
|
||
BSR GetLPBuf ; get A4->cache buffer to reuse
|
||
|
||
;; need to store the fork type in CB hdr <13Jun90>
|
||
MOVE.B MQHFkType(A3),MCBFkType(A4) ; copy fork type <13Jun90>
|
||
|
||
MOVE.L D3,MCBDBlk(A4) ; disk block number
|
||
CLR.B MCBFlags(A4) ; set not empty, not dirty
|
||
BSET #inuseBit,MCBFlags(A4) ; set in use
|
||
|
||
MOVE.W D1,MCBRefNum(A4) ; file refnum
|
||
MOVE.L D2,MCBFlBlk(A4) ; file block number
|
||
|
||
MOVE.L A5,D0 ; file order pos already determined?
|
||
BEQ.S @1 ; br if not
|
||
|
||
LEA -MCBDQFlink(A5),A0 ; get pointer to MRU queue <01Jan86>
|
||
CMP.L A3,A0 ; file header? <01Jan86>
|
||
BEQ.S @3 ; br if so (we’re still ok) <01Jan86>
|
||
|
||
CMP.L DCBFQPtr(A5),A3 ; A5 buffer still in this cache queue? <01Jan86>
|
||
BNE.S @1 ; br if not (was recycled in Lg2Phys <01Jan86>
|
||
; mapping or by GetLPBuf) <01Jan86>
|
||
|
||
@3 LEA MCBDQFlink(A4),A4 ; adjust for regular queueing routines
|
||
MOVE.L A5,A0 ; queue up after this block
|
||
BSR EQElement ; queue up after A0 element
|
||
LEA -MCBDQFlink(A4),A4
|
||
BRA.S @2
|
||
|
||
@1 BSR EQCBlkFO ; queue A4 block up in file order
|
||
|
||
@2 MOVE.L A3,MCBFQPtr(A4) ; ptr to file queue head <01Jan86>
|
||
MOVE.L A3,A0
|
||
BSR EQElement ; put it at head of MRU queue
|
||
ADDQ.W #1,MQHBufCnt(A3) ; bump buffer count for this queue
|
||
|
||
IF CacheDebug THEN
|
||
MOVE.L CacheVars,A0 ; stamp with age for cachecheck routine <31Dec85>
|
||
MOVE.L CacheAge(A0),MCBAge(A4) ; <31Dec85>
|
||
BSR CacheCheck ; <31Dec85>
|
||
ENDIF
|
||
|
||
BTST #GBnoRead,D7 ; no-read requested ?
|
||
BNE gbReQueue ; ...yes, skip read ->
|
||
|
||
; (5) Read from disk into the chosen buffer.
|
||
|
||
gbReadBlk CLR.L BufTgDate ; always clear tag date
|
||
|
||
BSR ReadBlock ; read node from disk
|
||
BNE.S @1 ; exit on errors
|
||
|
||
; we scavenge the VCB write count whenever we read a block . . . this may be
|
||
; overkill (doing it at volume mount scan should be sufficient . . . )
|
||
|
||
BSR TFSVCBTst ; TFS volume?
|
||
BNE.S gbReQueue ; skip write-count scavenge if not
|
||
MOVE.L BufTgDate,D0 ; get tag write-count
|
||
CMP.L VCBWrCnt(A2),D0 ; make sure it’s <= VCB write count
|
||
BLS.S gbReQueue ; br if so
|
||
MOVE.L D0,VCBWrCnt(A2) ; update VCB write count if not . . .
|
||
BRA.S gbReQueue ; go re-queue the buffer ->
|
||
|
||
@1 ;; ST D7 ; D7.B = FF, don't save in DHT <04Jun90>
|
||
BSR FreeCBlk ; trash the block and put it in free Q
|
||
BRA gbFail ; take the error exit
|
||
|
||
; (6) Found requested buffer. Check for 'in-use' error. Check for
|
||
; forced read option.
|
||
|
||
gbFoundIt
|
||
MOVE.L CacheVars,A1 ; set the incache flag <01Aug90>
|
||
ST LcGBinFQueue(A1) ; buffer found in file queue <01Aug90>
|
||
BSET #inuseBit,MCBFlags(A4) ; buffer in use?
|
||
BEQ.S @1 ; ...no ->
|
||
_CacheDebug StrBufUse
|
||
MOVEQ #ChInUse,D0 ; error, buffer in use
|
||
BRA.S gbFail ; exit ->
|
||
|
||
@1 BTST #diskRdBit,CacheCom ; force read?
|
||
BNE.S @2 ; br if so
|
||
BTST #GBread,D7 ; forced read requested
|
||
BEQ.S gbReQueue ; br if not
|
||
|
||
; This option is only used for read-verify which flushes all blocks first.
|
||
;
|
||
; We still check for a dirty block for the 'forced-read' external option.
|
||
|
||
@2 BCLR #cacheDirtyBit,MCBFlags(A4) ; is it dirty? <08Dec85>
|
||
BEQ gbReadBlk ; just go read it if not
|
||
BSR WriteBlock ; otherwise, flush it first
|
||
BRA gbReadBlk ; then go read it again
|
||
|
||
; (7) Re-queue the buffer for MRU, then return at last.
|
||
|
||
gbReQueue ; A3 = File Header as MRU Queue Header
|
||
MOVE.L CacheVars,A0 ; x stamp with age
|
||
MOVE.L CacheAge(A0),MCBAge(A4) ; x
|
||
CMP.L MQHFlink(A3),A4 ; already first?
|
||
BEQ.S @1 ; br if so
|
||
BSR DQElement ; detach it
|
||
MOVEA.L A3,A0 ; put on top
|
||
BSR EQElement ; ...of queue
|
||
|
||
@1 BTST #GBrelease,D7 ; immediate release requested?
|
||
BEQ.S gbExitOK ; br if not
|
||
BCLR #inuseBit,MCBFlags(A4) ; set not in-use
|
||
|
||
gbExitOK MOVE.W D1,MCBRefNum(A4) ; label with latest file refnum (for debug only now)
|
||
|
||
; save the translation map <20Apr90>
|
||
MOVEA.L MCBDataPtr(A4),A0 ; it's a pointer now <20Apr90>
|
||
MOVEA.L CacheVars,A1 ; we have a new mapping <26Apr90>
|
||
MOVE.L A0,LcXCacheKey(A1) ; save the new key <26Apr90>
|
||
MOVE.L A4,LcXCacheData(A1) ; save the translation <26Apr90>
|
||
|
||
MOVEQ #0,D0 ; indicate no error
|
||
|
||
gbExit MOVEM.L (A6)+,D1-D7/A1-A5 ; restore regs
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set up condition codes
|
||
RTS ; exit GetBlock
|
||
|
||
IF ktDebugCache THEN
|
||
DC.B $80, 'GetBlock'
|
||
ALIGN
|
||
DC.W 0
|
||
ENDIF
|
||
|
||
gbFail MOVE.L MinusOne,A0 ; return bogus pointer (odd to incite
|
||
BRA.S GBExit ; address errors if used)
|
||
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: MarkBlock (Mark Block Dirty)
|
||
;
|
||
; Function: Marks a specified disk block dirty. This is really a special case
|
||
; of RelBlock in which the block is not released (though it may have
|
||
; already been released) but always marked dirty. This routine is
|
||
; synchronous.
|
||
;
|
||
; Input: A0.L - addr(cache buffer) containing disk block
|
||
;
|
||
; Output: none
|
||
;
|
||
; Called By: MarkA5Block: MFSDir1 (FndFilSpc - MFSCreate/ReName), MFSVol (CkFilLen,
|
||
; MFSFlush), TFSRfn1 (FileWrite), TFSRfn2 (MFSClose).
|
||
; MarkBlock: MFSDir2 (MFS delete), MFSDir3 (MFS Set/Reset file lock,
|
||
; SetFileInfo, SetFilType), VSM (5 times).
|
||
;_________________________________________________________________________________
|
||
|
||
MarkBlock
|
||
MOVE.L A4,-(SP) ; save buffer ptr <29May90>
|
||
|
||
; now we have pointer <29May90>
|
||
BSR mapCBHeader ; map buffer to header <29May90>
|
||
|
||
BSET #cacheDirtyBit,MCBFlags(A4) ; mark buffer dirty <29May90>
|
||
MOVEA.L MCBFQPtr(A4),A4 ; get its filequeue hdr <29May90>
|
||
BSET #fqDirtyBit,MQHFlags(A4) ; mark the filequeue dirty <29May90>
|
||
MOVEA.L (SP)+,A4 ; restore A4 <29May90>
|
||
RTS ; exit MarkBlock
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: RelBlock (Release Block)
|
||
;
|
||
; Function: Releases use of a specified disk block; optionally marks block dirty,
|
||
; trashes block, and/or writes block to disk. Note: this routine may be
|
||
; called for an already released block to mark it dirty, trashed, or to
|
||
; write it.
|
||
;
|
||
; Input: D1.B - option flags:
|
||
; RBdirty - mark buffer dirty
|
||
; RBtrash - trash buffer contents (set empty)
|
||
; RBwrite - force write buffer to disk
|
||
; RBfreeBit - trash buffers but save in the free queue hash
|
||
; A0.L - addr(cache buffer) containing disk block
|
||
; A1.L - pointer to cache queue header (if RBWrite specified)
|
||
; (not used in RamCache)
|
||
; Output: D0.W - result code
|
||
; 0 = ok
|
||
; other = error (can only occur if RBwrite option specified)
|
||
; Called by: BTIntf (D1=0, Control cache block)
|
||
; BTMaint2 (D1=0, Control cache block)
|
||
; MFSVol (D1=kRBTrash, A1 not set up) - trash empty dir blks on scan
|
||
; TFSCommon (D1=kRBTrash, A1 = volume cache queue)
|
||
; TFSRfn2 (D1=kRBDirty, A1 = volume cache queue) - Res fork block 0
|
||
; TFSVol (D1=kRBTrash, A1 = volume cache queue)
|
||
;
|
||
; Revision History:
|
||
; 27Aug90 KSCT Added RBfreeBit option.
|
||
;_________________________________________________________________________________
|
||
|
||
RelBlock
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVE.L A4,-(A6) ; save registers
|
||
|
||
IF NOT forROM THEN ; Not for the new ROMs <SM3> rb
|
||
|
||
; The next nine lines patch a bug in ROM 75 (FlushMDB never cleared dirty bits
|
||
; on control file FCBs . . .)
|
||
|
||
MOVE.L ROMBase,A4 ; make sure we have the correct ROM
|
||
CMP.W #$0075,8(A4)
|
||
BNE.S @4 ; br if not
|
||
CMP.L #FlMDBCall,4(A6) ; from FlushMDB?
|
||
BNE.S @4 ; br if not
|
||
|
||
MOVE.W VCBXTRef(A2),D0 ; Get extent file refNum
|
||
BCLR #FCBModBit,FCBMdRByt(A3,D0) ; clear the dirty flag
|
||
MOVE.W VCBCTRef(A2),D0 ; Get catalog file refNum
|
||
BCLR #FCBModBit,FCBMdRByt(A3,D0) ; clear the dirty flag
|
||
ENDIF ; <SM3> rb
|
||
|
||
@4
|
||
; now we have pointer to data buffer <23Apr90>
|
||
BSR mapCBHeader ; map buffer to header <23Apr90>
|
||
|
||
BCLR #inuseBit,MCBFlags(A4) ; set not-in-use
|
||
|
||
BTST #RBfreeBit,D1 ; free and save in DHT requested? <27Aug90>
|
||
BEQ.S @0 ; no <27Aug90>
|
||
MOVE.L D7,-(SP) ; save D7 <27Aug90>
|
||
MOVE.L #SaveinHash,D7 ; set flag <27Aug90>
|
||
BRA.S @1 ; use common code <27Aug90>
|
||
|
||
@0 BTST #RBtrash,D1 ; buffer to be trashed?
|
||
BEQ.S @2 ; br if not
|
||
MOVE.L D7,-(SP) ; save D7 <04Jun90>
|
||
MOVEQ #0,D7 ; clear flag, really TRASH it <04Jun90>
|
||
@1 BSR FreeCBlk ; trash the block and put it in free Q
|
||
MOVE.L (SP)+,D7 ; restore D7 <04Jun90>
|
||
BRA.S rbExit ; exit ok
|
||
|
||
@2 BTST #RBdirty,D1 ; buffer dirty?
|
||
BEQ.S @3 ; br if not
|
||
BSET #cacheDirtyBit,MCBFlags(A4) ; set dirty flag
|
||
|
||
MOVE.L A1,-(SP) ; free A1 <26Apr90>
|
||
MOVEA.L MCBFQPtr(A4),A1 ; get its filequeue hdr <26Apr90>
|
||
BSET #fqDirtyBit,MQHFlags(A1) ; mark the filequeue dirty <26Apr90>
|
||
MOVEA.L (SP)+,A1 ; restore A1 <26Apr90>
|
||
|
||
@3 BTST #RBwrite,D1 ; force write requested ?
|
||
BEQ.S RBExit ; br if not
|
||
BSR WriteBlock ; write A4 block to disk
|
||
BCLR #cacheDirtyBit,MCBFlags(A4) ; set cache buffer not dirty
|
||
BRA.S rbExit1 ; all done ->
|
||
|
||
rbExit MOVEQ #0,D0 ; indicate no error
|
||
|
||
rbExit1 MOVE.L (A6)+,A4 ; restore registers
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set up condition codes
|
||
RTS ; exit RelBlock
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: TrashVBlks
|
||
;
|
||
; Function: Trashes all buffers for the specified volume. When the volume is
|
||
; unmounted this is essential: recycled VCBs can cause real headaches
|
||
; otherwise. This routine is synchronous.
|
||
;
|
||
; Input: A2.L - VCB pointer
|
||
; A1.L - ignored
|
||
; Output: none
|
||
;
|
||
; Called By: UnMountVol (when VCB goes away), MountVol (errors), Eject/OffLine (it’s
|
||
; best not to keep the buggers around . . .).
|
||
; Modification history:
|
||
; 06Jun90 KST We now need to invalidate the free queue hash.
|
||
; 22Sep90 KST Need to preserve D0 across the call. MountVol depends on it???!!!
|
||
; Called by DsposVBlks.
|
||
;
|
||
; ToDo: Unmount calls FlushBuffers and it in turns calls TrashVBlks 3 times
|
||
; in a row. Since RamCache has only 1 cache buffer, 2 out of the 3 TrashVBlks
|
||
; calls are redundant. (Need to fix the high level code)
|
||
;_________________________________________________________________________________
|
||
|
||
TrashVBlks
|
||
MOVEM.L D0/D3/A3-A4,-(SP) ; save registers
|
||
|
||
MOVEQ #0,D3 ; D3 = 0, invalidate the whole volume <06Jun90>
|
||
BSR invalidDHash ; trash dev hash associated with this volume<06Jun90>
|
||
|
||
MOVEA.L FileQHdr,A3 ; A3 = ptr(FQH)
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
@1 MOVEA.L FHFlink(A4),A4 ; position to next file queue
|
||
CMPA.L A3,A4 ; back to top of queue ?
|
||
BEQ.S tvbExit ; exit if so
|
||
CMPA.L FHVCBPtr(A4),A2 ; same volume?
|
||
BNE.S @1 ; no, continue search
|
||
MOVE.L FHBlink(A4),-(SP) ; remember previous queue element <09Dec85>
|
||
BSR TrashFCache ; trash this file cache (recycles it to the end)<09Dec85>
|
||
MOVE.L (SP)+,A4 ; start from previous element <09Dec85>
|
||
BRA.S @1
|
||
|
||
tvbExit MOVEM.L (SP)+,D0/D3/A3-A4 ; restore registers
|
||
RTS ; exit TrashVBlks (ALL regs preserved)
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: TrashBlocks, TrashFBlocks
|
||
;
|
||
; Function: Trashes a specified range of disk blocks for a given file.
|
||
; TrashFBlocks trashes all blocks of a file. This routine is
|
||
; synchronous.
|
||
;
|
||
; TrashBlocks (Device hash table is trashed also depends on the D7 flag)
|
||
; Input: D0.W - file refnum
|
||
; D1.L - beginning file byte position (should be a 512-byte multiple . . .)
|
||
; D2.L - number of bytes (should be a 512-byte multiple or -1 to trash to end of file)
|
||
; A2.L - VCB ptr
|
||
; D7.L - If D7.L == 'hash' then add buffer to the hash table (used by freeCBlk) <04Jun90>
|
||
; Called by: FileWrite (write-in-place), SetEOF (truncating a file),
|
||
; CacheWrIP and FlushCache.
|
||
;
|
||
; TrashFBlocks (trash all cache things of the file)
|
||
; Input: D0.L - file number
|
||
; A2.L - VCB ptr
|
||
; Called by: FileDelete (TFS), MFSDelete.
|
||
;
|
||
; Output: none
|
||
; Modification history:
|
||
; 02Aug90 KST Now takes D7 as an input parameter for TrashBlocks.
|
||
;_________________________________________________________________________________
|
||
|
||
;TrashFBlocks
|
||
; MOVEM.L D0-D6/A0-A4,-(SP) ; save registers
|
||
; MOVE.L D0,D3 ; file number
|
||
; MOVEQ #0,D1 ; zero means trash all blocks
|
||
; BRA.S TBStartSearch ; go start the search
|
||
|
||
; NOTE: TrashBlocks down to TBStartSearch is needed by the CacheWrIP routine.
|
||
; D7.L is the hash flag and should not be used -- KSCT <08May90>
|
||
TrashBlocks
|
||
MOVEM.L D0-D6/A0-A4,-(SP) ; save registers
|
||
MOVEQ #9,D3 ; divide-by-512 factor
|
||
MOVE.L D1,D4 ; starting byte number
|
||
LSR.L D3,D4 ; D4 = beginning block #
|
||
|
||
MOVEQ #-1,D5 ;
|
||
CMP.L D2,D5 ; trash to end of file? (if D2=-1)
|
||
BEQ.S @1 ; br if so
|
||
MOVE.L D2,D5 ; number of bytes
|
||
ADD.L D1,D5 ; add in starting position
|
||
SUBQ.L #1,D5 ; adjust so we get last blk
|
||
@1 LSR.L D3,D5 ; 'divide' by 512 to get end block #
|
||
; (if D5 was -1, it is now $007FFFFF,
|
||
; largest possible file block number)
|
||
MOVE.W D0,D1 ; D1 = refnum
|
||
BLE tbErr ; exit if not a valid refnum
|
||
|
||
MOVEA.L FCBSPtr,A1 ; A1 = FCB ptr
|
||
MOVEA.L FCBVPtr(A1,D1),A2 ; A2 = VCB ptr
|
||
MOVE.L FCBFlNm(A1,D1),D3 ; D3 = file number
|
||
BTST #FCBRscBit,FCBMdRByt(A1,D1) ; resource fork?
|
||
SNE D6 ; $FF = resource fork, $00 = data fork
|
||
|
||
TBStartSearch ; common code for TrashBlocks, TrashFBlocks
|
||
MOVE.L FileQHdr,A3 ; file queue header
|
||
TST.W D1 ; trash all blocks?
|
||
BEQ.S tbDelete ; br if so . . . (special for FileDelete)
|
||
|
||
BSR FindFileCache ; search for file block cache
|
||
BEQ.S tbExit ; br if not found
|
||
LEA FHDQHFLink(A4),A3 ; traverse this queue in disk order
|
||
MOVE.L A3,A4 ; start at the top
|
||
|
||
; For resource files, SetEOF is a common action, and it should be more efficient to look
|
||
; at the last block in the queue and skip if the start block to trash is past this . . .
|
||
|
||
MOVE.L DCBBlink(A4),A4 ; point to last buffer
|
||
CMP.L A3,A4 ; empty queue?
|
||
BEQ.S tbExit ; exit if so
|
||
|
||
MOVE.L DCBFlBlk(A4),D0 ; D0 = file block #
|
||
CMP.L D4,D0 ; last block in file order < beginning block to trash?
|
||
BLT.S tbExit ; then skip all this nonsense
|
||
|
||
MOVE.L A3,A4 ; otherwise, start at the top (might want to look at MRU block?)
|
||
|
||
@1 MOVE.L DCBFlink(A4),A4 ; point to next buffer
|
||
CMP.L A3,A4 ; back to start?
|
||
BEQ.S tbExit ; exit if so
|
||
|
||
MOVE.L DCBFlBlk(A4),D0 ; D0 = file block #
|
||
CMP.L D4,D0 ; block < beginning block?
|
||
BLT.S @1 ; br if so, continue search
|
||
CMP.L D5,D0 ; block > ending block?
|
||
BGT.S tbExit ; exit if so
|
||
|
||
MOVE.L DCBBLink(A4),-(SP) ; save previous buffer ptr
|
||
LEA -MCBDQFlink(A4),A4 ; point to MRU queue header
|
||
;; D7.L is the hash flag, set to preserve buffer in DHT -- KSCT <08May90>
|
||
BSR FreeCBlk ; trash the block and put it in free queue
|
||
MOVE.L (SP)+,A4 ; start again with previous element
|
||
BRA.S @1 ; continue search
|
||
|
||
tbDelete ;; trash both forks
|
||
MOVEQ #-1,D6 ; look for resource fork first
|
||
|
||
@1 BSR FindFileCache ; search for file block cache
|
||
BEQ.S @2 ; br if none found
|
||
BSR TrashFCache ; trash this file cache
|
||
@2 NOT.B D6 ; try the other fork
|
||
BEQ.S @1 ; loop for data fork
|
||
;; file has been deleted, we should remove buffers associated with this file in DHT
|
||
BSR invalidDHash ; A2.L=VCB, A3.L=cachevars, D3.L=Filenum
|
||
BRA.S tbRet ; clean up the hash too
|
||
|
||
tbExit ;; clean up and exit
|
||
|
||
CMPI.L #SaveinHash,D7 ; user wants to save it in DHT?
|
||
BEQ.S tbRet ; yes, then it's all done
|
||
; else, remove from DHT
|
||
;; this happens at filedelete, setEOF, writeIP, and at bad fclose
|
||
BSR invDHTRange ; invalidate a range, A3.L = cachevars
|
||
|
||
tbRet
|
||
;; debug code here
|
||
tbErr
|
||
MOVEM.L (SP)+,D0-D6/A0-A4 ; restore registers
|
||
RTS ; exit TrashBlocks
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: CacheWrIP, CacheRdIP
|
||
;
|
||
; Function: Read/write a consecutive number of blocks from/to the disk. The I/O
|
||
; is done directly to/from the user’s buffer. This routine is in the
|
||
; cache to allow future caching schemes to buffer entire files if the
|
||
; room exists.
|
||
;
|
||
; Input: A0.L - user parameter block ptr
|
||
; (A1,D1.W) - FCB pointer
|
||
; D4.L - maximum number of bytes to read/write
|
||
; D5.L - current file position
|
||
; A2.L - VCB Ptr
|
||
;
|
||
; Output: D0.W - error code
|
||
; D3.L - trashed for readIP, preserved for writeIP
|
||
; D6.L - bytes actually read/written (set up by Lg2Phys)
|
||
; All other registers preserved
|
||
;
|
||
; Called by: FileRead,FileWrite
|
||
;
|
||
; By having these routines call Lg2Phys, it would be possible in the
|
||
; future to skip the call and deal with file block numbers in the cache
|
||
; (like GetBlock does), as well as do more than a consecutive block’s
|
||
; worth of I/O with one call (maybe even figure all blocks in advance
|
||
; and then do the I/O in disk order).
|
||
;
|
||
;
|
||
;_________________________________________________________________________________
|
||
|
||
; See if we want to go the cache route for this one.
|
||
|
||
IPLimCheck
|
||
MOVE.L A0,-(SP)
|
||
MOVE.L CacheVars,A0
|
||
MOVE.L CacheByteLim(A0),D0 ; get max bytesize request to cache
|
||
MOVE.L (SP)+,A0
|
||
CMP.L D0,D4 ; more than this size?
|
||
BHI.S @1 ; br if so (do the disk I/O)
|
||
btst #noCacheBit,IOPosMode+1(A0) ; disabling cache for this request? <20Nov87>
|
||
bne.s @1 ; xfer if so... <20Nov87>
|
||
BTST #noRWIPBit,CacheCom ; do disk I/O anyway? Finder will set for disk copy r/w
|
||
@1 RTS ; BNE to disk I/O
|
||
|
||
CacheRdIP:
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVE.L D3,-(A6) ; preserve D3 also
|
||
|
||
BSR.S IPLimCheck ; is this request too large to cache?
|
||
BNE regRdIP ; br if so (read from disk)
|
||
MOVEQ #rdVerify,D0 ; get read-verify option bit <20Nov87>
|
||
AND.B IOPosMode+1(A0),D0 ; check verify mode bit
|
||
BNE regRdIP ; br if verify (read from disk)
|
||
|
||
; Quick hack: treat 1-8 block reads as separate GetBlock calls . . . we want to build the
|
||
; GetBlock function in here eventually to save the multiple queue searches and file block
|
||
; mapping.
|
||
|
||
CMPI.L #bufSiz,D4 ; read 1 block? <18Jun90>
|
||
BNE.S rdIPloop ; no, more than 1 block <18Jun90>
|
||
;; we can do read ahead here <18Jun90>
|
||
BSR rwIPSetUp ; save regs, set up A5=buf addr, D7=refnum, D3=buf size,
|
||
; D4=block count, D5=start file block
|
||
MOVE.L D5,D2 ; file block number to read
|
||
MOVEQ #kGBrelease,D1 ; release immediately
|
||
MOVE.W D7,D0 ; refnum
|
||
BSR GetBlock ; get it
|
||
BNE rdIPErr ; exit on errors
|
||
MOVE.L A5,A1 ; destination
|
||
MOVE.L D3,D0 ; bytecount=bufsiz
|
||
_BlockMove
|
||
BRA rdIPok ; that's all for single block <18Jun90>
|
||
|
||
rdIPloop
|
||
;; here, we should do multi-block read -- KSCT <18Jun90>
|
||
;; First find out how many blocks are really contiguous <18Jun90>
|
||
|
||
JSR Lg2Phys ; get D6=byte count, D3=disk start blk
|
||
BNE rdIPExit ; punt on errors; only D3 on A6 stack
|
||
CMP.L D4,D6 ; D6 > user size?
|
||
BLE.S @0 ; no, can't read them all
|
||
MOVE.L D4,D6 ; D6>D4, we’ll do only this many bytes
|
||
@0 MOVEM.L D1-D7/A0-A5,-(A6) ; Save away almost all registers on TFS stack
|
||
; note here original D4 preserved on stack
|
||
MOVE.L D6,D4 ; only these many bytes are contiguous
|
||
MOVE.L IOBuffer(A0),A5 ; user buffer address
|
||
ADD.L IOActCount(A0),A5 ; buffer start + count so far = target
|
||
MOVEQ #9,D0
|
||
LSR.L D0,D4 ; block count
|
||
LSR.L D0,D5 ; starting file block
|
||
MOVE.L D4,D7 ; save block count in D7
|
||
|
||
BSR inCachep ; all data in the cache?
|
||
BEQ.S rdIPok ; yes, don't need to read/copy (how nice)
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; new code here <18Jun90>
|
||
|
||
;; else, read blocks from disk, D6= total bytes to be read,
|
||
;; D4.L = remaining number of blks to be read.
|
||
MOVEM.L A1/D1,-(A6) ; save registers over cache call
|
||
MOVE.W D1,D0 ; file refnum for flushcache call
|
||
MOVEQ #0,D1 ; no options on FlushCache
|
||
ST CacheFlag ; make sure we flush this one!
|
||
BSR FlushCache ; flush all the blocks of the file
|
||
MOVEM.L (A6)+,A1/D1 ; restore registers for our use
|
||
BNE.S rdIPErr ; fail on errors
|
||
|
||
SUB.L D4,D7 ; any blk in the cache?
|
||
BEQ.S @1 ; no, the world hasn't been changed
|
||
MOVEQ #9,D0 ; D7=number of blks found in the cache
|
||
LSL.L D0,D7 ; convert to bytes
|
||
ADD.L D7,IOActCount(A0) ; update param
|
||
SUB.L D7,D6 ; D6=remaining number of bytes to be read
|
||
|
||
@1 MOVE.L D7,D2 ; D2=bytes we added to IOActCount
|
||
MOVE.W D1,D7 ; save refnum
|
||
JSR RdBlocks ; keep low-level I/O in its place
|
||
BNE.S rdIPErr ; punt on errors
|
||
TST.L D2 ; have we changed IOActCount
|
||
BEQ.S @no ; no =>
|
||
SUB.L D2,IOActCount(A0) ; undo it
|
||
@no MOVE.L #BufSiz,D3 ; transfer one buffer at a time
|
||
|
||
@copyloop MOVE.L D5,D2 ; starting file block
|
||
MOVEQ #kGBrelease+kGBnoread,D1 ; release immediately and don't read
|
||
MOVE.W D7,D0 ; refnum
|
||
BSR GetBlock ; get it
|
||
BNE.S rdIPok ; exit on errors, but user got his data
|
||
|
||
MOVE.L CacheVars,A1 ; newly allocated buffer?
|
||
TST.W LcGBinCache(A1) ; data already in cache buffer?
|
||
BNE.S @4 ; yes, don't need copy
|
||
MOVE.L A5,A1 ; A1=user buffer
|
||
EXG A0,A1 ; exg for blockmove
|
||
MOVE.L D3,D0 ; bytecount=bufsiz
|
||
_BlockMove
|
||
@4
|
||
ADD.L D3,A5 ; bump to next buffer
|
||
ADDQ.L #1,D5 ; bump file block number
|
||
SUBQ #1,D4 ; decr block count
|
||
BGT.S @copyloop ; loop until done
|
||
|
||
rdIPok
|
||
MOVEQ #0,D0 ; success!
|
||
|
||
rdIPErr MOVEM.L (A6)+,D1-D7/A0-A5
|
||
BRA.S rdIPExit ; back to caller
|
||
|
||
;; read directly into user's buffer, no cache involved ...
|
||
regRdIP MOVEM.L A1/D1,-(A6) ; save registers over cache call
|
||
MOVE.W D1,D0 ; file refnum for flushcache call
|
||
MOVEQ #0,D1 ; no options on FlushCache
|
||
ST CacheFlag ; make sure we flush this one! <06Dec85>
|
||
BSR FlushCache ; flush all the blocks of the file
|
||
MOVEM.L (A6)+,A1/D1 ; restore registers for our use
|
||
BNE.S rdIPExit ; fail on errors
|
||
|
||
JSR Lg2Phys ; get D6=byte count, D3=disk start blk
|
||
BNE.S rdIPExit ; punt on errors
|
||
|
||
JSR RdBlocks ; keep low-level I/O in its place
|
||
|
||
rdIPExit MOVE.L (A6)+,D3 ; restore D3
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set CCR on result code
|
||
RTS ;
|
||
|
||
rwIPSetUp
|
||
MOVE.L D4,D6 ; we’ll do this many bytes
|
||
MOVEM.L D1-D7/A0-A5,-(A6) ; Save away almost all registers on TFS stack
|
||
MOVE.L IOBuffer(A0),A5 ; user buffer address
|
||
ADD.L IOActCount(A0),A5 ; buffer start + count so far = target
|
||
MOVE.W D1,D7 ; save refnum
|
||
MOVEQ #9,D3
|
||
LSR.L D3,D4 ; block count
|
||
LSR.L D3,D5 ; starting file block
|
||
MOVE.L #BufSiz,D3 ; transfer one buffer at a time
|
||
RTS
|
||
|
||
CacheWrIP:
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVE.L D3,-(A6) ; preserve D3 also
|
||
|
||
BSR IPLimCheck ; is this request too large to cache?
|
||
BNE.S regWrIP ; br if so (write to disk)
|
||
|
||
; Quick hack: treat 1-8 block writes as separate GetBlock calls . . . we want to build the
|
||
; GetBlock function in here eventually to save the multiple queue searches and file block
|
||
; mapping.
|
||
|
||
BSR.S rwIPSetUp ; save regs, set up A5=buf addr, D7=refnum, D3=buf size,
|
||
; D4=block count, D5=start file block
|
||
wrIPloop MOVE.L D5,D2 ; file block
|
||
MOVEQ #(kGBrelease+kGBnoread),D1 ; release immediately, don’t read it if not found
|
||
MOVE.W D7,D0 ; refnum
|
||
BSR GetBlock ; get it
|
||
BNE.S @3 ; exit on errors
|
||
BSR MarkBlock ; mark it dirty
|
||
MOVE.L A0,A1 ; buffer is the destination
|
||
MOVE.L A5,A0 ; source is user buffer
|
||
MOVE.L D3,D0 ; bytecount=bufsiz
|
||
_BlockMove ; write to cache !
|
||
ADD.L D3,A5 ; bump to next buffer
|
||
ADDQ.L #1,D5 ; bump file block number
|
||
SUBQ #1,D4 ; decr block count
|
||
BGT.S wrIPloop ; loop until done
|
||
|
||
MOVEQ #0,D0 ; success!
|
||
|
||
@3 MOVEM.L (A6)+,D1-D7/A0-A5 ; all done
|
||
BRA.S wrIPExit ; back to caller
|
||
|
||
regWrIP JSR Lg2Phys ; get D6=byte count, D3=disk start blk
|
||
BNE.S wrIPExit ; punt on error
|
||
|
||
; Trash the volume cache if it contains a block in the range about to be written:
|
||
MOVEM.L D1-D2/D7,-(A6) ; save registers over cache call <09Jun90>
|
||
MOVE.W D1,D0 ; File RefNum
|
||
MOVE.L D5,D1 ; Starting pos is current file pos
|
||
MOVE.L D6,D2 ; Number of bytes . . .
|
||
;;don't save in DHT, could there be left over buffers? <09Jun90>
|
||
;; D7 is the flag used in FreeCBlks <09Jun90>
|
||
MOVE.L #fromWriteIP,D7 ; don't save in DHT but mark where from <09Jun90>
|
||
BSR Trashblocks ; trash the file blocks (we don't really want to trash)
|
||
|
||
MOVEM.L (A6)+,D1-D2/D7 ; no errors for now <09Jun90>
|
||
JSR WrBlocks ;
|
||
wrIPExit BRA rdIPExit ; share exit with read-in-place
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Cache subroutines
|
||
;_________________________________________________________________________________
|
||
|
||
|
||
;_______________________________________;
|
||
;
|
||
; TFS or MFS?
|
||
;_______________________________________;
|
||
|
||
TFSVCBTst:
|
||
CMP.W #TSigWord,VCBSigWord(A2) ; TFS volume?
|
||
RTS ;
|
||
|
||
;_______________________________________;
|
||
;
|
||
; File mapping routine . . .
|
||
;_______________________________________;
|
||
|
||
Lg2Phys:
|
||
MOVE.L jLg2Phys,-(SP) ; let the FS do the mapping
|
||
RTS ;
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: RdBlocks, WrBlocks
|
||
;
|
||
; Function: Read/write a consecutive number of blocks from/to the disk. The I/O
|
||
; is done directly to/from the user’s buffer. This routine is in the
|
||
; cache to allow future caching schemes to buffer entire files if the
|
||
; room exists.
|
||
;
|
||
; Input: A0.L - user parameter block ptr
|
||
; (A1,D1.W) - FCB pointer
|
||
; A2.L - VCB Ptr
|
||
; D3.L - disk start block
|
||
; D6.L - bytecount to read/write
|
||
;
|
||
; Output: D0.W - error code
|
||
; All other registers preserved
|
||
;
|
||
; Called by: CacheRdIP,CacheWrIP
|
||
;
|
||
;_________________________________________________________________________________
|
||
|
||
;_______________________________________;
|
||
;
|
||
; Multi-block write routine . . .
|
||
;_______________________________________;
|
||
|
||
WrBlocks:
|
||
MOVE.L jWrBlocks,-(SP) ; jumptable entry for vWrBlocks
|
||
RTS ; go there
|
||
|
||
;_______________________________________;
|
||
;
|
||
; Multi-block read routine . . .
|
||
;_______________________________________;
|
||
|
||
RdBlocks:
|
||
MOVE.L jRdBlocks,-(SP) ; jumptable entry for vRdBlocks
|
||
RTS ; go there
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine ReadBlock (Read Block)
|
||
;
|
||
; Function: Reads a block from disk into a specified cache buffer.
|
||
;
|
||
; Input: A4.L - Cache Buffer pointer
|
||
;
|
||
; Output: D0.W - error code
|
||
; 0 = ok
|
||
; other = error
|
||
;
|
||
; Called by: GetBlock
|
||
;_________________________________________________________________________________
|
||
|
||
ReadBlock
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L D1-D7/A0-A5,-(A6) ; Save away almost all registers on TFS stack
|
||
|
||
MOVEQ #0,D1 ; indicate 'read' operation
|
||
BRA.S RWSetup ; use common code ->
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine WriteBlock (Write Block)
|
||
;
|
||
; Function: Writes contents of cache buffer to disk
|
||
;
|
||
; Input: A4.L - Cache Buffer pointer
|
||
;
|
||
; Output: D0.W - error code
|
||
; 0 = ok
|
||
; other = error
|
||
;
|
||
; Called by: FlushCache,GetBlock,RelBlock
|
||
;_________________________________________________________________________________
|
||
|
||
WriteBlock
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L D1-D7/A0-A5,-(A6) ; Save away almost all registers on TFS stack
|
||
|
||
; set up tag data
|
||
|
||
MOVE.L MCBFQPtr(A4),A3 ; point to queue header
|
||
MOVEA.L MQHVCBPtr(A3),A2 ; A2 = VCB ptr
|
||
MOVE.L MQHFlNum(A3),D0 ; file number (0 for vol blks)
|
||
BEQ.S @1 ; br if volume blk
|
||
|
||
MOVE.L MCBFlBlk(A4),BufTgFFlg ; ...file block number
|
||
MOVE.B MQHAttrib(A3),BufTgFFlg ; ...flags byte
|
||
BRA.S @2
|
||
|
||
@1 MOVEQ #-1,D0 ; pass -1 as file blk for vol blks
|
||
MOVE.L D0,BufTgFFlg
|
||
MOVEQ #FSRtParID,D0
|
||
|
||
@2 MOVE.L D0,BufTgFNum ; set up file number
|
||
MOVE.L MQHFlType(A3),HFSTagData ; set Finder file type (0 for MFS, vol blks)
|
||
MOVE.L MQHParDID(A3),HFSTagData+4 ; ...and parent DirID (ibid)
|
||
|
||
MOVE.L Time,BufTgDate ; use date for MFS
|
||
BSR.S TFSVCBTst ; is it MFS or is it TFS?
|
||
BNE.S @3 ; br if not Memorex
|
||
MOVE.L VCBWrCnt(A2),BufTgDate ; set write count in tag data <01Oct85>
|
||
ADDQ.L #1,VCBWrCnt(A2) ; bump volume write count <01Oct85>
|
||
|
||
@3 MOVEQ #1,D1 ; indicate 'write' operation
|
||
|
||
; set up IO parameter block (ReadBlock code joins here)
|
||
|
||
RWSetUp
|
||
LEA Params,A0 ; use FS IO param block
|
||
|
||
MOVE.W VCBDrvNum(A2),IODrvNum(A0) ; drive number
|
||
MOVE.W VCBDRefNum(A2),IORefNum(A0) ; driver refnum
|
||
|
||
MOVEA.L MCBDataPtr(A4),A1 ; it's a pointer now <20Apr90>
|
||
MOVE.L A1,IOBuffer(A0) ;
|
||
MOVE.W #1,IOPosMode(A0) ; position mode 1 (from disk start)
|
||
MOVE.L #BufSiz,IOByteCount(A0) ; always r/w 512 bytes
|
||
|
||
MOVE.L MCBDBlk(A4),D0 ; physical disk block
|
||
IF CacheDebug THEN
|
||
BNE.S @1 ; should never be zero
|
||
_CacheDebug StrBlk0
|
||
ENDIF
|
||
|
||
@1 LSL.L #8,D0 ; ...x 512
|
||
ADD.L D0,D0 ;
|
||
MOVE.L D0,IOPosOffset(A0) ; ... gives disk address
|
||
|
||
;_______________________________________;
|
||
;
|
||
; All I/O goes through this routine . . .
|
||
;_______________________________________;
|
||
|
||
BasicIO:
|
||
MOVE.L jBasicIO,-(SP) ; jumptable entry for BasicIO
|
||
RTS ; go there
|
||
|
||
;__________________________________________________________________________;
|
||
;
|
||
; Subroutine: FindFileCache
|
||
; Function: Search file queue for the file header for a particular file
|
||
; fork. (There should only be one).
|
||
;
|
||
; Arguments:
|
||
; (input) A3.L -- FileQHdr
|
||
; A2.L -- VCB ptr
|
||
; D3.L -- file number
|
||
; D6.B -- fork type
|
||
;
|
||
; (output) D0 -- non-zero if found (CCR set on A4)
|
||
; A4.L -- queue ptr if found
|
||
;
|
||
; Note: We check the file number first since it is the most unique:
|
||
; this keeps the loop to 5 instructions typically. We could
|
||
; use the file count in the header and use a DBRA loop instead?
|
||
;
|
||
;__________________________________________________________________________;
|
||
|
||
FindFileCache
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
@1 MOVEA.L FHFlink(A4),A4 ; position to next file queue
|
||
CMPA.L A3,A4 ; back to top of queue ?
|
||
BEQ.S @2 ; fail if so
|
||
|
||
CMP.L FHFlNum(A4),D3 ; same file number?
|
||
BNE.S @1 ; br if not, continue search
|
||
CMP.L FHVCBPtr(A4),A2 ; same volume? <06Dec85>
|
||
BNE.S @1 ; br if not, continue search <06Dec85>
|
||
CMP.B FHFkType(A4),D6 ; same fork?
|
||
BNE.S @1 ; br if not, continue search
|
||
|
||
BRA.S @3 ; exit if found
|
||
|
||
@2 SUB.L A4,A4 ; not found
|
||
@3 MOVE.L A4,D0 ; set CCR
|
||
RTS ; BEQ for not found
|
||
|
||
;__________________________________________________________________________;
|
||
;
|
||
; Subroutine: FlushFCache
|
||
; Function: This routine actually flushes out a cache queue. For a volume
|
||
; flush, it is called one time for each file cache belonging
|
||
; to that volume.
|
||
; Arguments:
|
||
; (input) A4.L -- File Queue Header
|
||
;
|
||
; (output) D0 -- result of last WriteBlock call.
|
||
;
|
||
; Called By: FlushCache, GetBlock (when it needs a new File Queue),
|
||
; GetCQBlk (flush a low-priority queue).
|
||
; Modification history:
|
||
; 25Apr90 KST Don't flush unless something in the file queue is dirty.
|
||
; We call this too often.
|
||
;__________________________________________________________________________;
|
||
|
||
FlushFCache
|
||
BCLR #fqDirtyBit,FHFlags(A4) ; queue dirty? <25Apr90>
|
||
BEQ.S ffExit ; no, don't need to flush <25Apr90>
|
||
; don't even need to save regs <25Apr90>
|
||
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L A3-A4,-(A6) ; save registers
|
||
|
||
LEA FHDQHFLink(A4),A3 ; disk-ordered queue header
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
tfcLoop MOVEA.L DCBFlink(A4),A4 ; position to next buffer, disk order
|
||
CMPA.L A3,A4 ; back to top of queue ?
|
||
BEQ.S @1 ; exit if so
|
||
|
||
BCLR #cacheDirtyBit,DCBFlags(A4) ; buffer dirty?
|
||
BEQ.S tfcLoop ; no, continue search
|
||
|
||
LEA -MCBDQFlink(A4),A4
|
||
JSR WriteBlock ; flush next buffer in disk order
|
||
LEA MCBDQFlink(A4),A4
|
||
BRA.S tfcLoop ; and keep searching
|
||
|
||
@1 MOVEM.L (A6)+,A3-A4 ; restore registers
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
ffExit
|
||
RTS ; exit Flush Cache
|
||
|
||
;__________________________________________________________________________;
|
||
;
|
||
; Subroutine: TrashFCache (internal routine)
|
||
; Function: This routine trashes a cache queue. All blocks are transferred
|
||
; to the free queue. (doesn't touch DHT)
|
||
; Arguments:
|
||
; (input) A3.L -- FileQHdr
|
||
; A4.L -- File Queue Header
|
||
; (output) none
|
||
;
|
||
; Called By: GetBlock (when it needs a new File Queue), TrashVBlks,
|
||
; GetLPBuf (flush a low-priority queue).
|
||
; Modification history:
|
||
; 27Aug90 KST Some optimization here, if the file queue is never used,
|
||
; skip all the nonsense.
|
||
;__________________________________________________________________________;
|
||
|
||
; Also called by GetBlock to recycle a file cache queue.
|
||
|
||
TrashFCache
|
||
MOVEM.L A0-A1,-(SP) ; save registers
|
||
TST.L FHVCBPtr(A4) ; is this cache queue free? <27Aug90>
|
||
BEQ.S @Qnotused ; yes, skip the rest <27Aug90>
|
||
|
||
CLR.L FHVCBPtr(A4) ; mark this cache queue free
|
||
MOVE.L FreeQHdr,A0 ; get free queue header
|
||
LEA FHMQHFlink(A4),A1 ; add these buffers to free queue
|
||
BSR AddQToQ ; add A1 queue buffers to A0 queue
|
||
|
||
LEA FHMQHFlink(A4),A0 ; MRU-ordered queue header
|
||
MOVE.L A0,FLink(A0) ; init header
|
||
MOVE.L A0,BLink(A0)
|
||
|
||
LEA FHDQHFLink(A4),A0 ; disk-ordered queue header
|
||
MOVE.L A0,FLink(A0) ; init header
|
||
MOVE.L A0,BLink(A0)
|
||
|
||
CLR.L FHBufCnt(A4) ; no buffers in this queue, no max
|
||
CLR.W FHFlags(A4) ; zero flags, fork type
|
||
CLR.W FHAttrib(A4) ; zero attributes, extra
|
||
CLR.L FHFlType(A4) ; clear Finder file type
|
||
CLR.L FHParDID(A4) ; ...and parent DirID
|
||
@tfcRequeue
|
||
BSR DQElement ; DeQueue the element <08Dec85>
|
||
MOVEA.L FQHBlink(A3),A0 ; and ReQueue it on the end <08Dec85>
|
||
BSR EQElement ; <08Dec85>
|
||
@tfcRet
|
||
MOVEM.L (SP)+,A0-A1 ; restore registers
|
||
RTS
|
||
@Qnotused ; file queue is not used <27Aug90>
|
||
CMPA.L FQHBlink(A3),A4 ; already at the end? <27Aug90>
|
||
BEQ.S @tfcRet ; yes, all done <27Aug90>
|
||
BRA.S @tfcRequeue ; no, requeue it <27Aug90>
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; FreeCBlk (Free cache block) subroutine
|
||
;
|
||
; Function: Detaches a cache buffer from a cache queue and requeues
|
||
; it in the free queue.
|
||
;
|
||
; Input: A4.L - element pointer
|
||
; D7.L - If D7.L == 'hash' then add buffer to the hash table. <04Jun90>
|
||
; Output: none, A4/D7 unchanged
|
||
; 02Aug90 KSCT Now takes D7 as an input parameter.
|
||
; 28Aug90 KSCT Put empty buffer at the end of the free queue, (GetBlock will take
|
||
; one from the end), non-empty buffer in the front.
|
||
;_________________________________________________________________________________
|
||
|
||
FreeCBlk
|
||
MOVEM.L D6/A0,-(SP) ; save D6 also <06Jun90>
|
||
BSET #emptyBit,MCBFlags(A4) ; mark buffer empty
|
||
|
||
MOVE.L MCBFQPtr(A4),A0 ; get queue header
|
||
SUBQ.W #1,MQHBufCnt(A0) ; decr element count in header
|
||
MOVE.L MQHFlNum(A0),D6 ; D6.L = file number
|
||
BSR DQElement ; dequeue it from MRU (fqHdr) queue
|
||
|
||
LEA MCBDQFlink(A4),A4
|
||
BSR DQElement ; dequeue it from Disk-order queue
|
||
MOVE.L A4,FLink(A4) ; init disk-ordered queue header to <01Jan86>
|
||
MOVE.L A4,BLink(A4) ; point to itself <01Jan86>
|
||
LEA -MCBDQFlink(A4),A4
|
||
|
||
MOVE.L FreeQHdr,A0 ; get free queue header
|
||
MOVE.L A0,MCBFQPtr(A4) ; update header pointer for DQCacheBufs
|
||
ADDQ.W #1,FreeQBufCnt(A0) ; increment count in free queue
|
||
|
||
CLR.L MCBAge(A4) ; age = 0 means not in DHT <06Jun90>
|
||
CMPI.L #SaveinHash,D7 ; save in DHT? <06Jun90>
|
||
BEQ.S @1 ; yes <06Jun90>
|
||
;; put EMPTY buffer at the end of the free queue: <28Aug90>
|
||
MOVE.L FreeQBLink(A0),A0 ; point to last buffer in free queue <28Aug90>
|
||
BSR EQElement ; put it to the end <28Aug90>
|
||
BRA.S @2 ; done <28Aug90>
|
||
|
||
@1 ;; put buffer in the front of the queue and add it to device hash table.<06Jun90>
|
||
BSR EQElement ; enqueue it <28Aug90>
|
||
;; here, A4 = ptr(buffer header) and is on freeQ. <06Jun90>
|
||
BCLR #emptyBit,MCBFlags(A4) ; mark non-empty (contents valid) <28Aug90>
|
||
BSR addToDhash ; add to dev hash <06Jun90>
|
||
@2
|
||
MOVEM.L (SP)+,D6/A0 ; restore <06Jun90>
|
||
RTS
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; EQCBlkFO (Enqueue Cache Block in File Order)
|
||
;
|
||
; Function: Inserts CBlk queue element into A3 queue in
|
||
; file order.
|
||
;
|
||
; Input: A4.L - Cache Buffer ptr
|
||
; A3.L - MCB position in FileQueue header
|
||
; CBlkFlBlk(A4) - file block of buffer to be queued
|
||
; A4 block must not be in A3 MRU queue yet.
|
||
;
|
||
; Output: none
|
||
;
|
||
; Note: Let {a}=first block in file order
|
||
; {b}=first block in MRU order
|
||
; {c}=last block in file order
|
||
;
|
||
; Then {a} ≤ {b} ≤ {c}. To speed the queuing up,
|
||
; we look at all three cases to get the nearest
|
||
; lower-block.
|
||
;
|
||
; To get this to work, the block must be queued up
|
||
; in file order before it is queued in MRU order.
|
||
;_________________________________________________________________________________
|
||
|
||
EQCBlkFO
|
||
MOVEM.L D0/A0-A2,-(SP) ; save regs
|
||
MOVE.L MCBFlBlk(A4),D0 ; file block of element to be queued
|
||
|
||
; First we check to see if this block is past {c}, the last queue block
|
||
; (this should be a common case, as files tend to be read sequentially).
|
||
|
||
LEA MCBDQFlink(A3),A0 ; first queue position
|
||
MOVE.L DCBBlink(A0),A1 ; A1 -> {c}
|
||
CMP.L A0,A1 ; empty queue?
|
||
BEQ.S @4 ; br if so (queue at start)
|
||
CMP.L DCBFlBlk(A1),D0 ; after last block?
|
||
BCS.S @1 ; br if not
|
||
MOVE.L A1,A0 ; queue up after {c} (A4 is the last file blk)
|
||
BRA.S @4
|
||
|
||
; Next we check if it is before the first block.
|
||
|
||
@1 MOVE.L DCBFlink(A0),A1 ; A1 -> {a}
|
||
CMP.L DCBFlBlk(A1),D0 ; before first block?
|
||
BCS.S @4 ; br if so (queue at start)
|
||
|
||
; OK, it was neither of the boundary cases (before first or after last).
|
||
; Check MRU block (block {b}), and then start search from {a} or {b}.
|
||
|
||
MOVE.L MCBFlink(A3),A2 ; check if after MRU block
|
||
CMP.L MCBFlBlk(A2),D0 ; after MRU block?
|
||
BCS.S @2 ; br if not (search from {a})
|
||
LEA MCBDQFlink(A2),A1 ; search from {b}, the MRU block
|
||
|
||
@2 MOVE.L A0,A2 ; save queue start ptr
|
||
|
||
@3 MOVE.L A1,A0 ; save last blk ptr
|
||
MOVE.L DCBFlink(A1),A1 ; look at next block
|
||
CMP.L A1,A2 ; reached start? error??
|
||
BEQ.S @4 ; br if so (queue right after {c})
|
||
CMP.L DCBFlBlk(A1),D0 ; after this block?
|
||
BHI.S @3 ; loop if so
|
||
|
||
; OK, we queue it up after the position pointed to by A0.
|
||
|
||
@4 LEA MCBDQFlink(A4),A4 ; adjust for regular queueing routines
|
||
BSR EQElement ; queue up after A0 element
|
||
LEA -MCBDQFlink(A4),A4
|
||
MOVEM.L (SP)+,D0/A0-A2 ; restore regs
|
||
RTS ; exit EQCBlkFO
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; GetLPBuf (Get low priority buffer) subroutine
|
||
;
|
||
; Function: Gets the least needed buffer and recycles it.
|
||
;
|
||
; Input: D3.L -- disk block
|
||
; A2.L -- VCB ptr
|
||
; Output: A4.L - CB header ptr
|
||
; D0.L - error if all are busy?
|
||
;
|
||
; Return free block, if any.
|
||
; If not, recycle all buffers with an age the same or earlier than that of the
|
||
; age of the LRU buffer in the LRU file queue.
|
||
;
|
||
; This algorithm will make this routine more efficient: since it recycles
|
||
; a number of blocks, it will have to do this recycling less often.
|
||
; By getting rid of all LRU blocks in all files, it prevents a buildup
|
||
; of old blocks in a file queue which may be hit fairly often (e.g., a
|
||
; directory file may have all blocks in it from a scavenge operation at
|
||
; mount, but only the first few may actually be used on a dynamic basis).
|
||
;
|
||
; MODIFICATION HISTORY:
|
||
; 05Jun90 KSCT Searches device hash table for a possible reuse
|
||
;_________________________________________________________________________________
|
||
|
||
GetLPBuf
|
||
MOVE.L (SP)+,-(A6)
|
||
MOVE.L A3,-(A6) ; save regs
|
||
|
||
; (0) First check if the requested block is in the dev hash table. <05Jun90>
|
||
|
||
BSR srchDevhash ; give hash a chance <05Jun90>
|
||
BEQ.S gLPBuf1 ; not in the hash table <05Jun90>
|
||
;; found it in the hash, A4 = CBhdr <05Jun90>
|
||
MOVE.L CacheVars,A3 ; set the incache flag <01Aug90>
|
||
ST LcGBinDHTable(A3) ; buffer found in free queue <01Aug90>
|
||
|
||
BTST #GBread,D7 ; forced read requested? <05Jun90>
|
||
BNE.S @4 ; br if yes <05Jun90>
|
||
;; if user doesn't specify forced read, then we skip read <05Jun90>
|
||
BSET #GBnoRead,D7 ; no-read 'cause we found it in DHT <05Jun90>
|
||
@4 MOVE.L FreeQHdr,A3 ; A3 = free queue header <05Jun90>
|
||
BRA.S gLinHash ; found it in DHT <05Jun90>
|
||
; D2.L = file blk!! <05Jun90>
|
||
|
||
; (1) and then check the free queue, the lowest priority blocks.
|
||
gLPBuf1 MOVE.L FreeQHdr,A3 ; check the free queue first
|
||
MOVE.L FreeQBlink(A3),A4 ; get last buffer (these are initially highest in memory)
|
||
CMP.L A3,A4 ; empty queue?
|
||
BEQ.S gLPBuf2 ; br if so
|
||
|
||
gLinHash ; hash code joins here <05Jun90>
|
||
TST.L MCBAge(A4) ; in the hash table too? <05Jun90>
|
||
BEQ.S @2 ; no <05Jun90>
|
||
LEA MCBHashqFlink(A4),A4 ; A4 = hashq header <07Jun90>
|
||
BSR DQfromDHash ; yes, remove it from hash <05Jun90>
|
||
LEA -MCBHashqFlink(A4),A4 ; A4 = CB header <07Jun90>
|
||
|
||
@2 BSR DQElement ; dequeue A4 element from free queue
|
||
SUBQ.W #1,FreeQBufCnt(A3) ; one less buffer
|
||
BRA gLPbufExit ; and exit
|
||
|
||
; (2) Flush the LRU buffers and recycle them. LRU buffers are those with an age the same
|
||
; or older than that of the age of the LRU buffer in the LRU file. We invalidate
|
||
; file-order position since we may touch first file queue.
|
||
|
||
gLPBuf2 MOVEM.L D0-D3/A0-A2,-(A6) ; save all regs
|
||
|
||
; First, find the LRU file queue which has a buffer in it.
|
||
|
||
MOVE.L FileQHdr,A3 ; go through the file queue
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
@1 MOVEA.L FHBlink(A4),A4 ; position to previous file queue
|
||
MOVE.L FHVCBPtr(A4),D0 ; queue free?
|
||
BEQ.S @1 ; br if so
|
||
TST.W FHBufCnt(A4) ; any buffers?
|
||
BEQ.S @1 ; br if not
|
||
|
||
; OK, the age of the LRU buffer in this queue will determine which buffers to recycle.
|
||
|
||
MOVE.L FHMQHBlink(A4),A2 ; MRU queue LRU buffer
|
||
MOVE.L MCBAge(A2),D2 ; this is our cutoff age
|
||
|
||
GLPFilLoop MOVEM.L A3-A4,-(A6) ; save the current file queue head, file queue header
|
||
|
||
LEA FHMQHFlink(A4),A3 ; MRU queue header
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
;<01Jan86>;MOVEQ #0,D3 ; clear flag indicating buffer was recycled
|
||
|
||
GLPBufLoop MOVEA.L MCBBlink(A4),A4 ; position to next buffer, LRU order
|
||
CMPA.L A3,A4 ; end of queue?
|
||
BEQ.S @2 ; exit if so
|
||
CMP.L MCBAge(A4),D2 ; same age or older?
|
||
;; if age > D2 then this buffer is RUsed, leave it in the cache.
|
||
BCS.S @2 ; exit if not (other MRU buffers will also not be)
|
||
BTST #inuseBit,MCBFlags(A4) ; in-use?
|
||
BNE.S @2 ; exit if so
|
||
BTST #cacheDirtyBit,MCBFlags(A4) ; dirty?
|
||
BNE.S @1 ; exit if so (flush it, recycle it next time around)
|
||
|
||
MOVE.L MCBFLink(A4),A2 ; save previous buffer ptr
|
||
;; since we involuntarily give up the buffer, so keep it in the DHT <04Jun90>
|
||
MOVE.L D7,-(SP) ; save D7 <04Jun90>
|
||
MOVE.L #SaveinHash,D7 ; save in DHT <04Jun90>
|
||
BSR FreeCBlk ; dequeue buffer and put it in free queue
|
||
MOVE.L (SP)+,D7 ; restore D7 <04Jun90>
|
||
|
||
MOVE.L A2,A4 ; start again with previous element
|
||
BRA.S GLPBufLoop ; continue search
|
||
|
||
@1 LEA -FHMQHFlink(A3),A4 ; pass file queue header
|
||
BSR FlushFCache ; flush this queue (it had a dirty old block)
|
||
BTST #noFlushBit,FHFlags(A4) ; were we avoiding intermediate flushes on this one?
|
||
BEQ.S @2 ; br if not
|
||
ST CacheFlag ; it’s flushing time till we exit this FS call
|
||
|
||
@2 MOVEM.L (A6)+,A3-A4 ; retrieve the current file queue head, file queue header
|
||
MOVE.L FHBlink(A4),A4 ; go to next previous file queue
|
||
CMP.L A3,A4 ; back to header?
|
||
BNE.S GLPFilLoop ; loop if not
|
||
|
||
;<01Jan86>;TST D3 ; did we recycle a buffer in the last file queue (the current queue)?
|
||
;<01Jan86>;BEQ.S @3 ; br if not
|
||
;<01Jan86>;SUB.L A5,A5 ; note that file-order position may be invalid
|
||
|
||
@3 MOVEM.L (A6)+,D0-D3/A0-A2 ; restore regs
|
||
BRA gLPBuf1 ; go get one off free queue now
|
||
|
||
gLPbufExit MOVE.L (A6)+,A3 ; restore A3
|
||
MOVE.L (A6)+,-(SP)
|
||
RTS
|
||
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; EQElement (Enqueue Element) subroutine
|
||
;
|
||
; Function: Inserts a queue element into a queue at a specified position.
|
||
;
|
||
; Input: A4.L - element pointer
|
||
; A0.L - position pointer (previous queue element)
|
||
;
|
||
; Output: none
|
||
;_________________________________________________________________________________
|
||
|
||
EQElement ;; A0 -> A4 -> A1 ...... (insert A4 after A0)
|
||
MOVE.L A1,-(SP) ; save reg
|
||
|
||
MOVEA.L Flink(A0),A1 ; addr of next Q element
|
||
MOVE.L A1,Flink(A4) ; link to next element
|
||
MOVE.L A4,Blink(A1) ; ...
|
||
|
||
MOVE.L A4,Flink(A0) ; link to previous element
|
||
MOVE.L A0,Blink(A4) ; ...
|
||
|
||
MOVE.L (SP)+,A1 ; restore reg
|
||
RTS ; exit EQElement
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; DQElement (Dequeue Element) subroutine
|
||
;
|
||
; Function: Detaches a queue element from a queue.
|
||
;
|
||
; Input: A4.L - element pointer
|
||
;
|
||
; Output: none
|
||
;_________________________________________________________________________________
|
||
|
||
DQElement MOVEM.L A0-A1,-(SP) ; save regs
|
||
|
||
MOVEA.L Blink(A4),A0 ; addr of previous Q element
|
||
MOVEA.L Flink(A4),A1 ; addr of next Q element
|
||
MOVE.L A1,Flink(A0) ; link to next element
|
||
MOVE.L A0,Blink(A1) ; link to previous element
|
||
|
||
MOVEM.L (SP)+,A0-A1 ; restore regs
|
||
RTS ; exit DQCBuf
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; AddQToQ (Add Queue to Queue) subroutine
|
||
;
|
||
; Function: Takes all cache buffers from one queue, detaches them
|
||
; from the queue header, and adds them to the end of
|
||
; another queue. (Only used by TrashFCache)
|
||
;
|
||
; Input: A1.L - queue header of source queue
|
||
; A0.L - queue header of destination queue (freeqhdr)
|
||
;
|
||
; Output: none
|
||
;
|
||
; Before: {DQH} <-> {DQ1} <-> {DQE} <-> {DQH
|
||
; {SQH} <-> {SQ1} <-> {SQE} <-> {SQH
|
||
;
|
||
; After: {DQH} <-> {DQ1} <-> {DQE} <-> {SQ1} <-> {SQE} <-> {DQH
|
||
; {SQH} <-> {SQH
|
||
;
|
||
; Note: This was made more specific to the free queue as destination queue
|
||
; since queue header backpointers needed to be filled in.
|
||
;_________________________________________________________________________________
|
||
|
||
AddQToQ ; add A1 queue buffers to A0 queue
|
||
MOVEM.L D0/A0-A4,-(SP) ; save regs
|
||
|
||
MOVE.W QCount(A1),D0 ; get count of buffers we are adding
|
||
ADD.W D0,QCount(A0) ; add to dest q count
|
||
|
||
; Check to see if the source queue is empty.
|
||
|
||
MOVE.L Flink(A1),A3 ; A3 -> {SQ1}
|
||
CMP.L A1,A3 ; {SQ1}={SQH}? any buffers at all?
|
||
BEQ.S addQExit ; exit if empty
|
||
|
||
; Fill in A0 as the new queue header backpointer in the source queue elements.
|
||
|
||
MOVE.L A1,A3 ; start at beginning
|
||
@1 MOVE.L Flink(A3),A3 ; get next element
|
||
CMP.L A1,A3 ; back to start?
|
||
BEQ.S @2 ; exit loop if so
|
||
MOVE.L A0,MCBFQPtr(A3) ; fill in new header
|
||
CLR.L MCBAge(A3) ; clear age so we know it's not in DHT <07Jun90>
|
||
LEA MCBDQFlink(A3),A2 ; point to disk-ordered queue <01Jan86>
|
||
MOVE.L A2,FLink(A2) ; init disk-ordered queue header to <01Jan86>
|
||
MOVE.L A2,BLink(A2) ; point to itself <01Jan86>
|
||
BRA.S @1
|
||
|
||
@2 MOVE.L Flink(A1),A3 ; A3 -> {SQ1}
|
||
|
||
; Unlink the source header from its elements
|
||
|
||
MOVE.L A1,A4 ; A4 -> {SQH}
|
||
BSR.S DQElement ; {SQ1} <-> {SQE} <-> {SQ1
|
||
; {SQH} -> {SQ1} <-> {SQE} <- {SQH
|
||
|
||
; Link end of dest queue to first buffer in src queue, and
|
||
; link end of src queue to header of of dest queue
|
||
;
|
||
; note: A3 -> {SQ1}, A0 -> {DQH}
|
||
|
||
MOVE.L Blink(A0),A2 ; A2 -> {DQE}
|
||
MOVE.L Blink(A3),A4 ; A4 -> {SQE}
|
||
|
||
MOVE.L A3,Flink(A2) ; {DQE} <-> {SQ1}
|
||
MOVE.L A2,Blink(A3) ;
|
||
|
||
MOVE.L A4,Blink(A0) ; {SQE} <-> {DQH
|
||
MOVE.L A0,Flink(A4) ;
|
||
|
||
; Init src queue header to point to itself (since it’s empty)
|
||
|
||
MOVE.L A1,Flink(A1) ; {SQH} <-> {SQH
|
||
MOVE.L A1,Blink(A1) ;
|
||
|
||
addQExit MOVEM.L (SP)+,D0/A0-A4 ; restore regs
|
||
RTS
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; hash code shared by 6.0 and 7.0
|
||
|
||
|
||
;_______________________________________________________________________
|
||
; Function: Copy data from cache to user's buffer
|
||
; Input: A4=CB header, A5=buf addr, Assume D2 to be free.
|
||
; Output: A5/D3-D5 updated. D4.L = number of blocks remain to be read
|
||
;_______________________________________________________________________
|
||
copyTOuserbuf
|
||
MOVEM.L A0-A1,-(SP) ; save A0-A1
|
||
MOVEA.L MCBDataPtr(A4),A0 ; it's a pointer now
|
||
MOVE.L #bufSiz,D2 ; D2 = 512
|
||
MOVEA.L A5,A1 ; A1 = dest
|
||
MOVE.L D2,D0
|
||
_BlockMove
|
||
ADD.L D2,A5 ; bump to next buffer
|
||
ADDQ.L #1,D3 ; bump disk block number
|
||
ADDQ.L #1,D5 ; bump file block number
|
||
SUBQ #1,D4 ; decr block count
|
||
MOVEM.L (SP)+,A0-A1
|
||
RTS
|
||
|
||
;_______________________________________________________________________
|
||
; Function: Search if block is already in the cache. If it is in the cache we copy
|
||
; it into user's buffer as much as we can. Once we find a block that is not
|
||
; in the cache, we stop immediately. We'll then do multiblock read from there.
|
||
; Input: A2=VCB, A5=buf addr, D1.W=refnum, D3=disk block number,
|
||
; D4=block count, D5=start file block, D6.L=bytes to read
|
||
; (A1,D1.W) - FCB pointer
|
||
; Assume D0-D2/A3-A4 to be free
|
||
; Output: if all found in the cache, then they are copied into A5 and return
|
||
; D0 = 0. BNE if need to read (D0=block count) and A5/D3-D5 maybe changed.
|
||
;_______________________________________________________________________
|
||
inCachep MOVE.L (SP)+,-(A6)
|
||
MOVEM.L D3/D6,-(A6)
|
||
MOVEA.L FileQHdr,A3 ; A3 = ptr(FQH)
|
||
; MOVEA.L FCBSPtr,A1 ; A1 = ptr(1st FCB)
|
||
; MOVEA.L FCBVPtr(A1,D1.W),A2 ; A2 = ptr(VCB)
|
||
|
||
MOVE.L FCBFlNm(A1,D1.W),D3 ; D3 = file number
|
||
BTST #FCBRscBit,FCBMdRByt(A1,D1.W) ; resource fork?
|
||
SNE D6 ; $FF=res fork, $00=data fork
|
||
|
||
; (1) Find appropriate file queue.
|
||
@1 BSR FindFileCache ; get the file cache ptr in A4
|
||
MOVEM.L (A6)+,D3/D6
|
||
BEQ.S @7 ; br if we don't find it
|
||
;; A4 = file queue header
|
||
TST.W FHBufCnt(A4) ; any buffers?
|
||
BEQ.S @7 ; no, it's empty
|
||
LEA FHDQHFlink(A4),A3 ; file ordered queue header
|
||
MOVEA.L BLINK(A3),A4 ; last file buffer
|
||
;; MOVE.L D5,D0 ; D0 = start file block
|
||
;; ADD.L D4,D0 ; D0 = last file block (exclusive)
|
||
CMP.L DCBFlBlk(A4),D5 ; after the last one?
|
||
BHI.S @7 ; yes, none in the file queue
|
||
MOVEA.L A3,A4 ; start at top of queue
|
||
|
||
@3 MOVEA.L Flink(A4),A4 ; position to next buffer, file order
|
||
CMPA.L A3,A4 ; back to the top?
|
||
BEQ.S @7 ; yes, we searched the whole queue =>
|
||
@4 CMP.L DCBFlBlk(A4),D5 ; same file block?
|
||
BHI.S @3 ; after current block, BHI before BEQ
|
||
BEQ.S @found1 ; br if so (we found it)
|
||
MOVE.L A4,-(SP) ; not in file queue, try free queue
|
||
BSR srchDevhash ; D3.L=disk blk
|
||
BEQ.S @restoreA4 ; not found, stop searching
|
||
BSR.S copyTOuserbuf ; A4=MCB header
|
||
MOVEA.L (SP)+,A4 ; A4=file order queue hdr
|
||
BGT.S @4 ; loop until we pass the block
|
||
BRA.S @done ; all done
|
||
@found1 ;; found the buffer in file queue
|
||
LEA -MCBDQFlink(A4),A4 ; A4=MCB header
|
||
BSR.S copyTOuserbuf ; A4=MCB header
|
||
LEA.L MCBDQFlink(A4),A4 ; A4=file order queue hdr
|
||
BGT.S @3 ; loop until done
|
||
BRA.S @done ; all done
|
||
|
||
@7 ;; none of the buffers is in the file queue, serach DHT ...
|
||
BSR srchDevhash ; D3.L=disk blk
|
||
BEQ.S @notfnd ; not found, stop searching
|
||
BSR copyTOuserbuf ; just copy the data without enqueueing the buffer,
|
||
; multi-blocks read usually is read once only
|
||
BGT.S @7 ; loop until done
|
||
@done MOVEQ #0,D0 ; all in cache
|
||
|
||
@ret MOVE.L (A6)+,-(SP)
|
||
TST D0
|
||
RTS
|
||
@restoreA4 MOVEA.L (SP)+,A4
|
||
@notfnd MOVE.L D4,D0 ; not in the cache, D0=block count
|
||
BRA.S @ret
|
||
|
||
|
||
;_______________________________________________________________________
|
||
; Routine: invDHTRange
|
||
; Function: trash dev hash associated with this file/fork in a certain range.
|
||
; Input: A1.L = FCB pointer
|
||
; A2.L = VCB pointer ; assume A3 to be free
|
||
; D1.W = file refnum
|
||
; D3.L = vol/file flag. If file, D3.L = file number
|
||
; If D3 = 0, invalidate the whole volume, else just a file
|
||
; D4.L = beginning file block #
|
||
; D5.L = ending file block #
|
||
; D6.B = fork type, $FF = resource fork, $00 = data fork
|
||
; Output: none. Assume D0-D2/A0/A3/A4 to be free
|
||
; Called by trashblocks from setEOF.
|
||
;_______________________________________________________________________
|
||
invDHTRange
|
||
MOVE.W vcbVRefNum(A2),D0 ; D0 = vol refnum
|
||
MOVEA.L freeqHdr,A3 ; A3 = free q
|
||
MOVEA.L A3,A4
|
||
@5 MOVEA.L FLink(A4),A4 ; get next CB hdr
|
||
CMPA.L A3,A4 ; to the top?
|
||
BEQ.S @end ; yes
|
||
TST.L mcbAge(A4) ; in the hash?
|
||
BEQ.S @5 ; no
|
||
CMP.W MCBDevNum(A4),D0 ; same volume?
|
||
BNE.S @5 ; no
|
||
CMP.L MCBFileNum(A4),D3 ; same file number?
|
||
BNE.S @5 ; no
|
||
CMP.B MCBFkType(A4),D6 ; same fork type?
|
||
BNE.S @5 ; no
|
||
CMP.L MCBFlBlk(A4),D4 ; block to be trashed?
|
||
BHI.S @5 ; no, before start blk
|
||
CMP.L MCBFlBlk(A4),D5 ; block to be trashed?
|
||
BLO.S @5 ; no, after ending blk
|
||
; this buffer to be removed
|
||
LEA MCBHashqFlink(A4),A4 ; A4 = hashq header <13Jun90>
|
||
BSR.S DQfromDHash ; yes, dequeue it <13Jun90>
|
||
LEA -MCBHashqFlink(A4),A4 ; A4 = CB header <13Jun90>
|
||
BRA.S @5
|
||
@end
|
||
RTS
|
||
|
||
;_____________________________________________________________________
|
||
; Function: remove buffer from hash table (using vrefnum for now)
|
||
; Input: A4 = ptr(hashq header)
|
||
; Output: none, A4 preserved
|
||
;_____________________________________________________________________
|
||
DQfromDHash MOVEM.L A0,-(SP)
|
||
MOVEA.L CacheVars,A0
|
||
MOVEA.L LcDevHTPtr(A0),A0 ; A0 = hash table
|
||
SUBQ.W #1,htCount(A0) ; decrement counter
|
||
@2 CLR.L cbHqDevNum(A4) ; clear hash flag (was cbHqAge) <03Dec91 #24>
|
||
BSR DQElement ; remove it
|
||
MOVE.L A4,FLink(A4) ; init hash queue header to
|
||
MOVE.L A4,BLink(A4) ; point to itself
|
||
MOVEM.L (SP)+,A0
|
||
RTS
|
||
|
||
;_______________________________________________________________________
|
||
; Routine: invalidDHash
|
||
; Function: Trash dev hash associated with this file/volume.
|
||
; For a file, both forks are trashed.
|
||
; Input: A2.L = VCB pointer, ; D0/A3/A4 assume to be free
|
||
; D3.L = vol/file flag.
|
||
; If D3 = 0, invalidate the whole volume, else just a file
|
||
; Output: none. A3/A4 trashed
|
||
; Called by trashVblks, and trashFblocks.
|
||
;_______________________________________________________________________
|
||
invalidDHash
|
||
MOVE.W vcbVRefNum(A2),D0 ; D0 = vol refnum
|
||
MOVEA.L freeqHdr,A3 ; A3 = free q
|
||
MOVEA.L A3,A4
|
||
@loop MOVEA.L FLink(A4),A4 ; get next CB hdr
|
||
CMPA.L A3,A4 ; to the top?
|
||
BEQ.S @end ; yes
|
||
TST.L mcbAge(A4) ; in the hash?
|
||
BEQ.S @loop ; no
|
||
;; A4 is in the hash, if it belongs to this volume then we need to remove it
|
||
CMP.W MCBDevNum(A4),D0 ; same volume?
|
||
BNE.S @loop ; no, not for us
|
||
; checking my code
|
||
TST.L D3 ; whole volume?
|
||
BEQ.S @3 ; yes
|
||
CMP.L MCBFileNum(A4),D3 ; belongs to THE file?
|
||
BNE.S @loop ; no, not for us
|
||
|
||
@3 LEA MCBHashqFlink(A4),A4; A4 = hashq header
|
||
BSR.S DQfromDHash ; yes, dequeue it
|
||
LEA -MCBHashqFlink(A4),A4; A4 = CB header
|
||
BRA.S @loop ; and continue
|
||
|
||
@end RTS
|
||
|
||
;_____________________________________________________________________
|
||
; Function: convert refnum to vrefnum and store in CB HDr (using vrefnum for now)
|
||
; Save vol refnum and file number if we store this buffer in DHT.
|
||
; Input: A4.L = ptr(buffer header)
|
||
; D6.L = file number, for volume this will be 0. (this is all right
|
||
; because file number is only used by trash file blocks in DHT)
|
||
; Output: none, A4 preserved
|
||
;_____________________________________________________________________
|
||
;getdrvnum MOVEM.L D0/A0,-(SP)
|
||
; move.w MCBRefNum(A4),D0 ; file refnum?
|
||
; BLE.S @vol ; no
|
||
;
|
||
; MOVEA.L FCBsPtr,A0 ; Point to FCB array
|
||
; MOVEA.L FCBVptr(A0,D0),A0 ; Point to VCB
|
||
; MOVE.W vcbVRefNum(A0),D0 ; D0 = vol refnum
|
||
;@vol
|
||
; move.w D0,MCBDevNum(A4) ; save it in header
|
||
; MOVE.L D6,MCBFileNum(A4) ; save filenumber too
|
||
; MOVEM.L (SP)+,D0/A0
|
||
; RTS
|
||
|
||
;_____________________________________________________________________
|
||
; Function: save the buffer in the device hash table
|
||
; Input: A4 = ptr(buffer header)
|
||
; D6.L = file number, for volume this will be 0. (this is all right
|
||
; because file number is only used by trash file blocks in DHT)
|
||
; Output: none, A4 preserved
|
||
;_____________________________________________________________________
|
||
addToDhash MOVEM.L D0-D1/A0/A4,-(SP)
|
||
;; First, convert refnum to VRefnum and store in CB Hdr
|
||
MOVEQ #0,D0 ; clear high word
|
||
move.w MCBRefNum(A4),D0 ; file refnum?
|
||
BLE.S @vol ; no, volume
|
||
|
||
MOVEA.L FCBsPtr,A0 ; Point to FCB array
|
||
MOVEA.L FCBVptr(A0,D0),A0 ; Point to VCB
|
||
MOVE.W vcbVRefNum(A0),D0 ; D0 = vol refnum
|
||
@vol ;; Save vol Refnum and file number if we store this buffer in DHashTable.
|
||
move.w D0,MCBDevNum(A4) ; save VRefnum in CB header
|
||
MOVE.L D6,MCBFileNum(A4) ; save filenumber too
|
||
|
||
MOVEA.L CacheVars,A0
|
||
MOVEA.L LcDevHTPtr(A0),A0 ; A0 = hash table
|
||
ADDQ.W #1,htCount(A0) ; add one entry
|
||
MOVE.L MCBDBlk(A4),D1 ; D1 = disk blk number
|
||
BSR.S getHashKey ; D0 = VRefnum
|
||
MOVE.W D1,MCBHashKey(A4) ; save hash table position for dequeueing
|
||
LEA (A0,D1.W),A0 ; A0 = table entry
|
||
LEA MCBHashqFlink(A4),A4
|
||
BSR EQelement
|
||
|
||
MOVEM.L (SP)+,D0-D1/A0/A4
|
||
RTS
|
||
|
||
|
||
;_____________________________________________________________________
|
||
; Function: Get hash key and convert to offset into the hash table.
|
||
; Note: the first key starts from 1 because 0 is the table header.
|
||
; Input: D0.L -- vol refnum, top word cleared
|
||
; D1.L -- disk block
|
||
; A0.L -- ptr(hash table)
|
||
; Output: D1.W = key (convert to offset), offset is only a word 'cause max. entry is 512.
|
||
; D0 trashed, A0 preserved.
|
||
;
|
||
; 04Jul90 KST New today
|
||
; 15Oct90 KST Changed to use logical AND to compute hash key. This is faster
|
||
; than DIVU and we don't need to worry about divide overflow.
|
||
;_____________________________________________________________________
|
||
getHashKey
|
||
ADD.L D0,D1 ; D1 = D0+D1
|
||
MOVE.W htKeyMask(A0),D0 ; D0.W = bucket number mask
|
||
getFHKey ; get file hash key
|
||
;; DIVU.W D0,D1 ; if overflow, operand unchanged !!!
|
||
;; SWAP D1 ; take reminder as key
|
||
AND.W D0,D1 ; D1 is reminder (always a WORD), use it as key
|
||
ADDQ.W #1,D1 ; but 0 is the header
|
||
LSL.W #htEntryLog2,D1 ; convert to offset
|
||
RTS
|
||
|
||
;_____________________________________________________________________
|
||
; Function: search the buffer in the device hash table
|
||
; Input: D3.L -- disk block
|
||
; A2.L -- VCB ptr
|
||
; Output: D0 = 0 if not found, else D0=A4.L = CB header
|
||
;_____________________________________________________________________
|
||
srchDevhash MOVEM.L D1/A0-A1,-(SP)
|
||
|
||
MOVEA.L CacheVars,A0
|
||
MOVE.L LcDevHTPtr(A0),D0 ; D0 = dev. hash table
|
||
BEQ.S srchend ; no hash table
|
||
MOVEA.L D0,A0 ; A0 = hash table
|
||
MOVE.W htCount(A0),D0 ; any entry?
|
||
BEQ.S srchend ; empty
|
||
MOVEQ #0,D0
|
||
MOVE.W vcbVRefNum(A2),D0 ; D0.L = vol refnum
|
||
MOVE.L D3,D1 ; D1.L = disk block
|
||
|
||
BSR.S getHashKey
|
||
MOVE.W vcbVRefNum(A2),D0 ; D0 = vol refnum
|
||
LEA (A0,D1.W),A1 ; A1 = hashq header
|
||
MOVEA.L A1,A4
|
||
@loop MOVEA.L (A4),A4 ; next cb hdr
|
||
CMP.L A1,A4 ; that's all?
|
||
BEQ.S srchnotfnd ; yes
|
||
CMP.L cbHqDevBlk(A4),D3 ; same disk block?
|
||
BNE.S @loop ; no
|
||
CMP.W cbHqDevNum(A4),D0 ; same refnum?
|
||
BNE.S @loop ; no
|
||
|
||
srchFnd LEA -MCBHashqFlink(A4),A4
|
||
MOVE.L A4,D0 ; return MCBhdr
|
||
srchend MOVEM.L (SP)+,D1/A0-A1
|
||
RTS
|
||
|
||
srchnotfnd MOVEQ #0,D0
|
||
BRA.S srchend
|
||
|
||
|
||
;; hash code end
|
||
|
||
;_____________________________________________________________________
|
||
; Function: Map a buffer ptr into CB header ptr
|
||
; Input: A0.L = ptr(data buffer), assume A4 is free.
|
||
; Output: A4.L = ptr(CBheader), all other registers unchanged
|
||
;_____________________________________________________________________
|
||
mapCBHeader
|
||
MOVEM.L D0-D1/A0-A1,-(SP)
|
||
MOVEA.L CacheVars,A4
|
||
|
||
MOVE.L LcXCacheKey(A4),D0
|
||
CMPA.L D0,A0 ; in the cache?
|
||
BEQ.S @incache ; yes
|
||
;; not in cache, need to do some computation
|
||
MOVE.L BufStart(A4),A1 ; data buf start
|
||
SUBA.L A1,A0 ; A0 = delta
|
||
MOVE.L A0,D0
|
||
BEQ.S @0 ; D0 = 0
|
||
MOVEQ #log2Bsize,D1
|
||
LSR.L D1,D0 ; get buffer index
|
||
MULU #MCBHLength,D0 ; offset into CB header array
|
||
|
||
@0 MOVE.L CBHdrStart(A4),A4
|
||
ADDA.L D0,A4 ; this is the header
|
||
BRA.S @ret
|
||
@incache
|
||
MOVEA.L LcXCacheData(A4),A4
|
||
|
||
@ret MOVEM.L (SP)+,D0-D1/A0-A1
|
||
RTS
|
||
|
||
;__________________________________________________________________________;
|
||
;
|
||
; Subroutine: CacheCheck
|
||
; Function: Verify that the cache buffers are cool. This is for debug only.
|
||
; Arguments: none. Traps if error detected.
|
||
;__________________________________________________________________________;
|
||
|
||
IF CacheDebug THEN
|
||
|
||
CacheCheck
|
||
MOVEM.L D0-D4/A0-A4,-(SP) ; save all regs
|
||
|
||
MOVEA.L FileQHdr,A3 ; step through the file queue
|
||
MOVEQ #0,D4 ; total # buffers
|
||
MOVEA.L A3,A4 ; start at top of file queue
|
||
|
||
fqLoop MOVE.L A4,A0 ; save ptr to current element <31Dec85>
|
||
MOVEA.L FHFlink(A4),A4 ; position to next file queue
|
||
CMPA.L FHBlink(A4),A0 ; back link cool? <31Dec85>
|
||
BEQ.S @0 ; br if so <31Dec85>
|
||
_CacheDebug StrFQBad
|
||
|
||
@0 CMPA.L A3,A4 ; back to top of queue ? <31Dec85>
|
||
BEQ fqLoopEnd ; exit loop if so
|
||
MOVE.L FHVCBPtr(A4),D0 ; queue free?
|
||
BEQ.S fqLoop ; loop if so
|
||
MOVE.L D0,A2 ; get VCB ptr
|
||
TST.W VCBDrvNum(A2) ; better be an on-line volume
|
||
BGT.S @1 ; br if so
|
||
_CacheDebug StrFQBad
|
||
|
||
@1 LEA FHMQHFlink(A4),A1
|
||
MOVE.L A1,A2 ; start at top of mru queue
|
||
MOVEQ #0,D0 ; count of buffers we find
|
||
MOVEQ #-1,D1 ; all blocks should have age < this
|
||
|
||
mruLoop MOVE.L A2,A0 ; save ptr to current element <31Dec85>
|
||
MOVEA.L MCBFlink(A2),A2 ; position to next file buffer, mru order
|
||
CMPA.L MCBBlink(A2),A0 ; back link cool? <31Dec85>
|
||
BEQ.S @4 ; br if so <31Dec85>
|
||
_CacheDebug StrMRUQBad
|
||
|
||
@4 CMPA.L A1,A2 ; back to top of queue ? <31Dec85>
|
||
BEQ.S @2 ; exit loop if so
|
||
ADDQ #1,D0 ; incr buffer count
|
||
CMP.L MCBFQPtr(A2),A1 ; queue head ptr ok?
|
||
BEQ.S @0
|
||
_CacheDebug StrMRUQBad
|
||
|
||
@0 MOVE.L MCBAge(A2),D3 ; make sure block ages are decreasing
|
||
CMP.L D1,D3
|
||
BLS.S @1 ; br if so
|
||
_CacheDebug StrAgeProb
|
||
|
||
@1 MOVE.L D3,D1
|
||
BRA.S mruLoop ; and keep looping
|
||
|
||
@2 CMP.W MQHBufCnt(A1),D0 ; buffer count ok?
|
||
BEQ.S @3
|
||
_CacheDebug StrMRUCnt
|
||
|
||
@3 ADD D0,D4 ; keep total count ok
|
||
LEA FHDQHFLink(A4),A1 ; file-ordered queue header
|
||
MOVEA.L A1,A2 ; start at top of queue
|
||
MOVEQ #-1,D1 ; all blocks should be > this
|
||
MOVEQ #0,D2 ; buffer count
|
||
|
||
foLoop MOVE.L A2,A0 ; save ptr to current element <31Dec85>
|
||
MOVEA.L DCBFlink(A2),A2 ; position to next buffer, file order
|
||
CMPA.L DCBBlink(A2),A0 ; back link cool? <31Dec85>
|
||
BEQ.S @4 ; br if so <31Dec85>
|
||
_CacheDebug StrDQBad
|
||
|
||
@4 CMPA.L A1,A2 ; back to top of queue ? <31Dec85>
|
||
BEQ.S @2 ; exit loop if so
|
||
ADDQ #1,D2 ; incr buffer count
|
||
MOVE.L DCBFlBlk(A2),D3 ; make sure file block numbers are increasing
|
||
CMP.L D1,D3
|
||
BGT.S @1 ; br if so
|
||
_CacheDebug StrDQBad
|
||
@1 MOVE.L D3,D1
|
||
BRA.S foLoop ; and keep looping
|
||
|
||
@2 CMP D0,D2 ; same buffer count as mru queue?
|
||
BEQ.S @3 ; br if so
|
||
_CacheDebug StrDQCnt
|
||
|
||
@3 BRA fqLoop ; go for the next file
|
||
|
||
fqLoopEnd MOVE.L FreeQHdr,A3 ; now check the free queue
|
||
MOVEQ #0,D3 ; total # buffers
|
||
MOVEA.L A3,A4 ; start at top of file queue
|
||
|
||
freeLoop MOVE.L A4,A0 ; save ptr to current element <31Dec85>
|
||
MOVEA.L FreeQFlink(A4),A4 ; position to next free buffer
|
||
CMPA.L FreeQBlink(A4),A0 ; back link cool? <31Dec85>
|
||
BEQ.S @4 ; br if so <31Dec85>
|
||
_CacheDebug StrFreeQ
|
||
|
||
@4 CMPA.L A3,A4 ; back to top of queue ? <31Dec85>
|
||
BEQ.S @1 ; exit loop if so
|
||
ADDQ #1,D3 ; bump buffer count
|
||
CMP.L MCBFQPtr(A4),A3 ; header ptr ok?
|
||
BEQ.S @0
|
||
_CacheDebug StrFreeQ
|
||
|
||
@0 LEA MCBDQFlink(A4),A0 ; <01Jan86>
|
||
CMP.L FLink(A0),A0 ; file-ordered queue header should <01Jan86>
|
||
BNE.S @5 ; point to itself <01Jan86>
|
||
CMP.L BLink(A0),A0 ; <01Jan86>
|
||
BEQ.S @6 ; <01Jan86>
|
||
@5 _CacheDebug StrFreeQ ; <01Jan86>
|
||
@6 BRA.S freeLoop ; <01Jan86>
|
||
|
||
@1 CMP.W FreeQBufCnt(A3),D3 ; does the total add up?
|
||
BEQ.S @2 ; br if so
|
||
_CacheDebug StrFreeQCnt
|
||
|
||
@2 ADD.W D3,D4 ; add to get total buffer count
|
||
CMP.W TotalBufCnt(A3),D4 ; check?
|
||
BEQ.S @3 ; br if so
|
||
_CacheDebug StrTotalCnt
|
||
|
||
@3 MOVEM.L (SP)+,D0-D4/A0-A4 ; restore regs
|
||
RTS ; return if nothing wrong
|
||
|
||
ENDIF
|
||
|
||
STRING PASCAL
|
||
IF CacheDebug THEN
|
||
StrDQErr DC.B 'Cache: DQ Dirty'
|
||
StrBufUse DC.B 'Cache: Buf in use'
|
||
StrBlk0 DC.B 'Cache: Disk Block 0'
|
||
StrFQBad DC.B 'Cache: FQ Bad'
|
||
StrMRUQBad DC.B 'Cache: MRU Bad'
|
||
StrMRUCnt DC.B 'Cache: MRU Cnt Bad'
|
||
StrAgeProb DC.B 'Cache: Aging Bad'
|
||
StrDQBad DC.B 'Cache: DQ Bad'
|
||
StrDQCnt DC.B 'Cache: DQ Cnt Bad'
|
||
StrFreeQ DC.B 'Cache: Free Q Bad'
|
||
StrFreeQCnt DC.B 'Cache: Free Q Cnt Bad'
|
||
StrTotalCnt DC.B 'Cache: Total Cnt Bad'
|
||
ENDIF
|
||
|
||
IF NOT forROM THEN ; <SM3> rb
|
||
|
||
CutBack
|
||
_SetHandleSize ; adjust our size
|
||
|
||
IF NewInit70 THEN ; detach for INIT
|
||
MOVE.L A0,-(SP) ; A0 = handle
|
||
_DetachResource ; pass it to resource manager
|
||
ENDIF
|
||
|
||
RTS
|
||
EndResident
|
||
|
||
ENDIF ; <SM3> rb
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; Code after this point will be discarded !!
|
||
|
||
;_________________________________________________________________________________
|
||
; Routine: verifyCacheSize (System 7 only!)
|
||
; Function: Make sure cache size is not too big for 7.0 system to boot.
|
||
; 1. We require at least 16K for the DiskCache to boot this MAC!
|
||
; 2. We reserve 32K for other INITs that move 'Bufptr' such as AppleShare.
|
||
; •• This code is not efficient, but it's all right since we only run it once at install time.
|
||
; Input: A0.L - Ptr to cache locals.
|
||
; D0.L - User requested cache size in 32K multiple.
|
||
; D2.L - The lower bound of BufPtr (from 7.0b4+). Preserved on return.
|
||
; Assume D1 to be free.
|
||
; Output: D0.L - verified cache size in number of bytes.
|
||
; Modification History:
|
||
; 15May90 KST Adding the code to check upper bound limit against actual
|
||
; physical memory that Ramcache can allocate. The max size
|
||
; is calculated based on memory CDEV and may be changed later.
|
||
; 20Dec90 KST In B4, Boot code passes me the lower bound of BufPtr in D2.
|
||
;_________________________________________________________________________________
|
||
verifyCacheSize
|
||
;; from B4, we use parameter D2 to calculate the max. cache size.
|
||
MOVEM.L D2-D3,-(SP) ; do we need to save ??
|
||
MOVE.L BufPtr,D1 ; current bufptr
|
||
SUB.L D2,D1 ; D1 is the available memory
|
||
;; SUB.L #n32K,D1 ; reserve 32K for other INITs that move 'Bufptr'
|
||
MOVE.L D1,D2 ; move to D2
|
||
;; but we can't grab them all, some more arithmetics required:
|
||
MOVE.L #fileHdrCnt,D1 ; # of file headers
|
||
MULU #FHlength,D1 ; size of file Hdrs
|
||
ADD.W #FreeQHlen+FQHlength,D1 ; some bytes for freeQ header record
|
||
; and some bytes for fileQ header record
|
||
SUB.L D1,D2 ; reserve memory for file header
|
||
|
||
;; D2.L = memory available - file headers - 32K reserved
|
||
;; D0.L = n*32K, D1,D3 are free
|
||
MOVEQ #Byteto32K,D1 ; shift by this many bits
|
||
LSL.L D1,D0 ; D0.L = max bytes
|
||
MOVEQ #log2Bsize,D1 ; D1.L = log2 of buffer size
|
||
|
||
@2 MOVE.L D0,D3 ; save in D3
|
||
LSR.L D1,D3 ; D3 = number of buffers
|
||
MULU #MCBHLength,D3 ; total memory for CB headers
|
||
ADD.L D0,D3 ; total memory for DiskCache
|
||
|
||
CMP.L D2,D3 ; do we have enough memory?
|
||
BLT.S @4 ; yes
|
||
SUB.L #n32K,D0 ; else, reduce cache size by 32K
|
||
BLE.S @6 ; can't be negative or zero
|
||
BRA.S @2 ; and check again
|
||
|
||
@4 CMP.L #n256K,D0 ; D0 > 256K?
|
||
BLE.S @5 ; no
|
||
ANDI.L #$FFFF0000,D0 ; ignore lower 32K if size is above 256K
|
||
; to be consistent with memory CDEV
|
||
@5 MOVEM.L (SP)+,D2-D3 ; restore regs and
|
||
RTS ; return size in D0
|
||
|
||
@6 BMI noMemDead ; if < 0, no memory to boot
|
||
MOVE.L #kMinBootSize,D0 ; if = 0, try minimum boot size
|
||
BRA.S @2 ; give it one last try
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: CacheInstall
|
||
;
|
||
; Function: This routine is called by the CacheLoad routine in the cache
|
||
; INIT resource. It installs RAM-caching.
|
||
;
|
||
; Input: (Pre-System 7 only!)
|
||
; A0.L - Ptr to cache locals.
|
||
; NewBufPtr(A0) - base of RAM cache space
|
||
; OldBufPtr(A0) - limit of RAM cache space
|
||
; (System 7 only!)
|
||
; D2.L - The lower bound of BufPtr (from 7.0b4+).
|
||
; Modification History:
|
||
; 21May90 KST Adding the code to check upper bound limit against actual
|
||
; physical memory that Ramcache can allocate. The max size
|
||
; is calculated based on memory CDEV and may be changed later.
|
||
;_________________________________________________________________________________
|
||
EXPORT CacheInstall ; <SM3> rb
|
||
CacheInstall
|
||
|
||
; assume A0/A1 to be free
|
||
MOVEQ #CacheLclLen,D0 ; get space for locals
|
||
_NewPtr ,SYS,CLEAR ; off system heap (it MUST not fail)
|
||
MOVE.L A0,CacheVars ; save for later
|
||
|
||
; Determine size of cache and allocate the space above BufPtr.
|
||
MOVEQ #0,D0 ; clear high bytes
|
||
MOVE.B CacheConfig,D0 ; get size configuration
|
||
BEQ.S @useMinCache ; use minimum size if zero
|
||
BSR.S verifyCacheSize ; make sure cachesize is not too big for 7.0?
|
||
BRA.S @gotSize
|
||
@useMinCache
|
||
MOVE.W #kMinCacheSize,D0 ; current minimum is 32K <23Apr90>
|
||
BSET #5,CacheEnable ; enable ramcache <23Apr90>
|
||
|
||
@gotSize
|
||
;; D0.L = DiskCache size in number of bytes.
|
||
;; Since we are not going to use the memory allocated by ROM, we need to do some
|
||
;; calculation to figure out how much more memory we need to grab from above BufPtr.
|
||
;; (low mem)
|
||
|
||
; --+------------- => LcNewBufPtr(A0)
|
||
; | FreeQ hdr -> FreeQHdr ($378)
|
||
; --+-------------
|
||
; | FileQ buf -> FileQHdr ($37C)
|
||
; --+-------------
|
||
; |
|
||
; | File Headers
|
||
; |
|
||
; --+------------- => LcCBHdrStart(A0)
|
||
; |
|
||
; | CBlock Headers
|
||
; |
|
||
; --+------------- => LcBufStart(A0)
|
||
; |
|
||
; |
|
||
; | Data buffers
|
||
; |
|
||
; |
|
||
; --+-------------
|
||
; | possible unused mem
|
||
; --+------------- => LcOldBufPtr(A0)
|
||
|
||
;; (high mem)
|
||
|
||
MOVE.L BufPtr,A3 ; get current value of BufPtr
|
||
MOVE.L A3,OldBufPtr(A0) ; save value of BufPtr at enable time
|
||
SUBA.L D0,A3 ; <02Dec86> (BufPtr)-(configured cache size)
|
||
;; make data buffer on page boundary ???
|
||
MOVE.L A3,D1
|
||
ANDI.L #BufSiz-1,D1 ; buffer on page boundary ???
|
||
MOVE.W D1,LcFreeMemSize(A0); bytes unused
|
||
beq.s @2 ; yes
|
||
SUBA.L D1,A3 ; make it on page boundary
|
||
; yes, we waste some memory
|
||
@2 MOVE.L A3,BufStart(A0) ; start of data Buffers <20Apr90>
|
||
MOVEQ #log2Bsize,D1 ; <20Apr90>
|
||
LSR.L D1,D0 ; get number of buffers <20Apr90>
|
||
MOVE.W D0,TotalBufN(A0) ; save total number <20Apr90>
|
||
MULU #MCBHLength,D0 ; size of CB headers <20Apr90>
|
||
SUB.L D0,A3 ; allocate mem for headers <20Apr90>
|
||
MOVE.L A3,CBHdrStart(A0) ; remember it <20Apr90>
|
||
BSR clearMem ; header default to 0 <20Apr90>
|
||
|
||
MOVE.L #fileHdrCnt,D0 ; # of file headers <20Apr90>
|
||
MULU #FHlength,D0 ; size of file Hdrs <20Apr90>
|
||
ADD.W #FreeQHlen,D0 ; some bytes for freeQ record <20Apr90>
|
||
ADD.W #FQHlength,D0 ; and some bytes for fileQ rec <20Apr90>
|
||
SUB.L D0,A3 ; allocate mem for all three <20Apr90>
|
||
;; final check against lower bound: <21Dec90>
|
||
CMP.L D2,A3 ; do we have enough memory? <21Dec90>
|
||
BLT noMemdead ; can't get lower than D2 <21Dec90>
|
||
|
||
@4 BSR clearMem ; default to 0 <20Apr90>
|
||
MOVE.L A3,BufPtr ; set new value of BufPtr
|
||
MOVE.L A3,NewBufPtr(A0) ; remember it
|
||
MOVE.W #19284,LcReserved1(A0) ; for debugging
|
||
|
||
lea CacheInSub,A1 ; <17APR90>
|
||
move.l A1,D0 ; <17APR90> Clean up the address
|
||
_StripAddress ; <17APR90>
|
||
MOVE.L D0,-(SP) ; <17APR90> pass routine address
|
||
|
||
BSR SyncFSCall ; <02Dec86> synchronize with fs and call below
|
||
|
||
IF NOT forROM THEN ; <SM3> rb
|
||
|
||
LEA CacheStart,A0 ; get beginning of resident code
|
||
LEA EndResident,A1 ; get end of resident code
|
||
SUB.L A0,A1 ; calculate size
|
||
_RecoverHandle ; recover handle to code (pointed to by A0)
|
||
MOVE.L A1,D0 ; put size in D0 for SetHandleSize
|
||
BRA CutBack ; and slice away.
|
||
|
||
ELSE ; we are the ROM, this is no patch <SM3> rb
|
||
RTS ; <SM3> rb
|
||
ENDIF ; <SM3> rb
|
||
|
||
; The following code is executed while the file system is in a suspended state.
|
||
; Input: A0.L = ptr(cachevar)
|
||
CacheInSub
|
||
ST CurEnable(A0) ; mark enabled
|
||
|
||
BSR Init700CacheQs ; init for new memory scheme <20Apr90>
|
||
BSR InstDHTable ; install device hash table <04Aug90>
|
||
BSR InstCVect ; install the RAM cache vectors
|
||
|
||
MOVE.B CacheConfig,CurConfig(A0) ; save configuration
|
||
CLR.L CacheAge(A0) ; start age at 0 again (counts up)
|
||
CLR.B CacheCom ; init Cache communication word
|
||
|
||
IF NewInit70 THEN ; <17Apr90>
|
||
BSET #dontInstRC,CacheCom; don't do RamCacheInstall in the system file
|
||
ENDIF ; <17Apr90>
|
||
|
||
move.l #minApplZone,CacheMinZn(a0) ; set initial min zone size <20Nov87>
|
||
|
||
IF CacheDebug THEN
|
||
BSR CacheCheck ; see if we installed correctly
|
||
ENDIF
|
||
|
||
RTS
|
||
|
||
;; Init code
|
||
;_________________________________________________________________________________
|
||
; Function: install device hash table -- bucket number = 1/8 buffer number
|
||
; Input: A0 - cache locals ptr
|
||
; Output: none, A0 preserved
|
||
;
|
||
; 04Jul90 KST New today
|
||
; 15Oct90 KST Make minimum bucket to be 16. Allocate one more entry for the header
|
||
; so we can use logical AND to compute hash key.
|
||
;_________________________________________________________________________________
|
||
InstDHTable
|
||
MOVEM.L D0-D1/A0-A1,-(SP) ; free some regs
|
||
MOVEA.L A0,A1 ; A1 = ptr(cachevars)
|
||
;; allocate memory here for the free queue device hash table.
|
||
MOVEQ #0,D0 ; bucket = bufN/8
|
||
MOVE.W TotalBufN(A1),D0 ; buf number
|
||
|
||
;; D0.L = number of buckets of the hash table, check bounds
|
||
LSR.L #DevHTsize,D0 ; bucket number = 1/8 buffer number
|
||
CMPI.L #minDHTnum,D0 ; >= 16?
|
||
BEQ.S @sizeOK ; skip next 2 checkS
|
||
BHI.S @3 ; yes,
|
||
MOVE.L #minDHTnum,D0 ; or else, use minimum
|
||
BRA.S @sizeOK ; skip next check
|
||
@3 CMPI.L #maxDHTnum,D0 ; too big?
|
||
BLO.S @5 ; no
|
||
MOVE.L #maxDHTnum,D0 ; or else, use maximum
|
||
BRA.S @sizeOK ; skip next check
|
||
@5 ;; need to make sure D0 is power of 2 for the key mask to work correctly.
|
||
;; we know D0 is 16 < D0 < 512, it has to be 32,64,128, or 256.
|
||
MOVEQ #minDHTnum,D1 ; start from minimum
|
||
@7 LSL.W #1,D1 ; * 2
|
||
CMP.W D1,D0 ; <= D1?
|
||
BEQ.S @11 ; right on
|
||
BHI.S @7 ; D0 > D1, check next level
|
||
;; upperbound (D1) > D0 > lowerbound, max will be 512
|
||
MOVE.W D1,D0 ; use upper bound
|
||
@sizeOK MOVE.W D0,D1 ; save in D1 (WORD)
|
||
@11 ADDQ.L #1,D0 ; one more entry for the hash table header
|
||
LSL.L #htEntryLog2,D0 ; convert to bytes of the hash table
|
||
;; D0.L = byte size of the hash table (upper word = 0)
|
||
_NewPtr ,SYS,CLEAR ; off system heap (it MUST not fail)
|
||
BNE.S noMemdead ; something's wrong
|
||
MOVE.L A0,LcDevHTPtr(A1) ; save it in cachevars
|
||
|
||
;_____________________________________________________________________
|
||
; Function: initialize hash table
|
||
; Input: A0.L = ptr(hash table)
|
||
; D1.W = total number of buckets of the hash table excluding header
|
||
; OutPut: none, all other registers unchanged
|
||
;_____________________________________________________________________
|
||
;initHashTable
|
||
MOVE.L D1,htReserved(A0) ; save for debugging only (as LONG)
|
||
SUBQ #1,D1 ; for Dbraing, also is the key mask
|
||
MOVE.W D1,htKeyMask(A0) ; save bucket count-1 as mask (word)
|
||
CLR.W htCount(A0) ; empty state
|
||
LEA htStart(A0),A0 ; table actually starts here
|
||
@initHQloop
|
||
MOVE.L A0,FLink(A0) ; init hash queue to <04May90>
|
||
MOVE.L A0,BLink(A0) ; point to itself <04May90>
|
||
ADDA.L #htEntrysize,A0 ; points to next entry
|
||
DBRA D1,@initHQloop
|
||
MOVEM.L (SP)+,D0-D1/A0-A1
|
||
RTS
|
||
|
||
noMemdead moveq.l #dsMemFullErr, d0 ; sys heap must screw up, so punt
|
||
_SysError
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: SyncFSCall
|
||
;
|
||
; Function: This routine is used to synchronize with the file system, disable it,
|
||
; call the passed routine, then reenable the file system.
|
||
;
|
||
; Arguments: Routine address is on the stack.
|
||
;
|
||
; - get in at cmd done hook since file system busy is cleared before the
|
||
; completion routine takes over.
|
||
;_________________________________________________________________________________
|
||
|
||
SyncStkFrame RECORD {A6Link},DECR
|
||
ParamTop DS.W 1
|
||
routine DS.L 1 ; pointer to routine to call
|
||
ParamSize EQU ParamTop-*
|
||
Return DS.L 1
|
||
A6Link DS.L 1
|
||
lclIOBlk DS.B ioHFQElSiz ; local i/o block
|
||
LocalSize EQU *
|
||
ENDR
|
||
|
||
SyncFSCall ;<02Dec86>
|
||
WITH SyncStkFrame
|
||
LINK A6,#LocalSize
|
||
|
||
MOVE.L CacheVars,A0 ; get ptr to cache locals
|
||
LEA lclIOBlk(A6),A1 ; pass I/O block
|
||
MOVE.L A1,CacheIOBlk(A0) ; this tells us when the call comes thru
|
||
MOVE.L routine(A6),CacheRoutine(A0) ; also pass the routine to call
|
||
MOVE.L ExtFSHook,SavedHook(A0) ; save CmdDone file system hook
|
||
|
||
LEA Intercept,A0 ; <17APR90>
|
||
move.l A0,D0 ; <17APR90> Clean up the address
|
||
_StripAddress ; <17APR90>
|
||
MOVE.L D0,ExtFSHook ; now install our own intercept at fs done
|
||
|
||
MOVE.L A1,A0
|
||
MOVEQ #(ioHFQElSiz/2-1),D0 ; DBRA word loop index
|
||
@1 CLR.W (A1)+ ; ...cleared.
|
||
DBRA D0,@1
|
||
|
||
_FSControl ; call FS synchronously
|
||
|
||
MOVE.L CacheVars,A0 ; get ptr to cache locals
|
||
MOVE.L SavedHook(A0),ExtFSHook ; restore old hook
|
||
|
||
MOVEQ #ParamSize,D0 ; and exit
|
||
UNLK A6
|
||
MOVE.L (SP)+,A0 ; get return address
|
||
ADD D0,SP ; strip parameters
|
||
JMP (A0)
|
||
|
||
HookRegs REG D0-D1/D3-D7/A0/A2-A6 ; save all but D2/A1
|
||
|
||
Intercept ;; called in CmdDone to install RamCache -- KST
|
||
MOVEM.L HookRegs,-(SP)
|
||
MOVE.L CacheVars,A0 ; get ptr to cache locals
|
||
MOVE.L FSQHead,A1 ; A1 -> current request
|
||
CMP.L CacheIOBlk(A0),A1 ; is this our call?
|
||
BNE.S @next
|
||
|
||
BSR.S FlushAll ; <02Dec86> do a low-level flush
|
||
; clear old cache buffers
|
||
MOVE.L CacheVars,A0 ; get ptr to cache locals
|
||
MOVE.L CacheRoutine(A0),A1 ; get routine
|
||
JSR (A1) ; call it, passing A0
|
||
|
||
@next
|
||
MOVE.L CacheVars,A0 ; get ptr to cache locals
|
||
MOVE.L SavedHook(A0),D2 ; save CmdDone file system hook
|
||
MOVEM.L (SP)+,HookRegs ; restore regs
|
||
BLE.S @1 ; br if no other hook . . .
|
||
MOVE.L D2,A1 ;
|
||
JMP (A1) ; call it in turn
|
||
@1
|
||
RTS
|
||
|
||
ENDWITH ; SyncStkFrame
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: FlushAll
|
||
;
|
||
; Function: This routine is used flush out all cache buffers. It is called
|
||
; before adding or deleting buffers, or installing or removing
|
||
; the RAM cache. It must be called with the file system suspended
|
||
; since it uses the file system’s A6 stack.
|
||
;
|
||
; Arguments: None.
|
||
;
|
||
; NOTE: This flush needs to work regardless of which cache is currently
|
||
; installed. It may not use a FlushVolume call since the file system
|
||
; is currently suspended.
|
||
;
|
||
;_________________________________________________________________________________
|
||
|
||
vFlushCache
|
||
MOVE.L jFlushCache,-(SP) ; always go thru vector
|
||
RTS
|
||
|
||
FlushAll ;<02Dec86>
|
||
MOVEM.L AllRegs,-(SP)
|
||
CLR.B FSCallAsync ; do it sync!
|
||
MOVEA.L HFSStkTop,A6 ; Set up A6 for use as stack pointer
|
||
MOVE.L VCBQHdr+QHead,D0 ; zip thru all VCBs
|
||
|
||
@loop
|
||
BEQ.S @exit ; exit if no more VCBs.
|
||
MOVE.L D0,A2 ; VCB ptr
|
||
BSR.S FlushOne ; flush it
|
||
MOVE.L QLink(A2),D0 ; advance to next VCB
|
||
BRA.S @loop ; go check this one
|
||
@exit
|
||
MOVEM.L (SP)+,AllRegs
|
||
RTS
|
||
|
||
FlushOne
|
||
MOVEQ #0,D1 ; Clear options byte (we’ll trash later)
|
||
ST CacheFlag ; Yes, REALLY flush . . .
|
||
|
||
MOVE.L SysBMCPtr,A1 ; System-wide bitmap cache pointer
|
||
MOVE.W VCBVRefNum(A2),D0 ; Volume refnum
|
||
BSR.S vFlushCache ; A2=VCB ptr
|
||
|
||
MOVE.L SysCtlCPtr,A1 ; System-wide control cache pointer
|
||
MOVE.W VCBVRefNum(A2),D0 ; Volume refnum
|
||
BSR.S vFlushCache ; A2=VCB ptr
|
||
|
||
MOVE.L SysVolCPtr,A1 ; System-wide volume cache pointer
|
||
MOVE.W VCBVRefNum(A2),D0 ; Volume refnum
|
||
BSR.S vFlushCache ; A2=VCB ptr
|
||
RTS
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; Routine: AdjByteLim
|
||
;
|
||
; Arguments: A0 (in ) -- cache locals ptr
|
||
;
|
||
; Function: Sets up CacheByteLim(A0).
|
||
;
|
||
; 128K cache has about 256 buffers. Caching 8 block requests seems to work fine here,
|
||
; so, 256/8=32. We divide buffer count by 32 then multiply by 512 to get byte count:
|
||
; equivalent to just a x16 multiply.
|
||
;
|
||
; Buffers Max Size Cached
|
||
; cache size 32K 60 960 - 1 block
|
||
; 64K 120 1920 - 3 blocks
|
||
; 128K 240 3840 - 7 blocks
|
||
; 256K 481 7696 - 15 blocks
|
||
; 384K 721 11536 - 22 blocks
|
||
; 512K 962 15392 - 30 blocks
|
||
; 768K 1443 23088 - 45 blocks
|
||
;
|
||
; It might be better to have some limit on this since the cache is not set up
|
||
; yet to efficiently handle very large requests . . .
|
||
;_________________________________________________________________________________
|
||
|
||
AdjByteLim
|
||
MOVEM.L A1/D0,-(SP) ; save all regs
|
||
|
||
MOVE.L FreeQHdr,A1
|
||
MOVEQ #0,D0
|
||
MOVE.W TotalBufCnt(A1),D0 ; get cache size in buffers
|
||
SUBQ.W #1,D0 ; 7.0 has more buffers than 6.0
|
||
LSL.L #4,D0 ; X16 = max byte size to cache
|
||
MOVE.L D0,CacheByteLim(A0) ; set limit on size of request to cache
|
||
|
||
MOVEM.L (SP)+,A1/D0
|
||
RTS
|
||
|
||
;_________________________________________________________________________________
|
||
;
|
||
; InstCVect subroutine.
|
||
;
|
||
; Function: Installs 8 cache routines by replacement of appropriate
|
||
; trap vectors.
|
||
;
|
||
; Input: A0 - cache locals ptr
|
||
; Output: none
|
||
;_________________________________________________________________________________
|
||
|
||
InstTbl
|
||
DC.W FlushCache-InstTbl
|
||
DC.W GetBlock-InstTbl
|
||
DC.W MarkBlock-InstTbl
|
||
DC.W RelBlock-InstTbl
|
||
DC.W TBStartSearch-InstTbl
|
||
DC.W TrashVBlks-InstTbl
|
||
DC.W CacheWrIP-InstTbl
|
||
DC.W CacheRdIP-InstTbl
|
||
|
||
InstCVect
|
||
MOVEM.L D0-D1/A1-A4,-(SP) ; preserve all regs
|
||
LEA InstTbl,A3 ; get base address
|
||
MOVE.L A3,A4 ; save it
|
||
LEA jFlushCache,A2 ; first vector address
|
||
MOVEQ #CacheVCnt-1,D0 ; set up loop count
|
||
|
||
@1
|
||
MOVE.W (A3)+,D1 ; get next offset
|
||
EXT.L D1 ; extend it
|
||
ADD.L A4,D1 ; add base address
|
||
MOVE.L D1,(A2)+ ; install the new routine
|
||
DBRA D0,@1
|
||
|
||
MOVEM.L (SP)+,D0-D1/A1-A4 ; restore regs
|
||
RTS ; return
|
||
|
||
_AssumeEq jFlushCache+4,jGetBlock
|
||
_AssumeEq jGetBlock+4,jMarkBlock
|
||
_AssumeEq jMarkBlock+4,jRelBlock
|
||
_AssumeEq jRelBlock+4,jTrashBlocks
|
||
_AssumeEq jTrashBlocks+4,jTrashVBlks
|
||
_AssumeEq jTrashVBlks+4,jCacheWrIP
|
||
_AssumeEq jCacheWrIP+4,jCacheRdIP
|
||
|
||
;_____________________________________________________________________
|
||
; Function: clear memory in a fast way
|
||
; Input: A3.L = ptr(buffer) D0.L = size
|
||
; OutPut: none, all registers unchanged
|
||
;_____________________________________________________________________
|
||
clearMem MOVEM.L A3/D0-D2,-(SP)
|
||
MOVE.L D0,D1
|
||
BEQ.S @6 ; 0 bytes
|
||
LSR.L #2,D0 ; D0 = # of long words <18Sep90>
|
||
BEQ.S @4 ; < 4 bytes
|
||
MOVE.L D0,D2 ; D2 = # of long words
|
||
LSL.L #2,D2 ; *4 = # of bytes <18Sep90>
|
||
SUB.L D2,D1 ; D1 = bytes left
|
||
SUBQ #1,D0 ; for DBRing
|
||
@1 CLR.L (A3)+
|
||
DBRA D0,@1
|
||
|
||
BRA.S @4 ; D1 will decrement first
|
||
@3 CLR.B (A3)+
|
||
@4 DBRA D1,@3
|
||
@6 MOVEM.L (SP)+,A3/D0-D2
|
||
RTS
|
||
|
||
;_________________________________________________________________________________
|
||
; Routine: Init700CacheQs
|
||
; Function: This routine initializes the File and Free queues.
|
||
; The old cache space are released !!
|
||
; Input: A0.L = Ptr to cache locals.
|
||
; Output: none
|
||
; Modification history:
|
||
; 20Apr90 KST New Today
|
||
;_________________________________________________________________________________
|
||
Init700CacheQs
|
||
MOVEM.L D0-D3/A0-A4,-(SP) ; save all regs
|
||
MOVEA.L A0,A1 ; A1 = ptr(cachevars)
|
||
|
||
;; release ROM cache memory. Check for interrupt
|
||
MOVE.W SR,D0 ; can't release memory if called from interrupt
|
||
ANDI.W #$0700,D0 ; interrupt masked? <31Oct90>
|
||
BEQ.S @0 ; no, it's not
|
||
;; _DEBUGGER ; interrupt masked
|
||
|
||
@dead moveq.l #dsMemFullErr, d0 ; sys heap must screw up, so punt
|
||
_SysError
|
||
|
||
@0
|
||
MOVEA.L FileQHdr,A0 ; free data cache
|
||
_disposePtr
|
||
MOVEA.L FreeQHdr,A0 ; free BitMap buffer
|
||
_disposePtr
|
||
MOVEA.L SysCtlCPtr,A0 ; free control cache
|
||
_disposePtr
|
||
|
||
;; initialize free queue header ...
|
||
MOVEA.L NewBufPtr(A1),A0 ; start of freeQ Hdr
|
||
MOVE.L A0,FreeQHdr ; save it in the right place
|
||
MOVE.L A0,FreeQFlink(A0) ; and initialize it
|
||
MOVE.L A0,FreeQBlink(A0)
|
||
CLR.L FreeQBufCnt(A0) ; clear FreeQBufCnt and TotalBufCnt
|
||
_AssumeEq FreeQBufCnt+2,TotalBufCnt
|
||
|
||
;; initialize file queue header ...
|
||
LEA FreeQHlen(A0),A0 ; go to fileQ Hdr
|
||
MOVE.L A0,FileQHdr ; save it in the right place
|
||
MOVE.L A0,FQHFlink(A0) ; and initialize it too
|
||
MOVE.L A0,FQHBlink(A0)
|
||
CLR.L FQHFileCnt(A0) ; clear FQHFileCnt and FQHExtra
|
||
_AssumeEq FQHFileCnt+2,FQHExtra
|
||
|
||
; init file queue headers (FHxxx)
|
||
LEA FQHlength(A0),A4 ; go to file Hdr buffer
|
||
MOVEQ #fileHdrCnt-1,D2 ; init loop count for file headers
|
||
@initFQloop
|
||
BSR EQElement ; enqueue A4 element in A0 file queue
|
||
LEA FHMQHFlink(A4),A3
|
||
MOVE.L A3,MQHFlink(A3)
|
||
MOVE.L A3,MQHBlink(A3)
|
||
LEA FHDQHFLink(A4),A3
|
||
MOVE.L A3,DQHFLink(A3)
|
||
MOVE.L A3,DQHBLink(A3)
|
||
ADDA.L #FHlength,A4 ; move to the next header
|
||
ADDQ.W #1,FQHFileCnt(A0) ; bump header count
|
||
DBRA D2,@initFQloop
|
||
IF ktDebugCache THEN
|
||
MOVEA.L CBHdrStart(A1),A3
|
||
MOVEQ #1,D3 ; note where are we coming from
|
||
BSR.S ckMemQueue ; so far so good
|
||
ENDIF
|
||
|
||
MOVEA.L FreeQHdr,A0
|
||
MOVE.L BufStart(A1),A3 ; start of data Buffers
|
||
MOVEA.L CBHdrStart(A1),A4 ; CB header
|
||
MOVE.W TotalBufN(A1),D2 ; # of cache buffers
|
||
SUBQ #1,D2 ; for DBRing
|
||
@initBQloop ; init buffer headers
|
||
BSR EQElement ; enqueue A4 element in A0 free queue
|
||
MOVE.L A3,MCBDataPtr(A4) ; link data buffer w/ header
|
||
BSET #emptyBit,MCBFlags(A4) ; mark it empty
|
||
CLR.L MCBAge(A4) ; clear age since it's not in DHT <02Aug90>
|
||
MOVE.L A0,MCBFQPtr(A4) ; put in back pointer
|
||
|
||
;; init hash queue pointers: (buffer is not in the hash) <04May90>
|
||
LEA MCBHashqFlink(A4),A2; point to hash queue <04May90>
|
||
MOVE.L A2,FLink(A2) ; init hash queue to <04May90>
|
||
MOVE.L A2,BLink(A2) ; point to itself <04May90>
|
||
|
||
;; init disk-ordered queue pointers:
|
||
LEA MCBDQFlink(A4),A2 ; point to disk-ordered queue
|
||
MOVE.L A2,FLink(A2) ; init disk-ordered queue header to
|
||
MOVE.L A2,BLink(A2) ; point to itself
|
||
|
||
ADDQ #1,FreeQBufCnt(A0) ; incr number of buffers in free queue
|
||
ADDQ #1,TotalBufCnt(A0) ; incr total number of buffers
|
||
ADDA #BufSiz,A3 ; next data buf
|
||
ADDA #MCBHLength,A4 ; next CBhdr
|
||
DBRA D2,@initBQloop
|
||
|
||
IF ktDebugCache THEN
|
||
MOVE.W TotalBufCnt(A0),D1
|
||
MOVE.L BufStart(A1),A3
|
||
MOVEQ #2,D3 ; note where are we coming from
|
||
BSR.S ckMemQueue
|
||
ENDIF
|
||
|
||
MOVEM.L (SP)+,D0-D3/A0-A4 ; restore regs
|
||
JMP AdjByteLim ; readjust size limit for multi-block caching
|
||
|
||
|
||
IF ktDebugCache THEN ; this should deleted in the future
|
||
;_________________________________________________________________________________
|
||
; Routine: ckMemQueue
|
||
; Function: This routine verifies cache queue at INIT time.
|
||
; Input: A1.L = Ptr to cache locals. D3.W = flag
|
||
; if D3 = 2 then D1.W = total number of buffers in free queue
|
||
; A3.L = someone's ceiling, A4.L = someone's floor
|
||
;_________________________________________________________________________________
|
||
ckMemQueue
|
||
MOVEA.L CacheVars,A0
|
||
CMPA.L A0,A1 ; A1 should = CacheVars
|
||
BNE.S @debugErr
|
||
CMP.W #2,D3 ; check number of buffers?
|
||
BNE.S @no ; no
|
||
CMP.W TotalBufN(A1),D1 ; total count match?
|
||
BNE.S @debugErr ; no
|
||
@no CMPA.L A3,A4
|
||
BEQ.S @debugNoErr
|
||
|
||
@debugErr _debugger
|
||
@debugNoErr
|
||
RTS
|
||
|
||
ENDIF ;; end IF ktDebugCache
|
||
|
||
END
|
||
|