/*
 * z64
 *
 * Copyright (C) 2007  ziggy
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
**/

#include <string.h>

#include "m64p_plugin.h"
#include "rdp.h"
#include "rgl.h"

const char *rdpImageFormats[] =
{ "RGBA", "YUV", "CI", "IA", "I", "???", "???", "???" };

rdpState_t rdpState;
uint32_t   rdpChanged;
//rdpColor_t rdpTlut[1024];
uint8_t    rdpTmem[4*0x1000];
int        rdpFbFormat;
int        rdpFbSize;
int        rdpFbWidth;
uint32_t   rdpFbAddress;
uint32_t   rdpZbAddress;
int        rdpTiFormat;
int        rdpTiSize;
int        rdpTiWidth;
uint32_t   rdpTiAddress;
rdpTile_t  rdpTiles[8];
int        rdpTileSet;

struct area_t {
    int start, stop;
    uint32_t from;
    int fromLine, fromFormat, fromSize;
};

#define MAX_TMEM_AREAS 16
static area_t tmemAreas[MAX_TMEM_AREAS];
static int nbTmemAreas;

#ifdef RDP_DEBUG
int rdp_dump;
#endif

#define MAXCMD 0x100000
static uint32_t rdp_cmd_data[MAXCMD+44];
static volatile int rdp_cmd_ptr = 0;
static volatile int rdp_cmd_cur = 0;
static int rdp_cmd_left = 0;

#ifdef RDP_DEBUG
uint32_t rdpTraceBuf[0x100000];
int rdpTracePos;
#endif


static void MarkTmemArea(int start, int stop, uint32_t from, uint32_t fromLine,
                         int fromFormat, int fromSize)
{
    int i;

    // remove areas that intersect
    for (i=0; i<nbTmemAreas; i++)
        while (i<nbTmemAreas &&
            tmemAreas[i].start<stop && tmemAreas[i].stop>start) {
                memmove(tmemAreas+i, tmemAreas+i+1, nbTmemAreas-i-1);
                nbTmemAreas--;
        }

        DUMP("marking tmem %x --> %x rdram %x\n", start, stop, from);

        // add new area
        //rglAssert(nbTmemAreas < MAX_TMEM_AREAS);
        if (nbTmemAreas == MAX_TMEM_AREAS) {
            LOG("tmem areas buffer full, clearing\n");
            nbTmemAreas = 0;
        }
        tmemAreas[nbTmemAreas].start = start;
        tmemAreas[nbTmemAreas].stop = stop;
        tmemAreas[nbTmemAreas].from = from;
        tmemAreas[nbTmemAreas].fromLine = fromLine;
        tmemAreas[nbTmemAreas].fromFormat = fromFormat;
        tmemAreas[nbTmemAreas].fromSize = fromSize;
        nbTmemAreas++;
}

uint32_t rdpGetTmemOrigin(int tmem, int * line, int * stop, int * format, int * size)
{
    int i;
    for (i=0; i<nbTmemAreas; i++) {
        if (tmemAreas[i].start == tmem) {
            *line = tmemAreas[i].fromLine;
            *stop = tmemAreas[i].stop;
            *format = tmemAreas[i].fromFormat;
            *size = tmemAreas[i].fromSize;
            return tmemAreas[i].from;
        }
    }

    return ~0;
}

inline uint32_t READ_RDP_DATA(uint32_t address)
{
    if (dp_status & 0x1)		// XBUS_DMEM_DMA enabled
    {
        return rsp_dmem[(address & 0xfff) / 4];
    }
    else
    {
        return rdram[(address / 4)];
    }
}

