summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2026-02-10 13:12:03 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2026-02-10 13:12:03 +0700
commit6ba52f7299edb531a97a053ca186c2c00d43a752 (patch)
tree9772934b113c8752b5e909a2892993be99199183
parent24c1f51a6d8cbafc4bd0e9f1e39528086cc6a1d6 (diff)
sgi/iris: add wip hle gl1 graphics
-rw-r--r--src/mame/sgi/gl1.cpp380
-rw-r--r--src/mame/sgi/gl1.h13
-rw-r--r--src/mame/sgi/iris.cpp12
-rw-r--r--src/mame/sgi/pm2.cpp44
4 files changed, 414 insertions, 35 deletions
diff --git a/src/mame/sgi/gl1.cpp b/src/mame/sgi/gl1.cpp
new file mode 100644
index 00000000000..35e5df76004
--- /dev/null
+++ b/src/mame/sgi/gl1.cpp
@@ -0,0 +1,380 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * SGI IRIS GL1 high-level emulation
+ *
+ * This device is an interim high-level emulation of a combination of GF1, UC3,
+ * DC3 and BP2 boards. Currently, it supports the limited subset of functions
+ * used by the IRIS 1400 firmware to display the monitor. When the hardware is
+ * better understood and more software becomes usable, this device should be
+ * replaced by individual Multibus devices emulating each of the four cards
+ * mentioned earlier.
+ *
+ * Sources:
+ * - IRIS 3.7 source code
+ *
+ * TODO:
+ * - everything
+ */
+
+#include "emu.h"
+
+#include "gl1.h"
+
+#include "emupal.h"
+#include "screen.h"
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+namespace {
+
+class sgi_gl1_device
+ : public device_t
+ , public device_multibus_interface
+{
+public:
+ sgi_gl1_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SGI_GL1, tag, owner, clock)
+ , device_multibus_interface(mconfig, *this)
+ , m_screen(*this, "screen")
+ , m_cmap(*this, "cmap")
+ , m_font(*this, "font", 0x8000, ENDIANNESS_BIG)
+ , m_bp(nullptr)
+ , m_installed(false)
+ {
+ }
+
+ static constexpr feature_type imperfect_features() { return feature::GRAPHICS; }
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ // GF1
+ u16 fbc_readdpx_r();
+ void fbc_clrint_w(u16 data);
+ u16 fbc_flags_r();
+ void fbc_flags_w(u16 data);
+ u16 fbc_data_r();
+ void fbc_data_w(u16 data);
+ void ge_flags_w(u16 data);
+
+ // DC3
+ void flags_w(u16 data);
+ template <unsigned Channel> void colormap_w(offs_t offset, u16 data, u16 mem_mask);
+ u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect);
+
+ // BP2
+ void bp_w(offs_t offset, u8 data, u8 mask);
+
+private:
+ required_device<screen_device> m_screen;
+ required_device<palette_device> m_cmap;
+ memory_share_creator<u16> m_font;
+
+ std::unique_ptr<u8[]> m_bp;
+
+ //u16 m_ed; // error delta
+ //u16 m_ec; // error correct
+ u16 m_xs; // x start
+ u16 m_xe; // x end
+ u16 m_ys; // y start
+ u16 m_ye; // y end
+ u16 m_fma; // font memory address
+ //u16 m_cf; // config
+
+ u16 m_ch; // character height
+ u16 m_cw; // character width
+ u16 m_cs; // character spacing
+
+ u16 m_ca; // cursor address
+ u16 m_cm; // cursor color
+ u16 m_cx; // cursor x
+ u16 m_cy; // cursor y
+
+ u16 m_color;
+ u16 m_wrten;
+
+ u16 m_flags;
+
+ util::fifo<u16, 16> m_params;
+ u8 m_expect;
+
+ bool m_installed;
+};
+
+void sgi_gl1_device::device_start()
+{
+ m_bp = std::make_unique<u8[]>(0x10'0000); // 1024x1024x4 (not packed)
+
+ save_pointer(NAME(m_bp), 0x10'0000);
+}
+
+void sgi_gl1_device::device_reset()
+{
+ if (!m_installed)
+ {
+ // GF1 handlers
+ m_bus->space(AS_IO).install_readwrite_handler(0x1000, 0x1001,
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_readdpx_r)),
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_clrint_w)));
+
+ m_bus->space(AS_IO).install_readwrite_handler(0x1400, 0x1401,
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_flags_r)),
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_flags_w)));
+
+ m_bus->space(AS_IO).install_readwrite_handler(0x1800, 0x1801,
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_data_r)),
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::fbc_data_w)));
+
+ m_bus->space(AS_IO).install_write_handler(0x1c00, 0x1c01,
+ emu::rw_delegate(*this, FUNC(sgi_gl1_device::ge_flags_w)));
+
+ // DC3 handlers
+ m_bus->space(AS_IO).install_write_handler(0x4000, 0x4001, emu::rw_delegate(*this, FUNC(sgi_gl1_device::flags_w)));
+ m_bus->space(AS_IO).install_write_handler(0x4200, 0x43ff, emu::rw_delegate(*this, FUNC(sgi_gl1_device::colormap_w<0>)));
+ m_bus->space(AS_IO).install_write_handler(0x4400, 0x45ff, emu::rw_delegate(*this, FUNC(sgi_gl1_device::colormap_w<1>)));
+ m_bus->space(AS_IO).install_write_handler(0x4600, 0x47ff, emu::rw_delegate(*this, FUNC(sgi_gl1_device::colormap_w<2>)));
+
+ m_installed = true;
+ }
+
+ m_params.clear();
+ m_expect = 0;
+}
+
+static const gfx_layout gl1_layout =
+{
+ 8, 16, 1024, 1,
+ { 0 },
+ { 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 15 * 16, 14 * 16, 13 * 16, 12 * 16, 11 * 16, 10 * 16, 9 * 16, 8 * 16, 7 * 16, 6 * 16, 5 * 16, 4 * 16, 3 * 16, 2 * 16, 1 * 16, 0 * 16 },
+ 16 * 16
+};
+
+static GFXDECODE_START(gl1_gfx)
+ GFXDECODE_RAM("font", 0x0, gl1_layout, 0, 1)
+GFXDECODE_END
+
+void sgi_gl1_device::device_add_mconfig(machine_config &config)
+{
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_raw(1024 * 768 * 60, 1024, 0, 1024, 768, 0, 768);
+ m_screen->set_screen_update(FUNC(sgi_gl1_device::screen_update));
+
+ // MCM6168P45 x8x3 (4096x4 SRAM) 16K x3
+ PALETTE(config, m_cmap);
+ m_cmap->set_entries(4096);
+
+ // gfxdecode is only to show the font data in the tile viewer
+ GFXDECODE(config, "gfx", m_cmap, gl1_gfx);
+}
+
+u32 sgi_gl1_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, rectangle const &cliprect)
+{
+ u8 const *src = m_bp.get();
+
+ 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++)
+ {
+ bitmap.pix(screen.visible_area().max_y - y, x) = m_cmap->pen_color(*src++);
+ }
+ }
+
+ return 0;
+}
+
+u16 sgi_gl1_device::fbc_readdpx_r()
+{
+ return 0;
+}
+
+void sgi_gl1_device::fbc_clrint_w(u16 data)
+{
+ LOG("%s: fbc_clrint_w 0x%04x\n", machine().describe_context(), data);
+}
+
+u16 sgi_gl1_device::fbc_flags_r()
+{
+ return 0x0002;
+}
+
+void sgi_gl1_device::fbc_flags_w(u16 data)
+{
+ //LOG("%s: fbc_flags_w 0x%04x\n", machine().describe_context(), data);
+}
+
+static const unsigned num_params[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 0, 2, 2, 1, 8,
+ 8, 0, 0, 0, 0, 6, 4, 1,
+
+ 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 0, 0, 0, 0, 0, 0,
+};
+
+u16 sgi_gl1_device::fbc_data_r()
+{
+ return 0x40; // HACK: always ready
+}
+
+void sgi_gl1_device::fbc_data_w(u16 data)
+{
+ LOG("%s: fbc_data_w 0x%04x expect %u\n", machine().describe_context(), data, m_expect);
+
+ if (m_params.empty())
+ {
+ // enqueue command
+ m_params.enqueue(data);
+
+ m_expect = num_params[data & 0x3f];
+ }
+ else if (m_expect)
+ {
+ // enqueue parameter
+ m_params.enqueue(data);
+ m_expect--;
+ }
+
+ // more parameters expected?
+ if (m_expect)
+ return;
+
+ u8 const command = m_params.dequeue();
+ LOG("command 0x%02x params %u\n", command, m_params.queue_length());
+
+ switch (command & 0x3f)
+ {
+ case 0x12: // point
+ m_xs = m_params.dequeue() & 0xfffU;
+ m_params.dequeue();
+ m_params.dequeue();
+ m_ys = m_params.dequeue() & 0xfffU;
+ break;
+
+ case 0x14: // color
+ m_color = m_params.dequeue();
+ m_color &= m_params.dequeue();
+ break;
+ case 0x15: // wrten
+ m_wrten = m_params.dequeue();
+ m_wrten |= m_params.dequeue();
+ break;
+ //case 0x16: // config
+ case 0x17: // loadmasks (passthrough)
+ m_fma = m_params.dequeue();
+ while (!m_params.empty())
+ m_font[m_fma++ & 0x3fff] = m_params.dequeue();
+ break;
+ //case 0x18: // fbviewport
+
+ case 0x1d: // selectcursor
+ m_ca = m_params.dequeue();
+ m_cm = m_params.dequeue();
+ m_params.dequeue();
+ m_params.dequeue();
+ m_params.dequeue();
+ m_params.dequeue();
+ break;
+ case 0x1e: // drawcursor
+ m_cx = m_params.dequeue() & 0xfffU;
+ m_params.dequeue();
+ m_params.dequeue();
+ m_cy = m_params.dequeue() & 0xfffU;
+
+ for (int y = 0; y <= m_ch; y++)
+ {
+ u8 const data = m_font[(m_ca + (y & 0xfU)) & 0x3fff];
+
+ for (int x = 0; x <= m_cw; x++)
+ bp_w((m_cy + y) * 0x400 + m_cx + x, BIT(data, (x & 7) ^ 7) ? m_cm : 0, m_cm);
+ }
+ break;
+ case 0x1f: // undrawcursor
+ m_params.dequeue();
+ for (int y = 0; y <= m_ch; y++)
+ for (int x = 0; x <= m_cw; x++)
+ bp_w((m_cy + y) * 0x400 + m_cx + x, 0, m_cm);
+ break;
+
+ //case 0x21: // polystipple
+
+ case 0x2c: // fixcharload
+ m_ch = m_params.dequeue();
+ m_cw = m_params.dequeue();
+ m_cs = m_params.dequeue();
+ break;
+ case 0x2d: // fixchardraw
+ m_fma = m_params.dequeue();
+ for (int y = 0; y <= m_ch; y++)
+ {
+ u8 const data = m_font[(m_fma + (y & 0xfU)) & 0x3fff];
+
+ for (int x = 0; x <= m_cw; x++)
+ bp_w((m_ys + y) * 0x400 + m_xs + x, BIT(data, (x & 7) ^ 7) ? m_color : 0, m_wrten);
+ }
+ m_xs = (m_xs + m_cs) & 0x0fff;
+ break;
+
+ case 0x39: // blockfill
+ m_xs = m_params.dequeue() & 0x0fff;
+ m_params.dequeue();
+ m_params.dequeue();
+ m_ys = m_params.dequeue() & 0x0fff;
+ m_xe = m_params.dequeue() & 0x0fff;
+ m_params.dequeue();
+ m_params.dequeue();
+ m_ye = m_params.dequeue() & 0x0fff;
+
+ for (int y = m_ys; y < m_ye; y++)
+ for (int x = m_xs; x < m_xe; x++)
+ bp_w(y * 0x400 + x, m_color, m_wrten);
+ break;
+
+ default:
+ LOG("command 0x%02x unemulated\n", command);
+ m_params.clear();
+ break;
+ }
+}
+
+void sgi_gl1_device::ge_flags_w(u16 data)
+{
+ LOG("%s: ge_flags_w 0x%04x\n", machine().describe_context(), data);
+}
+
+void sgi_gl1_device::flags_w(u16 data)
+{
+ LOG("%s: flags_w 0x%04x\n", machine().describe_context(), data);
+
+ m_flags = data;
+}
+
+template <unsigned Channel> void sgi_gl1_device::colormap_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ unsigned const index = BIT(m_flags, 4, 4) << 8 | offset;
+
+ switch (Channel)
+ {
+ case 0: m_cmap->set_pen_red_level(index, data); break;
+ case 1: m_cmap->set_pen_green_level(index, data); break;
+ case 2: m_cmap->set_pen_blue_level(index, data); break;
+ }
+}
+
+void sgi_gl1_device::bp_w(offs_t offset, u8 data, u8 mask)
+{
+ m_bp[offset] = (m_bp[offset] & ~mask) | (data & mask);
+}
+
+} // anonymous namespace
+
+DEFINE_DEVICE_TYPE_PRIVATE(SGI_GL1, device_multibus_interface, sgi_gl1_device, "sgi_gl1", "Silicon Graphics GL1")
diff --git a/src/mame/sgi/gl1.h b/src/mame/sgi/gl1.h
new file mode 100644
index 00000000000..aff9b54835a
--- /dev/null
+++ b/src/mame/sgi/gl1.h
@@ -0,0 +1,13 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_SGI_GL1_H
+#define MAME_SGI_GL1_H
+
+#pragma once
+
+#include "bus/multibus/multibus.h"
+
+DECLARE_DEVICE_TYPE(SGI_GL1, device_multibus_interface)
+
+#endif // MAME_SGI_GL1_H
diff --git a/src/mame/sgi/iris.cpp b/src/mame/sgi/iris.cpp
index 5d53989d054..76cf18c7ad6 100644
--- a/src/mame/sgi/iris.cpp
+++ b/src/mame/sgi/iris.cpp
@@ -45,6 +45,8 @@
#include "ip2.h"
#include "pm2.h"
+#include "gl1.h"
+
#include "iris1400.lh"
namespace {
@@ -81,6 +83,9 @@ void iris_state::machine_reset()
}
static DEVICE_INPUT_DEFAULTS_START(dsd)
+ // winchester drive class 5
+ DEVICE_INPUT_DEFAULTS("W6", 0x1c, 0x14)
+
// wake-up address 0x7f00
DEVICE_INPUT_DEFAULTS("W7", 0x01, 0x01)
DEVICE_INPUT_DEFAULTS("W7", 0x02, 0x02)
@@ -138,6 +143,7 @@ static void iris_p_cards(device_slot_interface &device)
static void iris_g_cards(device_slot_interface &device)
{
// graphics side cards
+ device.option_add("gl1", SGI_GL1);
}
// 20-slot chassis, card positions assumed from 2xxx
@@ -167,7 +173,7 @@ void iris_state::common(machine_config &config)
MULTIBUS_SLOT(config, m_slot[16], m_bus, iris_g_cards, nullptr, false); // bit plane
MULTIBUS_SLOT(config, m_slot[17], m_bus, iris_g_cards, nullptr, false); // display controller
MULTIBUS_SLOT(config, m_slot[18], m_bus, iris_g_cards, nullptr, false); // update controller
- MULTIBUS_SLOT(config, m_slot[19], m_bus, iris_g_cards, nullptr, false); // frame buffer
+ MULTIBUS_SLOT(config, m_slot[19], m_bus, iris_g_cards, nullptr, false); // graphics processor/frame buffer controller
}
void iris_state::iris1400(machine_config &config)
@@ -175,8 +181,11 @@ void iris_state::iris1400(machine_config &config)
common(config);
m_slot[1]->set_default_option("pm2");
+ m_slot[2]->set_default_option("nex");
m_slot[3]->set_default_option("dsd");
+ m_slot[19]->set_default_option("gl1");
+
config.set_default_layout(layout_iris1400);
}
@@ -185,6 +194,7 @@ void iris_state::iris3130(machine_config &config)
common(config);
m_slot[1]->set_default_option("ip2");
+ m_slot[2]->set_default_option("nex");
m_slot[3]->set_default_option("dsd");
}
diff --git a/src/mame/sgi/pm2.cpp b/src/mame/sgi/pm2.cpp
index a733de8b66a..43b35b8085a 100644
--- a/src/mame/sgi/pm2.cpp
+++ b/src/mame/sgi/pm2.cpp
@@ -87,8 +87,6 @@ protected:
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
- void gfx_mconfig(machine_config &config);
-
void mem_map(address_map &map) ATTR_COLD;
u16 status_r();
@@ -113,9 +111,6 @@ private:
required_ioport m_config;
output_finder<> m_led;
- memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_mem;
- memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_spc;
-
u16 m_status;
u16 m_exception;
u16 m_map[240]; // AM2148-55DC 1Kx4 SRAM (x3)?
@@ -129,9 +124,6 @@ void sgi_pm2_device::device_start()
save_item(NAME(m_exception));
save_item(NAME(m_map));
- m_cpu->space(AS_PROGRAM).specific(m_cpu_mem);
- m_cpu->space(m68000_device::AS_CPU_SPACE).specific(m_cpu_spc);
-
m_led.resolve();
}
@@ -195,8 +187,8 @@ void sgi_pm2_device::device_add_mconfig(machine_config &config)
MC68681(config, m_duart[0], 3.6864_MHz_XTAL).irq_cb().set(irq6, FUNC(input_merger_any_high_device::in_w<0>));
MC68681(config, m_duart[1], 3.6864_MHz_XTAL).irq_cb().set(irq6, FUNC(input_merger_any_high_device::in_w<1>));
- RS232_PORT(config, m_port[0], keyboard_devices, nullptr);
- RS232_PORT(config, m_port[1], default_rs232_devices, "terminal");
+ RS232_PORT(config, m_port[0], keyboard_devices, "kbd");
+ RS232_PORT(config, m_port[1], default_rs232_devices, nullptr);
RS232_PORT(config, m_port[2], default_rs232_devices, nullptr);
RS232_PORT(config, m_port[3], default_rs232_devices, nullptr);
@@ -209,8 +201,6 @@ void sgi_pm2_device::device_add_mconfig(machine_config &config)
m_port[1]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w));
m_port[2]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w));
m_port[3]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w));
-
- gfx_mconfig(config);
}
void sgi_pm2_device::device_config_complete()
@@ -226,8 +216,12 @@ void sgi_pm2_device::mem_map(address_map &map)
// TODO: was there a 2M PM2 variant?
map(0x00'0000, 0x17'ffff).ram().share("ram"); // mt4264-15 64kx1 DRAM (8x9) + 1M on PM2M board
+ map(0x18'0000, 0xf7'ffff).noprw(); // HACK: silence ram sizing
+
map(0xf8'0000, 0xf8'7fff).rom().region("prom0", 0);
map(0xf9'0000, 0xf9'7fff).rom().region("prom1", 0);
+ // 0xfa'0000 prom2
+ // 0xfb'0000 prom3 - DC4?
map(0xfc'0000, 0xfc'1fff).rw(m_mmu, FUNC(pm2_mmu_device::page_r), FUNC(pm2_mmu_device::page_w));
map(0xfc'2000, 0xfc'3fff).rw(m_mmu, FUNC(pm2_mmu_device::prot_r), FUNC(pm2_mmu_device::prot_w));
@@ -285,7 +279,8 @@ u16 sgi_pm2_device::mem_r(offs_t offset, u16 mem_mask)
offs_t const physical = map(offset);
data = m_ram[physical];
- LOG("%s: mem_r 0x%06x physical 0x%06x data 0x%04x\n", machine().describe_context(), offset << 1, physical << 1, data);
+ if (!machine().side_effects_disabled())
+ LOG("%s: mem_r 0x%06x physical 0x%06x data 0x%04x\n", machine().describe_context(), offset << 1, physical << 1, data);
if ((offset < 0x8000) && (m_status & STATUS_MBOX))
irq4_w<EXCEPTION_MBOX>(1);
@@ -302,7 +297,8 @@ void sgi_pm2_device::mem_w(offs_t offset, u16 data, u16 mem_mask)
{
offs_t const physical = map(offset);
- LOG("%s: mem_w 0x%06x physical 0x%06x data 0x%04x\n", machine().describe_context(), offset << 1, physical << 1, data);
+ if (!machine().side_effects_disabled())
+ LOG("%s: mem_w 0x%06x physical 0x%06x data 0x%04x\n", machine().describe_context(), offset << 1, physical << 1, data);
COMBINE_DATA(&m_ram[physical]);
@@ -335,26 +331,6 @@ void sgi_pm2_device::map_w(offs_t offset, u16 data, u16 mem_mask)
m_map[offset >> 11] = data;
}
-static const gfx_layout sgi_pm2 =
-{
- 8, 16, 144, 1,
- { 0 },
- { 0, 1, 2, 3, 4, 5, 6, 7 },
- { 15 * 8, 14 * 8, 13 * 8, 12 * 8, 11 * 8, 10 * 8, 9 * 8, 8 * 8, 7 * 8, 6 * 8, 5 * 8, 4 * 8, 3 * 8, 2 * 8, 1 * 8, 0 * 8 },
- 8 * 16
-};
-
-static GFXDECODE_START(sgi_pm2_gfx)
- GFXDECODE_ENTRY("prom1", 0x1b8c, sgi_pm2, 0, 1)
-GFXDECODE_END
-
-void sgi_pm2_device::gfx_mconfig(machine_config &config)
-{
- // gfxdecode is only to show the font data in the tile viewer
- PALETTE(config, "palette", palette_device::MONOCHROME);
- GFXDECODE(config, "gfx", "palette", sgi_pm2_gfx);
-}
-
ROM_START(sgi_pm2)
ROM_DEFAULT_BIOS("v36")
ROM_SYSTEM_BIOS(0, "v36", "V3.6 February 28, 1985")