/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          3DFX Voodoo emulation.
 *
 * Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
 *
 *          Copyright 2008-2020 Sarah Walker.
 */
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <wchar.h>
#include <math.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
#include <86box/machine.h>
#include <86box/device.h>
#include <86box/mem.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/plat.h>
#include <86box/thread.h>
#include <86box/video.h>
#include <86box/vid_svga.h>
#include <86box/vid_voodoo_common.h>
#include <86box/vid_voodoo_banshee.h>
#include <86box/vid_voodoo_blitter.h>
#include <86box/vid_voodoo_dither.h>
#include <86box/vid_voodoo_fifo.h>
#include <86box/vid_voodoo_regs.h>
#include <86box/vid_voodoo_render.h>
#include <86box/vid_voodoo_setup.h>
#include <86box/vid_voodoo_texture.h>

enum {
    CHIP_FBI   = 0x1,
    CHIP_TREX0 = 0x2,
    CHIP_TREX1 = 0x4,
    CHIP_TREX2 = 0x8
};

#ifdef ENABLE_VOODOO_REG_LOG
int voodoo_reg_do_log = ENABLE_VOODOO_REG_LOG;

static void
voodoo_reg_log(const char *fmt, ...)
{
    va_list ap;

    if (voodoo_reg_do_log) {
        va_start(ap, fmt);
        pclog_ex(fmt, ap);
        va_end(ap);
    }
}
#else
#    define voodoo_reg_log(fmt, ...)
#endif

