/*---------------------------------------------------------------------------*
  Project:  NitroSDK - OS
  File:     os_thread.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_thread.c,v $
  Revision 1.71  2004/11/02 09:53:44  yada
  just fix comment

  Revision 1.70  2004/10/04 10:06:30  yasu
  support SDK_NO_MESSAGE

  Revision 1.69  2004/10/04 02:09:40  yada
  initialize profiler and joinQueue member in OS_CreaetThread()

  Revision 1.68  2004/09/02 06:23:35  yada
  only change comment

  Revision 1.67  2004/07/02 08:07:19  yada
  OS_WakeupThread( queue ) skips codes if queue == 0

  Revision 1.66  2004/07/01 09:49:44  yada
  let OS_JoinThread() be thread safe

  Revision 1.65  2004/07/01 09:42:28  yasu
  support autoload

  Revision 1.64  2004/07/01 05:44:18  yada
  fix comment

  Revision 1.63  2004/06/28 06:30:07  yada
  fix comment typo, and add OSi_DestroyThread

  Revision 1.62  2004/06/25 06:09:18  yada
  exit OS_JoinThread() if already terminated

  Revision 1.61  2004/06/25 04:41:54  yada
  add OS_JoinThread(), OS_IsThreadTerminate()

  Revision 1.60  2004/06/11 06:42:06  yada
  tune up a little

  Revision 1.59  2004/06/10 10:43:06  yada
  fix OSi_IdleThreadProc(). Test code has mixed.

  Revision 1.58  2004/06/08 00:38:48  yasu
  Don't create idle thread for ARM7

  Revision 1.57  2004/06/08 00:26:10  yada
  add '#pragma unused()' for FINALROM

  Revision 1.56  2004/06/03 04:24:35  yada
  put system callback in switching thread

  Revision 1.55  2004/05/26 12:42:04  yada
  only fix comment

  Revision 1.54  2004/05/26 12:13:19  yada
  fix bug in OS_GetNumberOfThread()

  Revision 1.53  2004/05/26 05:17:05  yada
  add OS_GetNumberOfThread(), OS_THREAD_MAX_NUM changed 8 to 16

  Revision 1.52  2004/05/21 12:33:56  yada
  arrange idle thread stack size

  Revision 1.51  2004/05/18 12:16:32  yasu
  small fix

  Revision 1.50  2004/05/18 11:59:44  yasu
  add IRQ level counter 'irqDepth' in OSThreadInfo

  Revision 1.49  2004/05/12 05:13:20  yada
  modify SDK_XXX / OS_XXX option

  Revision 1.48  2004/05/10 12:04:44  yada
  consider for functionCost

  Revision 1.47  2004/04/30 02:17:20  yada
  add stack check function OS_GetStackStatus()

  Revision 1.46  2004/04/27 11:07:12  yada
  forbid idleThread to do OS_SetThreadPriority()

  Revision 1.45  2004/04/27 02:43:38  yada
  add function to get idle thread

  Revision 1.44  2004/04/26 07:36:38  yada
  add idle thread

  Revision 1.43  2004/04/14 02:57:50  yada
  CALLSTACK->CALLTRACE

  Revision 1.42  2004/04/12 12:12:50  yada
  add  callTrace

  Revision 1.41  2004/04/07 03:08:56  yasu
  fix in OS_ExitThread

  Revision 1.40  2004/04/02 12:47:18  yada
  Added the mutex queue to OSThread
  Set so that, with OS_ExitThread(), mutex during lock is released

  Revision 1.39  2004/03/31 09:25:14  yada
  add thread switch callback function

  Revision 1.38  2004/03/30 05:58:29  yada
  Added OS_IsThreadAvailable

  Revision 1.37  2004/03/18 13:00:08  yada
  Made revisions to the MI include

  Revision 1.36  2004/03/03 04:06:14  yada
  Added an assertion for when Tick and Alarm are not started up in OS_Sleep()

  Revision 1.35  2004/03/02 11:54:29  yada
  Added OS_Sleep()

  Revision 1.34  2004/02/26 12:13:15  yada
  Added assert to the priority field

  Revision 1.33  2004/02/26 10:52:11  yasu
  RollBack to 1.31

  Revision 1.32  2004/02/26 10:36:25  yasu
  Add idle thread

  Revision 1.31  2004/02/26 09:42:04  yada
  Changed the thread so that it manages the priority list

  Revision 1.30  2004/02/26 05:49:21  yada
  Added the startup function arguments to OS_CreateThread()

  Revision 1.29  2004/02/25 11:30:46  yada
  only fix comment

  Revision 1.28  2004/02/20 08:28:53  yasu
  MI_CpuClearFast -> MI_CpuClear32

  Revision 1.27  2004/02/19 02:41:15  yada
  Set the magic number for the stack check

  Revision 1.26  2004/02/17 09:30:44  yada
  Made changes to the thread information storage position on ARM9 and ARM7 

  Revision 1.25  2004/02/17 08:22:48  yada
  Made revisions to the IRQ stack position definition

  Revision 1.24  2004/02/16 00:18:32  yada
  Made the stack clear section into something considered by the check code

  Revision 1.23  2004/02/13 02:26:46  yada
  Made refinements to the idle thread stack definition

  Revision 1.22  2004/02/09 11:17:22  yasu
  include code32.h

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

  Revision 1.20  2004/01/21 12:02:05  yada
  Created a stack overflow check

  Revision 1.19  2004/01/18 02:25:37  yada
  Made cosmetic changes such as indentation, etc.

  Revision 1.18  2004/01/16 11:48:14  yada
  Corrected an error in the DTCM heap region

  Revision 1.17  2004/01/16 04:07:13  yada
  Made a change so that the idle Thread stack is taken from the lcf definition

  Revision 1.16  2004/01/07 10:57:23  yada
  Only fixed a comment

  Revision 1.15  2003/12/29 04:27:24  yasu
  workaround for OS_Warning, OS_Panic

  Revision 1.14  2003/12/25 07:31:26  yada
  Made changes due to the standardization of formatting rules

  Revision 1.13  2003/12/17 08:19:05  yasu
  Integrated OSi_isNeedRescheduling into OS_ThreadInfo
  Made changes to the OS_InitContext argument

  Revision 1.12  2003/12/17 06:07:48  yasu
  Made revisions in accordance with the changes to the constant name

  Revision 1.11  2003/12/16 11:28:07  yada
  Made changes due to the compilation of thread information

  Revision 1.10  2003/12/16 08:13:06  yada
  Changed the stack overflow check to SDK_ASSERTMSG()

  Revision 1.9  2003/12/16 07:16:06  yada
  Moved the thread region information to a thread structure rather than a context

  Revision 1.8  2003/12/16 00:17:44  yada
  Included the stack region information into the thread

  Revision 1.7  2003/12/12 04:40:06  yada
  Made revisions in accordance with the changes to the processor mode definition

  Revision 1.6  2003/12/11 11:55:08  yada
  Described the actual function, since OS_GetMaxThreadId() was changed so that it is not inline

  Revision 1.5  2003/12/01 04:14:52  yasu
  Changed the stack align to 4
  Made temporary changes so that the stack will clear under its own accord

  Revision 1.4  2003/11/30 05:10:42  yasu
  Made changes so that thread-related functions can be called from an interrupt service

  Revision 1.3  2003/11/30 04:19:25  yasu
  Made changes to the format of the OS_EnableInterrupt/DisableInterrupt return values

  Revision 1.2  2003/11/30 02:08:12  yada
  Added stack size to the OS_CreateThread() arguments, and made changes so that the stack clears when the thread is created

  Revision 1.1  2003/11/29 01:25:46  yada
  Made changes to file nomenclature

  Revision 1.9  2003/11/28 08:31:27  yada
  Debugging...

  Revision 1.8  2003/11/26 10:51:42  yasu
  Released OS_GetCurrentThread/OS_SetCurrentThread and made them into inline

  Revision 1.7  2003/11/26 09:31:15  yasu
  Split up OS_Reschedule and created OS_SelectThread

  Revision 1.6  2003/11/26 01:27:13  yasu
  Made changes to the OS_CreateThread interface
  Added OS_InitThread 

  Revision 1.5  2003/11/25 13:31:56  yasu
  Added ThreadQueue

  Revision 1.4  2003/11/25 09:26:37  yasu
  IdleThread formatting

  Revision 1.3  2003/11/25 06:28:32  yasu
  Made changes to the format of the OS_CreateThread argument

  Revision 1.2  2003/11/25 05:02:11  yasu
  Fixed bugs

  Revision 1.1  2003/11/25 04:31:57  yasu
  Added the initial version

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

//---------------------------------------------------------------------------

extern  void    SDK_SYS_STACKSIZE( void );
extern  void    SDK_IRQ_STACKSIZE( void );
#define OSi_SYS_STACKSIZE             ((u32)SDK_SYS_STACKSIZE)
#define OSi_IRQ_STACKSIZE             ((u32)SDK_IRQ_STACKSIZE)

//---- Stack CheckNumber
#ifdef SDK_ARM9
#  define  OSi_STACK_CHECKNUM_BOTTOM     0xfddb597dUL
#  define  OSi_STACK_CHECKNUM_TOP        0x7bf9dd5bUL
#  define  OSi_STACK_CHECKNUM_WARN       0x597dfbd9UL
#else
#  define  OSi_STACK_CHECKNUM_BOTTOM     0xd73bfdf7UL
#  define  OSi_STACK_CHECKNUM_TOP        0xfbdd37bbUL
#  define  OSi_STACK_CHECKNUM_WARN       0xbdf7db3dUL
#endif

//---- defs for launcher thread stack
#ifdef SDK_ARM9
void SDK_AUTOLOAD_DTCM_END(void);    // defined in LCF
#  define  OSi_LAUNCHER_STACK_LO_DEFAULT    SDK_AUTOLOAD_DTCM_END
#  define  OSi_LAUNCHER_STACK_HI_DEFAULT    SDK_AUTOLOAD_DTCM_END
#  define  OSi_LAUNCHER_STACK_HI_MAX        (HW_DTCM_SVC_STACK_END - OSi_IRQ_STACKSIZE)
#  define  OSi_LAUNCHER_STACK_BOTTOM        (HW_DTCM_SVC_STACK_END - OSi_IRQ_STACKSIZE)
#else
#  define  OSi_LAUNCHER_STACK_LO_DEFAULT    HW_WRAM
#  define  OSi_LAUNCHER_STACK_HI_DEFAULT    HW_WRAM
#  define  OSi_LAUNCHER_STACK_HI_MAX        (HW_PRV_WRAM_IRQ_STACK_END - OSi_IRQ_STACKSIZE)
#  define  OSi_LAUNCHER_STACK_BOTTOM        (HW_PRV_WRAM_IRQ_STACK_END - OSi_IRQ_STACKSIZE)
#endif

//---- defs for idle thread
// for checkNumber and SVC stack (if defined)
#ifdef SDK_CONTEXT_HAS_SP_SVC
#define OSi_IDLE_CHECKNUM_SIZE  ( sizeof(u32)*2 + HW_SVC_STACK_SIZE )
#else
#define OSi_IDLE_CHECKNUM_SIZE  ( sizeof(u32)*2 )
#endif
// for SVC to stack out registers
#ifdef SDK_ARM9
#define OSi_IDLE_SVC_SIZE  ( sizeof(u32)*32 ) // arm9 svc stacks 14 words and makes in 8byte-alignment
#else
#define OSi_IDLE_SVC_SIZE  ( sizeof(u32)*16 ) // arm7 svc stacks 14 words
#endif
// stack size of idle thread
#define OSi_IDLE_THREAD_STACK_SIZE    ( OSi_IDLE_CHECKNUM_SIZE + OSi_IDLE_SVC_SIZE )

//---- threads
OSThread      OSi_LauncherThread;
OSThread      OSi_IdleThread;

//---- thread information
OSThreadInfo  OSi_ThreadInfo;

//---- current thread pointer (for quick reference)
OSThread**    OSi_CurrentThreadPtr;
#define       OSi_GetCurrentThread()    (*OSi_CurrentThreadPtr)

//---- thread initialization flag
BOOL          OSi_IsThreadInitialized = FALSE;

//---- idle thread stack
u32           OSi_IdleThreadStack[ OSi_IDLE_THREAD_STACK_SIZE / sizeof(u32) ];

//---- system callback in switch thread ( maybe for profile )
void*         OSi_SystemCallbackInSwitchThread = NULL;

static int   OSi_SearchFreeEntry( void );
static void  OSi_InsertThreadToList( OSThread* thread );
static void  OSi_RemoveThreadFromList( OSThread* thread );
static void  OSi_SleepAlarmCallback( void* arg );
static void  OSi_IdleThreadProc( void* );
void         OSi_SetSystemCallbackInSwitchThread( void* callback );



/*---------------------------------------------------------------------------*
  Name:         OSi_SetSystemCallbackInSwitchThread

  Description:  set system callback in switching thread

  Arguments:    callback

  Returns:      None
 *---------------------------------------------------------------------------*/
