
	IFD	COMMENT

C initial startup procedure under AmigaDOS. For SAS/C (and based on
SC:source/c.a), with some differences from the normal code:

	Requires OS 2.0. If not, bail out with return code 100.

	No argument parsing done. Assumes ReadArgs() or normal Workbench
	parsing.

	Doesn't open dos.library. This is left to autoinit code.

	Provides _exit() (the sc.lib version simply calls XCEXIT() directly
	anyway).

	Doesn't clear BSS data (as the loader will do it for us).

	Handles WBStartup message better (always reply, even if early
	fail, fetch it before DOS even opens).

	Doesn't save current directory (only useful during development
	anyway).

	Doesn't set _ProgramName.

	Calls Main instead, so that you notice the difference. ;) Main
	takes no arguments and is expected to return a LONG. Check
	_WBenchMsg for any WBStartup message.

	Assembled with BAsm.


Possible flavors:

RESIDENT
	Cres-version.
CPU020
	Requires a 68020+ (return 100 if not available).
FPU
	Requires an FPU (and a 68020+. CPU020 is set automatically)

No other flavours available.

	ENDC	; COMMENT


	IFD	FPU
	IFND	CPU020
CPU020	EQU	1
	ENDC
	ENDC

	SMALLDATA
	ADDSYM
	DEBUG
	LINKOBJ
	BOPT	O+,OT+,OG+
	BOPT	wo-

	IFD	CPU020
	IFD	FPU
	BOPT	mf+
	ENDC
	ENDC


	INCLUDE	exec/types.i
	INCLUDE	exec/tasks.i
	INCLUDE	exec/memory.i
	INCLUDE	exec/execbase.i
	INCLUDE	libraries/dos.i
	INCLUDE	libraries/dosextens.i
	INCLUDE	lvo/exec_lib.i
	INCLUDE	lvo/dos_lib.i


MEMFLAGS	EQU	MEMF_CLEAR+MEMF_PUBLIC
AbsExecBase	EQU	4

SYS	MACRO
	jsr	(_LVO\1,a6)
	ENDM


	XDEF	Startup

	XREF	_LinkerDB		; Linker defined base value
	XREF	__BSSBAS		; Linker defined base of BSS
	XREF	__BSSLEN		; Linker defined length of BSS
	XREF	___stack

	IFD	RESIDENT
	XREF	_RESLEN
	XREF	_RESBASE
	XREF	_NEWDATAL
	ENDC

	* Library references

	SECTION	text,CODE

	XREF	_Main			; Name of C function to start with.


Startup:
	lea	(_LinkerDB).l,a4		; Load base register
	movea.l	(AbsExecBase).w,a6
	movea.l	(ThisTask,a6),a3
	cmp.w	#36,(LIB_VERSION,a6)
	ble	EarlyFail		; Not OS 2.04+

	IFD	CPU020
	btst	#AFB_68020,(AttnFlags+1,a6)
	beq	EarlyFail

	IFD	FPU
	btst	#AFB_68881,(AttnFlags+1,a6)
	beq	EarlyFail

	fmove.l	#$00,fpcr		; Reset FPU to what SAS/C expects
	ENDC	; FPU

	ENDC	; CPU020

	IFND	RESIDENT		; Not resident
	move.l	a7,(__StackPtr,a4)	; Save stack
	move.l	a6,(_SysBase,a4)	; Save ExecBase

	* Get the size of the stack, if CLI use cli_DefaultStack
	*			     if WB use a7 - TC_SPLOWER

	move.l	(pr_CLI,a3),d0
	beq	.FromWB

	lsl.l	#2,d0
	movea.l	d0,a0
	move.l	(cli_DefaultStack,a0),d0
	lsl.l	#2,d0			; # longwords -> # bytes
	bra	.DoStack

.FromWB:
	move.l	a7,d0
	sub.l	(TC_SPLOWER,a3),d0

