/***********************************************************************
*
*             Copyright 2006 Mentor Graphics Corporation
*                         All Rights Reserved.
*
* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
* SUBJECT TO LICENSE TERMS.
*
************************************************************************

************************************************************************
*
*   FILE NAME
*
*       rvct_arm_rtl.c
*
*   DESCRIPTION
*
*       This file contains the RVCT specific functions
*       related to run-time environment requirements
*
*   FUNCTIONS
*
*       ESAL_TS_RTE_Initialize
*       ESAL_TS_RTE_Cxx_Region_Objects_Initialize
*       ESAL_TS_RTE_Cxx_System_Objects_Initialize
*       ESAL_TS_RTE_Cxx_Exceptions_Initialize
*
*   DEPENDENCIES
*
*       nucleus.h
*
************************************************************************/

/* Include required header files */
#include            "nucleus.h"
#include            "kernel/nu_kernel.h"
#include            "os/include/arch/arm/tool-rvct/cxx_rte_inc.h"

/* Check version of RVCT tools. */
#if (__ARMCC_VERSION <= 400000)
#error ERROR: Unsupported version of RVCT tools.
#endif /* (__ARMCC_VERSION <= 400000) */

/* External variable declarations */
#if (ESAL_TS_RTL_SUPPORT == NU_TRUE)

#include            <locale.h>
#include            <alloca.h>
#include            <rt_heap.h>
#include            <stdio.h>
#include            <rt_sys.h>

/* Local Globals */
static INT          scanf_leftover;
static INT          use_scanf_leftover = NU_FALSE;

/* External variable declarations */
extern UINT32       Image$$heap$$ZI$$Base;
extern UINT32       Image$$heap$$ZI$$Limit;

/* File I/O error value (supports ferror API) */
static INT          ferror_value = 0;

/* Ensure that no malloc functions included from tools. */
#pragma import(__use_no_heap)

#endif /* ESAL_TS_RTL_SUPPORT == NU_TRUE */

#if (__ARMCC_VERSION > 400000)

#include            "nucleus.h"

/* Used for remapping of malloc functions. */
#include            "kernel/rtl_extr.h"

    #ifdef CFG_NU_OS_SVCS_CXX_ENABLE

        #if (CFG_NU_OS_SVCS_CXX_INIT_STATIC_OBJECTS == NU_TRUE)

/* C++ Static Object support. */
__weak const size_t SHT$$INIT_ARRAY$$Base[];    /* Symbol taken from linker command file. */
__weak const size_t SHT$$INIT_ARRAY$$Limit[];   /* Symbol taken from linker command file. */

        #endif /* (CFG_NU_OS_SVCS_CXX_INIT_STATIC_OBJECTS == NU_TRUE) */

        #if (CFG_NU_OS_SVCS_CXX_INIT_EXCEPTION_SUPPORT == NU_TRUE)

/* C++ Exceptions initialization support. */
extern void         __cxa_get_globals(void);

        #endif /* (CFG_NU_OS_SVCS_CXX_INIT_EXCEPTION_SUPPORT == NU_TRUE) */

    #endif /* CFG_NU_OS_SVCS_CXX_ENABLE */

#endif /* (__ARMCC_VERSION > 400000) */

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_Initialize
*
*   DESCRIPTION
*
*       This function initializes the run-time environment as required
*       for the given toolset
*
*   CALLED BY
*
*       ESAL_GE_RTE_Initialize
*
*   CALLS
*
*       setlocale
*       _init_alloc
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_TS_RTE_Initialize(VOID)
{
#if (ESAL_TS_RTL_SUPPORT == NU_TRUE)

    setlocale(LC_ALL, "C");

#endif /* ESAL_TS_RTL_SUPPORT == NU_TRUE */
}

#if (__ARMCC_VERSION > 400000)

/*************************************************************************
*
*   FUNCTION
*
*       malloc
*
*   DESCRIPTION
*
*       Allocates memory (using Nucleus RTL).
*
*   INPUTS
*
*       size - Indicates the size (in bytes) of the requested memory.
*
*   OUTPUTS
*
*      <pointer> - Pointer to allocated memory.
*
*      NU_NULL - Indicates internal error or requested memory not
*                available.
*
*************************************************************************/
void * malloc(size_t size)
{
    void *      mem_ptr;

    NU_SUPERV_USER_VARIABLES

    /***** BEGIN SUPERVISOR MODE *****/

    NU_SUPERVISOR_MODE();

    mem_ptr = RTL_malloc(size);

    NU_USER_MODE();

    /****** END SUPERVISOR MODE ******/

    return (mem_ptr);
}