static const int rdp_command_length[64] =
{
    8,			// 0x00, No Op
    8,			// 0x01, ???
    8,			// 0x02, ???
    8,			// 0x03, ???
    8,			// 0x04, ???
    8,			// 0x05, ???
    8,			// 0x06, ???
    8,			// 0x07, ???
    32,			// 0x08, Non-Shaded Triangle
    32+16,		// 0x09, Non-Shaded, Z-Buffered Triangle
    32+64,		// 0x0a, Textured Triangle
    32+64+16,	// 0x0b, Textured, Z-Buffered Triangle
    32+64,		// 0x0c, Shaded Triangle
    32+64+16,	// 0x0d, Shaded, Z-Buffered Triangle
    32+64+64,	// 0x0e, Shaded+Textured Triangle
    32+64+64+16,// 0x0f, Shaded+Textured, Z-Buffered Triangle
    8,			// 0x10, ???
    8,			// 0x11, ???
    8,			// 0x12, ???
    8,			// 0x13, ???
    8,			// 0x14, ???
    8,			// 0x15, ???
    8,			// 0x16, ???
    8,			// 0x17, ???
    8,			// 0x18, ???
    8,			// 0x19, ???
    8,			// 0x1a, ???
    8,			// 0x1b, ???
    8,			// 0x1c, ???
    8,			// 0x1d, ???
    8,			// 0x1e, ???
    8,			// 0x1f, ???
    8,			// 0x20, ???
    8,			// 0x21, ???
    8,			// 0x22, ???
    8,			// 0x23, ???
    16,			// 0x24, Texture_Rectangle
    16,			// 0x25, Texture_Rectangle_Flip
    8,			// 0x26, Sync_Load
    8,			// 0x27, Sync_Pipe
    8,			// 0x28, Sync_Tile
    8,			// 0x29, Sync_Full
    8,			// 0x2a, Set_Key_GB
    8,			// 0x2b, Set_Key_R
    8,			// 0x2c, Set_Convert
    8,			// 0x2d, Set_Scissor
    8,			// 0x2e, Set_Prim_Depth
    8,			// 0x2f, Set_Other_Modes
    8,			// 0x30, Load_TLUT
    8,			// 0x31, ???
    8,			// 0x32, Set_Tile_Size
    8,			// 0x33, Load_Block
    8,			// 0x34, Load_Tile
    8,			// 0x35, Set_Tile
    8,			// 0x36, Fill_Rectangle
    8,			// 0x37, Set_Fill_Color
    8,			// 0x38, Set_Fog_Color
    8,			// 0x39, Set_Blend_Color
    8,			// 0x3a, Set_Prim_Color
    8,			// 0x3b, Set_Env_Color
    8,			// 0x3c, Set_Combine
    8,			// 0x3d, Set_Texture_Image
    8,			// 0x3e, Set_Mask_Image
    8			// 0x3f, Set_Color_Image
};

/*****************************************************************************/

////////////////////////
// RDP COMMANDS
////////////////////////

static void rdp_invalid(uint32_t w1, uint32_t w2)
{
    LOGERROR("RDP: invalid command  %d, %08X %08X\n", (w1 >> 24) & 0x3f, w1, w2);
}

static void rdp_noop(uint32_t w1, uint32_t w2)
{

}

static void triangle(uint32_t w1, uint32_t w2, int shade, int texture, int zbuffer)
{
    rglTriangle(w1, w2, shade, texture, zbuffer, rdp_cmd_data + rdp_cmd_cur);
}

static void rdp_tri_noshade(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 0, 0, 0);
}

static void rdp_tri_noshade_z(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 0, 0, 1);
}

static void rdp_tri_tex(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 0, 1, 0);
}

static void rdp_tri_tex_z(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 0, 1, 1);
}

static void rdp_tri_shade(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 1, 0, 0);
}

static void rdp_tri_shade_z(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 1, 0, 1);
}

static void rdp_tri_texshade(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 1, 1, 0);
}

static void rdp_tri_texshade_z(uint32_t w1, uint32_t w2)
{
    triangle(w1, w2, 1, 1, 1);
}

static void rdp_tex_rect(uint32_t w1, uint32_t w2)
{
    uint32_t w3, w4;
    rdpTexRect_t rect;

    w3 = rdp_cmd_data[rdp_cmd_cur+2];
    w4 = rdp_cmd_data[rdp_cmd_cur+3];

    rect.tilenum	= (w2 >> 24) & 0x7;
    rect.xl			= (w1 >> 12) & 0xfff;
    rect.yl			= (w1 >>  0) & 0xfff;
    rect.xh			= (w2 >> 12) & 0xfff;
    rect.yh			= (w2 >>  0) & 0xfff;
    rect.s			= (w3 >> 16) & 0xffff;
    rect.t			= (w3 >>  0) & 0xffff;
    rect.dsdx		= (w4 >> 16) & 0xffff;
    rect.dtdy		= (w4 >>  0) & 0xffff;

    rglTextureRectangle(&rect, 0);
}

