UFailure.a

;
;   Apple Macintosh Developer Technical Support
;
;   Exception handling for MPW Pascal, MacApp and MPW C
;
;   UFailure (aka Signals) - ÒExceptional code, with a few exceptions.Ó
;
;   UFailure.a  -   Assembly Source
;
;   Copyright © 1985-1988 Apple Computer, Inc.
;   All rights reserved.
;
;   Versions:   1.0                 11/88
;
;   Components: UFailure.p          November 1, 1988
;               UFailure.h          November 1, 1988
;               UFailure.inc1.p     November 1, 1988
;               UFailure.a          November 1, 1988
;
;   UFailure (or Signals) is a set of exception handling routines suitable for
;   use with MacApp, MPW C, and MPW Pascal. It is a jazzed-up version of the MacApp
;   UFailure unit. There is a set of C interfaces to it as well.
;
 
            PRINT   OFF
            INCLUDE 'Traps.a'
            PRINT   ON
            
            STRING  ASIS
 
;=============================================================================================
            GblA    &UsingMacApp
    If      &TYPE('&Debugging') = 'UNDEFINED' Then
            GblA    &Debugging
&UsingMacApp    SetA    0           ; we assume weÕre MacAppless if &Debugging isnÕt defined 
        If      &TYPE('&Debug') = 'UNDEFINED' Then
&Debugging  SetA    0           ; default to debugging off
        Else
&Debugging  SetA    &Debug      ; use the makefile definition of &Debug
        EndIf
    Else    
&UsingMacApp    SetA    1           ; we assume we have MA if &Debugging is defined 
    EndIf
;=============================================================================================
    If      &TYPE('Head') = 'UNDEFINED' Then
        Macro   
        Head
                GblA    &Debugging
            If  &Debugging Then
                Link    A6,#0       ; These two instructions form a slow no-op
                Move.L  (SP)+,A6
            EndIf
        EndM
    EndIf
;=============================================================================================
    If      &TYPE('Tail') = 'UNDEFINED' Then
        Macro   
        Tail
                GblA    &Debugging
            If  &Debugging Then
                Unlk    A6
                Rts
                DC.B    &Ord(&SubStr(&Syslst[1],2,1)) + $80
                DC.B    '&SubStr(&Syslst[1],3,7)'
            EndIf
        EndM
    EndIf
;=============================================================================================
        
; the following constant sets the limit on the depth of nested CatchSignals
SigBlockSize EQU    8                           ;number of elements in block
 
CatchSigErr EQU     200                         ;"insufficient heap" message
SigElSize   EQU     72                          ;byte size of FailInfo record
FrameRet    EQU     4                           ;return addr. for frame (off A6)
SigBigA6    EQU     $FFFFFFFF                   ;A6 at outer level (for Pascal)
nSavedRegs  EQU     11                          ;number of registers saved in FailInfo record
 
; offsets into the FailInfo record (see UFailure.p)
SigA6       EQU     36
SigSP       EQU     40
SigCode     EQU     44
SigMessage  EQU     46
SigFailA6   EQU     50
SigFailPC   EQU     54
whatSignals EQU     66
SigFRet     EQU     68
 
    
; The global data used by these routines follows. It is in the form of a 
; RECORD, but, unlike above, no origin is specified, which means that memory 
; space *will* be allocated.
; This data is referenced through a WITH statement at the beginning of the 
; procs that need to get at this data. Since the Assembler knows when it is 
; referencing data in a data module (since they must be declared before they 
; are accessed), and since such data can only be accessed based on A5, there 
; is no need to explicitly specify A5 in any code which references the data 
; (unless indexing is used).  Thus, in this program we have omitted all A5 
; references when referencing the data.
 
SigGlobals RECORD       ;no origin means this is a data record
                        ;not a template(as above)
SigEnd      DS.L    1   ;current end of table
SigNow      DS.L    1   ;the current offset
SigPointer  DC.L    0   ;the handle := 0 initially
            ENDR    
 
 
        If  &UsingMacApp Then
            Seg     'MAInit'
        EndIf