/*************************************************************************
*
*   FUNCTION
*
*       calloc
*
*   DESCRIPTION
*
*       Allocates zero-initialized memory (using Nucleus RTL).
*
*   INPUTS
*
*       nmemb - Number of objects to allocate.
*
*       size - Indicates the size (in bytes) of an object.
*
*   OUTPUTS
*
*      <pointer> - Pointer to allocated memory.
*
*      NU_NULL - Indicates internal error or requested memory not
*                available.
*
*************************************************************************/
void * calloc (size_t nmemb, size_t size)
{
    void *      mem_ptr;

    NU_SUPERV_USER_VARIABLES

    /***** BEGIN SUPERVISOR MODE *****/

    NU_SUPERVISOR_MODE();

    mem_ptr = RTL_calloc(nmemb, size);

    NU_USER_MODE();

    /****** END SUPERVISOR MODE ******/

    return(mem_ptr);
}

/*************************************************************************
*
*   FUNCTION
*
*       realloc
*
*   DESCRIPTION
*
*       Re-allocates memory (using Nucleus RTL).
*
*   INPUTS
*
*       ptr - Pointer to the memory to be re-allocated.
*
*       size - Indicates the new size (in bytes) of the requested memory.
*
*   OUTPUTS
*
*      <pointer> - Pointer to allocated memory.
*
*      NU_NULL - Indicates internal error or requested memory not
*                available.
*
*************************************************************************/
void * realloc(void * ptr, size_t size)
{
    void *      mem_ptr;

    NU_SUPERV_USER_VARIABLES

    /***** BEGIN SUPERVISOR MODE *****/

    NU_SUPERVISOR_MODE();

    mem_ptr = RTL_realloc(ptr, size);

    NU_USER_MODE();

    /****** END SUPERVISOR MODE ******/

    return(mem_ptr);
}

/*************************************************************************
*
*   FUNCTION
*
*       free
*
*   DESCRIPTION
*
*       Frees allocated memory (using Nucleus RTL).
*
*   INPUTS
*
*       ptr - Pointer to memory to be deallocated.
*
*   OUTPUTS
*
*       None
*
*************************************************************************/
void free(void * ptr)
{
    NU_SUPERV_USER_VARIABLES

    /***** BEGIN SUPERVISOR MODE *****/

    NU_SUPERVISOR_MODE();

    RTL_free(ptr);

    NU_USER_MODE();

    /****** END SUPERVISOR MODE ******/

    return;
}

    #ifdef CFG_NU_OS_SVCS_CXX_ENABLE

        #if (CFG_NU_OS_SVCS_CXX_INIT_STATIC_OBJECTS == NU_TRUE)

/*************************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_Cxx_Region_Objects_Initialize
*
*   DESCRIPTION
*
*       Initialize C++ Static Objects in a specific memory region.
*
*   CALLED BY
*
*       ESAL_TS_RTE_Cxx_System_Objects_Initialize
*
*   CALLS
*
*       (C++ Static Object Constructors)
*
*   INPUTS
*
*       region_start - Starting address of memory region to call
*                      constructors.
*
*       region_end - Ending address of memory region to call constructors.
*
*   OUTPUTS
*
*       None
*
*************************************************************************/
VOID ESAL_TS_RTE_Cxx_Region_Objects_Initialize(VOID *   region_start,
                                               VOID *   region_end)
{
    INT             cursor;
    INT             quantity;
    UINT *          ctor_start;
    const size_t *  list_cursor;

    VOID (*ptr_to_function)();

    /* Get the start address of the CTOR list */
    ctor_start = (UINT *)&SHT$$INIT_ARRAY$$Base;

    /* Calculate the number of static objects in the system */
    quantity = ((INT)((UINT) &SHT$$INIT_ARRAY$$Limit - (UINT) &SHT$$INIT_ARRAY$$Base)) / 4;

    for(cursor = 0; cursor < quantity; cursor++)
    {
        /* Get pointer to current element in constructor array. */
        list_cursor = (((const size_t *) ctor_start) + cursor);

        /* Calculate the next static object's constructor address. */
        ptr_to_function = (VOID (*)())((const char*)list_cursor + *list_cursor);

        /*  Does the address reside in specified memory region and is not
            NULL. */
        if(((UNSIGNED) *ptr_to_function > (UNSIGNED) region_start) &&
           ((UNSIGNED) *ptr_to_function < (UNSIGNED) region_end) &&
           (*ptr_to_function != NU_NULL))
        {
            /* The static object resides within this module -
               execute it */
            (*ptr_to_function)();
        }

    }

    return;
}

