/*---------------------------------------------------------------------------*
  Project:  NitroSDK - OS
  File:     os_arena.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: os_arena.c,v $
  Revision 1.33  2004/11/02 11:58:22  yada
  fix a little

  Revision 1.32  2004/10/04 06:16:53  yada
  protection region for MainRAM  is set to 4MB if specified

  Revision 1.31  2004/08/31 01:02:51  yada
  add functions to get/set initial setting in specified arena

  Revision 1.30  2004/08/18 02:45:44  yada
  change sub-processor private arena of mainRAM

  Revision 1.29  2004/07/29 07:25:56  yada
  remove OS_Printf() in OS_GetArenaLo()

  Revision 1.28  2004/07/20 11:41:23  yada
  in case sysStack=0 (means maximum size), DTCM arena lo
  was set to HW_DTCM. fix to SDK_AUTOLOAD_DTCM_BSS_END.

  Revision 1.27  2004/07/12 12:25:21  yasu
  Fix arena setting

  Revision 1.26  2004/07/10 04:07:56  yasu
  Add short comment

  Revision 1.25  2004/07/08 02:22:21  yada
  add OS_InitArenaEx(),  setting for extended mainRam arena

  Revision 1.24  2004/07/01 09:40:31  yasu
  add TARGET_ROM

  Revision 1.23  2004/05/27 04:40:25  yada
  change setting arena rule for ARM7

  Revision 1.22  2004/04/14 00:07:33  yada
  add checkcode in allocation memory from arena

  Revision 1.21  2004/02/20 12:23:47  yada
  Revise initialization bug

  Revision 1.20  2004/02/19 08:41:41  yada
  Separated ARM7 ARM9 initialization

  Revision 1.19  2004/02/16 09:39:46  yada
  Revise Sys stack

  Revision 1.18  2004/02/14 08:23:12  yasu
  comment out TCM arena setting code on ARM7 due to pass nightly build

  Revision 1.17  2004/02/14 07:05:47  yasu
  fix indent level

  Revision 1.16  2004/02/13 10:38:26  yada
  Changed the number of arenas from 6 to 9.

  Revision 1.15  2004/02/13 04:05:36  yada
  Take into consideration separation according to ARM9 ARM7

  Revision 1.14  2004/02/05 07:09:02  yasu
  change SDK prefix iris -> nitro

  Revision 1.13  2004/01/18 01:25:40  yada
  Only cleaned up indentation, etc

  Revision 1.12  2004/01/16 11:47:20  yada
  Revision due to moving arena information position

  Revision 1.11  2004/01/16 08:10:03  yada
  Revised ASSERT in DTCM arena setting

  Revision 1.10  2004/01/16 07:25:26  yada
  Revised arena initialized flag position

  Revision 1.9  2004/01/16 04:07:39  yada
  Changed so idle Thread stack is taken from lcf definition

  Revision 1.8  2004/01/15 12:13:55  yada
  Made to take arena structure in SystemWork

  Revision 1.7  2004/01/15 10:07:25  yasu
  Use SDK_MAIN_ARENA_LO from LCF to resolve arena region

  Revision 1.6  2004/01/07 02:52:04  yada
  Minor change

  Revision 1.5  2004/01/07 01:42:40  yada
  Changed part of arena initialize

  Revision 1.4  2004/01/05 04:28:24  yada
  Input dummy initialization for each arena

  Revision 1.3  2003/12/26 08:21:13  yada
  Fixed problem that occurs during Assert

  Revision 1.2  2003/12/26 08:11:35  yada
  Changed ID format: int -> OSArenaId

  Revision 1.1  2003/12/26 06:42:33  yada
  Initial version


  $NoKeywords: $
 *---------------------------------------------------------------------------*/
#include <nitro.h>

#define OSi_TRUNC(n, a)     (((u32) (n)) & ~((a) - 1))
#define OSi_ROUND(n, a)     (((u32) (n) + (a) - 1) & ~((a) - 1))

#define OS_ERR_GETARENAHI_INIT     "OS_GetArenaHi: init in advance"
#define OS_ERR_GETARENAHI_INVALID  "OS_GetArenaHi: invalid arena (hi<lo)"
#define OS_ERR_GETARENALO_INIT     "OS_GetArenaLo: init in advance"
#define OS_ERR_GETARENALO_INVALID  "OS_GetArenaHi: invalid arena (hi<lo)"

