
*------ startup.asm  v 34.12   Copyright 1988 Commodore-Amiga, Inc.
*------
*------ Conditional assembly flags
*------ ASTART:   1=Standard Globals Defined    0=Reentrant Only
*------ WINDOW:   1=AppWindow for WB startup    0=No AppWindow code
*------ XNIL:     1=Remove startup NIL: init    0=Default Nil: WB Output
*------ NARGS:    1=Argv[0] only                0=Normal cmd line arg parse
*------ DEBUG:    1=Set up old statics for Wack 0=No extra statics
*------ QARG:     1=No argv		     	0=Passes argc,argv

* Include the appropriate .i file to set the flags

	INCLUDE "astartup.i"

* Flags for  [A]start  AWstart  Rstart  RWstart  RXstart  QStart
* ASTART         1        1       0        0        0       0
* WINDOW         0        1       0        1        0       0
* XNIL           0        0       0        0        1       1
* NARGS          0        0       0        0        0       0
* DEBUG          0        0       0        0        0       0
* QARG           0        0       0        0        0       1

;------   Flag WB output initialization
WBOUT     SET   (ASTART!WINDOW!(1-XNIL))

************************************************************************
*
*   startup.asm --- Reentrant C Program Startup/Exit (CLI and WB)
*                   Made reentrant by C. Scheppner
*		    Optimized by Kodiak Burns
*                   v34.12  07/25/88
*
*   Copyright (c) 1988 Commodore-Amiga, Inc.
*
*   Title to this software and all copies thereof remain vested in the
*   authors indicated in the above copyright notice.  The object version
*   of this code may be used in software for Commodore Amiga computers.
*   All other rights are reserved.
*
*   NO REPRESENTATIONS OR WARRANTIES ARE MADE WITH RESPECT TO THE
*   ACCURACY, RELIABILITY, PERFORMANCE OR OPERATION OF THIS SOFTWARE,
*   AND ALL SUCH USE IS AT YOUR OWN RISK.  NEITHER COMMODORE NOR THE
*   AUTHORS ASSUME ANY RESPONSIBILITY OR LIABILITY WHATSOEVER WITH
*   RESPECT TO YOUR USE OF THIS SOFTWARE.
*
*
*   RSTARTUP.ASM
*
*      This startup dynamically allocates a structure which includes
*   the argv buffers.  If you use this startup, your code must return
*   to this startup when it exits.  Use exit(n) or final curly brace
*   (rts) to return here.  Do not use AmigaDOS Exit() function.
*   Due to this dynamic allocation and some code consolidation, this
*   startup can make executables several hundred bytes smaller.
*
*       Because a static initialSP variable can not be used, this
*   code depends on the fact that AmigaDOS places the address of
*   the top of our stack in SP and proc->pr_ReturnAddr right before
*   JSR'ing to us.  This code uses pr_ReturnAddr when restoring SP.
*
*       Most versions of startup will initialize a Workbench process's
*   input and output streams (and stdio globals if present) to NIL:
*   if no other form of Workbench output (like WINDOW) is provided.
*   This should help prevent crashes if a user puts an icon on a CLI
*   program, and will also protect against careless stdio debugging
*   or error messages left in a Workbench program.  The code for
*   initializing Workbench IO streams only be removed by assembling
*   startup with ASTART and WINDOW set to 0, and XNIL set to 1.
*
*
*   Some startups which can be conditionally assembled:
*
*      1. Standard Astartup for non-reentrant code
*      2. Reentrant Rstartup (no unshareable globals)
*      3. Smaller reentrant-only RXstartup (no NIL: WB init code)
*      4. Standard AWstartup (WB output window) for non-reentrant code
*      5. Reentrant RWstartup (WB output window, no unshareable globals)
*      6. Smallest Qstartup  (No argv - argv is ptr to NULL string)
*
*
*   Explanation of conditional assembly flags:
*
*      ASTART (ASTART SET 1) startups will set up and XDEF the
*   global variables _stdin, _stdout, _stderr, _errno and  _WBenchMsg.
*   These startups can be used as smaller replacements for startups
*   like (A)startup.obj and TWstartup.obj.  Startups with ASTART
*   would generally be used for non-reentrant programs, although the
*   startup code itself is still reentrant if the globals are not
*   referenced.
*      Reentrant (ASTART SET 0) startups will NOT set up or
*   XDEF the stdio and WBenchMsg globals.  This not only makes the
*   startup slightly smaller, but also lets you know if your code
*   is referencing these non-reentrant globals (you will get an
*   unresolved external reference when you link).  Programs
*   get their input and output handles from Input() and Output(),
*   and the WBenchMsg is passed in argv on Workbench startup.
*
*      WINDOW (WINDOW SET 1) startups use an XREF'd CON: string
*   named AppWindow, defined in your application, to open a stdio
*   console window when your application is started from Workbench.
*   For non-reentrant programs, this window can be used for normal
*   stdio (printf, getchar, etc).  For reentrant programs the window
*   is Input() and Output().  WINDOW is useful when adding Workbench
*   capability to a stdio application, and also for debugging other
*   Workbench applications.  To insure that applications requiring
*   a window startup are linked with a window startup, the label
*   _NeedWStartup can be externed and referenced in the application
*   so that a linker error will occur if linked with a standard
*   startup.
*
*       example:   /* Optional safety reference to NeedWStartup */
*                    extern UBYTE  NeedWStartup;
*                    UBYTE  *HaveWStartup = &NeedWStartup;
*                  /* Required window specification */
*                    char AppWindow[] = "CON:30/30/200/150/MyProgram";
*                    ( OR  char AppWindow[] = "\0";  for no window )
*
*
*      XNIL (XNIL SET 1) allows the creation of a smaller startup
*   by removing the code that initializes a Workbench process's
*   output streams to NIL:.  This flag can only remove the code
*   if it is not required for ASTART or WINDOW.
*
*      NARGS (NARGS SET 1) removes the code used to parse command line
*   arguments.  The command name is still passed to _main as argv[0].
*   This option can take about 120 bytes off the size of any program that
*   does not use command line args.
*
*      DEBUG (DEBUG SET 1) will cause the old startup.asm statics
*   initialSP, dosCmdLen and dosCmdBuf to be defined and initialized
*   by the startup code, for use as debugging symbols when using Wack.
*
*      QARG (QARG SET TO 1) will bypass all argument parsing.  A CLI
*   startup is passed argc == 1, and a Workbench startup is passed
*   argc == 0.  Argv[0] will be a pointer to a NULL string rather than
*   a pointer to the command name.  This option creates a very small
*   startup with no sVar structure allocation, and therefore must be used
*   with XNIL (it is incompatible with default or AWindow output options).
*
*
*   RULES FOR REENTRANT CODE
*
*      - Make no direct or indirect (printf, etc) references to the
*        globals _stdin, _stdout, _stderr, _errno, or _WBenchMsg.
*
*      - For stdio use either special versions of printf and getchar
*        that use Input() and Output() rather than _stdin and _stdout,
*        or use fprintf and fgetc with Input() and Output() file handles.
*
*      - Workbench applications must get the pointer to the WBenchMsg
*        from argv rather than from a global extern WBenchMsg.
*
*      - Use no global or static variables within your code.  Instead,
*        put all former globals in a dynamically allocated structure, and
*        pass around a pointer to that structure.  The only acceptable
*        globals are constants (message strings, etc) and global copies
*        of Library Bases to resolve Amiga.lib references.  Your code
*        must return all OpenLibrary's into non-global variables,
*        copy the result to the global library base only if successful,
*        and use the non-globals when deciding whether to Close any
*        opened libraries.  
*
************************************************************************


