##
## startup.s
##
## Startup code for PowerPC programs using the PowerUp kernel
## It saves registers, initializes _stdin, _stdout and _stderr,
## parses the command line and calls _main().
##
##
## V0.9 23-Dec-97 phx
##	No longer expect argc/argv from ppcrun/elfrun/etc.. A command
##	line pointer is received in r3 and must be parsed first. If no
##	problems occur, _main() will receive argc/argv as usual. Other-
##	wise it will get argc=argv=0.
##	Still no WB-startup support...
##	Fixed a bug with stderr (Enforcer-hit/IsInteractive()) in
##	"Non-StartupMessage mode".
##	Successfully tested with ppc.library V45.2.
## V0.8 21-Nov-97 phx
##	CallOS()-fix for ppc.library V44.29 with old libamiga.a.
## V0.7 02-Nov-97 phx
##	Shorter startup code by using stmw/lmw.
## V0.6 18-Oct-97 phx
##	The ppc.library executes ELF objects at .text+4, so the first
##	instruction was always skipped... :(
##	SysBase is exported. DOSBase must be initialized by _main().
## V0.5 15-Oct-97 phx
##	Also works with R.Schmidt's ppcload.
##	Supports blr in _main(), although libvc.a jumps to _exit directly.
##      No longer use r13 for debugging output. It's reserved for
##	Small Data pointer in V.4. :P
## V0.4 13-Oct-97 phx
##	No longer touches r2. r2 is reserved in System V.4 ABI!
## V0.3 07-Oct-97 phx
##	Rewritten in pure PowerPC assembler (pasm).
## V0.2 04-Oct-97 phx
##      Use "CONSOLE:" instead "*" (PowerPC will definitely not run
##      under Kickstart 1.x :)
##      argc and argv are passed via startup message. argc and argv are
##      also passed in GPR3 and GPR4 but I will better read them from the
##      startup message...
##      When the program terminates, the result code is returned in
##      the startup message, which is replied then.
## V0.1 03-Oct-97 phx
##      created


.set	DEBUG,0		# set to 1 for serial debugging
.set	WAITBUTTON,0	# if 1, then wait for left mouse button,
			#  after debugging output
.set	CALLOSFIX,1	# ppc.library V44.29 has PPCCallOS(), but
			#  old libamiga.a stil uses CallOS().

# powerup includes
.set	PPCTASKTAG_STARTUP_MSG,0x80020015
.set	PPCTASKTAG_STARTUP_MSGDATA,0x80020016

# struct StartupData
.set	sd_M68kPort,0	# the PowerPC task can send messages to this port
.set	sd_std_in,4	# standard input handle
.set	sd_std_out,8	# standard output handle
.set	sd_std_err,12	# standard error handle
.set	sd_retCode,16	# PPC return code, passed on to M68k
.set	sd_flags,20	# additional flags (currently unused)

.set	IOERROR,50	# can't open stdin/stdout/stderr

# serial debugging
.if	DEBUG
	.extern	PPCRawDoFmt

.macro	_debug		# _debug <format string>[,<gpr>, ...]
	.data		# ... works with all GPRs, except r12
.db\@:	.string	\1
	.text
	stwu	r12,-4(r1)
	lis	r12,.regsave@ha
	addi	r12,r12,.regsave@l
	stw	r0,0(r12)
	stw	r3,4(r12)
	stw	r4,8(r12)
	stw	r5,12(r12)
	stw	r6,16(r12)
	stw	r7,20(r12)
	stw	r8,24(r12)
	stw	r9,28(r12)
	stw	r10,32(r12)
	stw	r11,36(r12)
	lwz	r3,0(r1)
	addi	r1,r1,4
	stw	r3,40(r12)
	lwz	r3,4(r12)
	.ifge	$NARG-2
	lis	r12,.datastream@ha
	addi	r12,r12,.datastream@l
	stw	\2,0(r12)
	.endif
	.ifge	$NARG-3
	stw	\3,4(r12)
	.endif
	.ifge	$NARG-4
	stw	\4,8(r12)
	.endif
	.ifge	$NARG-5
	stw	\5,12(r12)
	.endif
	.iflt	$NARG-2
	li	r4,0
	.else
	mr	r4,r12
	.endif
	lis	r3,(.db\@)@ha
	addi	r3,r3,(.db\@)@l
	li	r5,1
	li	r6,0
	bl	PPCRawDoFmt		# output to serial line