void
voodoo_reg_writel(uint32_t addr, uint32_t val, void *priv)
{
    voodoo_t *voodoo                                      = (voodoo_t *) priv;
    void (*voodoo_recalc_tex)(voodoo_t * voodoo, int tmu) = NULL;
    union {
        uint32_t i;
        float    f;
    } tempif;
    int ad21 = addr & (1 << 21);
    int chip = (addr >> 10) & 0xf;
    if (!chip)
        chip = 0xf;

    if (voodoo->type == VOODOO_3)
        voodoo_recalc_tex = voodoo_recalc_tex3;
    else
        voodoo_recalc_tex = voodoo_recalc_tex12;

    tempif.i = val;
#if 0
    voodoo_reg_log("voodoo_reg_write_l: addr=%08x val=%08x(%f) chip=%x\n", addr, val, tempif.f, chip);
#endif
    addr &= 0x3fc;

    if ((voodoo->fbiInit3 & FBIINIT3_REMAP) && addr < 0x100 && ad21)
        addr |= 0x400;
    switch (addr) {
        case SST_swapbufferCMD:
            if (voodoo->type >= VOODOO_BANSHEE) {
#if 0
                voodoo_reg_log("swapbufferCMD %08x %08x\n", val, voodoo->leftOverlayBuf);
#endif

                voodoo_wait_for_render_thread_idle(voodoo);
                if (!(val & 1)) {
                    banshee_set_overlay_addr(voodoo->priv, voodoo->leftOverlayBuf);
                    thread_wait_mutex(voodoo->swap_mutex);
                    if (voodoo->swap_count > 0)
                        voodoo->swap_count--;
                    thread_release_mutex(voodoo->swap_mutex);
                    voodoo->frame_count++;
                } else if (TRIPLE_BUFFER) {
                    if (voodoo->swap_pending)
                        voodoo_wait_for_swap_complete(voodoo);
                    voodoo->swap_interval = (val >> 1) & 0xff;
                    voodoo->swap_offset   = voodoo->leftOverlayBuf;
                    voodoo->swap_pending  = 1;
                } else {
                    voodoo->swap_interval = (val >> 1) & 0xff;
                    voodoo->swap_offset   = voodoo->leftOverlayBuf;
                    voodoo->swap_pending  = 1;

                    voodoo_wait_for_swap_complete(voodoo);
                }

                voodoo->cmd_read++;
                break;
            }

            if (TRIPLE_BUFFER) {
                voodoo->disp_buffer = (voodoo->disp_buffer + 1) % 3;
                voodoo->draw_buffer = (voodoo->draw_buffer + 1) % 3;
            } else {
                voodoo->disp_buffer = !voodoo->disp_buffer;
                voodoo->draw_buffer = !voodoo->draw_buffer;
            }
            voodoo_recalc(voodoo);

            voodoo->params.swapbufferCMD = val;

#if 0
            voodoo_reg_log("Swap buffer %08x %d %p %i\n", val, voodoo->swap_count, &voodoo->swap_count, (voodoo == voodoo->set->voodoos[1]) ? 1 : 0);
            voodoo->front_offset = params->front_offset;
#endif
            voodoo_wait_for_render_thread_idle(voodoo);
            if (!(val & 1)) {
                memset(voodoo->dirty_line, 1, sizeof(voodoo->dirty_line));
                voodoo->front_offset = voodoo->params.front_offset;
                thread_wait_mutex(voodoo->swap_mutex);
                if (voodoo->swap_count > 0)
                    voodoo->swap_count--;
                thread_release_mutex(voodoo->swap_mutex);
            } else if (TRIPLE_BUFFER) {
                if (voodoo->swap_pending)
                    voodoo_wait_for_swap_complete(voodoo);

                voodoo->swap_interval = (val >> 1) & 0xff;
                voodoo->swap_offset   = voodoo->params.front_offset;
                voodoo->swap_pending  = 1;
            } else {
                voodoo->swap_interval = (val >> 1) & 0xff;
                voodoo->swap_offset   = voodoo->params.front_offset;
                voodoo->swap_pending  = 1;

                voodoo_wait_for_swap_complete(voodoo);
            }
            voodoo->cmd_read++;
            break;

        case SST_vertexAx:
        case SST_remap_vertexAx:
            voodoo->params.vertexAx = val & 0xffff;
            break;
        case SST_vertexAy:
        case SST_remap_vertexAy:
            voodoo->params.vertexAy = val & 0xffff;
            break;
        case SST_vertexBx:
        case SST_remap_vertexBx:
            voodoo->params.vertexBx = val & 0xffff;
            break;
        case SST_vertexBy:
        case SST_remap_vertexBy:
            voodoo->params.vertexBy = val & 0xffff;
            break;
        case SST_vertexCx:
        case SST_remap_vertexCx:
            voodoo->params.vertexCx = val & 0xffff;
            break;
        case SST_vertexCy:
        case SST_remap_vertexCy:
            voodoo->params.vertexCy = val & 0xffff;
            break;

        case SST_startR:
        case SST_remap_startR:
            voodoo->params.startR = val & 0xffffff;
            break;
        case SST_startG:
        case SST_remap_startG:
            voodoo->params.startG = val & 0xffffff;
            break;
        case SST_startB:
        case SST_remap_startB:
            voodoo->params.startB = val & 0xffffff;
            break;
        case SST_startZ:
        case SST_remap_startZ:
            voodoo->params.startZ = val;
            break;
        case SST_startA:
        case SST_remap_startA:
            voodoo->params.startA = val & 0xffffff;
            break;
        case SST_startS:
        case SST_remap_startS:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startS = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startS = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_startT:
        case SST_remap_startT:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startT = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startT = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_startW:
        case SST_remap_startW:
            if (chip & CHIP_FBI)
                voodoo->params.startW = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startW = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startW = (int64_t) (int32_t) val << 2;
            break;

        case SST_dRdX:
        case SST_remap_dRdX:
            voodoo->params.dRdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dGdX:
        case SST_remap_dGdX:
            voodoo->params.dGdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dBdX:
        case SST_remap_dBdX:
            voodoo->params.dBdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dZdX:
        case SST_remap_dZdX:
            voodoo->params.dZdX = val;
            break;
        case SST_dAdX:
        case SST_remap_dAdX:
            voodoo->params.dAdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dSdX:
        case SST_remap_dSdX:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dSdX = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dSdX = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_dTdX:
        case SST_remap_dTdX:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dTdX = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dTdX = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_dWdX:
        case SST_remap_dWdX:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dWdX = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dWdX = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_FBI)
                voodoo->params.dWdX = (int64_t) (int32_t) val << 2;
            break;

        case SST_dRdY:
        case SST_remap_dRdY:
            voodoo->params.dRdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dGdY:
        case SST_remap_dGdY:
            voodoo->params.dGdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dBdY:
        case SST_remap_dBdY:
            voodoo->params.dBdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dZdY:
        case SST_remap_dZdY:
            voodoo->params.dZdY = val;
            break;
        case SST_dAdY:
        case SST_remap_dAdY:
            voodoo->params.dAdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
            break;
        case SST_dSdY:
        case SST_remap_dSdY:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dSdY = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dSdY = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_dTdY:
        case SST_remap_dTdY:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dTdY = ((int64_t) (int32_t) val) << 14;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dTdY = ((int64_t) (int32_t) val) << 14;
            break;
        case SST_dWdY:
        case SST_remap_dWdY:
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dWdY = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dWdY = (int64_t) (int32_t) val << 2;
            if (chip & CHIP_FBI)
                voodoo->params.dWdY = (int64_t) (int32_t) val << 2;
            break;

        case SST_triangleCMD:
        case SST_remap_triangleCMD:
            voodoo->params.sign = val & (1 << 31);

            if (voodoo->ncc_dirty[0])
                voodoo_update_ncc(voodoo, 0);
            if (voodoo->ncc_dirty[1])
                voodoo_update_ncc(voodoo, 1);
            voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0;

            voodoo_queue_triangle(voodoo, &voodoo->params);

            voodoo->cmd_read++;
            break;

        case SST_fvertexAx:
        case SST_remap_fvertexAx:
            voodoo->fvertexAx.i     = val;
            voodoo->params.vertexAx = (int32_t) (int16_t) (int32_t) (voodoo->fvertexAx.f * 16.0f) & 0xffff;
            break;
        case SST_fvertexAy:
        case SST_remap_fvertexAy:
            voodoo->fvertexAy.i     = val;
            voodoo->params.vertexAy = (int32_t) (int16_t) (int32_t) (voodoo->fvertexAy.f * 16.0f) & 0xffff;
            break;
        case SST_fvertexBx:
        case SST_remap_fvertexBx:
            voodoo->fvertexBx.i     = val;
            voodoo->params.vertexBx = (int32_t) (int16_t) (int32_t) (voodoo->fvertexBx.f * 16.0f) & 0xffff;
            break;
        case SST_fvertexBy:
        case SST_remap_fvertexBy:
            voodoo->fvertexBy.i     = val;
            voodoo->params.vertexBy = (int32_t) (int16_t) (int32_t) (voodoo->fvertexBy.f * 16.0f) & 0xffff;
            break;
        case SST_fvertexCx:
        case SST_remap_fvertexCx:
            voodoo->fvertexCx.i     = val;
            voodoo->params.vertexCx = (int32_t) (int16_t) (int32_t) (voodoo->fvertexCx.f * 16.0f) & 0xffff;
            break;
        case SST_fvertexCy:
        case SST_remap_fvertexCy:
            voodoo->fvertexCy.i     = val;
            voodoo->params.vertexCy = (int32_t) (int16_t) (int32_t) (voodoo->fvertexCy.f * 16.0f) & 0xffff;
            break;

        case SST_fstartR:
        case SST_remap_fstartR:
            tempif.i              = val;
            voodoo->params.startR = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fstartG:
        case SST_remap_fstartG:
            tempif.i              = val;
            voodoo->params.startG = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fstartB:
        case SST_remap_fstartB:
            tempif.i              = val;
            voodoo->params.startB = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fstartZ:
        case SST_remap_fstartZ:
            tempif.i              = val;
            voodoo->params.startZ = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fstartA:
        case SST_remap_fstartA:
            tempif.i              = val;
            voodoo->params.startA = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fstartS:
        case SST_remap_fstartS:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startS = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startS = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fstartT:
        case SST_remap_fstartT:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startT = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startT = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fstartW:
        case SST_remap_fstartW:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].startW = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].startW = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_FBI)
                voodoo->params.startW = (int64_t) (tempif.f * 4294967296.0f);
            break;

        case SST_fdRdX:
        case SST_remap_fdRdX:
            tempif.i            = val;
            voodoo->params.dRdX = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdGdX:
        case SST_remap_fdGdX:
            tempif.i            = val;
            voodoo->params.dGdX = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdBdX:
        case SST_remap_fdBdX:
            tempif.i            = val;
            voodoo->params.dBdX = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdZdX:
        case SST_remap_fdZdX:
            tempif.i            = val;
            voodoo->params.dZdX = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdAdX:
        case SST_remap_fdAdX:
            tempif.i            = val;
            voodoo->params.dAdX = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdSdX:
        case SST_remap_fdSdX:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dSdX = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dSdX = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fdTdX:
        case SST_remap_fdTdX:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dTdX = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dTdX = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fdWdX:
        case SST_remap_fdWdX:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dWdX = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dWdX = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_FBI)
                voodoo->params.dWdX = (int64_t) (tempif.f * 4294967296.0f);
            break;

        case SST_fdRdY:
        case SST_remap_fdRdY:
            tempif.i            = val;
            voodoo->params.dRdY = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdGdY:
        case SST_remap_fdGdY:
            tempif.i            = val;
            voodoo->params.dGdY = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdBdY:
        case SST_remap_fdBdY:
            tempif.i            = val;
            voodoo->params.dBdY = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdZdY:
        case SST_remap_fdZdY:
            tempif.i            = val;
            voodoo->params.dZdY = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdAdY:
        case SST_remap_fdAdY:
            tempif.i            = val;
            voodoo->params.dAdY = (int32_t) (tempif.f * 4096.0f);
            break;
        case SST_fdSdY:
        case SST_remap_fdSdY:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dSdY = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dSdY = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fdTdY:
        case SST_remap_fdTdY:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dTdY = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dTdY = (int64_t) (tempif.f * 4294967296.0f);
            break;
        case SST_fdWdY:
        case SST_remap_fdWdY:
            tempif.i = val;
            if (chip & CHIP_TREX0)
                voodoo->params.tmu[0].dWdY = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_TREX1)
                voodoo->params.tmu[1].dWdY = (int64_t) (tempif.f * 4294967296.0f);
            if (chip & CHIP_FBI)
                voodoo->params.dWdY = (int64_t) (tempif.f * 4294967296.0f);
            break;

        case SST_ftriangleCMD:
            voodoo->params.sign = val & (1 << 31);

            if (voodoo->ncc_dirty[0])
                voodoo_update_ncc(voodoo, 0);
            if (voodoo->ncc_dirty[1])
                voodoo_update_ncc(voodoo, 1);
            voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0;

            voodoo_queue_triangle(voodoo, &voodoo->params);

            voodoo->cmd_read++;
            break;

        case SST_fbzColorPath:
            voodoo->params.fbzColorPath = val;
            voodoo->rgb_sel             = val & 3;
            break;

        case SST_fogMode:
            voodoo->params.fogMode = val;
            break;
        case SST_alphaMode:
            voodoo->params.alphaMode = val;
            break;
        case SST_fbzMode:
            voodoo->params.fbzMode = val;
            voodoo_recalc(voodoo);
            break;
        case SST_lfbMode:
            voodoo->lfbMode = val;
            voodoo_recalc(voodoo);
            break;

        case SST_clipLeftRight:
            if (voodoo->type >= VOODOO_2) {
                voodoo->params.clipRight = val & 0xfff;
                voodoo->params.clipLeft  = (val >> 16) & 0xfff;
            } else {
                voodoo->params.clipRight = val & 0x3ff;
                voodoo->params.clipLeft  = (val >> 16) & 0x3ff;
            }
            break;
        case SST_clipLowYHighY:
            if (voodoo->type >= VOODOO_2) {
                voodoo->params.clipHighY = val & 0xfff;
                voodoo->params.clipLowY  = (val >> 16) & 0xfff;
            } else {
                voodoo->params.clipHighY = val & 0x3ff;
                voodoo->params.clipLowY  = (val >> 16) & 0x3ff;
            }
            break;

        case SST_nopCMD:
            voodoo->cmd_read++;
            voodoo->fbiPixelsIn   = 0;
            voodoo->fbiChromaFail = 0;
            voodoo->fbiZFuncFail  = 0;
            voodoo->fbiAFuncFail  = 0;
            voodoo->fbiPixelsOut  = 0;
            break;
        case SST_fastfillCMD:
            voodoo_wait_for_render_thread_idle(voodoo);
            voodoo_fastfill(voodoo, &voodoo->params);
            voodoo->cmd_read++;
            break;

        case SST_fogColor:
            voodoo->params.fogColor.r = (val >> 16) & 0xff;
            voodoo->params.fogColor.g = (val >> 8) & 0xff;
            voodoo->params.fogColor.b = val & 0xff;
            break;

        case SST_zaColor:
            voodoo->params.zaColor = val;
            break;
        case SST_chromaKey:
            voodoo->params.chromaKey_r = (val >> 16) & 0xff;
            voodoo->params.chromaKey_g = (val >> 8) & 0xff;
            voodoo->params.chromaKey_b = val & 0xff;
            voodoo->params.chromaKey   = val & 0xffffff;
            break;
        case SST_stipple:
            voodoo->params.stipple = val;
            break;
        case SST_color0:
            voodoo->params.color0 = val;
            break;
        case SST_color1:
            voodoo->params.color1 = val;
            break;

        case SST_fogTable00:
        case SST_fogTable01:
        case SST_fogTable02:
        case SST_fogTable03:
        case SST_fogTable04:
        case SST_fogTable05:
        case SST_fogTable06:
        case SST_fogTable07:
        case SST_fogTable08:
        case SST_fogTable09:
        case SST_fogTable0a:
        case SST_fogTable0b:
        case SST_fogTable0c:
        case SST_fogTable0d:
        case SST_fogTable0e:
        case SST_fogTable0f:
        case SST_fogTable10:
        case SST_fogTable11:
        case SST_fogTable12:
        case SST_fogTable13:
        case SST_fogTable14:
        case SST_fogTable15:
        case SST_fogTable16:
        case SST_fogTable17:
        case SST_fogTable18:
        case SST_fogTable19:
        case SST_fogTable1a:
        case SST_fogTable1b:
        case SST_fogTable1c:
        case SST_fogTable1d:
        case SST_fogTable1e:
        case SST_fogTable1f:
            addr                                   = (addr - SST_fogTable00) >> 1;
            voodoo->params.fogTable[addr].dfog     = val & 0xff;
            voodoo->params.fogTable[addr].fog      = (val >> 8) & 0xff;
            voodoo->params.fogTable[addr + 1].dfog = (val >> 16) & 0xff;
            voodoo->params.fogTable[addr + 1].fog  = (val >> 24) & 0xff;
            break;

        case SST_clipLeftRight1:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->params.clipRight1 = val & 0xfff;
                voodoo->params.clipLeft1  = (val >> 16) & 0xfff;
            }
            break;
        case SST_clipTopBottom1:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->params.clipHighY1 = val & 0xfff;
                voodoo->params.clipLowY1  = (val >> 16) & 0xfff;
            }
            break;

        case SST_colBufferAddr:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->params.draw_offset = val & 0xfffff0;
                voodoo->fb_write_offset    = voodoo->params.draw_offset;