InitSignals PROC    EXPORT                      ;PROCEDURE InitSignals;
            EXPORT  InitUFailure
            IMPORT  gTopHandler:Data
            IMPORT  gInitHandler:Data
        If  &UsingMacApp And &Debugging Then
            IMPORT  gAskAboutAlloc:Data
            IMPORT  gAskFailure:Data
        EndIf
            WITH    SigGlobals
;the above statement makes the template SigElement and the global data 
;record SigGlobals available to this procedure
 
            Head
            MOVE.L  #SigBigA6,A6                ;make A6 valid for Signal
InitUFailure                                    ;PROCEDURE InitUFailure;
            CLR.L   gTopHandler(A5)             ;nothing in the list
            CLR.L   gInitHandler(A5)            ;MA has another list during initialization
        If  &UsingMacApp And &Debugging Then
            CLR.B   gAskAboutAlloc(A5)          ;set the MA debugging guys to FALSE
            CLR.B   gAskFailure(A5)
        EndIf
 
            MOVE.L  #SigBlockSize*SigElSize,D0
            _NewPtr                             ;try to get a table
            BNE.S   forgetit                    ;we couldn't get that!?
            
            MOVE.L  A0,SigPointer               ;save it
            MOVE.L  #-SigElSize,SigNow          ;point "now" before start
            MOVE.L  #SigBlockSize*SigElSize,SigEnd ;save the end
forgetit    RTS
        
            Tail    'INITUFAI'
            ENDP
 
 
        If  &UsingMacApp Then
            Seg     'MAMain'
        EndIf
CatchCFailures PROC EXPORT                      ;pascal void CatchCFailures(FailInfo &fi, HandlerFuncPtr handler);
            IMPORT  FailCEntry
            
            Head
            MOVE.L  (A7)+,D2                    ;save return address
            MOVEQ   #0,D1                       ;CatchCFailures used  (no A6 link, uses handler proc.)
            BRA.S   FailCEntry                  ;finish from CatchFailures
            Tail    'CATCHCFA'
 
CatchFailures PROC  EXPORT                      ;PROCEDURE CatchFailures(VAR fi: FailInfo;
;                                                   PROCEDURE Handler(code: INTEGER; message: LONGINT));
            IMPORT  FailuresEntry
            EXPORT  FailCEntry
            WITH    SigGlobals
            
            Head
            MOVE.L  (A7)+,D2                    ;save return address
            ADDQ    #4,A7                       ;discard A6 link
            MOVEQ   #-1,D1                      ;CatchFailures used  (maybe A6 link???, handler proc.)
FailCEntry
            MOVE.L  (A7)+,A1                    ;get handler address
            MOVE.L  (A7)+,A0                    ;FailInfo record address
            BRA.S   FailuresEntry               ;enter CatchSignal with D2, A0, A1 set
            Tail    'CATCHFAI'
 
CatchSignal PROC    EXPORT                      ;FUNCTION CatchSignal:INTEGER
            IMPORT  SiggyPop2,Signal,SigDeath
            IMPORT  gTopHandler:Data
            EXPORT  FailuresEntry
            WITH    SigGlobals
            
            Head
            MOVE.L  (A7)+,A1                    ;grab return address for ÒhandlerÓ address
            MOVE.L  A1,D2                       ;save return address
            MOVEQ   #1,D1                       ;CatchSignals used; no A6 link needed on stack, no proc.
            CLR.W   (A7)                        ;no error code (before its time)
 
            MOVE.L  SigPointer,D0               ;point to table
            BEQ     SigDeath
            MOVE.L  D0,A0
            MOVE.L  SigNow,D0
            ADD.L   #SigElSize,D0
            MOVE.L  D0,SigNow                   ;save new position
            CMP.L   SigEnd,D0                   ;have we reached the end?
            BNE.S   catchit                     ;no, proceed
            
