* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* |_o_o|\\  The Software Distillery					*
* |. o.| || Made available to the Amiga development community		*
* | .  | || the author:					     BBS:	*
* | o  | ||   Jim Cooper				(919)-471-6436  *
* |  . |//								*
* ======								*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Original code:
*	'c.a'		by Lattice, Inc.
*
* Modifications:
*	'catch.a'	by Jim Cooper, of The Software Distillery
*	'catchnr.a'	by Jim Cooper, of The Software Distillery
*
* C initial startup procedure under AmigaDOS
*
*************************************************************************
* If you are using the Lattice Assembler:
*
* Use the following command line to make c.o
* asm -u -iINCLUDE: -oc.o catch.a
*
* Use the following command line to make catch.o
* asm -u -iINCLUDE: -dCATCH -dAUTOREQ catch.a
*
* Use the following command line to make catchnr.o
* asm -u -iINCLUDE: -dCATCH -ocatchnr.o catch.a
*
*************************************************************************
*
* If you are using the CAPE Assembler from Inovatronics, or just Assem from
* MetaComCo, you must remove the asterisk from the 'set' lines below to
* obtain the output version of the program you desire.
*
* For example:
*
* To generate 'c.o', don't make any changes.
*
* To generate 'catch.o, remove the asterisk from the lines with CATCH and
*  AUTOREQ.
*
* To generate 'catchnr.o' remove the asterisk from the line with CATCH.
*
* Also, if using CAPE, you can edit the string on the OBJFILE line to
* represent the version of the file you are generating.
*
*************************************************************************
*

	ifnd	CAPE
	INCLUDE "exec/types.i"
	INCLUDE "exec/alerts.i"
	INCLUDE "exec/nodes.i"
	INCLUDE "exec/lists.i"
	INCLUDE "exec/ports.i"
	INCLUDE "exec/libraries.i"
	INCLUDE "exec/tasks.i"

	INCLUDE "libraries/dos.i"
	endc

	INCLUDE "exec/memory.i"
	INCLUDE "exec/execbase.i"
	INCLUDE "libraries/dosextens.i"
	INCLUDE "workbench/startup.i"
	INCLUDE "intuition/intuition.i"

*************************************************************************
* Library offsets - DOS

_LVOOpen		equ	-30
_LVOClose		equ	-36
_LVORead		equ	-42
_LVOWrite		equ	-48
_LVOSeek		equ	-66
_LVOCurrentDir		equ	-126

* Library offsets - EXEC

_LVOForbid		equ	-132
_LVOPermit		equ	-138
_LVOAllocMem		equ	-198
_LVOFreeMem		equ	-210
_LVOAvailMem		equ	-216
_LVOSetSignal		equ	-306
_LVOGetMsg		equ	-372
_LVOReplyMsg		equ	-378
_LVOWaitPort		equ	-384
_LVOCloseLibrary	equ	-414
_LVOOpenLibrary		equ	-552
_LVOGetCC		equ	-528

* Library offsets - Intuition

_LVOAutoRequest		equ	-348

*************************************************************************


	ifd	CAPE
	 CSYMFMT
**** edit the following line to reflect your choices
	 OBJFILE	'c.o'
	 OPTIMON	-1
	endc


VERSION 	equ	1
REVISION	equ	0

*CATCH		set	1
*AUTOREQ 	set	1


MEMFLAGS	EQU	MEMF_CLEAR+MEMF_PUBLIC
AbsExecBase	EQU	4

Call	macro
	jsr	_LVO\1(a6)
	endm

	xdef	XCEXIT		; exit(code) is standard way to leave C.
	ifnd	CAPE
	 xdef 	@XCEXIT
	endc

* I am using a BETA of CAPE 2.0, and it doesn't like '@' in symbol names.
* This means I have to kludge and use FileZap on the output file.
	ifd	CAPE
	 xdef	RCEXIT
	endc

	xref	LinkerDB	; linker defined base value
	xref	_BSSBAS 	; linker defined base of BSS
	xref	_BSSLEN 	; linker defined length of BSS

*	library references

	section	text,code

	xref	_main		; Name of C program to start with.
	xref	MemCleanup
	xref	__fpinit	; initialize floating point
	xref	__fpterm	; terminate floating point

start:
	movem.l	d1-d6/a0-a6,-(a7)