.DoStack:
	* Set __base for stack checking

	move.l	a7,d1
	sub.l	d0,d1			; Top of stack
	add.l	#128,d1			; Allow for parms overflow
	move.l	d1,(___base,a4)		; Save for stack checking

	cmp.l	(___stack,a4),d0
	bcc	.NoChange

	* Current stack is not as big as __stack says it needs
	* to be. Allocate a new one.

	move.l	(___stack,a4),d0
	add.l	#128,d0			; Extra room
	move.l	d0,(NewStackSize,a4)

	move.l	#MEMFLAGS,d1
	SYS	AllocMem
	tst.l	d0
	beq	EarlyFail

	move.l	d0,(NewStack,a4)
	add.l	#128,d0			; Extra room
	move.l	d0,(___base,a4)

	add.l	(___stack,a4),d0
	move.l	d0,d1			; Top of stack

	* Call StackSwap to set up the new stack.

	lea	(NewStk,a4),a0
	move.l	d0,(stk_Pointer,a0)
	move.l	d1,(stk_Upper,a0)
	sub.l	(NewStackSize,a4),d1
	move.l	d1,(stk_Lower,a0)
	SYS	StackSwap

.NoChange:

	ENDC

	IFD	RESIDENT

	* Get the size of the stack, if CLI use cli_DefaultStack
	*			     if WB use a7 - TC_SPLOWER

	move.l	(pr_CLI,a3),d1
	beq.b	.FromWB

	lsl.l	#2,d1			; Convert from BCPL pointer
	movea.l	d1,a0
	move.l	(cli_DefaultStack,a0),d1
	lsl.l	#2,d1			; # longwords -> # bytes
	bra.b	.DoStack

.FromWB:
	move.l	a7,d1
	sub.l	(TC_SPLOWER,a3),d1	; Size of stack
.DoStack:
	moveq	#0,d2			; Use d2 as flag for newstack or not
	move.l	#_RESLEN,d0
	cmp.l	(___stack,a4),d1	; This a4 is in the original set of data
	bcc.b	.NoChange

	move.l	(___stack,a4),d1
	add.l	d1,d0			; Increase size of mem for new stack
	moveq	#1,d2			; set flag

.NoChange:
	movea.l	d1,a5			; Save stacksize to set up stack checking
	move.l	#MEMFLAGS,d1
	SYS	AllocMem
	tst.l	d0
	beq	EarlyFail

	movea.l	d0,a0
	movea.l	d0,a1
	movea.l	d0,a2

	move.l	#_NEWDATAL,d0
	suba.l	#_RESBASE,a4

	* Copy and relocate program data

.Cp:	move.l	(a4)+,(a0)+		; Copy data over
	subq.l	#1,d0
	bne.b	.Cp

	move.l	(a4)+,d0		; Number of relocs
	beq.b	.NoReloc

.Rel:	movea.l	a1,a0			; Base address
	adda.l	(a4)+,a0		; A0 now has address of reloc
	adda.l	(a0),a2			; Add reloc to base
	move.l	a2,(a0)			; Write back new adress
	move.l	a1,a2			; Restore base
	subq.l	#1,d0
	bne.b	.Rel

.NoReloc:
	movea.l	a1,a4			; Set up new base register
	adda.l	#_RESBASE,a4

	move.l	#_RESLEN,(RealDataSize,a4)
	move.l	a6,(_SysBase,a4)
	move.l	a7,(__StackPtr,a4)
	tst.b	d2
	beq.b	.NoChg2

	* Set up new stack

	move.l	a4,d0
	sub.l	#_RESBASE,d0
	add.l	#_RESLEN,d0
	add.l	(___stack,a4),d0	; A4 points to new data, but __stack
					; will be the same if all goes well
	move.l	d0,d1
	lea	(NewStk,a4),a0
	move.l	d0,(stk_Pointer,a0)
	move.l	d1,(stk_Upper,a0)
	sub.l	(___stack,a4),d1
	move.l	d1,(stk_Lower,a0)
	SYS	StackSwap

	move.l	(___stack,a4),d0
	add.l	d0,(RealDataSize,a4)	; Need to know how much to free later