static void rdp_tex_rect_flip(uint32_t w1, uint32_t w2)
{
    uint32_t w3, w4;
    rdpTexRect_t rect;

    w3 = rdp_cmd_data[rdp_cmd_cur+2];
    w4 = rdp_cmd_data[rdp_cmd_cur+3];

    rect.tilenum	= (w2 >> 24) & 0x7;
    rect.xl			= (w1 >> 12) & 0xfff;
    rect.yl			= (w1 >>  0) & 0xfff;
    rect.xh			= (w2 >> 12) & 0xfff;
    rect.yh			= (w2 >>  0) & 0xfff;
    rect.t			= (w3 >> 16) & 0xffff;
    rect.s			= (w3 >>  0) & 0xffff;
    rect.dtdy		= (w4 >> 16) & 0xffff;
    rect.dsdx		= (w4 >>  0) & 0xffff;

    rglTextureRectangle(&rect, 1);
}

static void rdp_sync_load(uint32_t w1, uint32_t w2)
{
    // Nothing to do?
}

static void rdp_sync_pipe(uint32_t w1, uint32_t w2)
{
    // Nothing to do?
}

static void rdp_sync_tile(uint32_t w1, uint32_t w2)
{
    // Nothing to do?
}

void rdpSignalFullSync();
void rdpWaitFullSync();
#ifdef RDP_DEBUG
int nbFullSync;
#endif
static void rdp_sync_full(uint32_t w1, uint32_t w2)
{
    //printf("full sync\n");
    rglFullSync();
    rglUpdate();

    if (rglSettings.async)
        rdpSignalFullSync();
    else {
        *gfx.MI_INTR_REG |= 0x20;
        gfx.CheckInterrupts();
    }
#ifdef RDP_DEBUG
    nbFullSync++;
#endif
}

static void rdp_set_key_gb(uint32_t w1, uint32_t w2)
{
    //osd_die("RDP: unhandled command set_key_gb, %08X %08X\n", w1, w2);
}

static void rdp_set_key_r(uint32_t w1, uint32_t w2)
{
    //osd_die("RDP: unhandled command set_key_r, %08X %08X\n", w1, w2);
}

static void rdp_set_convert(uint32_t w1, uint32_t w2)
{
    rdpState.k5 = w2&0xff;
    //osd_die("RDP: unhandled command set_convert, %08X %08X\n", w1, w2);
}

static void rdp_set_scissor(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_CLIP;
    rdpState.clipMode = (w2 >> 24) & 3;
    rdpState.clip.xh = (w1 >> 12) & 0xfff;
    rdpState.clip.yh = (w1 >>  0) & 0xfff;
    rdpState.clip.xl = (w2 >> 12) & 0xfff;
    rdpState.clip.yl = (w2 >>  0) & 0xfff;
    // TODO: handle f & o?
}

static void rdp_set_prim_depth(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_MISC;
    rdpState.primitiveZ = (uint16_t)(w2 >> 16);
    rdpState.primitiveDeltaZ = (uint16_t)(w1);
}

static void rdp_set_other_modes(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_OTHER_MODES;
    rdpState.otherModes.w1 = w1;
    rdpState.otherModes.w2 = w2;
}

static void rdp_load_tlut(uint32_t w1, uint32_t w2)
{
    int tilenum = (w2 >> 24) & 0x7;

    rdpChanged |= RDP_BITS_TILE_SETTINGS;

#define tile rdpTiles[tilenum]
    //rdpTile_t tile;
    tile.sl = (w1 >> 12) & 0xfff;
    tile.tl = (w1 >>  0) & 0xfff;
    tile.sh = (w2 >> 12) & 0xfff;
    tile.th = (w2 >>  0) & 0xfff;

    int i;

    rdpChanged |= RDP_BITS_TLUT;

    int count = ((tile.sh - tile.sl + 4) >>2) * ((tile.th - tile.tl + 4) >>2);

    switch (rdpTiSize)
    {
    case RDP_PIXEL_SIZE_16BIT:
        {
            uint16_t *src = (uint16_t *)&rdram[(rdpTiAddress + (tile.tl >>2) * rdpTiWidth * 2 + ((tile.sl >>2) << rdpTiSize >> 1))/4];
            uint16_t *dst = (uint16_t *)(rdpTmem + rdpTiles[tilenum].tmem);

            //       printf("loading TLUT from %x --> %x\n",
            //              tile.th * rdpTiWidth / 2 + (tile.sh << rdpTiSize >> 1)/4

            for (i=0; i < count; i++)
            {
                dst[i*4] = src[i^1];
            }
            break;
        }
    default:	LOGERROR("RDP: load_tlut: size = %d\n", rdpTiSize);
    }
#undef tile
}