REGSIZE EQU	(6+7)*4
	lea	REGSIZE(a7),A5		; determine old stack pointer
	move.l	a0,a2			; save command pointer
	move.l	d0,d2			; and command length
	lea	LinkerDB,a4		; load base register
	
	lea	_BSSBAS,a3		; get base of BSS
	moveq	#0,d1
	move.l	#_BSSLEN,d0		; get length of BSS in longwords
	bra.s	clr_lp			; and clear for length given
clr_bss move.l	d1,(a3)+
clr_lp	dbf	d0,clr_bss
	
	move.l	AbsExecBase.W,a6
	move.l	a6,SysBase(A4)

	ifd	CATCH
	 move.w	AttnFlags(a6),Environment+2(a4) ; save copy for dump
	endc

	move.l	a7,_StackPtr(A4)	; Save stack ptr
	clr.l	WBenchMsg(A4)

*------ attempt to open DOS library:
	bsr.w	openDOS

	ifd	CATCH
	 ifd	AUTOREQ
*------ attempt to open Intuition library:
	  bsr.w	openIntui
	 endc
	endc

*-----	clear any pending signals
	moveq	#0,d0
	move.l	#$00003000,d1
	Call	SetSignal

*------ get the address of our task
	move.l	ThisTask(a6),A3

	ifd	CATCH
	 move.l	A3,TaskID(a4)

*------ initialize exception handler
*------ Remember to preserve the old handler first
	 move.l	TC_TRAPDATA(a3),oldtrapdata(A4)
	 move.l	TC_TRAPCODE(a3),d0	; check current exception
	 move.l	d0,oldtrapcode(A4)
	 swap	d0
	 cmpi.w	#$fb,d0 		; see if points to ROM
	 ble.s	1$			; somebody else (debugger?) has vector
	 move.l	#Exception,TC_TRAPCODE(a3)	; install pointers to code
	 move.l	a4,TC_TRAPDATA(a3)	; ...and data
1$:
	endc

*------ are we running as a son of Workbench?
	move.l	pr_CurrentDir(A3),curdir(A4)
	tst.l	pr_CLI(A3)
	beq	fromWorkbench

*=======================================================================
*====== CLI Startup Code ===============================================
*=======================================================================
*
* Entry: D2 = command length
*	A2 = Command pointer
fromCLI:
	ifd	CATCH
	 moveq	#-1,d0
	 move.l	d0,Starter(a4)		; non-zero means CLI
	 move.l	a5,StackTop(a4)
	endc

	move.l	a5,D0			; get top of stack
	sub.l	4(a5),D0		; compute bottom
	add.l	#128,D0 		; allow for parms overflow
	move.l	D0,_base(A4)		; save for stack checking

*------ find command name:
	move.l	pr_CLI(a3),a0
	add.l	a0,a0			; bcpl pointer conversion
	add.l	a0,a0
	move.l	cli_CommandName(a0),a1
	ifd	CATCH
	IFND	AUTOREQ
	 move.l	cli_StandardOutput(a0),GConsole(a4)	; save output fh
	ENDC
	endc
	add.l	a1,a1			; bcpl pointer conversion
	add.l	a1,a1

*------ collect parameters:
	move.l	d2,d0			; get command line length
	moveq.l	#0,d1
	move.b	(a1)+,d1
	move.l	a1,_ProgramName(A4)
	add.l	d1,d0			; add length of command name
	addq.l	#1,d0			; allow for space after command

	clr.w	-(A7)			; set null terminator for command line
	addq.l	#1,D0			; force to even number of bytes
	andi.w	#$fffe,D0		;(round up)
	sub.l	D0,A7			; make room on stack for command line
	subq.l	#2,D0

	clr.w	0(A7,D0)

*------ copy command line onto stack
	move.l	d2,d0			; get command line length
	subq.l	#1,d0
	add.l	d1,d2

copy_line:
	move.b	0(A2,D0.W),0(A7,D2.W)	; copy command line to stack
	subq.l	#1,d2
	dbf	d0,copy_line
	move.b	#' ',0(a7,d2.w)		; add space between command and parms
	subq.l	#1,d2

copy_cmd:
	move.b	0(a1,d2.w),0(a7,d2.w)	; copy command name to stack
	dbf	d2,copy_cmd
	move.l	A7,A1
	move.l	A1,-(A7)		; push command line address
	bra.w	main			; call C entrypoint

*=======================================================================
*====== Workbench Startup Code =========================================
*=======================================================================

