/*---------------------------------------------------------------------------*
  Project:  NitroSDK - SPI - demos
  File:     main.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: main.c,v $
  Revision 1.17  2004/11/04 07:18:57  terui
  Small revisions in line with revising the specification of MIC_GetLastSamplingAddress.

  Revision 1.16  2004/09/17 09:36:03  terui
  Revised in connection with changes to MIC_StartAutoSampling(Async) parameters

  Revision 1.15  2004/09/06 12:59:44  terui
  Only fixed indent.

  Revision 1.14  2004/08/23 07:27:27  yasu
  Workaround for pushing A button in IPL

  Revision 1.13  2004/08/20 02:44:31  terui
  Changed so as to do PM_SetAmp also when TS_VERSION=0.

  Revision 1.12  2004/08/04 23:44:28  terui
  Changed LCD backlight operation functions

  Revision 1.11  2004/08/02 07:33:45  terui
  Only fix comment

  Revision 1.10  2004/08/02 00:42:01  yada
  PM_GAINAMP_80 -> PM_AMPGAIN_80

  Revision 1.9  2004/07/31 12:46:19  terui
  Added code to initialize PMIC for microphone use

  Revision 1.8  2004/07/13 09:18:52  terui
  Changes reflecting  addition of signed sampling types

  Revision 1.7  2004/06/10 06:51:37  terui
  Eliminated possibility that address cache could be discarded unintentionally

  Revision 1.6  2004/06/03 04:09:50  terui
  Changed definition of shortest sampling rate

  Revision 1.5  2004/06/02 06:14:15  terui
  Support for shift of 0 padding to lower-order bits in 12-bit sampling

  Revision 1.4  2004/06/01 02:05:00  terui
  Changed initial sampling rate to MIC_SAMPLING_RATE_8K

  Revision 1.3  2004/06/01 00:58:24  terui
  Changed so that sampling period can be specified directly in ARM7 clock units.
  Revision 1.2  2004/05/31 09:14:38  terui
  Add explanation of this demo.

  Revision 1.1  2004/05/31 08:58:51  terui
  Initial upload.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
    A sample that controls mic sampling status.

    USAGE:
        UP , DOWN    : Control sampling span.
        LEFT , RIGHT : Control sampling bit range. ( 8bit or 12 bit )
        SEL , STA    : Control loop sampling enable or disable.

    HOWTO:
        1. Initialize memory allocate system to get 32byte aligned big buffer.
        2. Initialize MIC library.
        3. Start auto sampling of MIC by default status.
        4. When you change status , first , stop auto sampling.
           Then , edit status and start auto sampling again.
 *---------------------------------------------------------------------------*/

#include    <nitro.h>

#ifdef  SDK_TS
#if     ( SDK_TS_VERSION >= 100 )
#include    <nitro/spi/common/pm_common.h>
#include    <nitro/spi/ARM9/pm.h>
#endif
#endif


/*---------------------------------------------------------------------------*
    Constant definitions
 *---------------------------------------------------------------------------*/
#define     KEY_REPEAT_START    25              // Frame count until start of key repeat
#define     KEY_REPEAT_SPAN     10              // Frame count of key repeat interval
#define     TEST_BUFFER_SIZE    ( 1024 * 1024 ) // 1M


/*---------------------------------------------------------------------------*
    Structure definitions
 *---------------------------------------------------------------------------*/
// Key input information
typedef struct KeyInformation
{
    u16     cnt;    // Raw input value
    u16     trg;    // Trigger down input
    u16     up;     // Trigger up input
    u16     rep;    // Held repeat input

} KeyInformation;

/*---------------------------------------------------------------------------*
    Internal function definitions
 *---------------------------------------------------------------------------*/
static void     InitializeAllocateSystem( void );
static void     Init3D( void );
static void     Draw3D( void );
static void     DrawLine( s16 sx , s16 sy , s16 ex , s16 ey );
static void     VBlankIntr( void );
static void     KeyRead( KeyInformation* pKey );
static void     SetDrawData( void* address , MICSamplingType type );
static void     PrintfVariableData( void );

