mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-26 16:49:18 +00:00
1100 lines
42 KiB
Plaintext
1100 lines
42 KiB
Plaintext
;
|
|
; File: Cache.a
|
|
;
|
|
; Contains: These routines provide caching of disk blocks
|
|
;
|
|
; Written by: Bill Bruffey
|
|
;
|
|
; Copyright: © 1984-1991, 1994 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM3> 1/27/94 rab Fixed a bug in InitCache (thanks Cam). InitCache is called by
|
|
; InitFS during StartInit. It uses the HFS a6 stack to store off
|
|
; some registers. Unfortunately the HFS a6 stack hasn't been set
|
|
; up yet and a6 point to the BootGlobals. Changed it to use the a7
|
|
; stack instead since no IO is happening anyway.
|
|
; <2> 9/10/91 JSM Add a header.
|
|
; <1.2> 3/2/89 DNF removed references to forROM; HFS now builds identically for ram
|
|
; or rom
|
|
; <1.1> 11/10/88 CCH Fixed Header.
|
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|
; <•1.1> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles
|
|
; <1.0> 2/11/88 BBM Adding file for the first time into EASE…
|
|
; 2/4/87 BB Removed use of users own buffer. This MFS feature is no longer
|
|
; supported.
|
|
; 9/25/86 BB Updated to use new MPW equate files.
|
|
; 1/22/86 LAK ForRAM version of InitCache now grabs space off BufPtr.
|
|
; 10/30/85 LAK CacheRdIP, CacheWrIP now preserve D3 rather than pass back disk
|
|
; block start (FileWrite keeps rounded LEOF in D3 . . .).
|
|
; 10/22/85 LAK Added local subroutine MarkEmpty which also sets disk block to
|
|
; 0. Local buffer fields are all now in the same place as cache
|
|
; buffer.
|
|
; 10/22/85 LAK Added support for OwnBuf buffers. Fixed bug in pre-scan before
|
|
; flush. Cache block header exchanged CBHDBlk and CBHFlNum to
|
|
; match OwnBuf header.
|
|
; 10/20/85 LAK When GetBlock has to use a dirty block, call FlushCache rather
|
|
; than WriteBlock directly so that all dirty blocks of a file are
|
|
; flushed together. For control cache, if CacheFlag is not set,
|
|
; FlushCache now counts the number of clean blocks in cache and
|
|
; still flushes if this count is low.
|
|
; 10/2/85 LAK Added vectors for ROM versions. Fixed bug in CacheRdIP,
|
|
; CacheWrIP (wasn't popping return address before Lg2Phys call).
|
|
; Scavenge write-count for TFS volumes.
|
|
; 10/1/85 LAK Added MarkA5Block since MFS routines like to keep buffer address
|
|
; in A5. .Ref Lg2Phys instead of MapFBlock for MFS support. Added
|
|
; new routines CacheWrIP and CacheRdIP. Added Ref of
|
|
; RdBlocks,WrBlocks. Added GBrelease option. GetBlock and
|
|
; FlushCache now expect A2 to be a VCB ptr if volume refnum is
|
|
; passed in.
|
|
; 9/21/85 LAK TrashBlocks no longer needs the cache specified . . .
|
|
; (allocation map and control files are never shortened or deleted
|
|
; or written directly to). Byte ranges are now supplied. Fixed bug
|
|
; in FlushCache - no longer allows D0=0=flush all option (problem
|
|
; with two blocks in different volumes with the same number).
|
|
; 9/20/85 LAK Added routine TrashFBlocks. On GetBlock file search, just search
|
|
; by file number and skip by disk block if not found.
|
|
; 9/17/85 LAK Fixed bug in TrashBlocks.
|
|
; 9/10/85 LAK Don't trash A5 in GetBlock.
|
|
; 9/8/85 LAK FlushCache now flushes in disk order. Added a hack to avoid
|
|
; having the control cache filled with dirty blocks leaving only a
|
|
; single clean one to be thrashed about with.
|
|
; 9/6/85 LAK Ignore WriteBlock errors on GetBlock writes to avoid hang
|
|
; condition when block is bad.
|
|
; 9/5/85 LAK FlushCache only flushes control cache if CacheFlag non-zero. Set
|
|
; CacheFlag non-zero if we have to write a block in Getblock.
|
|
; 9/2/85 LAK GetBlock leaves cache block marked empty on read errors. Rewrote
|
|
; MarkBlock.
|
|
; 8/30/85 LAK Added TrashVBlks. Removed RelCache.
|
|
; 8/29/85 BB Added error check in RelBlock for already released blocks.
|
|
; 8/15/85 BB Added TrashBlocks.
|
|
; 8/1/85 BB Added MarkBlock.
|
|
; 8/1/85 BB Added forced read option to GetBlock.
|
|
; 6/13/85 BB Added some internal documentation.
|
|
; 5/17/85 BB Added 'trash' option to FlushCache, added use of an 'empty'
|
|
; flag, and added use of a fork type byte.
|
|
; 5/16/85 BB Did some code clean up.
|
|
; 4/18/85 BB Fixed bug which was trashing D3 when MapFBlock was called.
|
|
; 3/26/85 BB Modified to support both file and volume IO.
|
|
; 3/15/85 BB Modified to use A6 stack.
|
|
; 2/19/85 BB Added 'flush all' option to FlushCache.
|
|
; 2/12/85 BB Added save/restore for scratch registers
|
|
; 1/18/85 BB Removed IO routines (ReadBlock,WriteBlock now in TFSIO and
|
|
; BTUIO).
|
|
; 1/15/85 BB Modified to support multiple cache queues.
|
|
; 1/11/85 BB Added InitCache.
|
|
; 10/11/84 BB Modified to use file IO rather than physical disk IO.
|
|
; 9/30/84 BB Modified register usage, added use of refnum instead of drive
|
|
; number, and added use of queue header instead of hot and cold
|
|
; pointers.
|
|
; 9/28/84 BB Removed interfaces to prototype btree.
|
|
; 9/14/84 BB Modified to use TFSEQU.
|
|
; 8/13/84 BB New today.
|
|
;
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; External
|
|
; Routines: FlushCache - Flushes a cache queue to disk.
|
|
; GetBlock - Gets a specified disk block.
|
|
; InitCache - Sets up a new initialized cache queue.
|
|
; MarkBlock - Marks a specified disk block dirty.
|
|
; RelBlock - Releases use of a specified disk block.
|
|
; RelCache - Releases memory allocated to a specified a cache
|
|
; queue.
|
|
; TrashBlocks - Trashes a specified range of disk blocks for a
|
|
; given file or volume
|
|
;
|
|
; Internal
|
|
; Subroutines: DQCBuf - Detaches a cache buffer from a queue.
|
|
; NQCBuf - Inserts a cache buffer in a queue at a specified
|
|
; point.
|
|
;
|
|
; Notes: * Tag IO not supported yet.
|
|
;
|
|
;_________________________________________________________________________________
|
|
|
|
|
|
BLANKS ON
|
|
STRING ASIS
|
|
|
|
PRINT OFF
|
|
LOAD 'StandardEqu.d'
|
|
PRINT ON
|
|
PRINT NOGEN
|
|
|
|
|
|
Cache PROC EXPORT
|
|
|
|
EXPORT FlushCache,GetBlock,InitCache,MarkBlock,RelBlock
|
|
EXPORT TrashBlocks,TrashVBlks,TrashFBlocks,MarkA5Block
|
|
EXPORT CacheWrIP,CacheRdIP
|
|
|
|
EXPORT vFLushCache,vGetBlock,vMarkBlock,vRelBlock
|
|
EXPORT vTrashBlocks,vTrashVBlks
|
|
EXPORT vCacheWrIP,vCacheRdIP
|
|
|
|
IMPORT ReadBlock,WriteBlock,RdBlocks,WrBlocks
|
|
IMPORT ReadOwnBuf,WriteOwnBuf ; <22Oct85>
|
|
IMPORT Lg2Phys,TFSVCBTst
|
|
IMPORT GetVCBRfn
|
|
|
|
; The current cache implementation makes the following assumptions for OwnBuf support:
|
|
|
|
_AssumeEq COBFlags,CBHFlags
|
|
_AssumeEq COBDBlk,CBHDBlk
|
|
_AssumeEq COBFlBlk,CBHFlBlk
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Routine: FlushCache
|
|
;
|
|
; Function: Flushes a cache queue. The entire queue may be flushed or
|
|
; only those buffers for specified file or volume.
|
|
;
|
|
; Input: D0.W - file or volume refnum
|
|
; D1.B - option flags (default = don't trash buffers after flush)
|
|
; FCTrash - trash buffers after flush
|
|
; A1.L - pointer to cache queue header
|
|
; A2.L - VCB ptr if D0=volume refnum <01Oct85>
|
|
;
|
|
; Output: D0.W - result code
|
|
; 0 = ok
|
|
; other = error
|
|
;
|
|
; Called by: FileRead: (flush all blocks of a file in volume cache before a read-in-place),
|
|
; Close, FlushFile: (flush all blocks of a file in volume cache - trash on close),
|
|
; FlushVol: (flush all volume blocks in each cache - trash on eject, unmountvol),
|
|
; BTFlush, BTClose: (flush control cache B-Tree blocks - trash on BTCLose).
|
|
;
|
|
; TFSRfn1:
|
|
; FILEREAD (D0.W=file refnum, D1.L=0, A1=VCBBufAdr(A2) -> flush all blocks of a file before read-in-place)
|
|
; MyWriteIP (D0.W=file refnum, D1.L=0, A1=VCBBufAdr(A2) -> flush all blocks of a file before wr-in-place)
|
|
; TFSRfn2:
|
|
; FCLOSE (D0.W=file refnum, D1.L=0 or FCTrash bit, A1=VCBBufAdr(A2) -> flush data block on FlushFile or FileCLose)
|
|
; SETEof (D0.W=file refnum, D1.L=0, A1=VCBBufAdr(A2) -> always flush TFS vol buf on file shorten)
|
|
; TFSVol:
|
|
; FLUSHVOL(UNMOUNTVOL,EJECT,OFFLINE)
|
|
; (D0.W=VRefNum, D1.B=0 or FCTrash bit, A1=VCBMAdr(A2) -> flush map cache)
|
|
; (D0.W=VRefNum, D1.B=0 or FCTrash bit, A1=VCBCtlBuf(A2) -> flush control cache)
|
|
; (D0.W=VRefNum, D1.B=0 or FCTrash bit, A1=VCBBufAdr(A2) -> flush volume cache)
|
|
; BTINTF:
|
|
; BTCLOSE (D0.W=file refnum, D1.B=FCTrash bit set, A1=BTCCQptr(A4)
|
|
; BTFLUSH (D0.W=file refnum, D1.B=0, A1=BTCCQptr(A4)
|
|
;
|
|
;_________________________________________________________________________________
|
|
|
|
GetOwnBuf
|
|
; MOVE.L FCBBfAdr(A1,D1),D0 ; get local buffer address <04Feb87>
|
|
MOVEQ #0,D0 ; <04Feb87>
|
|
BEQ.S @1 ; exit if no OwnBuf <22Oct85>
|
|
MOVE.L D0,A4 ; get buffer ptr <22Oct85>
|
|
SUB #CBHData,A4 ; align it to cache buffer offsets <22Oct85>
|
|
@1 RTS ; BEQ for no OwnBuf
|
|
|
|
FlushOwnBuf
|
|
BSR.S GetOwnBuf ; check for OwnBuf buffer installed <22Oct85>
|
|
BEQ.S @1 ; exit if no local buffer <22Oct85>
|
|
BTST #CBHempty,COBFlags(A4) ; buffer empty? <22Oct85>
|
|
BNE.S @1 ; exit if so <22Oct85>
|
|
BCLR #CBHdirty,COBFlags(A4) ; buffer dirty? <22Oct85>
|
|
BEQ.S @1 ; ...no -> <22Oct85>
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack <22Oct85>
|
|
JSR WriteOwnBuf ; write it on disk <22Oct85>
|
|
MOVE.L (A6)+,-(SP) ; restore return address <22Oct85>
|
|
@1 RTS ;
|
|
|
|
FlushCache
|
|
MOVE.L jFlushCache,-(SP) ; jumptable entry for vFlushCache <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vFlushCache ; 'vectored' FlushCache routine <02Oct85>
|
|
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
|
MOVEM.L D1-D6/A0-A4,-(A6) ; save registers
|
|
|
|
CMP.L SysCtlCPtr,A1 ; going for the control cache? <05Sep85>
|
|
BNE.S FCSetUp ; br if not <20Oct85>
|
|
TST.B CacheFlag ; flushing time? <05Sep85>
|
|
BNE.S FCSetUp ; br if so <20Oct85>
|
|
|
|
; We skip the flush of the control cache if it is not too full of dirty
|
|
; buffers (this helps performance a lot).
|
|
|
|
MOVEQ #0,D5 ; zero number of clean blocks <20Oct85>
|
|
MOVEA.L A1,A3 ; A3 = ptr(CQH) <20Oct85>
|
|
MOVEA.L A3,A4 ; start at top of queue <20Oct85>
|
|
|
|
@1 MOVEA.L CBHFlink(A4),A4 ; position to next buffer <20Oct85>
|
|
CMPA.L A3,A4 ; back to top of queue ? <20Oct85>
|
|
BEQ.S @3 ; br if so <20Oct85>
|
|
BTST #CBHempty,CBHFlags(A4) ; buffer empty? <20Oct85>
|
|
BNE.S @2 ; br if so <20Oct85>
|
|
BTST #CBHinuse,CBHFlags(A4) ; buffer in use? <20Oct85>
|
|
BNE.S @1 ; ...yes, continue scan -> <20Oct85>
|
|
BTST #CBHdirty,CBHFlags(A4) ; dirty buffer? <20Oct85>
|
|
BNE.S @1 ; ...yes, continue scan -> <20Oct85>
|
|
@2 ADDQ #1,D5 ; bump clean block count <20Oct85>
|
|
BRA.S @1 ; and continue scan <20Oct85>
|
|
|
|
@3 MOVE.W CQHNumBuf(A3),D6 ; number of buffers in this cache <20Oct85>
|
|
LSR.W #2,D6 ; 25% count of buffers <20Oct85>
|
|
CMP.W D6,D5 ; more than 25% clean? <20Oct85>
|
|
BHI FCExit ; exit ok if so <20Oct85>
|
|
|
|
FCSetUp MOVE.B D1,D5 ; D5 = option flags <20Oct85>
|
|
MOVEA.L A1,A3 ; A3 = ptr(CQH)
|
|
MOVE.W D0,D1 ; D1 = refnum
|
|
BLE.S @1 ; br if volume flush ->
|
|
|
|
; set up for type of flush
|
|
|
|
MOVEA.L FCBSPtr,A1 ; A1 = FCB ptr
|
|
MOVEA.L FCBVPtr(A1,D1),A2 ; A2 = VCB ptr
|
|
BSR.S FlushOwnBuf ; flush any installed buffer <22Oct85>
|
|
|
|
MOVE.L FCBFlNm(A1,D1),D3 ; D3 = file number
|
|
BTST #FCBRscBit,FCBMdRByt(A1,D1) ; resource fork?
|
|
SNE D6 ;$FF=resource fork, $00=data fork
|
|
IF HFSDebug THEN
|
|
BRA.S @3 ; ->
|
|
ENDIF
|
|
|
|
@1
|
|
IF HFSDebug THEN
|
|
MOVE.L A2,A4 ; save passed VCB ptr
|
|
JSR GetVCBRfn ; locate the VCB
|
|
BNE.S @2 ; br if not found
|
|
CMP.L A2,A4 ; same as one passed in?
|
|
BEQ.S @3 ; br if so
|
|
@2 _HFSDebug $410
|
|
ENDIF
|
|
|
|
@3 MOVEQ #0,D4 ; zero last match <08Sep85>
|
|
|
|
|
|
; search for matching buffers, in disk order . . .
|
|
|
|
FCSearch
|
|
MOVEQ #0,D2 ; zero current match <08Sep85>
|
|
MOVEA.L A3,A4 ; start at top of queue <08Sep85>
|
|
|
|
FCNextLoop
|
|
MOVEA.L CBHFlink(A4),A4 ; position to next buffer
|
|
CMPA.L A3,A4 ; back to top of queue ?
|
|
BNE.S @0 ; br if not <08Sep85>
|
|
|
|
TST.L D2 ; any match? <08Sep85>
|
|
BEQ.S FCExit ; exit if not . . . <08Sep85>
|
|
MOVE.L A1,A4 ; pass buffer ptr in A4 <08Sep85>
|
|
MOVE.L CBHDBlk(A4),D4 ; save last match disk block <08Sep85>
|
|
BRA.S FCFlush ; and go flush it . . . <08Sep85>
|
|
|
|
@0 BTST #CBHempty,CBHFlags(A4) ; buffer empty?
|
|
BNE.S FCNextLoop ; yes, continue search ->
|
|
|
|
CMPA.L CBHVCBPtr(A4),A2 ; same volume?
|
|
BNE.S FCNextLoop ; no, continue search ->
|
|
|
|
TST.W D1 ; check type of flush <08Sep85>
|
|
BLT.S @1 ; volume refnum, flush volume -> <08Sep85>
|
|
|
|
CMP.L CBHFlNum(A4),D3 ; same file number?
|
|
BNE.S FCNextLoop ; no, continue search ->
|
|
CMP.B CBHFkType(A4),D6 ; same fork?
|
|
BNE.S FCNextLoop ; no, continue search ->
|
|
|
|
@1 TST.L D2 ; any match yet? <08Sep85>
|
|
BEQ.S @2 ; br if not <08Sep85>
|
|
CMP.L CBHDBlk(A4),D2 ; before current match block on disk? <08Sep85>
|
|
BLT.S FCNextLoop ; keep searching if not <08Sep85>
|
|
|
|
@2 CMP.L CBHDBlk(A4),D4 ; after last block we flushed? <08Sep85>
|
|
BGE.S FCNextLoop ; br if not <08Sep85>
|
|
|
|
MOVE.L CBHDBlk(A4),D2 ; save low-water mark <08Sep85>
|
|
MOVE.L A4,A1 ; and its buffer ptr <08Sep85>
|
|
BRA.S FCNextLoop ; and keep searching <08Sep85>
|
|
|
|
; flush next matching buffer in disk order
|
|
|
|
FCFlush
|
|
BTST #CBHdirty,CBHFlags(A4) ; buffer dirty?
|
|
BEQ.S @1 ; ...no ->
|
|
JSR WriteBlock ; write it on disk
|
|
;BNE.S FCExit1 ; write error (* continue flush ? *)
|
|
BCLR #CBHdirty,CBHFlags(A4) ; set not dirty
|
|
|
|
@1 BTST #FCtrash,D5 ; trash option requested?
|
|
BEQ.S FCSearch ; no, keep it around ->
|
|
BTST #CBHinuse,CBHFlags(A4) ; buffer in use?
|
|
BNE.S FCSearch ; yes, keep it around ->
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
BRA.S FCSearch ; continue search
|
|
FCExit
|
|
CLR.W D0 ; no error
|
|
FCExit1
|
|
MOVEM.L (A6)+,D1-D6/A0-A4 ; restore registers
|
|
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
|
TST.W D0 ; set up condition codes
|
|
RTS ; exit Flush Cache
|
|
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; 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
|
|
; A1.L - pointer to cache queue header
|
|
; A2.L - VCB ptr if D0=volume refnum <01Oct85>
|
|
; D1.B - option flags (default = read from disk if not found):
|
|
; GBRead - read from disk (forced read)
|
|
; GBnoRead - don't read from disk if not found
|
|
; GBexist - get existing cache block
|
|
; 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:
|
|
; FlushMDB (D0.W=VCBVRefnum(A2), D1.B=GBRead bit, D2=2, A1=VCBBufAdr(A2) -> get MDB before writing it)
|
|
; TFSRfn1:
|
|
; MyCRead (D0.W=VCBVRefnum(A2), D1.B=0, D2=search blk, A1=VCBBufAdr(A2) -> r/w of vol cache for TFS)
|
|
; TFSRfn2:
|
|
; FCLOSE (D0.W=file refnum, D1.B=0, D2=0, A1=VCBBufAdr(A2) -> get first resource block for directory copy)
|
|
; TFSVol:
|
|
; MOUNTVOL (D0.W=vrefnum, D1.B=0, D2=2, A1=VCBBufAdr(A2) -> read master directory block)
|
|
; VSM:
|
|
; GetBMBlk (D0.W=VCBVRefNum(A2), D1.B=0, D2=VCBVBMSt(A2)+n, A1=VCBMAdr(A2) -> read volume bitmap block n)
|
|
; BTINTF:
|
|
; BTFLUSH (D0.W=file refnum, D1.B=0, A1=BTCCQptr(A4), D2=0 -> get B-Tree header block)
|
|
; BTOPEN (D0.W=file refnum, D1.B=0, A1=Cache Queue Header, D2=0 -> get B-Tree header block)
|
|
; BTSVCS:
|
|
; BTDELETE (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=TPRNodeA(A0)-> get parent node)
|
|
; GRGETNODE (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2=??-> get a BT node)
|
|
; BTINSERT (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=TPRNodeA(A0)-> get parent node)
|
|
; BTSEARCH (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=btree hint-> get hint node)
|
|
; BTUPDATE (D0.W=file refnum?, D1.B=GBexist bit, A1=BTCCQptr(A4), D2.L=hint or locBTCB result? )
|
|
; BTALLOC:
|
|
; GetMap (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=0 or next map node -> get map node)
|
|
; BTMAINT1:
|
|
; TreeSearch (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=next node -> b-tree search)
|
|
; BTMAINT2:
|
|
; GetLtSib/GetRtSib (D0.W=BTCRefNum(A4), D1.B=0, A1=BTCCQptr(A4), D2.L=sibling node -> get l/r sibling node)
|
|
; InitNode (D0.W=v refnum, D1.B=GBnoread bit, A1=BTCCQptr(A4), D2.L=new node -> get a new node buffer)
|
|
;
|
|
;_________________________________________________________________________________
|
|
|
|
GetBlock
|
|
MOVE.L jGetBlock,-(SP) ; jumptable entry for vGetBlock <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vGetBlock ; 'vectored' GetBlock routine <02Oct85>
|
|
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
|
MOVEM.L D1-D7/A1-A5,-(A6) ; save regs <10Sep85>
|
|
MOVE.B D1,D3 ; D3 = option flags
|
|
MOVEA.L A1,A3 ; A3 = ptr(CQH)
|
|
|
|
MOVE.W D0,D1 ; D1 = volume or file refnum?
|
|
BLT GBVolRef ; volume ->
|
|
|
|
; search cache queue for specified file block
|
|
|
|
GBFSearch
|
|
MOVEA.L FCBSPtr,A1 ; A1 = ptr(1st FCB)
|
|
MOVEA.L FCBVPtr(A1,D1.W),A2 ; A2 = ptr(VCB)
|
|
|
|
MOVE.L FCBFlNm(A1,D1.W),D7 ; D7 = file number
|
|
BTST #FCBRscBit,FCBMdRByt(A1,D1.W) ; resource fork?
|
|
SNE D4 ; yes, indicate resource fork
|
|
|
|
MOVEA.L A3,A4 ; start at top of queue
|
|
|
|
@1 MOVEA.L CBHFlink(A4),A4 ; position to next buffer
|
|
CMPA.L A3,A4 ; back to top of queue ?
|
|
BEQ.S @2 ; yes -> <22Oct85>
|
|
|
|
BTST #CBHempty,CBHFlags(A4) ; buffer empty?
|
|
BNE.S @1 ; yes, continue search ->
|
|
|
|
CMPA.L CBHVCBPtr(A4),A2 ; same volume?
|
|
BNE.S @1 ; ... no, continue search ->
|
|
CMP.L CBHFlNum(A4),D7 ; same file number ?
|
|
BNE.S @1 ; ... no, continue search ->
|
|
CMP.B CBHFkType(A4),D4 ; same fork?
|
|
BNE.S @1 ; ... no, continue search ->
|
|
CMP.L CBHFlBlk(A4),D2 ; same file block ?
|
|
BEQ GBFoundIt ; ...yes, found it ->
|
|
BRA.S @1 ; continue search
|
|
|
|
; see if block could be in a local buffer . . .
|
|
|
|
@2 BSR GetOwnBuf ; Local buffer? <22Oct85>
|
|
BEQ.S GBMapFBlk ; give up search if not <22Oct85>
|
|
BTST #CBHempty,COBFlags(A4) ; buffer empty? <22Oct85>
|
|
BNE.S GBMapFBlk ; give up search if so <22Oct85>
|
|
CMP.L COBFlBlk(A4),D2 ; same file block ? <22Oct85>
|
|
BEQ GBExit ; exit successfully if so <22Oct85>
|
|
|
|
; didn't find file block, map file block number to logical block number
|
|
|
|
GBMapFBlk
|
|
MOVEM.L D3-D4,-(A6) ; save option flags and fork type
|
|
MOVEQ #0,D4 ; cache buffer size
|
|
MOVE.W CQHBufSize(A3),D4 ;
|
|
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
|
|
|
|
JSR Lg2Phys ; map to logical block <01Oct85>
|
|
|
|
MOVE.L D2,D6 ; D6 = file block number
|
|
MOVE.L D3,D2 ; D2 = logical block number
|
|
MOVEM.L (A6)+,D3-D4 ; restore option flags and fork type
|
|
TST.W D0 ; any mapping errors? <01Oct85>
|
|
BNE GBExit1 ; exit if we were unable to map block <01Oct85>
|
|
|
|
; MOVE.L FCBBfAdr(A1,D1),D0 ; Local buffer? <04Feb87>
|
|
MOVEQ #0,D0 ; <04Feb87>
|
|
BEQ.S GBLSearch ; search for cache block if not <22Oct85>
|
|
BSR FlushOwnBuf ; make sure it's clean (sets up A4) <22Oct85>
|
|
MOVE.L D2,COBDBlk(A4) ; <22Oct85>
|
|
MOVE.L D6,COBFlBlk(A4) ; <22Oct85>
|
|
CLR.B COBFlags(A4) ; clear all bits (not empty, not dirty) <22Oct85>
|
|
JSR ReadOwnBuf ; read the block <22Oct85>
|
|
BEQ GBExit ; exit if successful <22Oct85>
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
BRA GBExit1 ; <22Oct85>
|
|
|
|
; logical block on volume was requested
|
|
|
|
GBVolRef
|
|
IF HFSDebug THEN
|
|
MOVE.L A2,A4 ; save passed VCB ptr
|
|
JSR GetVCBRfn ; locate the VCB
|
|
BNE.S @1 ; br if not found
|
|
CMP.L A2,A4 ; same as one passed in?
|
|
BEQ.S @2 ; br if so
|
|
@1 _HFSDebug $411
|
|
@2
|
|
ENDIF
|
|
|
|
MOVEQ #0,D1 ; indicate no given file refnum
|
|
MOVEQ #0,D7 ; ...file number
|
|
MOVEQ #-1,D6 ; ...file block number
|
|
MOVEQ #0,D4 ;... or fork type
|
|
|
|
; search cache queue for logical block
|
|
|
|
GBLSearch
|
|
SUBA.L A0,A0 ; initialize ptr to empty buffer
|
|
SUBA.L A1,A1 ; ... to clean buffer
|
|
SUBA.L A5,A5 ; ... to dirty buffer
|
|
|
|
MOVEA.L A3,A4 ; start at top of queue
|
|
|
|
@1 MOVEA.L CBHFlink(A4),A4 ; position to next buffer
|
|
CMPA.L A3,A4 ; back to top of queue ?
|
|
BEQ.S GBSelect ; yes ->
|
|
|
|
BTST #CBHempty,CBHFlags(A4) ; empty buffer ?
|
|
BEQ.S @2 ; ...no ->
|
|
MOVEA.L A4,A0 ; ...yes, save ptr to empty buffer
|
|
BRA.S @1 ; continue search ->
|
|
|
|
; (for file blocks, we really only have to search until we find a free block; since
|
|
; we typically only have a few file buffer blocks, this is not important now)
|
|
|
|
@2 TST.W D1 ; searching by file? <20Sep85>
|
|
BNE.S @3 ; br if so (shouldn't be found here) <20Sep85>
|
|
CMPA.L CBHVCBPtr(A4),A2 ; same volume?
|
|
BNE.S @3 ; ... no ->
|
|
CMP.L CBHDBlk(A4),D2 ; same disk block ?
|
|
BEQ GBFoundIt ; ...yes, found it ->
|
|
|
|
@3 BTST #CBHinuse,CBHFlags(A4) ; buffer in use?
|
|
BNE.S @1 ; ...yes, continue search ->
|
|
|
|
BTST #CBHdirty,CBHFlags(A4) ; dirty buffer?
|
|
BEQ.S @4 ; ...no
|
|
MOVEA.L A4,A5 ; ....yes, save ptr to dirty buffer
|
|
BRA.S @1 ; continue search
|
|
|
|
@4 MOVEA.L A4,A1 ; save ptr to clean buffer
|
|
BRA.S @1 ; continue search
|
|
|
|
; didn't find requested block, select a buffer to use
|
|
|
|
GBSelect
|
|
BTST #GBexist,D3 ; request for an existing block?
|
|
BEQ.S @1 ; no ->
|
|
MOVEQ #Chnotfound,D0 ; result = 'not found' <01Oct85>
|
|
BRA GBExit1 ; exit ->
|
|
|
|
@1 MOVEA.L A0,A4 ; do we have an empty one ?
|
|
MOVE.L A4,D0 ;
|
|
BNE.S GBAssign ; ...yes, use it ->
|
|
|
|
MOVEA.L A1,A4 ; do we have a clean one ?
|
|
MOVE.L A4,D0 ;
|
|
BNE.S GBAssign ; ...yes, use it -> <20Oct85>
|
|
|
|
MOVEA.L A5,A4 ; do we have an dirty one ?
|
|
MOVE.L A4,D0 ;
|
|
BNE.S GBWrite ; ...yes, write it first ->
|
|
|
|
MOVEQ #ChNoBuf,D0 ; result = 'all buffers in use' <01Oct85>
|
|
BRA GBExit1 ; exit ->
|
|
|
|
; Write contents of dirty buffer to disk. We do this by flushing so we don't write out
|
|
; one block of a file (esp. a B-Tree file) without other dirty blocks of the file. If
|
|
;
|
|
GBWrite
|
|
MOVEM.L D0-D2/A1-A2,-(A6) ; save regs over flush call <20Oct85>
|
|
MOVE.B CacheFlag,D2 ; save CacheFlag value <20Oct85>
|
|
ST CacheFlag ; set for real flush <20Oct85>
|
|
MOVEQ #0,D1 ; regular flush options <20Oct85>
|
|
MOVE.L A3,A1 ; cache queue header <20Oct85>
|
|
MOVE.L CBHVCBPtr(A4),A2 ; VCB ptr for this dirty block <20Oct85>
|
|
MOVE.W CBHFRefnum(A4),D0 ; file refnum for this dirty block <20Oct85>
|
|
BGT.S @1 ; br if it's a file block <20Oct85>
|
|
MOVE.W VCBVRefnum(A2),D0 ; otherwise pass the VRefnum <20Oct85>
|
|
@1 JSR FlushCache ; should flush it out . . . <20Oct85>
|
|
MOVE.B D2,CacheFlag ; restore cache flag <20Oct85>
|
|
MOVEM.L (A6)+,D0-D2/A1-A2 ; restore registers <20Oct85>
|
|
|
|
IF HFSDebug THEN
|
|
BTST #CBHdirty,CBHFlags(A4) ; dirty buffer? should be clean now <20Oct85>
|
|
BEQ.S @2 ; br if clean <20Oct85>
|
|
_HFSDebug $413 ; something's happening here . . . <20Oct85>
|
|
ENDIF
|
|
|
|
@2 CMP.L SysCtlCPtr,A3 ; going for the control cache? <05Sep85>
|
|
BNE.S GBAssign ; br if not <08Sep85>
|
|
ST CacheFlag ; better start flushing: we're full <08Sep85>
|
|
|
|
; Assign buffer to new node read it from disk
|
|
|
|
GBAssign
|
|
BSET #CBHinuse,CBHFlags(A4) ; set in use
|
|
BCLR #CBHempty,CBHFlags(A4) ; ...not empty
|
|
BCLR #CBHdirty,CBHFlags(A4) ; ...and not dirty
|
|
MOVE.L A2,CBHVCBPtr(A4) ; new volume refnum
|
|
MOVE.L D2,CBHDBlk(A4) ; ...disk block number
|
|
MOVE.W D1,CBHFRefNum(A4) ; ...file refnum
|
|
MOVE.L D7,CBHFlNum(A4) ; ...file number
|
|
MOVE.B D4,CBHFkType(A4) ; ...fork type
|
|
MOVE.L D6,CBHFlBlk(A4) ; ...and file block number
|
|
|
|
BTST #GBnoRead,D3 ; no-read requested ?
|
|
BNE.S GBReQueue ; ...yes, skip read ->
|
|
GBReadBlk
|
|
CLR.L BufTgDate ; always clear tag date <02Oct85>
|
|
|
|
JSR ReadBlock ; read node from disk <02Oct85>
|
|
BNE.S @1 ; exit on errors <02Oct85>
|
|
|
|
; we scavenge the VCB write count whenever we read a block . . . this may be
|
|
; overkill (doing it at volume mount scan should be sufficient . . . )
|
|
|
|
JSR TFSVCBTst ; TFS volume? <02Oct85>
|
|
BNE.S GBReQueue ; skip write-count scavenge if not <02Oct85>
|
|
MOVE.L BufTgDate,D0 ; get tag write-count <02Oct85>
|
|
CMP.L VCBWrCnt(A2),D0 ; make sure it's <= VCB write count <02Oct85>
|
|
BLS.S GBReQueue ; br if so <02Oct85>
|
|
MOVE.L D0,VCBWrCnt(A2) ; update VCB write count if not . . . <02Oct85>
|
|
BRA.S GBReQueue ; go re-queue the buffer -> <02Sep85>
|
|
|
|
@1 CLR.B CBHFlags(A4) ; not in use or dirty <02Sep85>
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
BRA.S GBExit1 ; read error -> <02Sep85>
|
|
|
|
; found requested buffer, re-assign it
|
|
|
|
GBFoundIt
|
|
BSET #CBHinuse,CBHFlags(A4) ; buffer in use? <01Oct85>
|
|
BEQ.S @1 ; ...no ->
|
|
_HFSDebug $412
|
|
MOVEQ #ChInUse,D0 ; error, buffer in use <01Oct85>
|
|
BRA.S GBExit1 ; exit ->
|
|
|
|
@1 BTST #GBread,D3 ; forced read requested
|
|
BNE.S GBReadBlk ; yes, read it -> (if dirty should it be marked clean??)
|
|
|
|
; Re-queue the buffer for LRU
|
|
|
|
GBReQueue
|
|
JSR DQCBuf ; detach it
|
|
MOVEA.L A3,A0 ; put on top
|
|
JSR NQCBuf ; ...of queue
|
|
|
|
BTST #GBrelease,D3 ; immediate release requested? <01Oct85>
|
|
BEQ.S GBExit ; br if not
|
|
BCLR #CBHinuse,CBHFlags(A4) ; set not in-use
|
|
|
|
GBExit LEA CBHData(A4),A0 ; set up return ptr to buffer <01Oct85>
|
|
MOVEQ #0,D0 ; indicate no error <01Oct85>
|
|
|
|
GBExit1
|
|
MOVEM.L (A6)+,D1-D7/A1-A5 ; restore regs <10Sep85>
|
|
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
|
TST.W D0 ; set up condition codes
|
|
RTS ; exit GetBlock
|
|
|
|
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Routine: InitCache (Initialize Cache)
|
|
;
|
|
; Function: Sets up a new initialized cache queue. Memory is allocated for
|
|
; the cache queue and each cache buffer is set to an empty,
|
|
; not-in-use state.
|
|
;
|
|
; Input: D0.W - number of cache buffers
|
|
; D1.W - size of each cache buffer (not including the Cache Buffer
|
|
; Header - CBH)
|
|
;
|
|
; Output: D0.W - result code
|
|
; 0 = ok
|
|
; other = error
|
|
; A1.L - pointer to new Cache Queue Header (CQH)
|
|
;_________________________________________________________________________________
|
|
|
|
InitCache
|
|
MOVEM.L D1-D3/A0/A4,-(SP) ; save regs <SM3>
|
|
;
|
|
; allocate memory for cache queue
|
|
;
|
|
MOVE.W D0,D2 ; D2 = # of buffers
|
|
MOVE.W D1,D3 ; D3 = buffer size
|
|
ADDI.W #LenCBH,D1 ; D1 = size of buffer + buffer header
|
|
|
|
MOVEQ #0,D0 ; calculate total size
|
|
MOVE.W D2,D0 ;
|
|
MULU D1,D0 ; ...for all buffers
|
|
ADD.L #LenCQH,D0 ; ...+ queue header
|
|
|
|
_NewPtr ,SYS,CLEAR ; allocate the memory <22Jan86>
|
|
BNE.S ICExit1 ; couldn't get it -> <22Jan86>
|
|
|
|
MOVE.L A0,A1 ; A1 = ptr(CQH)
|
|
;
|
|
; initialize the cache queue
|
|
;
|
|
MOVE.L A1,CQHFlink(A1) ; initialize queue
|
|
MOVE.L A1,CQHBlink(A1) ; ...to point to itself
|
|
MOVE.W D2,CQHNumBuf(A1) ; set number of buffers
|
|
MOVE.W D3,CQHBufSize(A1) ; ... and buffer size
|
|
|
|
LEA LenCQH(A1),A4 ; A4 = addr(1st buffer)
|
|
SUBQ.W #1,D2 ; D2 = # of buffers - 1
|
|
|
|
@1 MOVEA.L A1,A0 ; attach buffer
|
|
JSR NQCBuf ; ...to top of queue
|
|
|
|
BSR.S MarkEmpty ; mark it empty <23Oct85>
|
|
|
|
ADDA.L D1,A4 ; bump to next buffer
|
|
DBRA D2,@1 ; init next buffer
|
|
|
|
CLR.W D0 ; result = 'ok'
|
|
ICExit1
|
|
MOVEM.L (SP)+,D1-D3/A0/A4 ; restore regs <SM3>
|
|
TST.W D0 ; set up condition codes
|
|
RTS ; exit InitCache
|
|
|
|
|
|
MarkEmpty BSET #CBHempty,CBHFlags(A4) ; mark buffer empty <22Oct85>
|
|
CLR.L CBHDBlk(A4) ; zero disk block for empty buffers <22Oct85>
|
|
RTS
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Routine: MarkBlock,MarkA5Block (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 jMarkBlock,-(SP) ; jumptable entry for vMarkBlock <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vMarkBlock ; 'vectored' MarkBlock routine <02Oct85>
|
|
|
|
BSET #CBHdirty,CBHFlags-lenCBH(A0) ; mark it dirty <02Sep85>
|
|
RTS ; exit MarkBlock
|
|
|
|
MarkA5Block
|
|
EXG A0,A5 ; pass A0=blk ptr to MarkBlock <02Oct85>
|
|
BSR.S MarkBlock ; use same routine for vectoring <02Oct85>
|
|
EXG A0,A5 ; <02Oct85>
|
|
RTS ; exit MarkBlock <01Oct85>
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; 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
|
|
; A0.L - addr(cache buffer) containing disk block
|
|
; A1.L - pointer to cache queue header (if RBWrite specified)
|
|
;
|
|
; 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)
|
|
;
|
|
;_________________________________________________________________________________
|
|
|
|
RelBlock
|
|
MOVE.L jRelBlock,-(SP) ; jumptable entry for vRelBlock <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vRelBlock ; 'vectored' RelBlock routine <02Oct85>
|
|
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
|
MOVEM.L D1/A0-A1/A3-A4,-(A6) ; save registers
|
|
LEA -lenCBH(A0),A4 ; A4 = ptr to CBH <29Aug85>
|
|
|
|
BCLR #CBHinuse,CBHFlags(A4) ; set not in-use
|
|
|
|
@0 BTST #RBtrash,D1 ; buffer to be trashed?
|
|
BEQ.S @1 ; ...no ->
|
|
BSR.S MarkEmpty ; mark it empty <23Oct85>
|
|
BCLR #CBHdirty,CBHFlags(A4) ; ...and not dirty
|
|
BRA.S RBExit ; all done ->
|
|
|
|
@1 BTST #RBdirty,D1 ; buffer dirty?
|
|
BEQ.S @2 ; no ->
|
|
BSET #CBHdirty,CBHFlags(A4) ; set dirty flag
|
|
|
|
@2 BTST #RBwrite,D1 ; force write requested ?
|
|
BEQ.S RBExit ; no ->
|
|
MOVEA.L A1,A3 ;
|
|
JSR WriteBlock ; write block to disk
|
|
BCLR #CBHdirty,CBHFlags(A4) ; set cache buffer not dirty
|
|
BRA.S RBExit1 ; all done ->
|
|
RBExit
|
|
CLR.W D0 ; indicate no error
|
|
RBExit1
|
|
MOVEM.L (A6)+,D1/A0-A1/A3-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 - ending file or volume block number
|
|
; A1.L - pointer to cache queue header
|
|
;
|
|
; Output: none
|
|
;
|
|
; Called By: UnMountVol (when VCB goes away), MountVol (errors), Eject/OffLine (it's
|
|
; best not to keep the buggers around . . .).
|
|
;_________________________________________________________________________________
|
|
|
|
TrashVBlks
|
|
MOVE.L jTrashVBlks,-(SP) ; jumptable entry for vTrashVBlks <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vTrashVBlks ; 'vectored' TrashVBlks routine <02Oct85>
|
|
|
|
MOVEM.L A3-A4,-(SP) ; save registers <30Aug85>
|
|
MOVEA.L A1,A3 ; A3 = ptr(CQH) <30Aug85>
|
|
MOVEA.L A3,A4 ; start at top of queue <30Aug85>
|
|
tvbSearch
|
|
MOVEA.L CBHFlink(A4),A4 ; position to next buffer <30Aug85>
|
|
CMPA.L A3,A4 ; back to top of queue ? <30Aug85>
|
|
BEQ.S tvbExit ; yes -> <30Aug85>
|
|
|
|
BTST #CBHempty,CBHFlags(A4) ; buffer empty? <30Aug85>
|
|
BNE.S tvbSearch ; yes, continue search -> <30Aug85>
|
|
|
|
CMPA.L CBHVCBPtr(A4),A2 ; same volume? <30Aug85>
|
|
BNE.S tvbSearch ; no, continue search -> <30Aug85>
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
BRA.S tvbSearch ; continue search <30Aug85>
|
|
tvbExit
|
|
MOVEM.L (SP)+,A3-A4 ; restore registers <30Aug85>
|
|
RTS ; exit TrashVBlks (ALL regs preserved) <30Aug85>
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Routine: TrashBlocks, TrashFBlocks
|
|
;
|
|
; Function: Trashes a specified range of disk blocks for a given file.
|
|
; TrashFBlocks trashes all blocks of a file. The volume cache is
|
|
; always used. This routine is synchronous.
|
|
;
|
|
; TrashBlocks
|
|
; 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
|
|
; Called by: FileWrite (write-in-place), SetEOF (truncating a file).
|
|
;
|
|
; TrashFBlocks
|
|
; Input: D0.L - file number
|
|
; A2.L - VCB ptr
|
|
; Called by: FileDelete (TFS), MFSDelete.
|
|
;
|
|
; Output: none
|
|
;_________________________________________________________________________________
|
|
|
|
TrashFBlocks
|
|
MOVEM.L D0-D6/A0-A4,-(SP) ; save registers <20Sep85>
|
|
MOVE.L D0,D3 ; file number <20Sep85>
|
|
MOVEQ #0,D1 ; zero means trash all blocks <20Sep85>
|
|
BRA.S TBStartSearch ; go start the search <20Sep85>
|
|
|
|
TrashBlocks
|
|
MOVEM.L D0-D6/A0-A4,-(SP) ; save registers
|
|
MOVEQ #9,D3 ; divide-by-512 factor <21Sep85>
|
|
MOVE.L D1,D4 ; starting byte number <17Sep85>
|
|
LSR.L D3,D4 ; D4 = beginning block # <21Sep85>
|
|
|
|
MOVEQ #-1,D5 ; <21Sep85>
|
|
CMP.L D2,D5 ; trash to end of file? (if D2=-1) <21Sep85>
|
|
BEQ.S @1 ; br if so <21Sep85>
|
|
MOVE.L D2,D5 ; number of bytes <21Sep85>
|
|
ADD.L D1,D5 ; add in starting position <21Sep85>
|
|
SUBQ.L #1,D5 ; adjust so we get last blk <21Sep85>
|
|
@1 LSR.L D3,D5 ; 'divide' by 512 to get end block # <21Sep85>
|
|
; (if D5 was -1, it is now $007FFFFF,
|
|
; largest possible file block number)
|
|
MOVE.W D0,D1 ; D1 = refnum <17Sep85>
|
|
BLE.S tbExit ; exit if not a valid refnum <20Sep85>
|
|
|
|
MOVEA.L FCBSPtr,A1 ; A1 = FCB ptr
|
|
MOVEA.L FCBVPtr(A1,D1),A2 ; A2 = VCB ptr <20Sep85>
|
|
MOVE.L FCBFlNm(A1,D1),D3 ; D3 = file number <20Sep85>
|
|
BTST #FCBRscBit,FCBMdRByt(A1,D1) ; resource fork? <20Sep85>
|
|
SNE D6 ; $FF = resource fork, $00 = data fork
|
|
|
|
BSR GetOwnBuf ; check for OwnBuf buffer installed <22Oct85>
|
|
BEQ.S TBStartSearch ; br if no local buffer <22Oct85>
|
|
BTST #CBHempty,COBFlags(A4) ; buffer empty?
|
|
BNE.S TBStartSearch ; do cache search if so <22Oct85>
|
|
MOVE.L COBFlBlk(A4),D0 ; D0 = file block # <22Oct85>
|
|
CMP.L D4,D0 ; block < beginning block? <22Oct85>
|
|
BLT.S TBStartSearch ; do cache search if so <22Oct85>
|
|
CMP.L D5,D0 ; block > ending block? <22Oct85>
|
|
BGT.S TBStartSearch ; do cache search if so <22Oct85>
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
|
|
TBStartSearch ; common code for TrashBlocks, TrashFBlocks
|
|
|
|
MOVE.L jTrashBlocks,-(SP) ; jumptable entry for TrashBlocks <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vTrashBlocks ; 'vectored' TrashBlocks routine <02Oct85>
|
|
|
|
MOVE.L VCBBufAdr(A2),A3 ; Point to cache queue <21Sep85>
|
|
MOVEA.L A3,A4 ; start at top of queue
|
|
|
|
tbSearch
|
|
MOVEA.L CBHFlink(A4),A4 ; position to next buffer
|
|
CMPA.L A3,A4 ; back to top of queue ?
|
|
BEQ.S tbExit ; yes ->
|
|
|
|
BTST #CBHempty,CBHFlags(A4) ; buffer empty?
|
|
BNE.S tbSearch ; yes, continue search ->
|
|
|
|
CMPA.L CBHVCBPtr(A4),A2 ; same volume?
|
|
BNE.S tbSearch ; no, continue search ->
|
|
CMP.L CBHFlNum(A4),D3 ; same file number?
|
|
BNE.S tbSearch ; no, continue search ->
|
|
TST.W D1 ; trash all blocks? <20Sep85>
|
|
BEQ.S tbTrash ; br if so . . . (special for Delete) <20Sep85>
|
|
|
|
CMP.B CBHFkType(A4),D6 ; same fork?
|
|
BNE.S tbSearch ; no, continue search ->
|
|
MOVE.L CBHFlBlk(A4),D0 ; D0 = file block #
|
|
CMP.L D4,D0 ; block < beginning block?
|
|
BLT.S tbSearch ; yes, continue search ->
|
|
CMP.L D5,D0 ; block > ending block?
|
|
BGT.S tbSearch ; yes, continue search ->
|
|
|
|
tbTrash
|
|
BSR MarkEmpty ; mark it empty <23Oct85>
|
|
BRA.S tbSearch ; continue search
|
|
|
|
; clean up and exit
|
|
|
|
tbExit
|
|
MOVEM.L (SP)+,D0-D6/A0-A4 ; restore registers
|
|
RTS ; exit TrashBlocks
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Routine: CacheWrIP, CacheRdIP
|
|
;
|
|
; Function: Read/write a consecutive number of blocks from/to the disk. The I/O
|
|
; is done directly to/from the user's buffer. This routine is in the
|
|
; cache to allow future caching schemes to buffer entire files if the
|
|
; room exists.
|
|
;
|
|
; Input: A0.L - user parameter block ptr
|
|
; (A1,D1.W) - FCB pointer
|
|
; D4.L - maximum number of bytes to read
|
|
; D5.L - current file position
|
|
; A2.L - VCB Ptr
|
|
;
|
|
; Output: D0.W - error code
|
|
; D6.L - bytes actually read/written (set up by Lg2Phys)
|
|
; All other registers preserved
|
|
;
|
|
; Called by: FileRead,FileWrite
|
|
;
|
|
; By having these routines call Lg2Phys, it would be possible in the
|
|
; future to skip the call and deal with file block numbers in the cache
|
|
; (like GetBlock does), as well as do more than a consecutive block's
|
|
; worth of I/O with one call (maybe even figure all blocks in advance
|
|
; and then do the I/O in disk order).
|
|
;
|
|
;
|
|
;_________________________________________________________________________________
|
|
|
|
CacheRdIP:
|
|
MOVE.L jCacheRdIP,-(SP) ; jumptable entry for CacheRdIP <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vCacheRdIP ; 'vectored' CacheRdIP routine <02Oct85>
|
|
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack <02Oct85>
|
|
MOVE.L D3,-(A6) ; preserve D3 over Lg2Phys, RdBlocks <30Oct85>
|
|
|
|
MOVEM.L A1/D1,-(A6) ; save registers over cache call <01Oct85>
|
|
MOVE.W D1,D0 ; file refnum for flushcache call <01Oct85>
|
|
MOVEQ #0,D1 ; No options on FlushCache <01Oct85>
|
|
MOVE.L VCBBufAdr(A2),A1 ; cache queue header <01Oct85>
|
|
BSR FlushCache ; flush all the blocks of the file <01Oct85>
|
|
MOVEM.L (A6)+,A1/D1 ; restore registers for our use <01Oct85>
|
|
BNE.S rdIPExit ; fail on errors <01Oct85>
|
|
|
|
JSR Lg2Phys ; get D6=byte count, D3=disk start blk <01Oct85>
|
|
BNE.S rdIPExit ; punt on errors <01Oct85>
|
|
|
|
JSR RdBlocks ; keep low-level I/O in its place <01Oct85>
|
|
|
|
rdIPExit BRA.S wrIPExit ; share exit code with CacheWrIP <30Oct85>
|
|
|
|
CacheWrIP:
|
|
MOVE.L jCacheWrIP,-(SP) ; jumptable entry for CacheWrIP <02Oct85>
|
|
RTS ; go there <02Oct85>
|
|
vCacheWrIP ; 'vectored' CacheWrIP routine <02Oct85>
|
|
|
|
MOVE.L (SP)+,-(A6) ; save return address on A6 stack <02Oct85>
|
|
MOVE.L D3,-(A6) ; preserve D3 over Lg2Phys, WrBlocks <30Oct85>
|
|
|
|
JSR Lg2Phys ; get D6=byte count, D3=disk start blk <01Oct85>
|
|
BNE.S wrIPExit ; punt on error <01Oct85>
|
|
|
|
; Trash the volume cache if it contains a block in the range about to be
|
|
; written:
|
|
|
|
MOVEM.L D1-D2,-(A6) ; save registers over cache call <01Oct85>
|
|
MOVE.W D1,D0 ; File RefNum <01Oct85>
|
|
MOVE.L D5,D1 ; Starting pos is current file pos <01Oct85>
|
|
MOVE.L D6,D2 ; Number of bytes . . . <01Oct85>
|
|
BSR Trashblocks ; trash the file blocks <01Oct85>
|
|
MOVEM.L (A6)+,D1-D2 ; no errors for now <01Oct85>
|
|
|
|
JSR WrBlocks ; <01Oct85>
|
|
|
|
wrIPExit MOVE.L (A6)+,D3 ; restore D3 <30Oct85>
|
|
MOVE.L (A6)+,-(SP) ; put return address back on stack <02Oct85>
|
|
TST.W D0 ; set CCR on result code <02Oct85>
|
|
RTS ; <02Oct85>
|
|
;_________________________________________________________________________________
|
|
;
|
|
; Cache subroutines
|
|
;_________________________________________________________________________________
|
|
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; DQCBuf (Dequeue Cache Buffer) subroutine
|
|
;
|
|
; Function: Detaches a cache buffer from a queue.
|
|
;
|
|
; Input: A4.L - CBH pointer
|
|
;
|
|
; Output: none
|
|
;_________________________________________________________________________________
|
|
|
|
DQCBuf
|
|
MOVEM.L A0-A1,-(SP) ; save regs
|
|
|
|
MOVEA.L CBHBlink(A4),A0 ; addr of previous Q element
|
|
MOVEA.L CBHFlink(A4),A1 ; addr of next Q element
|
|
MOVE.L A1,CBHFlink(A0) ; link to next element
|
|
MOVE.L A0,CBHBlink(A1) ; link to previous element
|
|
|
|
MOVEM.L (SP)+,A0-A1 ; restore regs
|
|
RTS ; exit DQCBuf
|
|
|
|
;_________________________________________________________________________________
|
|
;
|
|
; NQCBuf (Enqueue Cache Buffer) subroutine
|
|
;
|
|
; Function: Inserts a cache buffer in a queue at a specified point.
|
|
;
|
|
; Input: A4.L - CBH pointer
|
|
; A0.L - position pointer (previous queue element)
|
|
;
|
|
; Output: none
|
|
;_________________________________________________________________________________
|
|
|
|
NQCBuf
|
|
MOVE.L A1,-(SP) ; save reg
|
|
|
|
MOVEA.L CBHFlink(A0),A1 ; addr of next Q element
|
|
MOVE.L A1,CBHFlink(A4) ; link to next element
|
|
MOVE.L A4,CBHBlink(A1) ; ...
|
|
|
|
MOVE.L A4,CBHFlink(A0) ; link to previous element
|
|
MOVE.L A0,CBHBlink(A4) ; ...
|
|
|
|
MOVE.L (SP)+,A1 ; restore reg
|
|
RTS ; exit NQCBuf
|
|
|
|
END
|
|
|
|
|