void OSi_SetSystemCallbackInSwitchThread( void* callback )
{
  OSi_SystemCallbackInSwitchThread = callback;
}

/*---------------------------------------------------------------------------*
  Name:         OSi_SearchFreeEntry

  Description:  search free thread entry area

  Arguments:    None

  Returns:      0 - OS_THREAD_MAX_NUM-1  ... entry index
                -1                       ... not found
 *---------------------------------------------------------------------------*/
static int OSi_SearchFreeEntry( void )
{
    int i;

    for( i=0; i<OS_THREAD_MAX_NUM; i++ )
    {
        if ( ! OSi_ThreadInfo.entry[ i ] )
        {
            return i;
        }
    }
    return -1; // not found
}

/*---------------------------------------------------------------------------*
  Name:         OSi_InsertThreadToList

  Description:  insert thread to thread list which is arranged by priority

  Arguments:    pointer of thread

  Returns:      None
 *---------------------------------------------------------------------------*/
static void OSi_InsertThreadToList( OSThread* thread )
{
    OSThread* t   = OSi_ThreadInfo.list;
    OSThread* pre = NULL;

    while( t  &&  t->priority < thread->priority )
    {
        pre = t;
        t = t->next;
    }

    if ( ! pre )
    {
        thread->next = OSi_ThreadInfo.list;
        OSi_ThreadInfo.list = thread;
    }
    else
    {
        thread->next = pre->next;
        pre->next = thread;
    }
}

