boot3/ProcessMgr/ProcessMgrMisc.a
Elliot Nunn 5b0f0cc134 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 10:02:57 +08:00

1763 lines
72 KiB
Plaintext

;
; File: ProcessMgrMisc.a
;
; Contains: Routines which support the Process Manager that could not be
; coded in C.
;
; Written by: Phil Goldman
;
; Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <19> 11/25/92 DRF Add a conditional patch inside a_hfsdispatch to save and restore
; A2 across calls the the real _HFSDispatch. This is a temporary
; fix until the PowerPC trap glue follows the proper register
; saving conventions. Code is conditionalized under ÒPsychicTVÓ
; and will be removed at the earliest possibility.
; <18> 9/25/92 DRF Get rid of MyGestalt, since inline glue exists
; <17> 3/30/92 DTY #1025416,<DDG>: Add gestaltSkiaGlobalsSwitched to the list of
; attributes for gestaltOSAttr.
; <16> 11/25/91 DTY Call GetParallelFCBFromRefNum in the _OpenRF patch to nail the
; PSN in the parallel FCB for printer drivers, if it they have the
; ÒMultiFinder compatibleÓ bit set. This prevents the driver from
; being closed out from under other applications which happen to
; be using it at the same time.
; <15> 11/4/91 DTY Of course, keeping your own copy of an interface macro tends to
; screw things up when the selectors changeÉ
; <14> 11/1/91 DTY DonÕt assume SysMapHndl really has a handle to the system
; resource map, as it will have a handle to an override map when
; ROMMapInsert is true. Call _IsThisASystemResourceMap to
; determine whether it is or not instead.
; <13> 10/23/91 DTY Take GetTopSystemOverrideMap out of this file and hang it off of
; _ResourceDispatch.
; <12> 10/20/91 DTY Add GetTopSystemOverrideMap. This returns a handle to the first
; system override map in the resource chain to NewProcess so that
; new resource chains will have system override maps too.
; <11> 4/12/91 DFH TES, #86777 : Change _Close patch to just call through when trap
; was made async. Our Òshared printer fileÓ check can not be made
; safely at interrupt time. Fortunately, it is not necessary for
; it to be done on async Close's, since the printer files are
; closed by the Print Manager synchronously. First found in
; HyperCard testing.
; <10> 3/5/91 DFH dnf, #84115: Added support for new forced _Unmount (ignore
; result of notification proc).
; <9> 2/21/91 DFH JWM,WS#DFH-910221a: Fix gross register eating in c_zeroscrap and
; c_putscrap across call to DisposeOrphanedScrap.
; <7> 12/18/90 DFH Added GetScrap patch to return memFullErr when most recent
; MoveScrap() failed for lack of memory.
; <6> 12/17/90 DFH ZeroScrap and PutScrap now call DisposeOrphanedScrap.
; <5> 12/17/90 DFH Fixed RecoverHandle patch to preserve d0, as per Inside Mac,
; Volume II, page 35.
; <4> 12/3/90 DFH Changed CloseWD patch to return noErr in the error case. This
; is because error is not useful, and TOPS has an assert that it
; is zero.
; <3> 11/9/90 DFH Wakes up front process before calling dsForcedQuit SysError, so
; that front process promptly makes the event call that will clean
; up the confirmation dialog.
; <2> 11/9/90 DFH Added re-entrancy prevention in "forced quit" handling. Note
; that this means user can not "force quit" out of a hung SysError
; of any kind.
; <0> x/xx/86 PYG New Today.
;
;--------------------------------------------------------------------
CASE OBJECT
LOAD 'ProcessMgrIncludes.D'
INCLUDE 'Data.a'
include 'FileMgrPrivate.a'
include 'GestaltPrivateEqu.a'
STRING PASCAL
;-------------------------------------------------------------------------------
;
; This mega-box comment was extracted 18 April 1989 from the ROM dispatch.a source,
; mainly as a compatibility guideline.
;
; Of course, we strategically violate the register eating manners for particular
; traps that don't REALLY need the regs.
;
;
; EMT1010 -- MacIntosh Operating System Dispatcher
;
; The following code receives all Line 1010 emulator traps and transfers
; control to the appropriate system code to interpret the trap. Two 32-bit/entry
; RAM-based dispatch tables are used to derive the addresses to which to dispatch.
; Since this table is patchable, a system or application program can patch in
; and intercept any system call to fix bugs, etc.
;
; An A-line trap has the form: 1010 tabc nnnn nnnn
;
; t=1 ==> Toolbox; t=0 ==> OS. They differ mainly in register saving conventions.
;
; ToolBox:
; These calls follow Pascal parameter-passing conventions, receiving and
; returning values on the stack, and saving all but D0-D2/A0-A1.
; All registers are preserved up to the time the target routine is reached;
; the stack is as though a JSR had been executed.
;
; a=1 ==> an extra return address was on the stack when the trap was executed
;
; (e.g. a dumb compiler required a JSR to the A-line trap). This superfluous
; address will be removed by the dispatcher.
; bcnnnnnnnn = trap number
;
; OS:
; All parameters are passed in registers. Routine must follow Pascal
; conventions about D3-D7/A2-A7. D0/A0-A1 are passed through to the
; routine unchanged. D1.W is the actual A-line instruction.
;
; c=1 ==> D1-D2/A1 are preserved by the dispatcher (so values can
; be returned in D0/A0).
; c=0 ==> D1-D2/A0-A1 are preserved by the dispatcher (so D0 can be returned).
; a,b = don't care, but are used by the traps to indicate, say, SYS/APPL,
; SYNC/ASYNC, DIACRITICALS/NOT, . . .
; nnnnnnnn = trap number
;
; Dispatch addresses are maintained in two tables of long words, ToolTable with
; 512 entries, and OSTable with 256 entries. (They used to be rolled into one
; table of 512 word entries.)
; For backward compatibility, the GetTrapAddress and SetTrapAddress routines use
; the original trap numbering scheme, that is traps $00-$4F, $54, and $57 are OS
; traps, and the rest are Tool traps.
;
; The expanded routines GetTrapAddress and SetTrapAddress routines use bits #10
; to specify Tool=1/OS=0 and #9 to specify New=1/Old=0 numbering. Bit #10 is
; ignored when bit #9 is 0.
;
; A few things to remember. Although toolbox routines use pascal register
; saving conventions, the trap dispatcher must preserve all registers when
; dispatching a toolbox trap, since there are some routines which are
; documented as saving all registers (_Debugger, _LoadSeg, _FP68K, É).
;
; Even though the OS trap dispatcher saves A0-A2/D1-D2 for the user, it
; may not modify A0 or A1 before calling the OS routine, since routines
; like _BlockMove expect paramaters in A0/A1/D0. Additionally, register
; D1 must contain a copy of the A-Trap word, since that is how routines
; read the additional trap bits (eg. _NewPtr ,SYS,CLEAR).
;
; Although the register save order, and format of the stack frame for an
; OS trap is undocumented, Microsoft does a SetTrapAddress on _GetHandleSize
; and and their new routine calls the ROM GetHandleSize, and then restores
; the registers that the trap dispatcher saved, and does a TST.L on D0.
; Because of this, register save order must be maintained for compatibility,
; since we may never get back to restore them.
;
; For machines with CPUs earlier than the 68020, we include 3 versions of the
; trap dispatcher in ROM, 68000, 68010, and 68020/030. At startup time we
; determine which CPU we are running on, and install the correct dispatcher.
; We also modify the dispatch addresses for GetTrapAddress, SetTrapAddress,
; and BlockMove to use cache flushing versions when the CPU is a 68020 or later.
; On systems with 68020/030 cpus, we only provode the 020/030 versions.
;_______________________________________________________________________
; For resource maps, entries
mapForceSysHeap EQU 0
RAttr EQU 4
RHndl EQU 8
mNext equ 16
MRefNum EQU 20
MAttr EQU 22
;---------------------------------------------------------------------------------------
; generic_glue. Routine to make up for the fact that the C compiler is too
; brain-damaged to handle pointers to pascal functions. This is a pascal-type
; routine with the last parameter being the address of the routine to call.
generic_glue PROC EXPORT
move.l 4(sp),a0 ; get address of real routine
move.l (sp)+,(sp) ; move return address on top of routine address
jmp (a0) ; go do the routine
ENDPROC
;---------------------------------------------------------------------------------------
; settrapaddress_glue. This is glue to call the original SetTrapAddress.
; The C prototype is:
;
; pascal void
; settrapaddress_glue(Ptr trapAddr, short trapNum, short setTrapAddressNum, Ptr oldtrap)
;
; Eats a0, d0-d2 before calling trap
SETTRAPADDRESS_GLUE PROC EXPORT
move.l (sp)+,d2 ; save return address
move.l (sp)+,a1 ; get old settrapaddress addr
move.w (sp)+,d1 ; get settrapaddress (full) trap number
move.w (sp)+,d0 ; d0 <- trap number
move.l (sp)+,a0 ; a0 <- trap address
move.l d2,-(sp) ; restore return address
jmp (a1) ; go do routine
ENDPROC
;---------------------------------------------------------------------------------------
; OverlayRecoverHandle. The following is to get Works to live up to its name. Works
; assumes that if the ,SYS bit is not set then the handle must be in the App Heap.
; The only problem with this is if the handle belongs in SysZone. Therefore the patch
; forces ,SYS in this case.
OverlayRecoverHandle PROC EXPORT
IMPORT GetOverlayRecoverHandleOldTrap:CODE
SYSBITON EQU $0400
movem.l d0-d1/a0,-(sp) ; save working registers
move.l a0,d0 ; get ptr in parameter reg
_StripAddress ; strip!
move.l d0,a0 ; for unsigned compare
move.l SysZone,a1 ; ptr to system zone descriptor
cmpa.l bkLim(a1),a0 ; Ptr < SysZone->bkLim?
bcc.s ORH_done ; if not, branch
ORH_force_comma_sys
or.l #SYSBITON,4(sp) ; force ,SYS bit on for Works to see
ORH_done
jsr GetOverlayRecoverHandleOldTrap ; Get the actual value from the PCB
move.l d0,a1 ; and get it into an address register
movem.l (sp)+,d0-d1/a0 ; restore working registers
jmp (a1) ; and do it
ENDPROC
;---------------------------------------------------------------------------------------
; pascal long ProcessMgr_GZProc_Glue(long cbNeeded)
; The ROM memory manager keeps the "working zone" address in A6 wherever it
; travels. If our growzone changes the zone address (a unique experience!),
; we must be sure to keep the memory manager up to date for the remainder of
; his operation. The A6 is (handily) on our caller's stack frame. Yuck.
; NOTE: This is a massive hack! Fix this if at all possible.
; NOTE: This GZProc can be called from places other than the ROM CallGZProc (like our
; own routine, HiNewHandle), so checking for SavedA6 == oldProcessMgrZone is required.
;
; Here are the regs saved by CallGZProc in Sources:OS:MemoryMgr:HeapGuts.a
GZPROCREGS_SIZE EQU (14*4) ; d0-d1/d3-d7/a0-a6
GZPROCREGS_A6OFFSET EQU (GZPROCREGS_SIZE-4) ; GZPROCREGS_SIZE up to a6
; Here is how much we have on the stack when we need to access the saved a6
OURTEMPSTACKUSAGE EQU 16 ; a couple regs, etc.
; Here is how far we have to go from our stack pointer to the saved a6
SavedA6 EQU OURTEMPSTACKUSAGE + GZPROCREGS_A6OFFSET
PROCESSMGR_GZPROC_GLUE PROC EXPORT
IMPORT PROCESSMGRGZPROC:CODE
IMPORT (oldProcessMgrZone, newProcessMgrZone):DATA
move.l a5,-(sp) ; save old a5
movea.l ProcessMgrGlobals,a5 ; set up our a5
; clear the indicator of actual zone movement
clr.l oldProcessMgrZone ; zero is impossible address
; now call real grow zone procedure
clr.l -(sp) ; clear out ret value
move.l 12(sp),-(sp) ; push arg again
jsr PROCESSMGRGZPROC ; do it
move.l (sp)+,12(sp) ; save ret val
; did the zone header move?
tst.l oldProcessMgrZone ; has oldProcessMgrZone been set?
beq.s StackIsOK ; if not, branch
; are we being called from the Memory Manager? If so, fix up the a6 saved on the stack.
move.l SavedA6(sp),d0 ; get a6 value on stack (welcome to system software)
cmp.l oldProcessMgrZone,d0 ; is the same as mem mgr zone ptr?
bne.s StackIsOK ; if not, there is no fix to do
move.l newProcessMgrZone,SavedA6(sp) ; give Memory Mgr a new a6
StackIsOK
move.l (sp)+,a5 ; restore a5
move.l (sp)+,a0 ; get ret addr
addq.w #4,sp ; skip argument
jmp (a0) ; and return
ENDPROC
;---------------------------------------------------------------------------------------
; a_settrapaddress. Our patch to SetTrapAddress. Just calls our C function.
a_settrapaddress PROC EXPORT
IMPORT c_settrapaddress:CODE
; WARNING: This is dependent on new ROM trap dispatch routine (D1 has
; no meaning on old ROMs)
move.l d1,-(sp) ; pass the trap call
move.l d0,-(sp) ; pass the trap number
move.l a0,-(sp) ; pass the routine address
jsr c_settrapaddress
lea 12(sp),sp ; get past my C parameters
; Help out MS File by clearing d0 here. They are about to call _BlockMove without
; setting the top word of d0.
moveq.l #noErr, d0 ; clear this for MS File
rts
ENDPROC
;---------------------------------------------------------------------------------------
IF (&TYPE('MM_DEBUG') <> 'UNDEFINED') THEN
; CheckParam. For memory manager debugging. Checks that passed handle is valid.
CheckParam PROC
movem.l d0-d7/a0-a6,-(sp) ; save work registers
move.l a0,-(sp) ; push handle
jsr CheckBadHandle ; check if bad
addq.w #4,sp ; get rid of parameter
rts ; return
ENDPROC
ENDIF
;---------------------------------------------------------------------------------------
; a_newhandle. Our patch to NewHandle. Specially prepares for allocations
; from either the Process Mgr zone or the System zone.
MIN_SYS_HEAP_FREE_SPACE EQU $2000
a_newhandle PROC EXPORT
IMPORT ProcessMgrHMakeMoreMasters:CODE
IMPORT ProcessMgrZone:DATA, patchtraps:DATA
SaveRegs REG d0-d1/d3
; Check for needing more master pointers in the Process Mgr heap
movem.l SaveRegs,-(sp) ; save work regs
movea.l ProcessMgrGlobals,a0 ; get address of our globals
exg a0,a5 ; a0 <- orig a5, a5 <- our a5
move.l patchtraps+RomAllPatchTable.Rom75Patches.NEWHANDLE.oldtrap,d3 ; d3 <- old _NewHandle routine
move.l ProcessMgrZone,a5 ; get Process Mgr zone
exg a0,a5 ; a5 <- orig a5, a0 <- ProcessMgrZone
move.l TheZone,a1 ; a1 <- TheZone
cmp.l SysZone,a1 ; is this the sys zone?
beq.s DoSysCode ; if so, branch
btst #10,d1 ; is it sys heap override?
bne.s DoSysCode ; if so, do sys heap handling
cmp.l a1,a0 ; is this the ProcessMgrZone?
bne.s DoOldCode ; if not, branch
; Using Process Mgr zone. Make sure our moreMasters routine is used.
move.l hFstFree(a0),d0 ; get next MP
bne.s DoOldCode ; if not nil, branch
move.l a5, -(sp) ; save a5
movea.l ProcessMgrGlobals,a5 ; get our a5 for intersegment call
jsr ProcessMgrHMakeMoreMasters ; try for more masters
move.l (sp)+, a5 ; restore a5
tst.l d0 ; did we get them?
bpl.s DoOldCode ; if so, branch
NHErr
addq.w #4,sp ; skip over old d0
move.l (sp)+,d1 ; restore d1
move.l (sp)+,d3 ; restore d3
rts
; getting memory from the system heap. See if we should grow the zone right now.
DoSysCode
move.l SysZone,a0 ; get the sys zone header
move.l #MIN_SYS_HEAP_FREE_SPACE,d1 ; get min free in d1
cmp.l d0,d1 ; is it smaller than min
bcc.s GetMoreSpace ; if so, branch
move.l d0,d1 ; if not, substitute it
GetMoreSpace
sub.l zcbFree(a0),d1 ; are there at least this many bytes free?
bls.s MaybeEnoughRoom ; if so, branch
; requested size is greater than total free space, call the GZ proc, if any
move.l gzProc(a0),d0 ; get grow zone proc address
beq.s DoOldCode ; can't call it if it doesn't exist!
movem.l a0-a1/d3,-(sp) ; save still more work regs
subq.w #4,sp ; save room for function result
move.l d1,-(sp) ; push the desired handle size as bytes needed
movea.l d0,a0 ; put GZ proc address in address register
jsr (a0) ; try to grow
addq.w #4,sp ; trash function result
movem.l (sp)+,a0-a1/d3 ; restore still more work regs
MaybeEnoughRoom
DoOldCode
move.l d3,a0 ; a0 <- old _NewHandle routine
movem.l (sp)+,SaveRegs ; restore work regs
jmp (a0) ; tail recursion (if not from NHErr)
ENDPROC
;---------------------------------------------------------------------------------------
; Patch to HandleZone. If handle is purged temp memory, return ProcessMgrZone directly.
a_handlezone PROC EXPORT
IMPORT ProcessMgrZone:DATA,patchtraps:DATA
IMPORT InCurrTempMem:CODE
; a0 == handle
move.l a5,a1 ; save current a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l a0,d0 ; nil handle?
beq.s CallOld ; if so, let memory manager give error
move.l (a0),d0 ; dereference it, check for purged
bne.s CallOld ; if not purged, let memory manager find zone
; handle is purged, must check for it in current temp memory
movem.l d1/a0-a1,-(sp) ; save working registers
move.l a0,-(sp) ; pass supposed handle (as HListElem)
jsr InCurrTempMem ; try to locate it
addq.w #4,sp ; dump stack param
tst.w d0 ; was handle really temp memory?
movem.l (sp)+,d1/a0-a1 ; restore working registers
bne.s CallOld ; jump if block was not registered
; purged handle was ours, return ProcessMgrZone (d0 already equals noErr)
move.w d0,MemErr ; agree in lomem
move.l ProcessMgrZone,a0 ; return our zone address
move.l a1,a5 ; restore old a5
rts
; retrieve old trap address from table, and call it
CallOld
IF (&TYPE('MM_DEBUG') <> 'UNDEFINED') THEN
jsr CheckParam ; ensure handle and MP lists OK
ENDIF
move.l patchtraps+RomAllPatchTable.Rom75Patches.HANDLEZONE.oldtrap,-(sp)
move.l a1,a5 ; restore old a5
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_recoverhandle, Our patch to RecoverHandle. Make sure we find temp memory handles,
; too. Requires proper setting of TheZone, because real handle is determined by adding
; it to the relative handle in the block header. We try to find the pointer in temp
; handle allocations for the current process and system.
; IN: a0 == Ptr
; OUT: a0 = handle
; NOTE: We must preserve caller's D0, since Inside Mac, volume II, page 35 says so.
a_recoverhandle PROC EXPORT
IMPORT patchtraps:DATA
IMPORT RecoverCurrTempHdl:CODE
SaveRegs REG d0-d1/a0-a1
move.l a5,a1 ; a1 <- orig a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.RECOVERHANDLE.oldtrap,-(sp) ; -(sp) <- old _NewHandle routine
movem.l SaveRegs,-(sp) ; save working registers
move.l a0,-(sp) ; pass supposed master pointer
jsr RecoverCurrTempHdl ; try to locate it
addq.w #4,sp ; dump stack param
move.l d0,d2 ; did we find it in temp memory?
movem.l (sp)+,SaveRegs ; restore registers (CCs unchanged)
beq.s Return ; return to old trap if block not known
; pointer was temp block, so no need to call old trap
movea.l d2,a0 ; put handle in output register
clr.w MemErr ; set AOK lomem error code
addq.w #4,sp ; dump old trap address
Return
move.l a1,a5 ; a5 <- orig a5
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_reallochandle. Our patch to ReallocHandle. Make sure memory comes from correct
; place. If that's the system heap, call the growzone procedure if the free space
; is getting low. If the handle is Process Mgr temporary memory, use the ProcessMgrZone.
; NOTE: Our save/restore of THEZONE assumes that the current zone is not ProcessMgrZone
; if the handle is from temp memory, since call-through could change ProcessMgrZone.
a_reallochandle PROC EXPORT
IMPORT patchtraps:DATA, ProcessMgrZone:DATA
IMPORT InCurrTempMem:CODE
; IN: a0 == handle, d0 == requested size, d1 == trap word
; OUT: d0 == result code (long)
move.l a5,a1 ; a1 <- orig a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.REALLOCHANDLE.oldtrap,-(sp) ; -(sp) <- old _NewHandle routine
move.l a1,a5 ; a5 <- orig a5
move.l SysZone,a1 ; a1 <- TheZone
cmp.l TheZone,a1 ; is this the sys zone?
beq.s DoSysCode ; if so, branch
btst #10,d1 ; is it sys heap override?
bne.s DoSysCode ; if so, branch
; Handle being reallocated from current heap. Adjust THEZONE if the handle
; was registered as Process Mgr temp memory for the current process.
; NOTE: We could skip check if TheZone already equals ProcessMgrZone, but this
; is unlikely since Process Mgr doesn't use purgeable blocks.
movem.l d0-d1/a0/a5,-(sp) ; save working registers
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l a0,-(sp) ; pass supposed handle (as HListElem)
jsr InCurrTempMem ; try to locate it
addq.w #4,sp ; dump stack param
move.l ProcessMgrZone,a1 ; in case we need it
tst.w d0 ; was handle really temp memory?
movem.l (sp)+,d0-d1/a0/a5 ; restore working registers
bne.s DoOldCode ; jump if block was not registered
; block was MF temp memory, switch zones around call
move.l TheZone,-(sp) ; save current zone
move.l a1,TheZone ; switch to ProcessMgrZone
move.l 4(sp),a1 ; old trap address
jsr (a1) ; call it
move.l (sp)+,TheZone ; restore the zone
addq.w #4,sp ; dump trap address
bra.s ByeNow ; leave
; getting memory from system heap
DoSysCode
IF (&TYPE('MM_DEBUG') <> 'UNDEFINED') THEN
jsr CheckParam ; ensure handle and MP lists OK
ENDIF
move.l #MIN_SYS_HEAP_FREE_SPACE,d2 ; get min free in d2
cmp.l d0,d2 ; is requested smaller than min
bcc.s GetMoreSpace ; if so, branch
move.l d0,d2 ; if not, substitute it
GetMoreSpace
sub.l zcbFree(a1),d2 ; are there at least this many bytes free?
bls.s ProbablyEnoughRoom ; if so, branch
movem.l d0-d1/a0,-(sp) ; save still more work regs
subq.w #4,sp ; save room for function result
move.l d2,-(sp) ; push the desired handle size as bytes needed
move.l gzProc(a1),a1 ; get grow zone proc address
jsr (a1) ; try to grow
addq.w #4,sp ; trash function result
movem.l (sp)+,d0-d1/a0 ; restore still more work regs
ProbablyEnoughRoom
DoOldCode
IF (&TYPE('MM_DEBUG') <> 'UNDEFINED') THEN
jsr CheckParam ; ensure handle and MP lists OK
ENDIF
ByeNow
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_disposhandle. Our patch to DisposHandle. If handle is temp memory, remove it
; from the registry and switch to ProcessMgrZone around actual disposal. Just make sure
; the ProcessMgrZone doesn't end up with a master pointer from another heap in its free
; list. This would happen if someone disposed a purged handle, and we accidently
; switched zones. Tried to streamline the no-find case, because this trap is in even
; when no temp memory is allocated.
; NOTE: Getting an error while freeing a block we thought was temp mem is bad news.
; It probably means the block was already freed, but by a process other than the
; one that allocated it. This should be relatively harmless, though. What I am more
; concerned about is that this sort of thing is happening, and that it may lead to
; freeing a master pointer has since been reallocated.
; FURTHER NOTE: We'll be lucky beyond belief if the Memory Manager is ever smart
; enough to return error from DisposHandle.
PROC
IMPORT ProcessMgrZone:DATA,patchtraps:DATA
IMPORT RemoveCurrTempMem:CODE
EXPORT a_disposhandle:CODE
a_disposhandle
move.l a5,d0 ; save current a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.DISPOSHANDLE.oldtrap,-(sp)
movem.l d0-d1/a0,-(sp) ; save registers that might perish
move.l a0,-(sp) ; pass supposed handle (as HListElem)
jsr RemoveCurrTempMem ; try to deregister it
addq.w #4,sp ; dump stack params
tst.w d0 ; was handle really temp memory?
movem.l (sp)+,d0-d1/a0 ; restore registers (no touch CC)
bne.s NoSwitch ; jump if block wasn't registered
; awkward code only executed when freeing temporary memory
; (sp) contains old trap address
move.l TheZone,-(sp) ; push orig heap ptr
move.l ProcessMgrZone,TheZone ; force into Process Mgr zone for good form
move.l d0,a5 ; restore a5
move.l 4(sp),a1 ; get old trap address
jsr (a1) ; call old trap
IF (&TYPE('DEBUG') <> 'UNDEFINED') THEN
cmp.w #noErr,d0 ; check result
beq.s WentOK ; jump if no error
_Debugger ; block didn't dispose (registry bad??)
move.w MemErr,d0 ; restore condition codes
WentOK
ENDIF
move.l (sp)+,TheZone ; restore orig heap
addq.w #4,sp ; dump old trap address
rts ; return to programmer
NoSwitch
IF (&TYPE('MM_DEBUG') <> 'UNDEFINED') THEN
jsr CheckParam ; ensure handle and MP lists OK
ENDIF
move.l d0,a5 ; restore a5
rts ; make old call w/o switching zones
ENDPROC
;---------------------------------------------------------------------------------------
; a_handtohand. On numac ROM, put the synthesized fonts in the system heap. We
; detect them because the numac Font Mgr does a _HandToHand(FMOutHandle) into the app
; heap to get the new font, and then does a _SetHandleSize to grow it to its final
; immensity.
; NOTE: The zone save/restore below can only work if TheZone != ProcessMgrZone originally, since we
; may cause the sys heap to grow.
; NOTE: Can't save/restore TheZone across call for all calls because _HandToHand is patched to
; fix some CQD bug and of course it looks up the stack.
;
a_handtohand PROC EXPORT
IMPORT patchtraps:DATA
move.l a5,d0 ; d0 <- orig a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom78Patches.HANDTOHAND.oldtrap,a1
move.l d0,a5 ; a5 <- orig a5
cmp.l FMgrOutRec+fmOutFontH,a0 ; is it the orig strike?
bne.s DoOldH2H ; if not, branch
HandleSynFont
move.l TheZone,-(sp) ; save orig heap ptr on stack
move.l SysZone,TheZone ; else force into sys zone
jsr (a1) ; do it
move.l (sp)+,TheZone ; restore orig heap
rts
DoOldH2H
jmp (a1) ; do it
ENDPROC
;---------------------------------------------------------------------------------------
; a_open. Our patch to the file system Open. Several reasons for this:
; (1) If .Print, make sure the _Open goes thru IOCore
; (2) Check if the file name is the same as the clipboard name. If so, then
; force vrefnum to be BootDrive. This is done for MacPaint 1.5. When
; 1.5 goes away, so should this patch.
; (3) Call old routine
;
; NOTE: Clean up register usage
a_open PROC EXPORT
IMPORT patchtraps:DATA
IMPORT pCurrentProcess:DATA
OpenPatchRegs REG d1/a0/a5 ; trap word, param block, and a5
SavedA0 EQU 4 ; NOTE: This depends on d1/a0/a5 on the stack!
PrintSlotNo EQU 2
movem.l OpenPatchRegs,-(sp) ; save registers
movea.l ProcessMgrGlobals,a5 ; get address of our globals
; check for print driver open
lea printname,a1 ; point at print name
move.l ioFileName(a0),a0 ; point at openee...
clr.l d0
move.b (a0)+,d0 ; get length
swap d0 ; into high word
move.b (a1)+,d0 ; length into low word
_CmpString ; are they the same?
move.l SavedA0(sp),a0 ; restore a0 (doesn't change cond codes)
bne.s NotDotPrint ; not .Print
; ah! the print driver IS being opened!
move.l UTableBase,a1 ; address of unit table
move.l PrintSlotNo*4(a1),d0 ; get the dce handle for .Print from fixed location
beq.s CallOldOpen ; if none, no need to force permission
move.l d0,a1 ; get it back into an addr register
move.l (a1),d0 ; hdl -> ptr
beq.s CallOldOpen ; if purged, no need to force permission
move.l d0,a1 ; get it back into an addr register
btst #drvrActive,dCtlFlags+1(a1) ; is it active
bne.s CallOldOpen ; if so, branch (don't force -- IOCore can't handle it)
move.b #$40,ioPermssn(a0) ; set permissions to 40 to open again...
bra.s CallOldOpen ; now call through
; it's not the print driver, is it MacPaint opening the scrapbook file?
NotDotPrint
move.l pCurrentProcess,a1 ; get current app ptr
move.l 8(a1),d0 ; get current app's signature
; NOTE: Assumes this offset correct in PEntry!
cmp.l #'MPNT',d0 ; is it MacPaint?
bne.s CallOldOpen ; if not, restore regs and do normal trap
move.l ioFileName(a0),a0 ; get file name
lea ScrapTag,a1 ; get scrap file name
clr.l d0
move.b (a0),d0 ; get length
swap d0 ; into high word
move.b (a1),d0 ; length into low word
_CmpString ; are they the same?
bne.s CallOldOpen ; if not, don't change the refnum
move.l SavedA0(sp),a0 ; restore a0 pointing to iopb
IF (&TYPE('DEBUG') <> 'UNDEFINED') THEN
; Check whether MacPaint 2.0 still needs the hack. We officially no longer support 1.5
; Added this check on 9 Oct 1990.
move.w BootDrive, d0 ; value of BootDrive
cmp.w ioDrvNum(a0),d0 ; already set to BootDrive??
beq.s CallOldOpen ; if so, skip debugger message
pea MacPaintMsg ; message to show
_DebugStr ; use power of suggestion on testers
ENDIF
move.w BootDrive,ioDrvNum(a0) ; force boot drive to be vrefnum
; All done fooling around, so get the darn thing open
CallOldOpen
move.l patchtraps+RomAllPatchTable.Rom75Patches.OPEN.oldtrap,a1
movem.l (sp)+,OpenPatchRegs ; restore registers
jmp (a1) ; jump down the patch chain
IF (&TYPE('DEBUG') <> 'UNDEFINED') THEN
MacPaintMsg
dc.b 'This MacPaint still needs our fix (GO will be OK)'
ENDIF
printname
dc.b '.Print'
ENDPROC
;---------------------------------------------------------------------------------------
; GetMapHdlFromRefNum. A utility routine (similar to GetMap() in ROM) that
; searches the resource map chain whose topHandle is passed on the stack for the
; refNum on stack. If it finds it, it returns with non-zero in D0. It preserves
; all registers except D0. Its a C function in that it doesn't strip its parameters.
;
; Handle GetMapHdlFromRefNum(short refNum, Handle topMapHdl)
;
GetMapHdlFromRefNum PROC EXPORT
GetMapRegs REG a0/d1
moveq.l #0,d0 ; set default return value
movem.l GetMapRegs,-(sp) ; save registers
move.w 14(sp),d1 ; get ref num
bmi.s DoneFindRef ; negative refnum means no maps open
bne.s GotRefNum ; zero refnum means...
move.w SysMap,d1 ; ...change it to sys ref num
GotRefNum
move.l 16(sp),d0 ; get starting point
beq.s DoneFindRef ; make sure we can start
FindRefLoop
move.l d0,a0 ; put current suspect in a-reg
move.l (a0),a0 ; dereference handle to pointer
cmp.w 20(a0),d1 ; does map refnum match target?
beq.s DoneFindRef ; if so, we're done
move.l 16(a0),d0 ; get link handle
bne.s FindRefLoop ; if not nil, try again
DoneFindRef
movem.l (sp)+,GetMapRegs ; restore registers
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_openrf. OpenRF patch. Override an opWrErr (file already open) if the file is
; open in another process and the mapForceSysHeap bit is set in the resource map.
a_openrf PROC EXPORT
OpenRFPatchRegs REG a0-a1/a5/d1-d2
IMPORT patchtraps:DATA
IMPORT GetMapHdlFromRefNum:CODE
IMPORT GetMapHdlInOtherChainFromRefNum:CODE
; call the original routine, leave immediately if it worked or error wasn't opWrErr
movea.l ProcessMgrGlobals,a1 ; get address of our globals
exg.l a5,a1 ; a5 <- our a5, a1 <- old a5
move.l patchtraps+RomAllPatchTable.Rom75Patches.OPENRF.oldtrap,a5
exg.l a5,a1 ; a5 <- old a5, old trap addr
jsr (a1) ; do old guy
beq.s rf_done ; if no error, done
cmp.w #opWrErr,d0 ; is it being opened again?
bne.s rf_done ; if some other error, done
; can't override if the file is open by the current fellow
movem.l OpenRFPatchRegs,-(sp) ; save working registers
move.l TopMapHndl,-(sp) ; pass top most handle
moveq #0,d0 ; clear out top word
move.w ioRefNum(a0),d0 ; get ref num
move.l d0,-(sp) ; and push on stack as C parameter
jsr GetMapHdlFromRefNum
addq #8,sp ; pop off topHandle, ref num
tst.l d0 ; find it?
bne.s ORFLeaveErr ; if so, return error
; locate map in another process' chain, give error if not found
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.w ioRefNum(a0),d0 ; get ref num (we know d0 is 0 coming in here)
move.l d0,-(sp) ; push ref num
jsr GetMapHdlInOtherChainFromRefNum
addq #4,sp ; ditch argument
tst.l d0 ; is it in other chain?
beq.s ORFLeaveErr ; if not, branch
; finally, check whether others' map allows sharing (d0 = maphandle)
move.l d0,a0 ; get hdl into address register
moveq #0,d0 ; clear out return code
move.l (a0),a0 ; hdl -> ptr
btst.b #mapForceSysHeap,MAttr(a0) ; is sharing bit set?
beq.s ORFLeaveErr ; if not, leave with original error
; now, fix up HFS's table so ExitToShell by app that originally opened the file will not
; close the FCB out from under the other users of the file. That is, untag the FCB in
; the parallel array.
movea.l 8(sp),a0 ; <16> get iopb
move.l ioFDirIndex(a0),-(sp) ; <16> GetParallelFCB uses ioMisc
_GetParallelFCBFromRefNum ; <16> Get the parallel FCB
bnz.s ORFRestore ; <16> Bad refnum, bail out
move.l ioFDirIndex(a0),a1 ; <16> Get pointer to it
clr.l (a1)+ ; <16> clear high long of PSN
clr.l (a1)+ ; <16> clear low long of PSN
move.l (sp)+,ioFDirIndex(a0) ; <16> Restore ioMisc
bra.s ORFRestore ; <16> if so, branch (zero out error)
ORFLeaveErr
moveq #opWrErr,d0 ; return original error
ORFRestore
movem.l (sp)+,OpenRFPatchRegs ; restore working registers
rf_done
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_close. Our patch to file system Close. Keeps shared files from being closed out
; from under the other apps resource chain.
; Implemented by scanning the resource map chains in all active partitions for the
; refNum. If we find it and the file is a "Process Mgr compatible print driver", we
; don't close the file.
a_close PROC EXPORT
ClosePatchRegs REG d1-d3/a0-a2
IMPORT patchtraps:DATA
IMPORT GetMapHdlInOtherChainFromRefNum:CODE
movem.l ClosePatchRegs,-(sp) ; save working registers
move.l a5,a2 ; a2 <- old a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.CLOSE.oldtrap,d3
; do even try if call is async. Our structures are not safe, and it is not even necessary
; to check, since the print drivers are always closed synchronously.
andi.w #async,d1 ; is this an asynchronous call?
bne.s ReallyClose ; if so, just let it go
; check for "Process Mgr compatible print driver"
move.w ioRefNum(a0),d0 ; get ref num
bmi.s ReallyClose ; if driver, branch
ext.l d0 ; clear out high word
move.l d0,-(sp) ; push ref num
jsr GetMapHdlInOtherChainFromRefNum ; file open elsewhere, too?
addq #4,sp ; ditch argument
tst.l d0 ; well, is it?
beq.s ReallyClose ; if not, branch
move.l d0,a0 ; get in address register
move.l (a0),a0 ; hdl -> ptr
btst.b #mapForceSysHeap,MAttr(a0) ; is sharing bit set?
bne.s DontReallyClose ; if so, branch
ReallyClose
move.l d3,d0 ; d0 <- old trap addr
movea.l a2,a5 ; a5 <- old a5
movem.l (sp)+,ClosePatchRegs ; restore working registers
move.l d0,-(sp) ; push old trap address
rts
DontReallyClose
move.l a2,a5 ; a5 <- old a5
movem.l (sp)+,ClosePatchRegs ; restore working registers
moveq #noErr,d0 ; everything a-ok
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_hfsdispatch. Patch CloseWD to not close the WD if it is the one the application was
; launched into. Patch GetVol, GetVolInfo, and HFSDispatch to compensate for bug in
; the Mac SE HFS that checks ioNamePtr for validity against MemTop. This part of the
; patch should be in the system, and for SEs only.
PROC
IMPORT (patchtraps, initMemTop):DATA
IMPORT CHECKIFBACKINGWD:CODE
EXPORT a_hfsdispatch, a_getvol, a_getvolinfo:CODE
HFSPatchRegs REG d0-d2/a0/a5
a_getvol
move #RomAllPatchTable.Rom75Patches.GETVOL.oldtrap,d2
bra.s DoOldCall ; call through
a_getvolinfo
move #RomAllPatchTable.Rom75Patches.GETVOLINFO.oldtrap,d2
bra.s DoOldCall ; call through
a_hfsdispatch
move #RomAllPatchTable.Rom75Patches.HFSDISPATCH.oldtrap,d2
cmpi.w #2,d0 ; is call _CloseWD?
beq.s HandleCloseWD ; if so, branch
; call through to the original trap address
DoOldCall
move.l a5,a1 ; a1 <- old a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l MemTop, -(sp) ; save current memtop
move.l initMemTop, MemTop ; this is high enough
lea patchtraps, a5 ; get patch array address
move.l (a5,d2.w),a5 ; get old routine
exg.l a1,a5 ; restore a5, a1 <- old trap
if (PsychicTV) then
move.l a2,-(sp) ; <19>
endif
jsr (a1) ; call old trap
if (PsychicTV) then
move.l (sp)+,a2 ; <19>
endif
move.l (sp)+,MemTop ; restore memtop
rts ; and return to it
; it IS _CloseWD, so make our check
; NOTE: For awhile this code return fBsyErr in the error case. Unfortunately, the
; released version of the TOPS application actually has an assert that the ioResult
; of CloseWD is noErr. We roll over on this one, since seeing an error is of little
; value here anyway (what can the caller do to recover?).
HandleCloseWD
movem.l HFSPatchRegs,-(sp) ; save registers
movea.l ProcessMgrGlobals,a5 ; get address of our globals
subq.w #2,sp ; make room for ret val
move.w ioVRefNum(a0),-(sp) ; pass the wd refnum
jsr CHECKIFBACKINGWD ; can't close this one
tst.b (sp)+ ; check if uncloseable
movem.l (sp)+,HFSPatchRegs ; restore regs
beq.s DoOldCall ; if not, branch
moveq.l #noErr,d0 ; say it worked
move.w d0,ioResult(a0) ; and here too
rts
ENDPROC
;---------------------------------------------------------------------------------------
; Volume notification. These patches take care of notifying our list of registered
; volume aficionados. We patch MountVol, Eject, Offline, and UnmountVol. The patches
; use identical stacks so they can share the exit and error paths.
;---------------------------------------------------------------------------------------
PROC
EXPORT a_mountvol, a_eject, a_offline, a_unmountvol:CODE
IMPORT NotifyVolumeGoodbye, NotifyVolumeAction:CODE
IMPORT patchtraps:DATA
; Get the real refnum here by calling _GetVolInfo. We have to use a new param block
; because although _UnmountVol theoretically requires the same param block size as
; _GetVolInfo, it is possible to get away with illegally sending a shorter block to
; _Unmount, as it does not read or modify the fields at higher offsets. It is not known
; whether any apps do this, but if we can hypothesize about such a violation then some
; app has done it. If GetVolInfo returns an error we return -1.
;
; On entry: a0 points to HFS volume parameter block
; On exit: d0 = error code, d1 = vrefnum or -1, CC = tst.w d0
; Eats: a0, a4, d0, d1
GetRealRefNum
lea -ioVQElSize(sp),sp ; save room for new iopb
move.w #-1,ioVolIndex(sp) ; meaning: use name and vrefnum
moveq.l #0,d1 ; assume no file name
move.l ioFileName(a0),ioFileName(sp) ; use same name
beq.s @0 ; if none, branch
move.l ioFileName(a0),a4 ; get (good) name ptr
move.b (a4),d1 ; save length
@0
move.w ioVRefnum(a0),ioVRefnum(sp) ; use same vrefnum
move.l sp,a0 ; point a0 to new block
_GetVolInfo ; get info
bmi.s Error ; if err, branch
tst.b d1 ; was there a (non-zero) file name?
beq.s @1 ; if not, branch
move.b d1,(a4) ; otherwise, restore correct length
@1
moveq #0,d1 ; clear d1
move.w ioVRefNum(sp),d1 ; get "real" vrefnum in d1
doneExit
tst.w d0 ; restore condition codes
lea ioVQElSize(sp),sp ; restore room from new iopb
rts
Error
moveq #-1,d1 ; give impossible refnum
bra.s doneExit ; and return
; a_mountvol. A patch to _MountVol so that we notify the interested parties.
; NOTE: Special-cases the volOnLinErr error, since this is what happens for AppleShare
; volumes (MountVol is called via diskinserted event AFTER VCB as been created).
a_mountvol
move.l a5,a1 ; save a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.MOUNTVOL.oldtrap,a5 ; get old _MountVol routine
exg.l a1,a5 ; a1 = trap address, a5 = olda5
move.l a0,-(sp) ; save param block
jsr (a1) ; mount that volume!
move.l (sp)+,a0 ; retrieve param block
move.w d0,d0 ; test OSErr result
beq.s mounted ; jump if mounted
cmp #volOnLinErr,d0 ; is it an acceptable error?
bne.s leave ; jump if drive failed to mount
; mount worked, try to inform everyone
mounted
moveq #VNMounted,d1 ; get notification code
moveq #0,d2 ; clear the air
move.w ioVRefNum(a0),d2 ; get refnum
bra.s haveRefNum ; continue
; d0 == result of trap
haveNotice
move.l (sp)+,d2 ; get vrefnum again
bmi.s leave ; jump if vrefnum not known
haveRefNum
move.l d0,-(sp) ; save result
move.l a5,-(sp) ; save a5 again
movea.l ProcessMgrGlobals,a5 ; get address of our globals
pea $0 ; push nil for stopper
move.l d0,-(sp) ; push result
move.l d1,-(sp) ; push notice
move.l d2,-(sp) ; pass refnum as a param to the C routine
jsr NotifyVolumeAction ; notify everyone of the result
lea 16(sp),sp ; get rid of C params
move.l (sp)+,a5 ; restore a5 again
move.l (sp)+,d0 ; get result
leave
rts
SaveNotifyRegs REG d1-d3/a0/a4/a5
; a_offline. Our patch to _Offline sends out notification before and after the trap.
a_offline
movem.l SaveNotifyRegs,-(sp) ; save regs we'll use
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.OFFLINE.oldtrap,d3 ; get old _Offline routine
bra.s shareOfflineEject
; a_eject. Our patch to _Eject sends out notification before and after the trap (note
; that we can send no notification if the volume has already been unmounted, but then
; we shouldn't have to!).
a_eject
movem.l SaveNotifyRegs,-(sp) ; save regs we'll use
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.EJECT.oldtrap,d3 ; get old _Offline routine
shareOfflineEject
jsr GetRealRefNum ; get the real vrefnum in d1
bmi.s doOldCall1 ; jump if error
; send out notification of intent
move.l d1,-(sp) ; save refnum for later
clr.l -(sp) ; pass false for forced parameter
moveq #VNOffline,d0 ; secondary notice if aborted
move.l d0,-(sp) ; push it
moveq #VNAboutToGoOffline,d0 ; primary notice to send
move.l d0,-(sp) ; push it
move.l d1,-(sp) ; pass refnum as a param to the C routine
jsr NotifyVolumeGoodbye ; let everyone know
lea 16(sp),sp ; get rid of C params
move.l (sp)+,d1 ; get refnum back
move.w d0,d0 ; test OSErr result
bne.s allDone ; jump if someone complained
; got the high sign
doOldCall1
move.l d1,d0 ; copy refnum to safe reg
move.l d3,a1 ; get trap address
movem.l (sp)+,SaveNotifyRegs ; restore regs we used
move.l d0,-(sp) ; save refnum
jsr (a1) ; do old routine
; send out the result notification
moveq #VNOffline,d1 ; notification code
bra.s haveNotice ; share code
; unusual exit
allDone
movem.l (sp)+,SaveNotifyRegs ; restore trap word, iopb ptr, scratch addr reg, old a5
bra.s leave ; use common exit
; _UnmountVol. Our patch to _UnmountVol sends out notification before and after the
; actual unmount.
a_unmountvol
movem.l SaveNotifyRegs,-(sp) ; save regs we'll use
movea.l ProcessMgrGlobals,a5 ; get address of our globals
btst #HFSBit,d1 ; is this _Unmount forced?
sne d3 ; save answer for later parameter
jsr GetRealRefNum ; get the real vrefnum in d1
bmi.s doOldCall2 ; jump if error
; send out notification of intent
move.l d1,-(sp) ; save refnum for later
move.l d3,-(sp) ; say whether goodbye is forced
moveq #VNUnmount,d0 ; secondary notice if aborted
move.l d0,-(sp) ; push it
moveq #VNAboutToUnmount,d0 ; primary notice to send
move.l d0,-(sp) ; push it
move.l d1,-(sp) ; pass refnum as a param to the C routine
jsr NotifyVolumeGoodbye ; let everyone know
lea 16(sp),sp ; get rid of C params
move.l (sp)+,d1 ; get refnum back
move.w d0,d0 ; test OSErr result
bne.s allDone ; jump if someone complained
; got the high sign to unmount
doOldCall2
move.l d1,d0 ; copy refnum to safe reg
move.l patchtraps+RomAllPatchTable.Rom75Patches.UNMOUNTVOL.oldtrap,a1 ; get old _UnmountVol routine
movem.l (sp)+,SaveNotifyRegs ; restore regs we used
move.l d0,-(sp) ; save refnum
jsr (a1) ; do old routine
; send out the result notifications
moveq #VNUnmount,d1 ; notification code
bra haveNotice ; share code
ENDPROC
;---------------------------------------------------------------------------------------
; a_getscrap. Prevent call-through and return memFullErr if the current SCRAPINFO is
; not valid for the current process. Use our handy C routine since it can look better
; at the structures.
a_getscrap PROC EXPORT
IMPORT IsScrapOwnedByCurrentProcess:CODE
IMPORT patchtraps:DATA
GETSCRAPPARAMSIZE EQU (3*4)
move.l a5,-(sp) ; save callers a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
jsr IsScrapOwnedByCurrentProcess ; ask C routine what to do
move.l patchtraps+RomAllPatchTable.Rom75Patches.GETSCRAP.oldtrap,a0
movea.l (sp)+,a5 ; restore caller's a5
tst.w d0 ; check Boolean result
bne.s CallThrough ; jump if scrap OK to get
DontCallThrough
movea.l (sp)+,a0 ; get return address
lea GETSCRAPPARAMSIZE(sp),sp ; dump params
move.l #memFullErr,(sp) ; set OSErr function result
CallThrough
jmp (a0) ; return to caller
ENDPROC
;---------------------------------------------------------------------------------------
; a_zeroscrap and a_putscrap. Increment global counter so that next foreground switch
; will cause scrap to migrate to new front app. We call DisposeOrphanedScrap since the
; orphaned scrap will no longer be needed (the caller is building a new scrap).
PROC
EXPORT a_zeroscrap, a_putscrap:CODE
IMPORT (patchtraps, cutCopyCount):DATA
IMPORT DisposeOrphanedScrap:CODE
a_zeroscrap
move.w #RomAllPatchTable.Rom75Patches.ZEROSCRAP.oldtrap,d0
bra.s shareInc ; share code
a_putscrap
move.w #RomAllPatchTable.Rom75Patches.PUTSCRAP.oldtrap,d0
shareInc
move.l a5,a1 ; save a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
lea patchtraps,a0 ; get patch array address
move.l (a0,d0.w),-(sp) ; push old routine for later RTS
move.l a1,-(sp) ; save old a5 across call
addq.w #1,cutCopyCount ; inc word-sized counter (overflow OK)
jsr DisposeOrphanedScrap ; orphaned scrap is no longer needed
movea.l (sp)+,a5 ; restore a5
rts ; return to original trap
ENDPROC
;---------------------------------------------------------------------------------------
; a_pack3. Patch StandardFile to sneak in a puppet string file specification.
a_pack3 PROC EXPORT
IMPORT IsForcedOpen:CODE
IMPORT patchtraps:DATA
; Offsets from selector param to given param in _SFGetFile (_SFPGetFile must add 6)
OFFSET_SFREPLY EQU 0
OFFSET_DLGHOOK EQU 4
OFFSET_TYPELIST EQU 8
OFFSET_NUMTYPES EQU 12
OFFSET_FILEFILTER EQU 14
SF_PARAMS_LEN EQU 28 ; size of parameters for SFGetFile
SFP_PARAMS_LEN EQU 34 ; size of parameters for SFPGetFile
move.w 4(sp),d0 ; get routine selector
cmp.w #sfGetFile,d0 ; is it _SFGetFile?
beq.s CheckSFGetFile ; if so, branch
cmp.w #sfPGetFile,d0 ; is it _SFGetFile?
bne.s NormalPack3 ; if not, jump to old
lea 6+4(sp),a0 ; point past ret addr, selector, and extra param
move.w (a0)+,d0 ; get the dialog res ID
ext.l d0 ; and make it a long for C call
move.w #SFP_PARAMS_LEN,-(sp) ; put length of params on stack
bra.s CheckBothGetFiles ; and branch to merge point
CheckSFGetFile
lea 6(sp),a0 ; point past ret addr and selector
move.w #SF_PARAMS_LEN,-(sp) ; put length of params on stack
move.l #getDlgID,d0 ; just use standard dialog res ID
CheckBothGetFiles
move.l d0,-(sp) ; push the dialog ID
move.l OFFSET_SFREPLY(a0),-(sp) ; push pSFReply
move.l OFFSET_TYPELIST(a0),-(sp) ; push sfTypeList
move.l OFFSET_DLGHOOK(a0),-(sp) ; push dlgHook
move.w OFFSET_NUMTYPES(a0),d0 ; get numTypes
ext.l d0 ; extend numTypes
move.l d0,-(sp) ; push extended numTypes
move.l OFFSET_FILEFILTER(a0),-(sp) ; push fileFilterFnc
jsr IsForcedOpen ; check if we should force the result
; NOTE: register d2 is still valid here because the above is a C routine
lea 24(sp),sp ; ditch the params
move.w (sp)+,d2 ; get the size passed to this guy back into d2
tst.b d0 ; is it forced open?
beq.s NormalPack3 ; if not, jump to old
move.l (sp)+,a0 ; grab the ret addr
add.w d2,sp ; ditch the args (d2 holds size of them)
jmp (a0) ; and return
; False alarm. Call the old trap.
NormalPack3
move.l a5,a0 ; save old a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.PACK3.oldtrap,a1 ; get old trap routine
exg.l a0,a5 ; a5 <- old a5
jmp (a1) ; and go do it
ENDPROC
;---------------------------------------------------------------------------------------
; a_drawmenubar. Patch to make sure that the menu is only drawn by the foreground
; application, or the application coming to the foreground.
CS_GENERATE_DEACTIVATE1 EQU 3
CS_GENERATE_DEACTIVATE2 EQU 4
a_drawmenubar PROC EXPORT
IMPORT (patchtraps, pCurrentProcess, pFrontProcess, coercionState):DATA
move.l a5,a0 ; save a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l pCurrentProcess, d0 ; pointer to PEntry of current process
cmp.l pFrontProcess, d0 ; is current same as front?
bne.s DontCallOld ; if not, we can't do it!
move.w coercionState, d0 ; get global coercion state
cmp.w #CS_GENERATE_DEACTIVATE1, d0 ; are we deactivating?
beq.s DontCallOld
cmp.w #CS_GENERATE_DEACTIVATE2, d0 ; are we deactivating?
beq.s DontCallOld
move.l patchtraps+RomAllPatchTable.Rom75Patches.DRAWMENUBAR.oldtrap,-(sp) ; get old trap routine
DontCallOld
movea.l a0,a5 ; restore a5
rts ; return
ENDPROC
;---------------------------------------------------------------------------------------
; a_postevent. Our patch to PostEvent to catch users trying to break from the current
; application. The two possibilities are high-level debugger and the ExitToShell.
ESC_KEYCODE EQU $35
BACKTICK_KEYCODE EQU $32
a_postevent PROC EXPORT
IMPORT (patchtraps, pCurrentProcess, pDebugProcess, debugControlKeyCode, debugKeyTryCount, MachineHasMacPlusKbd):DATA
move.l a5,d2 ; save old a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
move.l patchtraps+RomAllPatchTable.Rom75Patches.POSTEVENT.oldtrap,a1 ; get old trap routine
; look for keydown event with specific modifier combo
cmp.w #keyDwnEvt,a0 ; is it really a keydown?
bne.s CallOldTrap ; if not, branch
btst.b #7,KeyMap+6 ; check for cmd key
beq.s CallOldTrap ; if not down, branch
btst.b #2,KeyMap+7 ; check for option key
beq.s CallOldTrap ; if not down, branch
; we're closeÉ is it the "break" key?
move.l d0,-(sp) ; save old d0 on stack
lsr.w #8,d0 ; put key code in lowest byte
tst.w MachineHasMacPlusKbd ; older keyboard?
bne.s NotADB ; jump if so (it can't have escape key)
cmp.b #ESC_KEYCODE,d0 ; is this the right key?
bne.s NotBreakKey ; if not, branch
KillCurrentProcess
tst.l pCurrentProcess ; is there an app running?
beq.s FixStackAndGoOlds ; if not, branch
; SysError handler is not re-entrant, so we must prevent it
move.b MacJmpFlag,d1 ; get possible debugger flag word
cmp.b #UNIMP,d1 ; is this an implemented flag byte?
bne.s HaveFlags ; if so, use it
move.b MacJmp,d1 ; else, use traditional flag
HaveFlags
btst #7,d1 ; already in?
bne.s FixStackAndGoOlds ; if so, ignore this key
; make sure foreground process is alert enough to remove the confirmation dialog
subq.l #8,sp ; allocate storage
clr.w -(sp) ; allocate OSErr result
pea 2(sp) ; push address of PSN
_GetFrontProcess ; find out who's singing
move.w (sp),d0 ; anyone?
bne.s DoneWaking ; jump if error
pea 2(sp) ; push address of PSN
_WakeupProcess ; roust the front process
DoneWaking
add.l #10,sp ; clean up the parms
; confirm our emergency cleanup with the user
move.l d2,(sp) ; save old a5 on stack (where d0 was)
move.l #dsForcedQuit,d0 ; call SysError to confirm the kill
_SysError
; shared exit when SysError was called
AfterSysError
moveq #evtNotEnb,d0 ; tell caller no event was posted!
move.l (sp)+,a5 ; get old a5 back
rts ; and return w/out ever calling old routine
; shared exit when false alarm, just call the old trap
FixStackAndGoOlds
move.l (sp)+,d0 ; restore d0
CallOldTrap
move.l d2,a5 ; a5 <- old a5
jmp (a1) ; go do it
; using older keyboard without escape key, so check for alternate key
NotADB
cmp.b #BACKTICK_KEYCODE,d0 ; is it the right key?
beq.s KillCurrentProcess ; jump if so
; is it the debugger key?
NotBreakKey
tst.l pDebugProcess ; does debugger exist?
beq.s FixStackAndGoOlds ; if not, debugControlKeyCode is invalid
cmp.b debugControlKeyCode,d0 ; is this the right key?
bne.s FixStackAndGoOlds ; if not, branch
add.w #1,debugKeyTryCount ; bump key counter
ble.s FixStackAndGoOlds ; if hit 1 then we can't get in from _WNE
moveq #-1,d0
move.w d0,debugKeyTryCount ; reset it to -1
move.l d2,(sp) ; save old a5 on stack (where d0 was)
moveq.l #enterDebugger,d0 ; call SysError to enter debugger
_SysError
bra.s AfterSysError ; share code
ENDPROC
;---------------------------------------------------------------------------------------
; a_launch. Our patch to Launch. Just calls the C routine.
; NOTE: The C routine ios
a_launch PROC EXPORT
IMPORT C_LAUNCH:CODE
clr.l -(sp) ; allocate return storage
move.l a0,-(sp) ; pass c_launch the pointer
jsr C_LAUNCH ; call the C routine
move.l (sp)+,d0 ; put result in output register
rts
ENDPROC
SEG 'kernel_segment'
;---------------------------------------------------------------------------------------
; a_exittoshell. Glue to call C routine, but using our own stack.
a_exittoshell PROC EXPORT
IMPORT BeginKernelStack, C_EXITTOSHELL:CODE
move.l a5,d1 ; save current a5
movea.l ProcessMgrGlobals,a5 ; get address of our globals
jsr BeginKernelStack ; switch to our stack
movea.l d1,a5 ; restore a5 for C routine to use
jsr C_EXITTOSHELL ; make call that don't come back!
move.l #dsLoadErr,d0 ; oh no!
_SysError ; we returned from ExitToShell!!
rts
ENDPROC
SEG 'Main'
;---------------------------------------------------------------------------------------
; a_osreserved. Implement the do-nothing trap. Here for future use/removal.
a_osreserved PROC EXPORT
rts
ENDPROC
;---------------------------------------------------------------------------------------
; a_wakeup. Our patch to Wakeup. Just calls the C routine.
a_wakeup PROC EXPORT
IMPORT c_Wakeup:CODE
move.l d0,-(sp) ; pass c_Wakeup the pid
jsr c_Wakeup ; call the C routine
addq.w #4,sp ; get past my C parameter
rts
ENDPROC
;---------------------------------------------------------------------------------------
; MyPrefixRelString. Glue to call RelString and make it appear that the second
; string is the same length as the first string.
; The C prototype is:
;
; short MyPrefixRelString(StringPtr pStr1, StringPtr pStr2);
;
MyPrefixRelString PROC EXPORT
move.l 4(sp),a0 ; get first string in a0
move.l 8(sp),a1 ; get second string in a1
moveq.l #0,d0 ; clear out d0
move.b (a0),d0 ; d0.w <- length of first string
swap d0
move.b (a0)+,d0 ; d0.w <- length of first string
addq.w #1,a1 ; pass by length byte of str2
_RelString ; call it
rts ; return value already in d0
ENDPROC
;---------------------------------------------------------------------------------------
; MyRelString. Glue to call RelString since args are in registers.
; The C prototype is:
;
; short MyRelString(StringPtr pStr1, StringPtr pStr2);
;
MyRelString PROC EXPORT
move.l 4(sp),a0 ; get first string in a0
move.l 8(sp),a1 ; get second string in a1
moveq.l #0,d0 ; clear out d0
move.b (a0)+,d0 ; d0.w <- length of first string
swap d0
move.b (a1)+,d0 ; d0.w <- length of second string
_RelString ; call it
rts ; return value already in d0
ENDPROC
;---------------------------------------------------------------------------------------
; a_checkload. Catch CheckLoad before entry and force all system resources into the
; system heap by setting the ResSysHeap bit.
; NOTE: An improvement here would be to have the CheckLoad patch just set & restore
; THEZONE = SYSZONE around calling through to the original CheckLoad. This way you
; could avoid setting and hiding attribute bits. It is rumored that system pieces
; besides the Resource Mgr use the resSysHeap bit to figure out whether the resource is
; in the system heap. Haven't seen it myself. RsrcZoneInit does it, but RsrcZoneInit
; is not called under Process Mgr.
; Input : a2 = pointer to resource entry
; a4 = handle to resource map
; Eats a0
; <14> This macro is here because including ResourceMgrPriv.a confuses
; a lot of things.
Macro
_IsThisASystemResourceMap
selectIsThisASystemResourceMap: equ -1
DoDispatch _ResourceDispatch,selectIsThisASystemResourceMap
EndM
a_checkload PROC EXPORT
EXPORT SetOldCheckLoad:CODE
subq #2,sp
move.l a4,-(sp)
_IsThisASystemResourceMap ; Is this the system map or system override?
tst.b (sp)+
bne.s system_rsrc ; if so, branch
move.l (a4),a0 ; a0 <- ptr to map
btst.b #mapForceSysHeap,MAttr(a0) ; should all entries in map go in system heap?
beq.s jumptoold ; if not, branch
system_rsrc
bset #ResSysHeap,RAttr(a2) ; get into the system heap (and stay there!)
bne.s jumptoold ; branch if was already set
bset #ResSysRef,RAttr(a2) ; mark that shouldn't be permanent
jumptoold
move.l old_checkload,-(sp) ; get address of old routine
rts ; and go there
old_checkload dc.l 0 ; address of old CheckLoad vector
; SetOldCheckLoad. Save the specified value as the chain address of CheckLoad.
; I think this is just to save loading A5 to look in the patchtraps table. Brilliant!
SetOldCheckLoad
lea old_checkload,a0
move.l 4(sp),(a0) ; parameter is address of old checkload
rts
ENDPROC
;---------------------------------------------------------------------------------------
; These routines have been moved out of rsrc_common to allow for their reentrancy,
; which is mandated by the MPW shell, which can get a grow zone request in
; _UpdateResFile, which might cause it to call _OpenResFile, which leads to
; _UpdateResFile. Although this is really an MPW bug, they claim it's the
; cornerstone of their memory management. Hmmm...
; a_updateresfile. Call the patch written in C.
a_updateresfile PROC EXPORT
IMPORT C_UPDATERESFILE:CODE
movem.l d0-d2/a1,-(sp) ; save regs
move.w 20(sp),-(sp) ; push the refnum
jsr C_UPDATERESFILE ; do it
movem.l (sp)+,d0-d2/a1 ; restore regs
move.l (sp)+,a0 ; get ret addr
addq.w #2,sp ; get rid of refnum
jmp (a0) ; and return
ENDPROC
; a_getresattrs. Call the patch written in C.
a_getresattrs PROC EXPORT
IMPORT C_GETRESATTRS:CODE
movem.l d0-d2/a1,-(sp) ; save regs
subq.w #2,sp ; make room for the result
move.l 22(sp),-(sp) ; push the handle
jsr C_GETRESATTRS ; do it
move.w (sp)+,24(sp) ; set real ret val from ours
movem.l (sp)+,d0-d2/a1 ; restore regs
move.l (sp)+,a0 ; get ret addr
addq.w #4,sp ; get rid of handle
jmp (a0) ; and return
ENDPROC
; a_releaseresource. Call the patch written in C.
a_releaseresource PROC EXPORT
IMPORT C_RELEASERESOURCE:CODE
movem.l d0-d2/a1,-(sp) ; save regs
move.l 20(sp),-(sp) ; push the handle
jsr C_RELEASERESOURCE ; do it
movem.l (sp)+,d0-d2/a1 ; restore regs
move.l (sp)+,a0 ; get ret addr
addq.w #4,sp ; get rid of handle
jmp (a0) ; and return
ENDPROC
; a_getnamedresource. Call the patch written in C.
a_getnamedresource PROC EXPORT
IMPORT C_GETNAMEDRESOURCE:CODE
movem.l d0-d2/a1,-(sp) ; save regs
subq.w #4,sp ; make room for our result
move.l 28(sp),-(sp) ; push the type
move.l 28(sp),-(sp) ; push the name ptr
jsr C_GETNAMEDRESOURCE ; do it
move.l (sp)+,28(sp) ; put our retval in real one
movem.l (sp)+,d0-d2/a1 ; restore regs
move.l (sp)+,a0 ; get ret addr
addq.w #8,sp ; get rid of params
jmp (a0) ; and return
ENDPROC
; a_sizersrc. Call the patch written in C.
a_sizersrc PROC EXPORT
IMPORT C_SIZERSRC:CODE
movem.l d0-d2/a1,-(sp) ; save regs
subq.w #4,sp ; make room for the result
move.l 24(sp),-(sp) ; push the handle
jsr C_SIZERSRC ; do it
move.l (sp)+,24(sp) ; set real ret val from ours
movem.l (sp)+,d0-d2/a1 ; restore regs
move.l (sp)+,a0 ; get ret addr
addq.w #4,sp ; get rid of handle
jmp (a0) ; and return
ENDPROC
;---------------------------------------------------------------------------------------
; a_getresource. Patch GetResource to get correct PDEF. This has to be assembler
; because System 4.1 and 4.2 _GetResource patches look at return addresses on the stack.
a_getresource PROC EXPORT
IMPORT MyRelString:CODE
IMPORT patchtraps:DATA
moveq.l #0,d0
move.b 5(sp),d0 ; get res id
move.l 6(sp),a0 ; get res type
move.l a5,-(sp) ; save old a5 on stack
movea.l ProcessMgrGlobals,a5 ; get address of our globals
; patch to get correct print driver based on current printer assignments
cmp.l #'PDEF',a0 ; is this a potential print entry point?
bne.s DoRealCall ; if not, branch
move.w 8(sp),d0 ; get res id (whole word)
cmp.w #7+1,d0 ; is it 0-7?
bcc.s DoRealCall ; if not, branch to real call
movem.l d1-d2/a1,-(sp) ; save work regs
lea -10(sp),sp ; room for 2 pointers + return value (on bottom)
pea 6(sp) ; push ptr to one ptr
pea 6(sp) ; push ptr to the other ptr
_MFGetPrTypeStrings ; get local and global ref nums
moveq.l #0,d0 ; assume pr types the same
tst.b (sp)+ ; is pr type locked for this guy?
beq.s @0 ; if so, branch (treat as strings equal)
jsr MyRelString ; are they the same?
move.l 4(sp),d2 ; save local name
; get here with condition codes saying whether the types are the same
@0
addq.w #8,sp ; get rid of the 2 string ptrs
tst.w d0 ; are they the same?
beq.s GotoPDEF ; if equal, branch
; Set the printer type for the current process, give a _Close call to the .Print driver, and
; then give it an _Open call. If the driver is not in yet, _Close will return an error which
; we ignore. If it is in, it simple passes it along to the appropriate .XPrint.
subq.w #2,sp ; room for return result
move.l d2,-(sp) ; push local name
move.w BootDrive,-(sp) ; has to be in sys folder
move.b #fsCurPerm,-(sp) ; default arg (need to r/w probably)
_OpenRFPerm ; bring to front (already opened)
move.w (sp)+,d2 ; save the refnum
_PrDrvrOpen ; Open new driver, thus closing old one
move.w d2,-(sp) ; push refnum of (old) local pr type name
_CloseResFile ; close old driver's res file
move.l TopMapHndl,a0 ; get top map
move.l (a0),a0 ; hdl -> ptr
move.l MRefNum(a0),CurMap ; force new guy (who is at top) to be current
GotoPDEF
movem.l (sp)+,d1-d2/a1 ; restore work regs
; enough of this! call the real routine!
DoRealCall
move.l patchtraps+RomAllPatchTable.Rom75Patches.GETRESOURCE.oldtrap,a0
move.l (sp)+,a5 ; restore old a5
jmp (a0) ; and go to old routine
PrintName dc.b '.Print'
ENDPROC
;---------------------------------------------------------------------------------------
; a_setgrowzone. Our patch to SetGrowZone. Calls a C routine to act on it. That routine
; returns an address we should call through to. Nil if we shouldn't. We, rather than
; the C routine, need to do the call, since it requires register setup.
a_setgrowzone PROC EXPORT
IMPORT SafeSetGrowZone:CODE
SGZPatchRegs REG d1-d2/a0-a1 ; volatile regs in C routine
movem.l SGZPatchRegs,-(sp) ; save registers
move.l a0,-(sp) ; put proc ptr as only arg
jsr SafeSetGrowZone ; and do it
addq.w #4,sp ; get rid of c param
movem.l (sp)+,SGZPatchRegs ; restore registers
tst.l d0 ; check for chain-through address
beq.s SGZDone ; exit if none (BTW: d0.w == noErr!!)
move.l d0,-(sp) ; push chain address for RTS
SGZDone
rts ; return to caller or old trap
ENDPROC
;---------------------------------------------------------------------------------------
; GetSysHeapString. Get a copy of the given string in the system heap.
; The C prototype is:
;
; StringPtr GetSysHeapString(StringPtr pStr);
;
GetSysHeapString PROC EXPORT
clr.l -(sp) ; assume retval is failure
move.l 8(sp),a0 ; param is src
moveq.l #1,d0 ; add 1 to...
add.b (a0),d0 ; ...string length to get block len
move.l d0,d1 ; save length
move.l a0,a1 ; save ptr
_NewPtr sys ; get space in system heap
bne.s GSHSDone ; if failure, branch
exg.l a0,a1 ; switch src/dest
move.l a1,(sp) ; and do the return value
move.l d1,d0 ; save length
_BlockMove
GSHSDone
move.l (sp)+,d0 ; pop retval into d0
rts
ENDPROC
SEG 'Main'
;------------------------------------------------------------------------------------
; Process Mgr implements the gestaltOSAttr selector. Below are the definitions, the
; selector routine, and the code to install it. The selector routine is supposed to
; sit in the system heap so it can be executed directly by applications. The installer
; copies the routine there, and was written in assembler to ensure that the effective
; address we pass is that of the routine itself, rather than its jump table address
; (which wouldn't be in the system heap).
;------------------------------------------------------------------------------------
OurGestaltValue EQU (1 << gestaltSysZoneGrowable) + \
(1 << gestaltLaunchCanReturn) + \
(1 << gestaltLaunchFullFileSpec) + \
(1 << gestaltLaunchControl) + \
(1 << gestaltTempMemSupport) + \
(1 << gestaltRealTempMemory) + \
(1 << gestaltTempMemTracked) + \
(1 << gestaltIPCSupport) + \
(1 << gestaltSysDebuggerSupport) + \
(1 << gestaltSkiaGlobalsSwitched)
PROC
EXPORT MyNewGestalt:CODE
;---------------------------------------------------------------------------------------
; gestaltOSAttrProc. The function we tell Gestalt to call when getting OS attributes.
; This is a Pascal style function looking like --
;
; FUNCTION gestaltOSAttrProc(selector : OSType; VAR result : LONGINT) : OSErr;
;
; Stack: (sp) return address
; 4(sp) address for Gestalt return value (long)
; 8(sp) Gestalt selector (long)
; 0A(sp) storage for result code (word)
gestaltOSAttrProc
move.l (sp)+,a0 ; move return address
move.l (sp)+,a1 ; get return value address
move.l (sp)+,d0 ; get selector
cmp.l #gestaltOSAttr,d0 ; is it our selector?
bne.s gOSAPError ; jump if not
move.l #OurGestaltValue,(a1) ; place the value
clr.w (sp) ; give good error code
gOSAPExit
jmp (a0) ; return
; we were called with a selector we don't understand (this should never be needed)
gOSAPError
move.w #gestaltUndefSelectorErr,(sp) ; set error code
bra.s gOSAPExit ; return
;---------------------------------------------------------------------------------------
; MyNewGestalt. Calls _NewGestalt to install the routine to handle the selector for
; which we are responsible (gestaltOSAttr).
MyNewGestalt
MNGSaveRegs REG d0-d1/a0-a2 ; working registers
movem.l MNGSaveRegs,-(sp) ; save working registers
lea gestaltOSAttrProc,a0 ; address of routine in our segment
move.l #gestaltOSAttr,d0 ; routine selector
_NewGestalt ; install our routine
movem.l (sp)+,MNGSaveRegs ; restore working registers
rts ; leave
ENDPROC
END