*
* C initial startup procedure under AmigaDOS
* 
* Use the following command line to make c.o
* asm -u -iINCLUDE: c.a
*
* Use the following command line to make cres.o
* asm -u -dRESIDENT -iINCLUDE: -ocres.o c.a
*
	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	"exec/memory.i"
	INCLUDE	"exec/execbase.i"
	INCLUDE	"libraries/dos.i"
	INCLUDE	"libraries/dosextens.i"
	INCLUDE	"workbench/startup.i"
FUNCDEF         MACRO   * function
_LVO\1          EQU     FUNC_CNT
FUNC_CNT        SET     FUNC_CNT-6
                ENDM

FUNC_CNT        SET     LIB_NONSTD

	INCLUDE	"exec/exec_lib.i"
	INCLUDE	"libraries/dos_lib.i"

MEMFLAGS	EQU	MEMF_CLEAR+MEMF_PUBLIC
AbsExecBase	EQU	4

;;;
;;;   Stack map.
;;;
      OFFSET  0
           ds.b    4
savereg    ds.b    13*4
stackbtm   ds.b    4



; some usefull macros:

callsys macro
	CALLLIB _LVO\1
	endm
	
	xdef	XCEXIT		* exit(code) is standard way to leave C.
	xdef	@XCEXIT
	
	xref	LinkerDB	* linker defined base value
	xref	_BSSBAS		* linker defined base of BSS
	xref	_BSSLEN		* linker defined length of BSS
	IFD	RESIDENT
	xref	RESLEN
	xref	RESBASE
	xref	NEWDATAL
	xref	_stack
	ENDC
	
*       library references

	section text,code

	xref    _main			* Name of C program to start with.
	xref    MemCleanup		* Free all allocated memory
	xref    __fpinit		* initialize floating point
	xref    __fpterm		* terminate floating point

start:
	movem.l d1-d6/a0-a6,-(a7)       * save registers

	move.l  a0,a2			* save command pointer
	move.l  d0,d2			* and command length
	lea     LinkerDB,a4		* load base register
	move.l  AbsExecBase.W,a6

	IFND	RESIDENT
	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  a7,_StackPtr(A4)       * Save stack ptr
	move.l  a6,SysBase(A4)
	ENDC
	

	IFD	RESIDENT
	move.l	d2,-(a7)
	movem.l	a0-a2,-(a7)

*------ get the size of the stack, if CLI use cli_DefaultStack
*------			    	   if WB use a7 - TC_SPLOWER
	move.l	ThisTask(a6),A3
	move.l  pr_CLI(A3),d1
	beq.s   fromwb
	lsl.l	#2,d1
	move.l	d1,a0
	move.l	cli_DefaultStack(a0),d1
	lsl.l	#2,d1			* # longwords -> # bytes
	bra.s	dostack

fromwb:
	move.l	a7,d1		 	
	sub.l	TC_SPLOWER(a3),d1
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.s	nochange	
	move.l	_stack(a4),d1
	add.l	d1,d0			* increase size of mem for new stack
	moveq	#1,d2			* set flag
		
nochange:
	move.l	d1,a3			* save stacksize to set up stack checking
	move.l	#MEMFLAGS,d1
	callsys AllocMem
	tst.l	d0
	bne.s	ok1
	movem.l	(a7)+,d2/a0-a2
	bra.w   return

ok1:	move.l	d0,a0
	move.l	d0,a2

;a2 now has difference
	move.l	d0,a1
	move.l	#NEWDATAL,d0
	sub.l	#RESBASE,a4
;copy data over
cpy:    move.l	(a4)+,(a0)+
	subq.l	#1,d0
	bne.s	cpy
;a4 now points at number of relocs
	move.l	(a4)+,d0
reloc:  beq.s	nreloc
	move.l	a1,a0
	add.l	(a4)+,a0		* a0 now has add of reloc
	add.l	(a0),a2
	move.l	a2,(a0) 
	move.l	a1,a2			* restore offset
	subq.l	#1,d0
	bra.s	reloc
	