;signals, we use 'em ourselves
            MOVE.L  SigNow,SigEnd               ;restore old ending offset
            MOVE.L  #SigElSize,D0
            SUB.L   D0,SigNow                   ;ditto for current position
            MOVE.W  #CatchSigErr,4(A7)          ;we'll signal a "couldn't
                                                ;  catch" error
            JSR     Signal                      ;never returns of course
                    
catchit     
            ADD.L   D0,A0                       ;point to new entry
FailuresEntry                                   ;A0=pointer to FailInfo block,A1=handler address,
                                                ; D1=flag showing how we entered, D2=return address
            MOVE.L  A0,D0                       ;save FailInfo record address
 
            MOVE.W  D1,whatSignals(A0)          ;remember whatÕs catching (-1=CatchFailures,
                                                ; 0=CatchCFailures, 1=CatchSignal)
            BLE.S   @1                          ;canÕt use frame to clean up if theyÕll call Success
 
            CMP.L   #SigBigA6,A6                ;are we at the outer level?
            BEQ.S   @1                          ;yes, no frame, no cleanup needed 
            MOVE.L  FrameRet(A6),SigFRet(A0)    ;save old frame return
                                                ; address
            LEA     SiggyPop,A0
            MOVE.L  A0,FrameRet(A6)             ;set cleanup code address
            MOVE.L  D0,A0                       ;restore FailInfo record address
@1      
            MOVEM.L A2-A7/D3-D7,(A0)            ;save all registers first (note that the saved
                                                ; stack points to the function result for CatchSignal)
            ADD     #nSavedRegs*4,A0            ;advance pointer by # bytes regs
        
            CLR.W   (A0)+                       ;clear error field
            CLR.L   (A0)+                       ;clear message field
            CLR.L   (A0)+                       ;clear A6 link
            
            TST.W   D1                          ;was CatchFailures used?
            BPL.S   @2                          ;no, CatchCFailures or CatchSignal; skip
            MOVE.L  A6,-4(A0)                   ;save the A6Link
@2
            MOVE.L  A1,(A0)+                    ;save the handler ptr
        
            MOVE.L  gTopHandler(A5),(A0)+       ;Link the FailInfo into the list
            MOVE.L  D0,gTopHandler(A5)
            MOVE.L  D2,(A0)+                    ;remember the caller's PC for MA debugging
 
            MOVE.L  D2,A0                       ;get return address
            JMP     (A0)
            Tail    'CATCHSIG'
 
            ALIGN
SiggyPop
            Head
            MOVE.L  D0,-(A7)                    ;save D0 in case weÕre returning from a c function
 
            MOVE.L  SigPointer,A0
            MOVE.L  A0,D0                       ;to set CCR
            BEQ     SigDeath                    ;nil pointer means trouble
            MOVE.L  SigNow,D0                   ;grab table offset to entry
            BMI     SigDeath                    ;if no entries then give up
            ADD.L   D0,A0                       ;point to current element
            
            MOVE.L  SigFRet(A0),-(A7)           ;push proc's real return address
            BSR     SiggyPop2                   ;pop element and call Success to pop handler
            MOVE.L  (A7)+,A1                    ;get procÕs return address
            MOVE.L  (A7)+,D0                    ;get back D0 in case weÕre returning from c
            JMP     (A1)
            Tail    'SIGGYPOP'
            
            ENDP
 
 
 
 
        If  &UsingMacApp Then
            Seg     'MAMain'
        EndIf
FreeSignal  PROC    EXPORT                      ;PROCEDURE FreeSignal;
            IMPORT  SigDeath,Success
            IMPORT  gTopHandler:Data
            EXPORT  SiggyPop2
            WITH    SigGlobals
            
            Head
            MOVE.L  gTopHandler(A5),A0          ;point to FailInfo record
            MOVE.L  A0,D0                       ;to set CCR
            BEQ.S   SigDeath
            
            TST.W   whatSignals(A0)             ;was CatchSignal used?
            BLE.S   SiggyPop3                   ;no, call Success and return
            
            MOVE.L  SigNow,D0                   ;grab table offset to entry
            
            MOVE.L  SigA6(A0),A1                ;get A6 at point of CatchSignal
            CMP.L   #SigBigA6,A1                ;is it at the outer level?
            BEQ.S   SiggyPop2                   ;yes, don't jam the address if no frame 
            MOVE.L  SigFRet(A0),FrameRet(A1)    ;"pop" cleanup code