#define SDK_ARENAID_ASSERT( id )        SDK_ASSERT( (u32)id < OS_ARENA_MAX )


//---- Arena infomation Address ( allocated in Shared Memory )
#define OSi_GetArenaInfo()              (*(OSArenaInfo*)HW_ARENA_INFO_BUF)


//---- default Main Arena Address (for arm9)
extern  void    SDK_MAIN_ARENA_LO(void);
#define OSi_MAIN_ARENA_LO_DEFAULT               ((u32)SDK_MAIN_ARENA_LO)  // Defined by LCF
#define OSi_MAIN_ARENA_HI_DEFAULT               HW_MAIN_MEM_MAIN_END

//---- default Main Arena Address (for arm7)
#ifdef SDK_ARM7
extern  void    SDK_SUBPRIV_ARENA_LO(void);
#define OSi_MAIN_SUBPRIV_ARENA_LO_DEFAULT       ((u32)SDK_SUBPRIV_ARENA_LO) // Defined by LCF
#define OSi_MAIN_SUBPRIV_ARENA_HI_DEFAULT       HW_MAIN_MEM_SUB_END
#endif

//---- default Extended Main Arena Address
#define OSi_MAINEX_ARENA_LO_DEFAULT             (HW_MAIN_MEM + HW_MAIN_MEM_SIZE)
#define OSi_MAINEX_ARENA_HI_DEFAULT             HW_MAIN_MEM_DEBUGGER

//---- default common WorkRAM (for Main-Proc) Arena Address
#define OSi_WRAM_MAIN_ARENA_LO_DEFAULT          HW_WRAM
#define OSi_WRAM_MAIN_ARENA_HI_DEFAULT          HW_WRAM

//---- default common WorkRAM (for Sub-Proc) Arena Address
#ifdef SDK_ARM7
extern  void    SDK_WRAM_ARENA_LO(void);
#define OSi_WRAM_SUB_ARENA_LO_DEFAULT           ((u32)SDK_WRAM_ARENA_LO)  // Defined by LCF
#define OSi_WRAM_SUB_ARENA_HI_DEFAULT           HW_WRAM_END
#endif

//---- default Sub-Proc Private WorkRAM Arena Address (for from ARM9)
#ifdef SDK_ARM7
#define OSi_WRAM_SUBPRIV_ARENA_LO_DEFAULT       HW_PRV_WRAM
#define OSi_WRAM_SUBPRIV_ARENA_HI_DEFAULT       HW_PRV_WRAM_END
#endif

//---- stack size indication from LCF
extern  void    SDK_SYS_STACKSIZE(void);
#define OSi_SYS_STACKSIZE               ((int)SDK_SYS_STACKSIZE)
extern  void    SDK_IRQ_STACKSIZE(void);
#define OSi_IRQ_STACKSIZE               ((int)SDK_IRQ_STACKSIZE)

static BOOL OSi_Initialized = FALSE;

#ifdef SDK_ARM9
void SDK_AUTOLOAD_ITCM_BSS_END(void);       // defined in LCF
void SDK_AUTOLOAD_DTCM_BSS_END(void);       // defined in LCF
#endif