static void rdp_set_tile_size(uint32_t w1, uint32_t w2)
{
    int tilenum = (w2 >> 24) & 0x7;

    rdpChanged |= RDP_BITS_TILE_SETTINGS;

#define tile rdpTiles[tilenum]
    tile.sl = (w1 >> 12) & 0xfff;
    tile.tl = (w1 >>  0) & 0xfff;
    tile.sh = (w2 >> 12) & 0xfff;
    tile.th = (w2 >>  0) & 0xfff;
#undef tile
}

static void rdp_load_block(uint32_t w1, uint32_t w2)
{
    int i, width;
    uint16_t sl, sh, tl, dxt;
    int tilenum = (w2 >> 24) & 0x7;
    uint32_t *src, *tc;
    int tb;

    rdpChanged |= RDP_BITS_TMEM;

    sl	= ((w1 >> 12) & 0xfff);
    tl	= ((w1 >>  0) & 0xfff) << 11;
    sh	= ((w2 >> 12) & 0xfff);
    dxt	= ((w2 >>  0) & 0xfff);

    width = (sh - sl + 1) << rdpTiSize >> 1;

    src = (uint32_t*)&rdram[0];
    tc = (uint32_t*)rdpTmem;
    tb = rdpTiles[tilenum].tmem/4;

    //printf("Load block to %x width %x\n", rdpTiles[tilenum].tmem, width);

    MarkTmemArea(rdpTiles[tilenum].tmem, rdpTiles[tilenum].tmem + width,
        tl * rdpTiWidth*4 + rdpTiAddress + sl*4, 0, ~0, ~0);

    if (tb+width/4 > 0x1000/4) {
        LOG("load_block : fixup too large width\n");
        width = 0x1000-tb*4;
    }

    if (dxt != 0)
    {
        int j=0;

        //rglAssert(tb+width/4 <= 0x1000/4);

        int swap = rdpTiles[tilenum].size == 3? 2 : 1;

        for (i=0; i < width / 4; i+=2)
        {
            int t = j >> 11;

            tc[(((tb+i) + 0)  ^ ((t & 1) ? swap : 0))&0x3ff] =
                src[rdpTiAddress / 4 + ((tl * rdpTiWidth) / 4) + sl + i + 0];
            tc[(((tb+i) + 1) ^ ((t & 1) ? swap : 0))&0x3ff] =
                src[rdpTiAddress / 4 + ((tl * rdpTiWidth) / 4) + sl + i + 1];

            j += dxt;
        }
    }
    else
    {
        //rglAssert(tb+width/4 <= 0x1000/4);
        for (i=0; i < width / 4; i++)
        {
            tc[(tb+i)&0x3ff] = src[((tl * rdpTiWidth) / 4) + rdpTiAddress / 4 + sl + i];
        }
    }
}

