summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/voodoo_2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/voodoo_2.cpp')
-rw-r--r--src/devices/video/voodoo_2.cpp1723
1 files changed, 1723 insertions, 0 deletions
diff --git a/src/devices/video/voodoo_2.cpp b/src/devices/video/voodoo_2.cpp
new file mode 100644
index 00000000000..5529c035eef
--- /dev/null
+++ b/src/devices/video/voodoo_2.cpp
@@ -0,0 +1,1723 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ voodoo_2.c
+
+ 3dfx Voodoo Graphics SST-1/2 emulator.
+
+****************************************************************************
+
+ Specs:
+
+ Voodoo 2:
+ 2,4MB frame buffer RAM
+ 2,4,8,16MB texture RAM
+ 90MHz clock frquency
+ clears @ 2 pixels/clock (RGB and depth simultaneously)
+ renders @ 1 pixel/clock
+ ultrafast clears @ 16 pixels/clock
+ 128 entry PCI FIFO
+ memory FIFO up to 65536 entries
+
+**************************************************************************/
+
+#include "emu.h"
+#include "voodoo_2.h"
+
+using namespace voodoo;
+
+
+//**************************************************************************
+// COMMAND FIFO
+//**************************************************************************
+
+//-------------------------------------------------
+// command_fifo - constructor
+//-------------------------------------------------
+
+command_fifo::command_fifo(voodoo_2_device &device) :
+ m_device(device),
+ m_ram(nullptr),
+ m_mask(0),
+ m_enable(false),
+ m_count_holes(false),
+ m_ram_base(0),
+ m_ram_end(0),
+ m_read_index(0),
+ m_address_min(0),
+ m_address_max(0),
+ m_depth(0),
+ m_holes(0)
+{
+}
+
+
+//-------------------------------------------------
+// register_save - register for save states
+//-------------------------------------------------
+
+void command_fifo::register_save(save_proxy &save)
+{
+ save.save_item(NAME(m_enable));
+ save.save_item(NAME(m_count_holes));
+ save.save_item(NAME(m_ram_base));
+ save.save_item(NAME(m_ram_end));
+ save.save_item(NAME(m_read_index));
+ save.save_item(NAME(m_address_min));
+ save.save_item(NAME(m_address_max));
+ save.save_item(NAME(m_depth));
+ save.save_item(NAME(m_holes));
+}
+
+
+//-------------------------------------------------
+// execute_if_ready - execute everything we have
+// the data for, until we encounter an operation
+// that consumes a non-zero number of cycles
+//-------------------------------------------------
+
+u32 command_fifo::execute_if_ready()
+{
+ while (1)
+ {
+ // all CMDFIFO commands need at least one word
+ if (m_depth == 0)
+ return 0;
+
+ // see if we have enough for the current command
+ u32 const needed_depth = words_needed(peek_next());
+ if (m_depth < needed_depth)
+ return 0;
+
+ // read the next command and handle it based on the low 3 bits
+ u32 command = read_next();
+ u32 cycles = (this->*s_packet_handler[BIT(command, 0, 3)])(command);
+
+ // if the number of cycles is non-zero, return
+ if (cycles > 0)
+ return cycles;
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle a write to the FIFO
+//-------------------------------------------------
+
+void command_fifo::write(offs_t addr, u32 data)
+{
+ if (LOG_CMDFIFO_VERBOSE)
+ m_device.logerror("CMDFIFO_w(%04X) = %08X\n", addr, data);
+
+ // write the data if it's within range
+ if (addr < m_ram_end)
+ m_ram[(addr / 4) & m_mask] = data;
+
+ // count holes?
+ if (m_count_holes)
+ {
+ // in-order, no holes
+ if (m_holes == 0 && addr == m_address_min + 4)
+ {
+ m_address_min = m_address_max = addr;
+ m_depth++;
+ }
+
+ // out-of-order, below the minimum
+ else if (addr < m_address_min)
+ {
+ if (m_holes != 0)
+ m_device.logerror("Unexpected CMDFIFO: AMin=%08X AMax=%08X Holes=%d WroteTo:%08X\n", m_address_min, m_address_max, m_holes, addr);
+ m_holes += (addr - m_ram_base) / 4;
+ m_address_min = m_ram_base;
+ m_address_max = addr;
+ m_depth++;
+ }
+
+ // out-of-order, but within the min-max range
+ else if (addr < m_address_max)
+ {
+ m_holes--;
+ if (m_holes == 0)
+ {
+ m_depth += (m_address_max - m_address_min) / 4;
+ m_address_min = m_address_max;
+ }
+ }
+
+ // out-of-order, bumping max
+ else
+ {
+ m_holes += (addr - m_address_max) / 4 - 1;
+ m_address_max = addr;
+ }
+ }
+
+ // execute if we can
+ if (!m_device.operation_pending())
+ {
+ s32 cycles = execute_if_ready();
+ if (cycles > 0)
+ {
+ attotime curtime = m_device.machine().time();
+ m_device.m_operation_end = curtime + m_device.clocks_to_attotime(cycles);
+
+ if (LOG_FIFO_VERBOSE)
+ m_device.logerror("VOODOO.FIFO:direct write start at %s end at %s\n", curtime.as_string(18), m_device.m_operation_end.as_string(18));
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// words_needed - return the total number of
+// words needed for the given command and all its
+// parameters
+//-------------------------------------------------
+
+u32 command_fifo::words_needed(u32 command)
+{
+ // low 3 bits specify the packet type
+ switch (BIT(command, 0, 3))
+ {
+ case 0:
+ // Packet type 0: 1 or 2 words
+ //
+ // Word Bits
+ // 0 31:29 = reserved
+ // 0 28:6 = Address [24:2]
+ // 0 5:3 = Function (0 = NOP, 1 = JSR, 2 = RET, 3 = JMP LOCAL, 4 = JMP AGP)
+ // 0 2:0 = Packet type (0)
+ return (BIT(command, 3, 3) == 4) ? 2 : 1;
+
+ case 1:
+ // Packet type 1: 1 + N words
+ //
+ // Word Bits
+ // 0 31:16 = Number of words
+ // 0 15 = Increment?
+ // 0 14:3 = Register base
+ // 0 2:0 = Packet type (1)
+ return 1 + BIT(command, 16, 16);
+
+ case 2:
+ // Packet type 2: 1 + N words
+ //
+ // Word Bits
+ // 0 31:3 = 2D Register mask
+ // 0 2:0 = Packet type (2)
+ return 1 + population_count_32(BIT(command, 3, 29));
+
+ case 3:
+ {
+ // Packet type 3: 1 + N words
+ //
+ // Word Bits
+ // 0 31:29 = Number of dummy entries following the data
+ // 0 28 = Packed color data?
+ // 0 25 = Disable ping pong sign correction (0=normal, 1=disable)
+ // 0 24 = Culling sign (0=positive, 1=negative)
+ // 0 23 = Enable culling (0=disable, 1=enable)
+ // 0 22 = Strip mode (0=strip, 1=fan)
+ // 0 17 = Setup S1 and T1
+ // 0 16 = Setup W1
+ // 0 15 = Setup S0 and T0
+ // 0 14 = Setup W0
+ // 0 13 = Setup Wb
+ // 0 12 = Setup Z
+ // 0 11 = Setup Alpha
+ // 0 10 = Setup RGB
+ // 0 9:6 = Number of vertices
+ // 0 5:3 = Command (0=Independent tris, 1=Start new strip, 2=Continue strip)
+ // 0 2:0 = Packet type (3)
+
+ // determine words per vertex
+ u32 count = 2; // X/Y
+ if (BIT(command, 28))
+ count += (BIT(command, 10, 2) != 0) ? 1 : 0; // ARGB in one word
+ else
+ count += 3 * BIT(command, 10) + BIT(command, 11); // RGB + A
+ count += BIT(command, 12); // Z
+ count += BIT(command, 13); // Wb
+ count += BIT(command, 14); // W0
+ count += 2 * BIT(command, 15); // S0/T0
+ count += BIT(command, 16); // W1
+ count += 2 * BIT(command, 17); // S1/T1
+
+ // multiply by the number of verticies
+ count *= BIT(command, 6, 4);
+ return 1 + count + BIT(command, 29, 3);
+ }
+
+ case 4:
+ // Packet type 4: 1 + N words
+ //
+ // Word Bits
+ // 0 31:29 = Number of dummy entries following the data
+ // 0 28:15 = General register mask
+ // 0 14:3 = Register base
+ // 0 2:0 = Packet type (4)
+ return 1 + population_count_32(BIT(command, 15, 14)) + BIT(command, 29, 3);
+
+ case 5:
+ // Packet type 5: 2 + N words
+ //
+ // Word Bits
+ // 0 31:30 = Space (0,1=reserved, 2=LFB, 3=texture)
+ // 0 29:26 = Byte disable W2
+ // 0 25:22 = Byte disable WN
+ // 0 21:3 = Num words
+ // 0 2:0 = Packet type (5)
+ return 2 + BIT(command, 3, 19);
+
+ default:
+ m_device.logerror("cmdfifo unknown packet type %d\n", command & 7);
+ return 1;
+ }
+}
+
+
+//-------------------------------------------------
+// packet_type_0 - handle FIFO packet type 0
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_0(u32 command)
+{
+ // Packet type 0: 1 or 2 words
+ //
+ // Word Bits
+ // 0 31:29 = reserved
+ // 0 28:6 = Address [24:2]
+ // 0 5:3 = Function (0 = NOP, 1 = JSR, 2 = RET, 3 = JMP LOCAL, 4 = JMP AGP)
+ // 0 2:0 = Packet type (0)
+ // 1 31:11 = reserved (JMP AGP only)
+ // 1 10:0 = Address [35:25]
+ u32 target = BIT(command, 6, 23) << 2;
+
+ // switch off of the specific command; many are unimplemented until we
+ // see them in real life
+ switch (BIT(command, 3, 3))
+ {
+ case 0: // NOP
+ if (LOG_CMDFIFO)
+ m_device.logerror(" NOP\n");
+ break;
+
+ case 1: // JSR
+ if (LOG_CMDFIFO)
+ m_device.logerror(" JSR $%06X\n", target);
+ m_device.logerror("cmdFifo: Unsupported JSR");
+ break;
+
+ case 2: // RET
+ if (LOG_CMDFIFO)
+ m_device.logerror(" RET $%06X\n", target);
+ m_device.logerror("cmdFifo: Unsupported RET");
+ break;
+
+ case 3: // JMP LOCAL FRAME BUFFER
+ if (LOG_CMDFIFO)
+ m_device.logerror(" JMP LOCAL FRAMEBUF $%06X\n", target);
+ m_read_index = target / 4;
+ break;
+
+ case 4: // JMP AGP
+ if (LOG_CMDFIFO)
+ m_device.logerror(" JMP AGP $%06X\n", target);
+ m_device.logerror("cmdFifo: Unsupported JMP AGP");
+ break;
+
+ default:
+ m_device.logerror("cmdFifo: Invalid jump command %d", BIT(command, 3, 3));
+ break;
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// packet_type_1 - handle FIFO packet type 1
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_1(u32 command)
+{
+ // Packet type 1: 1 + N words
+ //
+ // Word Bits
+ // 0 31:16 = Number of words
+ // 0 15 = Increment?
+ // 0 14:3 = Register base
+ // 0 2:0 = Packet type (1)
+ // 1 31:0 = Data word
+ u32 count = BIT(command, 16, 16);
+ u32 inc = BIT(command, 15);
+ u32 target = BIT(command, 3, 12);
+
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 1: count=%d inc=%d reg=%04X\n", count, inc, target);
+
+ // loop over all registers and write them one at a time
+ u32 cycles = 0;
+ for (u32 regbit = 0; regbit < count; regbit++, target += inc)
+ cycles += m_device.cmdfifo_register_w(target, read_next());
+ return cycles;
+}
+
+
+//-------------------------------------------------
+// packet_type_2 - handle FIFO packet type 2
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_2(u32 command)
+{
+ // Packet type 2: 1 + N words
+ //
+ // Word Bits
+ // 0 31:3 = 2D Register mask
+ // 0 2:0 = Packet type (2)
+ // 1 31:0 = Data word
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 2: mask=%X\n", BIT(command, 3, 29));
+
+ // loop over all registers and write them one at a time
+ u32 cycles = 0;
+ for (u32 regbit = 3; regbit <= 31; regbit++)
+ if (BIT(command, regbit))
+ cycles += m_device.cmdfifo_2d_w(regbit - 3, read_next());
+ return cycles;
+}
+
+
+//-------------------------------------------------
+// packet_type_3 - handle FIFO packet type 3
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_3(u32 command)
+{
+ // Packet type 3: 1 + N words
+ //
+ // Word Bits
+ // 0 31:29 = Number of dummy entries following the data
+ // 0 28 = Packed color data?
+ // 0 25 = Disable ping pong sign correction (0=normal, 1=disable)
+ // 0 24 = Culling sign (0=positive, 1=negative)
+ // 0 23 = Enable culling (0=disable, 1=enable)
+ // 0 22 = Strip mode (0=strip, 1=fan)
+ // 0 17 = Setup S1 and T1
+ // 0 16 = Setup W1
+ // 0 15 = Setup S0 and T0
+ // 0 14 = Setup W0
+ // 0 13 = Setup Wb
+ // 0 12 = Setup Z
+ // 0 11 = Setup Alpha
+ // 0 10 = Setup RGB
+ // 0 9:6 = Number of vertices
+ // 0 5:3 = Command (0=Independent tris, 1=Start new strip, 2=Continue strip)
+ // 0 2:0 = Packet type (3)
+ // 1 31:0 = Data word
+ u32 count = BIT(command, 6, 4);
+ u32 code = BIT(command, 3, 3);
+
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 3: count=%d code=%d mask=%03X smode=%02X pc=%d\n", count, code, BIT(command, 10, 12), BIT(command, 22, 6), BIT(command, 28));
+
+ // copy relevant bits into the setup mode register
+ m_device.m_reg.write(voodoo_regs::reg_sSetupMode, BIT(command, 10, 8) | (BIT(command, 22, 4) << 16));
+
+ // loop over triangles
+ setup_vertex svert = { 0 };
+ u32 cycles = 0;
+ for (u32 trinum = 0; trinum < count; trinum++)
+ {
+ // always extract X/Y
+ svert.x = read_next_float();
+ svert.y = read_next_float();
+
+ // load ARGB values
+ if (BIT(command, 28))
+ {
+ // packed form
+ if (BIT(command, 10, 2) != 0)
+ {
+ rgb_t argb = read_next();
+ if (BIT(command, 10))
+ {
+ svert.r = argb.r();
+ svert.g = argb.g();
+ svert.b = argb.b();
+ }
+ if (BIT(command, 11))
+ svert.a = argb.a();
+ }
+ }
+ else
+ {
+ // unpacked form
+ if (BIT(command, 10))
+ {
+ svert.r = read_next_float();
+ svert.g = read_next_float();
+ svert.b = read_next_float();
+ }
+ if (BIT(command, 11))
+ svert.a = read_next_float();
+ }
+
+ // load Z and Wb values
+ if (BIT(command, 12))
+ svert.z = read_next_float();
+ if (BIT(command, 13))
+ svert.wb = svert.w0 = svert.w1 = read_next_float();
+
+ // load W0, S0, T0 values
+ if (BIT(command, 14))
+ svert.w0 = svert.w1 = read_next_float();
+ if (BIT(command, 15))
+ {
+ svert.s0 = svert.s1 = read_next_float();
+ svert.t0 = svert.t1 = read_next_float();
+ }
+
+ // load W1, S1, T1 values
+ if (BIT(command, 16))
+ svert.w1 = read_next_float();
+ if (BIT(command, 17))
+ {
+ svert.s1 = read_next_float();
+ svert.t1 = read_next_float();
+ }
+
+ // if we're starting a new strip, or if this is the first of a set of verts
+ // for a series of individual triangles, initialize all the verts
+ if ((code == 1 && trinum == 0) || (code == 0 && trinum % 3 == 0))
+ {
+ m_device.m_sverts = 1;
+ m_device.m_svert[0] = m_device.m_svert[1] = m_device.m_svert[2] = svert;
+ }
+
+ // otherwise, add this to the list
+ else
+ {
+ // for strip mode, shuffle vertex 1 down to 0
+ if (!BIT(command, 22))
+ m_device.m_svert[0] = m_device.m_svert[1];
+
+ // copy 2 down to 1 and add our new one regardless
+ m_device.m_svert[1] = m_device.m_svert[2];
+ m_device.m_svert[2] = svert;
+
+ // if we have enough, draw
+ if (++m_device.m_sverts >= 3)
+ cycles += m_device.setup_and_draw_triangle();
+ }
+ }
+
+ // account for the extra dummy words
+ consume(BIT(command, 29, 3));
+ return cycles;
+}
+
+
+//-------------------------------------------------
+// packet_type_4 - handle FIFO packet type 4
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_4(u32 command)
+{
+ // Packet type 4: 1 + N words
+ //
+ // Word Bits
+ // 0 31:29 = Number of dummy entries following the data
+ // 0 28:15 = General register mask
+ // 0 14:3 = Register base
+ // 0 2:0 = Packet type (4)
+ // 1 31:0 = Data word
+ u32 target = BIT(command, 3, 12);
+
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 4: mask=%X reg=%04X pad=%d\n", BIT(command, 15, 14), target, BIT(command, 29, 3));
+
+ // loop over all registers and write them one at a time
+ u32 cycles = 0;
+ for (u32 regbit = 15; regbit <= 28; regbit++, target++)
+ if (BIT(command, regbit))
+ cycles += m_device.cmdfifo_register_w(target, read_next());
+
+ // account for the extra dummy words
+ consume(BIT(command, 29, 3));
+ return cycles;
+}
+
+
+//-------------------------------------------------
+// packet_type_5 - handle FIFO packet type 5
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_5(u32 command)
+{
+ // Packet type 5: 2 + N words
+ //
+ // Word Bits
+ // 0 31:30 = Space (0,1=reserved, 2=LFB, 3=texture)
+ // 0 29:26 = Byte disable W2
+ // 0 25:22 = Byte disable WN
+ // 0 21:3 = Num words
+ // 0 2:0 = Packet type (5)
+ // 1 31:30 = Reserved
+ // 1 29:0 = Base address [24:0]
+ // 2 31:0 = Data word
+ u32 count = BIT(command, 3, 19);
+ u32 target = read_next() / 4;
+
+ // handle LFB writes
+ switch (BIT(command, 30, 2))
+ {
+ // Linear FB
+ case 0:
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 5: FB count=%d dest=%08X bd2=%X bdN=%X\n", count, target, BIT(command, 26, 4), BIT(command, 22, 4));
+
+ m_device.renderer().wait("packet_type_5(0)");
+ for (u32 word = 0; word < count; word++)
+ m_ram[target++ & m_mask] = little_endianize_int32(read_next());
+ break;
+
+ // 3D LFB
+ case 2:
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 5: 3D LFB count=%d dest=%08X bd2=%X bdN=%X\n", count, target, BIT(command, 26, 4), BIT(command, 22, 4));
+
+ for (u32 word = 0; word < count; word++)
+ m_device.internal_lfb_w(target++, read_next(), 0xffffffff);
+ break;
+
+ // Planar YUV
+ case 1:
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 5: Planar YUV count=%d dest=%08X bd2=%X bdN=%X\n", count, target, BIT(command, 26, 4), BIT(command, 22, 4));
+
+ fatalerror("%s: Unsupported planar YUV write via cmdFifo", m_device.tag());
+ break;
+
+ // Texture port
+ case 3:
+ if (LOG_CMDFIFO)
+ m_device.logerror(" PACKET TYPE 5: textureRAM count=%d dest=%08X bd2=%X bdN=%X\n", count, target, BIT(command, 26, 4), BIT(command, 22, 4));
+
+ for (u32 word = 0; word < count; word++)
+ m_device.internal_texture_w(target++, read_next());
+ break;
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// packet_type_unknown - error out on unhandled
+// packets
+//-------------------------------------------------
+
+u32 command_fifo::packet_type_unknown(u32 command)
+{
+ fatalerror("%s: Unsupported cmdFifo packet type %d\n", m_device.tag(), BIT(command, 0, 3));
+}
+
+
+//-------------------------------------------------
+// s_packet_handler - static array of pointers to
+// handler functions
+//-------------------------------------------------
+
+command_fifo::packet_handler command_fifo::s_packet_handler[8] =
+{
+ &command_fifo::packet_type_0,
+ &command_fifo::packet_type_1,
+ &command_fifo::packet_type_2,
+ &command_fifo::packet_type_3,
+ &command_fifo::packet_type_4,
+ &command_fifo::packet_type_5,
+ &command_fifo::packet_type_unknown,
+ &command_fifo::packet_type_unknown
+};
+
+
+
+//**************************************************************************
+// VOODOO 2 DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// voodoo_2_device - constructor
+//-------------------------------------------------
+
+DEFINE_DEVICE_TYPE(VOODOO_2, voodoo_2_device, "voodoo_2", "3dfx Voodoo 2")
+
+voodoo_2_device::voodoo_2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, voodoo_model model) :
+ voodoo_1_device(mconfig, type, tag, owner, clock, model),
+ m_sverts(0),
+ m_cmdfifo(*this)
+{
+ for (int index = 0; index < std::size(m_regtable); index++)
+ m_regtable[index].unpack(s_register_table[index], *this);
+}
+
+
+//-------------------------------------------------
+// core_map - device map for core memory access
+//-------------------------------------------------
+
+void voodoo_2_device::core_map(address_map &map)
+{
+ // Voodoo-2 memory map:
+ //
+ // cmdfifo = fbi_init7().cmdfifo_enable()
+ //
+ // 00ab----`--ccccrr`rrrrrr-- Register access (if cmdfifo == 0)
+ // a = alternate register map if fbi_init3().tri_register_remap()
+ // b = byte swizzle data if fbi_init0().swizzle_reg_writes()
+ // c = chip mask select
+ // r = register index ($00-$FF)
+ // 000-----`------rr`rrrrrr-- Register access (if cmdfifo == 1)
+ // r = register index ($00-$FF)
+ // 001--boo`oooooooo`oooooo-- CMDFifo write (if cmdfifo == 1)
+ // b = byte swizzle data
+ // o = cmdfifo offset
+ // 01-yyyyy`yyyyyxxx`xxxxxxx- Linear frame buffer access (16-bit)
+ // 01yyyyyy`yyyyxxxx`xxxxxx-- Linear frame buffer access (32-bit)
+ // 1-ccllll`tttttttt`sssssss- Texture memory access, where:
+ // c = chip mask select
+ // l = LOD
+ // t = Y index
+ // s = X index
+ //
+ map(0x000000, 0x3fffff).rw(FUNC(voodoo_2_device::map_register_r), FUNC(voodoo_2_device::map_register_w));
+ map(0x400000, 0x7fffff).rw(FUNC(voodoo_2_device::map_lfb_r), FUNC(voodoo_2_device::map_lfb_w));
+ map(0x800000, 0xffffff).w(FUNC(voodoo_2_device::map_texture_w));
+}
+
+
+//-------------------------------------------------
+// read - generic read handler until everyone is
+// using the memory map
+//-------------------------------------------------
+
+u32 voodoo_2_device::read(offs_t offset, u32 mem_mask)
+{
+ switch (offset >> (22-2))
+ {
+ case 0x000000 >> 22:
+ return map_register_r(offset);
+
+ case 0x400000 >> 22:
+ return map_lfb_r(offset - 0x400000/4);
+
+ default:
+ return 0xffffffff;
+ }
+}
+
+
+//-------------------------------------------------
+// write - generic write handler until everyone is
+// using the memory map
+//-------------------------------------------------
+
+void voodoo_2_device::write(offs_t offset, u32 data, u32 mem_mask)
+{
+ switch (offset >> (22-2))
+ {
+ case 0x000000 >> 22:
+ map_register_w(offset, data, mem_mask);
+ break;
+
+ case 0x400000 >> 22:
+ map_lfb_w(offset - 0x400000/4, data, mem_mask);
+ break;
+
+ case 0x800000 >> 22:
+ case 0xc00000 >> 22:
+ map_texture_w(offset - 0x800000/4, data, mem_mask);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device startup
+//-------------------------------------------------
+
+void voodoo_2_device::device_start()
+{
+ // start like a Voodoo-1
+ voodoo_1_device::device_start();
+
+ // fogDelta skips the low 2 bits
+ m_renderer->set_fogdelta_mask(0xfc);
+
+ // bilinear is full resolution
+ m_renderer->set_bilinear_mask(0xff);
+
+ // TMU configuration has an extra bit
+ m_renderer->set_tmu_config(m_renderer->tmu_config() | 0x800);
+
+ // initialize Voodoo 2 additions
+ m_sverts = 0;
+ m_cmdfifo.init(m_fbram, m_fbmask + 1);
+}
+
+
+
+//-------------------------------------------------
+// map_register_w - handle a mapped write to
+// regular register space
+//-------------------------------------------------
+
+void voodoo_2_device::map_register_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ bool pending = prepare_for_write();
+
+ // handle cmdfifo writes
+ if (BIT(offset, 21-2) && m_reg.fbi_init7().cmdfifo_enable())
+ {
+ // check for byte swizzling (bit 18)
+ if (BIT(offset, 18-2))
+ data = swapendian_int32(data);
+ m_cmdfifo.write_direct(BIT(offset, 0, 16), data);
+ return;
+ }
+
+ // extract chipmask and register
+ u32 chipmask = chipmask_from_offset(offset);
+ u32 regnum = BIT(offset, 0, 8);
+
+ // handle register swizzling
+ if (BIT(offset, 20-2) && m_reg.fbi_init0().swizzle_reg_writes())
+ data = swapendian_int32(data);
+
+ // handle aliasing
+ if (BIT(offset, 21-2) && m_reg.fbi_init3().tri_register_remap())
+ regnum = voodoo_regs::alias(regnum);
+
+ // look up the register
+ auto const &regentry = m_regtable[regnum];
+
+ // if this is non-FIFO command, execute immediately
+ if (!regentry.is_fifo())
+ return void(regentry.write(*this, chipmask, regnum, data));
+
+ // track swap buffers
+ if (regnum == voodoo_regs::reg_swapbufferCMD)
+ m_swaps_pending++;
+
+ // if cmdfifo is enabled, ignore everything else
+ if (m_reg.fbi_init7().cmdfifo_enable())
+ {
+ logerror("Ignoring write to %s when CMDFIFO is enabled\n", regentry.name());
+ return;
+ }
+
+ // if we're busy add to the fifo
+ if (pending && m_init_enable.enable_pci_fifo())
+ return add_to_fifo(memory_fifo::TYPE_REGISTER | (chipmask << 8) | regnum, data, mem_mask);
+
+ // if we get a non-zero number of cycles back, mark things pending
+ int cycles = regentry.write(*this, chipmask, regnum, data);
+ if (cycles > 0)
+ {
+ m_operation_end = machine().time() + clocks_to_attotime(cycles);
+ if (LOG_FIFO_VERBOSE)
+ logerror("VOODOO.FIFO:direct write start at %s end at %s\n", machine().time().as_string(18), m_operation_end.as_string(18));
+ }
+}
+
+
+//-------------------------------------------------
+// soft_reset - handle reset when initiated by
+// a register write
+//-------------------------------------------------
+
+void voodoo_2_device::soft_reset()
+{
+ voodoo_1_device::soft_reset();
+ m_cmdfifo.set_enable(0);
+}
+
+
+//-------------------------------------------------
+// register_save - register for save states
+//-------------------------------------------------
+
+void voodoo_2_device::register_save(save_proxy &save, u32 total_allocation)
+{
+ voodoo_1_device::register_save(save, total_allocation);
+
+ // Voodoo 2 stuff
+ save.save_item(NAME(m_sverts));
+ save.save_class(NAME(m_svert[0]));
+ save.save_class(NAME(m_svert[1]));
+ save.save_class(NAME(m_svert[2]));
+ save.save_class(NAME(m_cmdfifo));
+}
+
+
+//-------------------------------------------------
+// execute_fifos - execute commands from the FIFOs
+// until a non-zero cycle count operation is run
+//-------------------------------------------------
+
+u32 voodoo_2_device::execute_fifos()
+{
+ // we might be in CMDFIFO mode
+ if (m_cmdfifo.enabled())
+ return m_cmdfifo.execute_if_ready();
+
+ // otherwise, run the traditional memory FIFOs
+ return voodoo_1_device::execute_fifos();
+}
+
+
+//-------------------------------------------------
+// reg_hvretrace_r - hvRetrace register read
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_hvretrace_r(u32 chipmask, u32 regnum)
+{
+ // return 0 for vertical if vblank is active
+ u32 result = m_vblank ? 0 : screen().vpos();
+ return result |= screen().hpos() << 16;
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifoptr_r - cmdFifoRdPtr register read
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifoptr_r(u32 chipmask, u32 regnum)
+{
+ return m_cmdfifo.read_pointer();
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifodepth_r - cmdFifoDepth register read
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifodepth_r(u32 chipmask, u32 regnum)
+{
+ return m_cmdfifo.depth();
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifoholes_r - cmdFifoHoles register read
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifoholes_r(u32 chipmask, u32 regnum)
+{
+ return m_cmdfifo.holes();
+}
+
+
+//-------------------------------------------------
+// reg_intrctrl_w - intrCtrl register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_intrctrl_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_reg.write(regnum, data);
+
+ // Setting bit 31 clears the PCI interrupts
+ if (BIT(data, 31))
+ m_pciint_cb(false);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_video2_w -- write to a video configuration
+// register; synchronize then recompute everything
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_video2_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_renderer->wait("reg_video2_w");
+ m_reg.write(regnum, data);
+
+ auto const hsync = m_reg.hsync<false>();
+ auto const vsync = m_reg.vsync<false>();
+ auto const back_porch = m_reg.back_porch<false>();
+ auto const video_dimensions = m_reg.video_dimensions<false>();
+ if (hsync.raw() != 0 && vsync.raw() != 0 && video_dimensions.raw() != 0 && back_porch.raw() != 0)
+ {
+ recompute_video_timing(
+ hsync.hsync_on(), hsync.hsync_off(),
+ video_dimensions.xwidth(), back_porch.horizontal() + 2,
+ vsync.vsync_on(), vsync.vsync_off(),
+ video_dimensions.yheight(), back_porch.vertical());
+ }
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_sargb_w -- sARGB register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_sargb_w(u32 chipmask, u32 regnum, u32 data)
+{
+ rgb_t rgbdata(data);
+
+ // expand ARGB values into their float registers
+ m_reg.write_float(voodoo_regs::reg_sAlpha, float(rgbdata.a()));
+ m_reg.write_float(voodoo_regs::reg_sRed, float(rgbdata.r()));
+ m_reg.write_float(voodoo_regs::reg_sGreen, float(rgbdata.g()));
+ m_reg.write_float(voodoo_regs::reg_sBlue, float(rgbdata.b()));
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_userintr_w -- userIntr register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_userintr_w(u32 chipmask, u32 regnum, u32 data)
+{
+ m_renderer->wait("reg_userintr_w");
+
+ // Bit 5 of intrCtrl enables user interrupts
+ if (m_reg.intr_ctrl().user_interrupt_enable())
+ {
+ // Bits 19:12 are set to cmd 9:2, bit 11 is user interrupt flag
+ m_reg.clear_set(voodoo_regs::reg_intrCtrl,
+ reg_intr_ctrl::EXTERNAL_PIN_ACTIVE | reg_intr_ctrl::USER_INTERRUPT_TAG_MASK,
+ ((data << 10) & reg_intr_ctrl::USER_INTERRUPT_TAG_MASK) | reg_intr_ctrl::USER_INTERRUPT_GENERATED);
+
+ // Signal PCI interrupt handler
+ m_pciint_cb(true);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifo_w -- general cmdFifo-related
+// register writes
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifo_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_renderer->wait("reg_cmdfifo_w");
+ m_reg.write(regnum, data);
+ m_cmdfifo.set_base(BIT(m_reg.read(voodoo_regs::reg_cmdFifoBaseAddr), 0, 10) << 12);
+ m_cmdfifo.set_end((BIT(m_reg.read(voodoo_regs::reg_cmdFifoBaseAddr), 16, 10) + 1) << 12);
+ m_cmdfifo.set_address_min(m_reg.read(voodoo_regs::reg_cmdFifoAMin));
+ m_cmdfifo.set_address_max(m_reg.read(voodoo_regs::reg_cmdFifoAMax));
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifoptr_w -- cmdFifoRdPtr register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifoptr_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_reg.write(regnum, data);
+ m_cmdfifo.set_read_pointer(data);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifodepth_w -- cmdFifoDepth register
+// write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifodepth_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_reg.write(regnum, data);
+ m_cmdfifo.set_depth(data);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_cmdfifoholes_w -- cmdFifoHoles register
+// write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_cmdfifoholes_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0))
+ {
+ m_reg.write(regnum, data);
+ m_cmdfifo.set_holes(data);
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_fbiinit5_7_w -- fbiInit5/6/7 register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_fbiinit5_7_w(u32 chipmask, u32 regnum, u32 data)
+{
+ if (BIT(chipmask, 0) && m_init_enable.enable_hw_init())
+ {
+ u32 delta = m_reg.read(regnum) ^ data;
+ m_reg.write(regnum, data);
+
+ // a few bits affect video memory configuration
+ if ((regnum == voodoo_regs::reg_fbiInit5 && BIT(delta, 9, 2) != 0) ||
+ (regnum == voodoo_regs::reg_fbiInit6 && BIT(delta, 30, 1) != 0))
+ {
+ m_renderer->wait("reg_fbiinit5_7_w");
+ recompute_video_memory();
+ }
+ m_cmdfifo.set_enable(m_reg.fbi_init7().cmdfifo_enable());
+ m_cmdfifo.set_count_holes(!m_reg.fbi_init7().disable_cmdfifo_holes());
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// reg_draw_tri_w -- sDrawTri register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_draw_tri_w(u32 chipmask, u32 regnum, u32 data)
+{
+ return draw_triangle();
+}
+
+
+//-------------------------------------------------
+// reg_begin_tri_w -- sBeginTri register write
+//-------------------------------------------------
+
+u32 voodoo_2_device::reg_begin_tri_w(u32 chipmask, u32 regnum, u32 data)
+{
+ return begin_triangle();
+}
+
+
+//-------------------------------------------------
+// cmdfifo_register_w -- handle a register write
+// from the cmdfifo
+//-------------------------------------------------
+
+u32 voodoo_2_device::cmdfifo_register_w(u32 offset, u32 data)
+{
+ u32 chipmask = chipmask_from_offset(offset);
+ u32 regnum = BIT(offset, 0, 8);
+ return m_regtable[regnum].write(*this, chipmask, regnum, data);
+}
+
+
+//-------------------------------------------------
+// cmdfifo_2d_w -- handle a 2D register write
+// from the cmdfifo
+//-------------------------------------------------
+
+u32 voodoo_2_device::cmdfifo_2d_w(u32 offset, u32 data)
+{
+ u32 regnum = voodoo_regs::reg_bltSrcBaseAddr + offset;
+ return m_regtable[regnum].write(*this, 0x1, regnum, data);
+}
+
+
+//-------------------------------------------------
+// vblank_start -- timer callback for the start
+// of VBLANK
+//-------------------------------------------------
+
+void voodoo_2_device::vblank_start(s32 param)
+{
+ voodoo_1_device::vblank_start(param);
+
+ // signal PCI VBLANK rising IRQ on Voodoo-2 and later
+ if (m_reg.intr_ctrl().vsync_rising_enable())
+ {
+ m_reg.clear_set(voodoo_regs::reg_intrCtrl, reg_intr_ctrl::EXTERNAL_PIN_ACTIVE, reg_intr_ctrl::VSYNC_RISING_GENERATED);
+ m_pciint_cb(true);
+ }
+}
+
+
+//-------------------------------------------------
+// vblank_stop -- timer callback for the end of
+// VBLANK
+//-------------------------------------------------
+
+void voodoo_2_device::vblank_stop(s32 param)
+{
+ voodoo_1_device::vblank_stop(param);
+
+ // signal PCI VBLANK falling IRQ on Voodoo-2 and later
+ if (m_reg.intr_ctrl().vsync_falling_enable())
+ {
+ m_reg.clear_set(voodoo_regs::reg_intrCtrl, reg_intr_ctrl::EXTERNAL_PIN_ACTIVE, reg_intr_ctrl::VSYNC_FALLING_GENERATED);
+ m_pciint_cb(true);
+ }
+}
+
+
+//-------------------------------------------------
+// recompute_video_memory -- compute the layout
+// of video memory
+//-------------------------------------------------
+
+void voodoo_2_device::recompute_video_memory()
+{
+ // for backwards compatibility, the triple-buffered bit is still supported
+ u32 config = m_reg.fbi_init2().enable_triple_buf();
+
+ // but if left at 0, configuration comes from fbiInit5 instead
+ if (config == 0)
+ config = m_reg.fbi_init5().buffer_allocation();
+
+ // 6-bit tile count is assembled from various bits; tiles are 32x32
+ u32 xtiles = m_reg.fbi_init6().x_video_tiles_bit0() |
+ (m_reg.fbi_init1().x_video_tiles() << 1) |
+ (m_reg.fbi_init1().x_video_tiles_bit5() << 5);
+ recompute_video_memory_common(config, xtiles * 32);
+}
+
+
+//-------------------------------------------------
+// begin_triangle - execute the 'beginTri'
+// command
+//-------------------------------------------------
+
+s32 voodoo_2_device::begin_triangle()
+{
+ // extract setup data
+ auto &sv = m_svert[2];
+ sv.x = m_reg.read_float(voodoo_regs::reg_sVx);
+ sv.y = m_reg.read_float(voodoo_regs::reg_sVy);
+ sv.wb = m_reg.read_float(voodoo_regs::reg_sWb);
+ sv.w0 = m_reg.read_float(voodoo_regs::reg_sWtmu0);
+ sv.s0 = m_reg.read_float(voodoo_regs::reg_sS_W0);
+ sv.t0 = m_reg.read_float(voodoo_regs::reg_sT_W0);
+ sv.w1 = m_reg.read_float(voodoo_regs::reg_sWtmu1);
+ sv.s1 = m_reg.read_float(voodoo_regs::reg_sS_Wtmu1);
+ sv.t1 = m_reg.read_float(voodoo_regs::reg_sT_Wtmu1);
+ sv.a = m_reg.read_float(voodoo_regs::reg_sAlpha);
+ sv.r = m_reg.read_float(voodoo_regs::reg_sRed);
+ sv.g = m_reg.read_float(voodoo_regs::reg_sGreen);
+ sv.b = m_reg.read_float(voodoo_regs::reg_sBlue);
+
+ // spread it across all three verts and reset the count
+ m_svert[0] = m_svert[1] = sv;
+ m_sverts = 1;
+ return 0;
+}
+
+
+//-------------------------------------------------
+// draw_triangle - execute the 'DrawTri'
+// command
+//-------------------------------------------------
+
+s32 voodoo_2_device::draw_triangle()
+{
+ // for strip mode, shuffle vertex 1 down to 0
+ if (!m_reg.setup_mode().fan_mode())
+ m_svert[0] = m_svert[1];
+
+ // copy 2 down to 1 regardless
+ m_svert[1] = m_svert[2];
+
+ // extract setup data
+ auto &sv = m_svert[2];
+ sv.x = m_reg.read_float(voodoo_regs::reg_sVx);
+ sv.y = m_reg.read_float(voodoo_regs::reg_sVy);
+ sv.wb = m_reg.read_float(voodoo_regs::reg_sWb);
+ sv.w0 = m_reg.read_float(voodoo_regs::reg_sWtmu0);
+ sv.s0 = m_reg.read_float(voodoo_regs::reg_sS_W0);
+ sv.t0 = m_reg.read_float(voodoo_regs::reg_sT_W0);
+ sv.w1 = m_reg.read_float(voodoo_regs::reg_sWtmu1);
+ sv.s1 = m_reg.read_float(voodoo_regs::reg_sS_Wtmu1);
+ sv.t1 = m_reg.read_float(voodoo_regs::reg_sT_Wtmu1);
+ sv.a = m_reg.read_float(voodoo_regs::reg_sAlpha);
+ sv.r = m_reg.read_float(voodoo_regs::reg_sRed);
+ sv.g = m_reg.read_float(voodoo_regs::reg_sGreen);
+ sv.b = m_reg.read_float(voodoo_regs::reg_sBlue);
+
+ // if we have enough verts, go ahead and draw
+ int cycles = 0;
+ if (++m_sverts >= 3)
+ cycles = setup_and_draw_triangle();
+ return cycles;
+}
+
+
+//-------------------------------------------------
+// setup_and_draw_triangle - process the setup
+// parameters and render the triangle
+//-------------------------------------------------
+
+s32 voodoo_2_device::setup_and_draw_triangle()
+{
+ auto &sv0 = m_svert[0];
+ auto &sv1 = m_svert[1];
+ auto &sv2 = m_svert[2];
+
+ // compute the divisor, but we only need to know the sign up front
+ // for backface culling
+ float divisor = (sv0.x - sv1.x) * (sv0.y - sv2.y) - (sv0.x - sv2.x) * (sv0.y - sv1.y);
+
+ // backface culling
+ auto const setup_mode = m_reg.setup_mode();
+ if (setup_mode.enable_culling())
+ {
+ int culling_sign = setup_mode.culling_sign();
+ int divisor_sign = (divisor < 0);
+
+ // if doing strips and ping pong is enabled, apply the ping pong
+ if (!setup_mode.fan_mode() && !setup_mode.disable_ping_pong_correction())
+ culling_sign ^= (m_sverts - 3) & 1;
+
+ // if our sign matches the culling sign, we're done for
+ if (divisor_sign == culling_sign)
+ return TRIANGLE_SETUP_CLOCKS;
+ }
+
+ // compute the reciprocal now that we know we need it
+ divisor = 1.0f / divisor;
+
+ // grab the X/Ys at least
+ m_reg.write(voodoo_regs::reg_vertexAx, s16(sv0.x * 16.0f));
+ m_reg.write(voodoo_regs::reg_vertexAy, s16(sv0.y * 16.0f));
+ m_reg.write(voodoo_regs::reg_vertexBx, s16(sv1.x * 16.0f));
+ m_reg.write(voodoo_regs::reg_vertexBy, s16(sv1.y * 16.0f));
+ m_reg.write(voodoo_regs::reg_vertexCx, s16(sv2.x * 16.0f));
+ m_reg.write(voodoo_regs::reg_vertexCy, s16(sv2.y * 16.0f));
+
+ // compute the dx/dy values
+ float dx1 = sv0.y - sv2.y;
+ float dx2 = sv0.y - sv1.y;
+ float dy1 = sv0.x - sv1.x;
+ float dy2 = sv0.x - sv2.x;
+
+ // set up R,G,B
+ float const argbzscale = 4096.0f;
+ float const argbzdiv = argbzscale * divisor;
+ if (setup_mode.setup_rgb())
+ {
+ m_reg.write(voodoo_regs::reg_startR, s32(sv0.r * argbzscale));
+ m_reg.write(voodoo_regs::reg_dRdX, s32(((sv0.r - sv1.r) * dx1 - (sv0.r - sv2.r) * dx2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_dRdY, s32(((sv0.r - sv2.r) * dy1 - (sv0.r - sv1.r) * dy2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_startG, s32(sv0.g * argbzscale));
+ m_reg.write(voodoo_regs::reg_dGdX, s32(((sv0.g - sv1.g) * dx1 - (sv0.g - sv2.g) * dx2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_dGdY, s32(((sv0.g - sv2.g) * dy1 - (sv0.g - sv1.g) * dy2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_startB, s32(sv0.b * argbzscale));
+ m_reg.write(voodoo_regs::reg_dBdX, s32(((sv0.b - sv1.b) * dx1 - (sv0.b - sv2.b) * dx2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_dBdY, s32(((sv0.b - sv2.b) * dy1 - (sv0.b - sv1.b) * dy2) * argbzdiv));
+ }
+
+ // set up alpha
+ if (setup_mode.setup_alpha())
+ {
+ m_reg.write(voodoo_regs::reg_startA, s32(sv0.a * argbzscale));
+ m_reg.write(voodoo_regs::reg_dAdX, s32(((sv0.a - sv1.a) * dx1 - (sv0.a - sv2.a) * dx2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_dAdY, s32(((sv0.a - sv2.a) * dy1 - (sv0.a - sv1.a) * dy2) * argbzdiv));
+ }
+
+ // set up Z
+ if (setup_mode.setup_z())
+ {
+ m_reg.write(voodoo_regs::reg_startZ, s32(sv0.z * argbzscale));
+ m_reg.write(voodoo_regs::reg_dZdX, s32(((sv0.z - sv1.z) * dx1 - (sv0.z - sv2.z) * dx2) * argbzdiv));
+ m_reg.write(voodoo_regs::reg_dZdY, s32(((sv0.z - sv2.z) * dy1 - (sv0.z - sv1.z) * dy2) * argbzdiv));
+ }
+
+ // set up Wb
+ float const wscale = 65536.0f * 65536.0f;
+ float const wdiv = wscale * divisor;
+ auto &tmu0reg = m_tmu[0].regs();
+ auto &tmu1reg = m_tmu[1].regs();
+ if (setup_mode.setup_wb())
+ {
+ s64 startw = s64(sv0.wb * wscale);
+ s64 dwdx = s64(((sv0.wb - sv1.wb) * dx1 - (sv0.wb - sv2.wb) * dx2) * wdiv);
+ s64 dwdy = s64(((sv0.wb - sv2.wb) * dy1 - (sv0.wb - sv1.wb) * dy2) * wdiv);
+ m_reg.write_start_w(startw);
+ m_reg.write_dw_dx(dwdx);
+ m_reg.write_dw_dy(dwdy);
+ tmu0reg.write_start_w(startw);
+ tmu0reg.write_dw_dx(dwdx);
+ tmu0reg.write_dw_dy(dwdy);
+ tmu1reg.write_start_w(startw);
+ tmu1reg.write_dw_dx(dwdx);
+ tmu1reg.write_dw_dy(dwdy);
+ }
+
+ // set up W0
+ if (setup_mode.setup_w0())
+ {
+ s64 startw = s64(sv0.w0 * wscale);
+ s64 dwdx = s64(((sv0.w0 - sv1.w0) * dx1 - (sv0.w0 - sv2.w0) * dx2) * wdiv);
+ s64 dwdy = s64(((sv0.w0 - sv2.w0) * dy1 - (sv0.w0 - sv1.w0) * dy2) * wdiv);
+ tmu0reg.write_start_w(startw);
+ tmu0reg.write_dw_dx(dwdx);
+ tmu0reg.write_dw_dy(dwdy);
+ tmu1reg.write_start_w(startw);
+ tmu1reg.write_dw_dx(dwdx);
+ tmu1reg.write_dw_dy(dwdy);
+ }
+
+ // set up S0,T0
+ float const stscale = 65536.0f * 65536.0f;
+ float const stdiv = stscale * divisor;
+ if (setup_mode.setup_st0())
+ {
+ s64 starts = s64(sv0.s0 * stscale);
+ s64 dsdx = s64(((sv0.s0 - sv1.s0) * dx1 - (sv0.s0 - sv2.s0) * dx2) * stdiv);
+ s64 dsdy = s64(((sv0.s0 - sv2.s0) * dy1 - (sv0.s0 - sv1.s0) * dy2) * stdiv);
+ s64 startt = s64(sv0.t0 * stscale);
+ s64 dtdx = s64(((sv0.t0 - sv1.t0) * dx1 - (sv0.t0 - sv2.t0) * dx2) * stdiv);
+ s64 dtdy = s64(((sv0.t0 - sv2.t0) * dy1 - (sv0.t0 - sv1.t0) * dy2) * stdiv);
+ tmu0reg.write_start_s(starts);
+ tmu0reg.write_start_t(startt);
+ tmu0reg.write_ds_dx(dsdx);
+ tmu0reg.write_dt_dx(dtdx);
+ tmu0reg.write_ds_dy(dsdy);
+ tmu0reg.write_dt_dy(dtdy);
+ tmu1reg.write_start_s(starts);
+ tmu1reg.write_start_t(startt);
+ tmu1reg.write_ds_dx(dsdx);
+ tmu1reg.write_dt_dx(dtdx);
+ tmu1reg.write_ds_dy(dsdy);
+ tmu1reg.write_dt_dy(dtdy);
+ }
+
+ // set up W1
+ if (setup_mode.setup_w1())
+ {
+ s64 startw = s64(sv0.w1 * wscale);
+ s64 dwdx = s64(((sv0.w1 - sv1.w1) * dx1 - (sv0.w1 - sv2.w1) * dx2) * wdiv);
+ s64 dwdy = s64(((sv0.w1 - sv2.w1) * dy1 - (sv0.w1 - sv1.w1) * dy2) * wdiv);
+ tmu1reg.write_start_w(startw);
+ tmu1reg.write_dw_dx(dwdx);
+ tmu1reg.write_dw_dy(dwdy);
+ }
+
+ // set up S1,T1
+ if (setup_mode.setup_st1())
+ {
+ s64 starts = s64(sv0.s1 * stscale);
+ s64 dsdx = s64(((sv0.s1 - sv1.s1) * dx1 - (sv0.s1 - sv2.s1) * dx2) * stdiv);
+ s64 dsdy = s64(((sv0.s1 - sv2.s1) * dy1 - (sv0.s1 - sv1.s1) * dy2) * stdiv);
+ s64 startt = s64(sv0.t1 * stscale);
+ s64 dtdx = s64(((sv0.t1 - sv1.t1) * dx1 - (sv0.t1 - sv2.t1) * dx2) * stdiv);
+ s64 dtdy = s64(((sv0.t1 - sv2.t1) * dy1 - (sv0.t1 - sv1.t1) * dy2) * stdiv);
+ tmu1reg.write_start_s(starts);
+ tmu1reg.write_start_t(startt);
+ tmu1reg.write_ds_dx(dsdx);
+ tmu1reg.write_dt_dx(dtdx);
+ tmu1reg.write_ds_dy(dsdy);
+ tmu1reg.write_dt_dy(dtdy);
+ }
+
+ // draw the triangle
+ return triangle();
+}
+
+
+//**************************************************************************
+// VOODOO 2 REGISTER MAP
+//**************************************************************************
+
+#define REGISTER_ENTRY(name, reader, writer, bits, chips, sync, fifo) \
+ { static_register_table_entry<voodoo_2_device>::make_mask(bits), register_table_entry::CHIPMASK_##chips | register_table_entry::SYNC_##sync | register_table_entry::FIFO_##fifo, #name, &voodoo_2_device::reg_##writer##_w, &voodoo_2_device::reg_##reader##_r },
+
+#define RESERVED_ENTRY REGISTER_ENTRY(reserved, invalid, invalid, 32, FBI, NOSYNC, FIFO)
+
+#define RESERVED_ENTRY_x8 RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY RESERVED_ENTRY
+
+static_register_table_entry<voodoo_2_device> const voodoo_2_device::s_register_table[256] =
+{
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(status, status, invalid, 32, FBI, NOSYNC, FIFO) // 000
+ REGISTER_ENTRY(intrCtrl, passive, intrctrl, 32, FBI, NOSYNC, NOFIFO) // 004 - cmdFIFO mode
+ REGISTER_ENTRY(vertexAx, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 008
+ REGISTER_ENTRY(vertexAy, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 00c
+ REGISTER_ENTRY(vertexBx, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 010
+ REGISTER_ENTRY(vertexBy, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 014
+ REGISTER_ENTRY(vertexCx, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 018
+ REGISTER_ENTRY(vertexCy, invalid, passive, 16, FBI_TREX, NOSYNC, FIFO) // 01c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(startR, invalid, passive, 24, FBI, NOSYNC, FIFO) // 020
+ REGISTER_ENTRY(startG, invalid, passive, 24, FBI, NOSYNC, FIFO) // 024
+ REGISTER_ENTRY(startB, invalid, passive, 24, FBI, NOSYNC, FIFO) // 028
+ REGISTER_ENTRY(startZ, invalid, passive, 32, FBI, NOSYNC, FIFO) // 02c
+ REGISTER_ENTRY(startA, invalid, passive, 24, FBI, NOSYNC, FIFO) // 030
+ REGISTER_ENTRY(startS, invalid, starts, 32, TREX, NOSYNC, FIFO) // 034
+ REGISTER_ENTRY(startT, invalid, startt, 32, TREX, NOSYNC, FIFO) // 038
+ REGISTER_ENTRY(startW, invalid, startw, 32, FBI_TREX, NOSYNC, FIFO) // 03c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(dRdX, invalid, passive, 24, FBI, NOSYNC, FIFO) // 040
+ REGISTER_ENTRY(dGdX, invalid, passive, 24, FBI, NOSYNC, FIFO) // 044
+ REGISTER_ENTRY(dBdX, invalid, passive, 24, FBI, NOSYNC, FIFO) // 048
+ REGISTER_ENTRY(dZdX, invalid, passive, 32, FBI, NOSYNC, FIFO) // 04c
+ REGISTER_ENTRY(dAdX, invalid, passive, 24, FBI, NOSYNC, FIFO) // 050
+ REGISTER_ENTRY(dSdX, invalid, dsdx, 32, TREX, NOSYNC, FIFO) // 054
+ REGISTER_ENTRY(dTdX, invalid, dtdx, 32, TREX, NOSYNC, FIFO) // 058
+ REGISTER_ENTRY(dWdX, invalid, dwdx, 32, FBI_TREX, NOSYNC, FIFO) // 05c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(dRdY, invalid, passive, 24, FBI, NOSYNC, FIFO) // 060
+ REGISTER_ENTRY(dGdY, invalid, passive, 24, FBI, NOSYNC, FIFO) // 064
+ REGISTER_ENTRY(dBdY, invalid, passive, 24, FBI, NOSYNC, FIFO) // 068
+ REGISTER_ENTRY(dZdY, invalid, passive, 32, FBI, NOSYNC, FIFO) // 06c
+ REGISTER_ENTRY(dAdY, invalid, passive, 24, FBI, NOSYNC, FIFO) // 070
+ REGISTER_ENTRY(dSdY, invalid, dsdy, 32, TREX, NOSYNC, FIFO) // 074
+ REGISTER_ENTRY(dTdY, invalid, dtdy, 32, TREX, NOSYNC, FIFO) // 078
+ REGISTER_ENTRY(dWdY, invalid, dwdy, 32, FBI_TREX, NOSYNC, FIFO) // 07c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(triangleCMD, invalid, triangle, 32, FBI_TREX, NOSYNC, FIFO) // 080
+ RESERVED_ENTRY // 084
+ REGISTER_ENTRY(fvertexAx, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 088
+ REGISTER_ENTRY(fvertexAy, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 08c
+ REGISTER_ENTRY(fvertexBx, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 090
+ REGISTER_ENTRY(fvertexBy, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 094
+ REGISTER_ENTRY(fvertexCx, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 098
+ REGISTER_ENTRY(fvertexCy, invalid, fpassive_4, 32, FBI_TREX, NOSYNC, FIFO) // 09c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fstartR, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0a0
+ REGISTER_ENTRY(fstartG, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0a4
+ REGISTER_ENTRY(fstartB, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0a8
+ REGISTER_ENTRY(fstartZ, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0ac
+ REGISTER_ENTRY(fstartA, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0b0
+ REGISTER_ENTRY(fstartS, invalid, fstarts, 32, TREX, NOSYNC, FIFO) // 0b4
+ REGISTER_ENTRY(fstartT, invalid, fstartt, 32, TREX, NOSYNC, FIFO) // 0b8
+ REGISTER_ENTRY(fstartW, invalid, fstartw, 32, FBI_TREX, NOSYNC, FIFO) // 0bc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fdRdX, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0c0
+ REGISTER_ENTRY(fdGdX, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0c4
+ REGISTER_ENTRY(fdBdX, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0c8
+ REGISTER_ENTRY(fdZdX, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0cc
+ REGISTER_ENTRY(fdAdX, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0d0
+ REGISTER_ENTRY(fdSdX, invalid, fdsdx, 32, TREX, NOSYNC, FIFO) // 0d4
+ REGISTER_ENTRY(fdTdX, invalid, fdtdx, 32, TREX, NOSYNC, FIFO) // 0d8
+ REGISTER_ENTRY(fdWdX, invalid, fdwdx, 32, FBI_TREX, NOSYNC, FIFO) // 0dc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fdRdY, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0e0
+ REGISTER_ENTRY(fdGdY, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0e4
+ REGISTER_ENTRY(fdBdY, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0e8
+ REGISTER_ENTRY(fdZdY, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0ec
+ REGISTER_ENTRY(fdAdY, invalid, fpassive_12, 32, FBI, NOSYNC, FIFO) // 0f0
+ REGISTER_ENTRY(fdSdY, invalid, fdsdy, 32, TREX, NOSYNC, FIFO) // 0f4
+ REGISTER_ENTRY(fdTdY, invalid, fdtdy, 32, TREX, NOSYNC, FIFO) // 0f8
+ REGISTER_ENTRY(fdWdY, invalid, fdwdy, 32, FBI_TREX, NOSYNC, FIFO) // 0fc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(ftriangleCMD, invalid, triangle, 32, FBI_TREX, NOSYNC, FIFO) // 100
+ REGISTER_ENTRY(fbzColorPath, passive, passive, 30, FBI_TREX, NOSYNC, FIFO) // 104
+ REGISTER_ENTRY(fogMode, passive, passive, 8, FBI_TREX, NOSYNC, FIFO) // 108
+ REGISTER_ENTRY(alphaMode, passive, passive, 32, FBI_TREX, NOSYNC, FIFO) // 10c
+ REGISTER_ENTRY(fbzMode, passive, passive, 22, FBI_TREX, SYNC, FIFO) // 110
+ REGISTER_ENTRY(lfbMode, passive, passive, 17, FBI_TREX, SYNC, FIFO) // 114
+ REGISTER_ENTRY(clipLeftRight, passive, passive, 28, FBI_TREX, SYNC, FIFO) // 118
+ REGISTER_ENTRY(clipLowYHighY, passive, passive, 28, FBI_TREX, SYNC, FIFO) // 11c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(nopCMD, invalid, nop, 2, FBI_TREX, SYNC, FIFO) // 120
+ REGISTER_ENTRY(fastfillCMD, invalid, fastfill, 0, FBI, SYNC, FIFO) // 124
+ REGISTER_ENTRY(swapbufferCMD, invalid, swapbuffer, 10, FBI, SYNC, FIFO) // 128
+ REGISTER_ENTRY(fogColor, invalid, passive, 24, FBI, SYNC, FIFO) // 12c
+ REGISTER_ENTRY(zaColor, invalid, passive, 32, FBI, SYNC, FIFO) // 130
+ REGISTER_ENTRY(chromaKey, invalid, passive, 24, FBI, SYNC, FIFO) // 134
+ REGISTER_ENTRY(chromaRange, invalid, passive, 29, FBI, SYNC, FIFO) // 138
+ REGISTER_ENTRY(userIntrCMD, invalid, userintr, 10, FBI, SYNC, FIFO) // 13c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(stipple, passive, passive, 32, FBI, SYNC, FIFO) // 140
+ REGISTER_ENTRY(color0, passive, passive, 32, FBI, SYNC, FIFO) // 144
+ REGISTER_ENTRY(color1, passive, passive, 32, FBI, SYNC, FIFO) // 148
+ REGISTER_ENTRY(fbiPixelsIn, stats, invalid, 24, FBI, NA, NA) // 14c
+ REGISTER_ENTRY(fbiChromaFail, stats, invalid, 24, FBI, NA, NA) // 150
+ REGISTER_ENTRY(fbiZfuncFail, stats, invalid, 24, FBI, NA, NA) // 154
+ REGISTER_ENTRY(fbiAfuncFail, stats, invalid, 24, FBI, NA, NA) // 158
+ REGISTER_ENTRY(fbiPixelsOut, stats, invalid, 24, FBI, NA, NA) // 15c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fogTable[0], invalid, fogtable, 32, FBI, SYNC, FIFO) // 160
+ REGISTER_ENTRY(fogTable[1], invalid, fogtable, 32, FBI, SYNC, FIFO) // 164
+ REGISTER_ENTRY(fogTable[2], invalid, fogtable, 32, FBI, SYNC, FIFO) // 168
+ REGISTER_ENTRY(fogTable[3], invalid, fogtable, 32, FBI, SYNC, FIFO) // 16c
+ REGISTER_ENTRY(fogTable[4], invalid, fogtable, 32, FBI, SYNC, FIFO) // 170
+ REGISTER_ENTRY(fogTable[5], invalid, fogtable, 32, FBI, SYNC, FIFO) // 174
+ REGISTER_ENTRY(fogTable[6], invalid, fogtable, 32, FBI, SYNC, FIFO) // 178
+ REGISTER_ENTRY(fogTable[7], invalid, fogtable, 32, FBI, SYNC, FIFO) // 17c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fogTable[8], invalid, fogtable, 32, FBI, SYNC, FIFO) // 180
+ REGISTER_ENTRY(fogTable[9], invalid, fogtable, 32, FBI, SYNC, FIFO) // 184
+ REGISTER_ENTRY(fogTable[10], invalid, fogtable, 32, FBI, SYNC, FIFO) // 188
+ REGISTER_ENTRY(fogTable[11], invalid, fogtable, 32, FBI, SYNC, FIFO) // 18c
+ REGISTER_ENTRY(fogTable[12], invalid, fogtable, 32, FBI, SYNC, FIFO) // 190
+ REGISTER_ENTRY(fogTable[13], invalid, fogtable, 32, FBI, SYNC, FIFO) // 194
+ REGISTER_ENTRY(fogTable[14], invalid, fogtable, 32, FBI, SYNC, FIFO) // 198
+ REGISTER_ENTRY(fogTable[15], invalid, fogtable, 32, FBI, SYNC, FIFO) // 19c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fogTable[16], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1a0
+ REGISTER_ENTRY(fogTable[17], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1a4
+ REGISTER_ENTRY(fogTable[18], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1a8
+ REGISTER_ENTRY(fogTable[19], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1ac
+ REGISTER_ENTRY(fogTable[20], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1b0
+ REGISTER_ENTRY(fogTable[21], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1b4
+ REGISTER_ENTRY(fogTable[22], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1b8
+ REGISTER_ENTRY(fogTable[23], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1bc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fogTable[24], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1c0
+ REGISTER_ENTRY(fogTable[25], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1c4
+ REGISTER_ENTRY(fogTable[26], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1c8
+ REGISTER_ENTRY(fogTable[27], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1cc
+ REGISTER_ENTRY(fogTable[28], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1d0
+ REGISTER_ENTRY(fogTable[29], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1d4
+ REGISTER_ENTRY(fogTable[30], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1d8
+ REGISTER_ENTRY(fogTable[31], invalid, fogtable, 32, FBI, SYNC, FIFO) // 1dc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(cmdFifoBaseAddr, passive, cmdfifo, 26, FBI, SYNC, NOFIFO) // 1e0 - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoBump, passive, unimplemented,16,FBI, SYNC, NOFIFO) // 1e4 - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoRdPtr, cmdfifoptr, cmdfifoptr, 32, FBI, SYNC, NOFIFO) // 1e8 - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoAMin, passive, cmdfifo, 32, FBI, SYNC, NOFIFO) // 1ec - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoAMax, passive, cmdfifo, 32, FBI, SYNC, NOFIFO) // 1f0 - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoDepth, cmdfifodepth,cmdfifodepth,16, FBI, SYNC, NOFIFO) // 1f4 - cmdFIFO mode
+ REGISTER_ENTRY(cmdFifoHoles, cmdfifoholes,cmdfifoholes,16, FBI, SYNC, NOFIFO) // 1f8 - cmdFIFO mode
+ RESERVED_ENTRY // 1fc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(fbiInit4, passive, fbiinit, 32, FBI, NOSYNC, NOFIFO) // 200
+ REGISTER_ENTRY(vRetrace, vretrace, invalid, 13, FBI, NA, NA) // 204
+ REGISTER_ENTRY(backPorch, passive, video2, 25, FBI, NOSYNC, NOFIFO) // 208
+ REGISTER_ENTRY(videoDimensions, passive, video2, 27, FBI, NOSYNC, NOFIFO) // 20c
+ REGISTER_ENTRY(fbiInit0, passive, fbiinit, 31, FBI, NOSYNC, NOFIFO) // 210
+ REGISTER_ENTRY(fbiInit1, passive, fbiinit, 32, FBI, NOSYNC, NOFIFO) // 214
+ REGISTER_ENTRY(fbiInit2, fbiinit2, fbiinit, 32, FBI, NOSYNC, NOFIFO) // 218
+ REGISTER_ENTRY(fbiInit3, passive, fbiinit, 32, FBI, NOSYNC, NOFIFO) // 21c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(hSync, invalid, video2, 27, FBI, NOSYNC, NOFIFO) // 220
+ REGISTER_ENTRY(vSync, invalid, video2, 29, FBI, NOSYNC, NOFIFO) // 224
+ REGISTER_ENTRY(clutData, invalid, clut, 30, FBI, NOSYNC, NOFIFO) // 228
+ REGISTER_ENTRY(dacData, invalid, dac, 14, FBI, NOSYNC, NOFIFO) // 22c
+ REGISTER_ENTRY(maxRgbDelta, invalid, unimplemented,24,FBI, NOSYNC, NOFIFO) // 230
+ REGISTER_ENTRY(hBorder, invalid, unimplemented,25,FBI, NOSYNC, NOFIFO) // 234 - cmdFIFO mode
+ REGISTER_ENTRY(vBorder, invalid, unimplemented,25,FBI, NOSYNC, NOFIFO) // 238 - cmdFIFO mode
+ REGISTER_ENTRY(borderColor, invalid, unimplemented,24,FBI, NOSYNC, NOFIFO) // 23c - cmdFIFO mode
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(hvRetrace, hvretrace, invalid, 27, FBI, NA, NA) // 240
+ REGISTER_ENTRY(fbiInit5, passive, fbiinit5_7, 32, FBI, NOSYNC, NOFIFO) // 244 - cmdFIFO mode
+ REGISTER_ENTRY(fbiInit6, passive, fbiinit5_7, 31, FBI, NOSYNC, NOFIFO) // 248 - cmdFIFO mode
+ REGISTER_ENTRY(fbiInit7, passive, fbiinit5_7, 28, FBI, NOSYNC, NOFIFO) // 24c - cmdFIFO mode
+ RESERVED_ENTRY // 250
+ RESERVED_ENTRY // 254
+ REGISTER_ENTRY(fbiSwapHistory, passive, invalid, 32, FBI, NA, NA) // 258
+ REGISTER_ENTRY(fbiTrianglesOut, passive, invalid, 24, FBI, NA, NA) // 25c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(sSetupMode, invalid, passive, 20, FBI, NOSYNC, FIFO) // 260
+ REGISTER_ENTRY(sVx, invalid, passive, 32, FBI, NOSYNC, FIFO) // 264
+ REGISTER_ENTRY(sVy, invalid, passive, 32, FBI, NOSYNC, FIFO) // 268
+ REGISTER_ENTRY(sARGB, invalid, sargb, 32, FBI, NOSYNC, FIFO) // 26c
+ REGISTER_ENTRY(sRed, invalid, passive, 32, FBI, NOSYNC, FIFO) // 270
+ REGISTER_ENTRY(sGreen, invalid, passive, 32, FBI, NOSYNC, FIFO) // 274
+ REGISTER_ENTRY(sBlue, invalid, passive, 32, FBI, NOSYNC, FIFO) // 278
+ REGISTER_ENTRY(sAlpha, invalid, passive, 32, FBI, NOSYNC, FIFO) // 27c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(sVz, invalid, passive, 32, FBI, NOSYNC, FIFO) // 280
+ REGISTER_ENTRY(sWb, invalid, passive, 32, FBI, NOSYNC, FIFO) // 284
+ REGISTER_ENTRY(sWtmu0, invalid, passive, 32, FBI, NOSYNC, FIFO) // 288
+ REGISTER_ENTRY(sS_W0, invalid, passive, 32, FBI, NOSYNC, FIFO) // 28c
+ REGISTER_ENTRY(sT_W0, invalid, passive, 32, FBI, NOSYNC, FIFO) // 290
+ REGISTER_ENTRY(sWtmu1, invalid, passive, 32, FBI, NOSYNC, FIFO) // 294
+ REGISTER_ENTRY(sS_W1, invalid, passive, 32, FBI, NOSYNC, FIFO) // 298
+ REGISTER_ENTRY(sT_W1, invalid, passive, 32, FBI, NOSYNC, FIFO) // 29c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(sDrawTriCMD, invalid, draw_tri, 1, FBI, NOSYNC, FIFO) // 2a0
+ REGISTER_ENTRY(sBeginTriCMD, invalid, begin_tri, 1, FBI, NOSYNC, FIFO) // 2a4
+ RESERVED_ENTRY // 2a8
+ RESERVED_ENTRY // 2ac
+ RESERVED_ENTRY // 2b0
+ RESERVED_ENTRY // 2b4
+ RESERVED_ENTRY // 2b8
+ RESERVED_ENTRY // 2bc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(bltSrcBaseAddr, passive, passive, 22, FBI, NOSYNC, FIFO) // 2c0
+ REGISTER_ENTRY(bltDstBaseAddr, passive, passive, 22, FBI, NOSYNC, FIFO) // 2c4
+ REGISTER_ENTRY(bltXYStrides, passive, passive, 28, FBI, NOSYNC, FIFO) // 2c8
+ REGISTER_ENTRY(bltSrcChromaRange,passive, passive, 32, FBI, NOSYNC, FIFO) // 2cc
+ REGISTER_ENTRY(bltDstChromaRange,passive, passive, 32, FBI, NOSYNC, FIFO) // 2d0
+ REGISTER_ENTRY(bltClipX, passive, passive, 26, FBI, NOSYNC, FIFO) // 2d4
+ REGISTER_ENTRY(bltClipY, passive, passive, 26, FBI, NOSYNC, FIFO) // 2d8
+ RESERVED_ENTRY // 2dc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(bltSrcXY, passive, passive, 27, FBI, NOSYNC, FIFO) // 2e0
+ REGISTER_ENTRY(bltDstXY, passive, passive, 32, FBI, NOSYNC, FIFO) // 2e4
+ REGISTER_ENTRY(bltSize, passive, passive, 32, FBI, NOSYNC, FIFO) // 2e8
+ REGISTER_ENTRY(bltRop, passive, passive, 16, FBI, NOSYNC, FIFO) // 2ec
+ REGISTER_ENTRY(bltColor, passive, passive, 32, FBI, NOSYNC, FIFO) // 2f0
+ RESERVED_ENTRY // 2f4
+ REGISTER_ENTRY(bltCommand, passive, unimplemented,32,FBI, NOSYNC, FIFO) // 2f8
+ REGISTER_ENTRY(bltData, invalid, passive, 32, FBI, NOSYNC, FIFO) // 2fc
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(textureMode, invalid, texture, 32, TREX, NOSYNC, FIFO) // 300
+ REGISTER_ENTRY(tLOD, invalid, texture, 32, TREX, NOSYNC, FIFO) // 304
+ REGISTER_ENTRY(tDetail, invalid, texture, 22, TREX, NOSYNC, FIFO) // 308
+ REGISTER_ENTRY(texBaseAddr, invalid, texture, 19, TREX, NOSYNC, FIFO) // 30c
+ REGISTER_ENTRY(texBaseAddr_1, invalid, texture, 19, TREX, NOSYNC, FIFO) // 310
+ REGISTER_ENTRY(texBaseAddr_2, invalid, texture, 19, TREX, NOSYNC, FIFO) // 314
+ REGISTER_ENTRY(texBaseAddr_3_8, invalid, texture, 19, TREX, NOSYNC, FIFO) // 318
+ REGISTER_ENTRY(trexInit0, invalid, passive, 32, TREX, SYNC, FIFO) // 31c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(trexInit1, invalid, passive, 32, TREX, SYNC, FIFO) // 320
+ REGISTER_ENTRY(nccTable0[0], invalid, palette, 32, TREX, SYNC, FIFO) // 324
+ REGISTER_ENTRY(nccTable0[1], invalid, palette, 32, TREX, SYNC, FIFO) // 328
+ REGISTER_ENTRY(nccTable0[2], invalid, palette, 32, TREX, SYNC, FIFO) // 32c
+ REGISTER_ENTRY(nccTable0[3], invalid, palette, 32, TREX, SYNC, FIFO) // 330
+ REGISTER_ENTRY(nccTable0[4], invalid, palette, 32, TREX, SYNC, FIFO) // 334
+ REGISTER_ENTRY(nccTable0[5], invalid, palette, 32, TREX, SYNC, FIFO) // 338
+ REGISTER_ENTRY(nccTable0[6], invalid, palette, 32, TREX, SYNC, FIFO) // 33c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(nccTable0[7], invalid, palette, 32, TREX, SYNC, FIFO) // 340
+ REGISTER_ENTRY(nccTable0[8], invalid, palette, 32, TREX, SYNC, FIFO) // 344
+ REGISTER_ENTRY(nccTable0[9], invalid, palette, 32, TREX, SYNC, FIFO) // 348
+ REGISTER_ENTRY(nccTable0[10], invalid, palette, 32, TREX, SYNC, FIFO) // 34c
+ REGISTER_ENTRY(nccTable0[11], invalid, palette, 32, TREX, SYNC, FIFO) // 350
+ REGISTER_ENTRY(nccTable1[0], invalid, palette, 32, TREX, SYNC, FIFO) // 354
+ REGISTER_ENTRY(nccTable1[1], invalid, palette, 32, TREX, SYNC, FIFO) // 358
+ REGISTER_ENTRY(nccTable1[2], invalid, palette, 32, TREX, SYNC, FIFO) // 35c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(nccTable1[3], invalid, palette, 32, TREX, SYNC, FIFO) // 360
+ REGISTER_ENTRY(nccTable1[4], invalid, palette, 32, TREX, SYNC, FIFO) // 364
+ REGISTER_ENTRY(nccTable1[5], invalid, palette, 32, TREX, SYNC, FIFO) // 368
+ REGISTER_ENTRY(nccTable1[6], invalid, palette, 32, TREX, SYNC, FIFO) // 36c
+ REGISTER_ENTRY(nccTable1[7], invalid, palette, 32, TREX, SYNC, FIFO) // 370
+ REGISTER_ENTRY(nccTable1[8], invalid, palette, 32, TREX, SYNC, FIFO) // 374
+ REGISTER_ENTRY(nccTable1[9], invalid, palette, 32, TREX, SYNC, FIFO) // 378
+ REGISTER_ENTRY(nccTable1[10], invalid, palette, 32, TREX, SYNC, FIFO) // 37c
+ // name rd handler wr handler bits chips sync? fifo?
+ REGISTER_ENTRY(nccTable1[11], invalid, palette, 32, TREX, SYNC, FIFO) // 380
+ RESERVED_ENTRY // 384
+ RESERVED_ENTRY // 388
+ RESERVED_ENTRY // 38c
+ RESERVED_ENTRY // 390
+ RESERVED_ENTRY // 394
+ RESERVED_ENTRY // 398
+ RESERVED_ENTRY // 39c
+
+ RESERVED_ENTRY_x8 // 3a0-3bc
+ RESERVED_ENTRY_x8 // 3c0-3dc
+ RESERVED_ENTRY_x8 // 3e0-3fc
+};