mac-rom/ProcessMgr/Error.a

342 lines
15 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; File: Error.a
;
; Contains: Routines which support the process error manager that could not be
; coded in C. Handles hardware exceptions and system errors.
;
; Written by: Erich Ringewald
;
; Copyright: © 1986-1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <4> 1/8/91 DFH (rdd) Calls to debugExitRoutine no longer dump params
; afterwards, since this is now a Pascal routine.
; <3> 1/8/91 DFH (rdd) Debug entry and exit routines are now Pascal style.
; <0> x/xx/86 ELR New Today.
;
;--------------------------------------------------------------------
CASE OBJECT
LOAD 'ProcessMgrIncludes.D'
INCLUDE 'data.a'
;-------------------------------------------------------------------------------
; Hardware exception handling. CPU exception vectors are routed through us.
;-------------------------------------------------------------------------------
PROC
EXPORT (buserr, addrerr, ilglerr, zeroerr):CODE
EXPORT (chkerr, trapverr):CODE
EXPORT (priverr, traceerr, line1111err):CODE
EXPORT (coprocerr, fmterr, uniniterr, spurint):CODE
EXPORT (unass,trap5err,trap6err): CODE
IMPORT (oldExceptionVectors, pCurrentProcess, dont_switch):DATA
IMPORT (IsOldDebugger, pDebugProcess, debugEntryRoutine, debugExitRoutine):DATA
IMPORT (patchtraps, ROMALLPATCHTABLE):DATA
IMPORT (SysErrRecover, CallDebugger):CODE
; ExceptionFunnel. Bottleneck for all exceptions we patch. Exception number is
; divinable from offset of beginning of our re-routing table to the return address atop
; the stack.
ExceptionFunnel
ExceptionRegs REG d0-d1/a0/a5
ExceptionRegsSize EQU 16 ; bytes in saved regs
; Calculate the error number
movem.l ExceptionRegs,-(sp) ; save work regs
lea ErrorCatchTab,a5 ; get start of re-routing table
move.l ExceptionRegsSize(sp),d0 ; get return address of bsr.s
sub.l a5,d0 ; subtract base to get 1-based offset * sizeof(bsr.s)
lsr #1,d0 ; normalize to get sys error number
move.w d0,DSErrCode ; save exception number in low memory
move.l ProcessMgrGlobals,a5 ; get Process Mgr a5
tst.l pDebugProcess ; is a debugger registered?
bne.s HandleForDebugger ; yes, go call it
TryMacsBug
cmp.w #dsTraceErr,d0 ; is it a trace exception?
beq.s LetMacsbugDoIt ; if so, always fall thru to MacsBug
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 #6,d1 ; is there a real debugger?
bne.s LetMacsbugDoIt ; if so, go to it
tst.b IsOldDebugger ; is MacsBug installed?
bne.s LetMacsbugDoIt ; if so, go to it
; This is the standard Process Mgr exception error handling. Call SysError! Takes
; advantage of the fact that our SysError patch will decide whether it's OK to
; ExitToShell, and, if not, just call the ROM to put up the bomb box. It will redo
; some of the checks we just made, but who cares about speed here!
; d0 == DSErrCode == exception code, a5 == ProcessMgrGlobals
_SysError ; ExitToShell or ROM bomb box
; Pass the call on to Macsbug, by returning through the saved vector for this
; error type. Note that many vectors map onto the same error number.
LetMacsbugDoIt
lea oldExceptionVectors,a5 ; get base of table (biased by 1)
asl.w #2,d0 ; vector size is 4
move.l -4(a5,d0.w),ExceptionRegsSize(sp) ; push vector for return (w/ 1-based table)
movem.l (sp)+,ExceptionRegs ; restore work regs
rts ; and jump there
;-------------------------------------------------------------------------------
;
; Table of entry points for the exceptions we handle
;
;-------------------------------------------------------------------------------
ErrorCatchTab
buserr bsr.s ExceptionFunnel ; exception type 1
addrerr bsr.s ExceptionFunnel ; exception type 2
ilglerr bsr.s ExceptionFunnel ; exception type 3
zeroerr bsr.s ExceptionFunnel ; exception type 4
chkerr bsr.s ExceptionFunnel ; exception type 5
trapverr bsr.s ExceptionFunnel ; exception type 6
priverr bsr.s ExceptionFunnel ; exception type 7
traceerr bsr.s ExceptionFunnel ; exception type 8
line1010err bsr.s ExceptionFunnel ; exception type 9
line1111err bsr.s ExceptionFunnel ; exception type 10
fmterr
uniniterr
spurint
unass bsr.s ExceptionFunnel ; exception type 11
coprocerr bsr.s ExceptionFunnel ; exception type 12
interr1 bsr.s ExceptionFunnel ; exception type 13
trap5err bsr.s ExceptionFunnel ; exception type 14
trap6err bsr.s ExceptionFunnel ; exception type 15
;-------------------------------------------------------------------------------
; A high-level debugger exists. Pass the error to him unless context switches are
; disabled, or the debugger itself is the errant process.
; There are three calls here, allowing the debugger to filter unwanted errors without
; our having to switch him in.
; 1) The onentry (filter) routine. It expects the the exception frame on the stack
; bottom, and all other registers preserved. It might not return.
; 2) If it does, call CallDebugger(), which will do a foreground switch into the debugger.
; 3) On return, call the onexit routine registered by the debugger. It restarts the
; process, not returning here.
; Stack is:
; sp -> d0
; d1
; a0
; a5
; bsr.s return address
; exception frame.
; and A5 == ProcessMgrGlobals
;
HandleForDebugger
tst.w dont_switch ; are we unswitchable?
bne.s TryMacsBug ; we can't switch -- call old standby
move.l pCurrentProcess,a0 ; get the current guy
cmp.l pDebugProcess,a0 ; is it the debugger?
beq.s TryMacsBug ; don't let debugger handle its own errors
; OK to call debugger filter procedure
movem.l d2/a1,-(sp) ; save more registers
subq.l #2,sp ; room for return value
pea SaveCurrPSN ; push ptr to serial number storage
_GetCurrentProcess ; call OS
addq.l #2,sp ; dump return code
movem.l (sp)+,d2/a1 ; restore registers
lea SaveArea,a0 ; get address of variables
move.l a5,(a0)+ ; set saveA5
move.l ExceptionRegsSize(sp),(a0)+ ; set saveBSRAddr
move.l debugEntryRoutine,(a0)+ ; set saveDBEntry
movem.l (sp)+,ExceptionRegs ; restore some regs
addq.w #4,sp ; get rid of bsr ret addr
; build parameter block on top of exception frame
pea DBWantsIt ; push ptr to boolean
move.l 4+SaveCurrPSN,-(sp) ; push low half of PSN
move.l SaveCurrPSN,-(sp) ; push high half of PSN
pea ReturnFromEntryRoutine ; make it look like a jsr
move.l saveDBEntry,-(sp) ; push the routine addr
rts ; and call it (stack has exception on bottom)
; if he wants this one, switch him in
ReturnFromEntryRoutine
move.l saveBSRAddr,-(sp) ; push this on again
movem.l ExceptionRegs,-(sp) ; re-save work regs
move.l saveA5,a5 ; restore a5
move.b DBWantsIt,d0 ; does it want it?
beq.s FromDebuggerToMacsBug ; if not, branch
; Do major switch to the system debugger. This suspends the current application
; (the debuggee), so CallDebugger does not return until the debuggee has been resumed.
jsr CallDebugger ; major switch to the debugger
; now that debuggee is running again, call the debugger's exit routine
lea -10(sp),sp ; room for serial number + return value (on bottom)
pea 2(sp) ; push ptr to serial number storage
_GetCurrentProcess ; call OS
addq.l #2,sp ; dump return code
move.l debugExitRoutine,a0 ; address of routine to call (PSN on stack!)
jsr (a0) ; and call it (might not return here)
; NOTE: We should never fall through to here, because the debugExitRoutine should
; clean up and reset the PC. But just in case...
FromDebuggerToMacsBug
move.w DSErrCode,d0 ; retrieve err val into d0 (need it in mainstream)
bra TryMacsBug ; let it fall thru to the rest
; yucky embedded variables
SaveArea
saveA5 DC.L 0
saveBSRAddr DC.L 0
saveDBEntry DC.L 0
SaveCurrPSN DC.L 0
DC.L 0
DBWantsIt DC.W 0
ENDPROC
;-------------------------------------------------------------------------------------------------------
; System error handling. A patch to _SysError.
;-------------------------------------------------------------------------------------------------------
; a_syserror. Our patch to SysError. Dole out the error to the appropriate debugger.
; If none, ExitToShell if we think that can help. Otherwise, call system handler. For
; errors in the benign range, flush accumulated events after calling through.
SOFT_SYSERRID_FIRST EQU 16384 ; beginning of softy syserr range
SE_saveregs REG d1-d2/a0-a1
a_syserror PROC EXPORT
IMPORT (NonStopSysErr, SysErrRecover, CallDebugger):CODE
IMPORT (pDebugProcess, dont_switch, debugEntryRoutine, debugExitRoutine):DATA
move.w d0,DSErrCode ; save error in low memory
ext.l d0 ; make err id a longword for C
movem.l d0/a5,-(sp) ; save on stack (d0 on top for NonStopSysErr)
move.l ProcessMgrGlobals,a5 ; and set up our a5
; if the error is benign, we can skip the high-level debugger altogether. Also, these
; are the errors that we should flush events for. Unsigned check to eliminate negatives.
cmpi.w #SOFT_SYSERRID_FIRST,d0 ; range reserved for benignity
bge.s CallSysErrFlush ; if so, don't bother with high-level debugger
; see if we should call high-level debugger filter procedure
move.l pDebugProcess,d0 ; is the debugger running?
beq.s CallSysErr ; if not, branch to old (d0 already on stack)
move.w dont_switch,d0 ; are we in a safe place?
beq.s TrySysDebugger ; if so, go through the debugger hoops
; NOTE: Clearing DSErrCode below assumes no application ever uses DSErrCode to change
; the program flow, since the system will not normally clear it. A potential problem
; could arise where someone hits the resume button and then the app tries to figure
; out from which error it is resuming.
CallSysErr
jsr SysErrRecover ; (error code already on top of stack)
clr.w DsErrCode ; if SysErrRecover returns, error is not real
move.l d0,a0 ; get real routine addr into addr register
tst.l d0 ; just set condition codes for the moment
movem.l (sp)+,d0/a5 ; get errid back into d0
beq.s ReturnNow ; jump if there was no special address
move.l a0,-(sp) ; push for rts
ReturnNow
rts ; good luck...
; Error is one to be ignored by debuggers. Some of these (like dsForcedQuit) put up a
; dialog. Unfortunately, the event (like mousedown) used to dismiss this dialog can not
; be flushed by the handler. We set up a new return address to take care of the flush.
CallSysErrFlush
movem.l (sp)+,d0/a5 ; restore stack and d0/a5
pea FlushEm ; so we return from system handler to flush
movem.l d0/a5,-(sp) ; re-push d0 and a5
bra.s CallSysErr ; go look for system handler
FlushEm
move.l #$0000FFFF,d0 ; events to flush (all of 'em!)
_FlushEvents ; call the OS
rts ; return to SysError caller
; There is a registered system debugger, and it is safe and sane to call it. First
; part of the protocol is to call the debugger's entry (filter) routine. After the RTS
; the stack looks like we just entered _SysErr and have preserved all of the registers
; except sp. The PC will be at the start of debugEntryRoutine.
TrySysDebugger
movem.l SE_saveregs,-(sp) ; save regs
subq.l #2,sp ; room for return value
pea SysErrorPSN ; push ptr to serial number storage
_GetCurrentProcess ; call OS
addq.l #2,sp ; dump return code
movem.l (sp)+,SE_saveregs ; restore regs
move.l debugEntryRoutine,d0 ; hold global in register
addq #4,sp ; forget the d0 on the stack
move.l (sp)+,a5 ; and restore a5
pea DBWantsIt ; push ptr to boolean
move.l SysErrorPSN+4,-(sp) ; push low long of PSN
move.l SysErrorPSN,-(sp) ; push high long of PSN
pea SysRetFromCall ; push return address
move.l d0,-(sp) ; push address of entry routine
move.w DSErrCode,d0 ; restore d0 from low memory
rts ; return to debug filter routine
; check result of filter
SysRetFromCall
moveq #0,d0 ; clear d0 so high-word will be 0
move.w DSErrCode,d0 ; re-get the err (caller's d0)
movem.l d0/a5,-(sp) ; push d0 and a5 (d0 on top!)
move.l ProcessMgrGlobals,a5 ; and set up ours
move.b DBWantsIt,d0 ; does the debugger want control?
beq.s CallSysErr ; if not, branch
; debugger filter proc said "yes", suspend debuggee to do a full switch into debugger
jsr CallDebugger ; suspend current process, call the debugger
; debuggee has been resumed, call debugger's debugExitRoutine
; NOTE: We reget the current PSN so that this is re-entrant while debugger is working
lea -10(sp),sp ; room for serial number + return value (on bottom)
pea 2(sp) ; push ptr to serial number storage
_GetCurrentProcess ; call OS
lea 2(sp),sp ; dump return code
move.l debugExitRoutine,a5 ; get addr of routine
jsr (a5) ; and go do it (PSN itself on the stack)
movem.l (sp)+,d0/a5 ; restore stack and d0/a5
rts ; good luck...
; yucky embedded variables
SysErrorPSN DC.L 0
DC.L 0
DBWantsIt DC.W 0
ENDPROC
; IsNonFatalSysErr. Determine whether appropriate handler for specified error is the
; old SysError routine, rather than an ExitToShell.
; NOTE: Have to return "true" for old menu mgr errors so that sys patch can either
; handle them (popups for MacII) or at least change them to the correct (i.e. positive
; ID) errors by recalling _SysError.
IsNonFatalSysErr PROC EXPORT
move.w 6(sp),d1 ; get error code
move.w #-1,d0 ; assume "true"
beq.s itsAOK ; noErr is fine by me!
cmp.w #dsReinsert,d1 ; well, OK, the user can insert a diskÉ
beq.s itsAOK
cmp.w #shutDownAlert,d1 ; and we'll even let him shut down!
beq.s itsAOK
cmp.w #hMenuFindErr,d1 ; deal with archaic error (I)
beq.s itsAOK
cmp.w #mBarNFnd,d1 ; deal with archaic error (II)
beq.s itsAOK
cmp.w #dsFinderErr,d1 ; ExitToShell here would be no good
beq.s itsAOK
cmp.w #SOFT_SYSERRID_FIRST,d1 ; range reserved for benignity
bge.s itsAOK
clr.w d0 ; return "false"
itsAOK
rts
ENDPROC
END