/*---------------------------------------------------------------------------*
  Name:         OSi_RemoveThreadFromList

  Description:  remove thread from thread list

  Arguments:    pointer of thread

  Returns:      None
 *---------------------------------------------------------------------------*/
static void OSi_RemoveThreadFromList( OSThread* thread )
{
    OSThread* t = OSi_ThreadInfo.list;
    OSThread* pre = NULL;

    while( t && t != thread )
    {
        pre = t;
        t = t->next;
    }

    SDK_ASSERTMSG( t, "Cannot remove thread from list." );

    if ( ! pre )
    {
        OSi_ThreadInfo.list = thread->next;
    }
    else
    {
        pre->next = thread->next;
    }
}


/*---------------------------------------------------------------------------*
  Name:         OS_InitThread

  Description:  Initialize thread system

  Arguments:    None

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_InitThread( void )
{
    void* stackLo;
    int   i;

    if ( OSi_IsThreadInitialized ){ return; }
    OSi_IsThreadInitialized = TRUE;

    //---- Set thread info
    for( i=0; i<OS_THREAD_MAX_NUM; i++ )
    {
        OSi_ThreadInfo.entry[i] = NULL;
    }

    //---- set pointer to current thread buffer
    //     (for quick reference)
    OSi_CurrentThreadPtr = &(OSi_ThreadInfo.current);

    //---- Setup launcher thread
    OSi_LauncherThread.priority = OS_THREAD_LAUNCHER_PRIORITY;
    OSi_LauncherThread.id       = 0;
    OSi_LauncherThread.state    = OS_THREAD_STATE_READY;
    OSi_LauncherThread.next     = NULL;

    //---- clear profile pointer
    OSi_LauncherThread.profiler = NULL;

    //---- clear thread entry and listPtr
    OSi_ThreadInfo.entry[0] = &OSi_LauncherThread;
    OSi_ThreadInfo.list     = &OSi_LauncherThread;

    //---- let launch thread be current
    OS_SetCurrentThread( &OSi_LauncherThread );

    //---- StackLo
    stackLo = (OSi_SYS_STACKSIZE <= 0)?
      (void*)( (u32)OSi_LAUNCHER_STACK_HI_DEFAULT - OSi_SYS_STACKSIZE ):
      (void*)( (u32)OSi_LAUNCHER_STACK_HI_MAX     - OSi_SYS_STACKSIZE );
    SDK_ASSERT( (u32)OSi_LAUNCHER_STACK_LO_DEFAULT <= (u32)stackLo && (u32)stackLo <= (u32)OSi_LAUNCHER_STACK_HI_MAX );

    //---- set Stack Bottom & Top
    OSi_LauncherThread.stackBottom = OSi_LAUNCHER_STACK_BOTTOM;
    OSi_LauncherThread.stackTop    = (u32)stackLo;
    OSi_LauncherThread.stackWarningOffset = 0;

    //---- Set Stack CheckNumber
    *(u32*)(OSi_LauncherThread.stackBottom - sizeof(u32)) = OSi_STACK_CHECKNUM_BOTTOM;
    *(u32*)OSi_LauncherThread.stackTop                    = OSi_STACK_CHECKNUM_TOP;

    //---- clear join queue
    OS_InitThreadQueue( &OSi_LauncherThread.joinQueue );

    //---- max number of thread
    OSi_ThreadInfo.max_entry = OS_THREAD_MAX_NUM;
    
    //---- around IRQ
    OSi_ThreadInfo.isNeedRescheduling = FALSE;
    OSi_ThreadInfo.irqDepth = 0;

    //---- store thread info pointer
#ifdef SDK_ARM9
    OS_GetSystemWork()->threadinfo_mainp = &OSi_ThreadInfo;
#else
    OS_GetSystemWork()->threadinfo_subp  = &OSi_ThreadInfo;
#endif

    //---- set thread switch callback
    (void)OS_SetSwitchThreadCallback( NULL );

    //---- idle thread only for ARM9
#ifdef SDK_ARM9
    OS_CreateThread( &OSi_IdleThread,
                     OSi_IdleThreadProc,
                     (void*)NULL,
                     OSi_IdleThreadStack + OSi_IDLE_THREAD_STACK_SIZE/sizeof(u32),
                     OSi_IDLE_THREAD_STACK_SIZE,
                     OS_THREAD_PRIORITY_MAX /*pseudo. change at next line.*/ );
    OSi_IdleThread.priority = OS_THREAD_PRIORITY_MAX + 1;   // lower priority than the lowest (=OS_THREAD_PRIORITY_MAX)
    OSi_IdleThread.state = OS_THREAD_STATE_READY;
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_IsThreadAvailable

  Description:  check if thread system is available

  Arguments:    None

  Returns:      TRUE if available, FALSE if not
 *---------------------------------------------------------------------------*/