fromWorkbench:
	move.l	TC_SPLOWER(a3),_base(A4)	; set base of stack
	add.l	#128,_base(A4)		; allow for parms overflow

	ifd	CATCH
	 move.l	TC_SPUPPER(a3),StackTop(a4)	; set top of stack
	endc

*------ we are now set up.  wait for a message from our starter
	lea	pr_MsgPort(A3),a0	; our process base
	Call	WaitPort
	lea	pr_MsgPort(A3),a0	; our process base
	Call	GetMsg
	move.l	d0,WBenchMsg(a4)
	move.l	d0,-(SP)
*
	move.l	d0,a2			; get first argument
	move.l	sm_ArgList(a2),d0
	beq.s	do_cons
	move.l	DOSBase(a4),a6
	move.l	d0,a0
	move.l	wa_Lock(a0),d1
	move.l	d1,curdir(A4)
	Call	CurrentDir
do_cons:
	move.l	sm_ToolWindow(a2),d1	; get the window argument
	beq.s	do_main
	move.l	#MODE_OLDFILE,d2
	Call	Open
	move.l	d0,stdin(a4)
	beq.s	do_main
	lsl.l	#2,d0
	move.l	d0,a0
	move.l	fh_Type(a0),pr_ConsoleTask(A3)
do_main:
	move.l	WBenchMsg(A4),a0	; get address of workbench message
	move.l	a0,-(a7)		; push argv
	pea	NULL(a4)		; push argc
	move.l	sm_ArgList(a0),a0	; get address of arguments
	move.l	wa_Name(a0),_ProgramName(A4)	; get name of program

*=============================================
*------ common code --------
*=============================================

main	jsr	__fpinit(PC)		; Initialize floating point
	jsr	_main(PC)		; call C entrypoint
	moveq.l	#0,d0			; set successful status
	bra.s	exit2
*

XCEXIT:
	move.l	4(SP),d0		; extract return code
RCEXIT:
exit2:
	move.l	d0,-(a7)

	move.l	_ONEXIT(A4),d0		; exit trap function?
	beq.s	exit3
	move.l	d0,a0
	jsr	(a0)
exit3:
	jsr	MemCleanup(PC)		; cleanup leftover memory alloc.

	move.l	AbsExecBase.W,a6

*------ Restore the original exception handler
	move.l	ThisTask(a6),A3
	ifd	CATCH
	 move.l	oldtrapdata(A4),TC_TRAPDATA(a3)
	 move.l	oldtrapcode(A4),TC_TRAPCODE(a3)	; check current exception
	endc

	move.l	DOSBase(A4),a1
	Call	CloseLibrary		; close Dos library

	ifd	CATCH
	 ifd	AUTOREQ
	  move.l	IntuiBase(a4),a1
	  Call	CloseLibrary		; close Intuition library
	 endc
	endc

	jsr	__fpterm(PC)		; clean up any floating point

done_1c:
*------ if we ran from CLI, skip workbench cleanup:
	tst.l	WBenchMsg(A4)
	beq.s	exitToDOS
	move.l	stdin(A4),d1
	beq.s	done_4
	Call	Close
done_4:

*------ return the startup message to our parent
*	we forbid so workbench can't UnLoadSeg() us
*	before we are done:
	move.l	AbsExecBase.W,A6
	Call	Forbid
	move.l	WBenchMsg(a4),a1
	Call	ReplyMsg

*------ this rts sends us back to DOS:
exitToDOS:
	MOVE.L	(A7)+,D0
	movea.l	_StackPtr(a4),SP	; restore stack ptr
	movem.l	(a7)+,d1-d6/a0-a6
	rts

	
*-----------------------------------------------------------------------
noDOS:
	moveq.l	#100,d0
	bra.w	exit2


*-----------------------------------------------------------------------
*	Open the DOS library:

openDOS:
	lea	DOSName(PC),A1
	moveq.l	#0,D0
	Call	OpenLibrary
	move.l	D0,DOSBase(A4)
	beq.s	noDOS
	rts
DOSName:
 	dc.b	'dos.library',0

	ifd	CATCH
*-----------------------------------------------------------------------
*	Open the Intuition library:

openIntui:
	lea	IntuiName(PC),A1
	moveq.l	#0,D0
	Call	OpenLibrary
	move.l	D0,IntuiBase(A4)
	beq.s	noDOS
	rts
IntuiName:
	dc.b	'intuition.library',0