.NoChg2:

	* Set __base for stack checking

	move.l	a7,d1
	sub.l	a5,d1			; Get top of stack
	add.l	#128,d1			; Allow for parms overflow
	move.l	d1,(___base,a4)		; Save for stack checking

	ENDC	; RESIDENT

	* So we can restore allocated stack at exit

	lea	(ExitStk,a4),a0
	move.l	(___base,a4),(stk_Lower,a0)
	move.l	a7,(stk_Upper,a0)
	move.l	a7,(stk_Pointer,a0)
	SYS	StackSwap

	* Clear any pending break-c signals

	moveq	#0,d0
	move.l	#SIGBREAKF_CTRL_C,d1
	SYS	SetSignal

	bsr.b	GetWBMsg		; Handle Workbench startup message

	bsr	Construct		; Run constructors
	tst.l	d0
	bne.b	Exit2

	jsr	(_Main,pc)		; Call C entrypoint
	bra.b	Exit2


	XDEF	_XCEXIT,__XCEXIT,__exit
	XDEF	@XCEXIT,@_XCEXIT,@_exit

_XCEXIT:
__XCEXIT:
__exit:
	move.l	(4,sp),d0		; Extract return code
@XCEXIT:
@_XCEXIT:
@_exit:
Exit2:

	move.l	d0,d7

	* Set the stack to the first NEW stack we allocated
	* So C++ destructors can run with a large stack if required

	lea	(ExitStk,a4),a0
	move.l	(stk_Lower,a0),(___base,a4)
	movea.l	(_SysBase,a4),a6
	SYS	StackSwap

	move.l	(__ONEXIT,a4),d0	; Exit trap function?
	beq.b	Exit3

	movea.l	d0,a0
	jsr	(a0)

Exit3:	bsr.b	Destruct		; Run destructors

	* Swap back to original stack

	tst.l	(NewStk+stk_Lower,a4)
	beq.b	.NoSwap			; No new stack

	lea	(NewStk,a4),a0
	SYS	StackSwap

.NoSwap:

	IFND	RESIDENT

	* Free the stack if we allocated one

	move.l	(NewStackSize,a4),d0
	beq.b	Exit4

	movea.l	(NewStack,a4),a1
	SYS	FreeMem
	ENDC

Exit4:
	* If we ran from CLI, skip workbench cleanup:

	tst.l	(__WBenchMsg,a4)
	beq.b	.ExitToDOS

	* Return the startup message to our parent. We Forbid() so
	* Workbench can't UnLoadSeg() us before we are done.

	SYS	Forbid
	movea.l	(__WBenchMsg,a4),a1
	SYS	ReplyMsg
	; bra.b	.NoDoFree

.ExitToDOS:

	* This rts sends us back to DOS:

.NoDoFree:
	IFD	RESIDENT
	move.l	(RealDataSize,a4),d0
	move.l	a4,a1
	sub.l	#_RESBASE,a1
	SYS	FreeMem
	ENDC

	move.l	d7,d0
	rts


* Handle early fails properly.

EarlyFail:
	bsr.b	GetWBMsg		; Return any WBStartup message
	tst.l	d0
	beq.b	.Cli

	SYS	Forbid
	movea.l	d0,a1
	SYS	ReplyMsg
.Cli:	moveq	#100,d0
	rts


GetWBMsg:
	moveq	#0,d0
	tst.l	(pr_CLI,a3)
	bne.b	.Rts

	lea	(pr_MsgPort,a3),a0
	SYS	WaitPort
	lea	(pr_MsgPort,a3),a0
	SYS	GetMsg
	move.l	d0,(__WBenchMsg,a4)
.Rts:	rts


	XREF	___ctors

Construct:
	move.l	#___ctors,d0
	beq.b	.End

	movea.l	(___ctors-4,a4),a0	; Constructor function
	jsr	(a0)
	tst.l	d0
	beq.b	.End

	moveq	#20,d0
.End:	rts


	XREF	___dtors

Destruct:
	move.l	#___dtors,d0
	beq.b	.End

	movea.l	(___ctors-4,a4),a0	; Destructor function
	jsr	(a0)
.End:	rts


	SECTION	__MERGED,BSS

	XDEF	_SysBase

	XREF	__WBenchMsg
	XREF	__ONEXIT
	XREF	__StackPtr,___base

_SysBase:
	ds.l	1

NewStk:
	ds.b	StackSwapStruct_SIZEOF

ExitStk:
	ds.b	StackSwapStruct_SIZEOF


	IFD	RESIDENT

RealDataSize:
	ds.l	1		; Size of memory allocated for data/stack

	ENDC


	IFND	RESIDENT

NewStack:
	ds.l	1		; Pointer to new stack (if needed)

NewStackSize:
	ds.l	1		; Size of new stack

	ENDC


	END
