// crt1-sim.S
// For the Xtensa simulator target, this code sets up the C calling context
// and calls main()  (via __clibrary_start).
// Control arrives here at _start from the reset vector or from crt0-app.S.

// Copyright (c) 1998-2017 Cadence Design Systems, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <xtensa/simboard.h>
#include <xtensa/simcall.h>
#include <xtensa/coreasm.h>
#include "xtos-internal.h"


// Exports
.global _start

// Imports
//   __clibrary_init	from C library (eg. newlib or uclibc)
//   exit		from C library
//   main		from user application
//   __stack		from linker script (see LSP Ref Manual)

.type	__clibrary_init, @function
.type	main, @function
.type	exit, @function


// Macros to abstract away ABI differences

#if __XTENSA_CALL0_ABI__
# define CALL	call0
# define CALLX	callx0
# define ARG1	a2	/* 1st outgoing call argument */
# define ARG2	a3	/* 2nd outgoing call argument */
# define ARG3	a4	/* 3rd outgoing call argument */
# define ARG4	a5	/* 4th outgoing call argument */
# define ARG5	a6	/* 5th outgoing call argument */
#else
# define CALL	call4
# define CALLX	callx4
# define ARG1	a6	/* 1st outgoing call argument */
# define ARG2	a7	/* 2nd outgoing call argument */
# define ARG3	a8	/* 3rd outgoing call argument */
# define ARG4	a9	/* 4th outgoing call argument */
# define ARG5	a10	/* 5th outgoing call argument */
#endif

		.data
		.weak	_start_envp	// allow overriding
		.align	4
_start_envp:	.word	0		// empty environ



	.text
	.align 4

_start:
	//  _start is typically NOT at the beginning of the text segment --
	//  it is always called from either the reset vector or other code
	//  that does equivalent initialization (such as crt0-app.S).
	//
	//  Assumptions on entry to _start:
	//	- low (level-one) and medium priority interrupts are disabled
	//	  via PS.INTLEVEL and/or INTENABLE (PS.INTLEVEL is expected to
	//	  be zeroed, to potentially enable them, before calling main)
	//	- C calling context not initialized:
	//	  - PS not initialized
	//	  - SP not initialized
	//	- the following are initialized:
	//	  - LITBASE, cache attributes, WindowBase, WindowStart,
	//	    CPENABLE, FP's FCR and FSR, EXCSAVE[n]

	// Keep a0 zero.  It is used to initialize a few things.
	// It is also the return address, where zero indicates
	// that the frame used by _start is the bottommost frame.
	//
	movi	a0, 0		// keep this register zero.

#if XTOS_RESET_UNNEEDED
#include "reset-unneeded.S"
#endif


	// Initialize the stack pointer.
	// See the "ABI and Software Conventions" chapter in the
	// Xtensa ISA Reference manual for details.

	// NOTE: Because the _start routine does not use any memory in its
	// stack frame, and because all of its CALL instructions use a
	// window size of 4, the stack frame for _start can be empty.
	movi	sp, __stack

	// reserve stack space for
	//    - argv array
	//    - argument strings
	movi	a2, SYS_iss_argv_size
	simcall		// returns size of argv[] + its strings in a2
#if XCHAL_HAVE_PIF
	// The stack only needs 16-byte alignment.
	// However, here we round up the argv size further to 128 byte multiples
	// so that in most cases, variations in argv[0]'s path do not result in
	// different stack allocation.  Otherwise, such variations can impact
	// execution timing (eg. due to cache effects etc) for the same code and data.
	// If we have a PIF, it's more likely the extra required space is okay.
	addi	a2, a2, 127
	srli	a2, a2, 7
	slli	a2, a2, 7
#else
	// Keep stack 16-byte aligned.
	addi	a2, a2, 15
	srli	a2, a2, 4
	slli	a2, a2, 4
#endif
	// No need to use MOVSP because we have no caller (we're the
	// base caller); in fact it's better not to use MOVSP in this
	// context, to avoid unnecessary ALLOCA exceptions and copying
	// from undefined memory:
	//   sub     a3, sp, a2
	//   movsp   sp, a3
	sub	sp, sp, a2


	/*
	 *  Now that sp (a1) is set, we can set PS as per the application
	 *  (user vector mode, enable interrupts, enable window exceptions if applicable).
	 */
#if XCHAL_HAVE_EXCEPTIONS
	movi	a3, PS_UM|PS_WOE_ABI	// PS.WOE = 0|1, PS.UM = 1, PS.EXCM = 0, PS.INTLEVEL = 0
	wsr.ps	a3
	rsync
