supermario/base/SuperMarioProj.1994-02-09/OS/HFS/Extensions/BTPScan.a
2019-06-29 23:17:50 +08:00

460 lines
18 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.

;
; File: BTPScan.a
;
; Written by: David Feldman
;
; Copyright: © 1988-1992 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM1> 4/15/92 kc Removed the "ROM" prefix from the RomBind routines.
; • Pre-SuperMario comments follow •
; <13> 9/13/91 JSM Cleanup header.
; <12> 3/11/91 dnf dba, #83780: Remove HFS stack overflow check from BTPScan, which
; isnt the right place for such a check. Also, this was left over
; debugging code which should be removed. Also, this code didnt
; really help because it checks the stack depth at the wrong time.
; <11> 2/12/91 dnf gbm, #RemoveDebuggerStatements: Change _Debugger break on stack
; overflow to a call to _SysErr with #dsFSErr.
; <10> 1/17/91 dnf (kst) Turn on the “please cache this” advisory bit when reading
; catalog nodes. Do a FlushVFiles before we start parsing records
; <9> 11/06/90 dnf (with dba) Remove explicit noCache setting on _Reads. Kenny's
; spiffy new cache code makes the cache case better.
; <8> 10/30/90 dnf (with dba) Fix off-by-one bug that was screwing up handling of
; the noMoreReads bit (showed up in timed CatSearch).
; <7> 9/22/90 dnf Add error check to #FirstTime case through ValidateNode
; <6> 9/10/90 dnf Fix incorrect handling of catPositionChangedErr.
; <5> 8/20/90 gbm (dnf and gbm, actually) Fix overcounting of nodes on search
; exit/restart.
; <4> 7/30/90 dnf convert to linked patch; change rom reference names and use
; jsrRom macro.
; <3> 2/26/90 dnf Add support for NoMoreReads bit and LastTime bit
; <2> 2/4/90 dnf Get rid of include of HFS70Equ.a
; <1.3> 9/7/89 dnf Rewrote BTIPScan and BTEndPScan to support CatPosition record
; <1.2> 8/26/89 dnf Put in temporary code to check for stack high water mark
; <1.1> 7/31/89 dnf Change cache bypass to per-call, redo BTGetPhys
; <1.0> 5/30/89 dnf Integrate CatSearch, FileID's and Desktop Database Mgr into one
; ptch
; 12/20/88 dnf Broke off from CatSearch
;
; BTree Manager routines
; Function: These routines provide rapid access to unordered BTree records
;
; External BTIPScan - Initializes state info for a physical file scan, returns 1st record
; Routines: BTEndPScan - Cleans state off of A6 stack, returns hint
; BTGetPhys - Increments current record, gets it
;
; Internal GetMultNodes - Reads disk-order sequential Btree nodes
; Subroutines: ValidateNode - Validates PSR.curNodePtr
; ValidateRec - Finds next valid record at or after PSR.curRec
;
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'BTScanPriv.a'
Include 'FileMgrPrivate.a'
Include 'LinkedPatchMacros.a'
PRINT ON
export BTGetPhys
;__________________________________________________________________________________
;
; Routine: BTIPScan
;
; Function: Initializes PScan state record on A6 stack.
; Checks CatPosition record
; Returns 1st record from btree
;
; Input: D0.W - Btree reference number
; D3.L - Length of read buffer
; A0.L - Pointer to CatPos record
; A1.L - Address of read buffer
;
; Output: D0.L - result code (CatPosChanged or result from BTFirstPhys)
; D1.W - size of record
; A0.L - ptr(key)
; A1.L - ptr(data record)
; A4.L - pointer to PScan state record
; A6.L - lower by size of PScan state record
; Called by: CMCatSearch
;__________________________________________________________________________________
BTIPScanRegs reg a2
BTIPScan: proc export
SUBA.W #PSR.size, A6 ; allocate a state record on A6 stack
MOVEA.L A6, A4 ; save pointer to state record
MOVE.L (SP)+, -(A6) ; move return address to file system stack
MOVE.L A2, -(A6) ; save a2
with CatPosition
MOVEA.L FCBSPtr, A2 ; A2 = ptr(base of FCB array)
ADDA.W D0, A2 ; A2 = ptr(Btree FCB)
MOVE.L A2, PSR.fcbPtr(A4) ; hold on to fcbPtr
st FlushOnly ; only flushing . . .
move.l fcbVPtr(a2), a2 ; vcb pointer into a2
jsrROM FlushVFiles ; flush all files on this volume
bne.s @BTIExit ; bail on the first hint of trouble
move.l PSR.fcbPtr(a4), a2 ; a2 = ptr(Catalog FCB)
MOVE.L fcbBTCBPtr(A2), A2 ; A2 = ptr(Catalog Btree BTCB)
MOVE.L A2, PSR.btcbPtr(A4) ; hold on to btree control block ptr
MOVE.L D3, PSR.readBufLen(A4) ; hold on to length of read buffer
MOVE.L A1, PSR.readBufPtr(A4) ; hold on to address of read buffer
MOVE.L cpNodeNumber(A0), PSR.curNode(A4) ; move 1st btree node # to state record
MOVE.W cpRecNumber(A0), PSR.curRec(A4) ; move 1st record # to state record
MOVE.L cpGoodNodeCount(A0), PSR.goodCount(A4) ; move count to state record
MOVE.L btcNNodes(A2), D0 ; D0 = total # of nodes in catalog btree
SUB.L btcFree(A2), D0 ; D0 = total # - free nodes. ( = # allocated)
MOVE.L D0, PSR.maxNodes(A4) ; D0 = max # nodes we need to look at
CLR.B PSR.flags(A4) ; clear the flags
MOVE.L cpWriteCount(A0), D0 ; D0 = user's expected writecount
BNE.S @UserWriteCount
; the user put a zero in the writecount, indicating that we should
; start at the beginning of the catalog.
CLR.L PSR.curNode(A4)
CLR.W PSR.curRec(A4)
CLR.L PSR.goodCount(A4)
MOVE.L btcWCount(A2), D0 ; D0 = current catalog writecount
MOVE.L D0, cpWriteCount(A0) ; validate the user's position
@UserWriteCount:
CMP.L btcWCount(A2), D0 ; does the catalog's writecount match the user's?
BEQ.S @sameCatalog
MOVE.L btcWCount(A2), cpWriteCount(A0) ; store current writecount in case caller really wants it
MOVE.W #catChangedErr, d0 ; report the catalog changed error
BRA.S @BTIExit
@sameCatalog:
BSET #FirstTime, PSR.flags(A4) ; since the buffer is empty, force a read
BSR BTGetPhys ; go get the 1st record
@BTIExit:
MOVE.L (A6)+, A2 ; restore a2
MOVE.L (A6)+, -(SP) ; restore return address
TST.W D0 ; re-test the result code to set flags
RTS
endwith
endproc
;__________________________________________________________________________________
;
; Routine: BTEndPScan
;
; Function: Cleans up PScan state record on A6 stack and transfers btree position
; back into CatPosition record.
;
; Input: A0.L - pointer to CatPos record
; A4.L - pointer to PScan state record
;
; Output: A0.L - pointer to updated CatPos record
; A6.L - higher by size of PScan state record
;
; Called by: CMCatSearch
;__________________________________________________________________________________
BTEndPScan: proc export
with CatPosition
MOVE.L PSR.curNode(A4), cpNodeNumber(A0) ; save node we're on now
MOVE.W PSR.curRec(A4), cpRecNumber(A0) ; save record we're on now
MOVE.L PSR.goodCount(A4), cpGoodNodeCount(A0) ; save how many good nodes we've seen
ADDA.W #PSR.size, A6 ; deallocate state record
RTS
endwith
endproc
;__________________________________________________________________________________
;
; Routine: GetMultNodes
;
; Function: Reads multiple Btree nodes into a buffer.
; Nodes in buffer are "raw", I.E. not ChkNode'd
; GetMultNodes trusts the caller about the size of the read buffer
;
; Input: D1.L - # nodes wanted
; D2.L - 1st btree node wanted
; A1.L - address of read buffer
; A2.L - BTCB ptr
;
; Output: D0.W - result code (from read)
;
; Called by: ValidateNode
;__________________________________________________________________________________
GMRegs reg D1-D6/A0-A3
GetMultNodes: proc
MOVE.L (A7)+, -(A6) ; move return address to HFS stack
MOVEM.L GMRegs, -(A6)
SUBA.W #HIOP.size, A6 ; allocate a param block on A6
MOVEA.L A6, A0 ; A0 = ptr(param block)
MOVE.W btcRefNum(A2), HIOP.ioRefNum(A0) ; refNum of btree file
MULU btcNodeSize(A2), D1 ; D1 = # bytes wanted
MOVE.L D1, HIOP.ioReqCount(A0) ; for consistency, stick in ioReqCount
MOVE.L A1, HIOP.ioBuffer(A0) ; user's buffer
MULU btcNodeSize(A2), D2 ; D2 = byte position of 1st node requested
MOVE.L D2, D5 ; CacheRdIP wants file pos in D5
MOVE.L FCBSPtr, A1 ; A1 = ptr(FCB array)
MOVE.W btcRefNum(A2), D1 ; (A1, D1.W) = FCB ptr
MOVE.L HIOP.ioReqCount(A0), D4 ; D4 = # bytes (for CacheRdIP)
LEA (A1, D1.W), A3 ; A3 = ptr(FCB)
MOVEA.L fcbVPtr(A3), A2 ; A2 = ptr(VCB) (for CacheRdIP)
; Since I don't call seek, I'll set the inputs anyway, for consistency
MOVE.W #fsFromStart, HIOP.ioPosMode(A0) ; we'll set the mark ourselves <1.1> <9>
ori.b #fsCacheAdvise, HIOP.ioPosMode+1(a0); ask for extra caching attention <10>
MOVE.B #fsRdPerm, HIOP.ioPermssn(A0) ; read permission on file
CLR.L ioPosOffset(A0) ; offset = 0 'cus we're doing the mapping
CLR.L ioActCount(A0) ; since we haven't read any bytes yet
MOVE.L D2, fcbCrPs(A3) ; put our current pos back into fcb
@RdTop:
jsrRom CACHERDIP ; go do the read
@1: BNE.S @Exit
ADD.L D6, D5 ; advance current file position
ADD.L D6, ioActCount(A0) ; and tally up bytes read
SUB.L D6, D4 ; D4 now has ioReqCount - what was just read
BNE.S @RdTop
@Exit
ADDA.W #HIOP.size, A6 ; deallocate param block
MOVEM.L (A6)+, GMRegs
MOVE.L (A6)+, -(A7) ; restore return address
TST.W D0
RTS
endproc
;__________________________________________________________________________________
;
; Routine: TallyAndCheckNodeCount
;
; Function: Add one to PSR.goodCount. Check PSR.goodCount against PSR.maxNodes
; and if we've seen maxNodes good nodes, set PSR.flags LastGoodNode
;
; When PSR.flags LastGoodNode is set it indicates that we've seen all of
; the allocated nodes in the current btree, and if more nodes are requested
; we should return end of file, since we know no more could possibly be valid.
;
; Input: A4.L - pointer to PScan state record
;
; Output: A4.L - pointer to PScan state record
;
; Called by: ValidateRec, ValidateNode
;__________________________________________________________________________________
TallyAndCheckNodeCount: proc
MOVE.L D1, -(SP) ; save one dirty register
ADD.L #1, PSR.goodCount(A4) ; tally another node
MOVE.L PSR.goodCount(A4), D1
CMP.L PSR.maxNodes(A4), D1 ; have we seen enough?
BLO.S @notDone ; if D1 < maxNodes, we've got lots of nodes left
BSET.B #LastGoodNode, PSR.flags(A4)
@notDone
MOVE.L (SP)+, D1 ; restore one dirty register
RTS
endproc
;__________________________________________________________________________________
;
; Routine: ValidateNode
;
; Function: Validates PSR.curNodePtr by looking for the current node # in the
; read buffer. If it's not there, then a buffer full of nodes
; is read in. Once the node is there, it is checked (with ChkNode)
; and then verified to be a leaf node. If not, the current node # is
; incremented and we try again from the top.
;
; On entry, PSR.curNode must be valid.
; If FirstTime is not set, then PSR.buf1stNode and PSR.bufCount must be valid
; On exit, PSR.curNode, PSR.curNodePtr, PSR.buf1stNode, and PSR.bufCount are all valid
;
; Buffer variables buf1stNode and bufCount:
; buf1stNode = btree node # of the 1st node in the buffer
; bufCount = total # nodes in the buffer
;
; If we find the flag "noMoreReads" set true, we'll return nodes
; until we're forced to go to disk. When we're forced to go to disk
; we return a userCanceledErr, which signals to the caller that
; we're done with the current buffer. This allows a caller to
; ask the bt scanner not to do any more expensive disk reads while
; processing all of the records already read in from disk. A caller
; can set noMoreReads true in between any two calls to BTGetPhys
;
; Input: A4.L - pointer to PScan state record
;
; Output: D0.W - result code (from read)
; A4.L - pointer to PScan state record
;
; Called by: ValidateRec, BTGetPhys
;__________________________________________________________________________________
VNRegs reg A0-A2/D1-D2
ValidateNode: proc
MOVE.L (A7)+, -(A6) ; move return address to HFS stack
MOVEM.L VNRegs, -(A6)
@VNTop MOVE.L PSR.curNode(A4), D0 ; D0 = node # we're looking for
BCLR.B #FirstTime, PSR.flags(A4) ; force a disk read?
BNE.S @read
BTST.B #LastGoodNode, PSR.flags(A4) ; force an eof?
BNE @eofExit
MOVE.L PSR.buf1stNode(A4), D1 ; D1 = 1st node # in buffer
CMP.L D1, D0 ; if (curNode < buf1stNode), read
BLO.S @read
ADD.L PSR.bufCount(A4), D1 ; D1 = 1st + count = last node # in buffer + 1
CMP.L D1, D0 ; if (curNode < last node + 1), don't read
BLO.S @inBuf ; I.E. curNode is already in the buffer
@read MOVEA.L PSR.btcbPtr(A4), A2 ; A2 = ptr(BTCB)
MOVE.L btcNNodes(A2), D1 ; D1 = # nodes in btree
SUB.L D0, D1 ; D1 = # nodes left to process
BHI.S @1 ; any left?
BRA @eofExit
@1: BTST.B #NoMoreReads, PSR.flags(A4) ; Should we do any more reads?
BEQ.S @DoMoreReads
MOVE.W #userCanceledErr, d0 ; set up our signaling error
BRA @done
@DoMoreReads:
MOVE.L PSR.readBufLen(A4), D2 ; D2 = buffer length in bytes
LSR.L #8, D2
LSR.L #1, D2 ; D2 = buffer length in 512-byte nodes
CMP.L D2, D1 ; is buffer big enough for all remaining nodes?
BLS.S @2 ; yup; go ahead and read
MOVE.L D2, D1 ; D1 = size of buffer in nodes
@2 MOVE.L D0, D2 ; D2 = curNode = 1st node to read
MOVE.L PSR.readBufLen(A4), D0 ; buffer length
MOVEA.L PSR.readBufPtr(A4), A1 ; buffer address
BSR GetMultNodes
BNE.S @done ; error?
MOVE.L D2, PSR.buf1stNode(A4) ; curNode is now 1st in read buffer
MOVE.L D1, PSR.bufCount(A4) ; # now in buffer = # nodes read
@inBuf MOVEA.L PSR.readBufPtr(A4), A1 ; A1 = ptr(read buffer)
MOVE.L PSR.curNode(A4), D1 ; D1 = current node
SUB.L PSR.buf1stNode(A4), D1 ; D1 = curNode - buf1stNode
MOVEA.L PSR.btcbPtr(A4), A2 ; A2 = ptr(BTCB)
MULU btcNodeSize(A2), D1 ; D1 = (curNode - buf1stNode) * nodeSize
ADDA.L D1, A1 ; A1 now points to current node in buffer
MOVE.L A1, PSR.curNodePtr(A4) ; and we have a valid curNodePr
MOVE.L A4, D2 ; save PSR around ChkNode
MOVEA.L PSR.btcbPtr(A4), A4 ; A4 = ptr(BTCB)
jsrRom CHKNODE ; is this node valid?
MOVEA.L D2, A4 ; restore PSR
BNE.S @more ; not valid, get another node
MOVEA.L PSR.curNodePtr(A4), A0 ; A0 = ptr(current node)
CMP.B #ndLeafNode, ndType(A0) ; is this also a leaf node?
BEQ.S @done ; yes, this is a valid leaf node
BSR TallyAndCheckNodeCount ; count the valid non-leaf node
@more ADDQ.L #1, PSR.curNode(A4) ; let's try the next node
CLR.W PSR.curRec(A4) ; starting at record 0
BRA @VNTop
@eofExit:
MOVEQ.L #eofErr, D0
@done MOVEM.L (A6)+, VNRegs
MOVE.L (A6)+, -(A7) ; restore return address
TST.W D0
RTS
endproc
;__________________________________________________________________________________
;
; Routine: ValidateRec
;
; Function: Looks for PSR.curRec in the current node. If it's not there,
; the node count is advanced and the next node read.
; N records in a node are numbered 0 - (N-1)
;
; PSR.curNodePtr must be valid on entry to ValidateRec
;
; Input: A4.L - pointer to PScan state record
;
; Output: DO.L - result code (from read)
; A4.L - pointer to PScan state record
;
; Called by: BTGetPhys
;__________________________________________________________________________________
ValidateRec: proc
MOVE.L (A7)+, -(A6) ; move return address to HFS stack
MOVE.L A0, -(A6)
@VRTop:
MOVEA.L PSR.curNodePtr(A4), A0 ; A0 = ptr(current node)
MOVE.W ndNRecs(A0), D0 ; D0 = # records in current node
CMP.W PSR.curRec(A4), D0 ; # recs in node >= current rec #?
BHI.S @noErr ; cur rec is in cur node, so exit
BSR TallyAndCheckNodeCount ; count the valid leaf node we're now done with
ADDQ.L #1, PSR.curNode(A4) ; next BTree node
CLR.W PSR.curRec(A4) ; and the 1st record in that node
BSR ValidateNode ; go find new node
BNE.S @1 ; error?
BRA.S @VRTop ; Keep going until you find a good one
@noErr:
SUB.W D0, D0 ; clear result code
@1: MOVEA.L (A6)+, A0
MOVE.L (A6)+, -(A7) ; restore return address
TST.W D0
RTS
endproc
;__________________________________________________________________________________
;
; Routine: BTGetPhys
;
; Function: Returns the next btree record in physical disk order.
;
; Input: A4.L - pointer to PScan state record.
; checks the state of the FirstTime bit in the flags word
;
; Output: D0.W - result code (from read)
; D1.W - size of record
; A0.L - ptr(key)
; A1.L - ptr(data record)
; A4.L - pointer to PScan state record
;
; Called by: CMCatSearch
;__________________________________________________________________________________
BTGetPhys: proc
MOVE.L (A7)+, -(A6) ; move return address to HFS stack
BTST.B #FirstTime, PSR.flags(A4) ; is this the first time through?
BEQ.S @NotFirstTime ; if not, just get the record
; it's the first time through, so do a read before trying
; to get a record from the buffer.
BSR ValidateNode ; get the 1st valid node
BNE.S @Exit ; punt on errors <7>
@NotFirstTime
MOVE.L A4, -(A6) ; save PSR ptr around LocRec call
BSR ValidateRec ; point PSR.curRecPtr at node w/next record
BNE.S @1 ; error?
MOVEA.L PSR.curNodePtr(A4), A1 ; A1 = ptr(node)
MOVE.W PSR.curRec(A4), D1 ; D1 = record we want
ADDQ.W #1, PSR.curRec(A4) ; advance to next record for next time <8>
MOVEA.L PSR.btcbPtr(A4), A4 ; A4 = ptr(BTCB)
jsrRom LOCREC
@1: MOVEA.L (A6)+, A4 ; restore PSR ptr
@Exit:
MOVE.L (A6)+, -(A7) ; restore return address
TST.W D0
RTS
endproc
end