/*---------------------------------------------------------------------------*
  Project:  NitroSDK - libraries - OS
  File:     os_interrupt.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_interrupt.c,v $
  Revision 1.25  2004/09/02 06:24:25  yada
  only change comment

  Revision 1.24  2004/08/16 04:18:58  yada
  in ARM9, no hook in VBlank interrupt

  Revision 1.23  2004/08/10 12:14:22  yada
  delete vblank counter variable, it's moved to shared area

  Revision 1.22  2004/08/10 08:01:26  yada
  add OS_GetVBlankCount(). make vblank hook

  Revision 1.21  2004/07/10 07:53:51  yada
  delete unused array

  Revision 1.20  2004/07/10 04:07:08  yasu
  Delete unused code

  Revision 1.19  2004/05/25 10:44:04  yada
  disable IME before touch IE or IF

  Revision 1.18  2004/04/30 02:19:55  yada
  add OSi_IrqThreadQueue

  Revision 1.17  2004/03/17 06:35:04  yada
  After exiting Timer call back, whether or not to stop IRQ was undefined, but fixed not to change the status.

  Revision 1.16  2004/03/02 11:05:05  yada
  Put void to function call that does not have return value.

  Revision 1.15  2004/03/02 09:56:03  yada
  Changed not to change handling of enable flag when ending handler for TIMER interrupt.

  Revision 1.14  2004/03/01 11:57:14  yada
  Creation of function comments in English

  Revision 1.13  2004/02/13 01:28:58  yada
  Module interrupt

  Revision 1.12  2004/02/12 11:08:30  yada
  Work to divide ARM9/ARM7

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

  Revision 1.10  2004/02/03 11:20:36  yada
  Added OSi_EnterTimerCallback()

  Revision 1.9  2004/02/02 05:33:03  yada
  Take into consideration irq system callback

  Revision 1.8  2004/01/18 02:25:37  yada
   Indent and other cleanup

  Revision 1.7  2004/01/17 11:44:20  yada
  Created Async mode in DMA transfer

  Revision 1.6  2003/12/25 07:31:26  yada
  Change due to unified format rules

  Revision 1.5  2003/12/12 01:27:14  yada
  Revision due to include name changes (os/system.h & hw/armArch.h)

  Revision 1.4  2003/12/08 12:19:29  yada
  Changed included file from MemoryMap.h to mmap_global.h

  Revision 1.3  2003/11/30 04:20:10  yasu
  CPU CPSR setting related descriptions to cpsr.h

  Revision 1.2  2003/11/29 03:53:25  yasu
There was some IRQHandler code I was thinking about moving to TCM?
  Commenting it out until overlay setup is arranged.

  Revision 1.1  2003/11/29 01:25:46  yada
   File name changed

  Revision 1.9  2003/11/28 12:18:27  yasu
  typo correction

  Revision 1.8  2003/11/28 12:17:23  yasu
  Added OS_IRQHandler size check ASSERT

  Revision 1.7  2003/11/26 00:45:07  yada
  Corrected mistaken return target for OS_RestoreInterrupt()

  Revision 1.6  2003/11/25 10:44:08  yada
  Name change: OS_xxxInterruptxxx > OS_xxxIRQxxx

  Revision 1.5  2003/11/13 06:51:15  yada
  Rewrite DMA function following dma.h revision

  Revision 1.4  2003/11/11 12:56:52  yada
  (none)

  Revision 1.3  2003/11/10 08:06:21  yada
  (1.2 comment correction) Made so interrupt process routine uses OS_InterruptHandler

  Revision 1.2  2003/11/10 08:05:07  yada
  Inverted input from pad

  Revision 1.1  2003/11/06 09:30:40  yada
  Provisional version


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

/*---------------------------------------------------------------------------*
  Name:         OS_InitIrqTable

  Description:  initialize IRQ handler table

  Arguments:    None

  Returns:      None
 *---------------------------------------------------------------------------*/
extern OSThreadQueue  OSi_IrqThreadQueue;