#if 0
                voodoo_reg_log("colorBufferAddr=%06x\n", voodoo->params.draw_offset);
#endif
            }
            break;
        case SST_colBufferStride:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->col_tiled        = val & (1 << 15);
                voodoo->params.col_tiled = voodoo->col_tiled;
                if (voodoo->col_tiled) {
                    voodoo->row_width = (val & 0x7f) * 128 * 32;
#if 0
                    voodoo_reg_log("colBufferStride tiled = %i bytes, tiled  %08x\n", voodoo->row_width, val);
#endif
                } else {
                    voodoo->row_width = val & 0x3fff;
#if 0
                    voodoo_reg_log("colBufferStride linear = %i bytes, linear\n", voodoo->row_width);
#endif
                }
                voodoo->params.row_width = voodoo->row_width;
            }
            break;
        case SST_auxBufferAddr:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->params.aux_offset = val & 0xfffff0;
#if 0
                pclog("auxBufferAddr=%06x\n", voodoo->params.aux_offset);
#endif
            }
            break;
        case SST_auxBufferStride:
            if (voodoo->type >= VOODOO_BANSHEE) {
                voodoo->aux_tiled        = val & (1 << 15);
                voodoo->params.aux_tiled = voodoo->aux_tiled;
                if (voodoo->aux_tiled) {
                    voodoo->aux_row_width = (val & 0x7f) * 128 * 32;
#if 0
                    voodoo_reg_log("auxBufferStride tiled = %i bytes, tiled\n", voodoo->aux_row_width);
#endif
                } else {
                    voodoo->aux_row_width = val & 0x3fff;
#if 0
                    voodoo_reg_log("auxBufferStride linear = %i bytes, linear\n", voodoo->aux_row_width);
#endif
                }
                voodoo->params.aux_row_width = voodoo->aux_row_width;
            }
            break;

        case SST_clutData:
            voodoo->clutData[(val >> 24) & 0x3f].b = val & 0xff;
            voodoo->clutData[(val >> 24) & 0x3f].g = (val >> 8) & 0xff;
            voodoo->clutData[(val >> 24) & 0x3f].r = (val >> 16) & 0xff;
            if (val & 0x20000000) {
                voodoo->clutData[(val >> 24) & 0x3f].b = 255;
                voodoo->clutData[(val >> 24) & 0x3f].g = 255;
                voodoo->clutData[(val >> 24) & 0x3f].r = 255;
            }
            voodoo->clutData_dirty = 1;
            break;

        case SST_sSetupMode:
            voodoo->sSetupMode = val;
            break;
        case SST_sVx:
            tempif.i             = val;
            voodoo->verts[3].sVx = tempif.f;
