/*
 *    CF_Startup.c - Default init/startup/termination routines for
 *                     Embedded Metrowerks C++
 *
 *    Copyright © 1993-1998 Metrowerks, Inc. All Rights Reserved.
 *    Copyright © 2005 Freescale semiConductor Inc. All Rights Reserved.
 *
 *
 *    THEORY OF OPERATION
 *
 *    This version of thestartup code is intended for linker relocated
 *    executables.  The startup code will assign the stack pointer to
 *    __SP_INIT, assign the address of the data relative base address
 *    to a5, initialize the .bss/.sbss sections to zero, call any
 *    static C++ initializers and then call main.  Upon returning from
 *    main it will call C++ destructors and call exit to terminate.
 */

#ifdef __cplusplus
#pragma cplusplus off
#endif
#pragma PID off
#pragma PIC off

#include "startcf.h"
#include "RuntimeConfig.h"

	/* imported data */

extern unsigned long far _SP_INIT, _SDA_BASE;
extern unsigned long far _START_BSS, _END_BSS;
extern unsigned long far _START_SBSS, _END_SBSS;
extern unsigned long far __DATA_RAM, __DATA_ROM, __DATA_END;
extern unsigned long far __VECTOR_RAM;

	/* imported routines */

extern void __call_static_initializers(void);
extern int main(int, char **);
extern void exit(int);

	/* exported routines */

extern void _ExitProcess(void);
extern asm void _startup(void);
extern void __initialize_hardware(void);
extern void __initialize_system(void);
static void __copy_rom_sections_to_ram(void);
static __declspec(register_abi) void clear_mem(char *dst, unsigned long n);


/*
 *    Startup routine for embedded application ...
 */

asm void _startup(void)
{
	/* disable interrupts */
    move.w        #0x2700,sr

	/* Pre-init SP, in case memory for stack is not valid it should be setup using 
	   MEMORY_INIT before __initialize_hardware is called 
	*/
	lea __SP_AFTER_RESET,a7; 

    /* initialize memory */
    MEMORY_INIT

	/* initialize any hardware specific issues */
    jsr           __initialize_hardware   
  
	/* setup the stack pointer */
    lea           _SP_INIT,a7

	/* setup A6 dummy stackframe */
    movea.l       #0,a6
    link          a6,#0

	/* setup A5 */
    lea           _SDA_BASE,a5


	/* zero initialize the .bss section */

    lea           _END_BSS, a0
    lea           _START_BSS, a1
    suba.l        a1, a0
    move.l        a0, d0

    beq           __skip_bss__

    lea           _START_BSS, a0

    /* call clear_mem with base pointer in a0 and size in d0 */
    jsr           clear_mem

__skip_bss__:

	/* zero initialize the .sbss section */

    lea           _END_SBSS, a0
    lea           _START_SBSS, a1
    suba.l        a1, a0
    move.l        a0, d0

    beq           __skip_sbss__

    lea           _START_SBSS, a0

    /* call clear_mem with base pointer in a0 and size in d0 */
    jsr           clear_mem

__skip_sbss__:

	/* copy all ROM sections to their RAM locations ... */
#if SUPPORT_ROM_TO_RAM

	/*
	 * _S_romp is a null terminated array of
	 * typedef struct RomInfo {
     *      unsigned long	Source;
     *      unsigned long	Target;
     *      unsigned long 	Size;
     *  } RomInfo;
     *
     * Watch out if you're rebasing using _PICPID_DELTA
     */

    lea           _S_romp, a0
    move.l        a0, d0
    beq           __skip_rom_copy__            
    jsr           __copy_rom_sections_to_ram

#else

	/*
   * There's a single block to copy from ROM to RAM, perform
   * the copy directly without using the __S_romp structure
   */

    lea           __DATA_RAM, a0
    lea           __DATA_ROM, a1
    
    cmpa          a0,a1
    beq           __skip_rom_copy__
              
    move.l        #__DATA_END, d0
    sub.l         a0, d0
                  
    jsr           __copy_rom_section

#endif
__skip_rom_copy__:
	
	/* call C++ static initializers (__sinit__(void)) */
	jsr			  __call_static_initializers

	jsr		  	  __initialize_system

	/* call main(int, char **) */
    move.w        #0x2000,sr		
	pea			  __argv
	clr.l		  -(sp)				/* clearing a long is ok since it's caller cleanup */
	jsr			  main
	addq.l		#8, sp
	
	unlk		  a6
	
	/* now call exit(0) to terminate the application */
	clr.l		  -(sp)
	jsr			  exit
	addq.l		#4, sp

	/* should never reach here but just in case */
	illegal
	rts

	/* exit will never return */
__argv:
    dc.l          0
}


