/********************************************************************************************/
/* CONFIDENTIAL AND PROPRIETARY:                                                            */
/*      Copyright 1998, Sega of America.  All rights reserved.                              */
/*      This sample source code is provided for demonstration purposes only.                */
/*      Use and distribution of Sega proprietary material is governed by                    */
/*      developer licensing agreement.                                                      */
/********************************************************************************************/

/********************************************************************************************/
/* Name:     TwRect.c                                                                       */
/* Title:    Twiddled Rectangle Example                                                     */
/* Author:   Gary Lake                                                                      */
/* Created:  April 8, 1998                                                                  */
/*                                                                                          */
/* Version:  1.0                                                                            */
/* Platform: Dreamcast | Set5.24 | Shinobi | Kamui                                          */
/*                                                                                          */
/* Description:                                                                             */
/*      This sample is based on QuikTest and has been modified to load and display          */
/*      a twiddled rectangle format texture.                                                */
/*                                                                                          */
/* History:                                                                                 */
/*      02/24/99 - Modified memory allocation routines for 32-byte alignment.               */
/*      11/06/98 - Added extra goodies to demonstrate Set5.24 functionality (Gary Lake).    */
/*      11/04/98 - Fixes added for Set5.23 (Kerry Thompson).                                */
/*      10/11/98 - Ported to Set5.16 (Gary Lake).                                           */
/*      07/03/98 - Ported to Set4 (Gary Lake).                                              */
/*      04/08/98 - Program created (Gary Lake).                                             */
/********************************************************************************************/


/********************************************************************************************/
/* Includes                                                                                 */
/********************************************************************************************/

#include <shinobi.h>    /* Shinobi system routines. */
#include <kamui.h>      /* Kamui low-level graphics HAL. */
#include <sn_fcntl.h>   /* LibCross file types. */
#include <usrsnasm.h>   /* LibCross I/O routines. */
#include <sg_syCbl.h>   /* NTSC/RGB/VGA Cable check interface. */


/********************************************************************************************/
/* Definitions                                                                              */
/********************************************************************************************/

#define DIRECT  0       /* Switch between normal and Kamui Direct operation. */

#define VBUFFER_SIZE    48000   /* Size of Kamui vertex buffer (to be double-buffered). */

/* Kamui vertex context parameter mask.  CTX_MASK_ALL = Mask all context modes. */
#define CTX_MASK_ALL   (KM_PARAMTYPE | KM_LISTTYPE | KM_COLORTYPE | KM_UVFORMAT |       \
                        KM_DEPTHMODE | KM_CULLINGMODE | KM_SCREENCOORDINATION |         \
                        KM_SHADINGMODE | KM_MODIFIER | KM_ZWRITEDISABLE |               \
                        KM_SRCBLENDINGMODE | KM_DSTBLENDINGMODE | KM_SRCSELECT |        \
                        KM_DSTSELECT | KM_FOGMODE | KM_USESPECULAR | KM_USEALPHA |      \
                        KM_IGNORETEXTUREALPHA | KM_CLAMPUV | KM_FLIPUV |                \
                        KM_FILTERMODE | KM_SUPERSAMPLE | KM_MIPMAPDADJUST |             \
                        KM_TEXTURESHADINGMODE | KM_COLORCLAMP | KM_PALETTEBANK |        \
                        KM_DCALCEXACT | KM_STRIPLENGTH | KM_USERCLIPMODE)

/* Override Shinobi's syMalloc() / syFree() macros. */
#ifdef syMalloc
#undef syMalloc
#define syMalloc    malloc
#endif /* syMalloc */

#ifdef syFree
#undef syFree
#define syFree      free
#endif /* syFree */


/********************************************************************************************/
/* Global Variables                                                                         */
/********************************************************************************************/

void *(*MallocPtr)(unsigned long) = syMalloc;   /* Default allocation is syMalloc(). */
void (*FreePtr)(void *) = syFree;               /* Default de-allocation is syFree(). */

extern long *kmiDeviceExtension;    /* External reference to Kamui device driver. */

KMVERTEXCONTEXT OpaqueColorContext =    /* Standard opaque colored polygon (no texture). */
{
    CTX_MASK_ALL, KM_POLYGON, KM_OPAQUE_POLYGON, KM_FLOATINGCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_NOTEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_ONE, KM_ZERO, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_POINT_SAMPLE, FALSE, 0x4, KM_MODULATE, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, FALSE, KM_STRIP_02, KM_USERCLIP_DISABLE
};

KMVERTEXCONTEXT OpaqueTextureContext =          /* Opaque textured polygon. */
{
    CTX_MASK_ALL, KM_POLYGON, KM_OPAQUE_POLYGON, KM_FLOATINGCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_TEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_ONE, KM_ZERO, FALSE, FALSE, KM_NOFOG, FALSE, FALSE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_BILINEAR, FALSE, 0x4, KM_MODULATE, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, FALSE, KM_STRIP_02, KM_USERCLIP_DISABLE
};