*-----------------------------------------------------------------------
*	The Exception Handler - catches GURUs and exits (semi)cleanly
Exception:
	move.l	AbsExecBase.W,a0
	move.l	ThisTask(a0),a0
	move.l	TC_TRAPDATA(a0),a0	; ...and data

	move.l	(a7)+,d0		; get exception # from stack
	move.l	d0,GURUNum(a0)		; and save it
	cmpi.l	#3,d0			; ADDRESS or BUS error?
	bgt.s	2$			; no, skip adjustment
	btst	#0,Environment+3(a0)	; is it 68010 or 68020?
	beq.s	1$			; 0 means NO
	bset	#7,8(a7)		; set Rerun flag
	bra.s	2$
1$:
	addq.l	#8,a7			; adjust for 68000
2$:
	move.l	2(a7),d0		; get PC at crash
	move.l	d0,GURUAddr(a0)		; and save it
	move.l	#GURUExit,2(a7)		; use our own exit point
	rte

*-----------------------------------------------------------------------
*	The Exception exit routine - write 'PGTB' IFF FORM to file
*	'SnapShot.TB' in current directory, then exit to system.

GURUExit:
	movem.l	d0-d7/a0-a7,-(sp)	; save all registers
	move.l	AbsExecBase.W,a6	; make sure we are working with Exec
	Call	GetCC			; safe way - works with all CPUs
	move.l	ThisTask(a6),a3
	move.l	TC_TRAPDATA(a3),a4	; make sure we have a valid # in a4
	move.l	d0,Flags(a4)		; save area
	movem.l	(sp)+,d0-d7
	movem.l	d0-d7,DDump(a4)		; save data reg contents
	movem.l	(sp)+,d0-d7
	movem.l	d0-d7,ADump(a4)		; save address reg contents
	tst.l	StackPtr(a4)		; if there's something there
	bne	GExit1			; ...we've been here before!
	lea	TempStore(a4),a0	; calculate addr of TempStore
	move.l	a0,TempAddr(a4)		; ...and save for later access
	move.l	A7Store(a4),d0		; make sure we have proper TOS
	move.l	d0,StackPtr(a4)		; ...and save it
	moveq	#0,d0
	move.l	_ProgramName(a4),a0	; find length of program name

	ifd	AUTOREQ
	 move.l	a0,PName(a4)
	endc

	subq.l	#1,a0
	move.b	(a0),d0
	addq.l	#4,d0			; adjust for shift
	lsr.l	#2,d0
	move.l	d0,NameLen(a4)		; store length
	add.l	d0,FAILlen(a4)		; and sub-chunk total

	moveq	#0,d0			; clear d0 for use
	lea	VBlankFrequency(a6),a0	; set up a0 to find correct data
	move.b	(a0)+,d0		; get just in case
	move.l	d0,VBlankFreq(a4)	; ...so we can figure what
	move.b	(a0),d0			; ...type of machine
	move.l	d0,PowerSupFreq(a4)	; ...we're working on

	lea	start-4(pc),a0		; get seglist ptr
	moveq	#-1,d0			; always at least 1
2$:
	addq.l	#1,d0
	move.l	(a0),d1			; find end of list
	beq.s	3$
	lsl.l	#2,d1			; BPTR!!!!!
	move.l	d1,a0
	bra.s	2$
3$:
	add.l	d0,SegCount(a4)		; store # of seglist pointers
	lsl.l	#1,d0			; multiply by 2 for longword count
	add.l	d0,FAILlen(a4)		; and sub-chunk length

	move.l	StackTop(a4),d0		; get top of stack
	sub.l	StackPtr(a4),d0		; find number of bytes used
	addq.l	#4,d0			; adjust for longword conversion
	lsr.l	#2,d0			; convert from bytes to long
	move.l	d0,StackLen(a4)		; and save
	add.l	d0,s2len(a4)		; and sub-chunk total

	move.l	a5,-(sp)		; save a5 for later
	Call	Forbid			; don't let 'em change while we ask
	move.l	MemList+LH_HEAD(a6),a0	; first node in MemList
checkchip:
	move.w	MH_ATTRIBUTES(a0),d4	; get node attributes
	btst	#MEMB_CHIP,d4		; is it chip?
	beq.s	checkfast		; no, go on
	lea	chipAvail(a4),a3
	bsr.w	AddIt
checkfast:
	btst	#MEMB_FAST,d4		; is it fast?
	beq.s	next			; no, go on
	lea	fastAvail(a4),a3
	bsr.w	AddIt