******* Included Files *************************************************

	INCLUDE	"exec/types.i"
	INCLUDE	"exec/alerts.i"
	INCLUDE	"exec/memory.i"
	INCLUDE	"libraries/dos.i"
	INCLUDE	"libraries/dosextens.i"
	INCLUDE	"workbench/startup.i"


******* Macros *********************************************************

xlib	macro
	xref	_LVO\1
	endm

callsys	macro
	CALLLIB	_LVO\1
	endm

******* Imported *******************************************************

ABSEXECBASE	EQU	4

	xref	_main		; C code entry point

	IFGT	WINDOW
	xref	_AppWindow	; CON: spec in application for WB stdio window
	xdef	_NeedWStartup	; May be externed and referenced in application
	ENDC	WINDOW

	xlib	Alert
	xlib	AllocMem
	xlib	FindTask
	xlib	Forbid
	xlib	FreeMem
	xlib	GetMsg
	xlib	OpenLibrary
	xlib	CloseLibrary
	xlib	ReplyMsg
	xlib	Wait
	xlib	WaitPort

	xlib	CurrentDir
	xlib	Open
	xlib	Close
	xlib	Input
	xlib	Output

******* Exported *******************************************************

*----- These globals are set up for standard startup code only
	IFGT	ASTART
	xdef	_stdin
	xdef	_stdout
	xdef	_stderr
	xdef	_errno
	xdef	_WBenchMsg
	ENDC	ASTART