static void rdp_load_tile(uint32_t w1, uint32_t w2)
{
    int i, j;
    uint16_t sl, sh, tl, th;
    int width, height;
    int tilenum = (w2 >> 24) & 0x7;
    int line;

    rdpChanged |= RDP_BITS_TMEM;

    sl	= ((w1 >> 12) & 0xfff) / 4;
    tl	= ((w1 >>  0) & 0xfff) / 4;
    sh	= ((w2 >> 12) & 0xfff) / 4;
    th	= ((w2 >>  0) & 0xfff) / 4;

    width = (sh - sl) + 1;
    height = (th - tl) + 1;

    //   printf("Load tile to %x line %x height %d\n",
    //          rdpTiles[tilenum].tmem,
    //          rdpTiles[tilenum].line,
    //          height);

    rdpTiles[tilenum].size = rdpTiSize; // CHECK THIS 
    line = rdpTiles[tilenum].line;
    switch (rdpTiles[tilenum].size /*rdpTiSize*/)
    {
    case RDP_PIXEL_SIZE_8BIT:
        {
            uint8_t *src = (uint8_t*)&rdram[0];
            uint8_t *tc = (uint8_t*)rdpTmem;
            int tb = rdpTiles[tilenum].tmem;

            MarkTmemArea(tb, tb + height*line, rdpTiAddress + tl * rdpTiWidth + sl,
                rdpTiWidth, rdpTiFormat, rdpTiSize);

            if (tb + (line * (height-1) + width) > 4096)
            {
                LOGERROR("rdp_load_tile 8-bit: tmem %04X, width %d, height %d = %d\n", rdpTiles[tilenum].tmem, width, height, width*height);
                height = (4096-tb)/line;
            }

            for (j=0; j < height; j++)
            {
                int tline = tb + (rdpTiles[tilenum].line * j);
                int s = ((j + tl) * rdpTiWidth) + sl;

                for (i=0; i < width; i++)
                {
                    tc[(((tline+i) ^ BYTE_ADDR_XOR) ^ ((j & 1) ? 4 : 0))&0xfff] = src[(rdpTiAddress + s++) ^ BYTE_ADDR_XOR];
                }
            }
            break;
        }
    case RDP_PIXEL_SIZE_16BIT:
        {
            uint16_t *src = (uint16_t*)&rdram[0];
            uint16_t *tc = (uint16_t*)rdpTmem;
            int tb = (rdpTiles[tilenum].tmem / 2);

            if (tb + (line/2 * (height-1) + width) > 2048)
            {
                LOGERROR("rdp_load_tile 16-bit: tmem %04X, width %d, height %d = %d\n", rdpTiles[tilenum].tmem, width, height, width*height);
                height = (2048 - tb) / (line/2);
            }

            MarkTmemArea(tb*2, tb*2 + height*line,
                rdpTiAddress + (tl * rdpTiWidth + sl)*2,
                rdpTiWidth*2, rdpTiFormat, rdpTiSize);

            for (j=0; j < height; j++)
            {
                int tline = tb + ((rdpTiles[tilenum].line / 2) * j);
                int s = ((j + tl) * rdpTiWidth) + sl;

                for (i=0; i < width; i++)
                {
                    tc[(((tline+i) ^ WORD_ADDR_XOR) ^ ((j & 1) ? 2 : 0))&0x7ff] = src[(rdpTiAddress / 2 + s++) ^ WORD_ADDR_XOR];
                }
            }
            break;
        }
    case RDP_PIXEL_SIZE_32BIT:
        {
            uint32_t *src = (uint32_t*)&rdram[0];
            uint32_t *tc = (uint32_t*)rdpTmem;
            int tb = (rdpTiles[tilenum].tmem / 4);

            MarkTmemArea(tb*4, tb*4 + height*line*2,
                rdpTiAddress + (tl * rdpTiWidth + sl)*4,
                rdpTiWidth*4, rdpTiFormat, rdpTiSize);

            if (tb + (line/2 * (height-1) + width) > 1024)
            {
                rdp_log(M64MSG_ERROR, "rdp_load_tile 32-bit: tmem %04X, width %d, height %d = %d\n", rdpTiles[tilenum].tmem, width, height, width*height);
            }

            for (j=0; j < height; j++)
            {
                int tline = tb + ((rdpTiles[tilenum].line / 2) * j);
                int s = ((j + tl) * rdpTiWidth) + sl;

                for (i=0; i < width; i++)
                {
                    tc[((tline+i) ^ ((j & 1) ? 2 : 0))&0x3ff] = src[(rdpTiAddress / 4 + s++)];
                }
            }
            break;
        }

    default:	
        rdp_log(M64MSG_ERROR, "RDP: load_tile: size = %d\n", rdpTiSize);
    }
}

static void rdp_set_tile(uint32_t w1, uint32_t w2)
{
    int tilenum = (w2 >> 24) & 0x7;
    //int i;

    rdpChanged |= RDP_BITS_TILE_SETTINGS;
    rdpTileSet |= 1<<tilenum;

#define tile rdpTiles[tilenum]
    tile.format	= (w1 >> 21) & 0x7;
    tile.size		= (w1 >> 19) & 0x3;
    tile.line		= ((w1 >>  9) & 0x1ff) * 8;
    tile.tmem		= ((w1 >>  0) & 0x1ff) * 8;
    tile.palette= (w2 >> 20) & 0xf;
    tile.ct			= (w2 >> 19) & 0x1;
    tile.mt			= (w2 >> 18) & 0x1;
    tile.mask_t	= (w2 >> 14) & 0xf;
    tile.shift_t= (w2 >> 10) & 0xf;
    if (tile.shift_t >= 12) tile.shift_t -= 16;
    tile.cs			= (w2 >>  9) & 0x1;
    tile.ms			= (w2 >>  8) & 0x1;
    tile.mask_s	= (w2 >>  4) & 0xf;
    tile.shift_s= (w2 >>  0) & 0xf;
    if (tile.shift_s >= 12) tile.shift_s -= 16;
#undef tile
}

static void rdp_fill_rect(uint32_t w1, uint32_t w2)
{
    rdpRect_t rect;
    rect.xl = (w1 >> 12) & 0xfff;
    rect.yl = (w1 >>  0) & 0xfff;
    rect.xh = (w2 >> 12) & 0xfff;
    rect.yh = (w2 >>  0) & 0xfff;

    rglFillRectangle(&rect);
}

static void rdp_set_fill_color(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_FILL_COLOR;
    rdpState.fillColor = w2;
}

static void rdp_set_fog_color(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_FOG_COLOR;
    rdpState.fogColor = w2;
}

static void rdp_set_blend_color(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_BLEND_COLOR;
    rdpState.blendColor = w2;
}

static void rdp_set_prim_color(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_PRIM_COLOR;
    // TODO: prim min level, prim_level
    rdpState.primColor = w2;
}

static void rdp_set_env_color(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_ENV_COLOR;
    rdpState.envColor = w2;
}

static void rdp_set_combine(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_COMBINE_MODES;

    rdpState.combineModes.w1 = w1;
    rdpState.combineModes.w2 = w2;
}

static void rdp_set_texture_image(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_TI_SETTINGS;

    rdpTiFormat	= (w1 >> 21) & 0x7;
    rdpTiSize		= (w1 >> 19) & 0x3;
    rdpTiWidth	= (w1 & 0x3ff) + 1;
    rdpTiAddress	= w2 & 0x01ffffff;
}

static void rdp_set_mask_image(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_ZB_SETTINGS;
    rdpZbAddress	= w2 & 0x01ffffff;
}

static void rdp_set_color_image(uint32_t w1, uint32_t w2)
{
    rdpChanged |= RDP_BITS_FB_SETTINGS;
    rdpFbFormat 	= (w1 >> 21) & 0x7;
    rdpFbSize		= (w1 >> 19) & 0x3;
    rdpFbWidth	= (w1 & 0x3ff) + 1;
    rdpFbAddress	= w2 & 0x01ffffff;
}

/*****************************************************************************/

static void (* rdp_command_table[64])(uint32_t w1, uint32_t w2) =
{
    /* 0x00 */
    rdp_noop,			rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_tri_noshade,	rdp_tri_noshade_z,		rdp_tri_tex,			rdp_tri_tex_z,
    rdp_tri_shade,		rdp_tri_shade_z,		rdp_tri_texshade,		rdp_tri_texshade_z,
    /* 0x10 */
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    /* 0x20 */
    rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
    rdp_tex_rect,		rdp_tex_rect_flip,		rdp_sync_load,			rdp_sync_pipe,
    rdp_sync_tile,		rdp_sync_full,			rdp_set_key_gb,			rdp_set_key_r,
    rdp_set_convert,	rdp_set_scissor,		rdp_set_prim_depth,		rdp_set_other_modes,
    /* 0x30 */
    rdp_load_tlut,		rdp_invalid,			rdp_set_tile_size,		rdp_load_block,
    rdp_load_tile,		rdp_set_tile,			rdp_fill_rect,			rdp_set_fill_color,
    rdp_set_fog_color,	rdp_set_blend_color,	rdp_set_prim_color,		rdp_set_env_color,
    rdp_set_combine,	rdp_set_texture_image,	rdp_set_mask_image,		rdp_set_color_image
};

