sys7.1-doc-wip/OS/HFS/Cache/DiskCache.a

3226 lines
122 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; Hacks to match MacOS (most recent first):
;
; <Sys7.1> 8/3/92 Conditionalized the 'bra fcExit' from <SM4> as forROM (all other SM changes are forROM)
; 9/2/94 SuperMario ROM source dump (header preserved below)
;
;
; 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>
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
bra.s fcExit ; exit if so (skip flush) <SM4> <Sys7.1>
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