.if	WAITBUTTON
	lis	r12,0xbfe001@h		# wait for left mouse button
	ori	r12,r12,0xbfe001@l
.dbwait1\@:
	lbz	r3,0(r12)
	andi.	r3,r3,0x40
	bne	.dbwait1\@
.dbwait2\@:
	lbz	r3,0(r12)
	andi.	r3,r3,0x40
	beq	.dbwait2\@
.endif
	lis	r12,.regsave@ha
	addi	r12,r12,.regsave@l
	lwz	r0,0(r12)
	lwz	r3,4(r12)
	lwz	r4,8(r12)
	lwz	r5,12(r12)
	lwz	r6,16(r12)
	lwz	r7,20(r12)
	lwz	r8,24(r12)
	lwz	r9,28(r12)
	lwz	r10,32(r12)
	lwz	r11,36(r12)
	lwz	r12,40(r12)
.endm

.else
.macro	_debug
.endm
.endif


	.text

	.extern	_main
	.extern	PPCGetTaskAttr
	.extern	PPCOpen
	.extern	PPCClose
	.extern	PPCAllocVec
	.extern	PPCFreeVec


# This is the first function in a program. Execution starts here!

__start:
	nop				# ppc.library bug fix
	stwu	r1,-448(r1)
	mflr	r11
	stw	r11,452(r1)
	stmw	r14,224(r1)		# save all non-volatile registers
	stfd	f14,296(r1)
	stfd	f15,304(r1)
	stfd	f16,312(r1)
	stfd	f17,320(r1)
	stfd	f18,328(r1)
	stfd	f19,336(r1)
	stfd	f20,344(r1)
	stfd	f21,352(r1)
	stfd	f22,360(r1)
	stfd	f23,368(r1)
	stfd	f24,376(r1)
	stfd	f25,384(r1)
	stfd	f26,392(r1)
	stfd	f27,400(r1)
	stfd	f28,408(r1)
	stfd	f29,416(r1)
	stfd	f30,424(r1)
	stfd	f31,432(r1)
	_debug	"\n-------\nstartup\n-------\nstack = 0x%08lx\n",r1
	lis	r11,_init_stk@ha	# save initial stack pointer
	stw	r1,_init_stk@l(r11)

	li	r14,0			# r14: argc = 0
	mr.	r15,r3			# r15: command line/argv
	bnel	parse_args
	lis	r16,.constring@ha	# r16 = "CONSOLE:"
	addi	r16,r16,.constring@l

# initialize SysBase
	li	r3,4
	lwz	r0,0(r3)
	lis	r3,SysBase@ha
	stw	r0,SysBase@l(r3)

# evaluate startup message from M68k
	lis	r3,PPCTASKTAG_STARTUP_MSG@h
	ori	r3,r3,PPCTASKTAG_STARTUP_MSG@l
	bl	PPCGetTaskAttr
	_debug	"PPCTASKTAG_STARTUP_MSG = 0x%08lx\n",r3
	cmpwi	r3,0
	beq	.1
	lis	r3,PPCTASKTAG_STARTUP_MSGDATA@h
	ori	r3,r3,PPCTASKTAG_STARTUP_MSGDATA@l
	bl	PPCGetTaskAttr
	_debug	"PPCTASKTAG_STARTUP_MSGDATA = 0x%08lx\n",r3
	lwz	r4,sd_std_in(r3)	# get _stdin
	lis	r11,_stdin@ha
	stw	r4,_stdin@l(r11)
	lwz	r5,sd_std_out(r3)	# get _stdout
	lis	r11,_stdout@ha
	stw	r5,_stdout@l(r11)
	lwz	r6,sd_std_err(r3)	# get _stderr
	lis	r11,_stderr@ha
	stw	r6,_stderr@l(r11)
	lis	r11,_startup_data@ha
	stw	r3,_startup_data@l(r11)
	_debug	"stdin=0x%08lx stdout=0x%08lx stderr=0x%08lx\n",r4,r5,r6
	b	.startup