#if 0
            voodoo_reg_log("sVx[%i]=%f\n", voodoo->vertex_num, tempif.f);
#endif
            break;
        case SST_sVy:
            tempif.i             = val;
            voodoo->verts[3].sVy = tempif.f;
#if 0
            voodoo_reg_log("sVy[%i]=%f\n", voodoo->vertex_num, tempif.f);
#endif
            break;
        case SST_sARGB:
            voodoo->verts[3].sBlue  = (float) (val & 0xff);
            voodoo->verts[3].sGreen = (float) ((val >> 8) & 0xff);
            voodoo->verts[3].sRed   = (float) ((val >> 16) & 0xff);
            voodoo->verts[3].sAlpha = (float) ((val >> 24) & 0xff);
            break;
        case SST_sRed:
            tempif.i              = val;
            voodoo->verts[3].sRed = tempif.f;
            break;
        case SST_sGreen:
            tempif.i                = val;
            voodoo->verts[3].sGreen = tempif.f;
            break;
        case SST_sBlue:
            tempif.i               = val;
            voodoo->verts[3].sBlue = tempif.f;
            break;
        case SST_sAlpha:
            tempif.i                = val;
            voodoo->verts[3].sAlpha = tempif.f;
            break;
        case SST_sVz:
            tempif.i             = val;
            voodoo->verts[3].sVz = tempif.f;
            break;
        case SST_sWb:
            tempif.i             = val;
            voodoo->verts[3].sWb = tempif.f;
            break;
        case SST_sW0:
            tempif.i             = val;
            voodoo->verts[3].sW0 = tempif.f;
            break;
        case SST_sS0:
            tempif.i             = val;
            voodoo->verts[3].sS0 = tempif.f;
            break;
        case SST_sT0:
            tempif.i             = val;
            voodoo->verts[3].sT0 = tempif.f;
            break;
        case SST_sW1:
            tempif.i             = val;
            voodoo->verts[3].sW1 = tempif.f;
            break;
        case SST_sS1:
            tempif.i             = val;
            voodoo->verts[3].sS1 = tempif.f;
            break;
        case SST_sT1:
            tempif.i             = val;
            voodoo->verts[3].sT1 = tempif.f;
            break;

        case SST_sBeginTriCMD:
#if 0
            voodoo_reg_log("sBeginTriCMD %i %f\n", voodoo->vertex_num, voodoo->verts[4].sVx);
#endif
            voodoo->verts[0]        = voodoo->verts[3];
            voodoo->verts[1]        = voodoo->verts[3];
            voodoo->verts[2]        = voodoo->verts[3];
            voodoo->vertex_next_age = 0;
            voodoo->vertex_ages[0]  = voodoo->vertex_next_age++;

            voodoo->num_verticies = 1;
            voodoo->cull_pingpong = 0;
            break;
        case SST_sDrawTriCMD:
#if 0
            voodoo_reg_log("sDrawTriCMD %i %i\n", voodoo->num_verticies, voodoo->sSetupMode & SETUPMODE_STRIP_MODE);
#endif
            /*I'm not sure this is the vertex selection algorithm actually used in the 3dfx
              chips, but this works with a number of games that switch between strip and fan
              mode in the middle of a run (eg Black & White, Viper Racing)*/
            if (voodoo->vertex_next_age < 3) {
                /*Fewer than three vertices already written, store in next slot*/
                int vertex_nr = voodoo->vertex_next_age;

                voodoo->verts[vertex_nr]       = voodoo->verts[3];
                voodoo->vertex_ages[vertex_nr] = voodoo->vertex_next_age++;
            } else {
                int vertex_nr = 0;

                if (!(voodoo->sSetupMode & SETUPMODE_STRIP_MODE)) {
                    /*Strip - find oldest vertex*/
                    if ((voodoo->vertex_ages[0] < voodoo->vertex_ages[1]) && (voodoo->vertex_ages[0] < voodoo->vertex_ages[2]))
                        vertex_nr = 0;
                    else if ((voodoo->vertex_ages[1] < voodoo->vertex_ages[0]) && (voodoo->vertex_ages[1] < voodoo->vertex_ages[2]))
                        vertex_nr = 1;
                    else
                        vertex_nr = 2;
                } else {
                    /*Fan - find second oldest vertex (ie pivot around oldest)*/
                    if ((voodoo->vertex_ages[1] < voodoo->vertex_ages[0]) && (voodoo->vertex_ages[0] < voodoo->vertex_ages[2]))
                        vertex_nr = 0;
                    else if ((voodoo->vertex_ages[2] < voodoo->vertex_ages[0]) && (voodoo->vertex_ages[0] < voodoo->vertex_ages[1]))
                        vertex_nr = 0;
                    else if ((voodoo->vertex_ages[0] < voodoo->vertex_ages[1]) && (voodoo->vertex_ages[1] < voodoo->vertex_ages[2]))
                        vertex_nr = 1;
                    else if ((voodoo->vertex_ages[2] < voodoo->vertex_ages[1]) && (voodoo->vertex_ages[1] < voodoo->vertex_ages[0]))
                        vertex_nr = 1;
                    else
                        vertex_nr = 2;
                }
                voodoo->verts[vertex_nr]       = voodoo->verts[3];
                voodoo->vertex_ages[vertex_nr] = voodoo->vertex_next_age++;
            }

            voodoo->num_verticies++;
            if (voodoo->num_verticies == 3) {
#if 0
                voodoo_reg_log("triangle_setup\n");
#endif
                voodoo_triangle_setup(voodoo);
                voodoo->cull_pingpong = !voodoo->cull_pingpong;

                voodoo->num_verticies = 2;
            }
            break;

        case SST_bltSrcBaseAddr:
            voodoo->bltSrcBaseAddr = val & 0x3fffff;
            break;
        case SST_bltDstBaseAddr:
#if 0
            voodoo_reg_log("Write bltDstBaseAddr %08x\n", val);
#endif
            voodoo->bltDstBaseAddr = val & 0x3fffff;
            break;
        case SST_bltXYStrides:
            voodoo->bltSrcXYStride = val & 0xfff;
            voodoo->bltDstXYStride = (val >> 16) & 0xfff;
            //                voodoo_reg_log("Write bltXYStrides %08x\n", val);
            break;
        case SST_bltSrcChromaRange:
            voodoo->bltSrcChromaRange = val;
            voodoo->bltSrcChromaMinB  = val & 0x1f;
            voodoo->bltSrcChromaMinG  = (val >> 5) & 0x3f;
            voodoo->bltSrcChromaMinR  = (val >> 11) & 0x1f;
            voodoo->bltSrcChromaMaxB  = (val >> 16) & 0x1f;
            voodoo->bltSrcChromaMaxG  = (val >> 21) & 0x3f;
            voodoo->bltSrcChromaMaxR  = (val >> 27) & 0x1f;
            break;
        case SST_bltDstChromaRange:
            voodoo->bltDstChromaRange = val;
            voodoo->bltDstChromaMinB  = val & 0x1f;
            voodoo->bltDstChromaMinG  = (val >> 5) & 0x3f;
            voodoo->bltDstChromaMinR  = (val >> 11) & 0x1f;
            voodoo->bltDstChromaMaxB  = (val >> 16) & 0x1f;
            voodoo->bltDstChromaMaxG  = (val >> 21) & 0x3f;
            voodoo->bltDstChromaMaxR  = (val >> 27) & 0x1f;
            break;
        case SST_bltClipX:
            voodoo->bltClipRight = val & 0xfff;
            voodoo->bltClipLeft  = (val >> 16) & 0xfff;
            break;
        case SST_bltClipY:
            voodoo->bltClipHighY = val & 0xfff;
            voodoo->bltClipLowY  = (val >> 16) & 0xfff;
            break;

        case SST_bltSrcXY:
            voodoo->bltSrcX = val & 0x7ff;
            voodoo->bltSrcY = (val >> 16) & 0x7ff;
            break;
        case SST_bltDstXY:
            //                voodoo_reg_log("Write bltDstXY %08x\n", val);
            voodoo->bltDstX = val & 0x7ff;
            voodoo->bltDstY = (val >> 16) & 0x7ff;
            if (val & (1 << 31))
                voodoo_v2_blit_start(voodoo);
            break;
        case SST_bltSize:
            //                voodoo_reg_log("Write bltSize %08x\n", val);
            voodoo->bltSizeX = val & 0xfff;
            if (voodoo->bltSizeX & 0x800)
                voodoo->bltSizeX |= 0xfffff000;
            voodoo->bltSizeY = (val >> 16) & 0xfff;
            if (voodoo->bltSizeY & 0x800)
                voodoo->bltSizeY |= 0xfffff000;
            if (val & (1 << 31))
                voodoo_v2_blit_start(voodoo);
            break;
        case SST_bltRop:
            voodoo->bltRop[0] = val & 0xf;
            voodoo->bltRop[1] = (val >> 4) & 0xf;
            voodoo->bltRop[2] = (val >> 8) & 0xf;
            voodoo->bltRop[3] = (val >> 12) & 0xf;
            break;
        case SST_bltColor:
            //                voodoo_reg_log("Write bltColor %08x\n", val);
            voodoo->bltColorFg = val & 0xffff;
            voodoo->bltColorBg = (val >> 16) & 0xffff;
            break;

        case SST_bltCommand:
            voodoo->bltCommand = val;
            //                voodoo_reg_log("Write bltCommand %08x\n", val);
            if (val & (1 << 31))
                voodoo_v2_blit_start(voodoo);
            break;
        case SST_bltData:
            voodoo_v2_blit_data(voodoo, val);
            break;

        case SST_textureMode:
            if (chip & CHIP_TREX0) {
                voodoo->params.textureMode[0] = val;
                voodoo->params.tformat[0]     = (val >> 8) & 0xf;
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                voodoo->params.textureMode[1] = val;
                voodoo->params.tformat[1]     = (val >> 8) & 0xf;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;
        case SST_tLOD:
            if (chip & CHIP_TREX0) {
                voodoo->params.tLOD[0] = val;
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                voodoo->params.tLOD[1] = val;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;
        case SST_tDetail:
            if (chip & CHIP_TREX0) {
                voodoo->params.detail_max[0]   = val & 0xff;
                voodoo->params.detail_bias[0]  = (val >> 8) & 0x3f;
                voodoo->params.detail_scale[0] = (val >> 14) & 7;
            }
            if (chip & CHIP_TREX1) {
                voodoo->params.detail_max[1]   = val & 0xff;
                voodoo->params.detail_bias[1]  = (val >> 8) & 0x3f;
                voodoo->params.detail_scale[1] = (val >> 14) & 7;
            }
            break;
        case SST_texBaseAddr:
            if (chip & CHIP_TREX0) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr[0] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr[0] = (val & 0x7ffff) << 3;
#if 0
                voodoo_reg_log("texBaseAddr = %08x %08x\n", voodoo->params.texBaseAddr[0], val);
#endif
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr[1] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr[1] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;
        case SST_texBaseAddr1:
            if (chip & CHIP_TREX0) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr1[0] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr1[0] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr1[1] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr1[1] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;
        case SST_texBaseAddr2:
            if (chip & CHIP_TREX0) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr2[0] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr2[0] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr2[1] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr2[1] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;
        case SST_texBaseAddr38:
            if (chip & CHIP_TREX0) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr38[0] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr38[0] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 0);
            }
            if (chip & CHIP_TREX1) {
                if (voodoo->type >= VOODOO_BANSHEE)
                    voodoo->params.texBaseAddr38[1] = val & 0xfffff0;
                else
                    voodoo->params.texBaseAddr38[1] = (val & 0x7ffff) << 3;
                voodoo_recalc_tex(voodoo, 1);
            }
            break;

        case SST_trexInit1:
            if (chip & CHIP_TREX0)
                voodoo->trexInit1[0] = val;
            if (chip & CHIP_TREX1)
                voodoo->trexInit1[1] = val;
            break;

        case SST_nccTable0_Y0:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][0].y[0] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][0].y[0] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable0_Y1:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][0].y[1] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][0].y[1] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable0_Y2:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][0].y[2] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][0].y[2] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable0_Y3:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][0].y[3] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][0].y[3] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;

        case SST_nccTable0_I0:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].i[0] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].i[0] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
            fallthrough;
        case SST_nccTable0_I2:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].i[2] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].i[2] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
        case SST_nccTable0_Q0:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].q[0] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].q[0] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
        case SST_nccTable0_Q2:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].i[2] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].i[2] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
            if (val & (1 << 31)) {
                int p = (val >> 23) & 0xfe;
                if (chip & CHIP_TREX0) {
                    voodoo->palette[0][p].u  = val | 0xff000000;
                    voodoo->palette_dirty[0] = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->palette[1][p].u  = val | 0xff000000;
                    voodoo->palette_dirty[1] = 1;
                }
            }
            break;

        case SST_nccTable0_I1:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].i[1] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].i[1] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
        case SST_nccTable0_I3:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].i[3] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].i[3] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
        case SST_nccTable0_Q1:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].q[1] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].q[1] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
        case SST_nccTable0_Q3:
            if (!(val & (1 << 31))) {
                if (chip & CHIP_TREX0) {
                    voodoo->nccTable[0][0].q[3] = val;
                    voodoo->ncc_dirty[0]        = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->nccTable[1][0].q[3] = val;
                    voodoo->ncc_dirty[1]        = 1;
                }
                break;
            }
            if (val & (1 << 31)) {
                int p = ((val >> 23) & 0xfe) | 0x01;
                if (chip & CHIP_TREX0) {
                    voodoo->palette[0][p].u  = val | 0xff000000;
                    voodoo->palette_dirty[0] = 1;
                }
                if (chip & CHIP_TREX1) {
                    voodoo->palette[1][p].u  = val | 0xff000000;
                    voodoo->palette_dirty[1] = 1;
                }
            }
            break;

        case SST_nccTable1_Y0:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].y[0] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].y[0] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Y1:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].y[1] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].y[1] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Y2:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].y[2] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].y[2] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Y3:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].y[3] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].y[3] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_I0:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].i[0] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].i[0] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_I1:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].i[1] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].i[1] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_I2:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].i[2] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].i[2] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_I3:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].i[3] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].i[3] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Q0:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].q[0] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].q[0] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Q1:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].q[1] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].q[1] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Q2:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].q[2] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].q[2] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;
        case SST_nccTable1_Q3:
            if (chip & CHIP_TREX0) {
                voodoo->nccTable[0][1].q[3] = val;
                voodoo->ncc_dirty[0]        = 1;
            }
            if (chip & CHIP_TREX1) {
                voodoo->nccTable[1][1].q[3] = val;
                voodoo->ncc_dirty[1]        = 1;
            }
            break;

        case SST_userIntrCMD:
            fatal("userIntrCMD write %08x from FIFO\n", val);
            break;

        case SST_leftOverlayBuf:
            voodoo->leftOverlayBuf = val;
            break;

        default:
            break;
    }
}