//================================================================================
//        INIT
//================================================================================
#ifdef SDK_ARM9
/*---------------------------------------------------------------------------*
  Name:         OS_InitArena

  Description:  Initialize Arena

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_InitArena( void )
{
    //---- check if arena initialized
    if ( OSi_Initialized )
    {
      return;
    }
    OSi_Initialized = TRUE;

    //---------------- set Arena Lo/Hi addresses
    //---- MainRam, Main-Processor private
    OS_InitArenaHiAndLo( OS_ARENA_MAIN );

    //---- Extended MainRam (temporary. set in OS_InitArenaEx later.)
    OS_SetArenaLo( OS_ARENA_MAINEX, (void*)0 );
    OS_SetArenaHi( OS_ARENA_MAINEX, (void*)0 );

    //---- ITCM
    OS_InitArenaHiAndLo( OS_ARENA_ITCM );

    //---- DTCM
    OS_InitArenaHiAndLo( OS_ARENA_DTCM );

    //---- set Shared Memory Arena
    OS_InitArenaHiAndLo( OS_ARENA_SHARED );

    //---- Work RAM, for Main-Processor
    OS_InitArenaHiAndLo( OS_ARENA_WRAM_MAIN );
}
#endif

#ifdef SDK_ARM7
/*---------------------------------------------------------------------------*
  Name:         OS_InitArena

  Description:  Initialize WRAM_SUBPRIV_ARENA

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_InitArena( void )
{
    //---- check if arena initialized
    if ( OSi_Initialized )
    {
      return;
    }
    OSi_Initialized = TRUE;

    //---------------- set Arena Lo/Hi addresses
    //---- MainRam, Sub-Processor private
    OS_InitArenaHiAndLo( OS_ARENA_MAIN_SUBPRIV );

    //---- Work RAM, for Sub-Processor
    OS_InitArenaHiAndLo( OS_ARENA_WRAM_SUB );

    //---- Sub-Processor private Work RAM
    OS_InitArenaHiAndLo( OS_ARENA_WRAM_SUBPRIV );
}
#endif

#ifdef SDK_ARM9
/*---------------------------------------------------------------------------*
  Name:         OS_InitArenaEx

  Description:  Initialize extended MainRam Arena 
                OS_InitArena() must be called before
  
  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_InitArenaEx( void )
{
    OS_InitArenaHiAndLo( OS_ARENA_MAINEX );

#ifdef SDK_4M
    //---- no Extended MainRam arena
    OS_SetProtectionRegion( 1, HW_MAIN_MEM_MAIN, 4MB );
#else
    if ( ( OS_GetConsoleType() & OS_CONSOLE_SIZE_MASK ) == OS_CONSOLE_SIZE_4MB )
    {
        OS_SetProtectionRegion( 1, HW_MAIN_MEM_MAIN, 4MB );
    }
#endif
}
#endif // ifdef SDK_ARM9

//================================================================================
//        GET ARENA INFO
//================================================================================
/*---------------------------------------------------------------------------*
  Name:         OS_GetArenaInfo

  Description:  Get the pointer of Arena structure

  Arguments:    None.

  Returns:      the pointer of Arena structure
 *---------------------------------------------------------------------------*/