next:
	tst.l	LN_SUCC(a0)
	move.l	LN_SUCC(a0),a0		; get address of next node
	bne.s	checkchip		; ...and loop back if valid
	Call	Permit			; allow others access again
	move.l	#MEMF_CHIP+MEMF_LARGEST,d1 ; to find largest hunk in chip ram
	Call	AvailMem
	move.l	d0,chipLargest(a4)	; store
	move.l	#MEMF_FAST+MEMF_LARGEST,d1 ; to find largest hunk in fast ram
	Call	AvailMem
	move.l	d0,fastLargest(a4)	; store
	move.l	(sp)+,a5		; and restore a5

	ifd	AUTOREQ
	moveq	#0,d0			; PosFlag
	move.l	d0,d1			; NegFlag
	move.l	d0,a0			; 0 means use current window
	lea	IText1(a4),a1		; Body Text
	lea	IText5(a4),a2		; Positive Gadget Text
	lea	IText6(a4),a3		; Negative Gadget Text
	moveq	#1,d2
	lsl.l	#8,d2			; quick way to set Width
	moveq	#76,d3			; Height
	move.l	IntuiBase(a4),a6	; get intuition library pointer
	Call	AutoRequest
	move.l	AbsExecBase.W,a6
	tst.l	d0			; save SnapShot?
	beq	GExit2			; no, just exit
	endc

	move.l	DOSBase(a4),a6
	lea	DumpName(a4),a0		; get name of output file
	move.l	a0,d1
	move.l	#MODE_NEWFILE,d2	; create new file
	Call	Open
	bne.s	4$
	lea	DumpPath(a4),a0		; if error in current dir, try DF1:
	move.l	a0,d1
	move.l	#MODE_NEWFILE,d2
	Call	Open
	bne.s	4$
	move.b	#'0',DumpPath+3(a4)	; still error?	Try DF0:
	lea	DumpPath(a4),a0
	move.l	a0,d1
	move.l	#MODE_NEWFILE,d2
	Call	Open

	ifnd	AUTOREQ
	bne.s	4$			; if no error, continue (finally!)
	move.l	GConsole(a4),d1
	beq.w	GExit2
	lea	failmsg(a4),a0
	move.l	a0,d2
	move.l	#23,d3
	Call	Write
	endc

	bra.w	GExit2			; else, print msg & DIE gracefully
4$:
	move.l	d0,d5			; save file handle for Write
	move.l	d0,fp(a4)		; ...and in a safe place for later
	move.l	d5,d1			; get file handle
	lea	PGTB(a4),a0		; first part of fixed
	move.l	a0,d2
	move.l	#chunk_len_1,d3 	; length of first
	Call	Write			; ...since it gets written over

	move.l	d5,d1			; get file handle
	move.l	_ProgramName(a4),d2	; get address of program name
	move.l	NameLen(a4),d3		; get # longs in program name
	lsl.l	#2,d3			; ..and convert to bytes
	Call	Write

	move.l	d5,d1			; get file handle
	lea	Environment(a4),a0	; second part of fixed
	move.l	a0,d2
	move.l	#chunk_len_2,d3 	; length of second part
	Call	Write

	lea	start-8(pc),a0		; address of seglist (size of seg)
	move.l	(a0)+,d0		; segsize
	move.l	d0,TempStore+4(a4)	; save it
	move.l	a0,TempStore(a4)	; store first number
	move.l	SegCount(a4),d4
5$:
	move.l	d5,d1			; get file handle
	move.l	TempAddr(a4),d2		; address of write buffer
	moveq	#TempSize,d3		; size of segment pointer
	Call	Write
	move.l	TempStore(a4),a0	; retrieve pointer
	move.l	(a0),d0			; get next seg pointer
	lsl.l	#2,d0			; adjust
	move.l	d0,TempStore(a4)	; ..and save
	move.l	d0,a0
	move.l	-4(a0),d0		; get segsize
	move.l	d0,TempStore+4(a4)	; ...and save it
	subq.l	#1,d4			; done yet?
	bne.s	5$			; no, do next

	tst.l	_FMEM(a4)		; do they want memory reported?
	beq.s	55$			; no, forget it
	move.l	d5,d1
	lea	subFMEM(a4),a0
	move.l	a0,d2
	move.l	#FMEMlen,d3
	Call	Write