/*************************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_Cxx_System_Objects_Initialize
*
*   DESCRIPTION
*
*       Initialize C++ System Objects.
*
*   CALLED BY
*
*       CXX_RTE_Initialize
*
*   CALLS
*
*       ESAL_TS_RTE_Cxx_Region_Objects_Initialize
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
*************************************************************************/
VOID ESAL_TS_RTE_Cxx_System_Objects_Initialize(VOID)
{
    /* Force C++ support inclusion. */
    CXX_RTE_INC_var = 1;

    /* Initialize all objects in system memory region (all of memory). */
    ESAL_TS_RTE_Cxx_Region_Objects_Initialize((VOID *)0x00000000,
                                              (VOID *)0xFFFFFFFF);

    return;
}

        #endif /* (CFG_NU_OS_SVCS_CXX_INIT_STATIC_OBJECTS == NU_TRUE) */

        #if (CFG_NU_OS_SVCS_CXX_INIT_EXCEPTION_SUPPORT == NU_TRUE)

/*************************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_Cxx_Exceptions_Initialize
*
*   DESCRIPTION
*
*       Initialize C++ Exceptions.
*
*   CALLED BY
*
*       CXX_RTE_Initialize
*
*   CALLS
*
*       __cxa_get_globals
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
*************************************************************************/
VOID ESAL_TS_RTE_Cxx_Exceptions_Initialize(VOID)
{
    __cxa_get_globals();

    return;
}

        #endif /* (CFG_NU_OS_SVCS_CXX_INIT_EXCEPTION_SUPPORT == NU_TRUE) */

    #endif /* CFG_NU_OS_SVCS_CXX_ENABLE */

#endif /* (__ARMCC_VERSION > 400000) */

#if (ESAL_TS_RTL_SUPPORT == NU_TRUE)

/***********************************************************************
*
*   FUNCTION
*
*       fputc
*
*   DESCRIPTION
*
*       Low level function to redirect IO to serial.  Used in C and C++
*       I/O support.
*
*   CALLED BY
*
*       C and C++ library high-level functions
*
*   CALLS
*
*       NU_SIO_Putchar
*
*   INPUTS
*
*       INT    ch                         - Character to output
*       FILE * f                          - Unused
*
*   OUTPUTS
*
*       INT status                        - Character written or -1 if
*                                           failed
*
***********************************************************************/
INT fputc(INT ch, FILE *f)
{
    INT status;
    NU_SUPERV_USER_VARIABLES
    
    NU_SUPERVISOR_MODE();

    /* Check for NULL pointer */
    if (ESAL_GE_RTE_Byte_Write != NU_NULL)
    {
        status = (*ESAL_GE_RTE_Byte_Write)(ch);

        /* Update global error value */
        ferror_value = 0;
    }
    else
    {
        /* Return error */
        status = -1;

        /* Update global error value */
        ferror_value = -1;
    }
    
    NU_USER_MODE();
    
    return(status);
}

/***********************************************************************
*
*   FUNCTION
*
*       ferror
*
*   DESCRIPTION
*
*       Low level function to return the error status accumulated during
*       the last file I/O operation.  Used in C and C++ I/O support.
*
*   CALLED BY
*
*       C and C++ library high-level functions
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       FILE * f                          - Unused
*
*   OUTPUTS
*
*       INT error                         - Error value set by previous
*                                           operation or zero if no error
*
***********************************************************************/
INT ferror(FILE *f)
{
    /* Return current ferror value. */
    return(ferror_value);
}