# get _stdin, _stdout, _stderr
.1:	li	r4,1005
	mr	r3,r16
	bl	PPCOpen			# PPCOpen("CONSOLE:",MODE_OLDFILE)
	_debug	"PPCOpen(\"%s\",MODE_OLDFILE) = 0x%08lx\n",r16,r3
	cmpwi	r3,0
	beq	__exit
	lis	r11,_stdin@ha
	stw	r3,_stdin@l(r11)

	li	r4,1006
	mr	r3,r16
	bl	PPCOpen			# PPCOpen("CONSOLE:",MODE_NEWFILE)
	_debug	"PPCOpen(\"%s\",MODE_NEWFILE) = 0x%08lx\n",r16,r3
	cmpwi	r3,0
	beq	__exit
	lis	r11,_stdout@ha
	stw	r3,_stdout@l(r11)
	lis	r12,_stderr@ha
	stw	r3,_stderr@l(r12)	# _stderr = _stdout

# call _main in libvc.a
.startup:
	mr	r3,r14
	mr	r4,r15
	_debug	"calling _main(argc=%ld,argv=0x%08lx)\n",r3,r4
	bl	_main			# _main(argc,argv)
	li	r3,0
	b	_exit

	.type	__start,@function
	.size	__start,$-__start

.constring:
	.string	"CONSOLE:"
	.align	2


__exit:
	li	r3,IOERROR		# error: can't open stdin/stdout


	.global	_exit
_exit:
# r3 = return code
	mr	r14,r3
	_debug	"\n-----\n_exit\n-----\nstack = 0x%08lx\nrc = %ld\n",r1,r14
# free argv
	lis	r11,_argv@ha
	lwz	r3,_argv@l(r11)
	bl	PPCFreeVec

	lis	r15,_startup_data@ha
	lwz	r15,_startup_data@l(r15)
	cmpwi	r15,0			# did we receive a startup message?
	bne	.3

# close _stdout
	lis	r11,_stdout@ha
	lwz	r3,_stdout@l(r11)
	cmpwi	r3,0
	beq	.2
	_debug	"PPCClose(stdout=0x%08lx)\n",r3
	bl	PPCClose

# close _stdin
.2:	lis	r11,_stdin@ha
	lwz	r3,_stdin@l(r11)
	cmpwi	r3,0
	beq	.4
	_debug	"PPCClose(stdin=0x%08lx)\n",r3
	bl	PPCClose
	b	.4

# write return code to startup message
.3:	_debug	"store rc=%ld in startup data (0x%08lx)\n",r14,r15
	stw	r14,sd_retCode(r15)

# restore initial stack frame
.4:	mr	r3,r14
	lis	r11,_init_stk@ha
	lwz	r1,_init_stk@l(r11)
	lmw	r14,224(r1)
	lfd	f14,296(r1)
	lfd	f15,304(r1)
	lfd	f16,312(r1)
	lfd	f17,320(r1)
	lfd	f18,328(r1)
	lfd	f19,336(r1)
	lfd	f20,344(r1)
	lfd	f21,352(r1)
	lfd	f22,360(r1)
	lfd	f23,368(r1)
	lfd	f24,376(r1)
	lfd	f25,384(r1)
	lfd	f26,392(r1)
	lfd	f27,400(r1)
	lfd	f28,408(r1)
	lfd	f29,416(r1)
	lfd	f30,424(r1)
	lfd	f31,432(r1)
	lwz	r11,452(r1)
	mtlr	r11
	addi	r1,r1,448
	blr

	.type	_exit,@function
	.size	_exit,$-__exit


.if	CALLOSFIX
	.extern	PPCCallOS
	.global	CallOS
CallOS:
	b	PPCCallOS

	.type	CallOS,@function
	.size	CallOS,$-CallOS
.endif


parse_args:
# Parse command line and generate argc/argv.
# r14: argc=0
# r15: cmdline (0-terminated, includes program name), will become argv
	mflr	r11
	mr	r10,r15
	li	r12,0
.pa1:	bl	nextarg
	cmpwi	r3,0
	beq	.pa2
	addi	r12,r12,4		# another argv slot
	bl	skiparg
	cmpwi	r3,0
	bne	.pa1
