mac-rom/ProcessMgr/ProcessMgrMisc.a

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