55$:
	move.l	d5,d1			; (get the idea?)
	lea	subREGS(a4),a0		; third part of fixed
	move.l	a0,d2
	move.l	#chunk_len_3,d3 	; length of third
	Call	Write

	move.l	StackLen(a4),d0		; get length of stack used
	cmpi.l	#2048,d0		; > 8k ?
	bgt.s	6$			; yes, dump two chunks
	move.l	d5,d1
	lea	STAK2(a4),a0		; whole stack chunk
	move.l	a0,d2
	moveq	#STAK2len,d3		; length of fixed part
	Call	Write

	move.l	d5,d1
	move.l	StackPtr(a4),d2		; address of stack
	move.l	StackLen(a4),d3		; get # longwords on stack
	lsl.l	#2,d3			; ..and convert to bytes
	Call	Write
	bra.s	7$
6$:
	move.l	d5,d1
	lea	STAK3(a4),a0		; top4k chunk
	move.l	a0,d2
	moveq	#STAK3len,d3		; length of fixed part
	Call	Write

	move.l	d5,d1
	move.l	StackTop(a4),d2		; find top of stack
	sub.l	#4096,d2		; find top-4k
	move.l	#4096,d3		; # bytes to write
	Call	Write

	move.l	d5,d1
	lea	STAK4(a4),a0		; bottom4k chunk
	move.l	a0,d2
	moveq	#STAK4len,d3		; length of fixed part
	Call	Write

	move.l	d5,d1
	move.l	StackPtr(a4),d2		; current stack address
	move.l	#4096,d3		; # bytes to write
	Call	Write
7$:
	move.l	_STAKOffset(a4),d3
	beq.s	8$
	lsr.l	#2,d3
	addq.l	#1,d3
	move.l	d3,_STAKOffset(a4)
	addq.l	#1,d3
	move.l	d5,d1
	lea	STAK5(a4),a0
	move.l	a0,d2
	moveq	#STAK5len,d3
	Call	Write

	move.l	d5,d1
	move.l	StackPtr(a4),d2
	move.l	_STAKOffset(a4),d3
	subq.l	#1,d3
	move.l	StackLen(a4),d4
	cmp.l	d3,d4
	bge.s	75$
	move.l	StackLen(a4),d3
75$:
	lsl.l	#2,d3
	Call	Write
8$:
	tst.l	_ONGURU(A4)		; user GURU function?
	beq.s	9$
	move.l	d5,-(sp)
	move.l	d5,d1
	lea	UDAT(a4),a0
	move.l	a0,d2
	move.l	#UDATlen,d3
	Call	Write
	move.l	d5,d1
	moveq	#0,d2			; zero offset
	moveq	#1,d3			; ...from EOF
	Call	Seek
	move.l	d0,SeekStore(a4)
	move.l	_ONGURU(a4),a0
	jsr	(a0)
	addq.l	#4,sp
9$:
	move.l	fp(a4),d5
	move.l	d5,d1
	moveq	#0,d2			; offset from EOF
	moveq	#1,d3			; OFFSET_END
	Call	Seek			; Seek returns OLD position
	move.l	d0,d1
	andi.l	#3,d1			; did user write even longwords?
	beq.s	10$			; Yep!	Nice Human.
	move.l	d1,d6			; Nope, save for later.
	clr.l	TempStore(a4)		; clear temp storage
	move.l	d5,d1
	move.l	TempAddr(a4),d2
	moveq	#4,d3
	sub.l	d6,d3			; find how many NULLs to pad
	Call	Write
	bra.s	9$
10$:
	tst.l	SeekStore(a4)		; did we write UDAT?
	beq.s	11$			; nope!
	sub.l	SeekStore(a4),d0	; find length of UDAT section
	lsr.l	#2,d0			; adjust to longwords
	move.l	d0,TempStore(a4)	; save UDAT length for write
	move.l	d5,d1
	move.l	SeekStore(a4),d2	; find where to write it
	subq.l	#4,d2
	moveq	#-1,d3			; OFFSET_BEGINNING
	Call	Seek
	move.l	d5,d1
	move.l	TempAddr(a4),d2
	move.l	#4,d3
	Call	Write			; write length of UDAT field to file
11$:
	move.l	d5,d1
	moveq	#0,d2
	moveq	#1,d3			; OFFSET_END
	Call	Seek			; make sure we are at end of file
	move.l	d5,d1
	moveq	#0,d2
	moveq	#-1,d3			; OFFSET_BEGINNING
	Call	Seek
	subq.l	#8,d0			; adjust total length
	lsr.l	#2,d0			; adjust to longwords
	move.l	d0,formLength(a4)	; and store FORM length
	subq.l	#2,d0			; adjust for PGTB chunk length
	move.l	d0,Length(a4)		; and store it
	move.l	d5,d1
	move.l	PGTB(a4),d2
	move.l	#16,d3
	Call	Write			; write revised FORM header