*----- These globals available to normal and reentrant code

	xdef	_SysBase
	xdef	_DOSBase
	xdef	_exit		; standard C exit function


***** Startup Variables structure **********************************

	IFEQ	QARG
ARGVSLOTS	EQU	32

 STRUCTURE  SVar,0
    LONG    sv_WbOutput
    STRUCT  sv_argvArray,ARGVSLOTS*4
    STRUCT  sv_argvBuffer,256
    LABEL   SV_SIZEOF
	ENDC	QARG

************************************************************************
*
*   Standard Program Entry Point
*
************************************************************************
*
*	Entered with
*	    d0  dosCmdLen
*	    a0  dosCmdBuf
*	Any registers (except sp) are allowed to be modified
*
*	Calls
*	    main (argc, argv)
*		int   argc;
*		char *argv[]; 
*
*	    For Workbench startup, argc=0, argv=WBenchMsg
*
************************************************************************
startup:
	IFGT	DEBUG
		move.l	sp,initialSP
		move.l	d0,dosCmdLen
		move.l	a0,dosCmdBuf
	ENDC	DEBUG

	IFEQ	QARG
		move.l	d0,d2
		move.l	a0,a2
	ENDC	QARG

	;------ get Exec library base pointer
		movea.l	ABSEXECBASE,a6
		move.l	a6,_SysBase

	;------ get the address of our task
		suba.l	a1,a1		; clear a1
		callsys	FindTask
		move.l	d0,a4		; keep task address in a4

	;------ get DOS library base pointer
		moveq	#0,d0
		lea	DOSName(pc),A1	; dos.library
		callsys	OpenLibrary

		tst.l   d0
		beq	alertDOS	; fail on null with alert
		move.l	d0,_DOSBase	; Else set the global
	

	IFEQ	QARG
	;------ alloc the argument structure
		move.l	#SV_SIZEOF,d0
		move.l	#(MEMF_PUBLIC!MEMF_CLEAR),d1
		callsys	AllocMem
		tst.l	d0
		beq	alertMem	; fail on null with alert
		move.l	d0,-(sp)	; save sVar ptr on stack
		move.l	d0,a5		; sVar ptr to a5
	ENDC	QARG
	IFGT	QARG
		clr.l	-(sp)
	ENDC	QARG

		clr.l	-(sp)		; reserve space for WBenchMsg if any

	;------ branch to Workbench startup code if not a CLI process
		move.l	pr_CLI(A4),d0
		beq	fromWorkbench

