; ; 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