KMVERTEXCONTEXT TranslucentTextureContext =     /* Translucent textured polygon. */
{
    CTX_MASK_ALL, KM_POLYGON, KM_TRANS_POLYGON, KM_FLOATINGCOLOR, KM_32BITUV,
    KM_GREATER, KM_CULLCCW, KM_SCREEN, KM_TEXTUREGOURAUD, KM_NOMODIFIER,
    FALSE, KM_SRCALPHA, KM_INVSRCALPHA, FALSE, FALSE, KM_NOFOG, FALSE, TRUE, FALSE,
    KM_NOCLAMP, KM_NOFLIP, KM_BILINEAR, FALSE, 0x4, KM_MODULATE_ALPHA, FALSE,
    0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f, 0.0f, FALSE, KM_STRIP_02, KM_USERCLIP_DISABLE
};

KMPACKEDARGB    Border;         /* Border color packed ARGB value. */


/********************************************************************************************/
/* Macros                                                                                   */
/********************************************************************************************/

/* Set up 32-byte aligned malloc() / free() memory management. */
#define Align32Malloc   (*MallocPtr)
#define Align32Free     (*FreePtr)

/* Align a pointer to the nearest 32-byte aligned memory address. */
#define Align32Byte(ADR)        ((((long) ADR) + 0x1F) & 0xFFFFFFE0)

/* Reference physical memory through a cached memory address (SH4 P1 memory region). */
#define SH4_P1CachedMem(ADR)    ((((long) ADR) & 0x0FFFFFFF) | 0x80000000)

/* Reference physical memory through a non-cached memory address (SH4 P2 memory region). */
#define SH4_P2NonCachedMem(ADR) ((((long) ADR) & 0x0FFFFFFF) | 0xA0000000)


/********************************************************************************************/
/* MACRO _TWIDDLEADR():                                                                     */
/*      Internal macro used by TwiddledOffset() to convert a rectangular coordinate into    */
/*      a twiddled address.                                                                 */
/********************************************************************************************/

#define _TWIDDLEADR(COORD)      ((COORD & 1)             | ((COORD >> 1) & 1) << 2   |  \
                                ((COORD >> 2) & 1) << 4  | ((COORD >> 3) & 1) << 6   |  \
                                ((COORD >> 4) & 1) << 8  | ((COORD >> 5) & 1) << 10  |  \
                                ((COORD >> 6) & 1) << 12 | ((COORD >> 7) & 1) << 14  |  \
                                ((COORD >> 8) & 1) << 16 | ((COORD >> 9) & 1) << 18)


/********************************************************************************************/
/* MACRO TwiddledOffset():                                                                  */
/*      Convert a rectangular coordinate pair (x, y) into a twiddled offset.                */
/*                                                                                          */
/* Inputs:                                                                                  */
/*      X   = Horizontal pixel coordinate.                                                  */
/*      Y   = Vertical pixel coordinate.                                                    */
/*                                                                                          */
/* Outputs:                                                                                 */
/*      Return = Pixel offset into twiddled texture.                                        */
/********************************************************************************************/

#define TwiddledOffset(X, Y)    ((_TWIDDLEADR(X) << 1) | _TWIDDLEADR(Y))


/********************************************************************************************/
/* Functions & Procedures                                                                   */
/********************************************************************************************/

/********************************************************************************************/
/* _Align32Malloc():                                                                        */
/*      Allocate a memory region with a size padded to the nearest multiple of 32 bytes     */
/*      and an alignment shifted to the nearest 32-byte boundary.                           */
/*                                                                                          */
/* Inputs:                                                                                  */
/*      Size    = Size of memory region to allocate.                                        */
/*                                                                                          */
/* Outputs:                                                                                 */
/*      Return  = Pointer to 32-byte aligned memory region.                                 */
/********************************************************************************************/

void *_Align32Malloc (unsigned long Size)
{
    void *Ptr, *AlignedPtr;

    /* Adjust the requested size to a multiple of 32 bytes. */
    Size = (Size + 0x1F) & 0xFFFFFFE0;

    /* Allocate requested size plus 32 bytes padding. */
    Ptr = syMalloc (Size + 32);

    /* Align to 32-bytes (add 32 bytes if already aligned - the padding is used below). */
    AlignedPtr = (void *) ((((long) Ptr) + 0x20) & 0xFFFFFFE0);

    /* Place cookie one byte earlier for _Align32Free(). */
    *((char *) AlignedPtr - 1) = (char) ((long) AlignedPtr - (long) Ptr);

    return (AlignedPtr);
}


/********************************************************************************************/
/* _Align32Free():                                                                          */
/*      Free memory allocated using _Align32Malloc().                                       */
/*                                                                                          */
/* Inputs:                                                                                  */
/*      Ptr     = Pointer to memory region to free.                                         */
/********************************************************************************************/