;=======================================================================
;====== CLI Startup Code ===============================================
;=======================================================================
;	d0  process CLI BPTR (passed in), then temporary
;	d2  dos command length (passed in)
;	d3  argument count
;	a0  temporary
;	a1  argv buffer
;	a2  dos command buffer (passed in)
;	a3  argv array
;	a4  Task (passed in)
;	a5  SVar structure if not QARG (passed in)
;	a6  AbsExecBase (passed in)
;	sp  WBenchMsg (still 0), sVar or 0, then RetAddr (passed in)
;	sp  argc, argv, WBenchMsg, sVar or 0,RetAddr (at bra domain)

	IFEQ	QARG
	;------ find command name
		lsl.l	#2,d0		; pr_CLI bcpl pointer conversion
		move.l	d0,a0
		move.l	cli_CommandName(a0),d0
		lsl.l	#2,d0		; bcpl pointer conversion

		;-- start argv array
		lea	sv_argvBuffer(a5),a1
		lea	sv_argvArray(a5),a3

		;-- copy command name
		move.l	d0,a0
		moveq.l	#0,d0
		move.b	(a0)+,d0	; size of command name
		clr.b	0(a0,d0.l)	; terminate the command name
		move.l	a0,(a3)+
		moveq	#1,d3		; start counting arguments

	IFEQ	NARGS	
	;------ null terminate the arguments, eat trailing control characters
		lea	0(a2,d2.l),a0
stripjunk:
		cmp.b	#' ',-(a0)
		dbhi	d2,stripjunk

		clr.b	1(a0)

	;------ start gathering arguments into buffer
newarg:
		;-- skip spaces
		move.b	(a2)+,d1
		beq.s	parmExit
		cmp.b	#' ',d1
		beq.s	newarg
		cmp.b	#9,d1		; tab
		beq.s	newarg

		;-- check for argument count overflow
		cmp.w	#ARGVSLOTS-1,d3
		beq.s	parmExit

		;-- push address of the next parameter
		move.l	a1,(a3)+
		addq.w	#1,d3

		;-- process quotes
		cmp.b	#'"',d1
		beq.s	doquote

		;-- copy the parameter in
		move.b	d1,(a1)+

nextchar:
		;------ null termination check
		move.b	(a2)+,d1
		beq.s	parmExit
		cmp.b	#' ',d1
		beq.s	endarg

		move.b	d1,(a1)+
		bra.s	nextchar

endarg:
		clr.b	(a1)+
		bra.s	newarg

doquote:
	;------ process quoted strings
		move.b	(a2)+,d1
		beq.s	parmExit
		cmp.b	#'"',d1
		beq.s	endarg

		;-- '*' is the BCPL escape character
		cmp.b	#'*',d1
		bne.s	addquotechar

		move.b	(a2)+,d1
		move.b	d1,d2
		and.b	#$df,d2		;d2 is temp toupper'd d1

		cmp.b	#'N',d2		;check for dos newline char
		bne.s	checkEscape

		;--	got a *N -- turn into a newline
		moveq	#10,d1
		bra.s	addquotechar

checkEscape:
		cmp.b	#'E',d2
		bne.s	addquotechar

		;--	got a *E -- turn into a escape
		moveq	#27,d1

addquotechar:
		move.b	d1,(a1)+
		bra.s	doquote

parmExit:
	;------ all done -- null terminate the arguments
		clr.b	(a1)
		clr.l	(a3)
	ENDC NARGS

		pea	sv_argvArray(a5) ; argv
		move.l	d3,-(sp)	 ; argc
	ENDC	QARG

	IFGT	QARG
		pea	nullArgV(pc)	; pointer to pointer to null string
		pea	1		; only one pointer
	ENDC

	IFGT	ASTART
		movea.l	_DOSBase,a6
	;------ get standard input handle:
		callsys	Input
		move.l	d0,_stdin

	;------ get standard output handle:
		callsys	Output
		move.l	d0,_stdout
		move.l	d0,_stderr
		movea.l	ABSEXECBASE,a6
	ENDC ASTART

		bra	domain


