GLES2N64 (from mupen64plus-ae) plugin. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / gles2n64 / src / gSP.cpp
diff --git a/source/gles2n64/src/gSP.cpp b/source/gles2n64/src/gSP.cpp
new file mode 100644 (file)
index 0000000..2b845f9
--- /dev/null
@@ -0,0 +1,1738 @@
+#include <math.h>
+#include <stdlib.h>
+
+#include "Common.h"
+#include "gles2N64.h"
+#include "Debug.h"
+#include "Types.h"
+#include "RSP.h"
+#include "GBI.h"
+#include "gSP.h"
+#include "gDP.h"
+#include "3DMath.h"
+#include "OpenGL.h"
+#include "CRC.h"
+#include <string.h>
+#include "convert.h"
+#include "S2DEX.h"
+#include "VI.h"
+#include "DepthBuffer.h"
+#include "Config.h"
+
+//Note: 0xC0 is used by 1080 alot, its an unknown command.
+
+#ifdef DEBUG
+extern u32 uc_crc, uc_dcrc;
+extern char uc_str[256];
+#endif
+
+void gSPCombineMatrices();
+
+//#ifdef __TRIBUFFER_OPT
+void __indexmap_init()
+{
+    memset(OGL.triangles.indexmapinv, 0xFF, VERTBUFF_SIZE*sizeof(u32));
+    for(int i=0;i<INDEXMAP_SIZE;i++)
+    {
+        OGL.triangles.indexmap[i] = i;
+        //OGL.triangles.indexmapinv[i] = i;
+    }
+
+    OGL.triangles.indexmap_prev = -1;
+    OGL.triangles.indexmap_nomap = 0;
+}
+
+void __indexmap_clear()
+{
+    memset(OGL.triangles.indexmapinv, 0xFF, VERTBUFF_SIZE * sizeof(u32));
+    for(int i=0;i<INDEXMAP_SIZE;i++)
+        OGL.triangles.indexmapinv[OGL.triangles.indexmap[i]] = i;
+}
+
+u32 __indexmap_findunused(u32 num)
+{
+    u32 c = 0;
+    u32 i = min(OGL.triangles.indexmap_prev+1, VERTBUFF_SIZE-1);
+    u32 n = 0;
+    while(n < VERTBUFF_SIZE)
+    {
+        c = (OGL.triangles.indexmapinv[i] == 0xFFFFFFFF) ? (c+1) : 0;
+        if ((c == num) && (i < (VERTBUFF_SIZE - num)))
+        {
+            break;
+        }
+        i=i+1;
+        if (i >= VERTBUFF_SIZE) {i=0; c=0;}
+        n++;
+    }
+    return (c == num) ? (i-num+1) : (0xFFFFFFFF);
+}
+
+void __indexmap_undomap()
+{
+    SPVertex tmp[INDEXMAP_SIZE];
+    memset(OGL.triangles.indexmapinv, 0xFF, VERTBUFF_SIZE * sizeof(u32));
+
+    for(int i=0;i<INDEXMAP_SIZE;i++)
+    {
+        u32 ind = OGL.triangles.indexmap[i];
+        tmp[i] = OGL.triangles.vertices[ind];
+        OGL.triangles.indexmap[i] = i;
+        OGL.triangles.indexmapinv[i] = i;
+    }
+
+    memcpy(OGL.triangles.vertices, tmp, INDEXMAP_SIZE * sizeof(SPVertex));
+    OGL.triangles.indexmap_nomap = 1;
+}
+
+u32 __indexmap_getnew(u32 index, u32 num)
+{
+    u32 ind;
+
+    //test to see if unmapped
+    u32 unmapped = 1;
+    for(int i=0;i<num;i++)
+    {
+        if (OGL.triangles.indexmap[i]!=0xFFFFFFFF)
+        {
+            unmapped = 0;
+            break;
+        }
+
+    }
+
+    if (unmapped)
+    {
+        ind = index;
+    }
+    else
+    {
+        ind = __indexmap_findunused(num);
+
+        //no more room in buffer....
+        if (ind > VERTBUFF_SIZE)
+        {
+            OGL_DrawTriangles();
+            ind = __indexmap_findunused(num);
+
+            //OK the indices are spread so sparsely, we cannot find a num element block.
+            if (ind > VERTBUFF_SIZE)
+            {
+                __indexmap_undomap();
+                ind = __indexmap_findunused(num);
+                if (ind > VERTBUFF_SIZE)
+                {
+                    LOG(LOG_ERROR, "Could not allocate %i indices\n", num);
+
+                    LOG(LOG_VERBOSE, "indexmap=[");
+                    for(int i=0;i<INDEXMAP_SIZE;i++)
+                        LOG(LOG_VERBOSE, "%i,", OGL.triangles.indexmap[i]);
+                    LOG(LOG_VERBOSE, "]\n");
+
+                    LOG(LOG_VERBOSE, "indexmapinv=[");
+                    for(int i=0;i<VERTBUFF_SIZE;i++)
+                        LOG(LOG_VERBOSE, "%i,", OGL.triangles.indexmapinv[i]);
+                    LOG(LOG_VERBOSE, "]\n");
+                }
+                return ind;
+            }
+        }
+    }
+
+    for(int i=0;i<num;i++)
+    {
+        OGL.triangles.indexmap[index+i] = ind+i;
+        OGL.triangles.indexmapinv[ind+i] = index+i;
+    }
+
+    OGL.triangles.indexmap_prev = ind+num-1;
+    OGL.triangles.indexmap_nomap = 0;
+
+    return ind;
+}
+//#endif
+
+void gSPTriangle(s32 v0, s32 v1, s32 v2)
+{
+    if ((v0 < INDEXMAP_SIZE) && (v1 < INDEXMAP_SIZE) && (v2 < INDEXMAP_SIZE))
+    {
+
+#ifdef __TRIBUFFER_OPT
+        v0 = OGL.triangles.indexmap[v0];
+        v1 = OGL.triangles.indexmap[v1];
+        v2 = OGL.triangles.indexmap[v2];
+#endif
+
+#if 0
+        // Don't bother with triangles completely outside clipping frustrum
+        if (config.enableClipping)
+        {
+            if (OGL.triangles.vertices[v0].clip & OGL.triangles.vertices[v1].clip & OGL.triangles.vertices[v2].clip)
+            {
+                return;
+            }
+        }
+#endif
+
+        OGL_AddTriangle(v0, v1, v2);
+
+    }
+
+    if (depthBuffer.current) depthBuffer.current->cleared = FALSE;
+    gDP.colorImage.changed = TRUE;
+    gDP.colorImage.height = (unsigned int)(max( gDP.colorImage.height, gDP.scissor.lry ));
+}
+
+void gSP1Triangle( const s32 v0, const s32 v1, const s32 v2)
+{
+    gSPTriangle( v0, v1, v2);
+    gSPFlushTriangles();
+}
+
+void gSP2Triangles(const s32 v00, const s32 v01, const s32 v02, const s32 flag0,
+                    const s32 v10, const s32 v11, const s32 v12, const s32 flag1 )
+{
+    gSPTriangle( v00, v01, v02);
+    gSPTriangle( v10, v11, v12);
+    gSPFlushTriangles();
+}
+
+void gSP4Triangles(const s32 v00, const s32 v01, const s32 v02,
+                    const s32 v10, const s32 v11, const s32 v12,
+                    const s32 v20, const s32 v21, const s32 v22,
+                    const s32 v30, const s32 v31, const s32 v32 )
+{
+    gSPTriangle(v00, v01, v02);
+    gSPTriangle(v10, v11, v12);
+    gSPTriangle(v20, v21, v22);
+    gSPTriangle(v30, v31, v32);
+    gSPFlushTriangles();
+}
+
+
+gSPInfo gSP;
+
+f32 identityMatrix[4][4] =
+{
+    { 1.0f, 0.0f, 0.0f, 0.0f },
+    { 0.0f, 1.0f, 0.0f, 0.0f },
+    { 0.0f, 0.0f, 1.0f, 0.0f },
+    { 0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+#ifdef __VEC4_OPT
+static void gSPTransformVertex4_default(u32 v, float mtx[4][4])
+{
+    float x, y, z, w;
+    int i;
+    for(i = 0; i < 4; i++)
+    {
+        x = OGL.triangles.vertices[v+i].x;
+        y = OGL.triangles.vertices[v+i].y;
+        z = OGL.triangles.vertices[v+i].z;
+        w = OGL.triangles.vertices[v+i].w;
+        OGL.triangles.vertices[v+i].x = x * mtx[0][0] + y * mtx[1][0] + z * mtx[2][0] + mtx[3][0];
+        OGL.triangles.vertices[v+i].y = x * mtx[0][1] + y * mtx[1][1] + z * mtx[2][1] + mtx[3][1];
+        OGL.triangles.vertices[v+i].z = x * mtx[0][2] + y * mtx[1][2] + z * mtx[2][2] + mtx[3][2];
+        OGL.triangles.vertices[v+i].w = x * mtx[0][3] + y * mtx[1][3] + z * mtx[2][3] + mtx[3][3];
+    }
+}
+
+void gSPClipVertex4(u32 v)
+{
+    int i;
+    for(i = 0; i < 4; i++){
+        SPVertex *vtx = &OGL.triangles.vertices[v+i];
+        vtx->clip = 0;
+        if (vtx->x > +vtx->w)   vtx->clip |= CLIP_POSX;
+        if (vtx->x < -vtx->w)   vtx->clip |= CLIP_NEGX;
+        if (vtx->y > +vtx->w)   vtx->clip |= CLIP_POSY;
+        if (vtx->y < -vtx->w)   vtx->clip |= CLIP_NEGY;
+    }
+}
+
+static void gSPTransformNormal4_default(u32 v, float mtx[4][4])
+{
+    float len, x, y, z;
+    int i;
+    for(i = 0; i < 4; i++){
+        x = OGL.triangles.vertices[v+i].nx;
+        y = OGL.triangles.vertices[v+i].ny;
+        z = OGL.triangles.vertices[v+i].nz;
+
+        OGL.triangles.vertices[v+i].nx = mtx[0][0]*x + mtx[1][0]*y + mtx[2][0]*z;
+        OGL.triangles.vertices[v+i].ny = mtx[0][1]*x + mtx[1][1]*y + mtx[2][1]*z;
+        OGL.triangles.vertices[v+i].nz = mtx[0][2]*x + mtx[1][2]*y + mtx[2][2]*z;
+        len =   OGL.triangles.vertices[v+i].nx*OGL.triangles.vertices[v+i].nx +
+                OGL.triangles.vertices[v+i].ny*OGL.triangles.vertices[v+i].ny +
+                OGL.triangles.vertices[v+i].nz*OGL.triangles.vertices[v+i].nz;
+        if (len != 0.0)
+        {
+            len = sqrtf(len);
+            OGL.triangles.vertices[v+i].nx /= len;
+            OGL.triangles.vertices[v+i].ny /= len;
+            OGL.triangles.vertices[v+i].nz /= len;
+        }
+    }
+}
+
+static void gSPLightVertex4_default(u32 v)
+{
+    gSPTransformNormal4(v, gSP.matrix.modelView[gSP.matrix.modelViewi]);
+    for(int j = 0; j < 4; j++)
+    {
+        f32 r,g,b;
+        r = gSP.lights[gSP.numLights].r;
+        g = gSP.lights[gSP.numLights].g;
+        b = gSP.lights[gSP.numLights].b;
+
+        for (int i = 0; i < gSP.numLights; i++)
+        {
+            f32 intensity = DotProduct( &OGL.triangles.vertices[v+j].nx, &gSP.lights[i].x );
+            if (intensity < 0.0f) intensity = 0.0f;
+/*
+// paulscode, cause of the shader bug (not applying intensity to correct varriables)
+            OGL.triangles.vertices[v+j].r += gSP.lights[i].r * intensity;
+            OGL.triangles.vertices[v+j].g += gSP.lights[i].g * intensity;
+            OGL.triangles.vertices[v+j].b += gSP.lights[i].b * intensity;
+*/
+//// paulscode, shader bug-fix:
+            r += gSP.lights[i].r * intensity;
+            g += gSP.lights[i].g * intensity;
+            b += gSP.lights[i].b * intensity;
+////
+        }
+        OGL.triangles.vertices[v+j].r = min(1.0f, r);
+        OGL.triangles.vertices[v+j].g = min(1.0f, g);
+        OGL.triangles.vertices[v+j].b = min(1.0f, b);
+    }
+}
+
+static void gSPBillboardVertex4_default(u32 v)
+{
+
+    int i = 0;
+#ifdef __TRIBUFFER_OPT
+    i = OGL.triangles.indexmap[0];
+#endif
+
+    OGL.triangles.vertices[v].x += OGL.triangles.vertices[i].x;
+    OGL.triangles.vertices[v].y += OGL.triangles.vertices[i].y;
+    OGL.triangles.vertices[v].z += OGL.triangles.vertices[i].z;
+    OGL.triangles.vertices[v].w += OGL.triangles.vertices[i].w;
+    OGL.triangles.vertices[v+1].x += OGL.triangles.vertices[i].x;
+    OGL.triangles.vertices[v+1].y += OGL.triangles.vertices[i].y;
+    OGL.triangles.vertices[v+1].z += OGL.triangles.vertices[i].z;
+    OGL.triangles.vertices[v+1].w += OGL.triangles.vertices[i].w;
+    OGL.triangles.vertices[v+2].x += OGL.triangles.vertices[i].x;
+    OGL.triangles.vertices[v+2].y += OGL.triangles.vertices[i].y;
+    OGL.triangles.vertices[v+2].z += OGL.triangles.vertices[i].z;
+    OGL.triangles.vertices[v+2].w += OGL.triangles.vertices[i].w;
+    OGL.triangles.vertices[v+3].x += OGL.triangles.vertices[i].x;
+    OGL.triangles.vertices[v+3].y += OGL.triangles.vertices[i].y;
+    OGL.triangles.vertices[v+3].z += OGL.triangles.vertices[i].z;
+    OGL.triangles.vertices[v+3].w += OGL.triangles.vertices[i].w;
+}
+
+void gSPProcessVertex4(u32 v)
+{
+    if (gSP.changed & CHANGED_MATRIX)
+        gSPCombineMatrices();
+
+    gSPTransformVertex4(v, gSP.matrix.combined );
+
+    if (config.screen.flipVertical)
+    {
+        OGL.triangles.vertices[v+0].y = -OGL.triangles.vertices[v+0].y;
+        OGL.triangles.vertices[v+1].y = -OGL.triangles.vertices[v+1].y;
+        OGL.triangles.vertices[v+2].y = -OGL.triangles.vertices[v+2].y;
+        OGL.triangles.vertices[v+3].y = -OGL.triangles.vertices[v+3].y;
+    }
+
+    if (gDP.otherMode.depthSource)
+    {
+        OGL.triangles.vertices[v+0].z = gDP.primDepth.z * OGL.triangles.vertices[v+0].w;
+        OGL.triangles.vertices[v+1].z = gDP.primDepth.z * OGL.triangles.vertices[v+1].w;
+        OGL.triangles.vertices[v+2].z = gDP.primDepth.z * OGL.triangles.vertices[v+2].w;
+        OGL.triangles.vertices[v+3].z = gDP.primDepth.z * OGL.triangles.vertices[v+3].w;
+    }
+
+    if (gSP.matrix.billboard)
+        gSPBillboardVertex4(v);
+
+    if (!(gSP.geometryMode & G_ZBUFFER))
+    {
+        OGL.triangles.vertices[v].z = -OGL.triangles.vertices[v].w;
+        OGL.triangles.vertices[v+1].z = -OGL.triangles.vertices[v+1].w;
+        OGL.triangles.vertices[v+2].z = -OGL.triangles.vertices[v+2].w;
+        OGL.triangles.vertices[v+3].z = -OGL.triangles.vertices[v+3].w;
+    }
+
+    if (gSP.geometryMode & G_LIGHTING)
+    {
+        if (config.enableLighting)
+        {
+            gSPLightVertex4(v);
+        }
+        else
+        {
+            OGL.triangles.vertices[v].r = 1.0f;
+            OGL.triangles.vertices[v].g = 1.0f;
+            OGL.triangles.vertices[v].b = 1.0f;
+            OGL.triangles.vertices[v+1].r = 1.0f;
+            OGL.triangles.vertices[v+1].g = 1.0f;
+            OGL.triangles.vertices[v+1].b = 1.0f;
+            OGL.triangles.vertices[v+2].r = 1.0f;
+            OGL.triangles.vertices[v+2].g = 1.0f;
+            OGL.triangles.vertices[v+2].b = 1.0f;
+            OGL.triangles.vertices[v+3].r = 1.0f;
+            OGL.triangles.vertices[v+3].g = 1.0f;
+            OGL.triangles.vertices[v+3].b = 1.0f;
+        }
+
+        if (gSP.geometryMode & G_TEXTURE_GEN)
+        {
+            gSPTransformNormal4(v, gSP.matrix.projection);
+
+            if (gSP.geometryMode & G_TEXTURE_GEN_LINEAR)
+            {
+                OGL.triangles.vertices[v].s = acosf(OGL.triangles.vertices[v].nx) * 325.94931f;
+                OGL.triangles.vertices[v].t = acosf(OGL.triangles.vertices[v].ny) * 325.94931f;
+                OGL.triangles.vertices[v+1].s = acosf(OGL.triangles.vertices[v+1].nx) * 325.94931f;
+                OGL.triangles.vertices[v+1].t = acosf(OGL.triangles.vertices[v+1].ny) * 325.94931f;
+                OGL.triangles.vertices[v+2].s = acosf(OGL.triangles.vertices[v+2].nx) * 325.94931f;
+                OGL.triangles.vertices[v+2].t = acosf(OGL.triangles.vertices[v+2].ny) * 325.94931f;
+                OGL.triangles.vertices[v+3].s = acosf(OGL.triangles.vertices[v+3].nx) * 325.94931f;
+                OGL.triangles.vertices[v+3].t = acosf(OGL.triangles.vertices[v+3].ny) * 325.94931f;
+            }
+            else // G_TEXTURE_GEN
+            {
+                OGL.triangles.vertices[v].s = (OGL.triangles.vertices[v].nx + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v].t = (OGL.triangles.vertices[v].ny + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+1].s = (OGL.triangles.vertices[v+1].nx + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+1].t = (OGL.triangles.vertices[v+1].ny + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+2].s = (OGL.triangles.vertices[v+2].nx + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+2].t = (OGL.triangles.vertices[v+2].ny + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+3].s = (OGL.triangles.vertices[v+3].nx + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v+3].t = (OGL.triangles.vertices[v+3].ny + 1.0f) * 512.0f;
+            }
+        }
+    }
+
+    if (config.enableClipping) gSPClipVertex4(v);
+}
+#endif
+
+void gSPClipVertex(u32 v)
+{
+    SPVertex *vtx = &OGL.triangles.vertices[v];
+    vtx->clip = 0;
+    if (vtx->x > +vtx->w)   vtx->clip |= CLIP_POSX;
+    if (vtx->x < -vtx->w)   vtx->clip |= CLIP_NEGX;
+    if (vtx->y > +vtx->w)   vtx->clip |= CLIP_POSY;
+    if (vtx->y < -vtx->w)   vtx->clip |= CLIP_NEGY;
+    //if (vtx->w < 0.1f)      vtx->clip |= CLIP_NEGW;
+}
+
+static void gSPTransformVertex_default(float vtx[4], float mtx[4][4])
+{
+    float x, y, z, w;
+    x = vtx[0];
+    y = vtx[1];
+    z = vtx[2];
+    w = vtx[3];
+
+    vtx[0] = x * mtx[0][0] + y * mtx[1][0] + z * mtx[2][0] + mtx[3][0];
+    vtx[1] = x * mtx[0][1] + y * mtx[1][1] + z * mtx[2][1] + mtx[3][1];
+    vtx[2] = x * mtx[0][2] + y * mtx[1][2] + z * mtx[2][2] + mtx[3][2];
+    vtx[3] = x * mtx[0][3] + y * mtx[1][3] + z * mtx[2][3] + mtx[3][3];
+}
+
+static void gSPLightVertex_default(u32 v)
+{
+    TransformVectorNormalize( &OGL.triangles.vertices[v].nx, gSP.matrix.modelView[gSP.matrix.modelViewi] );
+
+    f32 r, g, b;
+    r = gSP.lights[gSP.numLights].r;
+    g = gSP.lights[gSP.numLights].g;
+    b = gSP.lights[gSP.numLights].b;
+    for (int i = 0; i < gSP.numLights; i++)
+    {
+        f32 intensity = DotProduct( &OGL.triangles.vertices[v].nx, &gSP.lights[i].x );
+        if (intensity < 0.0f) intensity = 0.0f;
+        r += gSP.lights[i].r * intensity;
+        g += gSP.lights[i].g * intensity;
+        b += gSP.lights[i].b * intensity;
+    }
+    OGL.triangles.vertices[v].r = min(1.0, r);
+    OGL.triangles.vertices[v].g = min(1.0, g);
+    OGL.triangles.vertices[v].b = min(1.0, b);
+}
+
+static void gSPBillboardVertex_default(u32 v, u32 i)
+{
+    OGL.triangles.vertices[v].x += OGL.triangles.vertices[i].x;
+    OGL.triangles.vertices[v].y += OGL.triangles.vertices[i].y;
+    OGL.triangles.vertices[v].z += OGL.triangles.vertices[i].z;
+    OGL.triangles.vertices[v].w += OGL.triangles.vertices[i].w;
+}
+
+void gSPCombineMatrices()
+{
+    MultMatrix(gSP.matrix.projection, gSP.matrix.modelView[gSP.matrix.modelViewi], gSP.matrix.combined);
+    gSP.changed &= ~CHANGED_MATRIX;
+}
+
+void gSPProcessVertex( u32 v )
+{
+    f32 intensity;
+    f32 r, g, b;
+
+    if (gSP.changed & CHANGED_MATRIX)
+        gSPCombineMatrices();
+
+    gSPTransformVertex( &OGL.triangles.vertices[v].x, gSP.matrix.combined );
+
+    if (config.screen.flipVertical)
+    {
+        OGL.triangles.vertices[v].y = -OGL.triangles.vertices[v].y;
+    }
+
+    if (gDP.otherMode.depthSource)
+    {
+        OGL.triangles.vertices[v].z = gDP.primDepth.z * OGL.triangles.vertices[v].w;
+    }
+
+    if (gSP.matrix.billboard)
+    {
+        int i = 0;
+#ifdef __TRIBUFFER_OPT
+        i = OGL.triangles.indexmap[0];
+#endif
+
+        gSPBillboardVertex(v, i);
+    }
+
+    if (!(gSP.geometryMode & G_ZBUFFER))
+    {
+        OGL.triangles.vertices[v].z = -OGL.triangles.vertices[v].w;
+    }
+
+    if (config.enableClipping)
+        gSPClipVertex(v);
+
+    if (gSP.geometryMode & G_LIGHTING)
+    {
+        if (config.enableLighting)
+        {
+            gSPLightVertex(v);
+        }
+        else
+        {
+            OGL.triangles.vertices[v].r = 1.0f;
+            OGL.triangles.vertices[v].g = 1.0f;
+            OGL.triangles.vertices[v].b = 1.0f;
+        }
+
+        if (gSP.geometryMode & G_TEXTURE_GEN)
+        {
+            TransformVectorNormalize(&OGL.triangles.vertices[v].nx, gSP.matrix.projection);
+
+            if (gSP.geometryMode & G_TEXTURE_GEN_LINEAR)
+            {
+                OGL.triangles.vertices[v].s = acosf(OGL.triangles.vertices[v].nx) * 325.94931f;
+                OGL.triangles.vertices[v].t = acosf(OGL.triangles.vertices[v].ny) * 325.94931f;
+            }
+            else // G_TEXTURE_GEN
+            {
+                OGL.triangles.vertices[v].s = (OGL.triangles.vertices[v].nx + 1.0f) * 512.0f;
+                OGL.triangles.vertices[v].t = (OGL.triangles.vertices[v].ny + 1.0f) * 512.0f;
+            }
+        }
+    }
+}
+
+
+void gSPLoadUcodeEx( u32 uc_start, u32 uc_dstart, u16 uc_dsize )
+{
+    RSP.PCi = 0;
+    gSP.matrix.modelViewi = 0;
+    gSP.changed |= CHANGED_MATRIX;
+    gSP.status[0] = gSP.status[1] = gSP.status[2] = gSP.status[3] = 0;
+
+    if ((((uc_start & 0x1FFFFFFF) + 4096) > RDRAMSize) || (((uc_dstart & 0x1FFFFFFF) + uc_dsize) > RDRAMSize))
+    {
+        return;
+    }
+
+    MicrocodeInfo *ucode = GBI_DetectMicrocode( uc_start, uc_dstart, uc_dsize );
+
+    if (ucode->type != 0xFFFFFFFF)
+        last_good_ucode = ucode->type;
+
+    if (ucode->type != NONE)
+    {
+        GBI_MakeCurrent( ucode );
+    }
+    else
+    {
+        LOG(LOG_WARNING, "Unknown Ucode\n");
+    }
+}
+
+void gSPNoOp()
+{
+    gSPFlushTriangles();
+}
+
+void gSPTriangleUnknown()
+{
+#ifdef __TRIBUFFER_OPT
+    gSPFlushTriangles();
+#endif
+}
+
+void gSPMatrix( u32 matrix, u8 param )
+{
+#ifdef __TRIBUFFER_OPT
+    gSPFlushTriangles();
+#endif
+
+    f32 mtx[4][4];
+    u32 address = RSP_SegmentToPhysical( matrix );
+
+    if (address + 64 > RDRAMSize)
+    {
+        return;
+    }
+
+    RSP_LoadMatrix( mtx, address );
+
+    if (param & G_MTX_PROJECTION)
+    {
+        if (param & G_MTX_LOAD)
+            CopyMatrix( gSP.matrix.projection, mtx );
+        else
+            MultMatrix2( gSP.matrix.projection, mtx );
+    }
+    else
+    {
+        if ((param & G_MTX_PUSH) && (gSP.matrix.modelViewi < (gSP.matrix.stackSize - 1)))
+        {
+            CopyMatrix( gSP.matrix.modelView[gSP.matrix.modelViewi + 1], gSP.matrix.modelView[gSP.matrix.modelViewi] );
+            gSP.matrix.modelViewi++;
+        }
+        if (param & G_MTX_LOAD)
+            CopyMatrix( gSP.matrix.modelView[gSP.matrix.modelViewi], mtx );
+        else
+            MultMatrix2( gSP.matrix.modelView[gSP.matrix.modelViewi], mtx );
+    }
+
+    gSP.changed |= CHANGED_MATRIX;
+}
+
+void gSPDMAMatrix( u32 matrix, u8 index, u8 multiply )
+{
+    f32 mtx[4][4];
+    u32 address = gSP.DMAOffsets.mtx + RSP_SegmentToPhysical( matrix );
+
+    if (address + 64 > RDRAMSize)
+    {
+        return;
+    }
+
+    RSP_LoadMatrix( mtx, address );
+
+    gSP.matrix.modelViewi = index;
+
+    if (multiply)
+    {
+        //CopyMatrix( gSP.matrix.modelView[gSP.matrix.modelViewi], gSP.matrix.modelView[0] );
+        //MultMatrix( gSP.matrix.modelView[gSP.matrix.modelViewi], mtx );
+        MultMatrix(gSP.matrix.modelView[0], mtx, gSP.matrix.modelView[gSP.matrix.modelViewi]);
+    }
+    else
+        CopyMatrix( gSP.matrix.modelView[gSP.matrix.modelViewi], mtx );
+
+    CopyMatrix( gSP.matrix.projection, identityMatrix );
+    gSP.changed |= CHANGED_MATRIX;
+}
+
+void gSPViewport( u32 v )
+{
+    u32 address = RSP_SegmentToPhysical( v );
+
+    if ((address + 16) > RDRAMSize)
+    {
+#ifdef DEBUG
+        DebugMsg( DEBUG_HIGH | DEBUG_ERROR, "// Attempting to load viewport from invalid address\n" );
+        DebugMsg( DEBUG_HIGH | DEBUG_HANDLED, "gSPViewport( 0x%08X );\n", v );
+#endif
+        return;
+    }
+
+    gSP.viewport.vscale[0] = _FIXED2FLOAT( *(s16*)&RDRAM[address +  2], 2 );
+    gSP.viewport.vscale[1] = _FIXED2FLOAT( *(s16*)&RDRAM[address     ], 2 );
+    gSP.viewport.vscale[2] = _FIXED2FLOAT( *(s16*)&RDRAM[address +  6], 10 );// * 0.00097847357f;
+    gSP.viewport.vscale[3] = *(s16*)&RDRAM[address +  4];
+    gSP.viewport.vtrans[0] = _FIXED2FLOAT( *(s16*)&RDRAM[address + 10], 2 );
+    gSP.viewport.vtrans[1] = _FIXED2FLOAT( *(s16*)&RDRAM[address +  8], 2 );
+    gSP.viewport.vtrans[2] = _FIXED2FLOAT( *(s16*)&RDRAM[address + 14], 10 );// * 0.00097847357f;
+    gSP.viewport.vtrans[3] = *(s16*)&RDRAM[address + 12];
+
+    gSP.viewport.x      = gSP.viewport.vtrans[0] - gSP.viewport.vscale[0];
+    gSP.viewport.y      = gSP.viewport.vtrans[1] - gSP.viewport.vscale[1];
+    gSP.viewport.width  = gSP.viewport.vscale[0] * 2;
+    gSP.viewport.height = gSP.viewport.vscale[1] * 2;
+    gSP.viewport.nearz  = gSP.viewport.vtrans[2] - gSP.viewport.vscale[2];
+    gSP.viewport.farz   = (gSP.viewport.vtrans[2] + gSP.viewport.vscale[2]) ;
+
+    gSP.changed |= CHANGED_VIEWPORT;
+}
+
+void gSPForceMatrix( u32 mptr )
+{
+    u32 address = RSP_SegmentToPhysical( mptr );
+
+    if (address + 64 > RDRAMSize)
+    {
+        return;
+    }
+
+    RSP_LoadMatrix( gSP.matrix.combined, RSP_SegmentToPhysical( mptr ) );
+
+    gSP.changed &= ~CHANGED_MATRIX;
+}
+
+void gSPLight( u32 l, s32 n )
+{
+    n--;
+    if (n >= 8)
+        return;
+
+    u32 address = RSP_SegmentToPhysical( l );
+
+    if ((address + sizeof( Light )) > RDRAMSize)
+    {
+        return;
+    }
+
+    u8 *addr = &RDRAM[address];
+
+    if (config.hackZelda && (addr[0] == 0x08) && (addr[4] == 0xFF))
+    {
+        LightMM *light = (LightMM*)addr;
+        gSP.lights[n].r = light->r * 0.0039215689f;
+        gSP.lights[n].g = light->g * 0.0039215689f;
+        gSP.lights[n].b = light->b * 0.0039215689f;
+        gSP.lights[n].x = light->x;
+        gSP.lights[n].y = light->y;
+        gSP.lights[n].z = light->z;
+    }
+    else
+    {
+        Light *light = (Light*)addr;
+        gSP.lights[n].r = light->r * 0.0039215689f;
+        gSP.lights[n].g = light->g * 0.0039215689f;
+        gSP.lights[n].b = light->b * 0.0039215689f;
+        gSP.lights[n].x = light->x;
+        gSP.lights[n].y = light->y;
+        gSP.lights[n].z = light->z;
+    }
+    Normalize(&gSP.lights[n].x);
+}
+
+void gSPLookAt( u32 l )
+{
+}
+
+void gSPVertex( u32 v, u32 n, u32 v0 )
+{
+    //flush batched triangles:
+#ifdef __TRIBUFFER_OPT
+    gSPFlushTriangles();
+#endif
+
+    u32 address = RSP_SegmentToPhysical( v );
+
+    if ((address + sizeof( Vertex ) * n) > RDRAMSize)
+    {
+        return;
+    }
+
+    Vertex *vertex = (Vertex*)&RDRAM[address];
+
+    if ((n + v0) <= INDEXMAP_SIZE)
+    {
+        unsigned int i = v0;
+#ifdef __VEC4_OPT
+        for (; i < n - (n%4) + v0; i += 4)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            v = __indexmap_getnew(v, 4);
+#endif
+            for(int j = 0; j < 4; j++)
+            {
+                OGL.triangles.vertices[v+j].x = vertex->x;
+                OGL.triangles.vertices[v+j].y = vertex->y;
+                OGL.triangles.vertices[v+j].z = vertex->z;
+                //OGL.triangles.vertices[i+j].flag = vertex->flag;
+                OGL.triangles.vertices[v+j].s = _FIXED2FLOAT( vertex->s, 5 );
+                OGL.triangles.vertices[v+j].t = _FIXED2FLOAT( vertex->t, 5 );
+                if (gSP.geometryMode & G_LIGHTING)
+                {
+                    OGL.triangles.vertices[v+j].nx = vertex->normal.x;
+                    OGL.triangles.vertices[v+j].ny = vertex->normal.y;
+                    OGL.triangles.vertices[v+j].nz = vertex->normal.z;
+                    OGL.triangles.vertices[v+j].a = vertex->color.a * 0.0039215689f;
+                }
+                else
+                {
+                    OGL.triangles.vertices[v+j].r = vertex->color.r * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].g = vertex->color.g * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].b = vertex->color.b * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].a = vertex->color.a * 0.0039215689f;
+                }
+                vertex++;
+            }
+            gSPProcessVertex4(v);
+        }
+#endif
+        for (; i < n + v0; i++)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            v = __indexmap_getnew(v, 1);
+#endif
+            OGL.triangles.vertices[v].x = vertex->x;
+            OGL.triangles.vertices[v].y = vertex->y;
+            OGL.triangles.vertices[v].z = vertex->z;
+            OGL.triangles.vertices[v].s = _FIXED2FLOAT( vertex->s, 5 );
+            OGL.triangles.vertices[v].t = _FIXED2FLOAT( vertex->t, 5 );
+            if (gSP.geometryMode & G_LIGHTING)
+            {
+                OGL.triangles.vertices[v].nx = vertex->normal.x;
+                OGL.triangles.vertices[v].ny = vertex->normal.y;
+                OGL.triangles.vertices[v].nz = vertex->normal.z;
+                OGL.triangles.vertices[v].a = vertex->color.a * 0.0039215689f;
+            }
+            else
+            {
+                OGL.triangles.vertices[v].r = vertex->color.r * 0.0039215689f;
+                OGL.triangles.vertices[v].g = vertex->color.g * 0.0039215689f;
+                OGL.triangles.vertices[v].b = vertex->color.b * 0.0039215689f;
+                OGL.triangles.vertices[v].a = vertex->color.a * 0.0039215689f;
+            }
+            gSPProcessVertex(v);
+            vertex++;
+        }
+    }
+    else
+    {
+        LOG(LOG_ERROR, "Using Vertex outside buffer v0=%i, n=%i\n", v0, n);
+    }
+
+}
+
+void gSPCIVertex( u32 v, u32 n, u32 v0 )
+{
+
+#ifdef __TRIBUFFER_OPT
+    gSPFlushTriangles();
+#endif
+
+    u32 address = RSP_SegmentToPhysical( v );
+
+    if ((address + sizeof( PDVertex ) * n) > RDRAMSize)
+    {
+        return;
+    }
+
+    PDVertex *vertex = (PDVertex*)&RDRAM[address];
+
+    if ((n + v0) <= INDEXMAP_SIZE)
+    {
+        unsigned int i = v0;
+#ifdef __VEC4_OPT
+        for (; i < n - (n%4) + v0; i += 4)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            v = __indexmap_getnew(v, 4);
+#endif
+            for(unsigned int j = 0; j < 4; j++)
+            {
+                OGL.triangles.vertices[v+j].x = vertex->x;
+                OGL.triangles.vertices[v+j].y = vertex->y;
+                OGL.triangles.vertices[v+j].z = vertex->z;
+                OGL.triangles.vertices[v+j].s = _FIXED2FLOAT( vertex->s, 5 );
+                OGL.triangles.vertices[v+j].t = _FIXED2FLOAT( vertex->t, 5 );
+                u8 *color = &RDRAM[gSP.vertexColorBase + (vertex->ci & 0xff)];
+
+                if (gSP.geometryMode & G_LIGHTING)
+                {
+                    OGL.triangles.vertices[v+j].nx = (s8)color[3];
+                    OGL.triangles.vertices[v+j].ny = (s8)color[2];
+                    OGL.triangles.vertices[v+j].nz = (s8)color[1];
+                    OGL.triangles.vertices[v+j].a = color[0] * 0.0039215689f;
+                }
+                else
+                {
+                    OGL.triangles.vertices[v+j].r = color[3] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].g = color[2] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].b = color[1] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].a = color[0] * 0.0039215689f;
+                }
+                vertex++;
+            }
+            gSPProcessVertex4(v);
+        }
+#endif
+        for(; i < n + v0; i++)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            v = __indexmap_getnew(v, 1);
+#endif
+            OGL.triangles.vertices[v].x = vertex->x;
+            OGL.triangles.vertices[v].y = vertex->y;
+            OGL.triangles.vertices[v].z = vertex->z;
+            OGL.triangles.vertices[v].s = _FIXED2FLOAT( vertex->s, 5 );
+            OGL.triangles.vertices[v].t = _FIXED2FLOAT( vertex->t, 5 );
+            u8 *color = &RDRAM[gSP.vertexColorBase + (vertex->ci & 0xff)];
+
+            if (gSP.geometryMode & G_LIGHTING)
+            {
+                OGL.triangles.vertices[v].nx = (s8)color[3];
+                OGL.triangles.vertices[v].ny = (s8)color[2];
+                OGL.triangles.vertices[v].nz = (s8)color[1];
+                OGL.triangles.vertices[v].a = color[0] * 0.0039215689f;
+            }
+            else
+            {
+                OGL.triangles.vertices[v].r = color[3] * 0.0039215689f;
+                OGL.triangles.vertices[v].g = color[2] * 0.0039215689f;
+                OGL.triangles.vertices[v].b = color[1] * 0.0039215689f;
+                OGL.triangles.vertices[v].a = color[0] * 0.0039215689f;
+            }
+
+            gSPProcessVertex(v);
+            vertex++;
+        }
+    }
+    else
+    {
+        LOG(LOG_ERROR, "Using Vertex outside buffer v0=%i, n=%i\n", v0, n);
+    }
+
+}
+
+void gSPDMAVertex( u32 v, u32 n, u32 v0 )
+{
+
+    u32 address = gSP.DMAOffsets.vtx + RSP_SegmentToPhysical( v );
+
+    if ((address + 10 * n) > RDRAMSize)
+    {
+        return;
+    }
+
+    if ((n + v0) <= INDEXMAP_SIZE)
+    {
+        u32 i = v0;
+#ifdef __VEC4_OPT
+        for (; i < n - (n%4) + v0; i += 4)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            v = __indexmap_getnew(v, 4);
+#endif
+            for(int j = 0; j < 4; j++)
+            {
+                OGL.triangles.vertices[v+j].x = *(s16*)&RDRAM[address ^ 2];
+                OGL.triangles.vertices[v+j].y = *(s16*)&RDRAM[(address + 2) ^ 2];
+                OGL.triangles.vertices[v+j].z = *(s16*)&RDRAM[(address + 4) ^ 2];
+
+                if (gSP.geometryMode & G_LIGHTING)
+                {
+                    OGL.triangles.vertices[v+j].nx = *(s8*)&RDRAM[(address + 6) ^ 3];
+                    OGL.triangles.vertices[v+j].ny = *(s8*)&RDRAM[(address + 7) ^ 3];
+                    OGL.triangles.vertices[v+j].nz = *(s8*)&RDRAM[(address + 8) ^ 3];
+                    OGL.triangles.vertices[v+j].a = *(u8*)&RDRAM[(address + 9) ^ 3] * 0.0039215689f;
+                }
+                else
+                {
+                    OGL.triangles.vertices[v+j].r = *(u8*)&RDRAM[(address + 6) ^ 3] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].g = *(u8*)&RDRAM[(address + 7) ^ 3] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].b = *(u8*)&RDRAM[(address + 8) ^ 3] * 0.0039215689f;
+                    OGL.triangles.vertices[v+j].a = *(u8*)&RDRAM[(address + 9) ^ 3] * 0.0039215689f;
+                }
+                address += 10;
+            }
+            gSPProcessVertex4(v);
+        }
+#endif
+        for (; i < n + v0; i++)
+        {
+            u32 v = i;
+#ifdef __TRIBUFFER_OPT
+            //int ind = OGL.triangles.indexmap[i];
+            v = __indexmap_getnew(v, 1);
+
+            //if previously mapped copy across s/t.
+            //if (ind != -1)
+            //{
+            //    SPVertex *vtx = &OGL.triangles.vertices[ind];
+            //    OGL.triangles.vertices[v].s = vtx->s;
+            //    OGL.triangles.vertices[v].t = vtx->s;
+            //}
+#else
+            v = i;
+#endif
+            OGL.triangles.vertices[v].x = *(s16*)&RDRAM[address ^ 2];
+            OGL.triangles.vertices[v].y = *(s16*)&RDRAM[(address + 2) ^ 2];
+            OGL.triangles.vertices[v].z = *(s16*)&RDRAM[(address + 4) ^ 2];
+
+            if (gSP.geometryMode & G_LIGHTING)
+            {
+                OGL.triangles.vertices[v].nx = *(s8*)&RDRAM[(address + 6) ^ 3];
+                OGL.triangles.vertices[v].ny = *(s8*)&RDRAM[(address + 7) ^ 3];
+                OGL.triangles.vertices[v].nz = *(s8*)&RDRAM[(address + 8) ^ 3];
+                OGL.triangles.vertices[v].a = *(u8*)&RDRAM[(address + 9) ^ 3] * 0.0039215689f;
+            }
+            else
+            {
+                OGL.triangles.vertices[v].r = *(u8*)&RDRAM[(address + 6) ^ 3] * 0.0039215689f;
+                OGL.triangles.vertices[v].g = *(u8*)&RDRAM[(address + 7) ^ 3] * 0.0039215689f;
+                OGL.triangles.vertices[v].b = *(u8*)&RDRAM[(address + 8) ^ 3] * 0.0039215689f;
+                OGL.triangles.vertices[v].a = *(u8*)&RDRAM[(address + 9) ^ 3] * 0.0039215689f;
+            }
+
+            gSPProcessVertex(v);
+            address += 10;
+        }
+    }
+    else
+    {
+        LOG(LOG_ERROR, "Using Vertex outside buffer v0=%i, n=%i\n", v0, n);
+    }
+
+}
+
+void gSPDisplayList( u32 dl )
+{
+    u32 address = RSP_SegmentToPhysical( dl );
+
+    if ((address + 8) > RDRAMSize)
+    {
+        return;
+    }
+
+    if (RSP.PCi < (GBI.PCStackSize - 1))
+    {
+#ifdef DEBUG
+    DebugMsg( DEBUG_HIGH | DEBUG_HANDLED, "\n" );
+    DebugMsg( DEBUG_HIGH | DEBUG_HANDLED, "gSPDisplayList( 0x%08X );\n",
+        dl );
+#endif
+        RSP.PCi++;
+        RSP.PC[RSP.PCi] = address;
+        RSP.nextCmd = _SHIFTR( *(u32*)&RDRAM[address], 24, 8 );
+    }
+
+
+}
+
+void gSPDMADisplayList( u32 dl, u32 n )
+{
+    if ((dl + (n << 3)) > RDRAMSize)
+    {
+        return;
+    }
+
+    u32 curDL = RSP.PC[RSP.PCi];
+
+    RSP.PC[RSP.PCi] = RSP_SegmentToPhysical( dl );
+
+    while ((RSP.PC[RSP.PCi] - dl) < (n << 3))
+    {
+        if ((RSP.PC[RSP.PCi] + 8) > RDRAMSize)
+        {
+            break;
+        }
+
+        u32 w0 = *(u32*)&RDRAM[RSP.PC[RSP.PCi]];
+        u32 w1 = *(u32*)&RDRAM[RSP.PC[RSP.PCi] + 4];
+
+        RSP.PC[RSP.PCi] += 8;
+        RSP.nextCmd = _SHIFTR( *(u32*)&RDRAM[RSP.PC[RSP.PCi]], 24, 8 );
+
+        GBI.cmd[_SHIFTR( w0, 24, 8 )]( w0, w1 );
+    }
+
+    RSP.PC[RSP.PCi] = curDL;
+}
+
+void gSPBranchList( u32 dl )
+{
+    u32 address = RSP_SegmentToPhysical( dl );
+
+    if ((address + 8) > RDRAMSize)
+    {
+        return;
+    }
+
+    RSP.PC[RSP.PCi] = address;
+    RSP.nextCmd = _SHIFTR( *(u32*)&RDRAM[address], 24, 8 );
+}
+
+void gSPBranchLessZ( u32 branchdl, u32 vtx, f32 zval )
+{
+    u32 address = RSP_SegmentToPhysical( branchdl );
+
+    if ((address + 8) > RDRAMSize)
+    {
+        return;
+    }
+
+    if (OGL.triangles.vertices[vtx].z <= zval)
+        RSP.PC[RSP.PCi] = address;
+}
+
+void gSPSetDMAOffsets( u32 mtxoffset, u32 vtxoffset )
+{
+    gSP.DMAOffsets.mtx = mtxoffset;
+    gSP.DMAOffsets.vtx = vtxoffset;
+}
+
+void gSPSetVertexColorBase( u32 base )
+{
+    gSP.vertexColorBase = RSP_SegmentToPhysical( base );
+
+#ifdef __TRIBUFFER_OPT
+    gSPFlushTriangles();
+#endif
+}
+
+void gSPSprite2DBase( u32 base )
+{
+}
+
+void gSPCopyVertex( SPVertex *dest, SPVertex *src )
+{
+    dest->x = src->x;
+    dest->y = src->y;
+    dest->z = src->z;
+    dest->w = src->w;
+    dest->r = src->r;
+    dest->g = src->g;
+    dest->b = src->b;
+    dest->a = src->a;
+    dest->s = src->s;
+    dest->t = src->t;
+}
+
+void gSPInterpolateVertex( SPVertex *dest, f32 percent, SPVertex *first, SPVertex *second )
+{
+    dest->x = first->x + percent * (second->x - first->x);
+    dest->y = first->y + percent * (second->y - first->y);
+    dest->z = first->z + percent * (second->z - first->z);
+    dest->w = first->w + percent * (second->w - first->w);
+    dest->r = first->r + percent * (second->r - first->r);
+    dest->g = first->g + percent * (second->g - first->g);
+    dest->b = first->b + percent * (second->b - first->b);
+    dest->a = first->a + percent * (second->a - first->a);
+    dest->s = first->s + percent * (second->s - first->s);
+    dest->t = first->t + percent * (second->t - first->t);
+}
+
+void gSPDMATriangles( u32 tris, u32 n )
+{
+    u32 address = RSP_SegmentToPhysical( tris );
+
+    if (address + sizeof( DKRTriangle ) * n > RDRAMSize)
+    {
+        return;
+    }
+
+#ifdef __TRIBUFFER_OPT
+    __indexmap_undomap();
+#endif
+
+    DKRTriangle *triangles = (DKRTriangle*)&RDRAM[address];
+
+    for (u32 i = 0; i < n; i++)
+    {
+        int mode = 0;
+        if (!(triangles->flag & 0x40))
+        {
+            if (gSP.viewport.vscale[0] > 0)
+                mode |= G_CULL_BACK;
+            else
+                mode |= G_CULL_FRONT;
+        }
+
+        if ((gSP.geometryMode&G_CULL_BOTH) != mode)
+        {
+            OGL_DrawTriangles();
+            gSP.geometryMode &= ~G_CULL_BOTH;
+            gSP.geometryMode |= mode;
+            gSP.changed |= CHANGED_GEOMETRYMODE;
+        }
+
+
+        s32 v0 = triangles->v0;
+        s32 v1 = triangles->v1;
+        s32 v2 = triangles->v2;
+        OGL.triangles.vertices[v0].s = _FIXED2FLOAT( triangles->s0, 5 );
+        OGL.triangles.vertices[v0].t = _FIXED2FLOAT( triangles->t0, 5 );
+        OGL.triangles.vertices[v1].s = _FIXED2FLOAT( triangles->s1, 5 );
+        OGL.triangles.vertices[v1].t = _FIXED2FLOAT( triangles->t1, 5 );
+        OGL.triangles.vertices[v2].s = _FIXED2FLOAT( triangles->s2, 5 );
+        OGL.triangles.vertices[v2].t = _FIXED2FLOAT( triangles->t2, 5 );
+        gSPTriangle(triangles->v0, triangles->v1, triangles->v2);
+        triangles++;
+    }
+
+#ifdef __TRIBUFFER_OPT
+    OGL_DrawTriangles();
+#endif
+}
+
+void gSP1Quadrangle( s32 v0, s32 v1, s32 v2, s32 v3)
+{
+    gSPTriangle( v0, v1, v2);
+    gSPTriangle( v0, v2, v3);
+    gSPFlushTriangles();
+}
+
+bool gSPCullVertices( u32 v0, u32 vn )
+{
+    if (!config.enableClipping)
+        return FALSE;
+
+    s32 v = v0;
+#ifdef __TRIBUFFER_OPT
+    v = OGL.triangles.indexmap[v0];
+#endif
+
+    u32 clip = OGL.triangles.vertices[v].clip;
+    if (clip == 0)
+        return FALSE;
+
+    for (unsigned int i = (v0+1); i <= vn; i++)
+    {
+        v = i;
+#ifdef __TRIBUFFER_OPT
+        v = OGL.triangles.indexmap[i];
+#endif
+        if (OGL.triangles.vertices[v].clip != clip) return FALSE;
+    }
+    return TRUE;
+}
+
+void gSPCullDisplayList( u32 v0, u32 vn )
+{
+    if (gSPCullVertices( v0, vn ))
+    {
+        if (RSP.PCi > 0)
+            RSP.PCi--;
+        else
+        {
+            RSP.halt = TRUE;
+        }
+    }
+}
+
+void gSPPopMatrixN( u32 param, u32 num )
+{
+    if (gSP.matrix.modelViewi > num - 1)
+    {
+        gSP.matrix.modelViewi -= num;
+
+        gSP.changed |= CHANGED_MATRIX;
+    }
+}
+
+void gSPPopMatrix( u32 param )
+{
+    if (gSP.matrix.modelViewi > 0)
+    {
+        gSP.matrix.modelViewi--;
+
+        gSP.changed |= CHANGED_MATRIX;
+    }
+}
+
+void gSPSegment( s32 seg, s32 base )
+{
+    if (seg > 0xF)
+    {
+        return;
+    }
+
+    if ((unsigned int)base > RDRAMSize - 1)
+    {
+        return;
+    }
+
+    gSP.segment[seg] = base;
+}
+
+void gSPClipRatio( u32 r )
+{
+}
+
+void gSPInsertMatrix( u32 where, u32 num )
+{
+    f32 fraction, integer;
+
+    if (gSP.changed & CHANGED_MATRIX)
+        gSPCombineMatrices();
+
+    if ((where & 0x3) || (where > 0x3C))
+    {
+        return;
+    }
+
+    if (where < 0x20)
+    {
+        fraction = modff( gSP.matrix.combined[0][where >> 1], &integer );
+        gSP.matrix.combined[0][where >> 1] = (s16)_SHIFTR( num, 16, 16 ) + abs( (int)fraction );
+
+        fraction = modff( gSP.matrix.combined[0][(where >> 1) + 1], &integer );
+        gSP.matrix.combined[0][(where >> 1) + 1] = (s16)_SHIFTR( num, 0, 16 ) + abs( (int)fraction );
+    }
+    else
+    {
+        f32 newValue;
+
+        fraction = modff( gSP.matrix.combined[0][(where - 0x20) >> 1], &integer );
+        newValue = integer + _FIXED2FLOAT( _SHIFTR( num, 16, 16 ), 16);
+
+        // Make sure the sign isn't lost
+        if ((integer == 0.0f) && (fraction != 0.0f))
+            newValue = newValue * (fraction / abs( (int)fraction ));
+
+        gSP.matrix.combined[0][(where - 0x20) >> 1] = newValue;
+
+        fraction = modff( gSP.matrix.combined[0][((where - 0x20) >> 1) + 1], &integer );
+        newValue = integer + _FIXED2FLOAT( _SHIFTR( num, 0, 16 ), 16 );
+
+        // Make sure the sign isn't lost
+        if ((integer == 0.0f) && (fraction != 0.0f))
+            newValue = newValue * (fraction / abs( (int)fraction ));
+
+        gSP.matrix.combined[0][((where - 0x20) >> 1) + 1] = newValue;
+    }
+}
+
+void gSPModifyVertex( u32 vtx, u32 where, u32 val )
+{
+    s32 v = vtx;
+
+#ifdef __TRIBUFFER_OPT
+    v = OGL.triangles.indexmap[v];
+#endif
+
+    switch (where)
+    {
+        case G_MWO_POINT_RGBA:
+            OGL.triangles.vertices[v].r = _SHIFTR( val, 24, 8 ) * 0.0039215689f;
+            OGL.triangles.vertices[v].g = _SHIFTR( val, 16, 8 ) * 0.0039215689f;
+            OGL.triangles.vertices[v].b = _SHIFTR( val, 8, 8 ) * 0.0039215689f;
+            OGL.triangles.vertices[v].a = _SHIFTR( val, 0, 8 ) * 0.0039215689f;
+            break;
+        case G_MWO_POINT_ST:
+            OGL.triangles.vertices[v].s = _FIXED2FLOAT( (s16)_SHIFTR( val, 16, 16 ), 5 );
+            OGL.triangles.vertices[v].t = _FIXED2FLOAT( (s16)_SHIFTR( val, 0, 16 ), 5 );
+            break;
+        case G_MWO_POINT_XYSCREEN:
+            break;
+        case G_MWO_POINT_ZSCREEN:
+            break;
+    }
+}
+
+void gSPNumLights( s32 n )
+{
+    gSP.numLights = (n <= 8) ? n : 0;
+}
+
+
+void gSPLightColor( u32 lightNum, u32 packedColor )
+{
+    lightNum--;
+
+    if (lightNum < 8)
+    {
+        gSP.lights[lightNum].r = _SHIFTR( packedColor, 24, 8 ) * 0.0039215689f;
+        gSP.lights[lightNum].g = _SHIFTR( packedColor, 16, 8 ) * 0.0039215689f;
+        gSP.lights[lightNum].b = _SHIFTR( packedColor, 8, 8 ) * 0.0039215689f;
+    }
+}
+
+void gSPFogFactor( s16 fm, s16 fo )
+{
+    gSP.fog.multiplier = fm;
+    gSP.fog.offset = fo;
+
+    gSP.changed |= CHANGED_FOGPOSITION;
+}
+
+void gSPPerspNormalize( u16 scale )
+{
+}
+
+void gSPTexture( f32 sc, f32 tc, s32 level, s32 tile, s32 on )
+{
+    gSP.texture.scales = sc;
+    gSP.texture.scalet = tc;
+
+    if (gSP.texture.scales == 0.0f) gSP.texture.scales = 1.0f;
+    if (gSP.texture.scalet == 0.0f) gSP.texture.scalet = 1.0f;
+
+    gSP.texture.level = level;
+    gSP.texture.on = on;
+
+    if (gSP.texture.tile != tile)
+    {
+        gSP.texture.tile = tile;
+        gSP.textureTile[0] = &gDP.tiles[tile];
+        gSP.textureTile[1] = &gDP.tiles[(tile < 7) ? (tile + 1) : tile];
+        gSP.changed |= CHANGED_TEXTURE;
+    }
+
+    gSP.changed |= CHANGED_TEXTURESCALE;
+}
+
+void gSPEndDisplayList()
+{
+    if (RSP.PCi > 0)
+        RSP.PCi--;
+    else
+    {
+        RSP.halt = TRUE;
+    }
+
+#ifdef __TRIBUFFER_OPT
+    RSP.nextCmd = _SHIFTR( *(u32*)&RDRAM[RSP.PC[RSP.PCi]], 24, 8 );
+    gSPFlushTriangles();
+#endif
+}
+
+void gSPGeometryMode( u32 clear, u32 set )
+{
+    gSP.geometryMode = (gSP.geometryMode & ~clear) | set;
+    gSP.changed |= CHANGED_GEOMETRYMODE;
+}
+
+void gSPSetGeometryMode( u32 mode )
+{
+    gSP.geometryMode |= mode;
+    gSP.changed |= CHANGED_GEOMETRYMODE;
+}
+
+void gSPClearGeometryMode( u32 mode )
+{
+    gSP.geometryMode &= ~mode;
+    gSP.changed |= CHANGED_GEOMETRYMODE;
+}
+
+void gSPLine3D( s32 v0, s32 v1, s32 flag )
+{
+    OGL_DrawLine(v0, v1, 1.5f );
+}
+
+void gSPLineW3D( s32 v0, s32 v1, s32 wd, s32 flag )
+{
+    OGL_DrawLine(v0, v1, 1.5f + wd * 0.5f );
+}
+
+void gSPBgRect1Cyc( u32 bg )
+{
+
+#if 1
+
+    u32 addr = RSP_SegmentToPhysical(bg) >> 1;
+
+    f32 imageX = (((u16*)RDRAM)[(addr+0)^1] >> 5);     // 0
+    f32 imageY = (((u16*)RDRAM)[(addr+4)^1] >> 5);     // 4
+    f32 imageW = (((u16*)RDRAM)[(addr+1)^1] >> 2);     // 1
+    f32 imageH = (((u16*)RDRAM)[(addr+5)^1] >> 2);     // 5
+
+    f32 frameX = ((s16*)RDRAM)[(addr+2)^1] / 4.0f;     // 2
+    f32 frameY = ((s16*)RDRAM)[(addr+6)^1] / 4.0f;     // 6
+    f32 frameW = ((u16*)RDRAM)[(addr+3)^1] >> 2;               // 3
+    f32 frameH = ((u16*)RDRAM)[(addr+7)^1] >> 2;               // 7
+
+
+    //wxUint16 imageFlip = ((u16*)gfx.RDRAM)[(addr+13)^1];     // 13;
+    //d.flipX  = (u8)imageFlip&0x01;
+
+    gSP.bgImage.address        = RSP_SegmentToPhysical(((u32*)RDRAM)[(addr+8)>>1]);    // 8,9
+    gSP.bgImage.width = imageW;
+    gSP.bgImage.height = imageH;
+    gSP.bgImage.format = ((u8*)RDRAM)[(((addr+11)<<1)+0)^3];
+    gSP.bgImage.size = ((u8*)RDRAM)[(((addr+11)<<1)+1)^3];
+    gSP.bgImage.palette = ((u16*)RDRAM)[(addr+12)^1];
+
+    f32 scaleW = ((s16*)RDRAM)[(addr+14)^1] / 1024.0f; // 14
+    f32 scaleH = ((s16*)RDRAM)[(addr+15)^1] / 1024.0f; // 15
+    gDP.textureMode = TEXTUREMODE_BGIMAGE;
+
+#else
+    u32 address = RSP_SegmentToPhysical( bg );
+    uObjScaleBg *objScaleBg = (uObjScaleBg*)&RDRAM[address];
+
+    gSP.bgImage.address = RSP_SegmentToPhysical( objScaleBg->imagePtr );
+    gSP.bgImage.width = objScaleBg->imageW >> 2;
+    gSP.bgImage.height = objScaleBg->imageH >> 2;
+    gSP.bgImage.format = objScaleBg->imageFmt;
+    gSP.bgImage.size = objScaleBg->imageSiz;
+    gSP.bgImage.palette = objScaleBg->imagePal;
+    gDP.textureMode = TEXTUREMODE_BGIMAGE;
+
+    f32 imageX = _FIXED2FLOAT( objScaleBg->imageX, 5 );
+    f32 imageY = _FIXED2FLOAT( objScaleBg->imageY, 5 );
+    f32 imageW = objScaleBg->imageW >> 2;
+    f32 imageH = objScaleBg->imageH >> 2;
+
+    f32 frameX = _FIXED2FLOAT( objScaleBg->frameX, 2 );
+    f32 frameY = _FIXED2FLOAT( objScaleBg->frameY, 2 );
+    f32 frameW = _FIXED2FLOAT( objScaleBg->frameW, 2 );
+    f32 frameH = _FIXED2FLOAT( objScaleBg->frameH, 2 );
+    f32 scaleW = _FIXED2FLOAT( objScaleBg->scaleW, 10 );
+    f32 scaleH = _FIXED2FLOAT( objScaleBg->scaleH, 10 );
+#endif
+
+    f32 frameX0 = frameX;
+    f32 frameY0 = frameY;
+    f32 frameS0 = imageX;
+    f32 frameT0 = imageY;
+
+    f32 frameX1 = frameX + min( (imageW - imageX) / scaleW, frameW );
+    f32 frameY1 = frameY + min( (imageH - imageY) / scaleH, frameH );
+    //f32 frameS1 = imageX + min( (imageW - imageX) * scaleW, frameW * scaleW );
+    //f32 frameT1 = imageY + min( (imageH - imageY) * scaleH, frameH * scaleH );
+
+    gDP.otherMode.cycleType = G_CYC_1CYCLE;
+    gDP.changed |= CHANGED_CYCLETYPE;
+    gSPTexture( 1.0f, 1.0f, 0, 0, TRUE );
+    gDPTextureRectangle( frameX0, frameY0, frameX1 - 1, frameY1 - 1, 0, frameS0 - 1, frameT0 - 1, scaleW, scaleH );
+
+    if ((frameX1 - frameX0) < frameW)
+    {
+        f32 frameX2 = frameW - (frameX1 - frameX0) + frameX1;
+        gDPTextureRectangle( frameX1, frameY0, frameX2 - 1, frameY1 - 1, 0, 0, frameT0, scaleW, scaleH );
+    }
+
+    if ((frameY1 - frameY0) < frameH)
+    {
+        f32 frameY2 = frameH - (frameY1 - frameY0) + frameY1;
+        gDPTextureRectangle( frameX0, frameY1, frameX1 - 1, frameY2 - 1, 0, frameS0, 0, scaleW, scaleH );
+    }
+
+    gDPTextureRectangle( 0, 0, 319, 239, 0, 0, 0, scaleW, scaleH );
+}
+
+void gSPBgRectCopy( u32 bg )
+{
+
+    return;
+    u32 address = RSP_SegmentToPhysical( bg );
+    uObjBg *objBg = (uObjBg*)&RDRAM[address];
+
+    gSP.bgImage.address = RSP_SegmentToPhysical( objBg->imagePtr );
+    gSP.bgImage.width = objBg->imageW >> 2;
+    gSP.bgImage.height = objBg->imageH >> 2;
+    gSP.bgImage.format = objBg->imageFmt;
+    gSP.bgImage.size = objBg->imageSiz;
+    gSP.bgImage.palette = objBg->imagePal;
+    gDP.textureMode = TEXTUREMODE_BGIMAGE;
+
+    u16 imageX = objBg->imageX >> 5;
+    u16 imageY = objBg->imageY >> 5;
+
+    s16 frameX = objBg->frameX / 4;
+    s16 frameY = objBg->frameY / 4;
+    u16 frameW = objBg->frameW >> 2;
+    u16 frameH = objBg->frameH >> 2;
+
+    gSPTexture( 1.0f, 1.0f, 0, 0, TRUE );
+
+    gDPTextureRectangle( frameX, frameY, frameX + frameW - 1, frameY + frameH - 1, 0, imageX, imageY, 4, 1 );
+}
+
+void gSPObjRectangle( u32 sp )
+{
+    u32 address = RSP_SegmentToPhysical( sp );
+    uObjSprite *objSprite = (uObjSprite*)&RDRAM[address];
+
+    f32 scaleW = _FIXED2FLOAT( objSprite->scaleW, 10 );
+    f32 scaleH = _FIXED2FLOAT( objSprite->scaleH, 10 );
+    f32 objX = _FIXED2FLOAT( objSprite->objX, 2 );
+    f32 objY = _FIXED2FLOAT( objSprite->objY, 2 );
+    u32 imageW = objSprite->imageW >> 2;
+    u32 imageH = objSprite->imageH >> 2;
+
+    gDPTextureRectangle( objX, objY, objX + imageW / scaleW - 1, objY + imageH / scaleH - 1, 0, 0.0f, 0.0f, scaleW * (gDP.otherMode.cycleType == G_CYC_COPY ? 4.0f : 1.0f), scaleH );
+}
+
+void gSPObjLoadTxtr( u32 tx )
+{
+    u32 address = RSP_SegmentToPhysical( tx );
+    uObjTxtr *objTxtr = (uObjTxtr*)&RDRAM[address];
+
+    if ((gSP.status[objTxtr->block.sid >> 2] & objTxtr->block.mask) != objTxtr->block.flag)
+    {
+        switch (objTxtr->block.type)
+        {
+            case G_OBJLT_TXTRBLOCK:
+                gDPSetTextureImage( 0, 1, 0, objTxtr->block.image );
+                gDPSetTile( 0, 1, 0, objTxtr->block.tmem, 7, 0, 0, 0, 0, 0, 0, 0 );
+                gDPLoadBlock( 7, 0, 0, ((objTxtr->block.tsize + 1) << 3) - 1, objTxtr->block.tline );
+                break;
+            case G_OBJLT_TXTRTILE:
+                gDPSetTextureImage( 0, 1, (objTxtr->tile.twidth + 1) << 1, objTxtr->tile.image );
+                gDPSetTile( 0, 1, (objTxtr->tile.twidth + 1) >> 2, objTxtr->tile.tmem, 7, 0, 0, 0, 0, 0, 0, 0 );
+                gDPLoadTile( 7, 0, 0, (((objTxtr->tile.twidth + 1) << 1) - 1) << 2, (((objTxtr->tile.theight + 1) >> 2) - 1) << 2 );
+                break;
+            case G_OBJLT_TLUT:
+                gDPSetTextureImage( 0, 2, 1, objTxtr->tlut.image );
+                gDPSetTile( 0, 2, 0, objTxtr->tlut.phead, 7, 0, 0, 0, 0, 0, 0, 0 );
+                gDPLoadTLUT( 7, 0, 0, objTxtr->tlut.pnum << 2, 0 );
+                break;
+        }
+        gSP.status[objTxtr->block.sid >> 2] = (gSP.status[objTxtr->block.sid >> 2] & ~objTxtr->block.mask) | (objTxtr->block.flag & objTxtr->block.mask);
+    }
+}
+
+void gSPObjSprite( u32 sp )
+{
+    u32 address = RSP_SegmentToPhysical( sp );
+    uObjSprite *objSprite = (uObjSprite*)&RDRAM[address];
+
+    f32 scaleW = _FIXED2FLOAT( objSprite->scaleW, 10 );
+    f32 scaleH = _FIXED2FLOAT( objSprite->scaleH, 10 );
+    f32 objX = _FIXED2FLOAT( objSprite->objX, 2 );
+    f32 objY = _FIXED2FLOAT( objSprite->objY, 2 );
+    u32 imageW = objSprite->imageW >> 5;
+    u32 imageH = objSprite->imageH >> 5;
+
+    f32 x0 = objX;
+    f32 y0 = objY;
+    f32 x1 = objX + imageW / scaleW - 1;
+    f32 y1 = objY + imageH / scaleH - 1;
+
+    s32 v0=0,v1=1,v2=2,v3=3;
+
+#ifdef __TRIBUFFER_OPT
+    v0 = OGL.triangles.indexmap[v0];
+    v1 = OGL.triangles.indexmap[v1];
+    v2 = OGL.triangles.indexmap[v2];
+    v3 = OGL.triangles.indexmap[v3];
+#endif
+
+    OGL.triangles.vertices[v0].x = gSP.objMatrix.A * x0 + gSP.objMatrix.B * y0 + gSP.objMatrix.X;
+    OGL.triangles.vertices[v0].y = gSP.objMatrix.C * x0 + gSP.objMatrix.D * y0 + gSP.objMatrix.Y;
+    OGL.triangles.vertices[v0].z = 0.0f;
+    OGL.triangles.vertices[v0].w = 1.0f;
+    OGL.triangles.vertices[v0].s = 0.0f;
+    OGL.triangles.vertices[v0].t = 0.0f;
+    OGL.triangles.vertices[v1].x = gSP.objMatrix.A * x1 + gSP.objMatrix.B * y0 + gSP.objMatrix.X;
+    OGL.triangles.vertices[v1].y = gSP.objMatrix.C * x1 + gSP.objMatrix.D * y0 + gSP.objMatrix.Y;
+    OGL.triangles.vertices[v1].z = 0.0f;
+    OGL.triangles.vertices[v1].w = 1.0f;
+    OGL.triangles.vertices[v1].s = imageW - 1;
+    OGL.triangles.vertices[v1].t = 0.0f;
+    OGL.triangles.vertices[v2].x = gSP.objMatrix.A * x1 + gSP.objMatrix.B * y1 + gSP.objMatrix.X;
+    OGL.triangles.vertices[v2].y = gSP.objMatrix.C * x1 + gSP.objMatrix.D * y1 + gSP.objMatrix.Y;
+    OGL.triangles.vertices[v2].z = 0.0f;
+    OGL.triangles.vertices[v2].w = 1.0f;
+    OGL.triangles.vertices[v2].s = imageW - 1;
+    OGL.triangles.vertices[v2].t = imageH - 1;
+    OGL.triangles.vertices[v3].x = gSP.objMatrix.A * x0 + gSP.objMatrix.B * y1 + gSP.objMatrix.X;
+    OGL.triangles.vertices[v3].y = gSP.objMatrix.C * x0 + gSP.objMatrix.D * y1 + gSP.objMatrix.Y;
+    OGL.triangles.vertices[v3].z = 0.0f;
+    OGL.triangles.vertices[v3].w = 1.0f;
+    OGL.triangles.vertices[v3].s = 0;
+    OGL.triangles.vertices[v3].t = imageH - 1;
+
+    gDPSetTile( objSprite->imageFmt, objSprite->imageSiz, objSprite->imageStride, objSprite->imageAdrs, 0, objSprite->imagePal, G_TX_CLAMP, G_TX_CLAMP, 0, 0, 0, 0 );
+    gDPSetTileSize( 0, 0, 0, (imageW - 1) << 2, (imageH - 1) << 2 );
+    gSPTexture( 1.0f, 1.0f, 0, 0, TRUE );
+
+    //glOrtho( 0, VI.width, VI.height, 0, 0.0f, 32767.0f );
+    OGL.triangles.vertices[v0].x = 2.0f * VI.rwidth * OGL.triangles.vertices[v0].x - 1.0f;
+    OGL.triangles.vertices[v0].y = -2.0f * VI.rheight * OGL.triangles.vertices[v0].y + 1.0f;
+    OGL.triangles.vertices[v0].z = -1.0f;
+    OGL.triangles.vertices[v0].w = 1.0f;
+    OGL.triangles.vertices[v1].x = 2.0f * VI.rwidth * OGL.triangles.vertices[v0].x - 1.0f;
+    OGL.triangles.vertices[v1].y = -2.0f * VI.rheight * OGL.triangles.vertices[v0].y + 1.0f;
+    OGL.triangles.vertices[v1].z = -1.0f;
+    OGL.triangles.vertices[v1].w = 1.0f;
+    OGL.triangles.vertices[v2].x = 2.0f * VI.rwidth * OGL.triangles.vertices[v0].x - 1.0f;
+    OGL.triangles.vertices[v2].y = -2.0f * VI.rheight * OGL.triangles.vertices[v0].y + 1.0f;
+    OGL.triangles.vertices[v2].z = -1.0f;
+    OGL.triangles.vertices[v2].w = 1.0f;
+    OGL.triangles.vertices[v3].x = 2.0f * VI.rwidth * OGL.triangles.vertices[v0].x - 1.0f;
+    OGL.triangles.vertices[v3].y = -2.0f * VI.rheight * OGL.triangles.vertices[v0].y + 1.0f;
+    OGL.triangles.vertices[v3].z = -1.0f;
+    OGL.triangles.vertices[v3].w = 1.0f;
+
+    OGL_AddTriangle(v0, v1, v2);
+    OGL_AddTriangle(v0, v2, v3);
+    OGL_DrawTriangles();
+
+    if (depthBuffer.current) depthBuffer.current->cleared = FALSE;
+    gDP.colorImage.changed = TRUE;
+    gDP.colorImage.height = (unsigned int)(max( gDP.colorImage.height, gDP.scissor.lry ));
+}
+
+void gSPObjLoadTxSprite( u32 txsp )
+{
+    gSPObjLoadTxtr( txsp );
+    gSPObjSprite( txsp + sizeof( uObjTxtr ) );
+}
+
+void gSPObjLoadTxRectR( u32 txsp )
+{
+    gSPObjLoadTxtr( txsp );
+//  gSPObjRectangleR( txsp + sizeof( uObjTxtr ) );
+}
+
+void gSPObjMatrix( u32 mtx )
+{
+    u32 address = RSP_SegmentToPhysical( mtx );
+    uObjMtx *objMtx = (uObjMtx*)&RDRAM[address];
+
+    gSP.objMatrix.A = _FIXED2FLOAT( objMtx->A, 16 );
+    gSP.objMatrix.B = _FIXED2FLOAT( objMtx->B, 16 );
+    gSP.objMatrix.C = _FIXED2FLOAT( objMtx->C, 16 );
+    gSP.objMatrix.D = _FIXED2FLOAT( objMtx->D, 16 );
+    gSP.objMatrix.X = _FIXED2FLOAT( objMtx->X, 2 );
+    gSP.objMatrix.Y = _FIXED2FLOAT( objMtx->Y, 2 );
+    gSP.objMatrix.baseScaleX = _FIXED2FLOAT( objMtx->BaseScaleX, 10 );
+    gSP.objMatrix.baseScaleY = _FIXED2FLOAT( objMtx->BaseScaleY, 10 );
+}
+
+void gSPObjSubMatrix( u32 mtx )
+{
+}
+
+
+#ifdef __VEC4_OPT
+void (*gSPTransformVertex4)(u32 v, float mtx[4][4]) =
+        gSPTransformVertex4_default;
+void (*gSPTransformNormal4)(u32 v, float mtx[4][4]) =
+        gSPTransformNormal4_default;
+void (*gSPLightVertex4)(u32 v) = gSPLightVertex4_default;
+void (*gSPBillboardVertex4)(u32 v) = gSPBillboardVertex4_default;
+#endif
+void (*gSPTransformVertex)(float vtx[4], float mtx[4][4]) =
+        gSPTransformVertex_default;
+void (*gSPLightVertex)(u32 v) = gSPLightVertex_default;
+void (*gSPBillboardVertex)(u32 v, u32 i) = gSPBillboardVertex_default;
+