mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2025-01-07 20:29:52 +00:00
4325cdcc78
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.
416 lines
15 KiB
Plaintext
416 lines
15 KiB
Plaintext
;
|
|
; 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: © 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Õ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
|
|
; ¥¥ 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
|