// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** voodoo_banshee.cpp 3dfx Voodoo Graphics SST-1/2 emulator. **************************************************************************** Specs: Voodoo Banshee (h3): Integrated VGA support 2,4,8MB frame buffer RAM 90MHz clock frquency clears @ 2 pixels/clock (RGB and depth simultaneously) renders @ 1 pixel/clock ultrafast clears @ 32 pixels/clock Voodoo 3 ("Avenger"/h4): Integrated VGA support 4,8,16MB frame buffer RAM 143MHz clock frquency clears @ 2 pixels/clock (RGB and depth simultaneously) renders @ 1 pixel/clock ultrafast clears @ 32 pixels/clock **************************************************************************/ #include "emu.h" #include "voodoo_banshee.h" using namespace voodoo; //************************************************************************** // INTERNAL CLASSES //************************************************************************** //------------------------------------------------- // register_save - register for save states //------------------------------------------------- void banshee_2d_regs::register_save(save_proxy &save) { save.save_item(NAME(m_regs)); } //------------------------------------------------- // s_names - table of register names //------------------------------------------------- char const *const banshee_2d_regs::s_names[0x20] = { "reserved00", "reserved04", "clip0Min", "clip0Max", "dstBaseAddr", "dstFormat", "srcColorkeyMin", "srcColorkeyMax", "dstColorkeyMin", "dstColorkeyMax", "bresError0", "bresError1", "rop", "srcBaseAddr", "commandExtra", "lineStipple", "lineStyle", "pattern0Alias", "pattern1Alias", "clip1Min", "clip1Max", "srcFormat", "srcSize", "srcXY", "colorBack", "colorFore", "dstSize", "dstXY", "command", "reserved74", "reserved78", "reserved7c" }; //------------------------------------------------- // register_save - register for save states //------------------------------------------------- void banshee_io_regs::register_save(save_proxy &save) { save.save_item(NAME(m_regs)); } //------------------------------------------------- // s_names - table of register names //------------------------------------------------- char const *const banshee_io_regs::s_names[0x40] = { "status", "pciInit0", "sipMonitor", "lfbMemoryConfig", "miscInit0", "miscInit1", "dramInit0", "dramInit1", "agpInit", "tmuGbeInit", "vgaInit0", "vgaInit1", "dramCommand", "dramData", "reserved38", "reserved3c", "pllCtrl0", "pllCtrl1", "pllCtrl2", "dacMode", "dacAddr", "dacData", "rgbMaxDelta", "vidProcCfg", "hwCurPatAddr", "hwCurLoc", "hwCurC0", "hwCurC1", "vidInFormat", "vidInStatus", "vidSerialParallelPort", "vidInXDecimDeltas", "vidInDecimInitErrs", "vidInYDecimDeltas", "vidPixelBufThold", "vidChromaMin", "vidChromaMax", "vidCurrentLine", "vidScreenSize", "vidOverlayStartCoords", "vidOverlayEndScreenCoord", "vidOverlayDudx", "vidOverlayDudxOffsetSrcWidth", "vidOverlayDvdy", "vga[b0]", "vga[b4]", "vga[b8]", "vga[bc]", "vga[c0]", "vga[c4]", "vga[c8]", "vga[cc]", "vga[d0]", "vga[d4]", "vga[d8]", "vga[dc]", "vidOverlayDvdyOffset", "vidDesktopStartAddr", "vidDesktopOverlayStride", "vidInAddr0", "vidInAddr1", "vidInAddr2", "vidInStride", "vidCurrOverlayStartAddr" }; //------------------------------------------------- // register_save - register for save states //------------------------------------------------- void banshee_cmd_agp_regs::register_save(save_proxy &save) { save.save_item(NAME(m_regs)); } //------------------------------------------------- // s_names - table of register names //------------------------------------------------- char const *const banshee_cmd_agp_regs::s_names[0x80] = { "agpReqSize", "agpHostAddressLow", "agpHostAddressHigh", "agpGraphicsAddress", "agpGraphicsStride", "agpMoveCMD", "reserved18", "reserved1c", "cmdBaseAddr0", "cmdBaseSize0", "cmdBump0", "cmdRdPtrL0", "cmdRdPtrH0", "cmdAMin0", "reserved38", "cmdAMax0", "reserved40", "cmdFifoDepth0", "cmdHoleCnt0", "reserved4c", "cmdBaseAddr1", "cmdBaseSize1", "cmdBump1", "cmdRdPtrL1", "cmdRdPtrH1", "cmdAMin1", "reserved68", "cmdAMax1", "reserved70", "cmdFifoDepth1", "cmdHoleCnt1", "reserved7c", "cmdFifoThresh", "cmdHoleInt", "reserved88", "reserved8c", "reserved90", "reserved94", "reserved98", "reserved9c", "reserveda0", "reserveda4", "reserveda8", "reservedac", "reservedb0", "reservedb4", "reservedb8", "reservedbc", "reservedc0", "reservedc4", "reservedc8", "reservedcc", "reservedd0", "reservedd4", "reservedd8", "reserveddc", "reservede0", "reservede4", "reservede8", "reservedec", "reservedf0", "reservedf4", "reservedf8", "reservedfc", "yuvBaseAddress", "yuvStride", "reserved108", "reserved10c", "reserved110", "reserved114", "reserved118", "reserved11c", "crc1", "reserved124", "reserved128", "reserved12c", "crc2", "reserved134", "reserved138", "reserved13c", "reserved140", "reserved144", "reserved148", "reserved14c", "reserved150", "reserved154", "reserved158", "reserved15c", "reserved160", "reserved164", "reserved168", "reserved16c", "reserved170", "reserved174", "reserved178", "reserved17c", "reserved180", "reserved184", "reserved188", "reserved18c", "reserved190", "reserved194", "reserved198", "reserved19c", "reserved1a0", "reserved1a4", "reserved1a8", "reserved1ac", "reserved1b0", "reserved1b4", "reserved1b8", "reserved1bc", "reserved1c0", "reserved1c4", "reserved1c8", "reserved1cc", "reserved1d0", "reserved1d4", "reserved1d8", "reserved1dc", "reserved1e0", "reserved1e4", "reserved1e8", "reserved1ec", "reserved1f0", "reserved1f4", "reserved1f8", "reserved1fc" }; //------------------------------------------------- // register_save - register for save states //------------------------------------------------- void banshee_vga_regs::register_save(save_proxy &save) { save.save_item(NAME(m_regs)); save.save_item(NAME(m_crtc)); save.save_item(NAME(m_seq)); save.save_item(NAME(m_gc)); save.save_item(NAME(m_attr)); save.save_item(NAME(m_attr_flip_flop)); } //************************************************************************** // VOODOO BANSHEE DEVICE //************************************************************************** //------------------------------------------------- // voodoo_banshee_device - constructor //------------------------------------------------- DEFINE_DEVICE_TYPE(VOODOO_BANSHEE, voodoo_banshee_device, "voodoo_banshee", "3dfx Voodoo Banshee") voodoo_banshee_device::voodoo_banshee_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, voodoo_model model) : voodoo_2_device(mconfig, type, tag, owner, clock, model), m_cmdfifo2(*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_banshee_device::core_map(address_map &map) { // Voodoo Banshee/Voodoo 3 memory map: // // 0`00000xxx`xxxxxxxx`xxxxxxxx I/O register remap // 0`00001xxx`xxxxxxxx`xxxxxxxx CMD/AGP transfer/Misc registers // 0`0001xxxx`xxxxxxxx`xxxxxxxx 2D registers // 0`001xxxxx`xxxxxxxx`xxxxxxxx 3D registers // 0`010xxxxx`xxxxxxxx`xxxxxxxx 3D registers (cont'd) // 0`011xxxxx`xxxxxxxx`xxxxxxxx Texture TMU 0 download // 0`100xxxxx`xxxxxxxx`xxxxxxxx Texture TMU 1 download (Voodoo 3 only) // 0`101xxxxx`xxxxxxxx`xxxxxxxx Reserved // 0`11xxxxxx`xxxxxxxx`xxxxxxxx YUV planar space // 1`xxxxxxxx`xxxxxxxx`xxxxxxxx 3D LFB space // map(0x0000000, 0x007ffff).rw(FUNC(voodoo_banshee_device::map_io_r), FUNC(voodoo_banshee_device::map_io_w)); map(0x0080000, 0x00fffff).rw(FUNC(voodoo_banshee_device::map_cmd_agp_r), FUNC(voodoo_banshee_device::map_cmd_agp_w)); map(0x0100000, 0x01fffff).rw(FUNC(voodoo_banshee_device::map_2d_r), FUNC(voodoo_banshee_device::map_2d_w)); map(0x0200000, 0x05fffff).rw(FUNC(voodoo_banshee_device::map_register_r), FUNC(voodoo_banshee_device::map_register_w)); map(0x0600000, 0x07fffff).w(FUNC(voodoo_banshee_device::map_texture_w<0>)); if (BIT(m_chipmask, 2)) map(0x0800000, 0x09fffff).w(FUNC(voodoo_banshee_device::map_texture_w<1>)); map(0x0c00000, 0x0ffffff).w(FUNC(voodoo_banshee_device::map_yuv_w)); map(0x1000000, 0x1ffffff).w(FUNC(voodoo_banshee_device::map_lfb_w)); } //------------------------------------------------- // read - generic read handler until everyone is // using the memory map //------------------------------------------------- u32 voodoo_banshee_device::read(offs_t offset, u32 mem_mask) { switch (offset >> (19-2)) { case 0x0000000 >> 19: return map_io_r(offset - 0x0000000/4, mem_mask); case 0x0080000 >> 19: return map_cmd_agp_r(offset - 0x0080000/4); case 0x0100000 >> 19: case 0x0180000 >> 19: return map_2d_r(offset - 0x0100000/4); case 0x0200000 >> 19: case 0x0280000 >> 19: case 0x0300000 >> 19: case 0x0380000 >> 19: case 0x0400000 >> 19: case 0x0480000 >> 19: case 0x0500000 >> 19: case 0x0580000 >> 19: return map_register_r(offset - 0x0200000/4); default: logerror("%s:voodoo_banshee_device::read Address out of range %08X & %08X\n", machine().describe_context(), offset*4, mem_mask); return 0xffffffff; } } //------------------------------------------------- // write - generic write handler until everyone is // using the memory map //------------------------------------------------- void voodoo_banshee_device::write(offs_t offset, u32 data, u32 mem_mask) { switch (offset >> (19-2)) { case 0x0000000 >> 19: map_io_w(offset - 0x0000000/4, data, mem_mask); break; case 0x0080000 >> 19: map_cmd_agp_w(offset - 0x0080000/4, data, mem_mask); break; case 0x0100000 >> 19: case 0x0180000 >> 19: map_2d_w(offset - 0x0100000/4, data, mem_mask); break; case 0x0200000 >> 19: case 0x0280000 >> 19: case 0x0300000 >> 19: case 0x0380000 >> 19: case 0x0400000 >> 19: case 0x0480000 >> 19: case 0x0500000 >> 19: case 0x0580000 >> 19: map_register_w(offset - 0x0200000/4, data, mem_mask); break; case 0x0600000 >> 19: case 0x0680000 >> 19: case 0x0700000 >> 19: case 0x0780000 >> 19: map_texture_w<0>(offset - 0x0600000, data, mem_mask); break; case 0x0800000 >> 19: case 0x0880000 >> 19: case 0x0900000 >> 19: case 0x0980000 >> 19: if (BIT(m_chipmask, 2)) map_texture_w<1>(offset - 0x0800000, data, mem_mask); break; case 0xc000000 >> 19: case 0xc800000 >> 19: case 0xd000000 >> 19: case 0xd800000 >> 19: case 0xe000000 >> 19: case 0xe800000 >> 19: case 0xf000000 >> 19: case 0xf800000 >> 19: map_yuv_w(offset - 0xc000000/4, data, mem_mask); break; case 0x1000000 >> 19: case 0x1080000 >> 19: case 0x1100000 >> 19: case 0x1180000 >> 19: case 0x1200000 >> 19: case 0x1280000 >> 19: case 0x1300000 >> 19: case 0x1380000 >> 19: case 0x1400000 >> 19: case 0x1480000 >> 19: case 0x1500000 >> 19: case 0x1580000 >> 19: case 0x1600000 >> 19: case 0x1680000 >> 19: case 0x1700000 >> 19: case 0x1780000 >> 19: case 0x1800000 >> 19: case 0x1880000 >> 19: case 0x1900000 >> 19: case 0x1980000 >> 19: case 0x1a00000 >> 19: case 0x1a80000 >> 19: case 0x1b00000 >> 19: case 0x1b80000 >> 19: case 0x1c00000 >> 19: case 0x1c80000 >> 19: case 0x1d00000 >> 19: case 0x1d80000 >> 19: case 0x1e00000 >> 19: case 0x1e80000 >> 19: case 0x1f00000 >> 19: case 0x1f80000 >> 19: map_lfb_w(offset - 0x1000000/4, data, mem_mask); break; default: logerror("%s:voodoo_banshee_device::write Address out of range %08X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask); break; } } //------------------------------------------------- // lfb_map - device map for LFB space //------------------------------------------------- void voodoo_banshee_device::lfb_map(address_map &map) { map(0x0000000, 0x1ffffff).rw(FUNC(voodoo_banshee_device::read_lfb), FUNC(voodoo_banshee_device::write_lfb)); } //------------------------------------------------- // read_lfb - generic LFB space read handler //------------------------------------------------- u32 voodoo_banshee_device::read_lfb(offs_t offset, u32 mem_mask) { prepare_for_read(); // above LFB base goes the traditional route if (offset >= m_lfb_base) return internal_lfb_r(offset - m_lfb_base); // reads below the LFB base are direct? u32 addr = offset * 4; if (addr <= m_fbmask) { m_renderer->wait("read_lfb"); u32 result = *(u32 *)&m_fbram[addr]; if (LOG_LFB) logerror("%s:read_lfb(%X) = %08X\n", machine().describe_context(), addr, result); return result; } // log out-of-bounds accesses logerror("%s:read_lfb(%X) Access out of bounds\n", machine().describe_context(), addr); return 0xffffffff; } //------------------------------------------------- // write_lfb - generic LFB space write handler //------------------------------------------------- void voodoo_banshee_device::write_lfb(offs_t offset, u32 data, u32 mem_mask) { prepare_for_write(); // above LFB base goes the traditional route if (offset >= m_lfb_base) return internal_lfb_direct_w(offset - m_lfb_base, data, mem_mask); // could be writing to cmdfifo space u32 addr = offset * 4; if (m_cmdfifo.write_if_in_range(addr, data) || m_cmdfifo2.write_if_in_range(addr, data)) return; // writes below the LFB base are direct? if (addr <= m_fbmask) { m_renderer->wait("write_lfb"); if (LOG_LFB) logerror("%s:write_lfb(%X) = %08X & %08X\n", machine().describe_context(), addr, data, mem_mask); COMBINE_DATA((u32 *)&m_fbram[addr]); } else logerror("%s:write_lfb Out of bounds (%X) = %08X & %08X\n", machine().describe_context(), addr, data, mem_mask); } //------------------------------------------------- // io_map - device map for I/O space //------------------------------------------------- void voodoo_banshee_device::io_map(address_map &map) { map(0x00, 0xff).rw(FUNC(voodoo_banshee_device::map_io_r), FUNC(voodoo_banshee_device::map_io_w)); } //------------------------------------------------- // update - video update //------------------------------------------------- int voodoo_banshee_device::update(bitmap_rgb32 &bitmap, const rectangle &cliprect) { // if bypassing the clut, don't worry about the rest u32 config = m_io_regs.read(banshee_io_regs::vidProcCfg); if (BIT(config, 11)) return update_common(bitmap, cliprect, m_shared->rgb565); // if the CLUT is dirty, recompute the pens array if (m_clut_dirty) { rgb_t const *clutbase = &m_clut[256 * BIT(config, 13)]; // compute R/G/B pens first u8 rtable[32], gtable[64], btable[32]; for (u32 rawcolor = 0; rawcolor < 32; rawcolor++) { // treat X as a 5-bit value, scale up to 8 bits u32 color = pal5bit(rawcolor); rtable[rawcolor] = clutbase[color].r(); btable[rawcolor] = clutbase[color].b(); // treat X as a 6-bit value with LSB=0, scale up to 8 bits, and linear interpolate color = pal6bit(rawcolor * 2 + 0); gtable[rawcolor * 2 + 0] = clutbase[color].g(); // treat X as a 6-bit value with LSB=1, scale up to 8 bits, and linear interpolate color = pal6bit(rawcolor * 2 + 1); gtable[rawcolor * 2 + 1] = clutbase[color].g(); } // now compute the actual pens array for (u32 pen = 0; pen < 65536; pen++) m_pen[pen] = rgb_t(rtable[BIT(pen, 11, 5)], gtable[BIT(pen, 5, 6)], btable[BIT(pen, 0, 5)]); // no longer dirty m_clut_dirty = false; m_video_changed = true; } return update_common(bitmap, cliprect, &m_pen[0]); } //------------------------------------------------- // device_start - device startup //------------------------------------------------- void voodoo_banshee_device::device_start() { // expand CLUT to 512 entries m_clut.resize(512); // pre-set the chipmask and clear the TMU memory to indicate we're shared if (m_chipmask == 0x01) m_chipmask = 0x03; m_tmumem0_in_mb = m_tmumem1_in_mb = 0; // start like a Voodoo-2 voodoo_2_device::device_start(); // texture base address/shift is different compared to previous versions m_tmu[0].set_baseaddr_mask_shift(0xfffff0, 0); m_tmu[1].set_baseaddr_mask_shift(0xfffff0, 0); // initialize the second cmdfifo m_cmdfifo2.init(m_fbram, m_fbmask + 1); // LFB stride defaualts to 11 on Banshee and later m_lfb_stride = 11; // 512-entry CLUT on Banshee and later for (int pen = 0; pen < 512; pen++) m_clut[pen] = rgb_t(pen, pen, pen); // initialize banshee registers m_io_regs.reset(); m_io_regs.write(banshee_io_regs::pciInit0, 0x01800040); m_io_regs.write(banshee_io_regs::sipMonitor, 0x40000000); m_io_regs.write(banshee_io_regs::lfbMemoryConfig, 0x000a2200); u32 dram0 = 0x00579d29; if (m_fbmem_in_mb == 16) dram0 |= 0x0c000000; // Midway Vegas (denver) expects 2 banks of 16MBit SGRAMs else dram0 |= 0x08000000; // Konami Viper expects 16MBit SGRAMs m_io_regs.write(banshee_io_regs::dramInit0, dram0); m_io_regs.write(banshee_io_regs::dramInit1, 0x00f02200); m_io_regs.write(banshee_io_regs::tmuGbeInit, 0x00000bfb); } //------------------------------------------------- // soft_reset - handle reset when initiated by // a register write //------------------------------------------------- void voodoo_banshee_device::soft_reset() { voodoo_2_device::soft_reset(); m_cmdfifo2.set_enable(0); } //------------------------------------------------- // register_save - register for save states //------------------------------------------------- void voodoo_banshee_device::register_save(save_proxy &save, u32 total_allocation) { voodoo_2_device::register_save(save, total_allocation); // Voodoo Banshee stuff save.save_class(NAME(m_cmdfifo2)); save.save_class(NAME(m_io_regs)); save.save_class(NAME(m_cmd_agp_regs)); save.save_class(NAME(m_vga_regs)); save.save_class(NAME(m_2d_regs)); save.save_item(NAME(m_blt_dst_base)); save.save_item(NAME(m_blt_dst_x)); save.save_item(NAME(m_blt_dst_y)); save.save_item(NAME(m_blt_dst_width)); save.save_item(NAME(m_blt_dst_height)); save.save_item(NAME(m_blt_dst_stride)); save.save_item(NAME(m_blt_dst_bpp)); save.save_item(NAME(m_blt_cmd)); save.save_item(NAME(m_blt_src_base)); save.save_item(NAME(m_blt_src_x)); save.save_item(NAME(m_blt_src_y)); save.save_item(NAME(m_blt_src_width)); save.save_item(NAME(m_blt_src_height)); save.save_item(NAME(m_blt_src_stride)); save.save_item(NAME(m_blt_src_bpp)); } //------------------------------------------------- // lfb_buffer_indirect - return the buffer base // for LFB accesses via the classic 3D means //------------------------------------------------- u16 *voodoo_banshee_device::lfb_buffer_indirect(int index) { // LFB access is configured via I/O registers return (u16 *)(m_fbram + m_lfb_base * 4); } //------------------------------------------------- // draw_buffer_indirect - return the buffer base // for drawing; on Banshee this is always the // back buffer //------------------------------------------------- u16 *voodoo_banshee_device::draw_buffer_indirect(int index) { // drawing is confined to the back buffer return back_buffer(); } //------------------------------------------------- // map_io_r - handle a mapped read from I/O // space //------------------------------------------------- u32 voodoo_banshee_device::map_io_r(offs_t offset, u32 mem_mask) { prepare_for_read(); return internal_io_r(offset, mem_mask); } //------------------------------------------------- // map_cmd_agp_r - handle a mapped read from CMD/ // AGP space //------------------------------------------------- u32 voodoo_banshee_device::map_cmd_agp_r(offs_t offset) { prepare_for_read(); return internal_cmd_agp_r(offset); } //------------------------------------------------- // map_2d_r - handle a mapped read from 2D space //------------------------------------------------- u32 voodoo_banshee_device::map_2d_r(offs_t offset) { prepare_for_read(); logerror("%s:map_2d_r(%X)\n", machine().describe_context(), (offset*4) & 0xfffff); return 0xffffffff; } //------------------------------------------------- // map_register_r - handle a mapped read from // regular register space //------------------------------------------------- u32 voodoo_banshee_device::map_register_r(offs_t offset) { prepare_for_read(); // extract chipmask and register u32 chipmask = BIT(offset, 8, 4); if (chipmask == 0) chipmask = 0xf; chipmask &= m_chipmask; u32 regnum = BIT(offset, 0, 8); // look up the register return m_regtable[regnum].read(*this, chipmask, regnum); } //------------------------------------------------- // map_io_w - handle a mapped write to I/O space //------------------------------------------------- void voodoo_banshee_device::map_io_w(offs_t offset, u32 data, u32 mem_mask) { // no mechanism to stall I/O writes prepare_for_write(); internal_io_w(offset, data, mem_mask); } //------------------------------------------------- // map_agp_cmd_w - handle a mapped write to CMD/ // AGP space //------------------------------------------------- void voodoo_banshee_device::map_cmd_agp_w(offs_t offset, u32 data, u32 mem_mask) { // no mechanism to stall AGP/CMD writes prepare_for_write(); internal_cmd_agp_w(offset, data, mem_mask); } //------------------------------------------------- // map_2d_w - handle a mapped write to 2D space //------------------------------------------------- void voodoo_banshee_device::map_2d_w(offs_t offset, u32 data, u32 mem_mask) { // no mechanism to stall 2D writes prepare_for_write(); logerror("%s:map_2d_w(%X) = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask); } //------------------------------------------------- // map_register_w - handle a mapped write to // regular register space //------------------------------------------------- void voodoo_banshee_device::map_register_w(offs_t offset, u32 data, u32 mem_mask) { bool pending = prepare_for_write(); // extract chipmask and register u32 chipmask = BIT(offset, 8, 4); if (chipmask == 0) chipmask = 0xf; chipmask &= m_chipmask; u32 regnum = BIT(offset, 0, 8); // handle register swizzling if (BIT(offset, 20-2)) data = swapendian_int32(data); // handle aliasing if (BIT(offset, 21-2)) regnum = voodoo_regs::alias(regnum); // look up the register auto const ®entry = 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 we're busy add to the fifo if (pending && m_init_enable.enable_pci_fifo()) return add_to_fifo((chipmask << 4) | regnum | memory_fifo::TYPE_REGISTER, 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) { // if we ended up with cycles, mark the operation pending 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)); } } //------------------------------------------------- // map_texture_w - handle a mapped write to // texture download space //------------------------------------------------- template void voodoo_banshee_device::map_texture_w(offs_t offset, u32 data, u32 mem_mask) { prepare_for_write(); logerror("%s:map_texture_w<%d>(%X) = %08X & %08X\n", machine().describe_context(), Which, offset*4, data, mem_mask); } //------------------------------------------------- // map_yuv_w - handle a mapped write to YUV space //------------------------------------------------- void voodoo_banshee_device::map_yuv_w(offs_t offset, u32 data, u32 mem_mask) { prepare_for_write(); logerror("%s:map_yuv_w(%X) = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask); } //------------------------------------------------- // map_lfb_w - handle a mapped write to LFB space //------------------------------------------------- void voodoo_banshee_device::map_lfb_w(offs_t offset, u32 data, u32 mem_mask) { // if we're busy add to the fifo, else just execute immediately if (prepare_for_write() && m_init_enable.enable_pci_fifo()) add_to_fifo(offset | (0x400000/4), data, mem_mask); else internal_lfb_w(offset, data, mem_mask); } //------------------------------------------------- // internal_io_r - handle reads from the I/O // registers //------------------------------------------------- u32 voodoo_banshee_device::internal_io_r(offs_t offset, u32 mem_mask) { // by default just return regular data offset &= 0x3f; u32 result = m_io_regs.read(offset); // handle special offsets switch (offset) { // status reflects the voodoo status case banshee_io_regs::status: result = reg_status_r(0, 0); break; // DAC data reads fetch data from the CLUT case banshee_io_regs::dacData: result = m_clut[BIT(m_io_regs.read(banshee_io_regs::dacAddr), 0, 9)]; break; // VGA reads are special and byte-wise case banshee_io_regs::vgab0: case banshee_io_regs::vgab4: case banshee_io_regs::vgab8: case banshee_io_regs::vgabc: case banshee_io_regs::vgac0: case banshee_io_regs::vgac4: case banshee_io_regs::vgac8: case banshee_io_regs::vgacc: case banshee_io_regs::vgad0: case banshee_io_regs::vgad4: case banshee_io_regs::vgad8: case banshee_io_regs::vgadc: result = 0; if (ACCESSING_BITS_0_7) result |= internal_vga_r(offset * 4 + 0) << 0; if (ACCESSING_BITS_8_15) result |= internal_vga_r(offset * 4 + 1) << 8; if (ACCESSING_BITS_16_23) result |= internal_vga_r(offset * 4 + 2) << 16; if (ACCESSING_BITS_24_31) result |= internal_vga_r(offset * 4 + 3) << 24; // early out to skip extra logging return result; } if (LOG_REGISTERS) logerror("%s:internal_io_r(%s) = %08X\n", machine().describe_context(), m_io_regs.name(offset), result); return result; } //------------------------------------------------- // internal_io_w - handle writes to the I/O // registers //------------------------------------------------- void voodoo_banshee_device::internal_io_w(offs_t offset, u32 data, u32 mem_mask) { // need to block if Y origin is changing; everything else can proceed offset &= 0x3f; if (offset == banshee_io_regs::miscInit0) m_renderer->wait(m_io_regs.name(offset)); // fetch original value and compute new value u32 oldval = m_io_regs.read(offset); u32 newval = m_io_regs.write(offset, data, mem_mask); // handle special cases switch (offset) { // dacData writes indirectly to the CLUT registers; mark dirty if changing case banshee_io_regs::dacData: { u32 dacaddr = BIT(m_io_regs.read(banshee_io_regs::dacAddr), 0, 9); if (newval != m_clut[dacaddr]) { screen().update_partial(screen().vpos()); m_clut[dacaddr] = newval; m_clut_dirty = true; } break; } // vidProcCfg can change the CLUT index case banshee_io_regs::vidProcCfg: if (BIT(oldval ^ newval, 13)) m_clut_dirty = true; break; // miscInit0 can alter the rendering Y origin case banshee_io_regs::miscInit0: m_renderer->set_yorigin(BIT(newval, 18, 12)); break; // vidScreenSize causes the video to be reconfigured case banshee_io_regs::vidScreenSize: if (BIT(newval, 0, 12) != 0) m_width = BIT(data, 0, 12); if (BIT(newval, 12, 12) != 0) m_height = BIT(data, 12, 12); if (m_width != 0 && m_height != 0 && newval != oldval) recompute_video(); break; // vidOverlayDudx/vidOverlayDudy impact how we configure the video case banshee_io_regs::vidOverlayDudx: case banshee_io_regs::vidOverlayDvdy: recompute_video(); break; // lfbMemoryConfig affects LFB reads case banshee_io_regs::lfbMemoryConfig: m_lfb_base = BIT(newval, 0, 13) << (12-2); m_lfb_stride = BIT(newval, 13, 3) + 9; break; // VGA is special and byte-wise case banshee_io_regs::vgab0: case banshee_io_regs::vgab4: case banshee_io_regs::vgab8: case banshee_io_regs::vgabc: case banshee_io_regs::vgac0: case banshee_io_regs::vgac4: case banshee_io_regs::vgac8: case banshee_io_regs::vgacc: case banshee_io_regs::vgad0: case banshee_io_regs::vgad4: case banshee_io_regs::vgad8: case banshee_io_regs::vgadc: if (ACCESSING_BITS_0_7) internal_vga_w(offset * 4 + 0, BIT(data, 0, 8)); if (ACCESSING_BITS_8_15) internal_vga_w(offset * 4 + 1, BIT(data, 8, 8)); if (ACCESSING_BITS_16_23) internal_vga_w(offset * 4 + 2, BIT(data, 16, 8)); if (ACCESSING_BITS_24_31) internal_vga_w(offset * 4 + 3, BIT(data, 24, 8)); // early out to skip extra logging return; } if (LOG_REGISTERS) logerror("%s:internal_io_w(%s) = %08X & %08X\n", machine().describe_context(), m_io_regs.name(offset), data, mem_mask); } //------------------------------------------------- // internal_cmd_agp_r - handle reads from the // CMD/AGP registers //------------------------------------------------- u32 voodoo_banshee_device::internal_cmd_agp_r(offs_t offset) { // by default just return regular data offset &= 0x7f; u32 result = m_cmd_agp_regs.read(offset); // check for special cases switch (offset) { case banshee_cmd_agp_regs::cmdRdPtrL0: result = m_cmdfifo.read_pointer(); break; case banshee_cmd_agp_regs::cmdFifoDepth0: result = m_cmdfifo.depth(); break; case banshee_cmd_agp_regs::cmdHoleCnt0: result = m_cmdfifo.holes(); break; case banshee_cmd_agp_regs::cmdRdPtrL1: result = m_cmdfifo2.read_pointer(); break; case banshee_cmd_agp_regs::cmdFifoDepth1: result = m_cmdfifo2.depth(); break; case banshee_cmd_agp_regs::cmdHoleCnt1: result = m_cmdfifo2.holes(); break; } if (LOG_REGISTERS) logerror("%s:internal_cmd_agp_r(%s) = %08X\n", machine().describe_context(), m_cmd_agp_regs.name(offset), result); return result; } //------------------------------------------------- // internal_cmd_agp_w - handle writes to the // CMD/AGP registers //------------------------------------------------- void voodoo_banshee_device::internal_cmd_agp_w(offs_t offset, u32 data, u32 mem_mask) { offset &= 0x7f; // fetch original value and compute new value u32 newval = m_cmd_agp_regs.write(offset, data, mem_mask); // handle special cases switch (offset) { case banshee_cmd_agp_regs::cmdBaseAddr0: m_cmdfifo.set_base(BIT(newval, 0, 24) << 12); m_cmdfifo.set_size((BIT(m_cmd_agp_regs.read(banshee_cmd_agp_regs::cmdBaseSize0), 0, 8) + 1) << 12); break; case banshee_cmd_agp_regs::cmdBaseSize0: m_cmdfifo.set_size((BIT(newval, 0, 8) + 1) << 12); m_cmdfifo.set_enable(BIT(newval, 8)); m_cmdfifo.set_count_holes(!BIT(newval, 10)); break; case banshee_cmd_agp_regs::cmdBump0: fatalerror("%s: Unsupported write to cmdBump0", tag()); case banshee_cmd_agp_regs::cmdRdPtrL0: m_cmdfifo.set_read_pointer(newval); break; case banshee_cmd_agp_regs::cmdAMin0: m_cmdfifo.set_address_min(newval); break; case banshee_cmd_agp_regs::cmdAMax0: m_cmdfifo.set_address_max(newval); break; case banshee_cmd_agp_regs::cmdFifoDepth0: m_cmdfifo.set_depth(newval); break; case banshee_cmd_agp_regs::cmdHoleCnt0: m_cmdfifo.set_holes(newval); break; case banshee_cmd_agp_regs::cmdBaseAddr1: m_cmdfifo2.set_base(BIT(newval, 0, 24) << 12); m_cmdfifo2.set_size((BIT(m_cmd_agp_regs.read(banshee_cmd_agp_regs::cmdBaseSize1), 0, 8) + 1) << 12); break; case banshee_cmd_agp_regs::cmdBaseSize1: m_cmdfifo2.set_size((BIT(newval, 0, 8) + 1) << 12); m_cmdfifo2.set_enable(BIT(newval, 8)); m_cmdfifo2.set_count_holes(!BIT(newval, 10)); break; case banshee_cmd_agp_regs::cmdBump1: fatalerror("%s: Unsupported write to cmdBump1", tag()); case banshee_cmd_agp_regs::cmdRdPtrL1: m_cmdfifo2.set_read_pointer(newval); break; case banshee_cmd_agp_regs::cmdAMin1: m_cmdfifo2.set_address_min(newval); break; case banshee_cmd_agp_regs::cmdAMax1: m_cmdfifo2.set_address_max(newval); break; case banshee_cmd_agp_regs::cmdFifoDepth1: m_cmdfifo2.set_depth(newval); break; case banshee_cmd_agp_regs::cmdHoleCnt1: m_cmdfifo2.set_holes(newval); break; } if (LOG_REGISTERS) logerror("%s:internal_cmd_agp_w(%s) = %08X & %08X\n", machine().describe_context(), m_io_regs.name(offset), data, mem_mask); } //------------------------------------------------- // internal_2d_w - handle writes to the 2D // registers //------------------------------------------------- s32 voodoo_banshee_device::internal_2d_w(offs_t offset, u32 data) { static u8 const s_format_bpp[16] = { 1,1,1,2,3,4,1,1,2,2,1,1,1,1,1,1 }; // by default write through to the register offset &= 0x3f; m_2d_regs.write(offset, data); // handle special cases switch (offset) { case banshee_2d_regs::command: if (LOG_BANSHEE_2D) logerror(" 2D:command: cmd %d, ROP0 %02X\n", data & 0xf, data >> 24); m_blt_src_x = BIT(m_2d_regs.read(banshee_2d_regs::srcXY), 0, 12); m_blt_src_y = BIT(m_2d_regs.read(banshee_2d_regs::srcXY), 16, 12); m_blt_src_base = BIT(m_2d_regs.read(banshee_2d_regs::srcBaseAddr), 0, 24); m_blt_src_stride = BIT(m_2d_regs.read(banshee_2d_regs::srcFormat), 0, 14); m_blt_src_width = BIT(m_2d_regs.read(banshee_2d_regs::srcSize), 0, 12); m_blt_src_height = BIT(m_2d_regs.read(banshee_2d_regs::srcSize), 16, 12); m_blt_src_bpp = s_format_bpp[BIT(m_2d_regs.read(banshee_2d_regs::srcFormat), 16, 4)]; m_blt_dst_x = BIT(m_2d_regs.read(banshee_2d_regs::dstXY), 0, 12); m_blt_dst_y = BIT(m_2d_regs.read(banshee_2d_regs::dstXY), 16, 12); m_blt_dst_base = BIT(m_2d_regs.read(banshee_2d_regs::dstBaseAddr), 0, 24); m_blt_dst_stride = BIT(m_2d_regs.read(banshee_2d_regs::dstFormat), 0, 14); m_blt_dst_width = BIT(m_2d_regs.read(banshee_2d_regs::dstSize), 0, 12); m_blt_dst_height = BIT(m_2d_regs.read(banshee_2d_regs::dstSize), 16, 12); m_blt_dst_bpp = s_format_bpp[BIT(m_2d_regs.read(banshee_2d_regs::dstFormat), 16, 3)]; m_blt_cmd = BIT(data, 0, 4); break; default: if (offset >= 0x20 && offset < 0x40) execute_blit(data); break; } return 1; } //------------------------------------------------- // internal_texture_w - handle writes to texture // space //------------------------------------------------- void voodoo_banshee_device::internal_texture_w(offs_t offset, u32 data) { // statistics if (DEBUG_STATS) m_stats.m_tex_writes++; // point to the right TMU int tmunum = BIT(offset, 19, 2); if (!BIT(m_chipmask, 1 + tmunum)) return; // pull out modes from the TMU and update state auto ®s = m_tmu[tmunum].regs(); auto const texlod = regs.texture_lod(); auto const texmode = regs.texture_mode(); auto &texture = m_tmu[tmunum].prepare_texture(*m_renderer.get()); // swizzle the data if (texlod.tdata_swizzle()) data = swapendian_int32(data); if (texlod.tdata_swap()) data = (data >> 16) | (data << 16); // determine destination pointer u8 *dest = texture.write_ptr(0, offset * 4, 0, 1); // wait for any outstanding work to finish m_renderer->wait("internal_texture_w"); // write the four bytes in little-endian order u32 bytes_per_texel = (texmode.format() < 8) ? 1 : 2; if (bytes_per_texel == 1) { dest[BYTE4_XOR_LE(0)] = (data >> 0) & 0xff; dest[BYTE4_XOR_LE(1)] = (data >> 8) & 0xff; dest[BYTE4_XOR_LE(2)] = (data >> 16) & 0xff; dest[BYTE4_XOR_LE(3)] = (data >> 24) & 0xff; } else { u16 *dest16 = reinterpret_cast(dest); dest16[BYTE_XOR_LE(0)] = (data >> 0) & 0xffff; dest16[BYTE_XOR_LE(1)] = (data >> 16) & 0xffff; } } //------------------------------------------------- // internal_lfb_direct_w - handle "direct" writes // to LFB space; this is like previous generations // but ignores the LFB mode and treats everything // as 16-bit pixel data //------------------------------------------------- void voodoo_banshee_device::internal_lfb_direct_w(offs_t offset, u32 data, u32 mem_mask) { // statistics if (DEBUG_STATS) m_stats.m_lfb_writes++; // byte swizzling auto const lfbmode = m_reg.lfb_mode(); if (lfbmode.byte_swizzle_writes()) { data = swapendian_int32(data); mem_mask = swapendian_int32(mem_mask); } // word swapping if (lfbmode.word_swap_writes()) { data = (data << 16) | (data >> 16); mem_mask = (mem_mask << 16) | (mem_mask >> 16); } // TODO: This direct write is not verified. // no data expansion or pixel pipelines, just raw write of 2 16-bit pixels? offset <<= 1; // compute X,Y s32 x = offset & ((1 << m_lfb_stride) - 1); s32 y = offset >> m_lfb_stride; // select the target buffers u16 *dest = lfb_buffer_indirect(0); u16 *end = ram_end(); // advance pointers to the proper row dest += y * m_renderer->rowpixels() + x; // wait for any outstanding work to finish m_renderer->wait("internal_lfb_direct_w"); // write to the RGB buffer if (ACCESSING_BITS_0_15 && dest < end) dest[0] = BIT(data, 0, 16); if (ACCESSING_BITS_16_31 && dest + 1 < end) dest[1] = BIT(data, 16, 16); // notify that frame buffer has changed m_video_changed = true; if (LOG_LFB) logerror("VOODOO.LFB:write direct (%d,%d) = %08X & %08X\n", x, y, data, mem_mask); } //------------------------------------------------- // internal_vga_r - handle reads from VGA register // space //------------------------------------------------- u8 voodoo_banshee_device::internal_vga_r(offs_t offset) { // by default just return regular data offset &= 0x1f; u32 result = m_vga_regs.read(offset); // handle special offsets char const *logtype = "internal_vga_r"; u32 logoffs = offset + 0x3c0; switch (offset) { // attribute access case banshee_vga_regs::attributeData: result = m_vga_regs.read_attr(logoffs = m_vga_regs.attribute_index()); logtype = "vga_attr_r"; break; // input status 0 case banshee_vga_regs::inputStatus0: // bit 7 = Interrupt Status. When its value is ?1?, denotes that an interrupt is pending. // bit 6:5 = Feature Connector. These 2 bits are readable bits from the feature connector. // bit 4 = Sense. This bit reflects the state of the DAC monitor sense logic. // bit 3:0 = Reserved. Read back as 0. result = 0x00; break; // sequencer access case banshee_vga_regs::sequencerData: result = m_vga_regs.read_seq(logoffs = m_vga_regs.sequencer_index()); logtype = "vga_seq_r"; break; // feature control case banshee_vga_regs::featureControlR: result = m_vga_regs.read(banshee_vga_regs::featureControlW); m_vga_regs.clear_flip_flop(); break; // miscellaneous output case banshee_vga_regs::miscOutputR: result = m_vga_regs.read(banshee_vga_regs::miscOutputW); break; // graphics controller access case banshee_vga_regs::gfxControllerData: result = m_vga_regs.read_gc(logoffs = m_vga_regs.gfx_controller_index()); logtype = "vga_gc_r"; break; // CRTC access case banshee_vga_regs::crtcData: result = m_vga_regs.read_crtc(logoffs = m_vga_regs.crtc_index()); logtype = "vga_crtc_r"; break; // input status 1 case banshee_vga_regs::inputStatus1: // bit 7:6 = Reserved. These bits read back 0. // bit 5:4 = Display Status. These 2 bits reflect 2 of the 8 pixel data outputs from the Attribute // controller, as determined by the Attribute controller index 0x12 bits 4 and 5. // bit 3 = Vertical sync Status. A ?1? indicates vertical retrace is in progress. // bit 2:1 = Reserved. These bits read back 0x2. // bit 0 = Display Disable. When this bit is 1, either horizontal or vertical display end has occurred, // otherwise video data is being displayed. result = 0x04; break; } if (LOG_REGISTERS) logerror("%s:%s(%X) = %02X\n", machine().describe_context(), logtype, logoffs, result); return result; } //------------------------------------------------- // internal_vga_w - handle writes to VGA register // space //------------------------------------------------- void voodoo_banshee_device::internal_vga_w(offs_t offset, u8 data) { offset &= 0x1f; // fetch original value and compute new value m_vga_regs.write(offset, data); // handle special cases char const *logtype = "internal_vga_w"; u32 logoffs = offset + 0x3c0; switch (offset) { // attribute access case banshee_vga_regs::attributeData: case banshee_vga_regs::attributeIndex: if (m_vga_regs.toggle_flip_flop() == 0) m_vga_regs.write(banshee_vga_regs::attributeIndex, data); else { m_vga_regs.write_attr(logoffs = m_vga_regs.attribute_index(), data); logtype = "vga_crtc_w"; } break; // sequencer access case banshee_vga_regs::sequencerData: m_vga_regs.write_seq(logoffs = m_vga_regs.sequencer_index(), data); logtype = "vga_seq_w"; break; // graphics controller access case banshee_vga_regs::gfxControllerData: m_vga_regs.write_gc(logoffs = m_vga_regs.gfx_controller_index(), data); logtype = "vga_gc_w"; break; // CRTC access case banshee_vga_regs::crtcData: m_vga_regs.write_crtc(logoffs = m_vga_regs.crtc_index(), data); logtype = "vga_crtc_w"; break; } if (LOG_REGISTERS) logerror("%s:%s(%X) = %02X\n", machine().describe_context(), logtype, logoffs, data); } //------------------------------------------------- // execute_fifos - execute commands from the FIFOs // until a non-zero cycle count operation is run //------------------------------------------------- u32 voodoo_banshee_device::execute_fifos() { // we might be in CMDFIFO mode if (m_cmdfifo.enabled()) return m_cmdfifo.execute_if_ready(); if (m_cmdfifo2.enabled()) return m_cmdfifo2.execute_if_ready(); // otherwise, run the traditional memory FIFOs return voodoo_1_device::execute_fifos(); } //------------------------------------------------- // reg_status_r - status register read //------------------------------------------------- u32 voodoo_banshee_device::reg_status_r(u32 chipmask, u32 offset) { u32 result = 0; // bits 5:0 are the PCI FIFO free space result |= std::min(m_pci_fifo.space() / 2, 0x3f) << 0; // bit 6 is the vertical retrace result |= m_vblank << 6; // bit 7 is FBI graphics engine busy // bit 8 is TREX busy // bit 9 is overall busy */ if (operation_pending()) result |= (1 << 7) | (1 << 8) | (1 << 9); // bit 10 is 2D busy // bit 11 is cmd FIFO 0 busy if (m_cmdfifo.enabled() && m_cmdfifo.depth() > 0) result |= 1 << 11; // bit 12 is cmd FIFO 1 busy if (m_cmdfifo2.enabled() && m_cmdfifo2.depth() > 0) result |= 1 << 12; // bits 30:28 are the number of pending swaps result |= std::min(m_swaps_pending, 7) << 28; // eat some cycles since people like polling here if (m_status_cycles != 0) m_cpu->eat_cycles(m_status_cycles); // bit 31 is PCI interrupt pending (not implemented) return result; } //------------------------------------------------- // reg_colbufbase_w - colBufferAddr register write //------------------------------------------------- u32 voodoo_banshee_device::reg_colbufbase_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) { m_reg.write(regnum, data); m_rgboffs[1] = data & m_fbmask & ~0x0f; } return 0; } //------------------------------------------------- // reg_colbufstride_w - colBufferStride register // write //------------------------------------------------- u32 voodoo_banshee_device::reg_colbufstride_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) { m_reg.write(regnum, data); u32 newpix = BIT(data, 15) ? (BIT(data, 0, 7) << 6) : (BIT(data, 0, 14) >> 1); if (newpix != m_renderer->rowpixels()) { m_renderer->set_rowpixels(newpix); m_video_changed = true; } } return 0; } //------------------------------------------------- // reg_auxbufbase_w - auxBufferAddr register write //------------------------------------------------- u32 voodoo_banshee_device::reg_auxbufbase_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) { m_reg.write(regnum, data); m_auxoffs = data & m_fbmask & ~0x0f; } return 0; } //------------------------------------------------- // reg_auxbufstride_w - auxBufferStride register // write //------------------------------------------------- u32 voodoo_banshee_device::reg_auxbufstride_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) { m_reg.write(regnum, data); u32 newpix = BIT(data, 15) ? (BIT(data, 0, 7) << 6) : (BIT(data, 0, 14) >> 1); if (newpix != m_renderer->rowpixels()) fatalerror("%s: Unsupported aux buffer stride (%d) differs from color buffer stride (%d)", tag(), newpix, m_renderer->rowpixels()); } return 0; } //------------------------------------------------- // reg_swappending_w - swapPending register write //------------------------------------------------- u32 voodoo_banshee_device::reg_swappending_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) m_swaps_pending++; return 0; } //------------------------------------------------- // reg_overbuffer_w - leftOverlayBuf/ // rightOverlayBuf register write //------------------------------------------------- u32 voodoo_banshee_device::reg_overbuffer_w(u32 chipmask, u32 regnum, u32 data) { if (BIT(chipmask, 0)) m_reg.write(regnum, data); return 0; } //------------------------------------------------- // rotate_buffers -- rotate the buffers according // to the current buffer config; just update the // base to the left overlay buffer //------------------------------------------------- void voodoo_banshee_device::rotate_buffers() { m_rgboffs[0] = m_reg.read(voodoo_regs::reg_leftOverlayBuf) & m_fbmask & ~0x0f; } //------------------------------------------------- // recompute_video -- determine the video size // and configuration based on current registers //------------------------------------------------- void voodoo_banshee_device::recompute_video() { u8 crtc7 = m_vga_regs.read_crtc(7); // get horizontal total and vertical total from CRTC registers u32 htotal = (m_vga_regs.read_crtc(0) + 5) * 8; u32 vtotal = (m_vga_regs.read_crtc(6) | (BIT(crtc7, 0) << 8) | (BIT(crtc7, 5) << 9)) + 2; if (htotal == 0 || vtotal == 0) return; // compute start/stop for vertical retrace u32 vstart = m_vga_regs.read_crtc(16) | (BIT(crtc7, 2) << 8) | (BIT(crtc7, 7) << 9); u32 vstop = m_vga_regs.read_crtc(17) & 0xf; // compare to see if vstop is before or after low 4 bits of vstart if (vstop < (vstart & 0xf)) vstop |= (vstart + 0x10) & ~0xf; else vstop |= vstart & ~0xf; // get pll k, m and n from pllCtrl0 const u32 pll0 = m_io_regs.read(banshee_io_regs::pllCtrl0); const u32 k = BIT(pll0, 0, 2); const u32 m = BIT(pll0, 2, 6); const u32 n = BIT(pll0, 8, 8); const double video_clock = (XTAL(14'318'181) * (n + 2) / ((m + 2) << k)).dvalue(); const double frame_period = vtotal * htotal / video_clock; // compute scaled screen size, using the overlay fraction if specified u32 dudx = m_io_regs.read(banshee_io_regs::vidOverlayDudx); u32 dvdy = m_io_regs.read(banshee_io_regs::vidOverlayDvdy); u32 width = (dudx != 0) ? ((m_width * dudx) >> 20) : m_width; u32 height = (dvdy != 0) ? ((m_height * dvdy) >> 20) : m_height; if (LOG_REGISTERS) logerror("configure screen: htotal: %d vtotal: %d vstart: %d vstop: %d width: %d height: %d refresh: %f\n", htotal, vtotal, vstart, vstop, width, height, 1.0 / frame_period); // configure the screen rectangle visarea(0, width - 1, 0, height - 1); screen().configure(htotal, vtotal, visarea, DOUBLE_TO_ATTOSECONDS(frame_period)); // set the vsync start and stop m_vsyncstart = vstart; m_vsyncstop = vstop; adjust_vblank_start_timer(); } //------------------------------------------------- // cmdfifo_register_w -- handle a register write // from the cmdfifo //------------------------------------------------- u32 voodoo_banshee_device::cmdfifo_register_w(u32 offset, u32 data) { // bit 11 indicates a write to 2D register space u32 regnum = BIT(offset, 0, 8); if (BIT(offset, 11, 1)) return internal_2d_w(regnum, data); // otherwise, just a normal register write u32 chipmask = chipmask_from_offset(offset); return m_regtable[regnum].write(*this, chipmask, regnum, data); } //------------------------------------------------- // cmdfifo_2d_w -- handle a 2D register write // from the cmdfifo //------------------------------------------------- u32 voodoo_banshee_device::cmdfifo_2d_w(u32 offset, u32 data) { u32 regnum = banshee_2d_regs::clip0Min + offset; return internal_2d_w(regnum, data); } //------------------------------------------------- // execute_blit -- perform a 2D blitting operation //------------------------------------------------- void voodoo_banshee_device::execute_blit(u32 data) { switch (m_blt_cmd) { case 0: // NOP - wait for idle break; case 1: // Screen-to-screen blit // TODO if (LOG_BANSHEE_2D) logerror(" blit_2d:screen_to_screen: src X %d, src Y %d\n", data & 0xfff, (data >> 16) & 0xfff); break; case 2: // Screen-to-screen stretch blit fatalerror("%s: Unsupported blit_2d:screen_to_screen_stretch: src X %d, src Y %d\n", tag(), data & 0xfff, (data >> 16) & 0xfff); case 3: // Host-to-screen blit { u32 addr = m_blt_dst_base + m_blt_dst_y * m_blt_dst_stride + m_blt_dst_x * m_blt_dst_bpp; if (LOG_BANSHEE_2D) logerror(" blit_2d:host_to_screen: %08x -> %08x, %d, %d\n", data, addr, m_blt_dst_x, m_blt_dst_y); m_renderer->wait("execute_blit(3)"); switch (m_blt_dst_bpp) { case 1: m_fbram[addr + 0] = BIT(data, 0, 8); m_fbram[addr + 1] = BIT(data, 8, 8); m_fbram[addr + 2] = BIT(data, 16, 8); m_fbram[addr + 3] = BIT(data, 24, 8); m_blt_dst_x += 4; break; case 2: m_fbram[addr + 1] = BIT(data, 0, 8); m_fbram[addr + 0] = BIT(data, 8, 8); m_fbram[addr + 3] = BIT(data, 16, 8); m_fbram[addr + 2] = BIT(data, 24, 8); m_blt_dst_x += 2; break; case 3: m_blt_dst_x += 1; break; case 4: m_fbram[addr + 3] = BIT(data, 0, 8); m_fbram[addr + 2] = BIT(data, 8, 8); m_fbram[addr + 1] = BIT(data, 16, 8); m_fbram[addr + 0] = BIT(data, 24, 8); m_blt_dst_x += 1; break; } if (m_blt_dst_x >= m_blt_dst_width) { m_blt_dst_x = 0; m_blt_dst_y++; } break; } case 5: // Rectangle fill fatalerror("%s: Unsupported 2D rectangle_fill: src X %d, src Y %d", tag(), BIT(data, 0, 12), BIT(data, 16, 12)); case 6: // Line fatalerror("%s: Unsupported 2D line: end X %d, end Y %d", tag(), BIT(data, 0, 12), BIT(data, 16, 12)); case 7: // Polyline fatalerror("%s: Unsupported 2D polyline: end X %d, end Y %d", tag(), BIT(data, 0, 12), BIT(data, 16, 12)); case 8: // Polygon fill fatalerror("%s: Unsupported 2D polygon_fill", tag()); default: fatalerror("%s: Unsupported 2D unknown command %d", tag(), m_blt_cmd); } } //************************************************************************** // VOODOO 3 DEVICE //************************************************************************** //------------------------------------------------- // voodoo_3_device - constructor //------------------------------------------------- DEFINE_DEVICE_TYPE(VOODOO_3, voodoo_3_device, "voodoo_3", "3dfx Voodoo 3") voodoo_3_device::voodoo_3_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : voodoo_banshee_device(mconfig, VOODOO_3, tag, owner, clock, voodoo_model::VOODOO_3) { } //------------------------------------------------- // device_start - device startup //------------------------------------------------- void voodoo_3_device::device_start() { // Voodoo-3 has 2 TMUs standard if (m_chipmask == 0x01) m_chipmask = 0x07; // start like a Banshee voodoo_banshee_device::device_start(); } //************************************************************************** // VOODOO BANSHEE REGISTER MAP //************************************************************************** #define REGISTER_ENTRY(name, reader, writer, bits, chips, sync, fifo) \ { static_register_table_entry::make_mask(bits), register_table_entry::CHIPMASK_##chips | register_table_entry::SYNC_##sync | register_table_entry::FIFO_##fifo, #name, &voodoo_banshee_device::reg_##writer##_w, &voodoo_banshee_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 const voodoo_banshee_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, 32, FBI_TREX, SYNC, FIFO) // 118 REGISTER_ENTRY(clipLowYHighY, passive, passive, 32, 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, 28, 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? RESERVED_ENTRY // 1e0 RESERVED_ENTRY // 1e4 RESERVED_ENTRY // 1e8 REGISTER_ENTRY(colBufferAddr, passive, colbufbase, 24, FBI, SYNC, FIFO) // 1ec REGISTER_ENTRY(colBufferStride, passive, colbufstride,16, FBI, SYNC, FIFO) // 1f0 REGISTER_ENTRY(auxBufferAddr, passive, auxbufbase, 24, FBI, SYNC, FIFO) // 1f4 REGISTER_ENTRY(auxBufferStride, passive, auxbufstride,16, FBI, SYNC, FIFO) // 1f8 RESERVED_ENTRY // 1fc REGISTER_ENTRY(clipLeftRight1, passive, passive, 32, FBI, SYNC, FIFO) // 200 REGISTER_ENTRY(clipTopBottom1, passive, passive, 32, FBI, SYNC, FIFO) // 204 RESERVED_ENTRY // 208 RESERVED_ENTRY // 20c RESERVED_ENTRY // 210 RESERVED_ENTRY // 214 RESERVED_ENTRY // 218 RESERVED_ENTRY // 21c RESERVED_ENTRY_x8 // 220-23c RESERVED_ENTRY // 240 RESERVED_ENTRY // 244 RESERVED_ENTRY // 248 REGISTER_ENTRY(swapPending, invalid, swappending, 32, FBI, NOSYNC, NOFIFO) // 24c REGISTER_ENTRY(leftOverlayBuf, invalid, overbuffer, 32, FBI, NOSYNC, FIFO) // 250 REGISTER_ENTRY(rightOverlayBuf, invalid, overbuffer, 32, FBI, NOSYNC, FIFO) // 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, 32, FBI, NOSYNC, FIFO) // 2a0 REGISTER_ENTRY(sBeginTriCMD, invalid, begin_tri, 32, 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? RESERVED_ENTRY_x8 // 2c0-2dc RESERVED_ENTRY_x8 // 2e0-2fc // name rd handler wr handler bits chips sync? fifo? REGISTER_ENTRY(textureMode, invalid, texture, 31, TREX, NOSYNC, FIFO) // 300 REGISTER_ENTRY(tLOD, invalid, texture, 28, TREX, NOSYNC, FIFO) // 304 REGISTER_ENTRY(tDetail, invalid, texture, 22, TREX, NOSYNC, FIFO) // 308 REGISTER_ENTRY(texBaseAddr, invalid, texture, 32, TREX, NOSYNC, FIFO) // 30c REGISTER_ENTRY(texBaseAddr_1, invalid, texture, 24, TREX, NOSYNC, FIFO) // 310 REGISTER_ENTRY(texBaseAddr_2, invalid, texture, 24, TREX, NOSYNC, FIFO) // 314 REGISTER_ENTRY(texBaseAddr_3_8, invalid, texture, 24, 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 // name rd handler wr handler bits chips sync? fifo? RESERVED_ENTRY_x8 // 3a0-3bc RESERVED_ENTRY_x8 // 3c0-3dc RESERVED_ENTRY_x8 // 3e0-3fc };