home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-04-25 | 23.7 KB | 912 lines | [TEXT/MPS ] |
- TITLE 'ErrSignal Unit Implementation'
- COMMENT 'ErrSignal v2.0a6, Copyright © 1989, 1991, 1992 David B. Lamkins'
-
- ; Revisions
- ; 1.0 A long time ago…
- ; 2.0a1 03/29/91
- ; 2.0a2 06/12/91 Fixed SignalRes and TrimCatchDepth.
- ; 2.0a3 07/29/91 Added SignalWhen, SignalUnless, and IdentifySignaller.
- ; 2.0a4 09/18/91 Added SignalDebugLevel.
- ; 2.0a5 09/30/91 Added SignalNIL and SignalNILRes.
- ; 2.0a6 03/24/92 Added RegisterCleanupAction, Cleanup, and Ignore.
- ; Fixed ReSignal and PassSignal (and variants of both)
- ; to dispose the catch that reached current handler.
- ; Fixed stack protocol in SignalNIL and SignalNILRes.
- ; Corrected SP adjustment in SignalRes.
- ; 2.0a7 04/25/92 Moved debugger check into InitSignals. Changed signal
- ; "protocol errors" to signal sigFail, rather than
- ; invoking SigDeath. Replaced SignalDebugLevel with
- ; SetSignalStops. Corrected stack for FreeCatch call in
- ; ReSignalDO.
- ;
- ; Asm ErrSignal.a
- ;
- ; Formatted for Courier 10, tabs every 8
- ;
- ;
- ; ErrSignal unit interface:
- ; PROCEDURE InitSignals (failCode: Integer);
- ; FUNCTION CatchSignal: Integer;
- ; FUNCTION IdentifySignaller: Longint;
- ; PROCEDURE Signal (code: Integer);
- ; PROCEDURE SignalWhen (code: Integer; test: Boolean);
- ; PROCEDURE SignalUnless (code: Integer; test: Boolean);
- ; PROCEDURE SignalMem;
- ; PROCEDURE SignalRes;
- ; PROCEDURE SignalNIL (p: UNIV Ptr);
- ; PROCEDURE SignalNILRes (h: UNIV Handle);
- ; PROCEDURE ReSignal;
- ; PROCEDURE FreeCatch;
- ; PROCEDURE SignalHandlerDone;
- ; FUNCTION CatchInBlock: Boolean;
- ; PROCEDURE TerminateSignalHandler;
- ; PROCEDURE PassSignal (code: Integer);
- ; PROCEDURE PassMemSignal;
- ; PROCEDURE PassResSignal;
- ; FUNCTION CatchDepth: Integer;
- ; PROCEDURE TrimCatchMemory;
- ; FUNCTION LastSignalCode: Integer;
- ; FUNCTION HandlingSignal: Boolean;
- ; CONST
- ; esStopInTHINKProject = 128;
- ; esStopInApplication = 64;
- ; esStopAtSignal = 4;
- ; esStopAtReSignal = 2;
- ; esStopAtError = 1;
- ; FUNCTION SetSignalStops (stopsMask: Integer): Integer;
- ; FUNCTION RegisterCleanupAction (p: ProcPtr): Integer;
- ; PROCEDURE Cleanup (value: UNIV Longint; action: Integer);
- ; PROCEDURE Ignore (value: UNIV Longint);
- ;
- ; VAR SigGlobals:OPAQUE; { not declared in the Pascal interface… }
- ;
- ; Derived from Apple Macintosh Technical Note #88, Version 1.0 by Rick Blair.
- ; Adapted for THINK Pascal by David B. Lamkins. Should work with other
- ; Pascal systems without modification.
- ; 1) A6 no longer modified by InitSignals, now saved for comparison
- ; 2) No static initializers in SigGlobals
- ; 3) Makes check for no catches in scope
- ; 4) Invokes _SysError for fatal errors
- ; 5) Checks for nil handle in SigSetup
- ; 6) InitSignal has failure code as argument
- ; 7) Added a ReSignal mechanism
- ; 8) Allows use of CatchSignal in expressions
- ; 9) Sanity-checks A6 during frame search
- ; 10) Catch table will shrink as well as expand
- ; 11) Module structure satisfies THINK Pascal .o converter
- ; 12) Intra-unit references optimized
- ; 13) Added calls to signal Memory and Resource Manager errors
- ; 14) Added calls to pass along various re-signal codes
- ; 15) Added in-handler flag and HandlingSignal inquiry function
- ; 16) Added CatchDepth function
- ; 17) Added TrimCatchMemory procedure to minimize catch storage
- ; 18) Added LastSignalCode function
- ; 19) Added CatchInBlock to inquire about presence of catch
- ; 20) Added SignalHandlerDone to use at end of handler
- ; 21) Added TerminateSignalHandler to finish and disestablish handler
- ; 22) Allow non-local Exit(…) and Goto … by cutting back catch table
- ; 23) Added SignalWhen and SignalUnless.
- ; 24) Added IdentifySignaller to give return address of last signaller.
- ; 25) Added SignalDebugLevel to enable break into debugger on signal.
- ; 26) Added SignalNIL to signal memFullError on a nil pointer or handle.
- ; 27) Added SignalNILRes to signal resNotFound (nil handle and no error) or ResError.
- ; 28) Added RegisterCleanupAction and Cleanup to streamline use of multiple catches.
- ; 29) Added Ignore to discard a scalar argument.
- ;
- ; The following will cause fatal errors:
- ; 1) Failure to initialize using InitSignal (unpredictable)
- ; 2) FreeSignal with no catches in scope
- ; 3) Signal… with no catches in scope, or inside an active handler
- ; 4) ReSignal with no catches in scope
- ; 5) Pass…Signal outside an active handler
- ; 6) TerminateSignalHandler outside an active handler or its scope
- ; 7) SignalHandlerDone outside an active handler
- ; 8) Not enough memory for new catch, and no other catch active
- ; 9) Not enough memory for new cleanup action, and no catch active
-
-
- PRINT OFF
- INCLUDE 'Traps.a'
- INCLUDE 'SysEqu.a'
- INCLUDE 'ToolEqu.a'
- INCLUDE 'SysErr.a'
- PRINT ON
-
- SigChunks EQU 5 ;number of elements to expand by
- FrameRet EQU 4 ;return address for frame (off A6)
-
- ; Registers are saved to allow CatchSignal to be used in an expression. The
- ; compiler does not expect D0-D2/A0-A1 to be preserved across calls. A6 is
- ; restored by the frame search, but we use the saved value for comparison.
- ; THE FPU REGISTERS ARE NOT SAVED!
-
- RegList REG D3-D7/A2-A7
- NumRegs EQU 11
-
- ; A catch needs the following information, which is kept in a table of catches:
-
- SigElement RECORD 0
- SigRegs DS.L NumRegs-2 ; Regs D3-D7/A2-A5 put here by MOVEM
- SigFP DS.L 1 ; A6 is put here by MOVEM
- SigSP DS.L 1 ; SP is put here by MOVEM
- SigRetAddr DS.L 1 ; Return address of CatchSignal call
- SigFRet DS.L 1 ; Return address of enclosing routine
- SigCUValue DS.L 1 ; Value to be "cleaned up"
- SigCUAction DS.W 1 ; Action to take during cleanup
- SigElSize EQU * ; The size of this record
- ENDR
-
- ; The signal unit has these private globals:
-
- SigGlobals RECORD
- SigEnd DS.L 1 ; Offset to the end of the catch table
- SigNow DS.L 1 ; Offset to the most recently established catch
- SigHandle DS.L 1 ; Handle to the catch table
- SigOuterA6 DS.L 1 ; Value of A6 at the time of InitSignals call
- SigFailCode DS.W 1 ; Code to be signalled for fatal errors
- SigLastCode DS.W 1 ; Last code signalled
- SigActive DS.B 1 ; Handler active flag
- SigInProject DS.B 1 ; “In THINK Pascal project” flag
- Sig32Bit DS.B 1 ; Running in 32-bit environment
- SigSysDebugger DS.B 1 ; Debugger is present
- SigStopsMask DS.W 1 ; Signal stops control mask
- SignalRA DS.L 1 ; Return address of any Signal… call
- SigCUProcs DS.L 1 ; Handle to the cleanup procs table
- SigCUNextID DS.W 1 ; ID of next cleanup procs action
- ENDR
-
- PROC
- BRANCH SHORT
-
- EXPORT InitSignals,CatchSignal,FreeCatch
- EXPORT ReSignal,Signal,SignalMem,SignalRes
- EXPORT PassSignal,PassMemSignal,PassResSignal
- EXPORT SignalWhen,SignalUnless,IdentifySignaller
- EXPORT LastSignalCode,HandlingSignal
- EXPORT SignalHandlerDone,TerminateSignalHandler
- EXPORT CatchInBlock,CatchDepth,TrimCatchMemory
- EXPORT SetSignalStops,SignalNIL,SignalNILRes
- EXPORT RegisterCleanupAction,Cleanup,Ignore
-
- WITH SigElement,SigGlobals
-
- ;PROCEDURE InitSignals (failCode: Integer);
- ;
- ; This must be called from the outermost scope of the program which will
- ; use signals. Typically, this is the body of the main program. InitSignals
- ; creates the catch table and initializes globals. SigNow is initialized
- ; with a negative value to indicate an empty table. SigEnd is initialized
- ; with an offset to the end of the table. SigOuterA6 saves the A6 value
- ; for later use in finding the outermost scope. Failure to call InitSignals
- ; is unpredictably fatal - no guarantee of SigDeath. The argument will be
- ; used as the code to signal failure to establish a catch and to indicate
- ; fatal errors - it should be chosen to be distinguishable from Macintosh
- ; system error codes.
- ;
- ; For use in the THINK Pascal project environment, InitSignals must be called
- ; while the application's resource file is still current — we rely on the
- ; observation that there is no CODE 0 resource in the application while
- ; running in the project environment.
-
- MacJmp EQU $120 ; Pointer to debugger; flags in hi byte if 24-bit addr
- Dbg32 EQU $BFF ; Debugger flags if 32-bit addressing is enabled
-
- InitSignals
- ; Init private globals
- MOVEA.L (SP)+,A1
- MOVE.W (SP)+,SigFailCode
- MOVE.L A1,-(SP) ; Set up for RTS later
- MOVE.L #-SigElSize,SigNow
- MOVE.L A6,SigOuterA6
- CLR.W SigLastCode
- SF SigActive
- CLR.W SigStopsMask
- CLR.W SigCUNextID
-
- ; Test for 32-bit addressing
- MOVE.L #$FF000000,D0
- _StripAddress
- TST.L D0
- SNE.B Sig32Bit
-
- ; Create the catch table
- MOVE.L #SigChunks*SigElSize,D0
- MOVE.L D0,SigEnd
- _NewHandle
- MOVE.L A0,SigHandle
-
- ; Create the cleanup procs table
- CLR.L D0
- _NewHandle
- MOVE.L A0,SigCUProcs
-
- ; Test for THINK Pascal project environment
- SF ResLoad
- SUBQ.L #4,SP
- MOVE.L #'CODE',-(SP)
- CLR.W -(SP)
- _Get1Resource
- MOVE.L (SP)+,D0
- SEQ SigInProject
- ST ResLoad
-
- ; Test for system debugger
- LEA MacJmp,A0
- TST.B Sig32Bit
- BEQ InitDebuggerCheck
- LEA Dbg32,A0
-
- InitDebuggerCheck
- BTST.B #5,(A0)
- SNE.B SigSysDebugger
- RTS
-
- ;FUNCTION SetSignalStops (stopsMask: Integer): Integer;
- ;
- ;SetSignalStops sets the conditions under which a signal will cause a break
- ;into the low-level debugger. The conditions are determined by the es...
- ;masks. Note that a break, when enabled, only happens if there is
- ;a low-level debugger present in the system. SetSignalStops returns the
- ;previous mask.
-
- SetSignalStops
- MOVE.L (SP)+,A0
- MOVE.W SigStopsMask,D0
- MOVE.W (SP)+,SigStopsMask
- MOVE.W D0,(SP)
- JMP (A0)
-
- ; SigBreak conditionally breaks into the low-level debugger before signalling.
- ; The condition is based on the environment (THINK Pascal project vs compiled
- ; application), the presence of a debugger, and the stop mask (set by the
- ; last call to SetSignalStops.
-
- esStopInTHINKProject EQU 128
- inTHINKProjectBit EQU 7
- esStopInApplication EQU 64
- inApplicationBit EQU 6
- esStopAtSignal EQU 4
- esStopAtReSignal EQU 2
- esStopAtError EQU 1
-
- BreakMsg
- DC.B 'Signal stop'
-
- SigBreak
- ; On entry, D1 indicates condition (signal, resignal, error).
- ; D0 is preserved.
- MOVE.W D0,-(SP)
- MOVE.W SigStopsMask,D0
- MOVE.W D0,D2
- ANDI.W #esStopInThinkProject+esStopInApplication,D2
- BEQ SigBreakDone
-
- TST.B SigInProject
- BEQ InApp
-
- BTST.L #inTHINKProjectBit,D1
- BEQ SigBreakDone
-
- InApp
- BTST.L #inApplicationBit,D1
- BEQ SigBreakDone
-
- AND.W D0,D1
- BEQ SigBreakDone
-
- PEA BreakMsg
- _DebugStr
- SigBreakDone
- MOVE.W (SP)+,D0
- RTS
-
- ;FUNCTION RegisterCleanupAction (p: ProcPtr): Integer;
- ;
- ; RegisterCleanupAction adds a routine to the cleanup action table and returns
- ; the ID of the action (to be used in subsequent calls to Cleanup). If the table
- ; can not hold the entry, a memory error is signalled. This supports a maximum
- ; of 32767 entries; a negative number is returned if this limit is exceeded.
- ; The routine MUST reside in a locked segment or be referenced through a jump
- ; table entry!
- ;
- ; The action proc is declared as:
- ; PROCEDURE CleanupActionProc (value: UNIV Longint);
-
- RegisterCleanupAction
- ; Prepare the return value
- MOVE.W SigCUNextID,D0
- MOVE.W D0,8(SP)
- BMI RegisterCleanupActionDone
-
- ; Grow the table to hold another entry
- EXT.L D0
- LSL.L #2,D0
- MOVE.L D0,D1
- ADDQ.L #4,D0
- MOVEA.L SigCUProcs,A0
- _SetHandleSize
- TST.W D0
- BNE.W SignalD0
-
- ; Stuff the proc pointer into the new entry
- MOVE.L 4(SP),D0
- MOVEA.L (A0),A0
- MOVE.L D0,(A0,D1.L)
-
- ; Update the ID for the next entry
- MOVE.W 8(SP),D0
- ADDQ.W #1,D0 ; This will wrap around from 32767 to -32768
- MOVE.W D0,SigCUNextID
-
- RegisterCleanupActionDone
- ; Adjust the stack and return
- MOVEA.L (SP)+,A0
- ADDQ.L #4,SP
- JMP (A0)
-
- ;PROCEDURE Cleanup (value: UNIV Longint; action: Integer);
- ;
- ; Cleanup establishes a catch and records a value and the ID of an action proc
- ; (as returned by RegisterCleanupAction). When a signal reaches this catch, the
- ; action proc (if defined) is applied to the saved value. The action proc executes
- ; in the context of a signal handler. The cleanup handler finishes by propagating
- ; the signal to the next handler.
- ;
- ; The action proc is declared as:
- ; PROCEDURE CleanupActionProc (value: UNIV Longint);
-
- Cleanup
- ; Establish a catch at current lexical level
- SUBQ.L #2,SP ; First, set the catch
- BSR CatchSignalInternal
- MOVEA.L SigHandle,A0 ; Point A0 at the new catch entry
- MOVEA.L (A0),A0
- MOVE.L SigNow,D0
- ADDA.L D0,A0
- TST.W (SP)+ ; Check the CatchSignalResult
- BNE CleanupDoCleanup
-
- ; Stuff value and action ID into new catch table entry
- MOVE.W 4(SP),D0
- MOVE.W D0,SigCUAction(A0)
- MOVE.L 6(SP),D0
- MOVE.L D0,SigCUValue(A0)
-
- ; Adjust the stack and return
- MOVE.L (SP)+,A0
- ADDQ.L #6,SP
- JMP (A0)
-
- CleanupDoCleanup
- ; This is the signal handler - apply cleanup action to saved value
- SUBI.L #SigElSize,D0 ; Remove the top catch info
- MOVE.L D0,SigNow
- MOVE.W SigCUAction(A0),D0 ; Get the cleanup action ID
- BMI CleanupResignal ; Bail out if action ID < 0
-
- CMP.W SigCUNextID,D0
- BHS CleanupResignal ; Bail out if no action with matching ID
-
- ; Get the address of the cleanup handler and call it
- MOVEA.L SigCUProcs,A1
- MOVEA.L (A1),A1
- LSL #2,D0
- MOVEA.L (A1,D0.W),A1
- MOVE.L SigCUValue(A0),D0
- MOVE.L D0,-(SP)
- JSR (A1)
-
- CleanupResignal
- ; Continue on to the next handler…
- ; Don't bother too much about the stack - we never return
- MOVE.W SigLastCode,-(SP)
- CLR.L -(SP) ; Fake RA gets discarded, anyhow
- BRA.W SignalInternal
-
- ;FUNCTION CatchSignal: Integer;
- ;
- ; CatchSignal must be called from within a procedure or function which has
- ; a stack frame (created by a LINK #n,A6 instruction). CatchSignal establishes
- ; a catch by creating a new catch table entry, saving the SP of CatchSignal's
- ; caller, the CatchSignal return address and the return address of CatchSignal's
- ; caller, patching in the address of SigPop in place of CatchSignal's caller's
- ; return address, and finally returning a zero result.
- ;
- ; There are a few exception conditions which must be considered. If the catch
- ; table is missing, a fatal error is indicated via SigDeath. If the catch
- ; table is full, CatchSignal attempts to expand it to make room for additional
- ; entries, and signals (using Signal, of course) an error if the expansion of
- ; the catch table is unsuccessful, meaning that the catch could not be
- ; established. If the catch table is not more than half full and is larger than
- ; its initial size, its size will be reduced to half (rounding down) the number
- ; of chunks plus one. Finally, if CatchSignal is called at the same lexical
- ; level as InitSignals, it is unnecessary to patch in SigPop.
-
- CatchSignal
- ; Is this OK to do?
- LEA SigPop,A0
- CMPA.L FrameRet(A6),A0
- BEQ.W SigDeath
-
- CatchSignalInternal
- LEA SigPop,A0
-
- ; Grab return address
- MOVEA.L (SP)+,A1
-
- ; Get handle to catch table
- MOVE.L SigHandle,D0
- BEQ.W SigDeath
-
- ; Check for table full
- MOVEA.L D0,A0
- MOVE.L SigNow,D0
- ADDI.L #SigElSize,D0
- MOVE.L D0,SigNow
- CMP.L SigEnd,D0
- BEQ ChangeSize
-
- ; Check for table underutilization
- MOVE.L SigEnd,D1
- ASR.L #1,D1
- SUB.L D0,D1
- BLT SetCatch
-
- ; Halve the number of chunks, rounding down
- MOVE.L #SigChunks*SigElSize,D2
- DIVU D2,D1
- MULU D2,D1
- MOVE.L D1,D0
-
- ChangeSize
- ; Add a chunk and try to change catch table size
- ADD.L D2,D0
- CMP.L D2,D1
- BEQ SetCatch
-
- MOVE.L D0,SigEnd
- _SetHandleSize
- BNE NoCatchSet
-
- MOVE.L SigNow,D0
-
- SetCatch
- ; Point to new catch table entry
- MOVEA.L (A0),A0
- ADDA.L D0,A0
-
- ; Save regs and return address in catch entry
- MOVEM.L RegList,SigRegs(A0)
- MOVE.L A1,SigRetAddr(A0)
-
- ; Test for outermost lexical level
- CMPA.L SigOuterA6,A6
- BEQ CatchSet
-
- ; Only patch in SigPop once
- MOVE.L A0,-(SP)
- LEA SigPop,A0
- CMP.L FrameRet(A6),A0
- MOVEA.L (SP)+,A0
- BEQ CatchSet
-
- ; Patch in SigPop to precede caller's exit
- MOVE.L FrameRet(A6),SigFRet(A0)
- LEA SigPop,A0
- MOVE.L A0,FrameRet(A6)
-
- CatchSet
- ; Return a zero, meaning "catch established"
- CLR.W (SP)
- JMP (A1)
-
- NoCatchSet
- ; Restore catch globals, signal error "failed to establish catch"
- MOVE.L SigNow,SigEnd
- MOVEQ.L #SigElSize,D0
- SUB.L D0,SigNow
- MOVE.W SigFailCode,D0
- BRA SigError
-
- ;PROCEDURE SignalHandlerDone;
- ;
- ; Call this from a signal handler that's finished its work, but stays around.
-
- SignalHandlerDone
- TST.B SigActive
- BEQ SigError
-
- SF SigActive
- RTS
-
- ;PROCEDURE TerminateSignalHandler;
- ;
- ; Call this from a signal handler that's finished if you want to remove it.
-
- TerminateSignalHandler
- BSR SignalHandlerDone
- ; Fall through to FreeCatch…
-
- ;PROCEDURE FreeCatch;
- ;
- ; FreeCatch is used to disestablish the most recent catch. It is fatal to
- ; call FreeCatch with no catches in scope. FreeCatch unhooks the SigPop
- ; address from the stack, restores the prior return address for the calling
- ; procedure, and discards the most recent catch table entry.
-
- FreeCatch
- ; Is it OK to do this?
- LEA SigPop,A0
- CMPA.L FrameRet(A6),A0
- BNE SigError
-
- ; Unhook SigPop and remove the catch from the table
- BSR SigSetup
- MOVE.L SigFRet(A0),FrameRet(A6)
- SUBI.L #SigElSize,D0
- MOVE.L D0,SigNow
- RTS
-
- ; SigPop is used to remove the most recent catch entry from the table. It
- ; is patched into the procedure's return address by CatchSignal. When invoked,
- ; it removes the last entry from the catch table and transfers control to the
- ; procedure's normal return address. In the case of nested catches within a
- ; lexical scope, this will happen several times before the real return address
- ; is reached.
-
- SigPop
- BSR SigSetup ; Our caller unlinked frame before we got here…
- MOVEA.L SigElSize+SigFRet(A0),A0
- ; …so find the catch entry we just removed.
- JMP (A0)
-
- ;PROCEDURE PassSignal (code: Integer);
- ;
- ; PassSignal is similar to ReSignal, but allows a different result to be passed.
-
- PassSignal
- MOVEA.L (SP)+,A0
- MOVE.W (SP)+,D0
- MOVE.L A0,-(SP)
- BRA ReSignalD0
-
- ;PROCEDURE PassMemSignal;
- ;
- ; This is like calling PassSignal(MemError)
-
- PassMemSignal
- MOVE.W MemErr,D0
- BRA ReSignalD0
-
- ;PROCEDURE PassResSignal;
- ;
- ; This is like calling PassSignal(ResError)
-
- PassResSignal
- SUBQ.L #2,SP
- _ResError
- MOVE.W (SP)+,D0
- BRA ReSignalD0
-
- ;PROCEDURE SignalNIL (p: UNIV Ptr);
- ;
- ; This signals a memFullErr if its argument is NIL.
-
- SignalNIL
- MOVE.W #memFullErr,D0
- MOVEA.L (SP)+,A0
- MOVE.L (SP)+,D1
- MOVE.L A0,-(SP)
- TST.L D1
- BEQ SignalD0
-
- SignalNotNIL
- RTS
-
- ;PROCEDURE SignalNILRes (h: UNIV Handle);
- ;
- ; If its argument is NIL, this signals either the non-zero result
- ; of ResError or resNotFound.
-
- SignalNILRes
- MOVEA.L (SP)+,A0
- MOVE.L (SP)+,D1
- MOVE.L A0,-(SP)
- TST.L D1
- BNE SignalNotNIL
-
- SUBQ.L #2,SP
- _ResError
- MOVE.W (SP)+,D0
- BNE SignalD0
-
- MOVE.W #resNotFound,D0
- BRA SignalD0
-
- ; SigSetupDone is the tail of SigSetup — see below…
-
- SigSetupDone
- ; Point to the entry we found, and return its offset
- ADDA.L D0,A0
- MOVE.L D0,SigNow
- RTS
-
- ; SigError is used to signal that we did something to violate signal protocol.
-
- SigError
- MOVEQ #esStopAtError,D1
- BSR.W SigBreak
- MOVE.W SigFailCode,-(SP)
- BSR Signal
-
- ; So long as SigFailCode<>0, we'll never reach here
- ; Fall through to SigDeath…
-
- ; SigDeath invokes the Macintosh SysError handler to indicate a fatal error.
-
- SigDeath
- MOVE.W SigFailCode,D0
- _SysError
- _ExitToShell ; Just in case…
-
- ; SigSetup initializes A0 to point to the current entry in the catch table,
- ; as determined by the SigNow global and D0 to the value of SigNow. If there
- ; is anything amiss with the table or if there are no active catches, SigDeath
- ; is invoked to indicate a fatal error.
- ;
- ; SigSetup discards any catch table entries that have been abandoned by a
- ; non-local exit (caused by Exit(…) or Goto …) discarding one or more stack
- ; frames without calling SigPop.
-
- SigSetup
- ; Make sure we have a catch table
- MOVE.L SigHandle,D0
- BEQ SigDeath
-
- ; Get ready to search the table
- MOVEA.L D0,A0
- MOVEA.L (A0),A0
- MOVE.L SigNow,D0
-
- SigSetupClean
- ; Fail if we don't find our entry in the table
- BMI SigDeath
-
- ; We're looking for a table entry with an A6 that's still accessible
- CMPA.L SigFP(A0,D0.L),A6
- BLS SigSetupDone
-
- SUBI.L #SigElSize,D0
- BRA SigSetupClean
-
- ;PROCEDURE ReSignal;
- ;
- ; ReSignal is used to send the same signal sent by the most recent call to
- ; Signal. It is erroneous to call this outside of an active signal handler.
- ; ReSignal is provided mainly as a syntactic convenience, to be used in the
- ; 'otherwise' case of a nested handler.
-
- ReSignal
- ; Get the last signalled code
- MOVE.W SigLastCode,D0
-
- ReSignalD0
- MOVEQ #esStopAtReSignal,D1
- BSR.W SigBreak
- ; Make sure it's OK to do this
- TST.B SigActive
- BEQ SigError
-
- ; Get rid of the catch that brought us here
- MOVEA.L (SP)+,A0
- MOVE.W D0,-(SP)
- MOVE.L A0,-(SP)
- BSR.W FreeCatch
-
- ; Set up for entry into Signal
- BRA SignalInternal
-
- SignalD0
- MOVEA.L (SP)+,A0
- MOVE.W D0,-(SP)
- MOVE.L A0,-(SP)
- ; Continue into Signal…
-
- ;PROCEDURE Signal (code: Integer);
- ;
- ; Signal with a zero argument simply returns. Invoked with a nonzero argument,
- ; Signal causes a transfer of control to the active (i.e. not disestablished
- ; using a FreeSignal call) catch most recently established by a CatchSignal
- ; call. In this case, the argument passed to Signal is 'returned' by the
- ; CatchSignal call - control does not return to the statement following Signal.
-
- Signal
- ; Break into the debugger if required
- MOVEQ #esStopAtSignal,D1
- BSR.W SigBreak
-
- ; Get the RA to identify the signaller
- MOVE.L (SP),SignalRA
-
- ; Make sure it's OK to do this
- TST.B SigActive
- BNE SigError
-
- SignalInternal
- ; Get the signal argument
- MOVE.W 4(SP),D1
- MOVE.W D1,SigLastCode
- BNE SigFind
-
- ; Ignore zero argument
- MOVEA.L (SP),A0
- ADDQ.L #6,SP
- JMP (A0)
-
- ; Search stack for active catch, fatal if not found or stack corrupted
- SigFind
- BSR SigSetup
- BRA SigLoop1
-
- SigLoop
- CMPA.L SigOuterA6,A6
- BEQ SigDeath
-
- CMPA.L CurStackBase,A6
- BHI SigDeath
-
- MOVE.L A6,D0
- BTST #0,D0
- BNE SigDeath
-
- ; If we're in TP project, tell the debugger we're unwinding a frame
- TST.B SigInProject
- BEQ SigUnwind
-
- TRAP #$7
-
- ; The debugger has probably clobbered A0 and moved memory…
- MOVEA.L SigHandle,A0
- MOVEA.L (A0),A0
- MOVE.L SigNow,D0
- ADDA.L D0,A0
-
- ; Unwind one stack frame, then see if that's enough
- SigUnwind
- UNLK A6
-
- SigLoop1
- CMPA.L SigFP(A0),A6
- BLO SigLoop
-
- ST SigActive
-
- ; Found frame of active catch, restore regs and invoke the catch
- SigRestore
- MOVEM.L SigRegs(A0),RegList
- MOVEA.L SigRetAddr(A0),A0
- MOVE.W D1,(SP)
-
- ; Jump out to the catch with the signalled code
- JMP (A0)
-
- ;PROCEDURE SignalMem;
- ;
- ; SignalMem is used to signal any error from the last Memory Manager call.
-
- SignalMem
- MOVE.W MemErr,D0
- BRA SignalD0
-
- ;PROCEDURE SignalRes;
- ;
- ; SignalRes is used to signal any error from the last Resource Manager call.
-
- SignalRes
- SUBQ.L #2,SP
- _ResError
- MOVE.W (SP)+,D0
- BRA SignalD0
-
- ; DoCondSignal removes the 'test' argument and transfers to Signal.
-
- DoCondSignal
- MOVEA.L (SP)+,A0
- ADDQ.L #2,SP
- MOVE.L A0,-(SP)
- BRA Signal
-
- ;PROCEDURE SignalUnless (code: Integer; test: Boolean);
- ;
- ; SignalUnless is shorthand for “if not test then Signal(code)”.
-
- SignalUnless
- NOT.B 4(SP)
- ; Continue into SignalWhen…
-
- ;PROCEDURE SignalWhen (code: Integer; test: Boolean);
- ;
- ; SignalWhen is shorthand for “if test then Signal(code)”.
-
- SignalWhen
- BTST.B #0,4(SP)
- BNE DoCondSignal
-
- MOVE.L (SP),A0
- ADDQ.L #8,SP
- JMP (A0)
-
- ;FUNCTION LastSignalCode: Integer
- ;
- ; Returns the last result passed to a catch, or noErr
-
- LastSignalCode
- MOVE.W SigLastCode,4(SP)
- RTS
-
- ;FUNCTION HandlingSignal: Boolean
- ;
- ; Returns true only within an active signal handler
-
- HandlingSignal
- MOVE.B SigActive,4(SP)
- RTS
-
- ;FUNCTION CatchInBlock: Boolean;
- ;
- ; CatchInBlock returns true only if there is a handler established at the
- ; current lexical level.
-
- CatchInBlock
- LEA SigPop,A0
- CMPA.L FrameRet(A6),A0
- SEQ.B 4(SP)
- RTS
-
- ;FUNCTION CatchDepth: Integer
- ;
- ; CatchDepth returns the number of active catch handlers.
-
- CatchDepth
- MOVE.L SigNow,D0
- DIVS #SigElSize,D0
- ADDQ.W #1,D0
- MOVE.W D0,4(SP)
- RTS
-
- ;PROCEDURE TrimCatchMemory;
- ;
- ; TrimCatchMemory minimizes the size of the catch storage.
-
- TrimCatchMemory
- MOVEA.L SigHandle,A0
- MOVE.L SigNow,D0
- ADDI.L #SigElSize,D0
- CMPI.L #SigChunks*SigElSize,D0
- BLE TrimDone
-
- _SetHandleSize
- TrimDone
- RTS
-
- ;FUNCTION IdentifySignaller: Longint
- ;
- ; Gives the return address of the last call to a Signal… routine.
-
- IdentifySignaller
- MOVE.L SignalRA,4(SP)
- RTS
-
- ;PROCEDURE Ignore (value: UNIV Longint);
- ;
- ; This just discards the value passed to it. Saves you from allocating a variable…
-
- Ignore
- MOVEA.L (SP)+,A0
- ADDQ.L #4,SP
- JMP (A0)
-
- ; That's all there is, folks…
-
- END
-