;=======================================================================
;====== Workbench Startup Code =========================================
;=======================================================================
;	a2  WBenchMsg
;	a4  Task (passed in)
;	a5  SVar structure if not QARG (passed in)
;	a6  AbsExecBase (passed in)
;	sp  WBenchMsg (still 0), sVar or 0, then RetAddr (passed in)
;	sp  argc=0,argv=WBenchMsg,WBenchMsg,sVar or 0,RetAddr (at domain)

fromWorkbench:
	;------ get the startup message that workbench will send to us.
	;	must get this message before doing any DOS calls
		bsr.s   getWbMsg

	;------ save the message so we can return it later
		move.l	d0,(sp)
	IFGT	ASTART
		move.l	d0,_WBenchMsg
	ENDC	ASTART

	;------ push the message on the stack for wbmain (as argv)
		move.l	d0,-(sp)
		clr.l	-(sp)		; indicate run from Workbench (argc=0)

	IFNE	(1-QARG)+WBOUT
	;------ put DOSBase in a6 for next few calls
		move.l	_DOSBase,a6
	ENDC	(1-QARG)+WBOUT

	IFEQ	QARG
	;------ get the first argument
		move.l	d0,a2
		move.l	sm_ArgList(a2),d0
		beq.s	doCons

	;------ and set the current directory to the same directory
		move.l	d0,a0
		move.l	wa_Lock(a0),d1
		callsys	CurrentDir
doCons: 
	ENDC	QARG

	IFGT	WBOUT

	;------ Open NIL: or AppWindow for WB Input()/Output() handle
	;	Also for possible initialization of stdio globals
	;	Stdio used to be initialized to -1

      
	IFGT	WINDOW
	;------ Get AppWindow defined in application
		lea	_AppWindow,a0
		cmp.b	#0,(a0)
		bne.s	doOpen		; Open if not null string
	ENDC	WINDOW

	;------ Open NIL: if no window provided
	lea	NilName(PC),a0

doOpen:
	;------ Open up the file whose name is in a0
	;	DOSBase still in a6
		move.l	a0,d1
		move.l	#MODE_OLDFILE,d2 
		callsys	Open 
	;------ d0 now contains handle for Workbench Output
	;------ save handle for closing on exit
		move.l	d0,sv_WbOutput(a5)
		bne.s	gotOpen
		moveq.l #RETURN_FAIL,d2
		bra     exit2