void* OS_GetArenaInfo( void )
{
    return (void*)&OSi_GetArenaInfo();
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetArenaHi

  Description:  Get the high boundary of the arena

  Arguments:    id : arena ID

  Returns:      the high boundary of the arena
 *---------------------------------------------------------------------------*/
void* OS_GetArenaHi( OSArenaId id )
{
    SDK_ASSERT( OSi_Initialized );
    SDK_ARENAID_ASSERT( id );
    SDK_ASSERTMSG( (u32) OSi_GetArenaInfo().lo[id] != 0xffffffff, OS_ERR_GETARENALO_INIT );
    SDK_ASSERTMSG( (u32) OSi_GetArenaInfo().lo[id] <= (u32) OSi_GetArenaInfo().hi[id], OS_ERR_GETARENALO_INVALID );

    return OSi_GetArenaInfo().hi[id];
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetArenaLo

  Description:  Get the low boundary of the arena

  Arguments:    id : arena ID

  Returns:      the low boundary of the arena
 *---------------------------------------------------------------------------*/
void* OS_GetArenaLo( OSArenaId id )
{
    SDK_ASSERT( OSi_Initialized );
    SDK_ARENAID_ASSERT( id );
    SDK_ASSERTMSG( (u32) OSi_GetArenaInfo().lo[id] != 0xffffffff, OS_ERR_GETARENALO_INIT );
    SDK_ASSERTMSG( (u32) OSi_GetArenaInfo().lo[id] <= (u32) OSi_GetArenaInfo().hi[id], OS_ERR_GETARENALO_INVALID );

    return OSi_GetArenaInfo().lo[id];
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetInitArenaHi

  Description:  Get the initial high boundary of the arena

  Arguments:    id : arena ID

  Returns:      the initial high boundary of the arena
 *---------------------------------------------------------------------------*/
void* OS_GetInitArenaHi( OSArenaId id )
{
  SDK_ASSERT( OSi_Initialized );
  SDK_ARENAID_ASSERT( id );

  switch( id )
  {
#ifdef SDK_ARM9
    case OS_ARENA_MAIN:
      return (void*)OSi_MAIN_ARENA_HI_DEFAULT;
    case OS_ARENA_MAINEX:
#ifdef SDK_4M
      return (void*)0;
#else
      if ( ( OS_GetConsoleType() & OS_CONSOLE_SIZE_MASK ) == OS_CONSOLE_SIZE_4MB )
      {
        return (void*)0;
      }
      else
      {
        return (void*)OSi_MAINEX_ARENA_HI_DEFAULT;
      }
#endif
    case OS_ARENA_ITCM:
      return (void*)HW_ITCM_ARENA_HI_DEFAULT;
    case OS_ARENA_DTCM:
      {
        u32   irqStackLo;
        u32   sysStackLo;

        irqStackLo = (u32)HW_DTCM_IRQ_STACK_END - OSi_IRQ_STACKSIZE;

        if ( OSi_SYS_STACKSIZE == 0 ) // maximum SysStack in DTCM
        {
          sysStackLo = HW_DTCM;
          if ( sysStackLo < (u32)SDK_AUTOLOAD_DTCM_BSS_END )
          {
            sysStackLo = (u32)SDK_AUTOLOAD_DTCM_BSS_END;
          }
        }
        else if ( OSi_SYS_STACKSIZE < 0 )
        {
          sysStackLo = (u32)HW_DTCM_ARENA_HI_DEFAULT - OSi_SYS_STACKSIZE;
        }
        else
        {
          sysStackLo = irqStackLo - OSi_SYS_STACKSIZE;
        }
        SDK_ASSERT( (u32)HW_DTCM_ARENA_LO_DEFAULT <= sysStackLo && sysStackLo < irqStackLo );
        return (void*)sysStackLo;
      }
    case OS_ARENA_SHARED:
      return (void*)HW_SHARED_ARENA_HI_DEFAULT;
    case OS_ARENA_WRAM_MAIN:
      return (void*)(void*)OSi_WRAM_MAIN_ARENA_HI_DEFAULT;
#else
    case OS_ARENA_MAIN_SUBPRIV:
      return (void*)OSi_MAIN_SUBPRIV_ARENA_HI_DEFAULT;
    case OS_ARENA_WRAM_SUB:
        return (void*)OSi_WRAM_SUB_ARENA_HI_DEFAULT;
    case OS_ARENA_WRAM_SUBPRIV:
      {
        u32   irqStackLo;
        u32   sysStackLo;
        
        // ---- Sub-Processor private Work RAM
        // decide sysStackLo
        irqStackLo = (u32)HW_PRV_WRAM_IRQ_STACK_END - OSi_IRQ_STACKSIZE;
        if ( OSi_SYS_STACKSIZE == 0 ) // maximum SysStack in SUBPRIV-WRAM
        {
          sysStackLo = HW_PRV_WRAM;
        }
        else if ( OSi_SYS_STACKSIZE < 0 )
        {
          sysStackLo = (u32)HW_PRV_WRAM - OSi_SYS_STACKSIZE;
        }
        else
        {
          sysStackLo = irqStackLo - OSi_SYS_STACKSIZE;
        }
        SDK_ASSERT( (u32)HW_PRV_WRAM <= sysStackLo && sysStackLo < irqStackLo );
        return (void*)sysStackLo;
      }
#endif
    default:
      SDK_WARNING( 0, "Bad arena id" );
  }

  return NULL;
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetInitArenaLo

  Description:  Get the initial low boundary of the arena

  Arguments:    id : arena ID

  Returns:      the initial low boundary of the arena
 *---------------------------------------------------------------------------*/
void* OS_GetInitArenaLo( OSArenaId id )
{
  SDK_ASSERT( OSi_Initialized );
  SDK_ARENAID_ASSERT( id );

  switch( id )
  {
#ifdef SDK_ARM9
    case OS_ARENA_MAIN:
      return (void*)OSi_MAIN_ARENA_LO_DEFAULT;
    case OS_ARENA_MAINEX:
#ifdef SDK_4M
      return (void*)0;
#else
      if ( ( OS_GetConsoleType() & OS_CONSOLE_SIZE_MASK ) == OS_CONSOLE_SIZE_4MB )
      {
        return (void*)0;
      }
      else
      {
        return (void*)OSi_MAINEX_ARENA_LO_DEFAULT;
      }
#endif
    case OS_ARENA_ITCM:
      return (void*)SDK_AUTOLOAD_ITCM_BSS_END;
    case OS_ARENA_DTCM:
      return (void*)SDK_AUTOLOAD_DTCM_BSS_END;
    case OS_ARENA_SHARED:
      return (void*)HW_SHARED_ARENA_LO_DEFAULT;
    case OS_ARENA_WRAM_MAIN:
      return (void*)OSi_WRAM_MAIN_ARENA_LO_DEFAULT;
#else
    case OS_ARENA_MAIN_SUBPRIV:
      return (void*)OSi_MAIN_SUBPRIV_ARENA_LO_DEFAULT;
    case OS_ARENA_WRAM_SUB:
      {
        u32   wramSubLo = OSi_WRAM_SUB_ARENA_LO_DEFAULT;
        if ( (u32)HW_WRAM_END < (u32) wramSubLo )
        {
          wramSubLo = (u32)HW_WRAM_END;
        }
        return (void*)wramSubLo;
      }
    case OS_ARENA_WRAM_SUBPRIV:
      {
        u32   privWramLo = OSi_WRAM_SUBPRIV_ARENA_LO_DEFAULT;
        if ( (u32)SDK_WRAM_ARENA_LO > (u32) privWramLo )
        {
          privWramLo = (u32)SDK_WRAM_ARENA_LO;
        }
        return (void*)privWramLo;
      }
#endif
    default:
      SDK_WARNING( 0, "Bad arena id" );
  }

  return NULL;
}

//================================================================================
//        SET ARENA INFO
//================================================================================
/*---------------------------------------------------------------------------*
  Name:         OS_SetArenaHi

  Description:  Set the high boundary of the arena

  Arguments:    id    : arena ID
                newHi : New high boundary of the arena.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_SetArenaHi( OSArenaId id, void* newHi )
{
    SDK_ASSERT( OSi_Initialized );
    SDK_ARENAID_ASSERT( id );

    OSi_GetArenaInfo().hi[ id ] = newHi;
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetArenaLo

  Description:  Set the low boundary of the arena

  Arguments:    id    : arena ID
                newLo : New low boundary of the arena.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_SetArenaLo( OSArenaId id, void* newLo )
{
    SDK_ASSERT( OSi_Initialized );
    SDK_ARENAID_ASSERT( id );

    OSi_GetArenaInfo().lo[ id ] = newLo;
}


//================================================================================
//        ALLOC
//================================================================================
/*---------------------------------------------------------------------------*
  Name:         OS_AllocFromArenaLo

  Description:  Allocates /size/ bytes from the low boundary of the arena.
                Some additional memory will also be consumed from arena for
                the alignment.

  Arguments:    id    : arena ID
                size  : Size of object to be allocated
                align : Alignment of object and new arena boundary to
                        be set (must be power of 2).

  Returns:      a pointer to the allocated space aligned with /align/ bytes
                boundaries. The new low boundary of the arena is also
                aligned with /align/ bytes boundaries.

                NULL if cannot allocate.
 *---------------------------------------------------------------------------*/
void* OS_AllocFromArenaLo( OSArenaId id, u32 size, u32 align )
{
    void* ptr;
    u8*   arenaLo;

    ptr = OS_GetArenaLo( id );
    if ( ! ptr )
    {
      return NULL;
    }

    arenaLo = ptr = (void*) OSi_ROUND( ptr, align );
    arenaLo += size;
    arenaLo = (u8*) OSi_ROUND( arenaLo, align );

    if ( arenaLo > OS_GetArenaHi( id ) )
    {
      return NULL;
    }

    OS_SetArenaLo( id, arenaLo );

    return ptr;
}

/*---------------------------------------------------------------------------*
  Name:         OS_AllocFromArenaHi

  Description:  Allocates /size/ bytes from the high boundary of the arena.
                Some additional memory will also be consumed from arena for
                the alignment.

  Arguments:    id    : arena ID
                size  : Size of object to be allocated
                align : Alignment of object and new arena boundary to
                        be set (must be power of 2).

  Returns:      a pointer to the allocated space aligned with /align/ bytes
                boundaries. The new high boundary of the arena is also
                aligned with /align/ bytes boundaries.

                NULL if cannot allocate.
 *---------------------------------------------------------------------------*/
void* OS_AllocFromArenaHi( OSArenaId id, u32 size, u32 align )
{
    void* ptr;
    u8*   arenaHi;

    arenaHi = OS_GetArenaHi( id );
    if ( ! arenaHi )
    {
      return NULL;
    }

    arenaHi = (u8*) OSi_TRUNC( arenaHi, align );
    arenaHi -= size;
    arenaHi = ptr = (void*) OSi_TRUNC( arenaHi, align );

    if ( arenaHi < OS_GetArenaLo( id ) )
    {
      return NULL;
    }

    OS_SetArenaHi( id, arenaHi );

    return ptr;
}
