3219 lines
122 KiB
Plaintext
Raw Permalink Normal View History

2019-06-29 23:17:50 +08:00
;
; 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 = dont 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 wont 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 - dont 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 dont 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> dont 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 (well 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 (were 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 its <= 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 (its
; 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 users 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 blocks
; 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, well 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 ; well 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, dont 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 users 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 ; its 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 its 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 systems 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 (well 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