gotOpen:
	IFGT ASTART
	;------ set the C input and output descriptors 
		move.l	d0,_stdin
		move.l	d0,_stdout
		move.l	d0,_stderr
	ENDC ASTART

	;------ set the console task (so Open( "*", mode ) will work 
	;	task pointer still in A4
		move.l	d0,pr_CIS(A4)
		move.l	d0,pr_COS(A4)
		lsl.l	#2,d0 
		move.l	d0,a0 
		move.l	fh_Type(a0),d0
		beq.s	noConTask
		move.l	d0,pr_ConsoleTask(A4)
noConTask:
	ENDC WBOUT

	;------ Fall though to common WB/CLI code


****************************************************
**                                                **
** This code now used by both CLI and WB startup  **
**                                                **
****************************************************

domain:
		jsr	_main
	;------ main didn't use exit(n) so provide success return code
		moveq.l	#RETURN_OK,d2
		bra.s	exit2


****************************************************
**                                                **
**    subroutines here to allow short branches    **
**                                                **
****************************************************

getWbMsg:
	;------ a6 = ExecBase
		lea	pr_MsgPort(A4),a0	; our process base
		callsys	WaitPort
		lea	pr_MsgPort(A4),a0	; our process base
		callsys	GetMsg
		rts

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

alertDOS:
	;------ do recoverable alert for no DOS and exit
		ALERT	(AG_OpenLib!AO_DOSLib)
	
	;------ do recoverable alert for no memory and exit
	;------ If we got this far, DOS is open, so close it
	IFEQ QARG
		bra.s	failExit
alertMem:
		movea.l _DOSBase,a1
		callsys CloseLibrary	
		ALERT	AG_NoMemory
	ENDC QARG
failExit:
		tst.l   pr_CLI(a4)
		bne.s   fail2
		bsr.s	getWbMsg
		movea.l d0,a2
		bsr.s	repWbMsg
fail2:
		moveq.l	#RETURN_FAIL,d0
		rts

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

repWbMsg:
	;------ return the startup message to our parent
	;       a6 = ExecBase (passed)
	;	a2 = WBenchMsg (passed)
	;	we forbid so workbench can't UnLoadSeg() us before we are done
		callsys	Forbid
		move.l	a2,a1
		callsys	ReplyMsg
		rts


*******************************************************
**                                                   **
**  C Program exit() Function, return code on stack  **
**                                                   **
**  pr_ReturnAddr points to our RTS addr on stack    **
**  and we use this to calculate our stack ptr:      ** 
**                                                   **
**      SP ->   WBenchMsg or 0 (CLI)                 **
**              sVar ptr or 0 (QARG)                 **
**              Address for RTS to DOS               **
**                                                   **
*******************************************************

_exit:
		move.l	4(sp),d2	; exit(n) return code to d2

exit2:					;exit code in d2
	;------ restore initial stack ptr
		;-- FindTask
		movea.l	ABSEXECBASE,a6
		suba.l	a1,a1
		callsys	FindTask
		;-- get SP as it was prior to DOS's jsr to us
		move.l	d0,a4
		move.l	pr_ReturnAddr(a4),a5
		;-- subtract 4 for return address, 4 for SVar, 4 for WBenchMsg
		suba.w	#12,a5

		;-- restore sp
		move.l	a5,sp
	
		;-- recover WBenchMsg
		move.l	(sp)+,a2
		;-- recover SVar
		move.l	(sp)+,a5


	IFGT	WBOUT
	;------ Close any WbOutput file before closing dos.library
		move.l	sv_WbOutput(a5),d1
		beq.s	noWbOut
		move.l	_DOSBase,a6
		callsys Close
noWbOut:
	;------ Restore a6 = ExecBase
		movea.l ABSEXECBASE,a6
	ENDC	WBOUT

	;------ Close DOS library, if we got here it was opened
	;	SysBase still in a6
		movea.l	_DOSBase,a1
		callsys	CloseLibrary

	;------ if we ran from CLI, skip workbench reply
checkWB:
		move.l	a2,d0
		beq.s	deallocSV

		bsr.s	repWbMsg

deallocSV:
	IFEQ	QARG
	;------ deallocate the SVar structure
		move.l	a5,a1
		move.l	#SV_SIZEOF,d0
		callsys FreeMem
	ENDC	QARG

	;------ this rts sends us back to DOS:
		move.l	d2,d0
		rts


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

;----- PC relative data

DOSName		DOSNAME
NilName		dc.b	'NIL:',0
	IFGT	QARG
nullArgV	dc.l	nullArg
nullArg		dc.l	0		; "" & the null entry after nullArgV
	ENDC

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

   DATA

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

_SysBase	dc.l	0
_DOSBase	dc.l	0

	IFGT	ASTART
_WBenchMsg	dc.l	0
_stdin		dc.l	0
_stdout		dc.l	0
_stderr		dc.l	0
_errno		dc.l	0
	ENDC	ASTART

	IFGT	DEBUG
initialSP	dc.l	0
dosCmdLen	dc.l	0
dosCmdBuf	dc.l	0
	ENDC	DEBUG

VerRev		dc.w	34,12
	IFGT	ASTART
		dc.b	'A'
	ENDC	ASTART
	IFEQ	ASTART
		dc.b	'R'
	ENDC	ASTART
	IFGT	WINDOW
_NeedWStartup:
		dc.b	'W'
	ENDC	WINDOW
	IFEQ	WBOUT
		dc.b	'X'
	ENDC	WBOUT
	IFGT	NARGS
		dc.b	'N'
	ENDC	NARGS
	IFGT	DEBUG
		dc.b	'D'
	ENDC	DEBUG
	IFGT	QARG
		dc.b	'Q'
	ENDC	QARG

	END

