diff options
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | src/devices/bus/nubus/cards.cpp | 3 | ||||
-rw-r--r-- | src/devices/bus/nubus/thunder4gx.cpp | 810 | ||||
-rw-r--r-- | src/devices/bus/nubus/thunder4gx.h | 12 |
4 files changed, 827 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 1e5096f6ff9..3890f432883 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -3040,6 +3040,8 @@ if (BUSES["NUBUS"]~=null) then MAME_DIR .. "src/devices/bus/nubus/laserview.h", MAME_DIR .. "src/devices/bus/nubus/supermac.cpp", MAME_DIR .. "src/devices/bus/nubus/supermac.h", + MAME_DIR .. "src/devices/bus/nubus/thunder4gx.cpp", + MAME_DIR .. "src/devices/bus/nubus/thunder4gx.h", MAME_DIR .. "src/devices/bus/nubus/pds30_cb264.cpp", MAME_DIR .. "src/devices/bus/nubus/pds30_cb264.h", MAME_DIR .. "src/devices/bus/nubus/pds30_procolor816.cpp", diff --git a/src/devices/bus/nubus/cards.cpp b/src/devices/bus/nubus/cards.cpp index 8c85a4f81b0..a16f3e48497 100644 --- a/src/devices/bus/nubus/cards.cpp +++ b/src/devices/bus/nubus/cards.cpp @@ -29,6 +29,7 @@ #include "pds30_procolor816.h" #include "pds30_sigmalview.h" #include "quadralink.h" +#include "thunder4gx.h" void mac_nubus_cards(device_slot_interface &device) { @@ -48,6 +49,7 @@ void mac_nubus_cards(device_slot_interface &device) device.option_add("bootbug", NUBUS_BOOTBUG); // Brigent BootBug debugger card device.option_add("quadralink", NUBUS_QUADRALINK); // AE Quadralink serial card device.option_add("laserview", NUBUS_LASERVIEW); // Sigma Designs LaserView monochrome video card + device.option_add("thunder4gx", NUBUS_THUNDERIVGX); // Radius Thunder IV GX video card } @@ -92,6 +94,7 @@ void mac_iisi_cards(device_slot_interface &device) device.option_add("bootbug", NUBUS_BOOTBUG); // Brigent BootBug debugger card device.option_add("quadralink", NUBUS_QUADRALINK); // AE Quadralink serial card device.option_add("laserview", NUBUS_LASERVIEW); // Sigma Designs LaserView monochrome video card + device.option_add("thunder4gx", NUBUS_THUNDERIVGX); // Radius Thunder IV GX video card device.option_add("cb264se30", PDS030_CB264SE30); // RasterOps Colorboard 264/SE30 device.option_add("pc816", PDS030_PROCOLOR816); // Lapis ProColor Server 8*16 PDS diff --git a/src/devices/bus/nubus/thunder4gx.cpp b/src/devices/bus/nubus/thunder4gx.cpp new file mode 100644 index 00000000000..fbfdcb5209e --- /dev/null +++ b/src/devices/bus/nubus/thunder4gx.cpp @@ -0,0 +1,810 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/*************************************************************************** + + Radius Thunder IV GX video card emulation + + NuBus video card, 1/4/8/24 bit color, works up to at least MacOS 9.1. + Emulation by R. Belmont, with hat tips to Vas Crabb and Al Kossow. + + This is Radius branded, but it was released shortly after the Radius/SuperMac + merger and the hardware is distinctly SuperMac flavored. So it's likely a + finished/near finished SuperMac design that was just rebranded. The accelerator + registers look a lot like an evolved version of the blitter in the Spectrum PDQ, + and the CRTC is similar to the SuperMac CRTC in nubus/supermac.cpp. + + Usage: + - Hold down "T" when the Radius screen appears after the beep, and keep it + held down until you see the video mode you want. Many more options are + available if you install RadiusWare 3.4.1 (earlier versions may not + support this card). + - Do NOT enable acceleration in RadiusWare, however. The emulation isn't there yet. + + TODO: + - Monitor sensing (working for standard Apple monitors, but there are others) + - Sub-model sensing (We're IDing as the 1366, the 1600 is the top-end model) + - Acceleration/blitter (it's part-way there but not fully) + - Photoshop acceleration DSPs. These didn't work past MacOS 7.6 on hardware + and the PPC-native version of Photoshop was as fast for cheaper, but it'd + still be fun. + +***************************************************************************/ + +#include "emu.h" +#include "thunder4gx.h" + +#include "emupal.h" +#include "screen.h" + +#define LOG_BLITTER (1U << 1) +#define LOG_CLOCKGEN (1U << 2) +#define LOG_MONSENSE (1U << 3) +#define LOG_RAMDAC (1U << 4) + +#define VERBOSE (0) +#include "logmacro.h" + +namespace { + +static constexpr u32 VRAM_SIZE = 0x600000; + +class nubus_thunder4gx_device : public device_t, + public device_nubus_card_interface +{ +public: + // construction/destruction + nubus_thunder4gx_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + nubus_thunder4gx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + required_device<screen_device> m_screen; + required_device<palette_device> m_palette; + required_ioport m_monitor_config; + + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // optional information overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual const tiny_rom_entry *device_rom_region() const override; + virtual ioport_constructor device_input_ports() const override; + + u32 registers_r(offs_t offset, u32 mem_mask); + void registers_w(offs_t offset, u32 data, u32 mem_mask); + u32 ramdac_r(offs_t offset, u32 mem_mask); + void ramdac_w(offs_t offset, u32 data, u32 mem_mask); + u32 accel_r(offs_t offset, u32 mem_mask); + void accel_w(offs_t offset, u32 data, u32 mem_mask); + void clockgen_w(offs_t offset, u32 data, u32 mem_mask); + +private: + u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + void vblank_w(int state); + + std::unique_ptr<u32[]> m_vram; + u32 m_mode, m_irq_control; + u32 m_pal_idx, m_pal_address; + u32 m_hres, m_vres, m_stride, m_htotal, m_vtotal; + u32 m_display_enable; + u32 m_pixel_clock; + u32 m_crtc[12]; + u32 m_accel_regs[0x2000/4]; + u32 m_monitor_id; + + u32 m_blitter_dest_address, m_blitter_src_address, m_blitter_param1, m_blitter_param2, m_blitter_small_pattern, m_blitter_mask; + u32 m_blitter_medium_pattern[16]; + u32 m_blitter_large_pattern[64]; +}; + +ROM_START( thundergx ) + ROM_REGION(0x10000, "declrom", 0) + ROM_LOAD("radius thunder gx v1.2.3.rom", 0x000000, 0x010000, CRC(b8e035b6) SHA1(c5a1eab749b76aaf3e0ede2120fa99dd7f720196)) +ROM_END + +static constexpr u8 ext(u8 bc, u8 ac, u8 ab) +{ + return 0x40 | (bc << 4) | (ac << 2) | ab; +} + +static constexpr u8 ext6(u8 bc, u8 ac, u8 ab) +{ + return 0xc0 | (bc << 4) | (ac << 2) | ab; +} + +static INPUT_PORTS_START(monitor_config) + PORT_START("monitor") + PORT_CONFNAME(0x1ff, ext6(0, 2, 3), "Monitor type") + PORT_CONFSETTING(0x00, u8"Mac 21\" Color Display (1152\u00d7870)") // "RGB 2 Page" or "Kong" + PORT_CONFSETTING(0x01, u8"Mac Portrait Display (B&W 15\" 640\u00d7870)") // "Full Page" or "Portrait" + PORT_CONFSETTING(0x02, u8"Mac RGB Display (12\" 512\u00d7384)") // "Rubik" (modified IIgs AppleColor RGB) + PORT_CONFSETTING(0x03, u8"Mac Two-Page Display (B&W 21\" 1152\u00d7870)") // "2 Page" + PORT_CONFSETTING(0x06, u8"Mac Hi-Res Display (12-14\" 640\u00d7480)") // "High Res" + PORT_CONFSETTING(ext(1, 1, 3), "640\u00d7480 VGA") + PORT_CONFSETTING(ext(2, 3, 1), "832\u00d7624 16\" RGB") // "Goldfish" or "16 inch RGB" + PORT_CONFSETTING(ext(3, 2, 2), "1024x\u00d7768 19\" RGB") + PORT_CONFSETTING(ext6(0, 0, 3), "Multiple Scan 14\"") + PORT_CONFSETTING(ext6(0, 2, 3), "Multiple Scan 16\"") + PORT_CONFSETTING(ext6(2, 0, 3), "Multiple Scan 21\"") +INPUT_PORTS_END + +void nubus_thunder4gx_device::device_add_mconfig(machine_config &config) +{ + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_screen_update(FUNC(nubus_thunder4gx_device::screen_update)); + m_screen->set_size(1600, 1200); + m_screen->set_visarea(0, 1152 - 1, 0, 870 - 1); + m_screen->set_refresh_hz(60); + m_screen->screen_vblank().set(FUNC(nubus_thunder4gx_device::vblank_w)); + + PALETTE(config, m_palette).set_entries(256); +} + +const tiny_rom_entry *nubus_thunder4gx_device::device_rom_region() const +{ + return ROM_NAME( thundergx ); +} + +ioport_constructor nubus_thunder4gx_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(monitor_config); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +nubus_thunder4gx_device::nubus_thunder4gx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + nubus_thunder4gx_device(mconfig, NUBUS_THUNDERIVGX, tag, owner, clock) +{ +} + +nubus_thunder4gx_device::nubus_thunder4gx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), + device_nubus_card_interface(mconfig, *this), + m_screen(*this, "screen"), + m_palette(*this, "palette"), + m_monitor_config(*this, "monitor"), + m_mode(0), m_irq_control(0), m_pal_idx(0), m_pal_address(0), + m_hres(0), m_vres(0), m_stride(128), m_htotal(0), m_vtotal(0), + m_display_enable(0), m_pixel_clock(30282182), m_monitor_id(0xffff), + m_blitter_dest_address(0), m_blitter_src_address(0), m_blitter_param1(0), + m_blitter_param2(0), m_blitter_small_pattern(0), m_blitter_mask(0) +{ + std::fill(std::begin(m_crtc), std::end(m_crtc), 0); + std::fill(std::begin(m_blitter_medium_pattern), std::end(m_blitter_medium_pattern), 0); + std::fill(std::begin(m_blitter_large_pattern), std::end(m_blitter_large_pattern), 0);} + +void nubus_thunder4gx_device::device_start() +{ + const u32 slotspace = get_slotspace(); + + install_declaration_rom("declrom"); + + m_vram = std::make_unique<u32[]>(VRAM_SIZE); + + save_item(NAME(m_mode)); + save_item(NAME(m_irq_control)); + save_item(NAME(m_pal_idx)); + save_item(NAME(m_pal_address)); + save_item(NAME(m_stride)); + save_item(NAME(m_hres)); + save_item(NAME(m_vres)); + save_item(NAME(m_htotal)); + save_item(NAME(m_vtotal)); + save_item(NAME(m_pixel_clock)); + save_item(NAME(m_display_enable)); + save_pointer(NAME(m_vram), VRAM_SIZE); + + install_bank(slotspace, slotspace+VRAM_SIZE-1, &m_vram[0]); + + nubus().install_writeonly_device(slotspace+0xc00000, slotspace+0xc0000f, emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::clockgen_w))); + nubus().install_device(slotspace+0xc40000, slotspace+0xc8ffff, emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::registers_r)), emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::registers_w))); + nubus().install_device(slotspace+0xcc0000, slotspace+0xcc1fff, emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::accel_r)), emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::accel_w))); + nubus().install_device(slotspace+0xd00000, slotspace+0xd000ff, emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::ramdac_r)), emu::rw_delegate(*this, FUNC(nubus_thunder4gx_device::ramdac_w))); +} + +void nubus_thunder4gx_device::device_reset() +{ + m_pal_idx = 0; + m_pal_address = 0; + m_irq_control = 0; + m_mode = 0; + m_display_enable = 0; +} + +uint32_t nubus_thunder4gx_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + auto const vram8 = util::big_endian_cast<uint8_t const>(&m_vram[0]); + const pen_t *pens = m_palette->pens(); + + if ((m_display_enable != 0xffff) || (m_hres == 0) || (m_vres == 0)) + { + return 0; + } + + switch (m_mode) + { + case 0: // 1bpp + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + for (int x = 0; x < m_hres/8; x++) + { + u8 const pixels = vram8[(y * m_stride) + x]; + + *scanline++ = pens[pixels&0x80]; + *scanline++ = pens[(pixels<<1)&0x80]; + *scanline++ = pens[(pixels<<2)&0x80]; + *scanline++ = pens[(pixels<<3)&0x80]; + *scanline++ = pens[(pixels<<4)&0x80]; + *scanline++ = pens[(pixels<<5)&0x80]; + *scanline++ = pens[(pixels<<6)&0x80]; + *scanline++ = pens[(pixels<<7)&0x80]; + } + } + break; + + case 2: // 4bpp + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + for (int x = 0; x < m_hres/2; x++) + { + u8 const pixels = vram8[(y * m_stride) + x]; + + *scanline++ = pens[((pixels << 0) & 0xf0)]; + *scanline++ = pens[((pixels << 4) & 0xf0)]; + } + } + break; + + case 3: // 8bpp + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + for (int x = 0; x < m_hres; x++) + { + u8 const pixels = vram8[(y * m_stride) + x]; + + *scanline++ = pens[pixels]; + } + } + break; + + case 6: // 16bpp x555 + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + for (int x = 0; x < m_hres; x++) + { + u16 const pixels = (vram8[(y * m_stride) + (x << 1)] << 8) | vram8[(y * m_stride) + (x << 1) + 1]; + *scanline++ = rgb_t(((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, (pixels & 0x1f) << 3); + } + } + break; + + case 7: // 24bpp (arranged as 32 bits, so there's a wasted byte per pixel) + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + u32 const *base = &m_vram[(y * (m_stride >> 2))]; + for (int x = 0; x < m_hres; x++) + { + *scanline++ = *base++; + } + } + break; + } + + return 0; +} + +void nubus_thunder4gx_device::vblank_w(int state) +{ + if ((state) && (BIT(m_irq_control, 1))) + { + raise_slot_irq(); + } +} + +u32 nubus_thunder4gx_device::registers_r(offs_t offset, u32 mem_mask) +{ + switch (offset) + { + case 0: + return 0x6; + + // bit 16 = accelerator ready flag (or active-low busy) + // bit 17 = vblank + case 1: + return m_screen->vblank() ? 0x30000 : 0x10000; + + case 2: + return m_irq_control; + + case 3: // IRQ routine reads this immediately after writing to ack the IRQ (or does reading this ack it?) + return 0; + + case 9: + // 0a0028 = RadiusColor Twin + return 0x0a0028; + + case 0x10000: // mode + return m_mode; + + case 0x10002: // monitor sense + { + u8 mon = m_monitor_config->read(); + u8 monitor_id = ((m_monitor_id >> 2) ^ 7) & 0x7; + u8 res; + if (mon & 0x40) + { + res = 7; + + if (mon & 0x80) + { + res = 6; + } + + if (monitor_id == 0x4) + { + res &= 4 | (BIT(mon, 5) << 1) | BIT(mon, 4); + } + if (monitor_id == 0x2) + { + res &= (BIT(mon, 3) << 2) | 2 | BIT(mon, 2); + } + if (monitor_id == 0x1) + { + res &= (BIT(mon, 1) << 2) | (BIT(mon, 0) << 1) | 1; + } + } + else + { + res = mon; + } + + LOGMASKED(LOG_MONSENSE, "Sense result = %x\n", res); + return (res << 2) | 0x3; + } + + default: + LOGMASKED(LOG_GENERAL, "Read @ C4xxxx %08x mask %08x\n", offset, mem_mask); + break; + } + return 0; +} + +void nubus_thunder4gx_device::registers_w(offs_t offset, u32 data, u32 mem_mask) +{ + data &= mem_mask; + switch (offset) + { + case 1: + if (data & 2) + { + lower_slot_irq(); + } + break; + + case 2: + m_irq_control = data; + break; + + case 9: + LOGMASKED(LOG_MONSENSE, "Write %08x to extended sense, mask %08x\n", data, mem_mask); + break; + + case 0x10000: + m_mode = data & 0xf; + LOGMASKED(LOG_GENERAL, "%x to mode\n", data); + break; + + case 0x10002: + LOGMASKED(LOG_MONSENSE, "%x to monitor drive\n", data); + m_monitor_id = data; + break; + + case 0x10005: case 0x10006: case 0x10007: case 0x10008: case 0x10009: case 0x1000a: + case 0x1000b: case 0x1000c: case 0x1000d: case 0x1000e: case 0x1000f: + m_crtc[offset - 0x10005] = data & mem_mask; + LOGMASKED(LOG_GENERAL, "%04x to CRTC @ %x (%x)\n", data & mem_mask, offset, offset - 0x10005); + break; + + default: + LOGMASKED(LOG_GENERAL, "%s Write @ C4xxxx: %x to reg %x (mask %x)\n", machine().describe_context().c_str(), data & mem_mask, offset, mem_mask); + break; + } +} + +u32 nubus_thunder4gx_device::ramdac_r(offs_t offset, u32 mem_mask) +{ + LOGMASKED(LOG_RAMDAC, "%s: Read DAC @ %08x (mask %08x)\n", machine().describe_context().c_str(), offset, mem_mask); + + return 0; +} + +void nubus_thunder4gx_device::ramdac_w(offs_t offset, u32 data, u32 mem_mask) +{ + LOGMASKED(LOG_RAMDAC, "%s: %x to DAC at %x\n", machine().describe_context().c_str(), data & mem_mask, offset); + switch (offset) + { + case 0: + m_pal_address = (data&0xff); + m_pal_idx = 0; + break; + + case 1: + switch (m_pal_idx) + { + case 0: + m_palette->set_pen_red_level(m_pal_address, ((data>>2) & 0xff)); + break; + + case 1: + m_palette->set_pen_green_level(m_pal_address, ((data >> 2) & 0xff)); + break; + + case 2: + m_palette->set_pen_blue_level(m_pal_address, ((data >> 2) & 0xff)); + break; + } + + m_pal_idx++; + if (m_pal_idx == 3) + { + m_pal_address++; + m_pal_idx = 0; + } + break; + + case 2: + m_display_enable = data & mem_mask; + LOGMASKED(LOG_GENERAL, "%x to display enable\n", m_display_enable); + break; + + default: + LOGMASKED(LOG_GENERAL, "%08x to DAC @ %x\n", data & mem_mask, offset); + break; + } +} + +u32 nubus_thunder4gx_device::accel_r(offs_t offset, u32 mem_mask) +{ + LOGMASKED(LOG_BLITTER, "Read accelerator @ %08x (mask %08x)\n", offset, mem_mask); + + return m_accel_regs[offset]; +} + +void nubus_thunder4gx_device::accel_w(offs_t offset, u32 data, u32 mem_mask) +{ + data &= mem_mask; + + switch (offset) + { + case 2: + LOGMASKED(LOG_GENERAL, "%d to stride\n", data); + m_stride = data; + break; + + case 0x41: // command + // 0x1201 is a small pattern fill from the 4 byte pattern in register 0x47 + // 0x1401 is a medium pattern fill from the 64 bytes of pattern data in registers 0x80-0x8f + // 0x1045 is a large pattern fill from the 256 bytes of pattern data in registers 0x100-0x13f + // 0x1002 is a copy from the source VRAM address in register 0x42 to the destination VRAM address in register 0x43 + + LOGMASKED(LOG_BLITTER, "Blitter opcode %04x\n", data); + + // The blitter is only used in 8, 16, and 32 bpp modes. 1/2/4 bpp are unaccelerated. + assert(m_mode >= 3); + switch (data) + { + case 0x1002: // VRAM to VRAM copy + switch (m_mode) + { + case 3: // 8 bpp + { + u32 vram_src = (m_blitter_src_address & 0x7fffff); + u32 vram_dest = (m_blitter_dest_address & 0x7fffff); + const u16 width = m_blitter_param1 & 0xffff; + const u16 height = m_blitter_param1 >> 16; + u8 *vram8 = (u8 *)&m_vram[0]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + vram8[BYTE4_XOR_BE(vram_dest + x)] = vram8[BYTE4_XOR_BE(vram_src + x)]; + } + vram_src += m_stride; + vram_dest += m_stride; + } + } + break; + + case 5: // 16 bpp + { + u32 vram_src = (m_blitter_src_address & 0x7fffff) >> 1; + u32 vram_dest = (m_blitter_dest_address & 0x7fffff) >> 1; + const u16 width = m_blitter_param1 & 0xffff; + const u16 height = m_blitter_param1 >> 16; + u16 *vram16 = (u16 *)&m_vram[0]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + vram16[WORD_XOR_BE(vram_dest + x)] = vram16[WORD_XOR_BE(vram_src + x)]; + } + vram_src += (m_stride >> 1); + vram_dest += (m_stride >> 1); + } + } + break; + + case 6: // 32 bpp + { + u32 vram_src = (m_blitter_src_address & 0x7fffff) >> 2; + u32 vram_dest = (m_blitter_dest_address & 0x7fffff) >> 2; + const u16 width = m_blitter_param1 & 0xffff; + const u16 height = m_blitter_param1 >> 16; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + m_vram[vram_dest + x] = m_vram[vram_src + x]; + } + vram_src += (m_stride >> 2); + vram_dest += (m_stride >> 2); + } + } + break; + } + break; + + case 0x1201: + switch (m_mode) + { + case 3: // 8 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff); + const u16 width = m_blitter_param1 & 0xffff; + const u16 height = m_blitter_param1 >> 16; + u8 *vram8 = (u8 *)&m_vram[0]; + const u8 *pattern8 = (u8 *)&m_blitter_small_pattern; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + vram8[BYTE4_XOR_BE(vram_offs + x)] = pattern8[x % 3]; // is this relative to the start or is the pattern fixed? + } + vram_offs += m_stride; + } + } + break; + + case 5: // 16 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff) >> 1; + const u16 width = m_blitter_param1 & 0xffff; + const u16 height = m_blitter_param1 >> 16; + u16 *vram16 = (u16 *)&m_vram[0]; + const u16 *pattern16 = (u16 *)&m_blitter_small_pattern; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + vram16[WORD_XOR_BE(vram_offs + x)] = pattern16[x & 1]; + } + vram_offs += (m_stride >> 1); + } + } + break; + + case 6: // 32 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff) >> 2; + u16 width = m_blitter_param1 & 0xffff; + u16 height = m_blitter_param1 >> 16; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + m_vram[vram_offs + x] = m_blitter_small_pattern; + } + vram_offs += (m_stride >> 2); + } + } + break; + } + break; + + case 0x1401: + switch (m_mode) + { + case 3: // 8 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff); + const u16 width = (m_blitter_param1 & 0xffff); + const u16 height = m_blitter_param1 >> 16; + u16 pat_width = (m_blitter_param2 & 0xff); + u16 pat_height = (m_blitter_param2 >> 8) & 0xff; + u8 *vram8 = (u8 *)&m_vram[0]; + const u8 *pattern8 = (u8 *)&m_blitter_medium_pattern; + + if (pat_width == 0) + { + pat_width = (m_blitter_param2 >> 16) & 0xff; + } + + if (pat_height == 0) + { + pat_height = (m_blitter_param2 >> 24) & 0xff; + } + + // is this some kind of indirection or protection or something? + if (pat_width == 0) + { + pat_width = pat_height = 8; + } + + LOGMASKED(LOG_BLITTER, "Medium pattern fill 8: offs %08x width %d height %d pat width %d pat height %d\n", vram_offs, width, height, pat_width, pat_height); + int pat_offs = 0; + for (int y = 0; y < height; y++) + { + pat_offs = ((y % pat_height) * pat_width); + LOGMASKED(LOG_BLITTER, "Line %d vram offs %08x pattern offs %d\n", y, vram_offs, pat_offs % (16 * 4)); + for (int x = 0; x < width; x++) + { + vram8[BYTE4_XOR_BE(vram_offs + x)] = pattern8[(pat_offs + (x % pat_width)) % (16 * 4)]; + } + vram_offs += m_stride; + } + } + break; + + case 5: // 16 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff)>>1; + const u16 width = (m_blitter_param1 & 0xffff); + const u16 height = m_blitter_param1 >> 16; + const u16 pat_width = (m_blitter_param2 & 0xff); + const u16 pat_height = (m_blitter_param2 >> 8) & 0xff; + u16 *vram16 = (u16 *)&m_vram[0]; + const u16 *pattern16 = (u16 *)&m_blitter_medium_pattern; + + LOGMASKED(LOG_BLITTER, "Medium pattern fill 16: offs %08x width %d height %d pat width %d pat height %d\n", vram_offs, width, height, pat_width, pat_height); + int pat_offs = 0; + for (int y = 0; y < height; y++) + { + pat_offs = ((y % pat_height) * pat_width); + LOGMASKED(LOG_BLITTER, "Line %d vram offs %08x pattern offs %d\n", y, vram_offs, pat_offs % (16 * 2)); + for (int x = 0; x < width; x++) + { + vram16[WORD_XOR_BE(vram_offs + x)] = pattern16[(pat_offs + (x % pat_width)) % (16 * 2)]; + } + vram_offs += m_stride; + } + } + break; + + case 6: // 32 bpp + { + u32 vram_offs = (m_blitter_dest_address & 0x7fffff) >> 2; // convert address to offset in 32-bit words + u16 width = (m_blitter_param1 & 0xffff) >> 2; + u16 height = m_blitter_param1 >> 16; + u16 pat_width = (m_blitter_param2 & 0xff) >> 2; + u16 pat_height = (m_blitter_param2 >> 8) & 0xff; + LOGMASKED(LOG_BLITTER, "Medium pattern fill 32: offs %08x width %d height %d pat width %d pat height %d\n", vram_offs, width, height, pat_width, pat_height); + int pat_offs = 0; + for (int y = 0; y < height; y++) + { + pat_offs = ((y % pat_height) * pat_width); + LOGMASKED(LOG_BLITTER, "Line %d vram offs %08x pattern offs %d\n", y, vram_offs, pat_offs); + for (int x = 0; x < width; x++) + { + m_vram[vram_offs + x] = m_blitter_medium_pattern[(pat_offs + (x % pat_width)) % 16]; + } + vram_offs += (m_stride >> 2); + } + } + break; + } + break; + } + break; + + case 0x42: // physical source VRAM address (does this mean the card can accelerate cards in other slots?) + LOGMASKED(LOG_BLITTER, "%08x to blitter source\n", data); + m_blitter_src_address = data; + break; + + case 0x43: // physical destination VRAM address (does this mean the card can accelerate cards in other slots?) + LOGMASKED(LOG_BLITTER, "%08x to blitter destination\n", data); + m_blitter_dest_address = data; + break; + + case 0x44: // Blitter parameter 1 + LOGMASKED(LOG_BLITTER, "%08x to blitter param 1 (mask %08x)\n", data, mem_mask); + m_blitter_param1 = data; + break; + + case 0x45: // Blitter parameter 2 + LOGMASKED(LOG_BLITTER, "%08x to blitter param 2 (mask %08x)\n", data, mem_mask); + m_blitter_param2 = data; + break; + + case 0x49: // Small blitter pattern + LOGMASKED(LOG_BLITTER, "%08x to small pattern\n", data); + m_blitter_small_pattern = data; + break; + + case 0x48: // Blitter mask? + LOGMASKED(LOG_BLITTER, "%08x to mask?\n", data); + m_blitter_mask = data; + break; + + case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: + LOGMASKED(LOG_BLITTER, "%08x to medium pattern @ %x\n", data, offset & 0xf); + m_blitter_medium_pattern[offset & 0xf] = data; + break; + + case 0x100: case 0x101: case 0x102: case 0x103: case 0x104: case 0x105: case 0x106: case 0x107: + case 0x108: case 0x109: case 0x10a: case 0x10b: case 0x10c: case 0x10d: case 0x10e: case 0x10f: + case 0x110: case 0x111: case 0x112: case 0x113: case 0x114: case 0x115: case 0x116: case 0x117: + case 0x118: case 0x119: case 0x11a: case 0x11b: case 0x11c: case 0x11d: case 0x11e: case 0x11f: + case 0x120: case 0x121: case 0x122: case 0x123: case 0x124: case 0x125: case 0x126: case 0x127: + case 0x128: case 0x129: case 0x12a: case 0x12b: case 0x12c: case 0x12d: case 0x12e: case 0x12f: + case 0x130: case 0x131: case 0x132: case 0x133: case 0x134: case 0x135: case 0x136: case 0x137: + case 0x138: case 0x139: case 0x13a: case 0x13b: case 0x13c: case 0x13d: case 0x13e: case 0x13f: + LOGMASKED(LOG_BLITTER, "%08x to large pattern @ %x\n", data, offset - 0x100); + m_blitter_large_pattern[offset - 0x100] = data; + break; + + default: + LOGMASKED(LOG_BLITTER, "%s: %08x to blitter @ %08x (mask %08x)\n", machine().describe_context().c_str(), data, offset, mem_mask); + break; + } + + m_accel_regs[offset] = data; +} + +// IC Designs ICD2062B frequency synthesizer frontend. +// The chip has an I2C-style CLK/DATA 2 wire serial interface, but on this card +// you just write all 21 bits as a longword and the ASIC does it for you. +void nubus_thunder4gx_device::clockgen_w(offs_t offset, u32 data, u32 mem_mask) +{ + const int irange = (data >> 17) & 0xf; + const int p = ((data >> 10) & 0x7f) + 3; + const int mux = (data >> 7) & 0x7; + const int q = (data & 0x7f) + 2; + + LOGMASKED(LOG_CLOCKGEN, "I %d P %d Mux %d Q %d\n", irange, p, mux, q); + + double result = 2.0f * 14.3181818f * (double)p; + result /= (double)q; + result /= (double)(1 << mux); + m_pixel_clock = (int)(result * 1000000 + .5); + LOGMASKED(LOG_CLOCKGEN, "result %f, pixel clock %d\n", result, m_pixel_clock); + + m_hres = (m_crtc[2] - m_crtc[1]) << 2; + m_vres = (m_crtc[7] - m_crtc[6]); + m_htotal = m_crtc[3] << 2; + m_vtotal = m_crtc[8]; + + if ((m_hres > 0) && (m_vres > 0)) + { + const double refresh = (double)m_pixel_clock / (double)(m_htotal * m_vtotal); + LOGMASKED(LOG_CLOCKGEN, "hres %d vres %d htotal %d vtotal %d refresh %f stride %d mode %d\n", m_hres, m_vres, m_htotal, m_vtotal, refresh, m_stride, m_mode); + + rectangle visarea(0, m_hres - 1, 0, m_vres - 1); + m_screen->configure(m_htotal, m_vtotal, visarea, attotime::from_ticks(m_htotal * m_vtotal, m_pixel_clock).as_attoseconds()); + } +} + +} // anonymous namespace + +DEFINE_DEVICE_TYPE_PRIVATE(NUBUS_THUNDERIVGX, device_nubus_card_interface, nubus_thunder4gx_device, "nb_thungx", "Radius Thunder IV GX video card") diff --git a/src/devices/bus/nubus/thunder4gx.h b/src/devices/bus/nubus/thunder4gx.h new file mode 100644 index 00000000000..625238c0907 --- /dev/null +++ b/src/devices/bus/nubus/thunder4gx.h @@ -0,0 +1,12 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +#ifndef MAME_BUS_NUBUS_THUNDER_4_GX_H +#define MAME_BUS_NUBUS_THUNDER_4_GX_H + +#pragma once + +#include "nubus.h" + +DECLARE_DEVICE_TYPE(NUBUS_THUNDERIVGX, device_nubus_card_interface) + +#endif // MAME_BUS_NUBUS_THUNDER_4_GX_H |