/*---------------------------------------------------------------------------*
    Internal variable definitions
 *---------------------------------------------------------------------------*/
static KeyInformation   gKey;
static MICAutoParam     gMicAutoParam;
static u8*              gMicData;
static u8               gDrawData[ 192 ];


/*---------------------------------------------------------------------------*
  Name:         NitroMain

  Description:  Initialization and main loop.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void NitroMain( void )
{
    // Perform initialization
    OS_InitPrintServer();
    OS_Init();
    FX_Init();
    GX_Init();
    PXI_Init();
    GX_DispOff();
    GXS_DispOff();

    // Initialize display settings
    GX_SetBankForLCDC( GX_VRAM_LCDC_ALL );
    MI_CpuClearFast( (void*)HW_LCDC_VRAM , HW_LCDC_VRAM_SIZE );
    (void)GX_DisableBankForLCDC();
    MI_CpuFillFast( (void*)HW_OAM , 192 , HW_OAM_SIZE );
    MI_CpuClearFast( (void*)HW_PLTT , HW_PLTT_SIZE );
    MI_CpuFillFast( (void*)HW_DB_OAM , 192 , HW_DB_OAM_SIZE );
    MI_CpuClearFast( (void*)HW_DB_PLTT , HW_DB_PLTT_SIZE );

    // 3D related initialization
    Init3D();

    // Set interrupt
    OS_SetIrqFunction( OS_IE_V_BLANK , VBlankIntr );
    (void)OS_EnableIrqMask( OS_IE_V_BLANK );
    (void)OS_EnableIrq();
    (void)OS_EnableInterrupts();
    (void)GX_VBlankIntr( TRUE );

    //****************************************************************
    // Initialize MIC.
    InitializeAllocateSystem();
        // Memory allocated with OS_Alloc is 32-byte aligned and
        // does not destroy other memory even with cache operations.
    gMicData = (u8*)OS_Alloc( TEST_BUFFER_SIZE );
    MIC_Init();

#ifdef  SDK_TS
    // Initialize PMIC
    PM_Init();
    // AMP on
    (void)PM_SetAmp( PM_AMP_ON );
#if ( SDK_TS_VERSION >= 100 )
    // Amp gain adjustment
    (void)PM_SetAmpGain( PM_AMPGAIN_80 );
#endif
#if ( SDK_TS_VERSION == 100 )
    // To reduce noise, turn off LCD backlight
    (void)PM_SetBackLight( PM_LCD_ALL, PM_BACKLIGHT_OFF );
#endif
#endif

    gMicAutoParam.type = MIC_SAMPLING_TYPE_8BIT;
    gMicAutoParam.buffer = (void*)gMicData;
    gMicAutoParam.size = TEST_BUFFER_SIZE;
    gMicAutoParam.rate = MIC_SAMPLING_RATE_8K;
    gMicAutoParam.loop_enable = TRUE;
    gMicAutoParam.full_callback = NULL;
    (void)MIC_StartAutoSampling( &gMicAutoParam );
    //****************************************************************

    // Initialize internal variables
    {
    s32     i;

    for(i=0;i<192;i++){
        gDrawData[i] = 0x80;
    }
    }

    // Start LCD display
    GX_DispOn();
    GXS_DispOn();

    // Debug text string output
    OS_Printf("ARM9: MIC demo started.\n");
    OS_Printf("   up/down    -> change sampling span\n");
    OS_Printf("   left/right -> change bit range\n");
    OS_Printf("   select     -> change loop setting\n");
    OS_Printf("\n");
    PrintfVariableData();

    // empty call that gets key input information (push the A button in IPL)
    KeyRead( &gKey );

    // main loop
    while(TRUE)
    {
    // Get key input information
    KeyRead(&gKey);

    // Change variable parameters
    {
        // Change sampling type (bit width)
        if( ( gKey.trg | gKey.rep ) & ( PAD_KEY_LEFT | PAD_KEY_RIGHT ) )
        {
        //****************************************************************
        (void)MIC_StopAutoSampling();
        gMicAutoParam.type = (MICSamplingType)(
            ( gMicAutoParam.type + 1 ) % MIC_SAMPLING_TYPE_MAX
            );
        (void)MIC_StartAutoSampling( &gMicAutoParam );
        //****************************************************************
        PrintfVariableData();
        }
        // Change sampling rate
        if( ( gKey.trg | gKey.rep ) & PAD_KEY_UP )
        {
        //****************************************************************
        (void)MIC_StopAutoSampling();
        gMicAutoParam.rate = (u32)( gMicAutoParam.rate / 2 );
        if( gMicAutoParam.rate < MIC_SAMPLING_RATE_LIMIT )
        {
            gMicAutoParam.rate = MIC_SAMPLING_RATE_LIMIT;
        }

        (void)MIC_StartAutoSampling( &gMicAutoParam );
        //****************************************************************
        PrintfVariableData();
        }
        if( ( gKey.trg | gKey.rep ) & PAD_KEY_DOWN )
        {
        //****************************************************************
        (void)MIC_StopAutoSampling();
        gMicAutoParam.rate = (u32)( gMicAutoParam.rate * 2 );
        if( gMicAutoParam.rate < MIC_SAMPLING_RATE_LIMIT )
        {
            gMicAutoParam.rate = MIC_SAMPLING_RATE_LIMIT;
        }
        (void)MIC_StartAutoSampling( &gMicAutoParam );
        //****************************************************************
        PrintfVariableData();
        }
        // Change loop availability when buffer is full
        if( ( gKey.trg | gKey.rep ) & ( PAD_BUTTON_SELECT | PAD_BUTTON_START ) )
        {
        //****************************************************************
        (void)MIC_StopAutoSampling();
        gMicAutoParam.loop_enable = ( gMicAutoParam.loop_enable + 1 ) % 2;
        (void)MIC_StartAutoSampling( &gMicAutoParam );
        //****************************************************************
        PrintfVariableData();
        }
    }

    // Draw waveform
    SetDrawData( MIC_GetLastSamplingAddress() , gMicAutoParam.type );
    Draw3D();

    // Wait for V-Blank
    SVC_WaitVBlankIntr();
    // ARM7 debugger print output
    OS_PrintServer();
    }
}

/*---------------------------------------------------------------------------*
  Name:         InitializeAllocateSystem

  Description:  Initialize the memory allocation system in the main memory arena.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void InitializeAllocateSystem(void)
{
    void*           tempLo;
    OSHeapHandle    hh;

    // Assuming that OS_Init was called
    tempLo = OS_InitAlloc(
    OS_ARENA_MAIN ,
    OS_GetMainArenaLo() ,
    OS_GetMainArenaHi() ,
    1
    );
    OS_SetArenaLo(
    OS_ARENA_MAIN ,
    tempLo
    );
    hh = OS_CreateHeap(
    OS_ARENA_MAIN ,
    OS_GetMainArenaLo() ,
    OS_GetMainArenaHi()
    );
    if( hh < 0 )
    {
    OS_Panic("ARM9: Fail to create heap...\n");
    }
    hh = OS_SetCurrentHeap(
    OS_ARENA_MAIN ,
    hh
    );
}

/*---------------------------------------------------------------------------*
  Name:         Init3D

  Description:  Initialization for 3D display

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void Init3D(void)
{
    G3X_Init();
    G3X_InitMtxStack();
    G3_SwapBuffers(GX_SORTMODE_AUTO,GX_BUFFERMODE_W);
    GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS,GX_BGMODE_0,GX_BG0_AS_3D);
    GX_SetVisiblePlane(GX_PLANEMASK_BG0);
    G2_SetBG0Priority(0);
    G3X_SetShading(GX_SHADING_TOON);
    G3X_AlphaTest(FALSE,0);
    G3X_AlphaBlend(TRUE);
    G3X_AntiAlias(TRUE);
    G3X_EdgeMarking(FALSE);
    G3X_SetFog(FALSE,(GXFogBlend)0,(GXFogSlope)0,0);
    G3X_SetClearColor(0,0,0x7fff,63,FALSE);
    G3_ViewPort(0,0,255,191);
}

/*---------------------------------------------------------------------------*
  Name:         Draw3D

  Description:  Display waveform on 3D display

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void Draw3D(void)
{
    G3X_Reset();

    G3_MtxMode(GX_MTXMODE_PROJECTION);
    G3_Identity();
    G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
    G3_Identity();

    G3_PolygonAttr(GX_LIGHTMASK_NONE,GX_POLYGONMODE_MODULATE,
           GX_CULL_NONE,0,31,0);

    {
    s32     i;

    if(
        ( gMicAutoParam.type == MIC_SAMPLING_TYPE_SIGNED_8BIT ) ||
        ( gMicAutoParam.type == MIC_SAMPLING_TYPE_SIGNED_12BIT )
        )
    {
        for(i=0;i<191;i++){
        DrawLine(
            (s16)( (s8)gDrawData[ i ] ) ,
            (s16)i ,
            (s16)( (s8)gDrawData[ i + 1 ] ) ,
            (s16)( i + 1 )
            );
        }
    }
    else
    {
        for(i=0;i<191;i++){
        DrawLine(
            (s16)( gDrawData[ i ] ) ,
            (s16)i ,
            (s16)( gDrawData[ i + 1 ] ) ,
            (s16)( i + 1 )
            );
        }
    }
    }

    G3_SwapBuffers(GX_SORTMODE_AUTO,GX_BUFFERMODE_W);
}

/*---------------------------------------------------------------------------*
  Name:         DrawLine

  Description:  Draw line with triangular polygon

  Arguments:    sx  - x coordinate of line's starting point
                sy  - y coordinate of line's starting point
                ex  - x coordinate of line's ending point
                ey  - y coordinate of line's ending point

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void DrawLine(s16 sx,s16 sy,s16 ex,s16 ey)
{
    fx16    fsx,fsy,fex,fey;

    fsx = (fx16)( ( ( sx - 128 ) * 0x1000 ) / 128 );
    fsy = (fx16)( ( ( 96 - sy ) * 0x1000 ) / 96 );
    fex = (fx16)( ( ( ex - 128 ) * 0x1000 ) / 128 );
    fey = (fx16)( ( ( 96 - ey ) * 0x1000 ) / 96 );

    G3_Begin( GX_BEGIN_TRIANGLES );
    {
    G3_Color( GX_RGB( 31 , 31 , 31 ) );
    G3_Vtx( fsx , fsy , 0 );
    G3_Color( GX_RGB( 31 , 31 , 31 ) );
    G3_Vtx( fex , fey , 0 );
    G3_Color( GX_RGB( 31 , 31 , 31 ) );
    G3_Vtx( fsx , fsy , 1 );
    }
    G3_End();
}

/*---------------------------------------------------------------------------*
  Name:         VBlankIntr

  Description:  V-blank interrupt vector

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void VBlankIntr(void)
{
    // Set the IRQ check flag
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}

/*---------------------------------------------------------------------------*
  Name:         KeyRead

  Description:  Edit key input information
                Detect down trigger, up trigger, held repeat.

  Arguments:    pKey  - Structure that holds the key input info to edit.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
KeyRead( KeyInformation* pKey )
{
    static u16  repeat_count[12];
    int         i;
    u16         r;

    r = PAD_Read();
    pKey->trg = 0x0000;
    pKey->up = 0x0000;
    pKey->rep = 0x0000;

    for( i = 0 ; i < 12 ; i ++ )
    {
    if( r & ( 0x0001 << i ) )
    {
        if( !( pKey->cnt & ( 0x0001 << i ) ) )
        {
        pKey->trg |= ( 0x0001 << i );       // Trigger down
        repeat_count[ i ] = 1;
        }
        else
        {
        if( repeat_count[ i ] > KEY_REPEAT_START )
        {
            pKey->rep |= ( 0x0001 << i );       // Continue to press & repeat
            repeat_count[ i ] = KEY_REPEAT_START - KEY_REPEAT_SPAN;
        }
        else
        {
            repeat_count[ i ] ++;
        }
        }
    }
    else
    {
        if( pKey->cnt & ( 0x0001 << i ) )
        {
        pKey->up |= ( 0x0001 << i );        // Trigger up
        }
    }
    }
    pKey->cnt = r;      // Raw key input
}

/*---------------------------------------------------------------------------*
  Name:         SetDrawData

  Description:  Store in the buffer that reflects newest sampling data on the display.

  Arguments:    address - Location in main memory where the newest
  				sampling data is stored by the component.
                type    - Sampling type (bit width)

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void SetDrawData( void* address , MICSamplingType type )
{
    s32     i;

    // If no sampling has taken place, do nothing and stop.
    // (Because memory cache unrelated to microphone is discarded)
    if( ( address < gMicData ) || ( address >= ( gMicData + TEST_BUFFER_SIZE ) ) )
    {
    return;
    }

    // In the case of 8-bit sampling
    switch( type )
    {
    case MIC_SAMPLING_TYPE_8BIT:
    case MIC_SAMPLING_TYPE_SIGNED_8BIT:
    {
        u8*     p;

        p = (u8*)( (u32)address - 191 );
        if( p < gMicData )
        {
        p = (u8*)( (u32)p + TEST_BUFFER_SIZE );
        }
        DC_InvalidateRange( (void*)( (u32)p & 0xffffffe0 ) , 32 );
        for( i = 0 ; i < 192 ; i ++ )
        {
        gDrawData[ i ] = *p;
        p ++;
        if( (u32)p >= (u32)( gMicData + TEST_BUFFER_SIZE ) )
        {
            p -= TEST_BUFFER_SIZE;
        }
        if( ( (u32)p % 32 ) == 0 )
        {
            DC_InvalidateRange( p , 32 );
        }
        }
    }
    break;
    case MIC_SAMPLING_TYPE_12BIT:
    case MIC_SAMPLING_TYPE_SIGNED_12BIT:
    {
        u16*    p;

        p = (u16*)( (u32)address - 382 );
        if( (u32)p < (u32)gMicData )
        {
        p = (u16*)( (u32)p + TEST_BUFFER_SIZE );
        }
        DC_InvalidateRange( (void*)( (u32)p & 0xffffffe0 ) , 32 );
        for( i = 0 ; i < 192 ; i ++ )
        {
        gDrawData[ i ] = (u8)( ( *p >> 8 ) & 0x00ff );
        p ++;
        if( (u32)p >= (u32)( gMicData + TEST_BUFFER_SIZE ) )
        {
            p = (u16*)( (u32)p - TEST_BUFFER_SIZE );
        }
        if( ( (u32)p % 32 ) == 0 )
        {
            DC_InvalidateRange( p , 32 );
        }
        }
    }
    break;
    }
}

/*---------------------------------------------------------------------------*
  Name:         PrintfVariableData

  Description:  Print the variable sampling settings.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
PrintfVariableData( void )
{
    s32     range = 0;

    OS_Printf(" sampling-span: %d , bit-range: " , gMicAutoParam.rate );
    switch( gMicAutoParam.type )
    {
    case MIC_SAMPLING_TYPE_8BIT:
    OS_Printf("8");
    break;
    case MIC_SAMPLING_TYPE_12BIT:
    OS_Printf("12");
    break;
    case MIC_SAMPLING_TYPE_SIGNED_8BIT:
    OS_Printf("signed 8");
    break;
    case MIC_SAMPLING_TYPE_SIGNED_12BIT:
    OS_Printf("signed 12");
    break;
    }
    if( gMicAutoParam.loop_enable )
    {
    OS_Printf(" , loop: on\n");
    }
    else
    {
    OS_Printf(" , loop: off\n");
    }
}

/*---------------------------------------------------------------------------*
  End of file
 *---------------------------------------------------------------------------*/