.pa2:	cmpwi	r12,0
	beq	.pa_nomem		# no arguments found?
	addi	r12,r12,4		# + one termination slot
	sub	r3,r10,r15
	add	r3,r3,r12
	li	r4,0			# MEMF_ANY
	bl	PPCAllocVec		# memory for argv slots and buffer
	mr	r10,r15
	mr.	r15,r3
	beq	.pa_nomem
	lis	r9,_argv@ha
	stw	r15,_argv@l(r9)
	subi	r9,r15,4		# r9 argv slots
	add	r12,r15,r12		# r12 argv buffer
.pa4:	bl	nextarg
	cmpwi	r3,0
	beq	.pa_quit
	stwu	r12,4(r9)		# store arg-pointer
	addi	r14,r14,1		# increment argc
	cmpwi	r3,0x22
	bne	.pa10			# quoted?
.pa5:	lbzu	r3,1(r10)
	cmpwi	r3,0x22
	bne	.pa6
	addi	r10,r10,1		# end quote
	li	r3,0
.pa6:	cmpwi	r3,'*'			# BCPL escape character?
	bne	.pa8
	lbzu	r3,1(r10)
	andi.	r4,r3,0xdf
	cmpwi	r4,'N'			# newline
	bne	.pa7
	li	r3,10
.pa7:	cmpwi	r4,'E'			# escape
	bne	.pa8
	li	r3,27
.pa8:	stb	r3,0(r12)
	addi	r12,r12,1
	cmpwi	r3,0
	bne	.pa5
	b	.pa4
.pa10:	stb	r3,0(r12)		# normal arg copy
	addi	r12,r12,1
	lbzu	r3,1(r10)
	cmplwi	r3,0x20
	bgt	.pa10
	li	r3,0
	b	.pa8

.pa_nomem:
	li	r15,0
	mtlr	r11
	blr
.pa_quit:
	stwu	r3,4(r9)		# r3=0 terminates the arguments
	mtlr	r11
	blr

	.type	parse_args,@function
	.size	parse_args,$-parse_args


nextarg:
# move pointer to beginning of next argument
# r10: cmdline-pointer
# -> r10: new cmdline-pointer, r3: char
	lbz	r3,0(r10)
	cmplwi	r3,0x20
	bgtlr
	cmpwi	r3,0
	beqlr
	addi	r10,r10,1
	b	nextarg

	.type	nextarg,@function
	.size	nextarg,$-nextarg


skiparg:
# move pointer behind the current argument
# r10: cmdline-pointer
# -> r10: new cmdline-pointer, r3: char
	lbz	r3,0(r10)
	cmpwi	r3,0x22			# " ?
	beq	.skipquote
.sk1:	cmplwi	r3,0x20
	blelr
	cmpwi	r3,0
	beqlr
	lbzu	r3,1(r10)
	b	.sk1
.skipquote:
	lbzu	r3,1(r10)
	cmpwi	r3,0
	beqlr
	cmpwi	r3,'*'			# escape?
	bne	.sk2
	lbzu	r4,1(r10)
	cmpwi	r4,0
	beqlr
.sk2:	cmpwi	r3,0x22			# " ?
	bne	.skipquote
	lbzu	r3,1(r10)
	blr

	.type	skiparg,@function
	.size	skiparg,$-skiparg



	.type	_init_stk,@object
	.size	_init_stk,4
	.bss	_init_stk,4

	.type	_startup_data,@object
	.size	_startup_data,4
	.bss	_startup_data,4

	.type	_argv,@object
	.size	_argv,4
	.bss	_argv,4

	.global	_stdin
	.type	_stdin,@object
	.size	_stdin,4
	.comm	_stdin,4

	.global	_stdout
	.type	_stdout,@object
	.size	_stdout,4
	.comm	_stdout,4

	.global	_stderr
	.type	_stderr,@object
	.size	_stderr,4
	.comm	_stderr,4

	.global	SysBase
	.type	SysBase,@object
	.size	SysBase,4
	.comm	SysBase,4

.if	DEBUG
	.bss	.regsave,11*4		# r0,r3,...,r12
	.bss	.datastream,4*4		# max. 4 arguments
.endif
