summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2019-08-29 19:49:21 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2019-08-29 19:49:21 +0700
commit194691c63475ed5b5beee0174e119e946944613b (patch)
treee06dcf96efb93e88554f4d9eb0c6a30ac5dda2c6 /src
parent23a5e123030ddc65b970c2b23a1f77e1ee74675e (diff)
4dpi: graphics wip (nw)
Diffstat (limited to 'src')
-rw-r--r--src/mame/drivers/4dpi.cpp22
-rw-r--r--src/mame/video/sgi_ge5.cpp532
-rw-r--r--src/mame/video/sgi_ge5.h110
-rw-r--r--src/mame/video/sgi_gr1.cpp472
-rw-r--r--src/mame/video/sgi_gr1.h232
-rw-r--r--src/mame/video/sgi_re2.cpp80
-rw-r--r--src/mame/video/sgi_re2.h96
7 files changed, 1542 insertions, 2 deletions
diff --git a/src/mame/drivers/4dpi.cpp b/src/mame/drivers/4dpi.cpp
index 5f5add7c5c7..76310244261 100644
--- a/src/mame/drivers/4dpi.cpp
+++ b/src/mame/drivers/4dpi.cpp
@@ -66,7 +66,7 @@
#include "bus/rs232/hlemouse.h"
// video and audio
-#include "screen.h"
+#include "video/sgi_gr1.h"
#define LOG_GENERAL (1U << 0)
@@ -87,6 +87,7 @@ public:
, m_enet(*this, "enet")
, m_duart(*this, "duart%u", 0U)
, m_serial(*this, "serial%u", 1U)
+ , m_gfx(*this, "gfx")
, m_leds(*this, "led%u", 0U)
{
}
@@ -107,6 +108,7 @@ private:
required_device<am7990_device> m_enet;
required_device_array<scn2681_device, 2> m_duart;
required_device_array<rs232_port_device, 2> m_serial;
+ required_device<sgi_gr1_device> m_gfx;
enum leds : unsigned
{
@@ -241,6 +243,7 @@ void pi4d2x_state::map(address_map &map)
//map(0x1e000000, 0x1effffff); // vme a24 modifier 0x39 non-privileged
//map(0x1f000000, 0x1fbfffff); // local I/O (duarts, timers, etc.)
+ map(0x1f000000, 0x1f003fff).m(m_gfx, FUNC(sgi_gr1_device::map));
map(0x1f800000, 0x1f800003).lrw8("memcfg", [this]() { return m_memcfg; }, [this](u8 data) { m_memcfg = data; }).umask32(0xff000000);
map(0x1f800000, 0x1f800003).r(FUNC(pi4d2x_state::sysid_r)).umask32(0x00ff0000);
@@ -291,7 +294,7 @@ void pi4d2x_state::map(address_map &map)
m_eeprom->cs_write(BIT(data, 5));
m_eeprom->clk_write(BIT(data, 6));
- //BIT(data, 7); // gfx_reset: reset graphics subsystem
+ m_gfx->reset_w(BIT(data, 7));
m_cpuauxctl = data;
}).umask32(0xff000000);
@@ -593,6 +596,21 @@ void pi4d2x_state::common(machine_config &config)
m_serial[1]->cts_handler().set(m_duart[1], FUNC(scn2681_device::ip1_w));
m_serial[1]->dcd_handler().set(m_duart[1], FUNC(scn2681_device::ip2_w));
+ // graphics
+ SGI_GR12(config, m_gfx, 0);
+ m_gfx->out_vblank().set(
+ [this](int state)
+ {
+ if (state)
+ m_lio_isr |= LIO_VRSTAT;
+ else
+ m_lio_isr &= ~LIO_VRSTAT;
+
+ lio_interrupt(LIO_VR, state);
+ });
+ m_gfx->out_int_ge().set(*this, FUNC(pi4d2x_state::lio_interrupt<LIO_GE>)).invert();
+ m_gfx->out_int_fifo().set(*this, FUNC(pi4d2x_state::lio_interrupt<LIO_FIFO>)).invert();
+
// TODO: vme slot, cpu interrupt 0
}
diff --git a/src/mame/video/sgi_ge5.cpp b/src/mame/video/sgi_ge5.cpp
new file mode 100644
index 00000000000..d7e471aa6fd
--- /dev/null
+++ b/src/mame/video/sgi_ge5.cpp
@@ -0,0 +1,532 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Silicon Graphics GE5 and HQ1 devices.
+ *
+ * This board handles the interface to the host, and mainly consists of the HQ1
+ * instruction sequencer and the WTL3132 floating-point accelerator. The board
+ * also contains instruction and data RAM for the HQ1, and a FIFO for host
+ * communication.
+ *
+ * The undocumented HQ1 microcode instruction format is relatively well decoded
+ * now, but the exact timing and function of many operations remains incomplete.
+ *
+ * TODO:
+ * - skeleton only
+ *
+ */
+
+#include "emu.h"
+#include "debugger.h"
+#include "sgi_ge5.h"
+
+#define LOG_GENERAL (1U << 0)
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(SGI_GE5, sgi_ge5_device, "ge5", "SGI Geometry Engine 5")
+
+sgi_ge5_device::sgi_ge5_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig, SGI_GE5, tag, owner, clock)
+ , m_code_config("code", ENDIANNESS_BIG, 64, 15, -3, address_map_constructor(FUNC(sgi_ge5_device::code_map), this))
+ , m_data_config("data", ENDIANNESS_BIG, 32, 13, -2, address_map_constructor(FUNC(sgi_ge5_device::data_map), this))
+ , m_fpu(*this, "fpu")
+ , m_int_cb(*this)
+ , m_fifo_empty(*this)
+ , m_fifo_read(*this)
+ , m_re_r(*this)
+ , m_re_w(*this)
+ , m_icount(0)
+{
+}
+
+void sgi_ge5_device::device_add_mconfig(machine_config &config)
+{
+ WTL3132(config, m_fpu, clock());
+ m_fpu->out_fpcn().set([this](int state) { m_fpu_c = state; });
+ m_fpu->out_zero().set([this](int state) { m_fpu_z = state; });
+ m_fpu->out_port_x().set([this](u32 data) { m_bus = data; LOG("m_bus = %x\n", data); });
+}
+
+
+void sgi_ge5_device::code_map(address_map &map)
+{
+ map(0x0000, 0x3fff).ram().share("code");
+}
+
+void sgi_ge5_device::data_map(address_map &map)
+{
+ map(0x0000, 0x1fff).ram().share("data");
+}
+
+void sgi_ge5_device::device_start()
+{
+ m_int_cb.resolve_safe();
+ m_fifo_empty.resolve();
+ m_fifo_read.resolve();
+ m_re_r.resolve();
+ m_re_w.resolve();
+
+ // TODO: save state
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+ state_add(0, "PC", m_pc).formatstr("%04X");
+
+ state_add(1, "MEMPTR", m_memptr).formatstr("%04X");
+ state_add(2, "REPTR", m_reptr).formatstr("%04X");
+
+ set_icountptr(m_icount);
+}
+
+void sgi_ge5_device::device_reset()
+{
+ m_reptr = 0;
+ m_memptr = 0;
+
+ m_sp = 0;
+
+ if (m_int_state)
+ {
+ m_int_state = 0;
+ m_int_cb(m_int_state);
+ }
+
+ suspend(SUSPEND_REASON_HALT, false);
+ LOG("stalled\n");
+}
+
+device_memory_interface::space_config_vector sgi_ge5_device::memory_space_config() const
+{
+ return space_config_vector
+ {
+ std::make_pair(AS_PROGRAM, &m_code_config),
+ std::make_pair(AS_DATA, &m_data_config),
+ };
+}
+
+std::unique_ptr<util::disasm_interface> sgi_ge5_device::create_disassembler()
+{
+ return std::make_unique<sgi_ge5_disassembler>();
+}
+
+enum cx_mask : u8
+{
+ INCMEM = 0x08, // increment memptr
+ FIELD2 = 0x10, // second field active
+ INCRE = 0x20, // increment reptr
+ SRC = 0xc0, // data bus source
+ DST = 0x06, // data bus source
+ STALL = 0x01,
+};
+
+void sgi_ge5_device::execute_run()
+{
+ if (m_fetch)
+ {
+ // stall if fifo empty
+ if (m_fifo_empty())
+ {
+ suspend(SUSPEND_REASON_HALT, false);
+ return;
+ }
+
+ // fetch from fifo and set pc
+ u64 data = m_fifo_read();
+ m_bus = u32(data);
+ m_pc = (data >> 31) & 0x1fe;
+ m_fetch = false;
+ }
+
+ while (m_icount > 0)
+ {
+ debugger_instruction_hook(m_pc);
+
+ u64 const insn = space(AS_PROGRAM).read_qword(m_pc);
+ LOG("pc 0x%04x code 0x%011x\n", m_pc, insn);
+
+ u8 const hq1_op = (insn >> 32) & 0xff;
+ unsigned const src = (hq1_op & SRC) >> 6;
+ unsigned const dst = (hq1_op & DST) >> 1;
+ bool const stall = (hq1_op & STALL);
+ if (hq1_op & FIELD2)
+ LOG("hq1 op 0x%02x src %d dst %d stall %d branch %d\n", hq1_op, src, dst, stall, (insn >> 29) & 7);
+ else
+ LOG("hq1 op 0x%02x src %d dst %d stall %d\n", hq1_op, src, dst, stall);
+
+ // stall when fifo empty and reading from fifo or stall flag
+ if (((src == 1) || stall) && m_fifo_empty())
+ {
+ // enter fetch mode if destination is fetch
+ m_fetch = (dst == 1);
+ LOG(m_fetch ? "FETCH\n" : "STALL\n");
+ suspend(SUSPEND_REASON_HALT, false);
+ m_icount = 0;
+ continue;
+ }
+
+ u16 branch = 0;
+ if (hq1_op & FIELD2)
+ {
+ // handle field2
+ u64 const field2 = space(AS_PROGRAM).read_qword(++m_pc);
+ u16 const immediate = (field2 >> 19) & 0x7fff;
+
+ LOG("pc 0x%04x pref 0x%011x immediate 0x%04x\n", m_pc, field2, immediate);
+
+ switch ((field2 >> 32) & 0xfc)
+ {
+ case 0x8c:
+ m_reptr = m_bus;
+ LOG("REPTR=0x%04x\n", m_reptr);
+ break;
+ case 0x9c:
+ m_reptr = immediate;
+ LOG("REPTR=0x%04x\n", m_reptr);
+ break;
+ case 0xb0:
+ m_memptr = m_bus;
+ branch = immediate;
+ LOG("MEMPTR=0x%04x BRANCH=0x%04x\n", m_memptr, branch);
+ break;
+ case 0xb4:
+ m_memptr = immediate;
+ LOG("MEMPTR=0x%04x\n", m_memptr);
+ break;
+ case 0xb8:
+ m_memptr_temp = immediate;
+ LOG("MEMPTR_TMP=0x%04x\n", m_memptr_temp);
+ break;
+ case 0xbc:
+ branch = immediate;
+ LOG("BRANCH=0x%04x\n", branch);
+ break;
+ case 0xfc: // also clear interrupt?
+ if (!m_int_state)
+ {
+ LOG("ge interrupt asserted\n");
+ m_int_state = 1;
+ m_int_cb(m_int_state);
+ }
+ break;
+ //case 0xfe: // set dma count?
+ default:
+ LOG("unknown pref 0x%02x\n", (field2 >> 32) & 0xfc);
+ break;
+ }
+ }
+
+ // increment memptr
+ if (hq1_op & INCMEM)
+ {
+ m_memptr++;
+ LOG("MEMPTR++\n");
+ }
+
+ // increment reptr
+ if (hq1_op & INCRE)
+ {
+ m_reptr++;
+ LOG("REPTR++\n");
+ }
+
+ // xx - source (re, fifo, ram, fpu)
+ // x - increment reptr
+ // x - field2
+ // x - increment memptr
+ // xx - destination (re, fetch, ram, fpu)
+ // x - stall? (if fifo empty)
+ // xxx - branch
+
+ // 00xx xxxx -> ?
+ // 01xx xxxx -> read fifo
+ // 10xx xxxx -> read ram
+ // 11xx xxxx -> read fpu
+
+ u64 fpu_ctrl =
+ (m_cwen ? wtl3132_device::M_CWEN : 0) |
+ (2ULL << wtl3132_device::S_ENCN);
+
+ // 43 - get fifo tag
+ // 46 - get fifo data
+ // 83 - stall?
+ // generally reading the fifo causes stalls
+ // 83 also causes a stall (because can't read code from data ram?
+
+ switch (src)
+ {
+ case 0:
+ m_bus = m_re_r(m_reptr);
+ LOG("LOAD RE offset 0x%08x data 0x%08x\n", m_reptr, m_bus);
+ break;
+ case 1:
+ m_bus = m_fifo_read();
+ LOG("LOAD FIFO 0x%08x\n", m_bus);
+ break;
+ case 2:
+ m_bus = space(AS_DATA).read_dword(m_memptr);
+ LOG("LOAD MEM offset 0x%04x data 0x%08x\n", m_memptr, m_bus);
+ break;
+ case 3:
+ // tells this instruction to write to the bus in two cycles from now
+ LOG("FSTORE\n");
+ fpu_ctrl |= (2ULL << wtl3132_device::S_IOCT);
+ break;
+ }
+
+ // i/o dst
+ switch (dst)
+ {
+ case 0:
+ LOG("STORE RE offset 0x%02x data 0x%08x\n", m_reptr, m_bus);
+ m_re_w(m_reptr, m_bus);
+ break;
+ case 1: // fetch
+ if (stall)
+ {
+ m_pc = (m_bus >> 31) & 0x1fe;
+ m_icount--;
+ // skip pc update and fpu step?
+ continue;
+ }
+ break;
+ case 2:
+ space(AS_DATA).write_dword(m_memptr, m_bus);
+ LOG("STORE MEM offset 0x%02x data 0x%08x\n", m_memptr, m_bus);
+ break;
+ case 3:
+ m_fpu->x_port_w(m_bus);
+ fpu_ctrl |= (3ULL << wtl3132_device::S_IOCT);
+ LOG("FLOAD 0x%08x\n", m_bus);
+ break;
+ }
+
+ // hq1 branch
+ if (hq1_op & FIELD2)
+ {
+ switch ((insn >> 29) & 7)
+ {
+ case 0: m_pc++; break;
+ case 1:
+ m_pc = branch;
+ LOG("J 0x%04x\n", m_pc);
+ break;
+ case 2:
+ if (m_fpu_c)
+ {
+ m_pc = branch;
+ LOG("JC 0x%04x\n", m_pc);
+ }
+ break;
+ case 3:
+ if (!m_fpu_c)
+ {
+ m_pc = branch;
+ LOG("JNC 0x%04x\n", m_pc);
+ }
+ break;
+ case 4:
+ m_stack[m_sp] = m_pc + 1;
+ m_sp = (m_sp + 1) & 7;
+ m_pc = branch;
+ LOG("CALL 0x%04x\n", m_pc);
+ break;
+ case 5:
+ if (m_fpu_z)
+ {
+ m_pc = branch;
+ LOG("JZ 0x%04x\n", m_pc);
+ }
+ break;
+ case 6:
+ if (!m_fpu_z)
+ {
+ m_pc = branch;
+ LOG("JNZ 0x%04x\n", m_pc);
+ }
+ break;
+ case 7:
+ m_sp = (m_sp + 7) & 7;
+ m_pc = m_stack[m_sp];
+ LOG("RET 0x%04x\n", m_pc);
+ break;
+ }
+ }
+ else
+ m_pc++;
+
+ // step fpu
+ u64 const fpu = ((insn & 0x1fff'f800ULL) << 5) | ((insn & 0x0000'07ffULL) << 2);
+
+ m_fpu->c_port_w(fpu | fpu_ctrl);
+ m_fpu->clk_w(1);
+
+ m_icount--;
+ }
+}
+
+void sgi_ge5_device::command_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ switch (offset)
+ {
+ case 0x00: // clear stall
+ //resume(SUSPEND_REASON_HALT);
+ LOG("unstalled\n");
+ break;
+
+ case 0x10: // setss
+ case 0x20: // clearss
+ case 0x30: // executess
+ break;
+
+ case 0x50: // clearintr
+ if (m_int_state)
+ {
+ LOG("ge interrupt cleared\n");
+ m_int_state = 0;
+ m_int_cb(m_int_state);
+ }
+ break;
+ }
+}
+
+u32 sgi_ge5_device::code_r(offs_t offset)
+{
+ m_pc = offset | offs_t(m_mar & 0x7f) << 8;
+ u64 const data = space(AS_PROGRAM).read_qword(m_pc);
+
+ return m_mar_msb ? u32(data >> 32) : u32(data);
+}
+
+void sgi_ge5_device::code_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ m_pc = offset | offs_t(m_mar & 0x7f) << 8;
+
+ LOG("code_w msb %d offset 0x%08x data 0x%08x mask 0x%08x (%s)\n", m_mar_msb, m_pc, data, mem_mask, machine().describe_context());
+
+ if (m_mar_msb)
+ {
+ u64 const mask = u64(mem_mask & 0x000000ffU) << 32;
+
+ space(AS_PROGRAM).write_qword(m_pc, u64(data) << 32, mask);
+ }
+ else
+ space(AS_PROGRAM).write_qword(m_pc, data, mem_mask);
+}
+
+u32 sgi_ge5_device::data_r(offs_t offset)
+{
+ m_memptr = offset | offs_t(m_mar & 0x1f) << 8;
+
+ return space(AS_DATA).read_dword(m_memptr);
+}
+
+void sgi_ge5_device::data_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ m_memptr = offset | offs_t(m_mar & 0x1f) << 8;
+
+ LOG("data_w offset 0x%08x data 0x%08x mask 0x%08x (%s)\n", m_memptr, data, mem_mask, machine().describe_context());
+
+ space(AS_DATA).write_dword(m_memptr, data, mem_mask);
+}
+
+offs_t sgi_ge5_disassembler::disassemble(std::ostream &stream, offs_t pc, data_buffer const &opcodes, data_buffer const &params)
+{
+ std::string prefix = "";
+ std::string src, dst;
+ u16 branch;
+
+ u64 const insn = opcodes.r64(pc);
+
+ if (BIT(insn, 36))
+ {
+ u64 const insn_prefix = opcodes.r64(pc + 1);
+ u16 const immediate = (insn_prefix >> 19) & 0x7fff;
+
+ switch ((insn_prefix >> 32) & 0xfc)
+ {
+ case 0x8c: prefix = std::string("LOAD REPTR"); break;
+ case 0x9c: prefix = util::string_format("REPTR=%04x", immediate); break;
+ case 0xb0: prefix = std::string("LOAD MEMPTR"); branch = immediate; break;
+ case 0xb4: prefix = util::string_format("MEMPTR=%04x", immediate); break;
+ case 0xb8: prefix = util::string_format("MEMPTR_TEMP=%04x", immediate); break;
+ case 0xbc: branch = immediate; break;
+ case 0xfc: prefix = std::string("SET_INT"); break;
+ //case 0xfe: // set dma count?
+ }
+ }
+
+ u8 const hq1_op = (insn >> 32) & 0xff;
+
+ // pre-increment memptr
+ //if (BIT(hq1_op, 3))
+ // ;
+
+ // pre-increment reptr
+ //if (BIT(hq1_op, 5))
+ // ;
+
+ // xx - source (?, fifo, ram, fpu)
+ // x - increment reptr
+ // x - ?
+ // x - increment memptr
+ // xxx - destination (re, ?, bus, stall?, ram, ?, fpu, ?)
+
+ // 00xx xxxx -> ?
+ // 01xx xxxx -> read fifo
+ // 10xx xxxx -> read ram
+ // 11xx xxxx -> read fpu
+
+ u64 fpu_ctrl = 0x2'8000'0000; // ENCN=2, IOCT=2
+
+ switch (hq1_op >> 6)
+ {
+ case 0: src = std::string("0?"); break;
+ case 1: src = std::string("fifo"); break;
+ case 2: src = std::string("bus"); break;
+ case 3: src = std::string("fpu"); break; // IOCT=2
+ }
+
+ // i/o dst
+ switch (hq1_op & 7)
+ {
+ case 0: dst = std::string("REPTR"); break;
+ case 1: dst = std::string("1?"); break;
+ case 2: dst = std::string("bus?"); break;
+ case 3: dst = std::string("stall?"); break;
+ case 4: dst = std::string("MEMPTR"); break;
+ case 5: dst = std::string("5?"); break;
+ case 6: dst = std::string("FPU"); fpu_ctrl |= 0x4000'0000; break; // fpu IOCT=3
+ case 7: dst = std::string("7?"); break;
+ }
+
+ std::string fpu = wtl3132_device::disassemble(
+ bitswap<34>((insn & 0x0fff'ffff) | fpu_ctrl,
+ 28, 27, 26, // f
+ 25, 24, 23, 22, 21, // aadd
+ 20, 19, 18, 17, 16, // badd
+ 15, 14, 13, 12, 11, // cadd
+ 29, // cwen*
+ 31, 30, // ioct*
+ 10, 9, 8, 7, 6, // dadd
+ 5, 4, 3, // abin
+ 2, 1, // adst
+ 0, // mbin
+ 33, 32)); // encn*
+
+ // hq1 branch
+ switch ((insn >> 29) & 7)
+ {
+ case 0: util::stream_format(stream, "%s; %s; R:%s, W:%s", prefix, fpu, src, dst); break;
+ case 1: util::stream_format(stream, "%s; %s; R:%s, W:%s; J:%04x", prefix, fpu, src, dst, branch); break;
+ case 2: util::stream_format(stream, "%s; %s; R:%s, W:%s; JC:%04x", prefix, fpu, src, dst, branch); break;
+ case 3: util::stream_format(stream, "%s; %s; R:%s, W:%s; JNC:%04x", prefix, fpu, src, dst, branch); break;
+ case 4: util::stream_format(stream, "%s; %s; R:%s, W:%s; CALL:%04x", prefix, fpu, src, dst, branch); break;
+ case 5: util::stream_format(stream, "%s; %s; R:%s, W:%s; JZ:%04x", prefix, fpu, src, dst, branch); break;
+ case 6: util::stream_format(stream, "%s; %s; R:%s, W:%s; JNZ:%04x", prefix, fpu, src, dst, branch); break;
+ case 7: util::stream_format(stream, "%s; %s; R:%s, W:%s; RET", prefix, fpu, src, dst); break;
+ }
+
+ return BIT(insn, 36) ? 2 : 1;
+}
diff --git a/src/mame/video/sgi_ge5.h b/src/mame/video/sgi_ge5.h
new file mode 100644
index 00000000000..91cf029a211
--- /dev/null
+++ b/src/mame/video/sgi_ge5.h
@@ -0,0 +1,110 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_VIDEO_SGI_GE5_H
+#define MAME_VIDEO_SGI_GE5_H
+
+#pragma once
+
+#include "machine/wtl3132.h"
+
+class sgi_ge5_device : public cpu_device
+{
+public:
+ sgi_ge5_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ auto out_int() { return m_int_cb.bind(); }
+ auto fifo_empty() { return m_fifo_empty.bind(); }
+ auto fifo_read() { return m_fifo_read.bind(); }
+
+ auto re_r() { return m_re_r.bind(); }
+ auto re_w() { return m_re_w.bind(); }
+
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_execute_interface overrides
+ virtual u32 execute_min_cycles() const override { return 1; }
+ virtual u32 execute_max_cycles() const override { return 1; }
+ virtual u32 execute_input_lines() const override { return 1; }
+ virtual void execute_run() override;
+ virtual void execute_set_input(int inputnum, int state) override {}
+
+ u32 code_r(offs_t offset);
+ u32 data_r(offs_t offset);
+ void code_w(offs_t offset, u32 data, u32 mem_mask);
+ void data_w(offs_t offset, u32 data, u32 mem_mask);
+
+ void command_w(offs_t offset, u16 data, u16 mem_mask);
+ u16 pc_r() { return m_pc; }
+ void mar_w(offs_t offset, u32 data, u32 mem_mask) { m_mar = offset & 0x7f; }
+ void mar_msb_w(offs_t offset, u32 data, u32 mem_mask) { m_mar_msb = bool(offset); }
+ void cwen_w(int state) { m_cwen = bool(state); }
+
+protected:
+ void code_map(address_map &map);
+ void data_map(address_map &map);
+
+private:
+ // device configuration state
+ address_space_config m_code_config;
+ address_space_config m_data_config;
+
+ required_device<wtl3132_device> m_fpu;
+
+ devcb_write_line m_int_cb;
+ int m_int_state;
+
+ devcb_read_line m_fifo_empty;
+ devcb_read64 m_fifo_read;
+ devcb_read32 m_re_r;
+ devcb_write32 m_re_w;
+
+ // runtime state
+ int m_icount;
+
+ // cpu state
+ bool m_fetch;
+ u16 m_pc;
+ unsigned m_sp;
+ u16 m_stack[8];
+
+ u16 m_memptr;
+ u16 m_memptr_temp;
+ u8 m_reptr;
+ u16 m_dma_count;
+
+ u64 m_bus;
+
+ u8 m_mar;
+ bool m_mar_msb;
+
+ int m_fpu_c;
+ int m_fpu_z;
+
+ bool m_cwen;
+};
+
+class sgi_ge5_disassembler : public util::disasm_interface
+{
+public:
+ sgi_ge5_disassembler() = default;
+ virtual ~sgi_ge5_disassembler() = default;
+
+ virtual u32 opcode_alignment() const override { return 1; }
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, data_buffer const &opcodes, data_buffer const &params) override;
+
+private:
+};
+
+DECLARE_DEVICE_TYPE(SGI_GE5, sgi_ge5_device)
+
+#endif // MAME_VIDEO_SGI_GE5_H
diff --git a/src/mame/video/sgi_gr1.cpp b/src/mame/video/sgi_gr1.cpp
new file mode 100644
index 00000000000..cd29fe65f61
--- /dev/null
+++ b/src/mame/video/sgi_gr1.cpp
@@ -0,0 +1,472 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Silicon Graphics GR1 (aka Eclipse) graphics hardware.
+ *
+ * This graphics system was first designed for the Personal Iris 4D/20,
+ * connecting via a unique graphics port interface. The system was later
+ * adapted and released under the IrisVision brand, initially with a Micro
+ * Channel Architecture bus interface and support for IBM RS/6000 and some IBM
+ * PS/2 systems. A final variant has a 16-bit ISA bus interface enabling use in
+ * PC-AT and compatible systems.
+ *
+ * The TG-V variation fits into the VME chassis and slots of the SGI V20 and
+ * V30/35, however control and data signals are delivered via a ribbon cable to
+ * the CPU and I/O boards, and only draws power and ground from the VME slot.
+ *
+ * All versions of the hardware consist of two primary boards which mount into
+ * host system slots, plus several daughter-boards which mount on the two
+ * primary boards.
+ *
+ * The board set consists of:
+ *
+ * Name Description Function
+ * Geometry Engine host interface and geometry engine (HQ1, GE5)
+ * Display Engine raster video and display subsystem (RE2 + 1-2 Bt431 cursor chips + 8 pixel, 2 overlay/underlay, 2 wid bitplanes)
+ * Video Driver basic video daughter-board (5 XPC1 ASICs, Bt458 RGB RAMDAC)
+ * Video Driver enhanced video daughter-board (5 XMAP2 ASICs, 8K color map, 3 Bt457 RAMDACs)
+ * OP1 Bitplane Board optional bitplane expansion (adds 2 overlay/underlay, 2 wid planes)
+ * BP4 Bitplane Board optional bitplane expansion (adds 16 pixel, 2 overlay/underlay, 2 wid planes, 4k color map)
+ * ZB3 Z-buffer Z-buffer option (adds 24 Z-buffer planes, requires BP4)
+ * GT1/GT2 Turbo Board turbo graphics option (4 TMS320C30 DSPs)
+ *
+ * Sources:
+ * - http://ohlandl.ipv7.net/video/Iris.html
+ * - https://www.4crawler.com/IrisVision/index.shtml
+ * - http://archive.irix.cc/sgistuff/hardware/graphics/eclipse.html
+ *
+ * TODO:
+ * - ge5/hq1
+ * - raster engine
+ * - dma
+ * - slotify (SGI, MCA, ISA)
+ * - everything else
+ */
+
+#include "emu.h"
+#include "sgi_gr1.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_READS (1U << 1)
+
+//#define VERBOSE (LOG_GENERAL)
+
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(SGI_GR11, sgi_gr11_device, "sgi_gr11", "SGI GR1.1 Graphics")
+DEFINE_DEVICE_TYPE(SGI_GR12, sgi_gr12_device, "sgi_gr12", "SGI GR1.2 Graphics")
+
+sgi_gr1_device::sgi_gr1_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_screen(*this, "screen")
+ , m_re(*this, "re2")
+ , m_ge(*this, "ge5")
+ , m_cursor(*this, "cursor%u", 0U)
+ , m_vblank_cb(*this)
+ , m_int_fifo_cb(*this)
+{
+}
+
+sgi_gr11_device::sgi_gr11_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : sgi_gr1_device(mconfig, SGI_GR11, tag, owner, clock)
+ , m_ramdac(*this, "ramdac")
+{
+}
+
+sgi_gr12_device::sgi_gr12_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : sgi_gr1_device(mconfig, SGI_GR12, tag, owner, clock)
+ , m_ramdac(*this, "ramdac%u", 0U)
+{
+}
+
+void sgi_gr1_device::map(address_map &map)
+{
+ // TODO: bank based on mar_msb
+
+ map(0x0000, 0x03ff).rw(m_ge, FUNC(sgi_ge5_device::code_r), FUNC(sgi_ge5_device::code_w));
+
+ map(0x04c0, 0x04c3).rw(FUNC(sgi_gr1_device::dr1_r), FUNC(sgi_gr1_device::dr1_w)).umask32(0xff000000);
+ map(0x04e0, 0x04e3).rw(FUNC(sgi_gr1_device::dr0_r), FUNC(sgi_gr1_device::dr0_w)).umask32(0xff000000);
+
+ map(0x0560, 0x056f).m(m_cursor[0], FUNC(bt431_device::map)).umask32(0xff000000);
+ map(0x0580, 0x058f).m(m_cursor[1], FUNC(bt431_device::map)).umask32(0xff000000);
+
+ map(0x05a0, 0x05a3).rw(FUNC(sgi_gr1_device::dr4_r), FUNC(sgi_gr1_device::dr4_w)).umask32(0xff000000);
+ map(0x05c0, 0x05c3).rw(FUNC(sgi_gr1_device::dr3_r), FUNC(sgi_gr1_device::dr3_w)).umask32(0xff000000);
+ map(0x05e0, 0x05e3).rw(FUNC(sgi_gr1_device::dr2_r), FUNC(sgi_gr1_device::dr2_w)).umask32(0xff000000);
+
+ map(0x0640, 0x783).w(m_ge, FUNC(sgi_ge5_device::command_w)).umask32(0xffff0000);
+ map(0x0740, 0x743).r(m_ge, FUNC(sgi_ge5_device::pc_r)).umask32(0xffff0000);
+
+ map(0x0800, 0x0bff).w(FUNC(sgi_gr1_device::fifo_w));
+ map(0x0c00, 0x0dff).w(m_ge, FUNC(sgi_ge5_device::mar_w));
+ map(0x0e00, 0x0e07).w(m_ge, FUNC(sgi_ge5_device::mar_msb_w));
+
+ map(0x1400, 0x17ff).rw(m_ge, FUNC(sgi_ge5_device::data_r), FUNC(sgi_ge5_device::data_w));
+ map(0x2000, 0x2007).lrw32("ff",
+ [this](offs_t offset) { LOG("read finish flag %d\n", offset); return m_ff[offset]; },
+ [this](offs_t offset, u32 data, u32 mem_mask) { LOG("write finish flag %d data %d\n", offset, data); m_ff[offset] = data; });
+ //map(0x207c, 0x207f); // gr1 vs gr2
+}
+
+void sgi_gr11_device::map(address_map &map)
+{
+ sgi_gr1_device::map(map);
+
+ map(0x0510, 0x0517).rw(FUNC(sgi_gr11_device::xpc1_r<0>), FUNC(sgi_gr11_device::xpc1_w<0>)).umask32(0x000000ff);
+ map(0x0530, 0x0537).rw(FUNC(sgi_gr11_device::xpc1_r<1>), FUNC(sgi_gr11_device::xpc1_w<1>)).umask32(0x000000ff);
+ map(0x0550, 0x0557).rw(FUNC(sgi_gr11_device::xpc1_r<2>), FUNC(sgi_gr11_device::xpc1_w<2>)).umask32(0x000000ff);
+ map(0x0570, 0x0577).rw(FUNC(sgi_gr11_device::xpc1_r<3>), FUNC(sgi_gr11_device::xpc1_w<3>)).umask32(0x000000ff);
+ map(0x0590, 0x0597).rw(FUNC(sgi_gr11_device::xpc1_r<4>), FUNC(sgi_gr11_device::xpc1_w<4>)).umask32(0x000000ff);
+
+ map(0x05b0, 0x05b7).w(FUNC(sgi_gr11_device::xpc1_bc_w)).umask32(0x000000ff);
+
+ map(0x05d0, 0x05df).m(m_ramdac, FUNC(bt458_device::map));
+}
+
+void sgi_gr12_device::map(address_map &map)
+{
+ sgi_gr1_device::map(map);
+
+ map(0x0400, 0x041f).rw(FUNC(sgi_gr12_device::xmap2_r<0>), FUNC(sgi_gr12_device::xmap2_w<0>)).umask32(0x000000ff);
+ map(0x0420, 0x043f).rw(FUNC(sgi_gr12_device::xmap2_r<1>), FUNC(sgi_gr12_device::xmap2_w<1>)).umask32(0x000000ff);
+ map(0x0440, 0x045f).rw(FUNC(sgi_gr12_device::xmap2_r<2>), FUNC(sgi_gr12_device::xmap2_w<2>)).umask32(0x000000ff);
+ map(0x0460, 0x047f).rw(FUNC(sgi_gr12_device::xmap2_r<3>), FUNC(sgi_gr12_device::xmap2_w<3>)).umask32(0x000000ff);
+ map(0x0480, 0x049f).rw(FUNC(sgi_gr12_device::xmap2_r<4>), FUNC(sgi_gr12_device::xmap2_w<4>)).umask32(0x000000ff);
+ map(0x04a0, 0x04bf).w(FUNC(sgi_gr12_device::xmap2_bc_w)).umask32(0x000000ff);
+
+ map(0x0500, 0x050f).m(m_ramdac[0], FUNC(bt457_device::map)).umask32(0x000000ff);
+ map(0x0520, 0x052f).m(m_ramdac[1], FUNC(bt457_device::map)).umask32(0x000000ff);
+ map(0x0540, 0x054f).m(m_ramdac[2], FUNC(bt457_device::map)).umask32(0x000000ff);
+}
+
+void sgi_gr1_device::device_add_mconfig(machine_config &config)
+{
+ unsigned pixel_clock = 107'352'000;
+
+ //ADDRESS_MAP_BANK(config, m_ext).set_map(&lle_device_base::ext_map).set_options(ENDIANNESS_NATIVE, 8, 12, 0x100);
+
+ /*
+ * 1280x1024 @ 60Hz
+ * 107.352MHz pixel clock
+ * horizontal sync 63.9kHz, pulse width == 120 pixels
+ * vertical sync 60Hz, front/sync/back == 3/3/35 lines
+ *
+ // SGI GR1 offsets:
+ // 1280: Cx:D+H-P=0+246-57, Cy:V-32=39-32 (189, 7)
+ // 1024: Cx:D+H-P=0+126-57, Cy:V-32=43-32
+ // NTSC: Cx:D+H-P=-6, Cy:V-32=-554
+ // PAL: Cx:D+H-P=24, Cy:V-32=-457
+ // 30HZ: Cx:D+H-P=39, Cy:V-32=18
+ // STEREO: Cx:D+H-P=0+246-57, Cy:V-32=39-32+1
+ *
+ */
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_raw(pixel_clock, 1680, 246, 246 + 1280, 1065, 39, 39 + 1024);
+ m_screen->set_screen_update(FUNC(sgi_gr1_device::screen_update));
+ m_screen->screen_vblank().set([this](int state) { m_vblank_cb(state); });
+
+ SGI_RE2(config, m_re, 0);
+
+ SGI_GE5(config, m_ge, 10_MHz_XTAL);
+ m_ge->fifo_empty().set([this]() { return int(m_fifo.empty()); });
+ m_ge->fifo_read().set([this]() { return m_fifo.dequeue(); });
+ m_ge->re_r().set(m_re, FUNC(sgi_re2_device::reg_r));
+ m_ge->re_w().set(m_re, FUNC(sgi_re2_device::reg_w));
+
+ BT431(config, m_cursor[0], pixel_clock / 5);
+ BT431(config, m_cursor[1], pixel_clock / 5);
+}
+
+void sgi_gr11_device::device_add_mconfig(machine_config &config)
+{
+ sgi_gr1_device::device_add_mconfig(config);
+
+ BT458(config, m_ramdac, 107'352'000);
+}
+
+void sgi_gr12_device::device_add_mconfig(machine_config &config)
+{
+ sgi_gr1_device::device_add_mconfig(config);
+
+ BT457(config, m_ramdac[0], 107'352'000);
+ BT457(config, m_ramdac[1], 107'352'000);
+ BT457(config, m_ramdac[2], 107'352'000);
+}
+
+void sgi_gr1_device::device_start()
+{
+ m_vblank_cb.resolve_safe();
+ m_int_fifo_cb.resolve_safe();
+
+ //save_item(NAME());
+}
+
+void sgi_gr11_device::device_start()
+{
+ sgi_gr1_device::device_start();
+}
+
+void sgi_gr12_device::device_start()
+{
+ sgi_gr1_device::device_start();
+
+ m_vram = std::make_unique<u32[]>(1280 * 1024);
+}
+
+void sgi_gr1_device::device_reset()
+{
+ //m_dr0 = DR0_ZBIN;
+ //m_dr2 = DR2_BPIN;
+ //m_dr3 = DR3_MS;
+ m_dr4 = 0;
+
+ m_fifo.clear();
+}
+
+void sgi_gr11_device::device_reset()
+{
+ sgi_gr1_device::device_reset();
+}
+
+void sgi_gr12_device::device_reset()
+{
+ sgi_gr1_device::device_reset();
+
+ //m_dr0 &= ~DR0_ZBIN;
+ //m_dr2 &= ~DR2_BPIN;
+}
+
+u32 sgi_gr11_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
+{
+ /*
+ * fb_pixel (8) bits, cursor (2 bits), pup (2 bits), wid (2 bits)
+ *
+ * iterate through 5 xpc1 channels
+ * mode = xpc1[channel][wid]
+ *
+ */
+
+ return 0;
+}
+
+u32 sgi_gr12_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
+{
+ // TODO: underlay
+
+ unsigned offset = 0;
+
+ for (unsigned y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
+ for (unsigned x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 5)
+ for (unsigned c = 0; c < 5; c++)
+ {
+ u8 const overlay = (m_cursor[0]->cur_r(x + c, y) ? 1 : 0) | (m_cursor[1]->cur_r(x + c, y) ? 2 : 0);
+ u32 const data = m_vram[offset++];
+ u16 const mode = m_xmap2[c].mode[data >> 28];
+
+ switch (mode & 0x07)
+ {
+ case 0: // 8 bit ci single buffer
+ {
+ u16 const ci = BIT(mode, 9) ? (mode & 0x0f00) | u8(data) : u8(data);
+ rgb_t const cm = m_xmap2[c].color[(m_dr4 & DR4_MS) ? ci | 0x1000 : ci];
+
+ bitmap.pix(y, x) = rgb_t(
+ m_ramdac[0]->lookup(cm.r(), overlay),
+ m_ramdac[1]->lookup(cm.g(), overlay),
+ m_ramdac[2]->lookup(cm.b(), overlay));
+ }
+ break;
+
+ case 1: // 4 bit ci double buffer
+ {
+ u8 const fb = BIT(mode, 3) ? u8(data) >> 4 : data & 0x0f;
+ u16 const ci = BIT(mode, 9) ? (mode & 0x0f00) | fb : fb;
+ rgb_t const cm = m_xmap2[c].color[(m_dr4 & DR4_MS) ? ci | 0x1000 : ci];
+
+ bitmap.pix(y, x) = rgb_t(
+ m_ramdac[0]->lookup(cm.r(), overlay),
+ m_ramdac[1]->lookup(cm.g(), overlay),
+ m_ramdac[2]->lookup(cm.b(), overlay));
+ }
+ break;
+
+ case 2: // 12 bit ci double buffer
+ {
+ u8 const fb = u16(BIT(mode, 3) ? data >> 12 : data) & 0x0fff;
+ u16 const ci = BIT(mode, 9) ? (mode & 0x0f00) | (fb & 0xff) : fb;
+ rgb_t const cm = m_xmap2[c].color[(m_dr4 & DR4_MS) ? ci | 0x1000 : ci];
+
+ bitmap.pix(y, x) = rgb_t(
+ m_ramdac[0]->lookup(cm.r(), overlay),
+ m_ramdac[1]->lookup(cm.g(), overlay),
+ m_ramdac[2]->lookup(cm.b(), overlay));
+ }
+ break;
+
+ case 4: // 24 bit rgb single buffer
+ bitmap.pix(y, x) = rgb_t(
+ m_ramdac[0]->lookup(u8(data >> 0), overlay),
+ m_ramdac[1]->lookup(u8(data >> 8), overlay),
+ m_ramdac[2]->lookup(u8(data >> 16), overlay));
+ break;
+
+ case 5: // 12 bit rgb double buffer
+ bitmap.pix(y, x) = BIT(mode, 3) ?
+ rgb_t(
+ m_ramdac[0]->lookup((u8(data >> 0) & 0xf0) | (u8(data >> 4) & 0x0f), overlay),
+ m_ramdac[1]->lookup((u8(data >> 8) & 0xf0) | (u8(data >> 12) & 0x0f), overlay),
+ m_ramdac[2]->lookup((u8(data >> 16) & 0xf0) | (u8(data >> 20) & 0x0f), overlay)) :
+ rgb_t(
+ m_ramdac[0]->lookup((u8(data << 4) & 0xf0) | (u8(data >> 0) & 0x0f), overlay),
+ m_ramdac[1]->lookup((u8(data >> 4) & 0xf0) | (u8(data >> 8) & 0x0f), overlay),
+ m_ramdac[2]->lookup((u8(data >> 12) & 0xf0) | (u8(data >> 16) & 0x0f), overlay));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void sgi_gr1_device::fifo_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ if (m_fifo.full())
+ fatalerror("fifo_w: fifo overflow (%s)", machine().describe_context().c_str());
+
+ LOG("fifo_w 0x%010x\n", (u64(offset) << 32) | data);
+
+ m_fifo.enqueue((u64(offset) << 32) | data);
+
+ if (m_fifo.queue_length() > 256)
+ m_int_fifo_cb(ASSERT_LINE);
+
+ //if (m_ge->suspended())
+ // m_ge->resume(SUSPEND_REASON_HALT);
+}
+
+void sgi_gr1_device::reset_w(int state)
+{
+ if (state)
+ {
+ LOG("reset_w %d\n", state);
+ m_ge->pulse_input_line(INPUT_LINE_RESET, attotime::from_ticks(1, 10_MHz_XTAL));
+ }
+}
+
+template <unsigned Channel> u8 sgi_gr12_device::xmap2_r(offs_t offset)
+{
+ xmap2 const x = m_xmap2[Channel];
+
+ switch (offset)
+ {
+ case 0: // nop
+ break;
+
+ case 1: // blue data
+ if (x.addr & 0x1000)
+ return x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].b();
+ else if (x.addr < 0x10)
+ return x.overlay[x.addr].b();
+ break;
+
+ case 2: // green data
+ if (x.addr & 0x1000)
+ return x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].g();
+ else if (x.addr < 0x10)
+ return x.overlay[x.addr].g();
+ break;
+
+ case 3: // red data
+ if (x.addr & 0x1000)
+ return x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].r();
+ else if (x.addr < 0x10)
+ return x.overlay[x.addr].r();
+ break;
+
+ case 4: // increment address
+ // TODO: should reading increment the address register?
+ //x.addr = (x.addr + 1) & 0x1fff;
+ LOG("read address increment\n");
+ break;
+
+ case 5: // other data
+ if (x.addr < 0x20)
+ {
+ u16 const mode = x.mode[(x.addr >> 1) & 0xf];
+
+ return BIT(x.addr, 0) ? (mode >> 8) : u8(mode);
+ }
+ else if (x.addr == 0x20)
+ return x.wid_aux;
+ else if (x.addr == 0x21)
+ return 0x18; // z-buffer and bit-plane expansion not present?
+ break;
+
+ case 6: // address msb
+ return x.addr >> 8;
+
+ case 7: // address lsb
+ return u8(x.addr);
+ }
+
+ return 0;
+}
+
+template <unsigned Channel> void sgi_gr12_device::xmap2_w(offs_t offset, u8 data)
+{
+ xmap2 &x = m_xmap2[Channel];
+
+ switch (offset)
+ {
+ case 0: // nop
+ break;
+
+ case 1: // blue data
+ if (x.addr & 0x1000)
+ x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].set_b(data);
+ else if (x.addr < 0x10)
+ x.overlay[x.addr].set_b(data);
+ break;
+
+ case 2: // green data
+ if (x.addr & 0x1000)
+ x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].set_g(data);
+ else if (x.addr < 0x10)
+ x.overlay[x.addr].set_g(data);
+ break;
+
+ case 3: // red data
+ if (x.addr & 0x1000)
+ x.color[(m_dr4 & DR4_MS) ? x.addr : (x.addr & 0xfff)].set_r(data);
+ else if (x.addr < 0x10)
+ x.overlay[x.addr].set_r(data);
+ break;
+
+ case 4: // increment address
+ x.addr = (x.addr + 1) & 0x1fff;
+ break;
+
+ case 5: // other data
+ if (x.addr < 0x20)
+ {
+ u16 &mode = x.mode[(x.addr >> 1) & 0xf];
+
+ if (BIT(x.addr, 0))
+ mode = (u16(data & 0x3f) << 8) | (mode & 0x00ff);
+ else
+ mode = (mode & 0x3f00) | data;
+ }
+ else if (x.addr == 0x20)
+ x.wid_aux = BIT(data, 0);
+ break;
+
+ case 6: // address msb
+ x.addr = u16((data & 0x1f) << 8) | (x.addr & 0x00ff);
+ break;
+
+ case 7: // address lsb
+ x.addr = (x.addr & 0x1f00) | data;
+ break;
+ }
+}
diff --git a/src/mame/video/sgi_gr1.h b/src/mame/video/sgi_gr1.h
new file mode 100644
index 00000000000..40b3196c01b
--- /dev/null
+++ b/src/mame/video/sgi_gr1.h
@@ -0,0 +1,232 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_VIDEO_SGI_GR1_H
+#define MAME_VIDEO_SGI_GR1_H
+
+#pragma once
+
+#include "machine/bankdev.h"
+#include "screen.h"
+#include "video/sgi_ge5.h"
+#include "video/sgi_re2.h"
+#include "video/bt45x.h"
+#include "video/bt431.h"
+
+class sgi_gr1_device : public device_t
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::GRAPHICS; }
+
+ // configuration
+ auto out_vblank() { return m_vblank_cb.bind(); }
+ auto out_int_ge() { return subdevice<sgi_ge5_device>("ge5")->out_int(); }
+ auto out_int_fifo() { return m_int_fifo_cb.bind(); }
+
+ void reset_w(int state);
+
+ virtual void map(address_map &map);
+
+protected:
+ sgi_gr1_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock);
+
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect) = 0;
+
+ u8 dr0_r() { return m_dr0; }
+ u8 dr1_r() { return m_dr1; }
+ u8 dr2_r() { return m_dr2; }
+ u8 dr3_r() { return m_dr3 | (m_fifo.empty() ? 0 : DR3_FIFOEMPTY); }
+ u8 dr4_r() { return m_dr4 & DR4_RM; }
+ void dr0_w(u8 data) { m_dr0 = (m_dr0 & ~DR0_WM) | (data & DR0_WM); }
+ void dr1_w(u8 data) { m_dr1 = (m_dr1 & ~DR1_WM) | (data & DR1_WM); m_ge->cwen_w(BIT(data, 1)); }
+ void dr2_w(u8 data) { m_dr2 = (m_dr2 & ~DR2_WM) | (data & DR2_WM); }
+ void dr3_w(u8 data) { m_dr3 = (m_dr3 & ~DR3_WM) | (data & DR3_WM); }
+ void dr4_w(u8 data) { m_dr4 = (m_dr4 & ~DR4_WM) | (data & DR4_WM); }
+
+ void fifo_w(offs_t offset, u32 data, u32 mem_mask);
+
+//private:
+ required_device<screen_device> m_screen;
+ required_device<sgi_re2_device> m_re;
+ required_device<sgi_ge5_device> m_ge;
+ required_device_array<bt431_device, 2> m_cursor;
+
+ devcb_write_line m_vblank_cb;
+ devcb_write_line m_int_fifo_cb;
+
+ enum dr0_mask : u8
+ {
+ DR0_GRF1EN = 0x01, // grf1 board enable (active low, disable for RE2)
+ DR0_PGRINBIT = 0x02, // reflects PGROUTBIT (PGR)
+ DR0_PGROUTBIT = 0x04, // routed to PGRINBIT (PGR)
+ DR0_ZBUF0 = 0x08, // mzb1 card is installed (active low, ro, MGR)
+ DR0_SMALLMON0 = 0x08, // small monitor installed (active low, non-MGR)
+
+ DR0_WM = 0xf7, // write mask
+ };
+ enum dr1_mask : u8
+ {
+ DR1_SE = 0x01, // sync on green enable (active low, rw)
+ DR1_CWEN = 0x02, // wtl3132 cwen-
+ DR1_VRLS = 0x04, // vertical retrace latency select
+ DR1_MTYPE = 0x06, // monitor type msb (rw)
+ DR1_TURBO = 0x08, // turbo option installed (active low, ro)
+ DR1_OVERLAY0A = 0x10, // dac overlay bit 0 bank a (ro)
+
+ DR1_WM = 0xf7, // write mask
+ };
+ enum dr2_mask : u8
+ {
+ DR2_SCREENON = 0x01, // standby (rw)
+ DR2_UNCOM2 = 0x02, // uncomitted bit to xilinx
+ DR2_LEDOFF = 0x04, // disable led
+ DR2_BITPLANES = 0x08, // extra bitplanes installed (active low, ro)
+ DR2_ZBUF = 0x10, // z-buffer installed (active low, non-MGR, ro)
+
+ DR2_WM = 0xf1, // write mask
+ };
+ enum dr3_mask : u8
+ {
+ DR3_GENSTATEN = 0x01, // enable genlock status out
+ DR3_LSBBLUEOUT = 0x01, // latch blue lsb out (VGR only)
+ DR3_LCARESET = 0x02, // reset xilix lca (active low, rw)
+ DR3_MONITORRESET = 0x04, // monitor set (rw)
+ DR3_FIFOEMPTY = 0x08, // fifo empty (active low, ro)
+ DR3_FIFOFULL = 0x10, // fifo half full (active low, ro)
+
+ DR3_WM = 0xf7, // write mask
+ };
+ enum dr4_mask : u8
+ {
+ DR4_MONITORMASK = 0x03, // monitor type lsb (rw)
+ DR4_EXTCLKSEL = 0x04, // select external pixel clock (rw)
+ DR4_MEGOPT = 0x08, // 1M video rams installed (ro)
+ DR4_GESTALL = 0x10, // ge stalled (active low, ro)
+ DR4_ACLKEN = 0x20, // asynchronous clock enabled (wo)
+ DR4_SCLKEN = 0x40, // synchronous clock enabled (wo)
+ DR4_MS = 0x80, // select upper 4K color map (rw)
+
+ DR4_RM = 0x9f, // read mask
+ DR4_WM = 0xff, // write mask
+ };
+
+ u8 m_dr0;
+ u8 m_dr1;
+ u8 m_dr2;
+ u8 m_dr3;
+ u8 m_dr4;
+
+ util::fifo<u64, 512> m_fifo;
+ u32 m_ff[2];
+};
+
+class sgi_gr11_device : public sgi_gr1_device
+{
+public:
+ sgi_gr11_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual void map(address_map &map) override;
+
+protected:
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect) override;
+
+ template <unsigned Channel> u8 xpc1_r(offs_t offset)
+ {
+ if (offset)
+ {
+ unsigned const address = m_xpc1_addr[Channel];
+ u16 const mode = m_xpc1_mode[Channel][address >> 1];
+
+ return BIT(address, 0) ? (mode >> 8) : u8(mode);
+ }
+ else
+ return m_xpc1_addr[Channel];
+ }
+
+ template <unsigned Channel> void xpc1_w(offs_t offset, u8 data)
+ {
+ if (offset)
+ {
+ unsigned const address = m_xpc1_addr[Channel];
+ u16 &mode = m_xpc1_mode[Channel][address >> 1];
+
+ if (BIT(address, 0))
+ mode = (u16(data & 0x3f) << 8) | (mode & 0x00ff);
+ else
+ mode = (mode & 0x3f00) | data;
+ }
+ else
+ m_xpc1_addr[Channel] = data & 7;
+ }
+ void xpc1_bc_w(offs_t offset, u8 data)
+ {
+ xpc1_w<0>(offset, data);
+ xpc1_w<1>(offset, data);
+ xpc1_w<2>(offset, data);
+ xpc1_w<3>(offset, data);
+ xpc1_w<4>(offset, data);
+ }
+
+private:
+ required_device<bt458_device> m_ramdac;
+
+ u8 m_xpc1_addr[5];
+ u16 m_xpc1_mode[5][4];
+};
+
+class sgi_gr12_device : public sgi_gr1_device
+{
+public:
+ sgi_gr12_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual void map(address_map &map) override;
+
+protected:
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect) override;
+
+ template <unsigned Channel> u8 xmap2_r(offs_t offset);
+ template <unsigned Channel> void xmap2_w(offs_t offset, u8 data);
+ void xmap2_bc_w(offs_t offset, u8 data)
+ {
+ xmap2_w<0>(offset, data);
+ xmap2_w<1>(offset, data);
+ xmap2_w<2>(offset, data);
+ xmap2_w<3>(offset, data);
+ xmap2_w<4>(offset, data);
+ }
+
+private:
+ required_device_array<bt457_device, 3> m_ramdac;
+
+ std::unique_ptr<u32[]> m_vram;
+
+ struct xmap2
+ {
+ u16 addr;
+ rgb_t color[8192];
+ rgb_t overlay[16];
+ u16 mode[16];
+ bool wid_aux;
+ }
+ m_xmap2[5];
+};
+
+DECLARE_DEVICE_TYPE(SGI_GR11, sgi_gr11_device)
+DECLARE_DEVICE_TYPE(SGI_GR12, sgi_gr12_device)
+
+#endif // MAME_VIDEO_SGI_GR1_H
diff --git a/src/mame/video/sgi_re2.cpp b/src/mame/video/sgi_re2.cpp
new file mode 100644
index 00000000000..225ab16a598
--- /dev/null
+++ b/src/mame/video/sgi_re2.cpp
@@ -0,0 +1,80 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Silicon Graphics RE2 device.
+ *
+ * TODO:
+ * - skeleton only
+ *
+ */
+
+#include "emu.h"
+#include "debugger.h"
+#include "sgi_re2.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_REG (1U << 1)
+
+//#define VERBOSE (LOG_GENERAL|LOG_REG)
+#include "logmacro.h"
+
+static char const *const regnames[] =
+{
+ nullptr, nullptr, nullptr, nullptr, "ENABRGB", "BIGENDIAN", "FUNC", "HADDR",
+ "NOPUP", "XYFRAC", "RGB", "YX", "PUPDATA", "PATL", "PATH", "DZI",
+ "DZF", "DR", "DG", "DB", "Z", "R", "G", "B",
+ "STIP", "STIPCOUNT", "DX", "DY", "NUMPIX", "X", "Y", "IR",
+
+ "RWDATA", "PIXMASK", "AUXMASK", "WIDDATA", "UAUXDATA", "RWMODE", "READBUF", "PIXTYPE",
+ "ASELECT", "ALIGNPAT", "ENABPAT", "ENABSTIP", "ENABDITH", "ENABWID", "CURWID", "DEPTHFN",
+ "REPSTIP", "ENABLWID", "FBOPTION", "TOPSCAN", nullptr, nullptr, "ZBOPTION", "XZOOM",
+ "UPACMODE", "YMIN", "YMAX", "XMIN", "XMAX", "COLORCMP", "MEGOPTION", nullptr,
+};
+
+DEFINE_DEVICE_TYPE(SGI_RE2, sgi_re2_device, "sgi_re2", "SGI Raster Engine 2")
+
+sgi_re2_device::sgi_re2_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SGI_RE2, tag, owner, clock)
+{
+}
+
+void sgi_re2_device::device_start()
+{
+ // TODO: save state
+}
+
+void sgi_re2_device::device_reset()
+{
+}
+
+u32 sgi_re2_device::reg_r(offs_t offset)
+{
+ u32 data = 0;
+
+ switch (offset)
+ {
+ case RE2_STIP: data = m_stip; break;
+ case RE2_STIPCOUNT: data = m_stipcount; break;
+ case RE2_RWDATA: data = m_rwdata; break;
+ default:
+ logerror("reg_r unhandled register 0x%x\n", offset);
+ break;
+ }
+
+ return data;
+}
+
+void sgi_re2_device::reg_w(offs_t offset, u32 data)
+{
+ if ((offset < 64) && regnames[offset])
+ LOGMASKED(LOG_REG, "reg_w register 0x%x (%s) data 0x%x\n", offset, regnames[offset], data);
+ else
+ logerror("reg_w unhandled register 0x%x data 0x%x\n", offset, data);
+
+ switch (offset)
+ {
+ case RE2_STIP: m_stip = data; break;
+ case RE2_STIPCOUNT: m_stipcount = data; break;
+ }
+}
diff --git a/src/mame/video/sgi_re2.h b/src/mame/video/sgi_re2.h
new file mode 100644
index 00000000000..c82708aa85b
--- /dev/null
+++ b/src/mame/video/sgi_re2.h
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_VIDEO_SGI_RE2_H
+#define MAME_VIDEO_SGI_RE2_H
+
+#pragma once
+
+class sgi_re2_device : public device_t
+{
+public:
+ sgi_re2_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ enum register_number : unsigned
+ {
+ // buffered registers (write only unless noted)
+ RE2_ENABRGB = 0x04, // enable 8 bit rgb (1)
+ RE2_BIGENDIAN = 0x05, // enable big endian mode (1)
+ RE2_FUNC = 0x06, // raster op function (4)
+ RE2_HADDR = 0x07, // starting pixel location (2)
+ RE2_NOPUP = 0x08, // size of uaux (1)
+ RE2_XYFRAC = 0x09, // initial xyfrac (4)
+ RE2_RGB = 0x0a, // initial color values (27)
+ RE2_YX = 0x0b, // initial y and x (22)
+ RE2_PUPDATA = 0x0c, // pup data (2)
+ RE2_PATL = 0x0d, // pattern mask low (16)
+ RE2_PATH = 0x0e, // pattern mask high (16)
+ RE2_DZI = 0x0f, // delta z integer (24)
+ RE2_DZF = 0x10, // delta z fraction (14)
+ RE2_DR = 0x11, // delta red (24)
+ RE2_DG = 0x12, // delta green (20)
+ RE2_DB = 0x13, // delta blue (20)
+ RE2_Z = 0x14, // initial z integer (24)
+ RE2_R = 0x15, // initial red (23)
+ RE2_G = 0x16, // initial green (19)
+ RE2_B = 0x17, // initial blue (19)
+ RE2_STIP = 0x18, // stipple pattern (16, rw)
+ RE2_STIPCOUNT = 0x19, // stipple repeat (8, rw)
+ RE2_DX = 0x1a, // delta x (16)
+ RE2_DY = 0x1b, // delta y (16)
+ RE2_NUMPIX = 0x1c, // pixel count (11)
+ RE2_X = 0x1d, // initial x (12)
+ RE2_Y = 0x1e, // initial y (11)
+ RE2_IR = 0x1f, // instruction (3)
+
+ // unbuffered registers (write only unless noted)
+ RE2_RWDATA = 0x20, // read/write data (32, rw)
+ RE2_PIXMASK = 0x21, // pixel mask (24)
+ RE2_AUXMASK = 0x22, // auxiliary mask (9)
+ RE2_WIDDATA = 0x23, // window id (4)
+ RE2_UAUXDATA = 0x24, // uaux data (4)
+ RE2_RWMODE = 0x25, // read/write mode (3)
+ RE2_READBUF = 0x26, // buffer select (1)
+ RE2_PIXTYPE = 0x27, // pixel type (2)
+ RE2_ASELECT = 0x28, // antialias select (6)
+ RE2_ALIGNPAT = 0x29, // pattern alignment (1)
+ RE2_ENABPAT = 0x2a, // enable pattern mask (1)
+ RE2_ENABSTIP = 0x2b, // enable stipple (1)
+ RE2_ENABDITH = 0x2c, // enable dithering (1)
+ RE2_ENABWID = 0x2d, // enable wid check (1)
+ RE2_CURWID = 0x2e, // current wid (4)
+ RE2_DEPTHFN = 0x2f, // depth function (4)
+ RE2_REPSTIP = 0x30, // stipple repeat (8)
+ RE2_ENABLWID = 0x31, // enable line wid (1)
+ RE2_FBOPTION = 0x32, // frame buffer option (2)
+ RE2_TOPSCAN = 0x33, // first row/column (18)
+ RE2_ZBOPTION = 0x36, // z buffer option (1)
+ RE2_XZOOM = 0x37, // x zoom factor (8)
+ RE2_UPACMODE = 0x38, // packing mode (2)
+ RE2_YMIN = 0x39, // bottom screen mask (11)
+ RE2_YMAX = 0x3a, // top screen mask (11)
+ RE2_XMIN = 0x3b, // left screen mask (12)
+ RE2_XMAX = 0x3c, // right screen mask (12)
+ RE2_COLORCMP = 0x3d, // z compare source (1)
+ RE2_MEGOPTION = 0x3e, // vram size (1)
+ };
+
+ u32 reg_r(offs_t offset);
+ void reg_w(offs_t offset, u32 data);
+
+protected:
+
+private:
+ u16 m_stip;
+ u8 m_stipcount;
+
+ u32 m_rwdata;
+};
+
+DECLARE_DEVICE_TYPE(SGI_RE2, sgi_re2_device)
+
+#endif // MAME_VIDEO_SGI_RE2_H