nreloc: move.l	a1,a4			* set up new base register
	add.l	#RESBASE,a4

	move.l	#RESLEN,realdatasize(a4)
	movem.l (a7)+,a0-a2

	move.l  a6,SysBase(A4)
	tst.b	d2
	movem.l	(a7)+,d2		* restore d2 
	movem.l a7,_StackPtr(A4)	* Save stack ptr (movem doesn't
					* change flags
	beq.s	nochg2

*------ set up new stack
	move.l	a4,d0
	sub.l	#RESBASE,d0
	add.l	#RESLEN,d0
	add.l	_stack(a4),d0		* here a4 will be pointing at the
					* new data, but _stack will be the
					* same if all goes well
	sub.l	#128,d0			* 128 down for good measure
	move.l	d0,a7
	move.l	_stack(a4),d0
	move.l	d0,4(a7)		* fill in size of new stack	
	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	a3,d1			* get top of stack
	add.l   #128,D1			* allow for parms overflow
	move.l  D1,_base(A4)    	* save for stack checking

	ENDC

clrwb:
	clr.l   WBenchMsg(A4)

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

*JVP--- set up for my very own trap handler
	move.l	TC_TRAPCODE(a3),oldTrapCode(a4)	* save the old trap code
	lea	trapcode,a0		* do something a little less
	move.l	a0,TC_TRAPCODE(a3)	* drastic.

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

*------ attempt to open DOS library:
	lea     DOSName(PC),A1
	moveq.l #0,D0
	callsys OpenLibrary
	move.l  D0,DOSBase(A4)
	bne.s	ok2
	moveq.l #100,d0
	bra.w   exit2

ok2:
*------ are we running as a son of Workbench?
	move.l	ThisTask(a6),A3
	move.l  pr_CurrentDir(A3),curdir(A4)
	tst.l   pr_CLI(A3)
	beq.w  fromWorkbench
	
*=======================================================================
*====== CLI Startup Code ===============================================
*=======================================================================
*
* Entry: D2 = command length
*	A2 = Command pointer
fromCLI:
	ifnd	RESIDENT	* we need to set _base if not resident
        move.l  a7,D0           * get top of stack
        sub.l   stackbtm(a7),D0 * compute bottom
        add.l   #128,D0         * allow for parms overflow
        move.l  D0,_base(A4)    * save for stack checking
	endc
	
*------ 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
	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  #7,d0		   * allow for space after command, quotes
			           * and null terminator, as well as 
	andi.w  #$fffc,D0	   * force to long word boundary
        move.l   d0,Commandlen(a4)
   
   movem.l  d1/a1,-(a7)
	move.l	#MEMFLAGS,d1
	callsys AllocMem
	movem.l  (a7)+,d1/a1
	tst.l   d0
	bne.s   ok_copy
   
	move.l  #1000,d0        * what should the return code be for out of mem?
	move.l  d0,-(a7)        * put a return code on the stack
	beq.w   nodofree        * Was exitToDOS
   
ok_copy:
	move.l   d0,a0
	move.l   d0,Commandbuf(a4)
         
*------ copy command line into memory
	move.l  d2,d0		   * get command line length
	subq.l  #1,d0
	add.l   d1,d2
   
copy_line:
	move.b  0(A2,D0.W),2(A0,D2.W)   * copy command line to stack
	subq.l  #1,d2
	dbf     d0,copy_line
	move.b  #' ',2(a0,d2.w)	 * add space between command and parms
	subq.l  #1,d2
	move.b  #'"',2(a0,d2.w)	 * add end quote
   
copy_cmd:
	move.b  0(a1,d2.w),1(a0,d2.w)   * copy command name to stack
	dbf     d2,copy_cmd
        move.b  #'"',(a0)
	move.l  A0,-(A7)		* push command line address
	bra.s   main		    * call C entrypoint

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

fromWorkbench:

	ifnd	RESIDENT	* we need to set _base if not resident
        move.l  TC_SPLOWER(a3),_base(A4) * set base of stack
	moveq   #127,d0
	addq.l	#1,d0                    * Efficient way of getting in 128
        add.l   d0,_base(A4)             * allow for parms overflow
	endc

*------ we are now set up.  wait for a message from our starter
	lea     pr_MsgPort(A3),a0       * our process base
	callsys WaitPort
	lea     pr_MsgPort(A3),a0       * our process base
	callsys 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
        callsys DupLock
	move.l  d0,curdir(A4)
	move.l  d0,d1
	callsys CurrentDir
do_cons:
	move.l  sm_ToolWindow(a2),d1    * get the window argument
	beq.s   do_main
	move.l  #MODE_OLDFILE,d2
	callsys 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
@XCEXIT:
exit2:
        movea.l _StackPtr(a4),a7         * restore stack ptr
	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.
	jsr     __fpterm(PC)	    * clean up any floating point

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

*------ return the startup message to our parent
*       we forbid so workbench can't UnLoadSeg() us
*       before we are done:
        move.l  curdir(a4),d1
        beq.s   done_5
        callsys UnLock
done_5:
	move.l  AbsExecBase.W,A6
	callsys Forbid
	move.l  WBenchMsg(a4),a1
	callsys ReplyMsg
	bra.s   nodofree

exitToDOS:
*JVP--- restore original TC_TRAPCODE leaving the shell as it was.
	move.l	ThisTask(a6),a1
	move.l	oldTrapCode(a4),TC_TRAPCODE(a1)	* politely restore this guy.
*------ free the command line buffer
	move.l   Commandlen(a4),d0
	beq.s    nodofree
	move.l   Commandbuf(a4),a1
	move.l  AbsExecBase.W,a6
	callsys FreeMem
   
*------ this rts sends us back to DOS:
nodofree:
	move.l  DOSBase(A4),a1
	callsys CloseLibrary	    * close Dos library

	IFD	RESIDENT
	move.l	realdatasize(a4),d0
	move.l  a4,a1
	sub.l   #RESBASE,a1
	callsys FreeMem
	ENDC
	move.l  (a7)+,d0

return:	
	movem.l (a7)+,d1-d6/a0-a6
	rts

*JVP---------------------------------------------------------------------
*  Trap handler. Helps avoid the GURU.

trapcode:
		move.l	(sp)+,d0	; get the trap code off the supervisor stack
                andi    #$DFFF,SR	; switch back to Usermode
                lea     LinkerDB,A4    ; load linker defined base register
                move.l	d0,_TRAPCODE(A4) ; Make the trap code available to C functions
		move.l	_ONTRAP(A4),d0	; get the new trap vector
		beq	1$		; skip C routine call if not supplied
		move.l	d0,a0		; make it a funtion address
                jsr     (a0)		; Let C code clean up.
1$:		move.l	_TRAPCODE(A4),d0	; use the trap code as the return code
		add.l	#1000,d0	; give it an offset to force exit message
                jmp     exit2		; exit this error prone program.


DOSName	 dc.b    'dos.library',0

	section __MERGED,BSS
*
	xref    DOSBase

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

*JVP--- ontrap externals
	xdef	_ONTRAP,_TRAPCODE
*
	ifd	RESIDENT
realdatasize	ds.b	4	* size of memory allocated for data +
				* possible stack
	endc
	
NULL		ds.b	4	*
_base		ds.b	4	* base of stack
_mbase		ds.b    4	* base of memory pool
_mnext		ds.b    4	* next available memory location
_msize		ds.b    4	* size of memory pool
_tsize		ds.b    4	* total size?
_oserr		equ     *
_OSERR		ds.b    4
_FPERR		ds.b    4
_SIGFPE		ds.b    4
_SIGINT		ds.b    4
_ONERR		ds.b    4
_ONEXIT		ds.b    4
_ONBREAK	ds.b    4
curdir	 	ds.b    4
SysBase		ds.b    4
WBenchMsg	ds.b    4
_StackPtr	ds.b    4
stdin		ds.b    4
_ProgramName	ds.b    4
Commandbuf	ds.b    4
Commandlen	ds.b    4
*JVP--- ontrap storage
_ONTRAP         ds.b    4		* my trap handler vector
_TRAPCODE	ds.b    4
oldTrapCode     ds.b    4
		END