/*
 *    Dummy routine for initializing hardware.  For user's custom systems, you
 *    can create your own routine of the same name that will perform HW
 *    initialization.  The linker will do the right thing to ignore this
 *    definition and use the version in your file.
 */
#pragma overload void __initialize_hardware(void);
void __initialize_hardware(void)
{
}

/*
 *    Dummy routine for initializing systems.  For user's custom systems,
 *    you can create your own routine of the same name that will perform
 *    initialization.  The linker will do the right thing to ignore this
 *    definition and use the version in your file.
 */
#pragma overload void __initialize_system(void);
void __initialize_system(void)
{
}

/*
 *    Dummy routine for initializing C++.  This routine will get overloaded by the C++ runtime.
 */
#pragma overload void __call_static_initializers(void);
void __call_static_initializers(void)
{
}

/*
 *	Routine to copy a single section from ROM to RAM ...
 */
static __declspec(register_abi) void __copy_rom_section(char* dst, const char* src, unsigned long size)
{
	if (dst != src)
		 while (size--)
		    *dst++ = *src++;
}

/*
 *	Routine that copies all sections the user marked as ROM into
 *	their target RAM addresses ...
 *
 *	__S_romp is automatically generated by the linker if it
 *	is referenced by the program.  It is a table of RomInfo
 *	structures.  The final entry in the table has all-zero
 *	fields.
 */
static void __copy_rom_sections_to_ram(void)
{
	RomInfo		*info;

	/*
	 *	Go through the entire table, copying sections from ROM to RAM.
	 */
	for (info = _S_romp; info->Source != 0L || info->Target != 0L || info->Size != 0; ++info)
    __copy_rom_section( (char *)info->Target,(char *)info->Source, info->Size);
							
}

/*
 *    Exit handler called from the exit routine, if your OS needs
 *    to do something special for exit handling just replace this
 *    routines with what the OS needs to do ...
 */
asm void _ExitProcess(void)
{
	illegal
	rts
}

/*
 *    Routine to clear out blocks of memory should give good
 *    performance regardless of 68k or ColdFire part.
 */
static __declspec(register_abi) void clear_mem(char *dst, unsigned long n)
{
	unsigned long i;
	long *lptr;

	if (n >= 32)
	{
		/* align start address to a 4 byte boundary */
		i = (- (unsigned long) dst) & 3;

		if (i)
		{
			n -= i;
			do
				*dst++ = 0;
			while (--i);
		}

		/* use an unrolled loop to zero out 32byte blocks */
		i = n >> 5;
		if (i)
		{
			lptr = (long *)dst;
			dst += i * 32;
			do
			{
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
				*lptr++ = 0;
			}
			while (--i);
		}
		i = (n & 31) >> 2;

		/* handle any 4 byte blocks left */
		if (i)
		{
			lptr = (long *)dst;
			dst += i * 4;
			do
				*lptr++ = 0;
			while (--i);
		}
		n &= 3;
	}

	/* handle any byte blocks left */
	if (n)
		do
			*dst++ = 0;
		while (--n);
}