GExit1:
	move.l	fp(a4),d1
	beq.s	GExit2
	move.l	DOSBase(a4),a6
	Call	Close

	ifnd	AUTOREQ
	move.l	GConsole(a4),d1
	beq.s	GExit2
	lea	success(a4),a0
	move.l	a0,d2
	move.l	#32,d3
	Call	Write
	endc
GExit2:
	move.l	TaskID(a4),a6
	move.l	AbsExecBase.W,a6
	moveq	#$47,d0
	bra.w	exit2

*-----------------------------------------------------------------------
* AddIt:	routine to add memory parts to variables

AddIt:
	move.l	MH_FREE(a0),d0
	add.l	d0,(a3)			; add to available
	move.l	MH_UPPER(a0),d0
	sub.l	MH_LOWER(a0),d0
	add.l	d0,4(a3)		; add to Max section
	rts
	endc

	
	section	__MERGED,DATA
*
	xdef	NULL,SysBase,WBenchMsg
	xdef	curdir,_mbase,_mnext,_msize,_tsize
	xdef	_oserr,_OSERR,_FPERR,_SIGFPE,_ONERR,_ONEXIT,_ONBREAK
	xdef	_SIGINT
	xdef	_ProgramName,_StackPtr,_base

	ifd	CATCH
	xdef	_ONGURU,_FMEM,_STAKOffset
	endc
*
NULL		dc.l	0		;
_base		dc.l	0		; base of stack
_mbase		dc.l	0		; base of memory pool
_mnext		dc.l	0		; next available memory location
_msize		dc.l	0		; size of memory pool
_tsize		dc.l	0		; total size?
_oserr:
_OSERR		dc.l	0
_FPERR		dc.l	0
_SIGFPE 	dc.l	0
_SIGINT 	dc.l	0
_ONERR		dc.l	0
_ONEXIT 	dc.l	0
_ONBREAK	dc.l	0
curdir		dc.l	0
SysBase 	dc.l	0
DOSBase		dc.l	0
WBenchMsg	dc.l	0
_StackPtr	dc.l	0
stdin		dc.l	0
_ProgramName	dc.l	0

	ifd	CATCH
oldtrapcode	dc.l	0
oldtrapdata	dc.l	0
_ONGURU 	dc.l	0
IntuiBase	dc.l	0
TaskID		dc.l	0
	ifnd	AUTOREQ
GConsole	dc.l	0
failmsg 	dc.b	7,'Can''t write SnapShot!',10
success 	dc.b	7,'GURU caught; SnapShot written!',10
	endc
	cnop	0,4
_FMEM		dc.l	0
fp		dc.l	0	; save SnapShot file pointer
DumpPath	dc.b	'DF1:'
DumpName	dc.b	'SnapShot.TB',0
SeekStore	dc.l	0
TempAddr	dc.l	0	; Storage for &TempStore
TempStore	dc.l	0,0	; Temporary storage for BPTR -> APTR
TempSize	equ	*-TempStore

	cnop	0,4
	ifd	AUTOREQ
TAttr:				; Text attributes for font
	dc.l	TName		; name of font
	dc.w	TOPAZ_EIGHTY	; font size
	dc.b	FS_NORMAL	; font style
	dc.b	FPF_ROMFONT	; font preferences
TName:
	dc.b	'topaz',0
	cnop	0,4

IText1: 			; Text definitions for AutoReq call
	dc.b	3,0,RP_JAM1,0	; front & back pens, drawmode and filler byte
	dc.w	6,4		; XY origin relative to container TopLeft
	dc.l	TAttr		; font pointer or NULL for default
	dc.l	ITextText1	; pointer to text
	dc.l	IText2		; next IntuiText structure
ITextText1:
	dc.b	'Program:',0
	cnop 0,4
IText2:
	dc.b	3,0,RP_JAM1,0
	dc.w	78,4
	dc.l	TAttr
PName	dc.l	0
	dc.l	IText3
	cnop	0,4
IText3:
	dc.b	3,0,RP_JAM1,0
	dc.w	55,16
	dc.l	TAttr
	dc.l	ITextText3
	dc.l	IText4
ITextText3:
	dc.b	'I caught a GURU!',0
	cnop 0,4