#endif


	/*
	 *  Do any initialization that affects the memory map, such as
	 *  setting up TLB entries, that needs to be done before we can
	 *  successfully clear BSS (e.g. if some BSS segments are in
	 *  remapped areas).
	 *
	 *  NOTE:  This hook works where the reset vector does not unpack
	 *  segments (see "ROM packing" in the LSP manual), or where
	 *  unpacking of segments is not affected by memory remapping.
	 *  If ROM unpacking is affected, TLB setup must be done in
	 *  assembler from the reset vector.
	 *
	 *  The __memmap_init() routine can be a C function, however it
	 *  does not have BSS initialized!  In particular, __memmap_init()
	 *  cannot set BSS variables, i.e. uninitialized global variables
	 *  (they'll be wiped out by the following BSS clear), nor can it
	 *  assume they are yet initialized to zero.
	 *
	 *  The __memmap_init() function is optional.  It is marked as a
	 *  weak symbol, so that it gets valued zero if not defined.
	 */
	.weak	__memmap_init
	movi	a4, __memmap_init
	beqz	a4, 1f
	CALLX	a4
1:

#if !XCHAL_HAVE_BOOTLOADER	/* boot loader takes care of zeroing BSS */

	/*  If a system-specific BSS init routine is defined, call it.
	 *  Such a routine must be named __bss_init(). It can be a C
	 *  function, however it must be written to be able to work
	 *  with BSS not yet initialized. This function is optional.
	 *  It is marked as a weak symbol, so that it gets value zero
	 *  if not defined.
	 */
	.weak	__bss_init
	movi	a4, __bss_init
	beqz	a4, 2f
	movi	ARG1, _bss_table_start
	movi	ARG2, _bss_table_end
	CALLX	a4
	j	.Lnobss		// skip default BSS init code
2:

	/*  The new ISS simcall only appeared after RB-2007.2:  */
#if (XCHAL_HW_MAX_VERSION > XTENSA_HWVERSION_RB_2007_2)
	/*
	 *  Clear the BSS (uninitialized data) segments.
	 *  This code supports multiple zeroed sections (*.bss).
	 *  For speed, we clear memory using an ISS simcall
	 *  (see crt1-boards.S for more generic BSS clearing code).
	 */
	movi 	a6, _bss_table_start
	movi 	a7, _bss_table_end
	bgeu  	a6, a7, .Lnobss
.Lbssloop:
	movi	a2, SYS_memset
	l32i 	a3, a6, 0	// arg1 = fill start address
	movi	a4, 0		// arg2 = fill pattern
	l32i 	a5, a6, 4	// get end address
	addi   	a6, a6, 8	// next bss table entry
	sub	a5, a5, a3	// arg3 = fill size in bytes
	simcall			// memset(a3,a4,a5)
	bltu  	a6, a7, .Lbssloop	// loop until end of bss table
#endif /* XCHAL_HW_MAX_VERSION */
.Lnobss:
#endif /* XCHAL_HAVE_BOOTLOADER */


	/* 
	 *  Call __clibrary_init to initialize the C library:
	 *
	 *  void __clibrary_init(int argc, char ** argv, char ** environ, 
	 *		void(*init_func)(void), void(*fini_func)(void));
	 */	

	// Get argv with the arguments from the ISS
	mov	a3, sp		// tell simcall where to write argv[]
	movi	a2, SYS_iss_set_argv
	simcall			// write argv[] array at a3

	movi	a2, SYS_iss_argc
	simcall			// put argc in a2	


//	Alternative smaller code for Xtensa TX.
//	Many starting with simulation assume a full C env, so NOT DONE FOR NOW.
//
//#if XCHAL_HAVE_HALT
//
//	//  Assume minimalist environment for memory-constrained TX cores.
//	//  No C library or board initialization, and no call to exit().
//	//  However, in the interest of software regressions, for now we
//	//  still pass parameters to main (but not the rarely used envp).
//
//	//mov	ARG1, a2		// argc already in a2.
//	mov	ARG2, sp		// argv
//	CALL	main
//	halt
//
//#else /* !HALT */
//	...


#if __XTENSA_CALL0_ABI__
	mov 	a12, a2			// save argc (a2 is ARG1)
#else
	mov 	ARG1, a2		// argc
#endif
	mov	ARG2, sp		// argv
	movi 	ARG3, _start_envp	// envp
	movi 	ARG4, _init		// _init
	movi	ARG5, _fini		// _fini
	CALL	__clibrary_init

	//  Call:   int main(int argc, char ** argv, char ** environ);
#if __XTENSA_CALL0_ABI__
	mov 	ARG1, a12		// argc
#else
	mov	ARG1, a2		// argc
#endif
	mov	ARG2, sp		// argv
	movi	ARG3, _start_envp	// envp = [0]
	CALL	main
	//  The return value is the same register as the first outgoing argument.
	CALL	exit			// exit with main's return value
	// Does not return here.

	.size	_start, . - _start


// Local Variables:
// mode:fundamental
// comment-start: "// "
// comment-start-skip: "// *"
// End:
