/********************************************************************************************/
/* CONFIDENTIAL AND PROPRIETARY:                                                            */
/*      Copyright 1999, 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:     ClipTest.c                                                                     */
/* Title:    ClipTest Example                                                               */
/* Author:   Gary Lake                                                                      */
/* Created:  April 9, 1999                                                                  */
/*                                                                                          */
/* Version:  1.0                                                                            */
/* Platform: Dreamcast | Set5.24 | Shinobi | Kamui                                          */
/*                                                                                          */
/* Description:                                                                             */
/*      The purpose of this example is demonstrate the operation of the various             */
/*      hardware clipping operations.                                                       */
/*                                                                                          */
/* History:                                                                                 */
/*      04/09/99 - Ported QuikTest.c and added clipping code (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_OUTSIDE
};

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)


/********************************************************************************************/
/* 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);
}


/********************************************************************************************/
/* 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. */
    KMVERTEXBUFFDESC    VertexBufferDesc;       /* Vertex buffer. */
    PKMSURFACEDESC      FBSurfaces[2];          /* Array of pointers to FB surfaces. */
    KMVERTEX_01         CurrentStrip_01[16];    /* Strip of opaque color triangles. */
    PKMVERTEX_01        BackPlane[3];           /* Background plane. */
    PKMDWORD            VertexBufferPtr;        /* Pointer to user-allocated vertex buffer. */
    KMINT32             ClipAdjustOffset;       /* Adjusted user-clip offset value. */

    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). */
        {100.f, 0.f, 0.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, ColorDelta = 0.01f;
    float               y1 = 2.0f, y2 = 52.0f, yDelta1 = 2.0f, yDelta2 = 2.0f;
    char                TmpString[80];

#ifdef __GNUC__
	shinobi_workaround();
#endif

    /* Tell the user about this app. */
    debug_write (SNASM_STDOUT, "ClipTest: Demonstrates hardware clipping modes.", 47);

    /* 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);
#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 global clipping region (screen width & height, in tiles, from origin). */
    kmSetGlobalClipping ((640/32), (480/32 - 3));

    /* Set pixel unit clipping (clips at the final stage before pixel is written out. */
    kmSetPixelClipping (100, 40, 350, 480);

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

    /* Adjust display by 16 pixels to align user-clip region with the vertical center. */
    kmSetUserClipLevelAdjust (KM_LEVEL_ADJUST_HALF, &ClipAdjustOffset);

    /* 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 + (float) ClipAdjustOffset;
        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 + (float) ClipAdjustOffset;
        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 + (float) ClipAdjustOffset;
        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 a user clipping region (to the nearest 32x32 tile). */
        /* May be specified multiple times per frame. */
#if DIRECT
        kmSetUserClippingDirect (&VertexBufferDesc, KM_OPAQUE_POLYGON,
            0, (480/32/2 + 1), (640/32), (480/32 - 5));
#else
        kmSetUserClipping (&VertexBufferDesc, KM_OPAQUE_POLYGON,
            0, (480/32/2 + 1), (640/32), (480/32 - 5));
#endif /* DIRECT */

        /* 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 */

        y1 += yDelta1;
        if ((y1 > 376.0f) || (y1 < 3.0f))
            yDelta1 *= -1.0f;
        y2 += yDelta2;
        if ((y2 > 376.0f) || (y2 < 3.0f))
            yDelta2 *= -1.0f;

        /* Set the vertices in the current strip to draw a pair of colorful squares. */
        CurrentStrip_01[0].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[0].fX                   = 210.0f;
        CurrentStrip_01[0].fY                   = y1 + 100.0f + (float) ClipAdjustOffset;
        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                   = 210.0f;
        CurrentStrip_01[1].fY                   = y1 + (float) ClipAdjustOffset;
        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                   = 310.0f;
        CurrentStrip_01[2].fY                   = y1 + 100.0f + (float) ClipAdjustOffset;
        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                   = 310.0f;
        CurrentStrip_01[3].fY                   = y1 + (float) ClipAdjustOffset;
        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;

        CurrentStrip_01[4].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[4].fX                   = 270.0f;
        CurrentStrip_01[4].fY                   = y2 + 100.0f + (float) ClipAdjustOffset;
        CurrentStrip_01[4].u.fZ                 =  11.0f;
        CurrentStrip_01[4].fBaseAlpha           =   1.0f;
        CurrentStrip_01[4].fBaseRed             =   1.0f;
        CurrentStrip_01[4].fBaseGreen           =   0.0f;
        CurrentStrip_01[4].fBaseBlue            =   0.0f;

        CurrentStrip_01[5].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[5].fX                   = 270.0f;
        CurrentStrip_01[5].fY                   = y2 + (float) ClipAdjustOffset;
        CurrentStrip_01[5].u.fZ                 =  11.0f;
        CurrentStrip_01[5].fBaseAlpha           =   1.0f;
        CurrentStrip_01[5].fBaseRed             =   0.0f;
        CurrentStrip_01[5].fBaseGreen           =   1.0f;
        CurrentStrip_01[5].fBaseBlue            =   0.0f;

        CurrentStrip_01[6].ParamControlWord     = KM_VERTEXPARAM_NORMAL;
        CurrentStrip_01[6].fX                   = 370.0f;
        CurrentStrip_01[6].fY                   = y2 + 100.0f + (float) ClipAdjustOffset;
        CurrentStrip_01[6].u.fZ                 =  11.0f;
        CurrentStrip_01[6].fBaseAlpha           =   1.0f;
        CurrentStrip_01[6].fBaseRed             =   0.0f;
        CurrentStrip_01[6].fBaseGreen           =   0.0f;
        CurrentStrip_01[6].fBaseBlue            =   1.0f;

        CurrentStrip_01[7].ParamControlWord     = KM_VERTEXPARAM_ENDOFSTRIP;
        CurrentStrip_01[7].fX                   = 370.0f;
        CurrentStrip_01[7].fY                   = y2 + (float) ClipAdjustOffset;
        CurrentStrip_01[7].u.fZ                 =  11.0f;
        CurrentStrip_01[7].fBaseAlpha           =   1.0f;
        CurrentStrip_01[7].fBaseRed             =   0.0f;
        CurrentStrip_01[7].fBaseGreen           =   0.0f;
        CurrentStrip_01[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_01[i], KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));
#else
            kmSetVertex (&VertexBufferDesc, &CurrentStrip_01[i],
                KM_VERTEXTYPE_01, sizeof (KMVERTEX_01));
#endif /* DIRECT */

#if DIRECT
        kmSetEndOfListDirect(); /* Tell Kamui we're done with opaque 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();
    }

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

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

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

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

    return 0;
}