IText4:
	dc.b	3,0,RP_JAM1,0
	dc.w	20,28
	dc.l	TAttr
	dc.l	ITextText4
	dc.l	NULL
ITextText4:
	dc.b	'Should I make a SnapShot?',0
	cnop 0,4
IText5:
	dc.b	3,0,RP_JAM1,0
	dc.w	6,4
	dc.l	TAttr
	dc.l	ITextText5
	dc.l	NULL
ITextText5:
	dc.b	'YES',0
	cnop 0,4
IText6:
	dc.b	3,0,RP_JAM1,0
	dc.w	7,4
	dc.l	TAttr
	dc.l	ITextText6
	dc.l	NULL
ITextText6:
	dc.b	'NO',0
	endc

	cnop 0,4

*--------------------------------------------------------------------------
* New IFF chunk format -
*	PGTB = Program Traceback, header for chunk
*	FAIL = reason for and environment of crash
*	REGS = registers at time of crash, including PC and CCR
*	VERS = version, revision, name of this program
*	STAK = ENTIRE stack at time of crash or, alternately,
*	the top and bottom 4k if the stack used is > 8k
*	UDAT = optional user data dump (if _ONGURU is set to a
*	function pointer in the user's program)
*--------------------------------------------------------------------------

PGTB		dc.b	'FORM'		; required IFF header
formLength	dc.l	0

		dc.b	'PGTB'
Length		dc.l	0		; length of chunk (in longwords)

subFAIL 	dc.b	'FAIL'
FAILlen 	dc.l	9
NameLen 	dc.l	0		; length of program name
chunk_len_1	equ	*-PGTB
Environment	dc.l	0		; CPU (, Math)
VBlankFreq	dc.l	0		;	PAL = 50, NTSC = 60 (approx.)
PowerSupFreq	dc.l	0		; Europe = 50,	USA = 60 (approx.)
Starter 	dc.l	0		; 0 = WB, -1 = CLI
GURUNum 	dc.l	0		; cause of crash (GURU #)
SegCount	dc.l	1		; # hunks in seglist
chunk_len_2	equ	*-Environment

subFMEM 	dc.b	'FMEM'		; FMEM - free memory at crash
		dc.l	6
chipAvail	dc.l	0		; available chip memory
chipMax 	dc.l	0		;	maximum	chip memory
chipLargest	dc.l	0		;	largest	chip memory
fastAvail	dc.l	0		; available fast memory
fastMax 	dc.l	0		;	maximum	fast memory
fastLargest	dc.l	0		;	largest	fast memory
FMEMlen 	equ	*-subFMEM

subREGS 	dc.b	'REGS'		; REGS - register storage field
REGSlen 	dc.l	18
GURUAddr	dc.l	0		; PC at time of crash
Flags		dc.l	0		; Condition Code Register (CCR)
DDump		dc.l	0,0,0,0,0,0,0,0	; data registers
ADump		dc.l	0,0,0,0,0,0,0	; address registers
A7Store 	dc.l	0

subVERS 	dc.b	'VERS'		; VERS - program version field
		dc.l	6
		dc.l	VERSION 	; version #
		dc.l	REVISION	; revision #
		dc.l	3		; length of name of program
		ifd	AUTOREQ
		 dc.b	'catch.o',0,0,0,0,0 	; name
		endc
		ifnd	AUTOREQ
		 dc.b	'catchnr.o',0,0,0	; name
		endc

subSTAK 	dc.b	'STAK'		; STAK - stack field
STAKlen 	dc.l	4
Type		dc.l	0		; 0 = Info
StackTop	dc.l	0		; top of stack pointer
StackPtr	dc.l	0		; current Stack Pointer
StackLen	dc.l	0		; # bytes used on stack
chunk_len_3	equ	*-subREGS

STAK2		dc.b	'STAK'
s2len		dc.l	1		; length of subtype
		dc.l	1		; 1 = whole stack
STAK2len	equ	*-STAK2

STAK3		dc.b	'STAK'
		dc.l	1025
		dc.l	2		; 2 = top 4k of stack
STAK3len	equ	*-STAK3

STAK4		dc.b	'STAK'
		dc.l	1025
		dc.l	3		; 3 = bottom 4k of stack
STAK4len	equ	*-STAK4

STAK5		dc.b	'STAK'
_STAKOffset	dc.l	0
		dc.l	4		; 4 = user defined amount
STAK5len	equ	*-STAK5

UDAT		dc.b	'UDAT'
		dc.l	0
UDATlen 	equ	*-UDAT
	endc

	END