void _Align32Free (void *Ptr)
{
    char Diff;

    /* Read cookie and adjust pointer back to original unaligned address before freeing. */
    Diff = *((char *) Ptr - 1);
    Ptr = (void *) ((long) Ptr - Diff);

    syFree (Ptr);
}


/********************************************************************************************/
/* Init32Malloc():                                                                          */
/*      Check the built-in malloc() / free() routines for 32-byte alignment.                */
/*      If allocations are not aligned, install alternate Align32Malloc() and               */
/*      Align32Free() functions.                                                            */
/*                                                                                          */
/* Outputs:                                                                                 */
/*      Align32Malloc   = Global macro, adjusted to point to 32-byte aligned malloc().      */
/*      Align32Free     = Global macro, adjusted to point to valid 32-byte aligned free().  */
/********************************************************************************************/

void Init32Malloc (void)
{
    char *Ptr1, *Ptr2;

    /* Use syMalloc() / syFree() by default. */
    MallocPtr = syMalloc;
    FreePtr = syFree;

    Ptr1 = syMalloc (1);
    Ptr2 = syMalloc (1);

    /* Test if either allocation was not 32-byte aligned. */
    if (((long) Ptr1 & 0x1F) || ((long) Ptr2 & 0x1F))
    {
        MallocPtr = _Align32Malloc;
        FreePtr = _Align32Free;
    }

    syFree (Ptr1);
    syFree (Ptr2);
}


/********************************************************************************************/
/* Rectangle2Twiddled():                                                                    */
/*      Convert a rectangle texture (linear order) into a PVR twiddled format texture.      */
/*      This routine was coded for sample purposes and not optimized for run-time use.      */
/*                                                                                          */
/* Inputs:                                                                                  */
/*      Rectangle    = Pointer to rectangle texture.                                        */
/*      Dimension    = Texture dimension (assumed to be square, i.e. width = height).       */
/*      BitsPerPixel = Bit depth of texture.                                                */
/*                                                                                          */
/* Outputs:                                                                                 */
/*      Twiddled = Buffer for twiddled texture output (same size as rectangle).             */
/********************************************************************************************/

void Rectangle2Twiddled (char *Rectangle, char *Twiddled, int Dimension, int BitsPerPixel)
{
    int x, y;
    char *In = Rectangle, *Out;

    /* Convert BitsPerPixel into a shift value.. 8-bit = shift by 0, 16-bit = shift by 1. */
    BitsPerPixel = BitsPerPixel >> 4;

    for (y = 0; y < Dimension; y++)
    {
        for (x = 0; x < Dimension; x++)
        {
            /* TwiddledOffset macro converts coordinate into twiddled texture offset. */
            Out = Twiddled + ((TwiddledOffset (x, y)) << BitsPerPixel);

            /* Transfer pixel from Rectangle texture to Twiddled. */
            *Out = *In;
            if (BitsPerPixel > 0)           /* Account for 16-bit data. */
                *(Out + 1) = *(In + 1);
            In += BitsPerPixel + 1;
        }
    }
}


/********************************************************************************************/
/* LoadTextureFile():                                                                       */
/*      Allocate system memory for a PVR texture and load the specified texture file from   */
/*      the GD filesystem.  Returns a pointer to the allocated memory block.                */
/*                                                                                          */
/*      Note: The PVR texture data will be offset to the nearest 32-byte aligned address    */
/*      within the returned memory region.                                                  */
/*                                                                                          */
/* Inputs:                                                                                  */
/*      Filename    = Filename character string, must be valid for the current directory.   */
/*                                                                                          */
/* Outputs:                                                                                 */
/*      Twiddled    = Buffer containing PVR texture data.                                   */
/********************************************************************************************/

PKMDWORD LoadTextureFile (char *Filename)
{
    PKMDWORD    TexturePtr;
    GDFS        gdfs;
    long        FileBlocks;

    /* Open input file. */
    if (!(gdfs = gdFsOpen (Filename, NULL)))
        return NULL;

    /* Get file size (in blocks/sectors). */
    gdFsGetFileSctSize (gdfs, &FileBlocks);

    /* Allocate memory to nearest block size (2048 bytes). */
    TexturePtr = Align32Malloc (FileBlocks * 2048);

    /* Read file to memory region (Destination address must be 32-byte aligned). */
    gdFsReqRd32 (gdfs, FileBlocks, TexturePtr);

    /* Wait for file access to finish. */
    while (gdFsGetStat (gdfs) != GDD_STAT_COMPLETE)
        ;

    /* Close file. */
    gdFsClose (gdfs);

    return TexturePtr;
}


/********************************************************************************************/
/* main():                                                                                  */
/*      Entry point for C code (Where it all happens).                                      */
/********************************************************************************************/