asm BOOL OS_IsThreadAvailable( void )
{
  ldr      r0, =OSi_IsThreadInitialized
  ldr      r0, [r0, #0]
  bx       lr
}

/*---------------------------------------------------------------------------*
  Name:         OS_CreateThread

  Description:  Creates a new thread

  Arguments:    thread      pointer of thread structure
                func        function to start thread
                arg         argument for func
                stack       stack bottom address
                stackSize   stack size (byte. must be aligned by 4)
                prio        thread priority

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_CreateThread( OSThread*  thread,
                      void       (*func)(void*),
                      void*      arg,
                      void*      stack,
                      u32        stackSize,
                      u32        prio )
{
#define STACK_ALIGN 4

    OSIntrMode  enable;
    int         index;

    SDK_ASSERTMSG( OSi_GetCurrentThread(), "thread system were not initialized" );
    SDK_ASSERTMSG( OS_THREAD_PRIORITY_MIN <= prio && prio <= OS_THREAD_PRIORITY_MAX, "invalid priority" );
    SDK_ASSERTMSG( stackSize % STACK_ALIGN == 0, "stack size must be aligned by %d", STACK_ALIGN );
    SDK_ASSERTMSG( (u32)stack % STACK_ALIGN == 0, "stack must be aligned by %d", STACK_ALIGN );

    enable = OS_DisableInterrupts();

    //---- search free entry
    if ( ( index = OSi_SearchFreeEntry() ) < 0 )
    {
        SDK_ASSERTMSG( index >= 0, "OS_CreateThread: thread entry not allocated" );
        (void)OS_RestoreInterrupts(enable);
        return;
    }

    //---- setup thread
    thread->priority = prio;
    thread->id       = (u32)index;
    thread->state    = OS_THREAD_STATE_WAITING;

    //---- clear profile pointer
    thread->profiler = NULL;

    //---- set thread entry and listPtr
    OSi_ThreadInfo.entry[index] = thread;
    OSi_InsertThreadToList( thread );

    //---- set Stack Bottom & Top
    thread->stackBottom = (u32)stack;
    thread->stackTop    = (u32)stack - stackSize;
    thread->stackWarningOffset = 0;

    //---- Set Stack CheckNumber
    *(u32*)(thread->stackBottom - sizeof(u32)) = OSi_STACK_CHECKNUM_BOTTOM;
    *(u32*)thread->stackTop                    = OSi_STACK_CHECKNUM_TOP;

    //---- clear join queue
    OS_InitThreadQueue( &thread->joinQueue );

    //---- Init context
    OS_InitContext( &thread->context, (u32)func, (u32)stack-sizeof(u32) ); // minus for stack CheckNumber

    thread->context.r[0] = (u32)arg; // argument for func
    thread->context.lr   = (u32)OS_ExitThread;

    //---- clear Stack (except check code (=sizeof(u32)*2))
    MI_CpuClear32( (void*)( (u32)stack - stackSize + sizeof(u32) ), stackSize - sizeof(u32)*2 );

    //---- clear mutex
    thread->mutex = NULL;
    thread->mutexQueueHead = NULL;
    thread->mutexQueueTail = NULL;

    (void)OS_RestoreInterrupts(enable);
}


/*---------------------------------------------------------------------------*
  Name:         OS_ExitThread

  Description:  Terminates the current thread

  Arguments:    none

  Returns:      none
 *---------------------------------------------------------------------------*/
void OS_ExitThread( void )
{
    OSThread*   currentThread;

    (void)OS_DisableInterrupts();

    //---- current thread check
    currentThread = OSi_GetCurrentThread();
    SDK_ASSERT( currentThread );
    SDK_ASSERT( OSi_ThreadInfo.entry[currentThread->id] == currentThread );

    //---- Release all the locked mutexes by current thread
    OSi_UnlockAllMutex( currentThread );

    //---- remove from thread list
    OSi_RemoveThreadFromList( currentThread );

    //---- delete this thread
    OSi_ThreadInfo.entry[currentThread->id] = NULL;
    currentThread->state = OS_THREAD_STATE_TERMINATED;

    //---- wakeup thread waiting currentThread terminated
    OS_WakeupThread( &currentThread->joinQueue ); // possible to never return

    OS_RescheduleThread(); // never return
}

/*---------------------------------------------------------------------------*
  Name:         OS_JoinThread

  Description:  wait for specified thread to terminated

  Arguments:    thread : thread to wait to finish

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_JoinThread( OSThread* thread )
{
  BOOL enabled = OS_DisableInterrupts();

  SDK_ASSERT( thread );

  //---- skip if thread is terminated already
  if ( thread->state != OS_THREAD_STATE_TERMINATED )
  {
    OS_SleepThread( &thread->joinQueue );
  }

  (void)OS_RestoreInterrupts( enabled );
}

/*---------------------------------------------------------------------------*
  Name:         OS_IsThreadTerminated

  Description:  check thread status whether it's terminated

  Arguments:    thread : pointer to thread to be examined

  Returns:      TRUE if the thread is terminated. FALSE if not
 *---------------------------------------------------------------------------*/
BOOL OS_IsThreadTerminated( OSThread* thread )
{
    return ( thread->state == OS_THREAD_STATE_TERMINATED ) ? TRUE : FALSE;
}

/*---------------------------------------------------------------------------*
  Name:         OS_SleepThread

  Description:  Gets the current thread into sleep status

  Arguments:    waiting list queue

  Returns:      none
 *---------------------------------------------------------------------------*/
void OS_SleepThread( OSThreadQueue* queue )
{
    OSIntrMode    enable;
    OSThread*   currentThread;

    enable = OS_DisableInterrupts();
    {
        currentThread = OSi_GetCurrentThread();
        SDK_ASSERT( currentThread );

        if ( queue )
        {
            *queue |= (OSThreadQueue)( 1UL << currentThread->id );
        }

        currentThread->state = OS_THREAD_STATE_WAITING;
        OS_RescheduleThread();

    }
    (void)OS_RestoreInterrupts( enable );

}

/*---------------------------------------------------------------------------*
  Name:         OS_WakeupThread

  Description:  Gets the threads out of sleep status

  Arguments:    none

  Returns:      none
 *---------------------------------------------------------------------------*/
void OS_WakeupThread( OSThreadQueue* queue )
{
    OSIntrMode    enable;
    u32           mask;
    OSThread*     t;

    SDK_ASSERT( queue );
    enable = OS_DisableInterrupts();

    mask = (u32)*queue;
    if ( mask )
    {
        //---- wakeup threads
        t = OSi_ThreadInfo.list;
        while( t )
        {
            if ( mask & (1UL << t->id) )
            {
                t->state = OS_THREAD_STATE_READY;
            }

            t = t->next;
        }

        *queue = 0;

        OS_RescheduleThread();
    }

    (void)OS_RestoreInterrupts( enable );
}


/*---------------------------------------------------------------------------*
  Name:         OS_WakeupThreadDirect

  Description:  Gets the threads out of sleep status directly

  Arguments:    none

  Returns:      none
 *---------------------------------------------------------------------------*/
void OS_WakeupThreadDirect( OSThread* thread )
{
    OSIntrMode    enable;

    SDK_ASSERT( thread );
    enable = OS_DisableInterrupts();
    {
        thread->state = OS_THREAD_STATE_READY;
        OS_RescheduleThread();

    }
    (void)OS_RestoreInterrupts( enable );
}


/*---------------------------------------------------------------------------*
  Name:         OS_SelectThread

  Description:  Select the runnable thread highest priority

  Arguments:    None

  Returns:      Pointer to thread must run
                NULL if no thread candidate to run
 *---------------------------------------------------------------------------*/
OSThread* OS_SelectThread( void )
{
    OSThread* t = OSi_ThreadInfo.list;

    while( t && ! OS_IsThreadRunnable( t ) )
    {
        t = t->next;
    }

    return t;
}

/*---------------------------------------------------------------------------*
  Name:         OS_RescheduleThread

  Description:  Switch to the runnable thread highest priority

  Arguments:    None

  Returns:      None or Never return
 *---------------------------------------------------------------------------*/
void OS_RescheduleThread( void )
{
    OSThread*      currentThread;
    OSThread*      nextThread;
    OSThreadInfo*  info = &OSi_ThreadInfo;
    
    if ( info->irqDepth > 0 || OS_GetProcMode() == OS_PROCMODE_IRQ )
    {
        // If in IRQ, do rescheduling at end of IRQ handler
        info->isNeedRescheduling = TRUE;
        return;
    }
    else
    {
        currentThread = OSi_GetCurrentThread();
        nextThread    = OS_SelectThread();

        if ( currentThread == nextThread || ! nextThread ) // maybe nextThread != NULL
        {
            return;     // Don't have to switch the current context
        }

        if ( OS_SaveContext( &currentThread->context ) )
        {
            return;     // Return if go back via OS_LoadContext
        }

        //---- call thread switch callback for system
        if ( OSi_SystemCallbackInSwitchThread )
        {
          ( (OSSwitchThreadCallback)OSi_SystemCallbackInSwitchThread )( currentThread, nextThread );
        }

        //---- call thread switch callback for user
        if ( info->switchCallback )
        {
          ( (OSSwitchThreadCallback)info->switchCallback )( currentThread, nextThread );
        }

        OS_SetCurrentThread( nextThread );

        OS_LoadContext( &nextThread->context );
        // Never reached
    }
}


/*---------------------------------------------------------------------------*
  Name:         OS_DumpThreadList

  Description:  Dump thread list

  Arguments:    None

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_DumpThreadList( void )
{
#ifndef SDK_FINALROM
    int  i;

    OS_Printf( "----------------\n" );
    OS_Printf( "thread list top %08x\n", OSi_ThreadInfo.list );
    OS_Printf( "No:  address  prio     next\n" );

    for ( i = 0; i < OS_THREAD_MAX_NUM; i ++ )
    {
        OSThread* thread = OSi_ThreadInfo.entry[i];
        OS_Printf( "%02d: %08x %5d %08x\n", i, thread, (thread)?thread->priority:0, (thread)?thread->next:0 );
    }
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetNumberOfThread

  Description:  get number of thread whch exists in system

  Arguments:    None

  Returns:      number of thread which exists in system
 *---------------------------------------------------------------------------*/
int OS_GetNumberOfThread( void )
{
    int threads = 0;
    int i;

    for( i=0; i<OS_THREAD_MAX_NUM; i++ )
    {
      if ( OSi_ThreadInfo.entry[ i ] ) threads ++;
    }

    return threads;
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetStackStatus

  Description:  check thread stack. check each CheckNUM.
                return result.

  Arguments:    thread   thread checked

  Returns:      0 (OS_STACK_NO_ERROR)        no error
                OS_STACK_OVERFLOW            overflow 
                OS_STACK_ABOUT_TO_OVERFLOW   about to overflow
                OS_STACK_UNDERFLOW           underflow
 *---------------------------------------------------------------------------*/
OSStackStatus OS_GetStackStatus( OSThread* thread )
{
    //---- Check if overflow
    if ( *(u32*)(thread->stackTop) != OSi_STACK_CHECKNUM_TOP )
    {
      return OS_STACK_OVERFLOW;
    }
    //---- Check if about to overflow
    else if ( thread->stackWarningOffset
              && *(u32*)(thread->stackTop + thread->stackWarningOffset) != OSi_STACK_CHECKNUM_WARN )
    {
      return OS_STACK_ABOUT_TO_OVERFLOW;
    }
    //---- Check if underFlow
    else if ( *(u32*)(thread->stackBottom-sizeof(u32)) != OSi_STACK_CHECKNUM_BOTTOM )
    {
      return OS_STACK_UNDERFLOW;
    }
    //---- No Error, return.
    else
    {
        return OS_STACK_NO_ERROR;
    }
}

/*---------------------------------------------------------------------------*
  Name:         OSi_CheckStack

  Description:  check thread stack. check each CheckNUM.
                if changed, display warning and halt.

  Arguments:    file     file name displayed when stack overflow
                line     line number displayed when stack overflow
                thread   thread checked

  Returns:      None
                ( if error occurred, never return )
 *---------------------------------------------------------------------------*/
static char* OSi_CheckStack_mesg[] =
{
  "overflow", "about to overflow", "underflow"
};

#ifndef SDK_FINALROM
#ifndef SDK_NO_MESSAGE
void OSi_CheckStack( char* file, int line, OSThread* thread )
{
    OSStackStatus st;

    if ( (st = OS_GetStackStatus( thread )) == OS_STACK_NO_ERROR )
    {
      return;
    }

    OSi_Panic( file, line, " stack %x(id:%d) %s.\nstack area: %08x-%08x, warning offset: %x",
               thread,
               thread->id,
               OSi_CheckStack_mesg[(int)st-1],
               thread->stackTop,
               thread->stackBottom,
               thread->stackWarningOffset );
    // Never return
}
#endif
#endif

/*---------------------------------------------------------------------------*
  Name:         OSi_GetSystemStackPointer

  Description:  Get system mode stack pointer at svc/irq mode

  Arguments:    None

  Returns:      Stack Pointer
 *---------------------------------------------------------------------------*/
static u32 OSi_SystemStackBuffer;

#include  <nitro/code32.h>
asm u32 OSi_GetSystemStackPointer( void )
{
    ldr   r0, =OSi_SystemStackBuffer
    stmia r0, { sp }^
    ldr   r0, [ r0 ]
    bx    lr
}
#include  <nitro/codereset.h>

/*---------------------------------------------------------------------------*
  Name:         OSi_GetCurrentStackPointer

  Description:  Get current mode stack pointer

  Arguments:    None

  Returns:      Stack Pointer
 *---------------------------------------------------------------------------*/
asm u32 OSi_GetCurrentStackPointer( void )
{
    mov   r0, sp
    bx    lr
}

/*---------------------------------------------------------------------------*
  Name:         OS_SetThreadStackWarningOffset

  Description:  Set warning level for stack checker

  Arguments:    thread     thread to set
                offset     offset from stack top. must be multiple of 4

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_SetThreadStackWarningOffset( OSThread* thread, u32 offset )
{
    SDK_ASSERTMSG( (offset & 3) == 0, "Offset must be aligned by 4" );
    SDK_ASSERTMSG( offset > 0, "Cannot set warning level to stack top." );
    SDK_ASSERTMSG( OS_GetThreadContext( thread )->sp > thread->stackTop + offset, "Cannot set warning level below current sp." );

    //---- remember warning offset
    thread->stackWarningOffset = offset;

    //---- set Stack CheckNum
    *(u32*)(thread->stackTop + thread->stackWarningOffset) = OSi_STACK_CHECKNUM_WARN;
}

/*---------------------------------------------------------------------------*
  Name:         OS_SetThreadPriority

  Description:  change priority of thread

  Arguments:    thread     thread to set priority
                prio       new priority to be set

  Returns:      TRUE if success
 *---------------------------------------------------------------------------*/
BOOL OS_SetThreadPriority( OSThread* thread, u32 prio )
{
    OSThread*   t = OSi_ThreadInfo.list;
    OSThread*   pre = NULL;
    OSIntrMode  enable;

    SDK_ASSERTMSG( OS_THREAD_PRIORITY_MIN <= prio && prio <= OS_THREAD_PRIORITY_MAX, "invalid priority" );
    SDK_ASSERTMSG( thread != &OSi_IdleThread, "cannot change idle thread priority." );

    enable = OS_DisableInterrupts();

    while( t && t != thread )
    {
      pre = t;
      t = t->next;
    }

    //---- thread not found or thread is idle
    if ( !t || t == &OSi_IdleThread )
    {
      (void)OS_RestoreInterrupts( enable );
      return FALSE;
    }

    if ( t->priority != prio )
    {
      //---- remove thread from list
      if ( ! pre )
      {
        OSi_ThreadInfo.list = thread->next;
      }
      else
      {
        pre->next = thread->next;
      }

      //---- set priority and insert proper position
      thread->priority = prio;
      OSi_InsertThreadToList( thread );

      //---- re-schedule
      OS_RescheduleThread();
    }

    (void)OS_RestoreInterrupts( enable );

    return TRUE;
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetThreadPriority

  Description:  get priority of thread

  Arguments:    thread     thread to get priority

  Returns:      priority
 *---------------------------------------------------------------------------*/
u32 OS_GetThreadPriority( OSThread* thread )
{
  SDK_ASSERTMSG( thread, "OS_GetThreadPriority: bad thread" );

  return thread->priority;
}


/*---------------------------------------------------------------------------*
  Name:         OS_Sleep

  Description:  sleep specified period

  Arguments:    msec      sleeping period. ( milliseconds )

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_Sleep( u32 msec )
{
  OSAlarm alarm;

  SDK_ASSERTMSG( OS_IsTickAvailable() && OS_IsAlarmAvailable(), "OS_Sleep: need to start Tick and Alarm beforehand." );
  SDK_ASSERTMSG( OSi_IsThreadInitialized, "OS_Sleep: thread system not initialized." );

  OS_CreateAlarm( &alarm );
  OS_SetAlarm( &alarm, OS_MilliSecondsToTicks( msec ), &OSi_SleepAlarmCallback, (void*)OSi_GetCurrentThread() );

  OS_SleepThread( NULL );
}

//---------------- callback to wakeup sleeping thread
static void OSi_SleepAlarmCallback( void* arg )
{
  OS_WakeupThreadDirect( (OSThread *)arg );
}


/*---------------------------------------------------------------------------*
  Name:         OS_SetSwitchThreadCallback

  Description:  set callback called at switching thread

  Arguments:    callback      callback function

  Returns:      previous callback function before set callback now
 *---------------------------------------------------------------------------*/
OSSwitchThreadCallback OS_SetSwitchThreadCallback( OSSwitchThreadCallback callback )
{
    OSSwitchThreadCallback prev;
    OSIntrMode     enabled;

    enabled = OS_DisableInterrupts();
    prev = OSi_ThreadInfo.switchCallback;
    OSi_ThreadInfo.switchCallback = callback;

    (void)OS_RestoreInterrupts( enabled );
    return prev;
}

/*---------------------------------------------------------------------------*
  Name:         OSi_IdleThreadProc

  Description:  procedure of idle thread which system creates

  Arguments:    None

  Returns:      None (never return)
 *---------------------------------------------------------------------------*/
static void OSi_IdleThreadProc( void* )
{
  (void)OS_EnableInterrupts();
  while(1)
  {
    OS_Halt();
  }
  // never return
}

/*---------------------------------------------------------------------------*
  Name:         OSi_GetIdleThread

  Description:  get pointer to idle thread structure

  Arguments:    None

  Returns:      pointer to idle thread structure
 *---------------------------------------------------------------------------*/
OSThread* OSi_GetIdleThread( void )
{
  OSThread* t = NULL;
  if ( OSi_IsThreadInitialized )
  {
    t = &OSi_IdleThread;
  }
  return t;
}

/*---------------------------------------------------------------------------*
  Name:         OSi_DestroyThread

  Description:  destroy specified thread.

  Arguments:    thread: thread to be destroyed

  Returns:      None
 *---------------------------------------------------------------------------*/
void OSi_DestroyThread( OSThread* thread );
void OSi_DestroyThread( OSThread* thread )
{
  BOOL enabled;

  enabled = OS_DisableInterrupts();

  //---- Release all the locked mutexed by specified thread
  OSi_UnlockAllMutex( thread );

  //---- remove from thread list
  OSi_RemoveThreadFromList( thread );

  //---- delete thread
  OSi_ThreadInfo.entry[ thread->id ] = NULL;
  thread->state = OS_THREAD_STATE_TERMINATED;

  //---- wakeup thread waiting this thread terminated
  OS_WakeupThread( &thread->joinQueue );

  (void)OS_RestoreInterrupts( enabled );

  OS_RescheduleThread();
}