void OS_InitIrqTable( void )
{
    //---- initialize thread queue for irq
    OS_InitThreadQueue( &OSi_IrqThreadQueue );

#ifdef SDK_ARM7
    //---- clear VBlank counter
    OSi_SetVBlankCount( 0 );
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_SetIrqFunction

  Description:  set IRQ handler function

  Arguments:    intrBit         IRQ mask bit
                function        funtion to set

  Returns:      None
 *---------------------------------------------------------------------------*/
void OS_SetIrqFunction( OSIrqMask intrBit, OSIrqFunction function )
{
    int i;
    OSIrqCallbackInfo* info;

    for( i=0; i<OS_IRQ_TABLE_MAX; i++ )
    {
        if ( intrBit & 1 )
        {
          info = NULL;

          // ---- if dma(0-3)
          if ( REG_OS_IE_D0_SHIFT <= i && i <= REG_OS_IE_D3_SHIFT )
          {
            info = &OSi_IrqCallbackInfo[ i - REG_OS_IE_D0_SHIFT ];
          }
          // ---- if timer(0-3)
          else if ( REG_OS_IE_T0_SHIFT <= i && i <= REG_OS_IE_T3_SHIFT )
          {
            info = &OSi_IrqCallbackInfo[ i - REG_OS_IE_T0_SHIFT + OSi_IRQCALLBACK_NO_TIMER0 ];
          }
#ifdef SDK_ARM7
          // ---- if vblank
          else if ( REG_OS_IE_VB_SHIFT == i )
          {
            info = &OSi_IrqCallbackInfo[ OSi_IRQCALLBACK_NO_VBLANK ];
          }
#endif
          //---- other interrupt
          else
          {
              OS_IRQTable[i] = function;
          }

          if ( info )
          {
            info->func   = (void(*)(void*))function;
            info->arg    = 0;
            info->enable = TRUE;
          }

        }
        intrBit >>= 1;
    }
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetIrqFunction

  Description:  get IRQ handler function

  Arguments:    intrBit         IRQ mask bit

  Returns:      None
 *---------------------------------------------------------------------------*/
OSIrqFunction OS_GetIrqFunction( OSIrqMask intrBit )
{
    int i;
    OSIrqFunction* funcPtr = &OS_IRQTable[0];

    for( i=0; i<OS_IRQ_TABLE_MAX; i++ )
    {
        if ( intrBit & 1 )
        {
          //---- if dma(0-3)
          if ( REG_OS_IE_D0_SHIFT <= i && i <= REG_OS_IE_D3_SHIFT )
          {
              return (void(*)(void)) OSi_IrqCallbackInfo[ i - REG_OS_IE_D0_SHIFT ].func;
          }
          //---- if timer(0-3)
          else if ( REG_OS_IE_T0_SHIFT <= i && i <= REG_OS_IE_T3_SHIFT )
          {
              return (void(*)(void)) OSi_IrqCallbackInfo[ i - REG_OS_IE_T0_SHIFT + OSi_IRQCALLBACK_NO_TIMER0 ].func;
          }
#ifdef SDK_ARM7
          else if ( REG_OS_IE_VB_SHIFT == i )
          {
              return (void(*)(void)) OSi_IrqCallbackInfo[ OSi_IRQCALLBACK_NO_VBLANK ].func;
          }
#endif

          //---- other interrupt
          return *funcPtr;
        }
        intrBit >>= 1;
        funcPtr ++;
    }
    return 0;
}


/*---------------------------------------------------------------------------*
  Name:         OSi_EnterDmaCallback

  Description:  enter dma callback handler and arg

  Arguments:    dmaNo       dma number to set callback
                callback    callback for dma interrupt
                arg         its argument

  Returns:      None
 *---------------------------------------------------------------------------*/
void OSi_EnterDmaCallback( u32 dmaNo, void(*callback)(void*), void* arg )
{
    OSIrqMask imask = ( 1UL << (REG_OS_IE_D0_SHIFT + dmaNo) );

    //---- enter callback
    OSi_IrqCallbackInfo[ dmaNo ].func = callback;
    OSi_IrqCallbackInfo[ dmaNo ].arg = arg;

    //---- remember IRQ mask bit
    OSi_IrqCallbackInfo[ dmaNo ].enable = OS_EnableIrqMask( imask ) & imask;
}


/*---------------------------------------------------------------------------*
  Name:         OSi_EnterTimerCallback

  Description:  enter timer callback handler and arg

  Arguments:    timerNo     timer number to set callback
                callback    callback for timer interrupt
                arg         its argument

  Returns:      None
 *---------------------------------------------------------------------------*/
void OSi_EnterTimerCallback( u32 timerNo, void(*callback)(void*), void* arg )
{
    OSIrqMask imask = ( 1UL << (REG_OS_IE_T0_SHIFT + timerNo) );

    //---- enter callback
    OSi_IrqCallbackInfo[ timerNo + 4 ].func = callback;
    OSi_IrqCallbackInfo[ timerNo + 4 ].arg = arg;

    //---- remember IRQ mask bit (force to be ENABLE)
    (void)OS_EnableIrqMask( imask );
    OSi_IrqCallbackInfo[ timerNo + 4 ].enable = TRUE;
}

//================================================================================
//         IrqMask
//================================================================================
/*---------------------------------------------------------------------------*
  Name:         OS_SetIrqMask

  Description:  set irq factor

  Arguments:    intr        irq factor

  Returns:      previous factors
 *---------------------------------------------------------------------------*/
OSIrqMask OS_SetIrqMask( OSIrqMask intr )
{
    BOOL      ime  = OS_DisableIrq();   // IME disable
    OSIrqMask prep = reg_OS_IE;
    reg_OS_IE = intr;
    (void)OS_RestoreIrq( ime );
    return prep;
}

/*---------------------------------------------------------------------------*
  Name:         OS_EnableIrqMask

  Description:  set specified irq factor

  Arguments:    intr        irq factor

  Returns:      previous factors
 *---------------------------------------------------------------------------*/
OSIrqMask OS_EnableIrqMask( OSIrqMask intr )
{
    BOOL      ime  = OS_DisableIrq();   // IME disable
    OSIrqMask prep = reg_OS_IE;
    reg_OS_IE = prep | intr;    
    (void)OS_RestoreIrq( ime );
    return prep;
}

/*---------------------------------------------------------------------------*
  Name:         OS_DisableIrqMask

  Description:  unset specified irq factor

  Arguments:    intr        irq factor

  Returns:      previous factors
 *---------------------------------------------------------------------------*/
OSIrqMask OS_DisableIrqMask( OSIrqMask intr )
{
    BOOL      ime  = OS_DisableIrq();   // IME disable
    OSIrqMask prep = reg_OS_IE;
    reg_OS_IE = prep & ~intr;
    (void)OS_RestoreIrq( ime );
    return prep;
}

/*---------------------------------------------------------------------------*
  Name:         OS_ResetRequestIrqMask

  Description:  reset IF bit
                (setting bit causes to clear bit for interrupt)

  Arguments:    intr        irq factor

  Returns:      previous factors
 *---------------------------------------------------------------------------*/
OSIrqMask OS_ResetRequestIrqMask( OSIrqMask intr )
{
    BOOL      ime  = OS_DisableIrq();   // IME disable
    OSIrqMask prep = reg_OS_IF;
    reg_OS_IF = intr;
    (void)OS_RestoreIrq( ime );
    return prep;
}