int main (void)
{
    KMVERSIONINFO       VersionInfo;            /* Kamui version information. */
    KMSURFACEDESC       PrimarySurfaceDesc;     /* Primary framebuffer. */
    KMSURFACEDESC       BackSurfaceDesc;        /* Back framebuffer surface. */
    KMSURFACEDESC       TexSurfaceDesc;         /* Texture surface. */
    KMVERTEXBUFFDESC    VertexBufferDesc;       /* Vertex buffer. */
    PKMSURFACEDESC      FBSurfaces[2];          /* Array of pointers to FB surfaces. */
    KMVERTEX_01         CurrentStrip_01[16];    /* Strip of opaque color triangles. */
    KMVERTEX_05         CurrentStrip_05[16];    /* Strip of textured triangles. */
    PKMVERTEX_01        BackPlane[3];           /* Background plane. */
    PKMDWORD            VertexBufferPtr;        /* Pointer to user-allocated vertex buffer. */
    PKMDWORD            TexturePtr;             /* Pointer to texture. */
    PKMDWORD            TwiddledPtr;            /* Pointer to twiddled texture. */

    KMSYSTEMCONFIGSTRUCT kmsc =
    {
		sizeof(KMSYSTEMCONFIGSTRUCT),
        KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER  /* Special flags (i.e. clear frame buffer). */
#if DIRECT
        | KM_CONFIGFLAG_USEDIRECTMODE           /* Use Kamui Direct mode. */
#endif /* DIRECT */
        ,0, /* FBSurfaces */                    /* Pointer to array of FB surface pointers. */
        2,                                      /* Number of buffers = 2. */
        640, 480,                               /* Width and height of surface. */
        KM_DSPBPP_RGB565,                       /* Bits per pixel. */
        0x100000,                               /* VRAM alloc. for textures (DWORD) = 4MB. */
        0,  /* &VertexBufferDesc */             /* Pointer to vertex buffer descriptor. */
        0,  /* VertexBufferPtr */               /* Pointer to vertex buffer. */
        2 * VBUFFER_SIZE,                       /* DWORD size of vbuffers (multiple of 32). */
        {50.f, 0.f, 50.f, 0.f, 0.f},            /* Percent of vertbuf for each vertex type. */
        0, 0, 0, 0, 0, 0, 0,                    /* Reserved. */
        KM_VERTEXBUFMODEL_NORMAL                /* Latency model = 3VB */
	};

    int                 i;
    float               Red = 0.0f, Green = 0.0f, Blue = 0.0f;      /* Gradient colors. */
    float               ColorDelta = 0.01f;
    char                TmpString[80];

    /* Tell the user about this app. */
    debug_write (SNASM_STDOUT, "QuickTest: Demonstrates basic Kamui operation.", 46);

    /* Check the cable for NTSC/PAL or VGA */
    switch (syCblCheckCable())
	{
        /* Initialize the display device and set the frame buffer based on the video mode. */
        case SYE_CBL_CABLE_NTSC:    /* Note: SYE_CBL_CABLE_NTSC/PAL defined as same value. */
            switch (syCblCheckBroadcast())
            {
                case SYE_CBL_BROADCAST_NTSC:    /* U.S./North America NTSC (60Hz). */
                case SYE_CBL_BROADCAST_PALM:    /* Brazil PAL-M (60Hz). */
                    sbInitSystem (KM_DSPMODE_NTSCNI640x480, KM_DSPBPP_RGB565, 1);
                    break;
                case SYE_CBL_BROADCAST_PAL:     /* Europe PAL (50Hz). */
                case SYE_CBL_BROADCAST_PALN:    /* Argentina PAL-N (50Hz). */
                    sbInitSystem (KM_DSPMODE_PALNI640x480, KM_DSPBPP_RGB565, 1);
                    break;
            }
            break;
        case SYE_CBL_CABLE_VGA:
            sbInitSystem (KM_DSPMODE_VGA, KM_DSPBPP_RGB565, 1);
            break;
	}

    /* Check malloc alignment. */
    Init32Malloc();

    /* Check Kamui version and memory information. */
    debug_write (SNASM_STDOUT, "Kamui Version =", 15);

    /* Use kmGetVersionInfo() or peek around in memory like a real hacker. */
    #if 1
        kmGetVersionInfo (&VersionInfo);
    #else
        VersionInfo.kmMajorVersion = *kmiDeviceExtension;
        VersionInfo.kmLocalVersion = ((*(kmiDeviceExtension + 1) & 0xFF) << 24);
        VersionInfo.kmLocalVersion |= ((*(kmiDeviceExtension + 2) & 0xFF) << 16);
        VersionInfo.kmLocalVersion |= (*(kmiDeviceExtension + 3) & 0xFFFF);
        VersionInfo.kmFrameBufferSize = *((long *) ((long) &kmiDeviceExtension + 24));
    #endif

    /* Print version information to the Codescape log window. */
    sprintf (TmpString, "  Major Version: %d", VersionInfo.kmMajorVersion);
    debug_write (SNASM_STDOUT, TmpString, strlen (TmpString));
    sprintf (TmpString, "  Minor Version: %d", ((VersionInfo.kmLocalVersion >> 24) & 0xFF));
    debug_write (SNASM_STDOUT, TmpString, strlen (TmpString));
    sprintf (TmpString, "  Major Build:   %d", ((VersionInfo.kmLocalVersion >> 16) & 0xFF));
    debug_write (SNASM_STDOUT, TmpString, strlen (TmpString));
    sprintf (TmpString, "  Minor Build:   %d", (VersionInfo.kmLocalVersion & 0xFFFF));
    debug_write (SNASM_STDOUT, TmpString, strlen (TmpString));
    sprintf (TmpString, "VRAM size %d bytes.", VersionInfo.kmFrameBufferSize);
    debug_write (SNASM_STDOUT, TmpString, strlen (TmpString));

    /* Set screen border color. */
    Border.dwPacked = 0x0;
    kmSetBorderColor (Border);

    /* Set frame buffer surfaces. */
    FBSurfaces[0] = &PrimarySurfaceDesc;
    FBSurfaces[1] = &BackSurfaceDesc;
    kmsc.ppSurfaceDescArray = FBSurfaces;

#if DIRECT
    /* User must specify which lists are active for Kamui Direct mode. */
    VertexBufferDesc.fActiveList = (KM_ACTIVE_OPAQUE_POLYGON | KM_ACTIVE_TRANS_POLYGON);
#else
    /* Generate vertex buffer (double-buffered). */
    VertexBufferPtr = (PKMDWORD) Align32Malloc (2 * VBUFFER_SIZE * sizeof (KMDWORD));

    /* Kamui requires the vertex buffer region to be 32-byte aligned and non-cacheable. */
    kmsc.pVertexBuffer = (PKMDWORD) SH4_P2NonCachedMem (VertexBufferPtr);
#endif /* DIRECT */

    /* Set vertex buffer description. */
    kmsc.pBufferDesc = &VertexBufferDesc;

    /* Set system configuration. */
    kmSetSystemConfiguration (&kmsc);

    /* Create a texture surface. */
    kmCreateTextureSurface (&TexSurfaceDesc, 512, 128,
        (KM_TEXTURE_TWIDDLED_RECTANGLE | KM_TEXTURE_RGB565));

    /* Load a texture file from disk. */
    TexturePtr = LoadTextureFile ("Icy.pvr");

    /* Allocate memory for twiddled texture (512x128 16-bit). */
    TwiddledPtr = (PKMDWORD) Align32Malloc (512 * 128 * 2);

    /* Copy texture to 32-byte aligned address. */
    kmiMemCopy8 (TwiddledPtr, (PKMDWORD) ((long) TexturePtr + 16), 512 * 128 * 2);

    /* Transfer twiddled texture from system mem to video mem using a DMA transfer. */
    kmLoadTexture (&TexSurfaceDesc, TwiddledPtr, FALSE, FALSE);

    /* Pre-process the vertex context for opaque textures, using the loaded texture. */
    OpaqueTextureContext.pTextureSurfaceDesc = &TexSurfaceDesc;
    kmProcessVertexRenderState (&OpaqueTextureContext);

    /* Pre-process the vertex context for translucent textures, using the loaded texture. */
    TranslucentTextureContext.pTextureSurfaceDesc = &TexSurfaceDesc;
    kmProcessVertexRenderState (&TranslucentTextureContext);

    /* Pre-process the vertex context for opaque non-textured color polygons. */
    kmProcessVertexRenderState (&OpaqueColorContext);

    /* Initialize the background plane to point to each vertex in a screen-sized strip. */
    for (i = 0; i < 3; i++)
        BackPlane[i] = &CurrentStrip_01[i];

    /* The main render loop. */
    while (TRUE)
    {
        /* Set background plane render state to opaque color. */
        kmSetBackGroundRenderState (&OpaqueColorContext);

        /* Set the vertices of the background plane for a nice color gradient. */
        /* NOTE: Yes, you can change the background plane on-the-fly. */
        CurrentStrip_01[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[0].fX                   =   0.0f;
        CurrentStrip_01[0].fY                   = 480.0f;
        CurrentStrip_01[0].u.fZ                 =   0.2f;
        CurrentStrip_01[0].fBaseAlpha           =   1.0f;
        CurrentStrip_01[0].fBaseRed             =   0.0f;
        CurrentStrip_01[0].fBaseGreen           =   0.0f;
        CurrentStrip_01[0].fBaseBlue            =   0.0f;

        CurrentStrip_01[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[1].fX                   =   0.0f;
        CurrentStrip_01[1].fY                   =   0.0f;
        CurrentStrip_01[1].u.fZ                 =   0.2f;
        CurrentStrip_01[1].fBaseAlpha           =   1.0f;
        CurrentStrip_01[1].fBaseRed             =   Red;
        CurrentStrip_01[1].fBaseGreen           =   Green;
        CurrentStrip_01[1].fBaseBlue            =   Blue;

        CurrentStrip_01[2].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_01[2].fX                   = 640.0f;
        CurrentStrip_01[2].fY                   = 480.0f;
        CurrentStrip_01[2].u.fZ                 =   0.2f;
        CurrentStrip_01[2].fBaseAlpha           =   1.0f;
        CurrentStrip_01[2].fBaseRed             =   0.0f;
        CurrentStrip_01[2].fBaseGreen           =   0.0f;
        CurrentStrip_01[2].fBaseBlue            =   0.0f;

        /* Set the background plane to the vertices filled in above. */
		kmSetBackGroundPlane ((void *) BackPlane, KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));

        /* Update colors - just to show we're doing something. */
        if (Blue < 0.99)
        {
            Blue += ColorDelta;
            if (ColorDelta < 0)
            {
                Red += ColorDelta;
                Green += ColorDelta;
            }
        }
        else
        {
            if (Red > 0.99)
            {
                ColorDelta = -ColorDelta;
                Blue += ColorDelta;
            }
            Red += ColorDelta;
            Green += ColorDelta;
        }

        if (Blue < 0.0)
        {
            Blue = 0.0;
            Red = 0.0;
            Green = 0.0;
            ColorDelta = 0.01f;
        }

        /* Set Kamui's global render state for opaque non-textured color polygons. */
        kmSetVertexRenderState (&OpaqueColorContext);

        /* Start rendering opaque color strips. */
#if DIRECT
        kmStartVertexStripDirect();
#else
        kmStartVertexStrip (&VertexBufferDesc);
#endif /* DIRECT */

        /* Set the vertices in the current strip to draw a colorful square. */
        CurrentStrip_01[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[0].fX                   =  20.0f;
        CurrentStrip_01[0].fY                   = 460.0f;
        CurrentStrip_01[0].u.fZ                 =  10.0f;
        CurrentStrip_01[0].fBaseAlpha           =   1.0f;
        CurrentStrip_01[0].fBaseRed             =   1.0f;
        CurrentStrip_01[0].fBaseGreen           =   0.0f;
        CurrentStrip_01[0].fBaseBlue            =   0.0f;

        CurrentStrip_01[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[1].fX                   =  20.0f;
        CurrentStrip_01[1].fY                   = 332.0f;
        CurrentStrip_01[1].u.fZ                 =  10.0f;
        CurrentStrip_01[1].fBaseAlpha           =   1.0f;
        CurrentStrip_01[1].fBaseRed             =   0.0f;
        CurrentStrip_01[1].fBaseGreen           =   1.0f;
        CurrentStrip_01[1].fBaseBlue            =   0.0f;

        CurrentStrip_01[2].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[2].fX                   = 532.0f;
        CurrentStrip_01[2].fY                   = 460.0f;
        CurrentStrip_01[2].u.fZ                 =  10.0f;
        CurrentStrip_01[2].fBaseAlpha           =   1.0f;
        CurrentStrip_01[2].fBaseRed             =   0.0f;
        CurrentStrip_01[2].fBaseGreen           =   0.0f;
        CurrentStrip_01[2].fBaseBlue            =   1.0f;

        CurrentStrip_01[3].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_01[3].fX                   = 532.0f;
        CurrentStrip_01[3].fY                   = 332.0f;
        CurrentStrip_01[3].u.fZ                 =  10.0f;
        CurrentStrip_01[3].fBaseAlpha           =   1.0f;
        CurrentStrip_01[3].fBaseRed             =   0.0f;
        CurrentStrip_01[3].fBaseGreen           =   0.0f;
        CurrentStrip_01[3].fBaseBlue            =   0.0f;

        for (i=0; i<=3; i++)
#if DIRECT
            kmSetVertexDirect (&CurrentStrip_01[i], KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));
#else
            kmSetVertex (&VertexBufferDesc, &CurrentStrip_01[i],
                KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));
#endif /* DIRECT */

        /* Set Kamui's global render state for opaque textures. */
        kmSetVertexRenderState (&OpaqueTextureContext);

        /* Start rendering opaque textured strips. */
#if DIRECT
        kmStartVertexStripDirect();
#else
        kmStartVertexStrip (&VertexBufferDesc);
#endif /* DIRECT */

        /* Set the vertices in the current strip to draw a textured square. */
        CurrentStrip_05[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[0].fX                   = 108.0f;
        CurrentStrip_05[0].fY                   = 148.0f;
        CurrentStrip_05[0].u.fZ                 =  11.0f;
        CurrentStrip_05[0].fU                   =   0.0f;
        CurrentStrip_05[0].fV                   =   1.0f;
        CurrentStrip_05[0].fBaseAlpha           =   1.0f;
        CurrentStrip_05[0].fBaseRed             =   1.0f;
        CurrentStrip_05[0].fBaseGreen           =   0.0f;
        CurrentStrip_05[0].fBaseBlue            =   0.0f;

        CurrentStrip_05[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[1].fX                   = 108.0f;
        CurrentStrip_05[1].fY                   =  20.0f;
        CurrentStrip_05[1].u.fZ                 =  11.0f;
        CurrentStrip_05[1].fU                   =   0.0f;
        CurrentStrip_05[1].fV                   =   0.0f;
        CurrentStrip_05[1].fBaseAlpha           =   1.0f;
        CurrentStrip_05[1].fBaseRed             =   0.0f;
        CurrentStrip_05[1].fBaseGreen           =   1.0f;
        CurrentStrip_05[1].fBaseBlue            =   0.0f;

        CurrentStrip_05[2].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[2].fX                   = 620.0f;
        CurrentStrip_05[2].fY                   = 148.0f;
        CurrentStrip_05[2].u.fZ                 =  11.0f;
        CurrentStrip_05[2].fU                   =   1.0f;
        CurrentStrip_05[2].fV                   =   1.0f;
        CurrentStrip_05[2].fBaseAlpha           =   1.0f;
        CurrentStrip_05[2].fBaseRed             =   0.0f;
        CurrentStrip_05[2].fBaseGreen           =   0.0f;
        CurrentStrip_05[2].fBaseBlue            =   1.0f;

        CurrentStrip_05[3].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_05[3].fX                   = 620.0f;
        CurrentStrip_05[3].fY                   =  20.0f;
        CurrentStrip_05[3].u.fZ                 =  11.0f;
        CurrentStrip_05[3].fU                   =   1.0f;
        CurrentStrip_05[3].fV                   =   0.0f;
        CurrentStrip_05[3].fBaseAlpha           =   1.0f;
        CurrentStrip_05[3].fBaseRed             =   0.0f;
        CurrentStrip_05[3].fBaseGreen           =   0.0f;
        CurrentStrip_05[3].fBaseBlue            =   0.0f;

        for (i=0; i <= 3; i++)
#if DIRECT
            kmSetVertexDirect (&CurrentStrip_05[i], KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
#else
            kmSetVertex (&VertexBufferDesc, &CurrentStrip_05[i],
                KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
#endif /* DIRECT */

#if DIRECT
        /* Tell Kamui we're done with opaque polygon data. */
        kmSetEndOfListDirect();
#endif /* DIRECT */

        /* Set Kamui's global render state for translucent textures. */
        kmSetVertexRenderState (&TranslucentTextureContext);

        /* Start translucent opaque textured strips. */
#if DIRECT
        kmStartVertexStripDirect();
#else
        kmStartVertexStrip (&VertexBufferDesc);
#endif /* DIRECT */

        /* Set the vertices in the current strip to draw a translucent textured square. */
        CurrentStrip_05[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[0].fX                   =  50.0f;
        CurrentStrip_05[0].fY                   = 358.0f;
        CurrentStrip_05[0].u.fZ                 =  21.0f;
        CurrentStrip_05[0].fU                   =   0.0f;
        CurrentStrip_05[0].fV                   =   1.0f;
        CurrentStrip_05[0].fBaseAlpha           =   0.25f;
        CurrentStrip_05[0].fBaseRed             =   1.0f;
        CurrentStrip_05[0].fBaseGreen           =   0.0f;
        CurrentStrip_05[0].fBaseBlue            =   0.0f;

        CurrentStrip_05[1].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[1].fX                   =  50.0f;
        CurrentStrip_05[1].fY                   = 230.0f;
        CurrentStrip_05[1].u.fZ                 =  21.0f;
        CurrentStrip_05[1].fU                   =   0.0f;
        CurrentStrip_05[1].fV                   =   0.0f;
        CurrentStrip_05[1].fBaseAlpha           =   0.25f;
        CurrentStrip_05[1].fBaseRed             =   0.0f;
        CurrentStrip_05[1].fBaseGreen           =   1.0f;
        CurrentStrip_05[1].fBaseBlue            =   0.0f;

        CurrentStrip_05[2].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[2].fX                   = 562.0f;
        CurrentStrip_05[2].fY                   = 358.0f;
        CurrentStrip_05[2].u.fZ                 =  21.0f;
        CurrentStrip_05[2].fU                   =   1.0f;
        CurrentStrip_05[2].fV                   =   1.0f;
        CurrentStrip_05[2].fBaseAlpha           =   0.25f;
        CurrentStrip_05[2].fBaseRed             =   0.0f;
        CurrentStrip_05[2].fBaseGreen           =   0.0f;
        CurrentStrip_05[2].fBaseBlue            =   1.0f;

        CurrentStrip_05[3].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_05[3].fX                   = 562.0f;
        CurrentStrip_05[3].fY                   = 230.0f;
        CurrentStrip_05[3].u.fZ                 =  21.0f;
        CurrentStrip_05[3].fU                   =   1.0f;
        CurrentStrip_05[3].fV                   =   0.0f;
        CurrentStrip_05[3].fBaseAlpha           =   0.25f;
        CurrentStrip_05[3].fBaseRed             =   0.0f;
        CurrentStrip_05[3].fBaseGreen           =   0.0f;
        CurrentStrip_05[3].fBaseBlue            =   0.0f;

        /* NOTE: We can keep adding vertices, even past an ENDOFSTRIP flag - see below. */
        CurrentStrip_05[4].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[4].fX                   =  78.0f;
        CurrentStrip_05[4].fY                   = 250.0f;
        CurrentStrip_05[4].u.fZ                 =  20.0f;
        CurrentStrip_05[4].fU                   =   0.0f;
        CurrentStrip_05[4].fV                   =   1.0f;
        CurrentStrip_05[4].fBaseAlpha           =   0.75f;
        CurrentStrip_05[4].fBaseRed             =   1.0f;
        CurrentStrip_05[4].fBaseGreen           =   0.0f;
        CurrentStrip_05[4].fBaseBlue            =   0.0f;

        CurrentStrip_05[5].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[5].fX                   =  78.0f;
        CurrentStrip_05[5].fY                   = 122.0f;
        CurrentStrip_05[5].u.fZ                 =  20.0f;
        CurrentStrip_05[5].fU                   =   0.0f;
        CurrentStrip_05[5].fV                   =   0.0f;
        CurrentStrip_05[5].fBaseAlpha           =   0.75f;
        CurrentStrip_05[5].fBaseRed             =   0.0f;
        CurrentStrip_05[5].fBaseGreen           =   1.0f;
        CurrentStrip_05[5].fBaseBlue            =   0.0f;

        CurrentStrip_05[6].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_05[6].fX                   = 590.0f;
        CurrentStrip_05[6].fY                   = 250.0f;
        CurrentStrip_05[6].u.fZ                 =  20.0f;
        CurrentStrip_05[6].fU                   =   1.0f;
        CurrentStrip_05[6].fV                   =   1.0f;
        CurrentStrip_05[6].fBaseAlpha           =   0.75f;
        CurrentStrip_05[6].fBaseRed             =   0.0f;
        CurrentStrip_05[6].fBaseGreen           =   0.0f;
        CurrentStrip_05[6].fBaseBlue            =   1.0f;

        CurrentStrip_05[7].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_05[7].fX                   = 590.0f;
        CurrentStrip_05[7].fY                   = 122.0f;
        CurrentStrip_05[7].u.fZ                 =  20.0f;
        CurrentStrip_05[7].fU                   =   1.0f;
        CurrentStrip_05[7].fV                   =   0.0f;
        CurrentStrip_05[7].fBaseAlpha           =   0.75f;
        CurrentStrip_05[7].fBaseRed             =   0.0f;
        CurrentStrip_05[7].fBaseGreen           =   0.0f;
        CurrentStrip_05[7].fBaseBlue            =   0.0f;

        /* NOTE: A lot of context switching and setup can be avoided by providing several   */
        /*       strips of the same type in kmSetVertex/Direct() calls without starting a   */
        /*       new strip.  Don't call kmStartVertexStrip/Direct() redundantly.            */
        for (i = 0; i <= 7; i++)
#if DIRECT
            kmSetVertexDirect (&CurrentStrip_05[i], KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
#else
            kmSetVertex (&VertexBufferDesc, &CurrentStrip_05[i],
            	KM_VERTEXTYPE_05, sizeof (KMVERTEX_05));
#endif /* DIRECT */

#if DIRECT
        kmSetEndOfListDirect(); /* Tell Kamui we're done with translucent polygon data. */
        kmRenderDirect();       /* Render the scene using Kamui Direct. */
#else
        kmRender();     /* Send vertex buffers to the TA (to be rendered on next vblank). */
#endif /* DIRECT */

        /* Enqueue the framebuffer flip command. */
        kmFlipFrameBuffer();
    }

    /* Free memory allocated for textures. */
    syFree (TexturePtr);
    syFree (TwiddledPtr);

    /* Discard vertex buffers. */
    kmDiscardVertexBuffer (&VertexBufferDesc);

#if !DIRECT
    /* Free memory allocated for vertex buffer. */
    syFree (VertexBufferPtr);
#endif /* DIRECT */

    /* Free the texture surface. */
    kmFreeTexture (&TexSurfaceDesc);

    /* Free the frame buffers. */
    kmFreeTexture (&PrimarySurfaceDesc);
    kmFreeTexture (&BackSurfaceDesc);

    /* Stop the display - blank screen. */
    /* kmStopDisplayFrameBuffer(); */

    return 0;
}