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