mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-29 07:29:15 +00:00
0ba83392d4
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.
342 lines
15 KiB
Plaintext
342 lines
15 KiB
Plaintext
;
|
||
; 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
|