416 lines
15 KiB
Plaintext
Raw Normal View History

;
; File: QMgr.a
;
; Contains: An FSQueue-CmdDone like queue mechanism that is more general
; and handles only asynchronous-type queueing.
;
; Written by: Dave Feldman
;
; Copyright: <09> 1989-1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <10> 9/13/91 JSM Cleanup header.
; <9> 3/17/91 dnf dba, dty, #84977: introduce the CallWithRegistersPreserved
; routine which keeps stack depth down; use
; CallWithRegistersPreserved in dispatching; save D0 across calls
; to completion routines
; <8> 3/11/91 dnf dba, #dnf10: change QMgr so that it always does things
; asynchronously; this works because there is always someone else to
; SyncWait for the synchronous calls (kill QMQueueSync)
; <7> 12/5/90 dnf Change <6> was with dba
; <6> 12/5/90 dnf Don't attempt to fall through an endproc
; <5> 11/12/90 dnf include FileMgrPrivateEqu.a
; <4> 9/22/90 dnf Adapt for multiple queues
; <3> 7/2/90 dnf Change to include HardwarePrivateEqu.a
; <2> 2/4/90 DNF Remove includes
; <1.2> 12/8/89 dnf Fix up includes
; <1.1> 10/17/89 dnf Set async bit at dispatch
; <1.0> 9/18/89 dnf Adding to ease for the first time
;
load 'StandardEqu.d'
include 'HardwarePrivateEqu.a'
include 'QMgrEqu.a'
include 'FileMgrPrivate.a'
; routines we'd like to see:
;
; QMNewQueue
; in a0 - ptr to alternate stack
; d0 - length of stack
; out d2 - queue refnum (or zero if no memory)
;
; QMRemoveQueue
; in d2 - queue refnum
; out : queue removed
;________________________________________________________________________________
;
; Routine: GetOffFSQueue
;
; Function: This routine extricates the parameter block currently on the head of
; the File System queue (which is the Desktop Manager parameter block
; currently being worked on) from the File System queue and onto the
; Desktop Manager queue. The only tricky bit is keeping the File System
; going full steam ahead at all times. For this we rely on the QMgr Enqueue
; routine to restart the file system for us later.
;
; Inputs: FSQHead -> Current Desktop Manager parameter block
; Outputs: A0 -> parameter block that was on the front of the file system queue
;
; This routine clobbers a0-a1/d0-d2 when it vectors into the File System's CmdDone code.
;
;________________________________________________________________________________
GetOffFSQueue: proc export
move sr,-(sp) ; save interrupt state
ori.w #HiIntMask,sr ; only debug interrupts allowed
clr.w FSBusy ; clear file system busy boolean
; (It's OK - Interrupts are disabled!)
; delete the current request from the queue
move.l FSQHead,a0 ; a0 -> current request
move.l QLink(a0),FSQHead ; get next request, if any
bne.s @MoreLater ; if another FS request, don't clear the tail ptr
clr.l FSQTail ; if no more requests, clear tail ptr
@MoreLater:
move (sp)+, sr ; restore interrupt state
rts
endproc
;_______________________________________________________________________
;
; Routine: GetQMRecPtr
;
; Arguments: in: d2 = queue type/refnum
; out:
; a1 = QMRec pointer (or -1 if bad type/refnum)
; Z = 0 implies dispatch trap (store d0.b in ioTrap+1)
; Z = 1 implies straight trap (leave ioTrap alone)
;
; Called by: all the QM routines that take queue refnums
;
; Function: goes and finds the QMRec for a given refnum.
; refnums are currently allocated staticly in this code.
;_______________________________________________________________________
GetQMRecPtr: proc export
move.l FSVarsPtr, a1 ; a1 = ptr(FSVars)
move.l FSVars.QMgr(a1), a1 ; a1 = ptr(QMgr globals)
cmp.b #desktopQType, d2 ; desktop manager queue?
bne.s @CheckCLQueue
move.l QMGlobals.DTQueuePtr(a1), a1 ; a1 = ptr(DTDB mgr QM rec)
bra.s @Exit
@CheckCLQueue:
cmp.b #fsCompatQType, d2 ; compatibility layer queue?
bne.s @BadRefNum
move.l QMGlobals.CLQueuePtr(a1), a1 ; a1 = ptr(comp. layer QM rec)
@Exit: tst.w d2 ; clear Z
rts
@BadRefnum:
move.l #-1, a1 ; no queue found
bra.s @Exit
endp
;_______________________________________________________________________
;
; Routine: QMEnqueue
; Arguments: d0 = (possibly) dispatch number
; d1 = trap word
; d2 = queue refnum
; a0 = parameter block pointer
;
; Calls: QMQueue enqueues the calling routine on the selected queue,
; and dispatches it if that queue is not busy.
; Like the file system queue mechanism, Pascal registers are
; saved on the a7 stack.
;
; Called by: Any client who wants queueing and dispatching services.
;
; Function: This entry point determines whether a call is synchronous
; or asynchronous; it queues up the call, then, if async and
; there is a command already executing, returns immediately.
; Otherwise, it enters the sync wait routine, then goes to
; command dispatch.
;
; The dispatched command is BSR'ed to; it should put
; the return address on the a6 stack and rts off that
; stack to complete the call.
;
; No registers are preserved across calls to the two queue hooks,
; allowing hook patches to grab the return address off the stack
; and not return to this code.
;
; Note: What the hell is @KickStartFS?
; If you're pulling a pb off of the front of the file system queue
; you normally dispatch the next pb, if there is one, so that all
; of the calls get processed. If you didn't, you'd hang the file
; system.
;
; However, several clients of the QMgr (such as the desktop
; manager and the file system compatibility layer) need to move
; calls from the file system queue to a QMgr queue. If they had
; to the "traditional" thing (i.e. restart the file system)
; they'd have no place to remember the address of the pb when
; they can back to place it on a qmgr queue.
;
; Since the QMgr always restarts the file system, clients can remove param
; blocks from the front of the file system queue and branch here
; secure in the knowledge that we'll restarts the file system for them.
; Restarting the file system when it is already busy has no effect.
;
; Modification History:
;
; 7-28-89 dnf Taken from FSQueue, which taken from the original MFS queue
;_______________________________________________________________________
QMEnqueue: proc export
import QMDispatchRequest
move.w d2, ioType(a0) ; save qType/refnum
bsr GetQMRecPtr ; pointer to QMRec in a1
beq.s @NotDispatch ; Z=1 implies straight trap
and.w #$0f00, d1 ; preserve modifier bits
move.b d0, d1 ; move dispatch byte into trap word
@NotDispatch:
move.w d1, ioTrap(a0) ; save the trap number
move.w #1, ioResult(a0) ; set ioResult to "in process"
with QMRec
move.l enQHook(a1), d2 ; enqueue hook?
beq.s @NoEnqueueHook ; cruise if not
move.l d2, a1 ; get the hook where we can call it
jsr (a1) ; call the enqueue hook (can't trash any regs)
@NoEnqueueHook:
move.w ioType(a0), d2 ; get qType/refnum
bsr GetQMRecPtr ; pointer to QMRec in a1
move.l (sp)+, ioCmdAddr(a0) ; save enqueuing routine's address
bset.l #AsyncTrpBit, d1 ; set async bit in trap word <8>
bne.s @DoAsyncCall ; if already an asynchronous call, go on <8>
clr.l ioCompletion(a0) ; no completion routines for sync calls
; here we fall through because we handle all calls asynchronously <8>
@DoAsyncCall:
move sr, -(sp) ; save interrupt state
ori #HiIntMask, sr ; only allow macsbug interrupts
_Enqueue ; put the request at the end of the queue
@ToDispatch:
bset.b #qIsBusy, qMFlags(a1) ; mark this queue busy
beq.s @Dispatch ; if it wasn't already busy, go do this request
; The queue is busy so we're done for now. This request will get executed after
; the completion of the one in front of it.
moveq.l #noErr,d0 ; no errors (yet)
move (sp)+, sr ; re-enable interrupts and return
bra.s @KickStartFS ; restarting the file system on the way out
@Dispatch:
move (sp)+,sr ; re-enable interrupts
movem.l pascalRegs, -(sp) ; save the Pascal registers
jsr QMDispatchRequest ; go do the rest of the dispatching <9>
movem.l (sp)+, pascalRegs ; restore the Pascal registers
@KickStartFS: ; (see massive note above)
movea.l ExpandMem, a1 ; aim at expanded lomem blob
move.l ExpandMemRec.jDispatchNext(a1), -(sp) ; push file system restart address
rts ; hop back down async thread, restarting fs on the way
endproc
;______________________________________________________________________
; QMDispatchRequest - the specific call dispatcher
;
; We assume that the appropriate registers have been saved and
; dispatch the call.
;
; Input:
; a1 - QMRec pointer
;______________________________________________________________________
QMDispatchRequest: proc
import QMCmdDone
with QMRec
bset.b #asyncCall, qmFlags(a1) ; ask for all I/O to be done asynchronously
move.l qHead(a1), a0 ; get pointer to first parameter block
move.w ioTrap(a0), d1 ; d1 = trap word for this call
move.l stackBase(a1), a6 ; set up a6 for alternate stack use
move.w ioType(a0), -(a6) ; save for later safety check
move.l ioCmdAddr(a0), a1 ; get address of enqueued routine
pea.l QMCmdDone ; push ptr(completion code)
move.l (sp)+, -(a6) ; move completion address onto a6
jmp (a1) ; go do the call <9>
endproc
;______________________________________________________________________
; QMCmdDone
;
; Complete the current request, post completion routines and
; possibly dispatch another request.
;
; QMCmdDone modifies a0/a1, d0-d2
;______________________________________________________________________
QMCmdDone: proc
import CallWithRegistersPreserved
move.w (a6)+, d2 ; get queue refnum
bsr GetQMRecPtr ; get QMRecPtr in a1
with QMRec
ext.l d0 ; Make a long of it <14Jun85>
ble.s @notDSErr ; If zero or negative, we're OK
move.w d0, dsErr(a1) ; save internal error for debug
moveq #FSDSIntErr, d0 ; replace with generic "deep shit" error
@notDSErr:
move.l qHead(a1), a0 ; a0 -> current param block (that just finished)
cmp.w ioType(a0), d2 ; it better be the right type
bne.s toDSFSErr ; or else die
move.l deQHook(a1), d2 ; just-before-dequeue hook?
ble.s @doTheDequeue ; branch if not . . .
move.l d2, a1 ; a1 = ptr(just-before-dequeue hook)
jsr (a1) ; go do the whatever the hook wants
@doTheDequeue:
move.w ioType(a0), d2 ; get qType/refnum
bsr GetQMRecPtr ; pointer to QMRec in a1
move sr, -(sp) ; save interrupt state
ori #HiIntMask, sr ; only debug interrupts allowed
bclr.b #qIsBusy, qMFlags(a1) ; mark this queue not busy
; delete the current request from the queue and post any completion routines
move.l qHead(a1), a0 ; a0 -> current request
move.l qLink(a0), qHead(a1) ; get next request, if any
bne.s @moreRequests ; just go call completion if there are more requests
clr.l qTail(a1) ; if no more, clear the tail, too
@moreRequests:
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 @dispatchNext ; if not, just do dispatch the next call
move.l a1, -(sp) ; save QMRecPtr around completion routine
move.l d1, a1 ; a1 = ptr(completion routine)
jsr (a1) ; go call the completion routine
move.l (sp)+, a1 ; get QMRecPtr back
; go dispatch the next call if there is one
@dispatchNext:
move sr, -(sp) ; save interrupt state
ori #HiIntMask, sr ; allow only MacsBug interrupts
tst.l qHead(a1) ; is there another call on the queue?
beq.s @Done ; if not, cruise <9>
bset.b #qIsBusy, qMFlags(a1) ; mark this queue busy <9>
bne.s @Done ; if it was already busy, we can return asynchronously <9>
move.w (sp)+,sr ; restore interrupt state <9>
lea.l QMDispatchRequest,a0 ; our dispatcher<65>s entry point <9>
jmp CallWithRegistersPreserved ; call it <9>
@Done:
move (sp)+, sr ; restore interrupt state
rts ; and leave
toDSFSErr MOVEQ #DSFSErr,D0
_SysError
endp
;_______________________________________________________________________
;
; Routine: QMInitQueue
; Arguments: d2 - queue refnum
;
; Function: Clear the queue of all pending commands except for the one
; running (if there is one . . .).
;
; Modification History:
;
; 05 Aug 84 New today. Taken directly from LAK's FS code
; 01 Aug 89 New again today. (Rewrite every 5 years, whether it needs it or not...)
;_______________________________________________________________________
QMInitQueue: proc export
; <20><> get QManRec in a4
move sr, -(sp) ; save interrupt state
ori #HiIntMask, sr ; allow only debug interrupts
move qHead(a4), d0 ; any commands?
beq.s @weAreDone ; then we're done
move.l d0, a0 ; a0 = ptr(currently running command)
clr.l ioLink(a0) ; blow away any other entries in the queue
move.l a0, qTail(a4) ; except this one
@weAreDone:
move.w #noErr, d0 ; this routine has no errors
move.w (sp)+, sr ; restore interrupt state
rts
endp
;________________________________________________________________________________
; <9>
; CallWithRegistersPreserved
;
; This routine provides an easy way for the kind of recursive code found in the
; File Manager, BTree Manager and Desktop Manager to eliminate tail recursion.
;
; CallWithRegistersPreserved will call any routine while making sure that the
; volatile interrupt registers (d4-d7/a4-a6) are preserved. We have an optimization
; to prevent stack buildup when registers are actually already preserved. The trick
; is to notice the instruction pointed to by (sp) and see if it is a
; movem.l (sp)+ instruction with enough registers to make us happy.
;
; The rule:
; You must call this routine at a point where you could otherwise execute an RTS.
;
; Inputs:
; a0.l address of routine to execute with preserved registers
;
; Outputs:
; d0 trash
; a0 trash
; all other registers passed through to the call to a0
;________________________________________________________________________________
CallWithRegistersPreserved: proc export
move.l a0,-(sp) ; put the target onto the stack
movea.l 4(sp),a0 ; point to the code we'd return to
cmpi.w #MOVEMSPPostIncOpCode,(a0)+ ; is it going to restore some registers?
bne.s Protected ; if not, be safe
move.w (a0),d0 ; get the Movem register mask
not.w d0 ; get 0 bits for registers that ARE saved
andi.w #VolatileInterruptRegisterMask, d0 ; see if all registers we care about are saved
beq.s Direct ; if so, get right to it
Protected:
movea.l (sp)+,a0 ; get our target back
movem.l InterruptRegs,-(sp)
jsr (a0) ; registers are protected, so call in
movem.l (sp)+,InterruptRegs
Direct:
rts
endproc
;________________________________________________________________________________
end