void rdp_process_list(void)
{
    //int i;
    uint32_t cmd;//, length, cmd_length;

    rglUpdateStatus();
    if (!rglSettings.threaded)
        rdp_store_list();

    if (rglStatus == RGL_STATUS_CLOSED)
        return;

    // this causes problem with depth writeback in zelda mm
    // but is necessary for in fisherman
    rglUpdate();

    while (rdp_cmd_cur != rdp_cmd_ptr)
    {
        cmd = (rdp_cmd_data[rdp_cmd_cur] >> 24) & 0x3f;
        //  if (((rdp_cmd_data[rdp_cmd_cur] >> 24) & 0xc0) != 0xc0)
        //  {
        //      LOGERROR("rdp_process_list: invalid rdp command %08X at %08X\n", rdp_cmd_data[rdp_cmd_cur], dp_start+(rdp_cmd_cur * 4));
        //  }

        if ((((rdp_cmd_ptr-rdp_cmd_cur)&(MAXCMD-1)) * 4) < rdp_command_length[cmd])
        {
            // 			LOGERROR("rdp_process_list: not enough rdp command data: cur = %d, ptr = %d, expected = %d\n", rdp_cmd_cur, rdp_cmd_ptr, rdp_command_length[cmd]);
            // 			return;
            break;
        }

#ifdef RDP_DEBUG
        if (rdp_dump)
        {
            char string[4000];
            int rdp_dasm(uint32_t * rdp_cmd_data, int rdp_cmd_cur, int length, char *buffer);
            rdp_dasm(rdp_cmd_data, rdp_cmd_cur, rdp_command_length[cmd], string);

            fprintf(stderr, "%08X: %08X %08X   %s\n", dp_start+(rdp_cmd_cur * 4), rdp_cmd_data[rdp_cmd_cur+0], rdp_cmd_data[rdp_cmd_cur+1], string);
        }
#endif

#ifdef RDP_DEBUG
        memcpy(rdpTraceBuf+rdpTracePos, rdp_cmd_data+rdp_cmd_cur, rdp_command_length[cmd]);
#endif

        if (rdp_cmd_cur + rdp_command_length[cmd]/4 > MAXCMD)
            memcpy(rdp_cmd_data + MAXCMD, rdp_cmd_data, rdp_command_length[cmd] - (MAXCMD - rdp_cmd_cur)*4);

        // execute the command
        rdp_command_table[cmd](rdp_cmd_data[rdp_cmd_cur+0], rdp_cmd_data[rdp_cmd_cur+1]);

#ifdef RDP_DEBUG
        rdpTracePos += rdp_command_length[cmd] / 4;
        rglAssert(rdpTracePos < sizeof(rdpTraceBuf)/sizeof(rdpTraceBuf[0]));
#endif

        rdp_cmd_cur = (rdp_cmd_cur + rdp_command_length[cmd] / 4) & (MAXCMD-1);
    }

    // 	dp_current = dp_end;
    // 	dp_start = dp_end;
    dp_start = dp_current;

    dp_status &= ~0x0002;
}

int rdp_store_list(void)
{
    uint32_t i;
    uint32_t data, cmd, length;
    int sync = 0;

    //   while (dp_current < dp_end) {

    //   }
    //   dp_status &= ~0x0002;

    length = dp_end - dp_current;

    //   LOG("rdp start %x cur %x end %x length %d dp_status %x\n",
    //       dp_start, dp_current, dp_end,
    //       length, dp_status);

    if (dp_end <= dp_current) {
        return 0;
    }

    // load command data
    for (i=0; i < length; i += 4)
    {
        data = READ_RDP_DATA(dp_current + i);
        if (rglSettings.async) {
            if (rdp_cmd_left) {
                rdp_cmd_left--;
            } else {
                cmd = (data >> 24) & 0x3f;
                rdp_cmd_left = rdp_command_length[cmd]/4-1;
                if (cmd == 0x29) // full_sync
                    sync = 1;
            }
        }
        rdp_cmd_data[rdp_cmd_ptr] = data;
        rdp_cmd_ptr = (rdp_cmd_ptr + 1) & (MAXCMD-1);
    }

    dp_current += length;

    return sync;
}


int rdp_init()
{
    rdp_cmd_cur = rdp_cmd_ptr = 0;
    rdp_cmd_left = 0;
#ifdef RDP_DEBUG
    rdpTracePos = 0;
#endif
    nbTmemAreas = 0;
    return rglInit();
}

