mac-rom/OS/HFS/Extensions/BTreeMgr/BTreeQueue.a
Elliot Nunn 0ba83392d4 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-09-20 18:04:16 +08:00

770 lines
30 KiB
Plaintext

;
; File: BTreeQueue.a
;
; Contains: This file separates Btree manager from HFS one layer above.
; Support async call for BTree Manager using its own cache buffers and
; param block queue.
;
; Written by: Kenny SC. Tung
;
; Copyright: © 1989-1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM1> 4/1/92 kc Changed the name prefex used to distinguish
; routines used by the external B-Tree manager
; from the versions used by the File System.
; Removed forRom conditional.
; ¥ Pre-SuperMario comments follow ¥
; <20> 9/13/91 JSM Cleanup header.
; <19> 6/12/91 LN Removed #includes for private interfaces from public interfaces.
; Changed #include 'HardwareEqu.a' to 'HardwarePrivateEqu.a'
; <18> 3/17/91 dnf ppd, #dnf-0013: Use the result code from ioResult(a0) instead of
; from d0 to be extra-clean on async calls.
; <17> 3/17/91 dnf dba, dty, #84977: use CallWithRegistersPreserved in dispatching;
; save D0 across calls to completion routines.
; <16> 12/18/90 KST With Bill B. Adding DoAlloc back.
; <15> 12/11/90 KST With Bill B. Change _Allocate. to _SetEOF because some foreign
; FS doesn't handle _Allocate call.
; <14> 9/10/90 KST Took out debugger labels.
; <13> 8/3/90 KST Adding FlushFile call in ExtDoAOCRWF.
; <12> 7/30/90 dnf Make some changes so BTree manager can work as a linked patch.
; <11> 7/18/90 KST btRsvAccess will not return error if the file has already been
; reserved, instead the request will be queued.
; <10> 7/11/90 gbm add END
; <9> 4/17/90 KST Adding btDebugSYS conditional in "ExtBTQueue".
; <8> 3/30/90 KST Save registers across ASYNC call.
; <7> 3/20/90 KST Fixing a bug in handling B*Tree request queue, removing <1.9>
; registers saving redundant code, and other documentation
; changes.
; <6> 3/13/90 KST documentation change and code cleanup.
; <5> 2/20/90 KST map node was written before "RelMap" was called, now we only
; mark it dirty and write it back in "ExtRelBlock" !
; <4> 2/4/90 DNF Add include of FileMgrPrivate.a
; <3> 1/22/90 KST Changed queue dispatcher to support access control. -- KST
; 1/16/90 KST added code to support AppleShare's access control.
; <1.9> 11/10/89 dnf Fix register trashing in ExtDoAOCRWF
; <1.8> 10/2/89 KST Code cleanup.
; <1.7> 9/25/89 KST Took out the Debugger call. BTFlush now will flush RamCAche.
; <1.6> 9/17/89 KST New buffer scheme. No more B*Tree cache buffers.
; <1.5> 9/8/89 KST Fixed a bug in ExtWriteBlock. We need to save the RefNum because
; the param is being reused.
; <1.4> 8/7/89 KST Code cleanup.
; 7/27/89 KST Use jump table for dispatching calls; Get rid of macro calls
; 7/27/89 KST Save async trap bit in BTVQflag of BTVar for all operations in
; ExtBTQueue (BTDispatch)
; <1.3> 7/13/89 KST Take out debug code for write error.
; <1.2> 7/6/89 KST Cleaned up the code.
; <1.1> 6/27/89 KST Fixed a bug in DoACORW routine that trashes A1.
; 6/27/89 KST Preserve A1 across ExtDoAOCRWF call.
; <1.0> 6/14/89 KST New file for the new implementation of BTreeMgr.
; 5/15/89 KST Check A6 stack overflow across ExtDoAOCRWF call.
; 4/24/89 KST New today.
;
LOAD 'StandardEqu.d'
INCLUDE 'BTreeEqu.a' ; BTManager
Include 'BTreePrivate.a'
Include 'FileMgrPrivate.a'
Include 'HardwarePrivateEqu.a'
IF (&TYPE('btDebug') = 'UNDEFINED') THEN
btDebug EQU 0
ENDIF
IF &trim(&type('btDebugSYS')) = 'UNDEFINED' THEN ; <16Apr90>
btDebugSYS EQU 0 ; <16Apr90>
ENDIF ; <16Apr90>
;__________________________________________________________________________
; Function: ExtBTQueue.
; All B*Tree requests branch to here to get queued and serviced in the
; order they enter the queue (but not necessarily serviced in FCFS because
; that file's access might have been reserved).
; Input: D0 = dispatch selector, A0 = ioParam, D1.W = trapword (A08E)
;__________________________________________________________________________
ExtBTQueue PROC EXPORT
export ExtBTDispatchRequest
IMPORT ExtIsBtree
MOVE.W #1,IOResult(A0) ; set IOResult to "in process"
MOVE.W D1,IOTrap(A0) ; save the trap number
MOVE.B D0,ioTrap+1(A0) ; Store trap index in low byte
MOVE.L (SP)+,IOCmdAddr(A0) ; save address to go to when ready
MOVE.W #btQType,IOType(A0) ; say its a Btree queueing element
BTST #btAsyncBit,D1 ; async bit on?
BNE BTAsync ; yes
;; else, it's a sync call:
CLR.L IOCompletion(A0) ; no completion routines for sync calls
MOVE.L A0,-(SP) ; save parameter block ptr
BSR.S BTAsync ; queue it up (and maybe call it)
MOVE.L (SP)+,A0 ; restore ioParam
WaitLoop MOVE.W IOResult(A0),D0 ; get the result code into D0
IF btDEBUG THEN ; debug ... see if we can continue ?
CMPI #btADAndWait,D0 ; waiting for a RelAccess call?
BNE.S @3 ; no, never mind
;; if it is, then we need a RelAccess call. How?
MOVE.L A0,-(SP) ; save parameter block ptr
BSR.S InstallVBL ; install a VBL task
MOVE.L (SP)+,A0 ; restore ioParam
MOVE.W #1,IOResult(A0) ; but do this only once
BRA.S WaitLoop
@3 TST D0
ENDIF ;...... end of debug
BGT.S WaitLoop ; done when result is zero or negative
EXT.L D0 ; ALWAYS return a long error code
RTS ; return to caller
IF btDEBUG THEN
DC.B $80, 'ExtBTQueue'
ALIGN
DC.W 0
InstallVBL ;; ask VBL to issue the release access BTcall
;; Test program issues a synchronous request and the access is denied.
MOVEQ #vblPhase+2,D0 ; 14 bytes
_NewPtr ,SYS ,Clear
BNE.S @nomem ; exit if no room
MOVE #vType,vblType(A0) ; VBL queue
MOVE.L @vblRelBT,vblAddr(A0) ; pointer to task (this will move the contents)
LEA @vblRelBT,A1
MOVE.L A1,vblAddr(A0) ; pointer to task (this will move the pointer)
MOVE #30,vblCount(A0) ; frequency count
_VInstall
@nomem RTS
@vblRelBT ;; this VBL task will release the file
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1 ; A1 = btVars
MOVEA.L btVParam(A1),A0 ; A0 = private Param
MOVEA.L btVQhead(A1),A1 ; A1 = current btioParam
MOVE ioRefNum(A1),ioRefNum(A0) ; release this file
MOVE.L ioBTRsrvUID(A1),D0 ; test program's UID
ADDQ #1,D0 ; it uses UID - 1
MOVE.L D0,ioBTRsrvUID(A0) ; use this ID to release
CLR.L ioCompletion(A0) ; no completion routine
_BTRelAccess ,Async ; call B*TreeMgr
RTS
ENDIF
BTAsync MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1 ; A1 is also the q header
MOVE SR,-(SP) ; only allow
ORI #HiIntMask,SR ; interrupt for debugging
_ENQUEUE ;
ToBTDispatch ; dispatch the next request (also called by ExtBTCmdDone)
; interrupt disabled. ; Is a request already executing?
BSET #btQBusyBit,btVQFlag(A1) ; A1=btVar, B*Tree busy?
BEQ.S BTDispatch ; if not, just call it
; The B*tree Manager is busy (and we've queued the request), so we're done for now.
; We'll get to it when ExtBTCmdDone is called.
MOVEQ #0,D0 ; no errors (yet)
MOVE.W (SP)+,SR ; re-enable interrupts and return
RTS
BTDispatch ;; A1 = ptr(BTvars)
MOVE (SP)+,SR ; re-enable interrupts
MOVEM.L D3-D7/A2-A6,-(SP) ; observe Pascal regsave conventions <17>
bsr.s ExtBTDispatchRequest
MOVEM.L (SP)+,D3-D7/A2-A6 ; restore registers <17>
rts
ExtBTDispatchRequest:
;; Added code to support AppleShare's access control: -- 12/14/89
;; We may not service the request in FCFS order. If the request we're servicing
;; is not in the front of the queue, then we move it to the front.
;; If we cannot service any of the requests, we clear the BUSY flag and leave.
LEA btVQHead(A1),A0 ; A1=btVar, A0=ioParam
@scanLoop MOVE.L qLink(A0),D0 ; Pick up the next element on the queue
BEQ @ad_ret ; If EQ, there are no requests pending
@loop2 MOVEA.L D0,A0 ; A0 -> BT Parameter block
;; Loop through the queue, looking for an element that can be started:
MOVEQ #0,D0
MOVE.B ioTrap+1(A0),D0 ; which operation
CMP.W #btRsrvAccessN,D0 ; reserveAccess will be queued now <17Jul90>
BEQ.S @controlled ; yes, this is RsrvAccess call <17Jul90>
CMP.W #btOpenN,D0 ; INIT, OPEN are free to go
BLE.S @moveQueue ; no access control
CMP.W #btDeleteN,D0
BHI.S @moveQueue ; no access control
@controlled
;; we are in the range of operations that will change the contents of B*Tree.
;; check if the file is locked by other process; if not then move the request
;; to the front if it isn't already at the front.
BSR btFileUid ; get this B*tree file's Uid in D1
BNE.S @moveQueue ; not btree error, but let B*TreeMgr handles it
TST.L D1 ; Check: B*-Tree access reserved?
BEQ.S @moveQueue ; If 0, it's a free-for-all
CMP.L ioBTRsrvUID(A0),D1 ; If not, is this call for the right user?
BEQ.S @moveQueue ; yes, we have the access
;; If we don't have the access permission and this is a SYNCHRONOUS call,
;; then we set a special value in ioResult.
BTST #btAsyncBit,ioTrap(A0) ; SYNC call?
BNE.S @scanLoop ; No, it's not - check next one in queue
MOVE #btADAndWait,IOResult(A0) ; access denied on a SYNC call, mark we're waiting
BRA.S @scanLoop
@moveQueue CMP.L btVQHead(A1),A0 ; is this head?
BEQ.S @doit ; yes
;; If not, then we need to move it to the front
MOVE SR,-(SP) ; only allow level 3
ORI #HiIntMask,SR ; interrupts for debugging
MOVE.L A1,-(SP) ; save btVars
LEA btVQHdr(A1),A1 ; A1 = theQueue, A0 = qEntry
_dequeue ; remove it from BT queue
MOVEA.L (SP),A1 ; A1 = btVars
MOVEA.L btVQHead(A1),A1 ; A1 = head
MOVE.L A1,qLink(A0) ; add it in the front
MOVEA.L (SP)+,A1 ; restore A1(btVars)
MOVE.L A0,btVQHead(A1) ; A0 = QHead
TST.L btVQTail(A1) ; tail = NULL?
BNE.S @1 ; no
MOVE.L A0,btVQTail(A1) ; A0 = QTail too
@1 MOVE.W (SP)+,SR ; re-enable interrupts
@doit ;;Really gonna execute it. Here, make sure A1=btVar.
MOVEA.L btVSTop(A1),A6 ; Set up A6 for use as stack pointer
;; Before servicing the call, save async bit in BTVars because B*Tree Manager
;; shares its private param block doing I/O, not user param.
BCLR #btAsyncBit,btVQFlag(A1) ; sync is default
BTST #btAsyncBit,ioTrap(A0) ; Async only?
BEQ.S @2 ; sync call
BSET #btAsyncBit,btVQFlag(A1) ; set async bit
@2 MOVEA.L IOCmdAddr(A0),A1 ; get address to call
JMP (A1) ; execute Btree command and return <17>
; (all commands finish by jumping to ExtBTCmdDone)
;; Access has been denied, clear busy flag and return,
;; (the request(s) will finally be serviced when a RelAccess call comes in).
@ad_ret BCLR #btQBusyBit,btVQFlag(A1) ; Btree not servicing
;; because this test and clear is not atomic, I need to do this:
MOVE.L qLink(A0),D0 ; A0 was tail
BEQ.S @qexit ; and it still is
BSET #btQBusyBit,btVQFlag(A1) ; is B*Tree busy?
BEQ @Loop2 ; no, go service the new request
@qexit RTS ; D0 = 0
;; _____________________________________________________________________
;; Function:verify if this file is B*Tree, if it is, get the UID in D1
;; Input: A0 = user param
;; Output: D0=0 if it's Btree; D1 = UID. All other registers preserved.
;; _____________________________________________________________________
btFileUid MOVEM.L D2/A1-A4,-(SP)
BSR ExtIsBtree ; BTree?
BNE.S @3 ; no, not btree
MOVE.L BTCRsrvUID(A4),D1 ; yes, get UID
IF btDebugSYS THEN ; if debugging <16Apr90>
MOVEQ #0,D1 ; .. system btree file <16Apr90>
ENDIF ; .. don't use UID <16Apr90>
@3 MOVEM.L (SP)+,D2/A1-A4
TST D0
RTS
IF btDEBUG THEN
DC.B $80, 'BTDispatch'
ALIGN
DC.W 0
ENDIF
ENDPROC
; _____________________________________________________________________________
; Function: ExtBTCmdDone. All BTcommands jump to here when finished
; ioCompletion routine is called for ASYNC call. The current request
; is removed from the queue, and the next one is serviced if there is one.
; Input: D0.W = error code
; _____________________________________________________________________________
ExtBTCmdDone PROC EXPORT
IMPORT ExtBTDispatchRequest, CallWithRegistersPreserved
IF btDebug THEN
BSR bufDebug ; debugging
ENDIF
EXT.L D0 ; Make a long of it
jsr btQerrorp ; get current ioParam in A0
MOVE SR,-(SP) ; save interrupt state
ORI #HiIntMask,SR ; only debug interrupts allowed
BCLR #btQBusyBit,btVQFlag(A1) ; clear Btree busy flag
; delete the current request from the queue and post any completion routines
MOVE.L QLink(A0),btVQHead(A1) ; get next request, if any
BNE.S bt_CallComp ; branch if queue not empty
CLR.L btVQTail(A1) ; clear tail ptr, too
bt_CallComp
MOVE (SP)+,SR ; restore interrupt state
MOVE.W D0,IOResult(A0) ; post error code
MOVE.L IOCompletion(A0),D1 ; is there a completion routine?
BEQ.S bt_DispNext ; skip if there's not
MOVE.L D1,A1 ; get the completion routine address
JSR (A1) ; call it with A0=param
bt_DispNext ;; check if there's anything else for us to do
MOVE SR,-(SP) ; save interrupt state
ORI #HiIntMask,SR ; only debug interrupts allowed
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1 ; get BT vars
TST.L btVQHead(A1) ; any req pending?
beq @noRequirements ; <dnf> change for linked patch
BSET #btQBusyBit,btVQFlag(A1) ; A1=btVar, B*Tree busy? <17>
bne.s @noRequirements ; if it was already busy, we can return asynchronously <17>
move.w (sp)+,sr ; restore interrupt state <17>
lea.l ExtBTDispatchRequest,a0 ; our dispatcherÕs entry point <17>
jmp CallWithRegistersPreserved ; call it <17>
@noRequirements:
MOVE.W (SP)+,SR ; re-enable interrupts and return
RTS
@doDispatch:
;; _______________________________________________________________
;; Function: get current ioParam in A0 and validate it
;; Output: A0=current request, A1=BTVar ptr
;; _______________________________________________________________
btQerrorp ;; if error just die
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1
MOVEA.L btVQHead(A1),A0 ; A0 = first ioParam
CMP #btQType,IOType(A0) ; it better be an Btree queue element
BNE.S @2 ; or else die
RTS
@2 MOVEQ #DSFSErr,D0
_SysError
IF btDebug THEN ; Debugging ........................
bufDebug MOVEM.L D0/D2/A3-A5,-(A7)
MOVEa.L FSVarsPtr,A5
MOVEA.L fsVars.btMgr(A5),A5
MOVEA.L btBufQHdr(A5),A5 ; A5 = ptr(buffer Qheader)
MOVEQ #0,D2 ; start from 0 <9/15/89>
LEA btQBStart(A5),A3 ; A3=first buffer <9/15/89>
@2 BTST #CBHinuse,btBHFlags(A3) ; buffer in use? <9/15/89>
BNE.S @4 ; yes, error <9/15/89>
ADDQ #1,D2 ; next index <9/15/89>
CMPI #btBufferN,D2 ; all done? <9/15/89>
BEQ.S @6 ; Yes, <9/15/89>
ADDA #$200,A3 ; next buffer <9/15/89>
ADDA #lenBTBH,A3 ; ... and skip header <9/15/89>
BRA.S @2 ; test next <9/15/89>
@4 _debugger ; this shouldn't happen <9/15/89>
; A3 buffer still in use <3/09/90>
@6 MOVEM.L (SP)+,D0/D2/A3-A5
ENDIF
RTS
IF btDEBUG THEN
DC.B $80, 'ExtBTCmdDone'
ALIGN
DC.W 0
ENDIF
ENDPROC ; ExtBTCmdDone
;; ___________________________________________________________________________
;; Function: This is the routine that handles async HFS calls
;; Input: D1 = option word (ALLOCATE/OPEN/CLOSE/READ/WRITE/Flush).
;; A0 = BT private param, assume A1 is free
;; Output: D0=error code
;; 28Jul89 KST Disable system cache for B*Tree when doing R/W
;; 24Aug89 KST Undo previous change, we depend on RamCache
;; 02Aug90 KST Adding flushfile call for BTFlush.
;; ___________________________________________________________________________
asyncRegs REG d2-d7/A2-A5
ExtDoAOCRWF PROC EXPORT
MOVEQ #paramErr,D0 ; preset error code
CMPI #DoMaxNum,D1 ; legal (0~5) ?
BHI.S syncRet ; bad
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1
IF btDebug THEN ;; debugging, check for stack overflow <6/12/89>
MOVE.L btVSTop(A1),D0 ; stack base
SUB.L A6,D0 ; how far does it go
CMPI.L #(btStkLen-24),D0 ; overflow? (reserve 24 bytes for AsyncOC)
BLO.S @1 ; no,
MOVEQ #dsStkNHeap,D0 ; stack overflow
_SysError ; bumb !
ENDIF
@1 LSL #2,D1 ; use jump table
BTST #btAsyncBit,btVQFlag(A1) ; Async call?
BNE.S AsyncOC ; yes
@2 ;; A0/A1 are saved by the system dispatcher
LEA syncTable,A1
JMP (A1,D1) ; service the call
syncTable
_Allocate ; put this back <18Dec90 #16>
BRA.S syncRet
_HOpen
BRA.S syncRet
_Close
BRA.S syncRet
_Read
BRA.S syncRet
_Write
BRA.S syncRet
_FlushFile
BRA.S syncRet
_SetEOF ; adding this <18Dec90 #16>
syncRet RTS
AsyncOC ;; A0 = BTParam to do async open/close...., D1 is the selector
;; A1 = ptr(BTVars)
MOVE.L (SP)+,-(A6) ; save return address
MOVEM.L asyncRegs,-(A6) ; save all regs across async
MOVE.L A6,btVSPtr(A1) ; save BT stack top
BCLR #btAsyncRetd,btVQFlag(A1) ; clear 'premature-return' flag
LEA btIOComp,A1 ; load btIOcomp routine
MOVE.L A1,IOCompletion(A0)
LEA asyncTable,A1
JMP (A1,D1) ; service the call
asyncTable
_Allocate ,Async ; put this back <18Dec90 #16>
BRA.S @4
_HOpen ,Async
BRA.S @4
_Close ,Async
BRA.S @4
_READ ,Async
BRA.S @4
_WRITE ,Async
BRA.S @4
_FlushFile ,Async
BRA.S @4
_SetEOF ,Async ; adding this <18Dec90 #16>
@4 BEQ.S @6 ; branch if no error
TST IOResult(A0) ; really error?
BLE.S btACont ; yes
@6 ; say we returned from B*Tree async call
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1
BSET #btAsyncRetd,btVQFlag(A1) ; set asyncReturn flag
BNE.S btACont1 ; if we already returned, do ioComp now
; do the completion routine not caused by interrupt
btAsyncRTS RTS ; else, return to App. We'll be back
btIOcomp ;; B*Tree ioCompletion routine
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1 ; say we're returning now
BSET #btAsyncRetd,btVQFlag(A1) ; set asyncReturn flag
BEQ.S btAsyncRTS ; if trap didn't really return ASYNC
; then RTS now, we'll be back (from @6)
;; SYNC case in an ASYNC call, it will eventually return to @6
btACont MOVEM.L D4-D7/A4-A6,-(SP) ; preserve non-interrupt registers
PEA btACont2 ; return to here to restore regs
btACont1
MOVEa.L FSVarsPtr,A1 ; restore B*TreeMgr stack
MOVEA.L fsVars.btMgr(A1),A1
MOVEa.L btVSPtr(A1),A6 ; A6 = BT stack
MOVEM.L (A6)+,asyncRegs ; restore regs across async
MOVE.L (A6)+,-(SP)
MOVE.W ioResult(a0),D0 ; any error? <18>
RTS ; return to B*tree manager
btACont2 MOVEM.L (SP)+,D4-D7/A4-A6 ; restore the non-int registers
RTS ;; we're done with it! (return to interrupt handler)
ENDPROC ;; exit ExtDoAOCRWF
;_________________________________________________________________________________
; Routine: ExtGetBlock
; Function: Gets a specified btree node (data fork of a B*Tree file).
; The desired block must be specified by file refnum;
; Unless GBnoRead option is specified, a FS read is performed.
; Input: D0.W - file refnum only
; A1.L - pointer to BTree cache queue header
; 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 - btree node number
; Output: A0.L - addr(buffer) containing desired block
; D0.W - result code, 0 = ok, other = error
; 08Aug89 KSCT Need a inuse flag for the new buffer scheme.
; 15Aug89 KSCT Clear inuse flag if read error!
;_________________________________________________________________________________
ExtGetBlock PROC EXPORT ;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L D1-D6/A1-A5,-(A6) ; save regs
MOVE.B D1,D3 ; D3 = option flags
MOVEA.L A1,A3 ; A3 = ptr(Buf QHdr)
MOVE.W D0,D1 ; D1 = volume or file refnum?
BLT GBErrExit ; volume -> unsupported
MOVEA.L FCBSPtr,A1 ; A1 = ptr(FCB)
MOVEA.L FCBVPtr(A1,D1.W),A2 ; A2 = ptr(VCB)
MOVEA.L FCBBTCBptr(A1,D1),A4; A4 = ptr(BTCB)
MOVEQ #0,D6 ; D6.L=request count
MOVE.W btcNodeSize(A4),D6 ; D6=B*Tree nodesize
BNE.S @1 ; B*tree opened
;; BTOpen wants to read the header but we don't know the NodeSize, assume
;; Header Nodesize is a constant, or at least Header info is in the first 512 bytes.
MOVE.W #btHdrNSize,D6
;; new buffer scheme: not cache (can't get an existing block) <8/21/89>
@1 BTST #GBexist,D3 ; request for an existing block?
BEQ.S @2 ; no ->
IF btDebug THEN
_debugger ; should not happen
ENDIF
MOVEQ #Chnotfound,D0 ; result = 'not found'
BRA GBExit1 ; exit ->
@2 MOVE btQCIndex(A3),D0 ; current index
MOVE D0,D5 ; save current index for error recovery
MOVEA.L A3,A5 ; A3 = A5 = buffer QHdr
CMPI #(btBufferN-1),D0 ; need to wrap around?
BEQ.S @3 ; yes
ADDQ #1,btQCIndex(A3) ; else, bump the index
BRA.S @4
@3 CLR btQCIndex(A3) ; wrap around
@4 MOVE btQBSize(A3),D4 ; D4=INIT buffer size
;; In future release, we need to make sure that D4>=D6, err otherwise.
;; get the buffer based on the index in D0
LEA btQBStart(A3),A3 ; A3=first buffer
BRA.S @6
@5 ADDA D4,A3 ; next buffer
ADDA #lenBTBH,A3 ; ... and skip header
@6 DBRA D0,@5
;; A3=buffer header pointer, check if the buffer is inuse <9/8/89>
;; can't just use the next one sequentially <9/8/89>
BTST #CBHinuse,btBHFlags(A3) ; buffer in use? <9/8/89>
BEQ.S @8 ; No, OK => <9/8/89>
BSR gNextbuf ; Yes, get next one <9/11/89>
BNE.S @gbNoBuf ; no buffer <9/11/89>
;; convert block number into byte position, A3 = buffer header address to use.
@8 MOVEQ #0,D0
MOVE.B btcL2NSize(A4),D0 ; D0=shift bit count
LSL.L D0,D2 ; file block number x size = file position
;; assign buffer to new node, but keep status info <9/8/89>
BSET #CBHinuse,btBHFlags(A3) ; set in use <9/8/89>
MOVE D1,btBHRefNum(A3) ; save refnum
MOVE D6,btBHNSize(A3) ; save node size
MOVE.L D2,btBHFilePos(A3) ; save file position
LEA btBHData(A3),A3 ; A3=ptr(buffer)
BTST #GBnoRead,D3 ; no-read requested ?
BNE.S NoReadBlk ; ...yes, skip read ->
;; FileRead here: D0/A0 are free, (A1,D1)=FCB, A4=BTCB, D2=file pos,D6=nodesize
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1
MOVEA.L btVParam(A1),A0 ; A0 = BT Param
MOVE.W D1,ioRefNum(A0) ; file refnum
MOVE.L A3,IOBuffer(A0) ; buffer to read in
MOVE.W #fsFromStart,IOPosMode(A0) ; position mode(from BOF)
MOVE.L D6,IOByteCount(A0) ; r/w node size
MOVE.L D2,IOPosOffset(A0) ; ... gives position offset
MOVEQ #DoRead,D1 ; indicate this is READ
BSR ExtDoAOCRWF
BEQ.S NoReadBlk ; read OK =>
;; some read error, clean up and exit (clear flag). A3=ptr(buffer)
LEA -btBHData(A3),A1 ; A1=ptr(bufHdr) <9/15/89>
BCLR #CBHinuse,btBHFlags(A1) ; not in use <9/15/89>
@gbNoBuf MOVE D5,btQCIndex(A5) ; restore old index as nothing has happened
BRA.S GBExit1 ; D0=read error ->
NoReadBlk MOVEQ #0,D0 ; all OK
GBExit1 MOVEA.L A3,A0 ; return buffer address in A0 (even error in read)
MOVEM.L (A6)+,D1-D6/A1-A5
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set up condition codes
RTS ; exit ExtGetBlock
GBErrExit MOVEQ #paramErr,D0
BRA.S GBExit1
;_________________________________________________________________________________
; Routine: gNextbuf (internal function)
; Function: Current buffer is inuse, try next one. This function is SYNC.
; Input: D4.W=Btree private buffer size, D5.W=index of current buffer,
; A3=ptr(buffer inuse), A5=ptr(buffer QHdr)
; Output: if D0=0, then A3=new buffer ptr, everything else unchanged,
; btQCIndex(A5) updated to reflect the change.
; if D0<>0 (all inuse), everything unchanged, including A3.
;_________________________________________________________________________________
gNextbuf MOVE.L D2,-(SP)
MOVE D5,D2 ; D2=D5=index <9/11/89>
@gnbLoop ADDQ #1,D2 ; next index <9/11/89>
CMPI #btBufferN,D2 ; need to wrap around? <9/11/89>
BNE.S @1 ; No <9/11/89>
MOVEQ #0,D2 ; Yes, wrap around <9/11/89>
LEA btQBStart(A5),A3 ; A3=first buffer <9/11/89>
BRA.S @2 ; <9/11/89>
@1 ADDA D4,A3 ; A3 points to next buffer <9/11/89>
ADDA #lenBTBH,A3 ; ... and skip header <9/11/89>
@2 ;; we knew D5 is inuse, so don't bother testing it. A3 = ptr(D5 buffer)
CMP D2,D5 ; all done? <9/11/89>
BEQ.S @gnbNobuf ; yes, all inuse. punt-> <9/11/89>
BTST #CBHinuse,btBHFlags(A3) ; buffer in use? <9/11/89>
BNE.S @gnbLoop ; yes, try next one <9/11/89>
;; found the free buffer, D2 is the index, update the btQCIndex field
ADDQ #1,D2 ; next index <9/11/89>
CMPI #btBufferN,D2 ; need to wrap around? <9/11/89>
BNE.S @3 ; No <9/11/89>
MOVEQ #0,D2 ; Yes, wrap around <9/11/89>
@3 MOVE D2,btQCIndex(A5) ; and save it in qHeader <9/11/89>
MOVEQ #0,D0 ; perfect <9/11/89>
@gnbExit MOVE.L (SP)+,D2
TST D0
RTS
@gnbNobuf
IF btDebug THEN
_debugger ; this shouldn't happen <9/11/89>
ENDIF
MOVEQ #ChNoBuf,D0 ; result=all buffers in use <9/11/89>
BRA.S @gnbExit ; <9/11/89>
ENDPROC
;_________________________________________________________________________________
;
; Routine: ExtRelBlock (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 buffer header (not used)
; Output: D0.W - result code
; 0 = ok
; other = error (can only occur if RBwrite option specified)
;_________________________________________________________________________________
ExtRelBlock PROC EXPORT ;
IMPORT ExtWriteBlock ;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L D1/A0/A4,-(A6) ; save registers
LEA -lenbtBH(A0),A4 ; A4 = ptr to CBH
BTST #CBHdirty,btBHFlags(A4) ; buffer dirty? <2/20/90>
BEQ.S @1 ; no <2/20/90>
CLR.B btBHFlags(A4) ; clear dirty/in-use <2/20/90>
BRA.S @4 ; write dirty mapnode <2/20/90>
@1 CLR.B btBHFlags(A4) ; clear in-use <9/8/89>
BTST #RBdirty,D1 ; buffer dirty?
BNE.S @4 ; Yes ->
@2 BTST #RBwrite,D1 ; force write requested ?
BEQ.S RBExit ; no ->
@4 JSR ExtWriteBlock ; write block to disk
BRA.S RBExit1 ; all done -> D0 set
RBExit
CLR.W D0 ; indicate no error
RBExit1
MOVEM.L (A6)+,D1/A0/A4 ; restore registers
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set up condition codes
RTS ; exit ExtRelBlock
ENDPROC
;_________________________________________________________________________________
; Function: write a B*Tree buffer to disk.
; Input: A4.L - buffer header to write
; Output: D0 = error code, set buffer not dirty!
; Modification History:
; 07Aug89 KSCT Get node size shift count from BTCB
; 08Sep89 KSCT Save the RefNum we are servicing because the param is reused.
;________________________________________________________________________________
ExtWriteBlock PROC EXPORT ;
IMPORT ExtDoAOCRWF
MOVE.L (SP)+,-(A6)
MOVEM.L D1/A1,-(A6) ; save regs over _write call
;; We don't do flush now, Do a filewrite here:
MOVEa.L FSVarsPtr,A1
MOVEA.L fsVars.btMgr(A1),A1
MOVEA.L btVParam(A1),A0 ; A0 = BT Param
IF BTDEBUG THEN
;; A0 is reused. stash info on stack. <9/8/89>
MOVE ioRefNum(A0),-(A6) ; save the RefNum we're servicing <9/8/89>
ENDIF
MOVE.W btBHRefnum(A4),D0 ; D0=refnum
MOVE.W D0,ioRefNum(A0) ; file refnum
LEA btBHdata(A4),A1
MOVE.L A1,IOBuffer(A0) ; buffer to write
MOVE.W #fsFromStart,IOPosMode(A0) ; position mode(from BOF)
MOVEQ #0,D0
MOVE.W btBHNSize(A4),D0 ; D0=node size
MOVE.L D0,IOByteCount(A0) ; r/w node size
MOVE.L btBHFilePos(A4),D0 ; file pos
MOVE.L D0,IOPosOffset(A0) ; ... gives position offset
MOVEQ #DoWrite,D1 ; indicate this is WRITE
BSR ExtDoAOCRWF ;
IF BTDEBUG THEN
MOVE (A6),D1 ; old refnum <9/8/89>
CMP ioRefNum(A0),D1 ; same refnum? <9/8/89>
BEQ.S @2 ; yes <9/8/89>
_debugger ; should not happen <9/8/89>
@2 MOVE (A6)+,ioRefNum(A0) ; restore RefNum <9/8/89>
ENDIF
MOVEM.L (A6)+,D1/A1 ; restore registers
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set up condition codes
RTS
ENDPROC ; ExtWriteBlock
;_________________________________________________________________________________
; Function: mark the block dirty
; Now we just write it but don't clear inuse flag <9/11/89>
; Input: A0=ptr(buffer)
; Output: D0 = 0 if no error
;_________________________________________________________________________________
ExtMarkBlock PROC EXPORT ;
; BSET #CBHdirty,CBHFlags-lenCBH(A0) ; mark it dirty
MOVE.L (SP)+,-(A6)
MOVE.L A4,-(A6)
LEA -lenbtBH(A0),A4 ; A4 = ptr to CBH
JSR ExtWriteBlock ;
MOVEa.L (A6)+,A4 ; restore registers
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set up condition codes
RTS
ENDPROC
END