/***********************************************************************
*
*   FUNCTION
*
*       fgetc
*
*   DESCRIPTION
*
*       Low level function to redirect IO to serial.  Used in C and C++
*       I/O support.
*
*   CALLED BY
*
*       C and C++ library high-level functions
*
*   CALLS
*
*       [NU_SIO_Getchar]
*
*   INPUTS
*
*       FILE * f                          - Unused
*
*   OUTPUTS
*
*       INT c                             - Character read
*
***********************************************************************/
INT fgetc(FILE *f)
{
    INT c;
    NU_SUPERV_USER_VARIABLES
    
    NU_SUPERVISOR_MODE();

    /* Check for NULL pointer */
    if (ESAL_GE_RTE_Byte_Read != NU_NULL)
    {
        /* Do we have a character that scanf has placed back in the
         * input stream by way of the __backspace function? */
        if (use_scanf_leftover)
        {
            /* Yes; clear the flag and return the unused character */
            use_scanf_leftover = NU_FALSE;
            c = scanf_leftover;
        }
        else
        {
            /* No; try and read a new character */
            while ((c = (*ESAL_GE_RTE_Byte_Read)()) == -1)
            {
            }   

            /* Remember this character in case scanf is calling us */
            scanf_leftover = c;
        }

        /* Update global error value */
        ferror_value = 0;
    }
    else
    {
        /* Return error */
        c = -1;

        /* Update global error value */
        ferror_value = -1;

    }
    
    NU_USER_MODE();

    return(c);
}


/***********************************************************************
*
*   FUNCTION
*
*       __backspace
*
*   DESCRIPTION
*
*       Low level function used by scanf to "put back" one character.
*
*   CALLED BY
*
*       scanf
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       FILE * f                          - Unused
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
INT __backspace(FILE *f)
{
    use_scanf_leftover = NU_TRUE;

     /* ARM documentation defines zero as "success" */
    return(0);
}

#endif /* ESAL_TS_RTL_SUPPORT == NU_TRUE */


/***********************************************************************
*
*   FUNCTION
*
*       __user_initial_stackheap
*
*   DESCRIPTION
*
*       This function returns the start address of the heap.
*       This function is needed by applications utilizing certain
*       ARM Developer Suite Tools library calls.
*
*       This function is called by some ADS libraries.
*
*   CALLED BY
*
*       RVCT
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       UINT64                              Upper 32-bits: stack pointer
*                                           Lower 32-bits: start address
*                                                          of heap
*
***********************************************************************/
UINT64  __user_initial_stackheap(VOID)
{
    UINT64          rtn_val;


    /* Get start address of heap in lower 32-bits (register r0) */
    rtn_val = (UINT64)&Image$$heap$$ZI$$Base;

    /* Put stack pointer in upper 32-bits (register r1) */
    rtn_val |= ((UINT64)ESAL_GE_STK_System_SP << 32);

    /* Return start address of heap */
    return (rtn_val);
}


#if (ESAL_AR_STK_V7_SUPPORT)
/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_SMI_Execute
*
*   DESCRIPTION
*
*       This function executes a smi/smc instruction.
*
*   CALLED BY
*
*       ESAL_TS_RTE_SMI_EXECUTE macro
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       r0_val
*       r12_op
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
__asm VOID  ESAL_TS_RTE_SMI_Execute(UINT r0_val, INT r12_op)
{
    /* Force ARM mode for this function */
    CODE32

    /* Save r12 */
    STMFD   r13!,{r12}

    MOV     r12,r1;
    SMC     #0

    /* Restore r12 */
    LDMFD   r13!,{r12}
    
    /* Return to caller */
    BX      lr
}
#endif /* ESAL_AR_STK_V7_SUPPORT */


#ifdef ESAL_CO_XSCALE_CORE
/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_RTE_BRK_Execute
*
*   DESCRIPTION
*
*       This function executes a breakpoint instruction.
*
*   CALLED BY
*
*       ESAL_TS_RTE_BRK_EXECUTE macro
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       unused
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
__asm VOID  ESAL_TS_RTE_BRK_Execute(UINT32 unused)
{
    /* Force ARM mode for this function */
    CODE32

    /* Execute breakpoint instruction */
    BKPT    #ESAL_AR_ISR_XSCALE_VECT_INSTALL_BRK
            
    /* Return to caller */
    BX      lr
}
#endif /* ESAL_CO_XSCALE_CORE */

