mac-rom/OS/DispatchHelper.a
Elliot Nunn 4325cdcc78 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-12-26 09:52:23 +08:00

314 lines
12 KiB
Plaintext

;
; File: DispatchHelper.a
;
; Written by: Jeff Miller
;
; Contains: Code implementing the generic dispatcher (DispatchHelper) and generic procedure
; dispatcher (ProcHelper). Both are installed as OSTraps, but they should always
; be called from the low memory vector rather than through the trap mechanism.
;
; Copyright: © 1990-1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <8> 8/29/91 JSM Cleanup header.
; <7> 10/22/90 JSM <dba> Use dispatchFlags equates and DispatchUnimplementedRoutine
; macro, add support for more efficient linked patch dispatching
; with absolute addresses, don't support NULL dispatch table
; entries anymore for more efficient dispatching, misc. other
; optimizations.
; <6> 9/25/90 KIP Change it back to a linked patch.
; <5> 8/9/90 gbm LinkPatch REALLY hates main modules...
; <4> 8/8/90 SAM Making DispatchHelper & ProcHelper load as an old style ptch.
; ¥¥¥---> Temporary <---¥¥¥ remove this when the Sound Mgr code is
; converted to a linked patch (as it should be).
; <3> 7/12/90 STB Moved _ProcHelper & _DispatchHelper patch installation code from
; MiscPatches.a to here.
; <2> 7/8/90 JSM ProcHelper takes a pointer to use count instead of an offset
; now.
; <1> 7/7/90 JSM First checked in.
;
LOAD 'StandardEqu.d'
INCLUDE 'DispatchHelperPriv.a'
INCLUDE 'LinkedPatchMacros.a'
;___________________________________________________________________________________
; These linked patches are installed on all ROM families
;
ROMs Plus,SE,II,Portable,IIci
;___________________________________________________________________________________
; DispatchHelper
;
; A generic dispatcher. Assumes all routines it dispatches to are
; Pascal functions returning an OSErr. Installed as OS trap $A0EC, but
; should always be called directly through the vector at $7B0.
;
; Selectors are in D0.W, with the low byte containing the actual selector
; and the high byte containing the number of words of parameters on the
; stack.
;
; Uses a standard DispatchHelperTable specifying the valid selector range and
; offsets to each routine.
;
; If the low byte of D0 is out of range, DispatchHelper strips the parameters
; off the stack and returns paramErr.
;
; If bit 1 (dhAbsoluteEntries) of dispatchFlags in the DispatchHelperTable is set,
; DispatchHelper assumes that the entries in the dispatch table are 4 byte absolute
; addresses (used mainly in linked patches). Otherwise, entries in the dispatch
; table are 2 byte offsets from the beginning of the table to the actual routine.
;
; If bit 0 (dhSelectorsEven) of dispatchFlags in the DispatchHelperTable is set,
; DispatchHelper assumes that the selector in D0 is already even and doesn't multiply
; it by two to get to the offset. Note that all even selectors are currently unsupported
; for absolute address dispatch tables.
;
; General implementation strategy: since absolute addresses are used by linked patches in
; some very common cases (for example: dialogs, windows, and menus), we want that case to
; execute the fastest (i.e. with the fewest branches). Note that trying to overload
; dispatchFlags too much goes against the original intent of DispatchHelper, which is
; speed.
;
; Inputs:
; A0 Pointer to DispatchHelperTable.
; D0.W Low byte is selector, high byte is number of words of parameters.
;
; Outputs:
; D0.W untouched
;
; Destroys:
; D1, A0
;
;___________________________________________________________________________________
DispatchHelper PatchProc _DispatchHelperTrap
WITH DispatchHelperTable
MOVE.W D0,D1 ; get selector in D1 so we don't touch D0
;
; Make sure the selector is in range
;
CMP.B lastSelector(A0),D1 ; is selector too large?
BGT.S DispatchHelperBadSelector ; yes, cleanup and return
SUB.B firstSelector(A0),D1 ; get absolute zero-based selector
BLT.S DispatchHelperBadSelector ; if too small, cleanup and return
;
; Selector is in range, now dispatch
; Since we only support dhAbsoluteEntries or dhSelectorsEven, but never both
; at the same time, we can do a CMP, which is faster than a BTST.
;
EXT.W D1 ; selector OK, make it a word
CMP.W #(1<<dhAbsoluteEntries),dispatchFlags(A0) ; are entries absolute addresses?
BNE.S @offsetEntries ; no, they're word offsets
;
; Entries in DispatchHelperTable are absolute addresses (used in linked patches)
;
ASL.W #2,D1 ; convert to offset from firstAddress
MOVE.L firstAddress(A0,D1.W),A0 ; A0 = absolute address of routine entry point
JMP (A0) ; jump to entry point
;
; Entries in DispatchHelperTable are word offsets from beginning
;
@offsetEntries
CMP.W #(1<<dhSelectorsEven),dispatchFlags(A0) ; are selectors even?
BEQ.S @D1IsOffset ; yes, no need to double D1
ADD.W D1,D1 ; convert to offset from firstOffset
@D1IsOffset
MOVE.W firstOffset(A0,D1.W),D1 ; D1.W = routine offset from DispatchHelperTable
ADD.W D1,A0 ; A0 = actual routine entry point
JMP (A0) ; jump to entry point
ENDWITH ; DispatchHelperTable
;
; Selector is out of range, clean up stack and return paramErr
;
EXPORT DispatchHelperBadSelector ; so Dispatcher linked patch macro can reference it
DispatchHelperBadSelector
DispatchUnimplementedRoutine ; cleans up stack and returns to caller
ENDPROC ; DispatchHelper
;___________________________________________________________________________________
; ProcHelper
;
; A resource-based procedure dispatcher. Assumes all routines it dispatches
; to are Pascal functions returning an OSErr. Originally designed for
; use with PACKs, but will work with all resource-based code of the correct
; format. Installed as OS trap $A09A, but should always be called directly
; through the vector at $668.
;
; Uses DispatchHelper, so selectors are in D0.W, with the low byte containing
; the actual selector and the high byte containing the number of words of
; parameters on the stack.
;
; A0 should be a handle to the procedure resource ('PACK', 'proc', or
; whatever) that begins with a ProcHelperHeader. If the handle is NIL,
; returns memFullErr. If the handle has been purged, and LoadResource fails,
; returns ResError. In either case, the parameters are stripped of the stack
; before returning.
;
; For maximum memory usage benefits, procedure resources should be purgeable.
;
; A1 should be a pointer to a word of globals ProcHelper can use to store a
; use count for this resource. The word should be initialized to 0 before
; ProcHelper is called the first time for a resource. ProcHelper uses this
; count to handle locking and unlocking the resource. ProcHelper updates
; this use count be changing the return address to always come back through
; ProcHelperReturn. See the ERS for more details on the stack manipulation
; used to do this, but here is an example of what happens to the stack
; when there are three words of parameters:
;
; On entry to called routine
;
; +--------+--------+
; | ProcHelperReturn|
; +--------+--------+
; | param3 | param2 |
; On entry to ProcHelper +--------+--------+
; | param1 | OSErr |
; +--------+--------+ +--------+--------+
; | return address | | return address |
; +--------+--------+ +--------+--------+
; | param3 | param2 | | use count ptr |
; +--------+--------+ +--------+--------+
; | param1 | OSErr | | proc handle |
; +--------+--------+ +--------+--------+
;
; Inputs:
; D0.W Low byte is selector, high byte is number of words of parameters.
; A1 Pointer to the use count global for this procedure
; A0 Handle to procedure resource
;
; Outputs:
; When branching to DispatchHelper:
; A0 Pointer to DispatchHelperTable.
; D0.W Low byte is selector, high byte is number of words of parameters.
;
; When returning to application:
; None
;
; Destroys:
; When branching to DispatchHelper:
; D1, D2, A1
;
;___________________________________________________________________________________
ProcHelper PatchProc _ProcHelperTrap
;
; Reload procedure resource, if necessary
;
MOVE.L A0,D1 ; is the procedure handle NIL?
BEQ.S ResHandleNil ; no handle -> resource gone
TST.L (A0) ; is procedure pointer NIL?
BNE.S @resLoaded ; no, we're OK
MOVE.L A0,-(SP) ; push handle for LoadResource
MOVE.W #MapTrue,ROMMapInsert ; use ROM map as well ???
_LoadResource ; Note: preserves ALL registers
TST.L (A0) ; Did load fail?
BEQ.S ResNotLoaded ; NIL handle -> could not load
@resLoaded
;
; Check use count to see if there is already a call in progress
;
TST.W (A1) ; is there a call in process (count <> 0)?
BNE.S @procNowLocked ; yes, not first time through
;
; This is the first call, so lock down the procedure resource
;
MOVE.W D0,-(SP) ; save selector on stack across HLock
_HLock ; lock down resource
MOVE.W (SP)+,D0 ; restore selector
@procNowLocked
ADDQ.W #1,(A1) ; increment use count
MOVE.L A1,D2 ; save ptr to use count
;
; Make space on stack for moving parameters down and saving of
; old return address, use count pointer, and resource handle
;
SUBQ #8,SP ; need two longs for use count ptr and proc handle
PEA ProcHelperReturn ; return to this address after call
MOVE.L 12(SP),-(SP) ; save off real return address
;
; Loop through number of words of parameters, moving them down each time
;
MOVE.W D0,D1 ; D1.W = selector
LSR.W #8,D1 ; D1.W = words of parameters
LEA 8(SP),A1 ; get ptr to place to put parameters
@moveLoop
MOVE.W 12(A1),(A1)+ ; D0.W = current parameter
DBRA D1,@moveLoop ; continue moving parameters
;
; Save off needed values underneath the parameters we just moved
;
MOVE.L (SP)+,(A1)+ ; save real return address
MOVE.L D2,(A1)+ ; save ptr to use count
MOVE.L A0,(A1)+ ; save handle to proc
;
; Set up A0 to point to DispatchTable, then branch to dispatcher
;
MOVE.L (A0),A0 ; A0 = pointer to proc header
ADD.L #ProcHelperHeader.ProcDispTable,A0 ; A0 = pointer to start of dispatch table
_DispatchHelper ; call DispatchHelper through vector
;
; Procedure called through DispatchHelper returns to here
;
ProcHelperReturn
MOVE.W (SP)+,D1 ; D1.W = returned OSErr
MOVE.L (SP)+,A1 ; get real return address
MOVE.L (SP)+,A0 ; get pointer to use count
SUBQ.W #1,(A0) ; decrement use count
MOVE.L (SP)+,A0 ; get handle to procedure resource
BNE.S @stillInUse ; if use count not zero, still calls processing
_HUnlock ; unlock procedure resorce in A0
@stillInUse
MOVE.W D1,-(SP) ; restore returned OSErr
JMP (A1) ; return to proc caller
ResHandleNil
;
; No handle for procedure, return memFullErr
;
MOVE.W #memFullErr,D1 ; put error in D1.W
BRA.S ProcHelperError ; and return
ResNotLoaded
;
; Could not reload the procedure resource, return ResError
;
MOVE.W ResErr,D1 ; get ResError() in D1.W
;BRA.S ProcHelperError ; fall through to ProcHelperError
;
; An error occurred before the dispatch could happen.
; Clean up the stack, and return to caller with error code in D1.W.
;
ProcHelperError
MOVE.L (SP)+,A0 ; get caller's return address
LSR.W #8,D0 ; D0.W = words of parameters
LSL.W #1,D0 ; D0.W = bytes of parameters
ADD.W D0,SP ; strip off parameters
MOVE.W D1,(SP) ; return error in D1.W
JMP (A0) ; return to caller
ENDPROC ; ProcHelper
END