SiggyPop2
            SUB.L   #SigElSize,D0
            MOVE.L  D0,SigNow                   ;"pop" the entry
SiggyPop3
            MOVE.L  A0,-(A7)                    ;push address for Success (FailInfoÕs)
            JSR     Success                     ;let MacApp remove FailInfo from list; return
            RTS
            Tail    'FREESIGN'
            
            ENDP
 
        If  &UsingMacApp Then
            Seg     'MAMain'
        EndIf
SignalMessage PROC  EXPORT                      ;PROCEDURE SignalMessage(code: INTEGER; message: LONGINT);
            IMPORT  SignalEntry
            WITH    SigGlobals
 
            Head
            MOVE.L  (A7)+,A1                    ;get return address 
            MOVE.L  (A7)+,D0                    ;get message
            BRA.S   SignalEntry
            Tail    'SIGNALME'
 
Signal      PROC    EXPORT                      ;PROCEDURE Signal(code:INTEGER);
            IMPORT  Failure
            EXPORT  SigDeath,SignalEntry
code        EQU     4
            WITH    SigGlobals
 
            Head
            MOVE.L  (A7)+,A1                    ;get return address 
            CLR.L   D0                          ;message := 0
SignalEntry
            MOVE.W  (A7)+,D1                    ;get code
            BNE.S   @0                          ;process the signal if code is non-zero
            JMP (A1)                            ;return to caller (code was 0)
@0
            MOVE.W  D1,-(A7)                    ;push code
            MOVE.L  D0,-(A7)                    ;push message
            JSR     Failure                     ;this will pop the handler and call SignalFailure
SigDeath
            _Debugger                           ;wasnÕt supposed to come backÉ
            Tail    'SIGNAL  '
 
            ENDP
 
        If  &UsingMacApp Then
            Seg     'MAMain'
        EndIf
DoFailure   PROC    EXPORT                      ;PROCEDURE DoFailure(VAR fi: FailInfo);
            IMPORT  Failure
fi          EQU     4
            
            Head
            BSR     FreeSignal                  ;pop this guy off (but the record is still valid)
            
            MOVE.L  fi(A7),A0                   ;get FailInfo record
            
            MOVEM.L (A0),A2-A7/D3-D7            ;restore regs (can't use A7 anymore)
            ADD     #nSavedRegs*4,A0            ;advance pointer
        
            MOVE.W  (A0)+,D0                    ;get error
        
            TST.W   whatSignals-nSavedRegs*4-2(A0)  ;was CatchSignal used?
            BGT.S   @2                          ;yes, skip
            
            MOVE.L  (A0)+,D1                    ;get message
            MOVEM.L D0/D1,-(SP)                 ;save error and message for later use
 
            MOVE.W  D0,-(SP)
            MOVE.L  D1,-(SP)                    ;parameters to failure handler
                
            MOVE.L  (A0)+,D0                    ;check the A6Link
            BEQ.S   @1                          ;if NIL then don't pass it
            MOVE.L  D0,-(SP)                    ;else do pass it
@1
            MOVE.L  (A0)+,A0                    ;get address of failure handler
        
            JSR     (A0)                        ;call failure handler
            
            MOVEM.L (SP)+,D0/D1                 ;get error & message back
            MOVE.W  D0,-(SP)
            MOVE.L  D1,-(SP)                    ;parameters to Failure
            JSR     Failure
            _Debugger                           ;not supposed to fail to fail
            
@2
            MOVE.W  D0,(A7)                     ;set CatchSignal function result
            MOVE.L  8(A0),A0                    ;address of the failure handler-skip msg. & A6 link
            JMP     (A0)                        ;go to point of CatchSignal
            Tail    'DOFAILUR'
            
            ENDP
                    
 
            END