summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2025-02-09 15:33:16 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2025-02-10 08:11:07 +0700
commitc2ccf4cf6edfb09bedcff06017a51c272e2c20ef (patch)
tree9793450e0da9cf67a663fbd859acda013e91ec7b /src
parent3cec2ae67f53aa581693f2b55308e5851802f0bd (diff)
sun1: refactor into multibus cage/card
* added more firmware revisions * added mmu emulation
Diffstat (limited to 'src')
-rw-r--r--src/mame/mame.lst1
-rw-r--r--src/mame/sun/sun1.cpp148
-rw-r--r--src/mame/sun/sun1_cpu.cpp408
-rw-r--r--src/mame/sun/sun1_cpu.h69
-rw-r--r--src/mame/sun/sun1_mmu.cpp309
-rw-r--r--src/mame/sun/sun1_mmu.h73
6 files changed, 889 insertions, 119 deletions
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index be2a973a831..71d5b50f36e 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -43703,7 +43703,6 @@ sothello
tonton
@source:sun/sun1.cpp
-multibox
sun1
@source:sun/sun2.cpp
diff --git a/src/mame/sun/sun1.cpp b/src/mame/sun/sun1.cpp
index 8b6f25422d3..b3ce3ee78f4 100644
--- a/src/mame/sun/sun1.cpp
+++ b/src/mame/sun/sun1.cpp
@@ -8,6 +8,7 @@
Sun-1
Processor(s): 68000
+ P/N: 270-0001
Notes: Large black desktop boxes with 17" monitors.
Uses the original Stanford-designed video board
and a parallel microswitch keyboard (type 1) and
@@ -55,14 +56,18 @@
04/04/2011 Modernised, added terminal keyboard.
+ TODO:
+ - graphic cards
+ - network cards
+ - storage cards
+
****************************************************************************/
#include "emu.h"
-#include "bus/rs232/rs232.h"
-#include "cpu/m68000/m68000.h"
-#include "machine/am9513.h"
-#include "machine/z80sio.h"
+#include "sun1_cpu.h"
+
+#include "bus/multibus/multibus.h"
namespace {
@@ -71,144 +76,51 @@ class sun1_state : public driver_device
public:
sun1_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
- , m_maincpu(*this, "maincpu")
- , m_iouart(*this, "iouart")
- , m_p_ram(*this, "p_ram")
+ , m_bus(*this, "slot")
{
}
void sun1(machine_config &config);
- void cyb(machine_config &config);
private:
- void sun1_mem(address_map &map) ATTR_COLD;
- void cyb_mem(address_map &map) ATTR_COLD;
+ virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
- required_device<cpu_device> m_maincpu;
- required_device<upd7201_device> m_iouart;
- required_shared_ptr<uint16_t> m_p_ram;
+ required_device<multibus_device> m_bus;
};
-
-void sun1_state::sun1_mem(address_map &map)
+void sun1_state::machine_start()
{
- map.unmap_value_high();
- map(0x00000000, 0x001fffff).ram().share("p_ram"); // 512 KB RAM / ROM at boot
- map(0x00200000, 0x00203fff).rom().region("monitor", 0);
- map(0x00600000, 0x00600007).mirror(0x1ffff8).rw(m_iouart, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
- map(0x00800000, 0x00800003).mirror(0x1ffffc).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
- map(0x00a00000, 0x00bfffff).unmaprw(); // page map
- map(0x00c00000, 0x00dfffff).unmaprw(); // segment map
- map(0x00e00000, 0x00ffffff).unmaprw(); // context register
}
-void sun1_state::cyb_mem(address_map &map)
-{
- sun1_mem(map);
- map(0x00400000, 0x00403fff).rom().region("cyb", 0);
-}
-
-/* Input ports */
-static INPUT_PORTS_START( sun1 )
-INPUT_PORTS_END
-
-
void sun1_state::machine_reset()
{
- uint8_t* user1 = memregion("monitor")->base();
-
- memcpy((uint8_t*)m_p_ram.target(),user1,0x4000);
}
-
-void sun1_state::sun1(machine_config &config)
+static void sun1_cards(device_slot_interface &device)
{
- /* basic machine hardware */
- M68000(config, m_maincpu, 16_MHz_XTAL / 2);
- m_maincpu->set_addrmap(AS_PROGRAM, &sun1_state::sun1_mem);
-
- am9513_device &timer(AM9513(config, "timer", 16_MHz_XTAL / 4));
- timer.fout_cb().set("timer", FUNC(am9513_device::gate1_w));
- timer.out1_cb().set_nop(); // Watchdog; generates BERR/Reset
- timer.out2_cb().set_inputline(m_maincpu, M68K_IRQ_6); // User timer
- timer.out3_cb().set_inputline(m_maincpu, M68K_IRQ_7); // Refresh timer (2 ms)
- timer.out4_cb().set(m_iouart, FUNC(upd7201_device::rxca_w));
- timer.out4_cb().append(m_iouart, FUNC(upd7201_device::txca_w));
- timer.out5_cb().set(m_iouart, FUNC(upd7201_device::rxcb_w));
- timer.out5_cb().append(m_iouart, FUNC(upd7201_device::txcb_w));
-
- UPD7201(config, m_iouart, 16_MHz_XTAL / 4);
- m_iouart->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
- m_iouart->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
- m_iouart->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
- m_iouart->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
- m_iouart->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
- m_iouart->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
- m_iouart->out_int_callback().set_inputline(m_maincpu, M68K_IRQ_5);
-
- rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
- rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
- rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
- rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
-
- rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
- rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
- rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
- rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
+ device.option_add("sun1cpu", MULTIBUS_SUN1CPU);
}
-void sun1_state::cyb(machine_config &config)
+void sun1_state::sun1(machine_config &config)
{
- sun1(config);
- m_maincpu->set_addrmap(AS_PROGRAM, &sun1_state::cyb_mem);
+ // FIXME: CPU board can optionally drive Multibus clocks
+ MULTIBUS(config, m_bus, 19.6608_MHz_XTAL / 2);
+
+ // slot 1 is physically located at top, only slots 1-3 have P2
+ MULTIBUS_SLOT(config, "slot:1", sun1_cards, nullptr, false); // memory expansion 2
+ MULTIBUS_SLOT(config, "slot:2", sun1_cards, nullptr, false); // memory expansion 1
+ MULTIBUS_SLOT(config, "slot:3", sun1_cards, "sun1cpu", false); // processor
+ MULTIBUS_SLOT(config, "slot:4", sun1_cards, nullptr, false); // bus master 1
+ MULTIBUS_SLOT(config, "slot:5", sun1_cards, nullptr, false); // bus master 2
+ MULTIBUS_SLOT(config, "slot:6", sun1_cards, nullptr, false); // multibus memory, optional
+ MULTIBUS_SLOT(config, "slot:7", sun1_cards, nullptr, false); // graphics
}
-/* ROM definition */
-ROM_START( sun1 )
- ROM_REGION16_BE(0x4000, "monitor", 0)
- ROM_DEFAULT_BIOS("1.0")
-
- ROM_SYSTEM_BIOS(0, "1.0", "Sun Monitor 1.0")
- ROMX_LOAD("v10.8.bin", 0x0000, 0x2000, CRC(3528a0f8) SHA1(be437dd93d1a44eccffa6f5e05935119482beab0), ROM_SKIP(1) | ROM_BIOS(0))
- ROMX_LOAD("v10.0.bin", 0x0001, 0x2000, CRC(1ad4c52a) SHA1(4bc1a19e8f202378d5d7baa8b95319275c040a6d), ROM_SKIP(1) | ROM_BIOS(0))
-
- ROM_SYSTEM_BIOS(1, "diag", "Interactive Tests")
- ROMX_LOAD("8mhzdiag.8.bin", 0x0000, 0x2000, CRC(808a549e) SHA1(d2aba014a5507c1538f2c1a73e1d2524f28034f4), ROM_SKIP(1) | ROM_BIOS(1))
- ROMX_LOAD("8mhzdiag.0.bin", 0x0001, 0x2000, CRC(7a92d506) SHA1(5df3800f7083293fc01bb6a7e7538ad425bbebfb), ROM_SKIP(1) | ROM_BIOS(1))
-
- ROM_REGION(0x0a40, "gfx", 0)
- ROM_LOAD("gfxu605.g4.bin", 0x0000, 0x0200, CRC(274b7b3d) SHA1(40d8be2cfcbd03512a05925991bb5030d5d4b5e9))
- ROM_LOAD("gfxu308.g21.bin", 0x0200, 0x0200, CRC(35a6eed8) SHA1(25cb2dd8e5343cd7927c3045eb4cb96dc9935a37))
- ROM_LOAD("gfxu108.g20.bin", 0x0400, 0x0200, CRC(ecee335e) SHA1(5f4d32dc918af15872cd6e700a04720caeb6c657))
- ROM_LOAD("gfxu105.g0.bin", 0x0600, 0x0200, CRC(8e1a24b3) SHA1(dad2821c3a3137ad69e78b6fc29ab582e5d78646))
- ROM_LOAD("gfxu104.g1.bin", 0x0800, 0x0200, CRC(86f7a483) SHA1(8eb3778f5497741cd4345e81ff1a903c9a63c8bb))
- ROM_LOAD("gfxu307.g61.bin", 0x0a00, 0x0020, CRC(b190f25d) SHA1(80fbdc843f1eb68a2d3713499f04d99dab88ce83))
- ROM_LOAD("gfxu107.g60.bin", 0x0a20, 0x0020, CRC(425d3a98) SHA1(9ae4ce3761c2f995d00bed8d752c55224d274062))
-
- ROM_REGION(0x0240, "cpu", 0)
- ROM_LOAD("cpuu503.p2.bin", 0x0000, 0x0200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848))
- ROM_LOAD("cpuu602.p1.bin", 0x0200, 0x0020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805))
- ROM_LOAD("cpuu502.p0.bin", 0x0220, 0x0020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735))
-ROM_END
-
-ROM_START( multibox )
- ROM_REGION16_BE(0x4000, "monitor", 0)
- // "Sun Network Monitor, Version 0.10"
- ROM_LOAD16_BYTE("cybu103-sun10v.bin", 0x0000, 0x2000, CRC(32e53691) SHA1(5b0a3c4b5352d4c19067d9eb4db6cd1f76427892))
- ROM_LOAD16_BYTE("cybu101-sun10v.bin", 0x0001, 0x2000, CRC(900b725c) SHA1(90343de9e64ff8227c7e58c9d4df02783ecf5a76))
-
- ROM_REGION16_BE(0x4000, "cyb", 0)
- // "CYB Monitor - Version 2.1 6/23/84"
- ROM_LOAD16_BYTE("cybu104-autov.bin", 0x0000, 0x2000, CRC(46570c0e) SHA1(42ad96ffb7cb0e2d9c0aa0ace283ff646eaa4584))
- ROM_LOAD16_BYTE("cybu102-autov.bin", 0x0001, 0x2000, CRC(6198c5f6) SHA1(7d880395c4da03ac5130905c850ad638cd4b86e9))
+ROM_START(sun1)
ROM_END
} // anonymous namespace
-
-/* Driver */
-
-// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
-COMP( 1982, sun1, 0, 0, sun1, sun1, sun1_state, empty_init, "Sun Microsystems", "Sun-1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-COMP( 1984, multibox, 0, 0, cyb, sun1, sun1_state, empty_init, "CYB Systems", "Multibox", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1982, sun1, 0, 0, sun1, 0, sun1_state, empty_init, "Sun Microsystems", "Sun-1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
diff --git a/src/mame/sun/sun1_cpu.cpp b/src/mame/sun/sun1_cpu.cpp
new file mode 100644
index 00000000000..8d329dc4096
--- /dev/null
+++ b/src/mame/sun/sun1_cpu.cpp
@@ -0,0 +1,408 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic, Patrick Mackinlay
+
+/*
+ * This device emulates the Multibus Sun-1 CPU board, which consists of the
+ * following key components:
+ *
+ * - 68000 CPU @ 8/10MHz
+ * - Sun-1 memory management unit
+ * - 256KiB RAM + up to 32KiB EPROM
+ * - Am9513 system timing controller
+ * - i8247/μPD7201 dual UART
+ *
+ * Sources:
+ * - Sun-1 System Reference Manual, Draft Version 1.0, July 27, 1982, Sun Microsystems, Inc.
+ * - Sun 68000 Board User's Manual, Revision B, February 1983, Sun Microsystems Inc.
+ *
+ * TODO:
+ * - second generation Sun-1.5 variant
+ * - variants from other companies
+ * - keyboard, mouse
+ * - jumpers
+ */
+
+/*
+ * WIP:
+ * - bios 1: "t" enters CYB monitor
+ * - bios 6: top 3 bits of parallel register control tests
+ */
+
+#include "emu.h"
+
+#include "sun1_cpu.h"
+
+#include "bus/rs232/rs232.h"
+#include "machine/clock.h"
+
+#define LOG_DOG (1U << 2)
+
+//#define VERBOSE (LOG_GENERAL|LOG_DOG)
+#include "logmacro.h"
+
+enum irq_mask : u8
+{
+ WATCHDOG = 0x80,
+};
+
+DEFINE_DEVICE_TYPE(MULTIBUS_SUN1CPU, sun1cpu_device, "sun1cpu", "Sun-1 CPU")
+
+sun1cpu_device::sun1cpu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, MULTIBUS_SUN1CPU, tag, owner, clock)
+ , device_multibus_interface(mconfig, *this)
+ , m_cpu(*this, "cpu")
+ , m_mmu(*this, "mmu")
+ , m_duart(*this, "duart")
+ , m_stc(*this, "stc")
+ , m_boot(*this, "boot")
+{
+}
+
+ROM_START(sun1)
+ /*
+ * These different firmware versions expect slightly different hardware/timer
+ * configurations and probably come from CPU boards that were made or modified
+ * by other companies. The standard configuration according to the available
+ * documentation should be:
+ *
+ * - stc @ 5MHz
+ * - counter 1: watchdog (@2.98ms)
+ * - counter 2: user irq 6
+ * - cuonter 3: refresh irq 7 (@2ms)
+ * - counter 4: uarta (16x baud)
+ * - counter 5: uartb (16x baud)
+ *
+ * Most of these firmware versions do not configure timer 1 as a watchdog, and
+ * probably rely on a dedicated watchdog timer circuit like that described in
+ * the VSI schematics instead.
+ *
+ * The "aj" firmware is quite different: it does not use the STC for the DUART
+ * clocks, and uses counter 5 for refresh.
+ *
+ */
+ ROM_SYSTEM_BIOS(0, "sun1", "Sun Monitor, Version 1.0") // osc=16, div=4
+ ROM_SYSTEM_BIOS(1, "cyb", "Sun Network Monitor, Version 0.10 (CYB)") // osc=16, div=4
+ ROM_SYSTEM_BIOS(2, "test", "Interactive Tests") // osc=16, div=2
+ ROM_SYSTEM_BIOS(3, "fti10_11", "Sun Monitor, Version 1.1 (FTI10)") // osc=19.6, div=8
+ ROM_SYSTEM_BIOS(4, "fti10_10", "Sun Monitor, Version 1.0 (FTI10)") // osc=19.6, div=8
+ ROM_SYSTEM_BIOS(5, "smi10", "Sun Monitor, Version 1.1 (SMI10)") // osc=19.6, div=4, watchdog=1
+ ROM_SYSTEM_BIOS(6, "aj", "Sun-1 Workstation Monitor; PROM 1&3: Rev AJ; PROM 2&4: Rev AJ") // osc=20, div=4, watchdog=1
+
+ /*
+ * The prom0 region at 0x02'0000 holds the primary firmware, and is also
+ * readable from address 0x00'0000 until boot mode is disabled.
+ */
+ ROM_REGION16_BE(0x4000, "prom0", ROMREGION_ERASEFF)
+
+ // stc @ 4MHz (16MHz / 4)
+ // counter 3 mode 0b22 load=8000 == 2ms
+ // counter 4 mode 0b22 load=13
+ // counter 5 mode 0b22 load=13
+ ROMX_LOAD("v10.8.u103", 0x0000, 0x2000, CRC(3528a0f8) SHA1(be437dd93d1a44eccffa6f5e05935119482beab0), ROM_SKIP(1) | ROM_BIOS(0))
+ ROMX_LOAD("v10.0.u101", 0x0001, 0x2000, CRC(1ad4c52a) SHA1(4bc1a19e8f202378d5d7baa8b95319275c040a6d), ROM_SKIP(1) | ROM_BIOS(0))
+
+ // Sun Network Monitor, Version 0.10
+ // stc @ 4MHz (16MHz / 4)
+ // counter 3 mode 0b22 load=8000 == 2ms
+ // counter 4 mode 0b22 load=13
+ // counter 5 mode 0b22 load=13
+ ROMX_LOAD("cybu103-sun10v.u103", 0x0000, 0x2000, CRC(32e53691) SHA1(5b0a3c4b5352d4c19067d9eb4db6cd1f76427892), ROM_SKIP(1) | ROM_BIOS(1))
+ ROMX_LOAD("cybu101-sun10v.u101", 0x0001, 0x2000, CRC(900b725c) SHA1(90343de9e64ff8227c7e58c9d4df02783ecf5a76), ROM_SKIP(1) | ROM_BIOS(1))
+
+ // Interactive Tests
+ // stc @ 8MHz (16MHz / 2)
+ // counter 3 mode 0b22 load=16000 == 2ms
+ // counter 4 mode 0b22 load=26
+ // counter 5 mode 0b22 load=26
+ ROMX_LOAD("8mhzdiag.8.u103", 0x0000, 0x2000, CRC(808a549e) SHA1(d2aba014a5507c1538f2c1a73e1d2524f28034f4), ROM_SKIP(1) | ROM_BIOS(2))
+ ROMX_LOAD("8mhzdiag.0.u101", 0x0001, 0x2000, CRC(7a92d506) SHA1(5df3800f7083293fc01bb6a7e7538ad425bbebfb), ROM_SKIP(1) | ROM_BIOS(2))
+
+ // Sun Monitor, Version 1.1
+ // stc @ 2.4576MHz (19.6MHz / 8)
+ // counter 3 mode 0b22 load=4915 == ~2ms
+ // counter 4 mode 0b22 load=8
+ // counter 5 mode 0b22 load=8
+ ROMX_LOAD("fti10_11.u103", 0x0000, 0x1000, CRC(1a5befa5) SHA1(cd7fc4c88a76ae393ada6a3f427993fbacb8a9c6), ROM_SKIP(1) | ROM_BIOS(3))
+ ROMX_LOAD("fti10_11.u101", 0x0001, 0x1000, CRC(ac733618) SHA1(baff471248b355d761a5dab375fc88d861cb6853), ROM_SKIP(1) | ROM_BIOS(3))
+
+ // Sun Monitor, Version 1.0
+ // stc @ 2.4576MHz (19.6MHz / 8)
+ // counter 3 mode 0b22 load=4915 == ~2ms
+ // counter 4 mode 0b22 load=8
+ // counter 5 mode 0b22 load=8
+ ROMX_LOAD("fti10_10.u103", 0x0000, 0x1000, CRC(fb5e599b) SHA1(5ab1ac61034aac956fd6e39b964996e0f9d0da97), ROM_SKIP(1) | ROM_BIOS(4))
+ ROMX_LOAD("fti10_10.u101", 0x0001, 0x1000, CRC(e7371fd6) SHA1(5a377a4363b59c5508f941c356d8827594a72dc8), ROM_SKIP(1) | ROM_BIOS(4))
+
+ // Sun Monitor, Version 1.1
+ // stc @ 4.9152MHz (19.6MHz / 4)
+ // counter 1 mode 0621 load=7568 == ~3ms
+ // counter 3 mode 0622 load=4915 == ~2ms
+ // counter 4 mode 0b22 load=16
+ // counter 5 mode 0b22 load=16
+ ROMX_LOAD("smi10_11.u103", 0x0000, 0x1000, CRC(89e1a6ed) SHA1(2dfcba08fb6e4582e05b803c4507419c25dbdaa8), ROM_SKIP(1) | ROM_BIOS(5))
+ ROMX_LOAD("smi10_11.u101", 0x0001, 0x1000, CRC(432db053) SHA1(99a54a653f18b914ae15f230a35ac55c5d076a4c), ROM_SKIP(1) | ROM_BIOS(5))
+
+ // PROM 1&3: Rev AJ
+ // stc @ 5MHz (20MHz / 4)
+ // counter 1 mode 0621 load=7450 == 2.98ms
+ // counter 5 mode 0622 load=5000 == 2ms
+ // uarts fixed @ 9600
+ ROMX_LOAD("sun1c_1h.u103", 0x0000, 0x2000, CRC(4c58de6c) SHA1(f417a0a013d4ca3aaf565b0e7a88dcf3da1347c1), ROM_SKIP(1) | ROM_BIOS(6))
+ ROMX_LOAD("sun1c_1l.u101", 0x0001, 0x2000, CRC(41c47355) SHA1(a38df286ab8ba9a462862ab1cc9fa28b79fb9949), ROM_SKIP(1) | ROM_BIOS(6))
+
+ /*
+ * The prom1 region at 0x04'0000 holds secondary/optional diagnostic or
+ * boot firmware.
+ */
+ ROM_REGION16_BE(0x4000, "prom1", ROMREGION_ERASEFF)
+
+ // CYB Monitor - Version 2.1 6/23/84
+ ROMX_LOAD("cybu104-autov.u104", 0x0000, 0x2000, CRC(46570c0e) SHA1(42ad96ffb7cb0e2d9c0aa0ace283ff646eaa4584), ROM_SKIP(1) | ROM_BIOS(1))
+ ROMX_LOAD("cybu102-autov.u102", 0x0001, 0x2000, CRC(6198c5f6) SHA1(7d880395c4da03ac5130905c850ad638cd4b86e9), ROM_SKIP(1) | ROM_BIOS(1))
+
+ // PROM 2&4: Rev AJ
+ ROMX_LOAD("sun1c_2h.u104", 0x0000, 0x2000, CRC(b5cbcb2b) SHA1(8f906dbdc9b2af90bc8f942eebe7006be0ff4e86), ROM_SKIP(1) | ROM_BIOS(6))
+ ROMX_LOAD("sun1c_2l.u102", 0x0001, 0x2000, CRC(dc82578b) SHA1(65bf18a8a40171ec08137a0c446da3b272b9ccbc), ROM_SKIP(1) | ROM_BIOS(6))
+
+ // TODO: move the following firmware to a graphics card
+ ROM_REGION(0x10000, "gfx", ROMREGION_ERASEFF)
+ ROM_LOAD("gfxu605.g4.bin", 0x0000, 0x0200, CRC(274b7b3d) SHA1(40d8be2cfcbd03512a05925991bb5030d5d4b5e9))
+ ROM_LOAD("gfxu308.g21.bin", 0x0200, 0x0200, CRC(35a6eed8) SHA1(25cb2dd8e5343cd7927c3045eb4cb96dc9935a37))
+ ROM_LOAD("gfxu108.g20.bin", 0x0400, 0x0200, CRC(ecee335e) SHA1(5f4d32dc918af15872cd6e700a04720caeb6c657))
+ ROM_LOAD("gfxu105.g0.bin", 0x0600, 0x0200, CRC(8e1a24b3) SHA1(dad2821c3a3137ad69e78b6fc29ab582e5d78646))
+ ROM_LOAD("gfxu104.g1.bin", 0x0800, 0x0200, CRC(86f7a483) SHA1(8eb3778f5497741cd4345e81ff1a903c9a63c8bb))
+ ROM_LOAD("gfxu307.g61.bin", 0x0a00, 0x0020, CRC(b190f25d) SHA1(80fbdc843f1eb68a2d3713499f04d99dab88ce83))
+ ROM_LOAD("gfxu107.g60.bin", 0x0a20, 0x0020, CRC(425d3a98) SHA1(9ae4ce3761c2f995d00bed8d752c55224d274062))
+
+ ROM_REGION(0x10000, "cpu", ROMREGION_ERASEFF)
+ ROM_LOAD("cpuu503.p2.bin", 0x0000, 0x0200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848))
+ ROM_LOAD("cpuu602.p1.bin", 0x0200, 0x0020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805))
+ ROM_LOAD("cpuu502.p0.bin", 0x0220, 0x0020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735))
+ROM_END
+
+const tiny_rom_entry *sun1cpu_device::device_rom_region() const
+{
+ return ROM_NAME(sun1);
+}
+
+static INPUT_PORTS_START(sun1)
+INPUT_PORTS_END
+
+ioport_constructor sun1cpu_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(sun1);
+}
+
+void sun1cpu_device::device_start()
+{
+ save_item(NAME(m_irq));
+ save_item(NAME(m_watchdog));
+ save_item(NAME(m_parity));
+
+ m_cpu->space(AS_PROGRAM).specific(m_cpu_mem);
+ m_cpu->space(m68000_device::AS_CPU_SPACE).specific(m_cpu_spc);
+
+ m_irq = 0;
+}
+
+void sun1cpu_device::device_reset()
+{
+ // HACK: configure oscillator, divisor and watchdog type to match selected
+ // firmware until more accurate board type/configuration can be determined
+ XTAL oscillator = 16_MHz_XTAL;
+ unsigned divisor = 4;
+ m_watchdog = false;
+
+ switch (system_bios())
+ {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ divisor = 2;
+ break;
+ case 4:
+ case 5:
+ oscillator = 19.6608_MHz_XTAL;
+ divisor = 8;
+ break;
+ case 6:
+ oscillator = 19.6608_MHz_XTAL;
+ m_watchdog = true;
+ break;
+ case 7:
+ oscillator = 20_MHz_XTAL;
+ m_watchdog = true;
+ break;
+ }
+
+ // configure cpu, stc and duart clocks
+ m_cpu->set_clock(oscillator / 2);
+ m_stc->set_clock(oscillator / divisor);
+ m_duart->set_clock(oscillator / divisor);
+
+ m_parity = false;
+
+ if (!boot())
+ {
+ m_cpu->set_current_mmu(this);
+
+ // disable interrupts
+ for (unsigned i = 0; i < 7; i++)
+ if (BIT(m_irq, i))
+ m_cpu->set_input_line(M68K_IRQ_1 + i, CLEAR_LINE);
+
+ m_boot.select(0);
+ }
+}
+
+void sun1cpu_device::device_add_mconfig(machine_config &config)
+{
+ M68000(config, m_cpu, 0);
+ m_cpu->set_current_mmu(this);
+ m_cpu->set_addrmap(AS_PROGRAM, &sun1cpu_device::cpu_mem);
+
+ // route multibus interrupts 1..4 to cpu
+ int_callback<1>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_1>)).invert();
+ int_callback<2>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_2>)).invert();
+ int_callback<3>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_3>)).invert();
+ int_callback<4>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_4>)).invert();
+
+ SUN1MMU(config, m_mmu);
+ m_mmu->error().set(
+ [this](int state)
+ {
+ // check multibus access pending
+ if (state == sun1mmu_device::MMU_DEFER)
+ {
+ if (!m_watchdog || (m_irq & WATCHDOG))
+ {
+ LOGMASKED(LOG_DOG, "watchdog bus error\n");
+ m_cpu->trigger_bus_error();
+ m_mmu->reset();
+ }
+ else
+ m_cpu->defer_access();
+ }
+ else
+ m_cpu->trigger_bus_error();
+ });
+
+ // default configuration (1=watchdog, 2=user, 3=refresh, 4=uarta, 5=uartb)
+ AM9513(config, m_stc, 0);
+ m_stc->fout_cb().set(m_stc, FUNC(am9513_device::gate1_w));
+ m_stc->out1_cb().set(FUNC(sun1cpu_device::watchdog_w));
+ m_stc->out2_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_6>));
+ m_stc->out3_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_7>));
+ m_stc->out4_cb().set(m_duart, FUNC(upd7201_device::rxca_w));
+ m_stc->out4_cb().append(m_duart, FUNC(upd7201_device::txca_w));
+ m_stc->out5_cb().set(m_duart, FUNC(upd7201_device::rxcb_w));
+ m_stc->out5_cb().append(m_duart, FUNC(upd7201_device::txcb_w));
+
+ if (false)
+ {
+ // TODO: "aj" firmware has refresh on counter 5, and fixed-speed uarts
+ m_stc->out3_cb().set_nop();
+ m_stc->out4_cb().set_nop();
+ m_stc->out5_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_7>));
+
+ // assume duart rxc/txc driven at 9600baud (x16 clock)
+ CLOCK(config, "duart_clock", 9600 * 16).signal_handler().set(
+ [this](int state)
+ {
+ m_duart->txca_w(state);
+ m_duart->rxca_w(state);
+ m_duart->txcb_w(state);
+ m_duart->rxcb_w(state);
+ });
+ }
+
+ // port A: txd, rxd, rts, cts, dsr, dtr
+ // port B: txd, rxd
+ UPD7201(config, m_duart, 0);
+ m_duart->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
+ m_duart->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
+ m_duart->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
+ m_duart->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
+ m_duart->out_int_callback().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_5>));
+
+ rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
+ rs232a.rxd_handler().set(m_duart, FUNC(upd7201_device::rxa_w));
+ rs232a.cts_handler().set(m_duart, FUNC(upd7201_device::ctsa_w));
+ rs232a.dcd_handler().set(m_duart, FUNC(upd7201_device::dcda_w));
+
+ rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
+ rs232b.rxd_handler().set(m_duart, FUNC(upd7201_device::rxb_w));
+}
+
+void sun1cpu_device::device_config_complete()
+{
+ m_mmu.lookup()->set_space<0>(m_cpu, AS_PROGRAM);
+ m_mmu.lookup()->set_space<1>(m_cpu, m68000_device::AS_CPU_SPACE);
+ m_mmu.lookup()->set_space<2>(m_bus, AS_PROGRAM);
+ m_mmu.lookup()->set_space<3>(m_bus, AS_IO);
+}
+
+void sun1cpu_device::cpu_mem(address_map &map)
+{
+ map(0x00'0000, 0x03'ffff).ram();
+
+ map(0x00'0000, 0x1f'ffff).view(m_boot);
+ m_boot[0](0x00'0000, 0x00'3fff).mirror(0x03'c000).rom().region("prom0", 0);
+
+ map(0x20'0000, 0x20'3fff).mirror(0x03'c000).rom().region("prom0", 0).w(FUNC(sun1cpu_device::prom0_w));
+ map(0x40'0000, 0x40'3fff).mirror(0x03'c000).rom().region("prom1", 0);
+ map(0x60'0000, 0x60'0007).mirror(0x1ffff8).rw(m_duart, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
+ map(0x80'0000, 0x80'0003).mirror(0x1ffffc).rw(m_stc, FUNC(am9513_device::read16), FUNC(am9513_device::write16));
+ map(0xa0'0000, 0xbf'ffff).rw(m_mmu, FUNC(sun1mmu_device::page_r), FUNC(sun1mmu_device::page_w));
+ map(0xc0'0000, 0xdf'ffff).rw(m_mmu, FUNC(sun1mmu_device::segment_r), FUNC(sun1mmu_device::segment_w));
+ map(0xe0'0000, 0xe0'0001).mirror(0x1f'fffe).r(FUNC(sun1cpu_device::parallel_r));
+ map(0xe0'0000, 0xe0'0001).mirror(0x1f'fffe).w(m_mmu, FUNC(sun1mmu_device::context_w));
+}
+
+void sun1cpu_device::watchdog_w(int state)
+{
+ if (state)
+ m_irq |= WATCHDOG;
+ else
+ m_irq &= ~WATCHDOG;
+
+ if (!BIT(m_irq, 6))
+ {
+ if (state)
+ {
+ LOGMASKED(LOG_DOG, "watchdog reset\n");
+ reset();
+ }
+ }
+}
+
+template <unsigned N> void sun1cpu_device::irq_w(int state)
+{
+ if (state)
+ m_irq |= 1U << (N - M68K_IRQ_1);
+ else
+ m_irq &= ~(1U << (N - M68K_IRQ_1));
+
+ if (!boot())
+ m_cpu->set_input_line(N, state);
+}
+
+void sun1cpu_device::prom0_w(u16 data)
+{
+ m_parity = BIT(data, 0);
+
+ if (boot())
+ {
+ LOG("boot mode disabled (%s)\n", machine().describe_context());
+
+ m_cpu->set_current_mmu(m_mmu);
+
+ // enable interrupts
+ for (unsigned i = 0; i < 7; i++)
+ if (BIT(m_irq, i))
+ m_cpu->set_input_line(M68K_IRQ_1 + i, ASSERT_LINE);
+
+ m_boot.disable();
+ }
+}
diff --git a/src/mame/sun/sun1_cpu.h b/src/mame/sun/sun1_cpu.h
new file mode 100644
index 00000000000..c50a15dbb66
--- /dev/null
+++ b/src/mame/sun/sun1_cpu.h
@@ -0,0 +1,69 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic, Patrick Mackinlay
+
+#ifndef MAME_SUN_SUN1_CPU_H
+#define MAME_SUN_SUN1_CPU_H
+
+#pragma once
+
+#include "sun1_mmu.h"
+
+#include "bus/multibus/multibus.h"
+#include "cpu/m68000/m68000.h"
+#include "machine/am9513.h"
+#include "machine/z80sio.h"
+
+class sun1cpu_device
+ : public device_t
+ , public device_multibus_interface
+ , public m68000_device::mmu
+{
+public:
+ sun1cpu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+protected:
+ // device_t implementation
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_config_complete() override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ void cpu_mem(address_map &map);
+
+ void prom0_w(u16 data);
+ u16 parallel_r() { return 0x1fff; }
+
+ void watchdog_w(int state);
+ template <unsigned N> void irq_w(int state);
+
+ // m68000_device::mmu boot mode implementation
+ virtual u16 read_program(offs_t logical, u16 mem_mask) override { return m_cpu_mem.read_word(logical, mem_mask); }
+ virtual void write_program(offs_t logical, u16 data, u16 mem_mask) override { m_cpu_mem.write_word(logical, data, mem_mask); }
+ virtual u16 read_data(offs_t logical, u16 mem_mask) override { return m_cpu_mem.read_word(logical, mem_mask); }
+ virtual void write_data(offs_t logical, u16 data, u16 mem_mask) override { m_cpu_mem.write_word(logical, data, mem_mask); }
+ virtual u16 read_cpu(offs_t logical, u16 mem_mask) override { return m_cpu_spc.read_word(logical, mem_mask); }
+ virtual void set_super(bool super) override {}
+
+ bool boot() const { return m_boot.entry().has_value(); }
+
+private:
+ required_device<m68000_device> m_cpu;
+ required_device<sun1mmu_device> m_mmu;
+ required_device<upd7201_device> m_duart;
+ required_device<am9513_device> m_stc;
+
+ memory_view m_boot;
+
+ memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_mem;
+ memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_spc;
+
+ u8 m_irq;
+ bool m_watchdog;
+ bool m_parity;
+};
+
+DECLARE_DEVICE_TYPE(MULTIBUS_SUN1CPU, sun1cpu_device)
+
+#endif // MAME_SUN_SUN1_CPU_H
diff --git a/src/mame/sun/sun1_mmu.cpp b/src/mame/sun/sun1_mmu.cpp
new file mode 100644
index 00000000000..8691e4f132a
--- /dev/null
+++ b/src/mame/sun/sun1_mmu.cpp
@@ -0,0 +1,309 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * This device emulates the Sun-1 memory management unit.
+ *
+ * Sources:
+ * - Sun-1 System Reference Manual, Draft Version 1.0, July 27, 1982, Sun Microsystems, Inc.
+ * - Sun 68000 Board User's Manual, Revision B, February 1983, Sun Microsystems Inc.
+ *
+ * TODO:
+ * - everything
+ */
+
+#include "emu.h"
+
+#include "sun1_mmu.h"
+
+#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+enum segment_mask : u16
+{
+ SEGMENT_PPTR = 0x003f,
+ SEGMENT_PROT = 0x0f00,
+ SEGMENT_CTXT = 0xf000,
+};
+enum page_mask : u16
+{
+ PAGE_ADDR = 0x0fff,
+ PAGE_TYPE = 0x3000, // 0=on-board memory, 1=nonexistent, 2=multibus memory, 3=multibus i/o
+ PAGE_MOD = 0x4000,
+ PAGE_ACC = 0x8000,
+};
+enum mode_mask : unsigned
+{
+ P_R = 0x04, // read
+ P_W = 0x02, // write
+ P_X = 0x01, // execute
+
+ P_RX = P_R | P_X,
+ P_RW = P_R | P_W,
+ P_RWX = P_R | P_W | P_X,
+};
+
+DEFINE_DEVICE_TYPE(SUN1MMU, sun1mmu_device, "sun1mmu", "Sun-1 MMU")
+
+sun1mmu_device::sun1mmu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SUN1MMU, tag, owner, clock)
+ , m_space{
+ {*this, finder_base::DUMMY_TAG, 0},
+ {*this, finder_base::DUMMY_TAG, 1},
+ {*this, finder_base::DUMMY_TAG, 2},
+ {*this, finder_base::DUMMY_TAG, 3},
+ }
+ , m_error(*this)
+ , m_context(0)
+ , m_segment{}
+ , m_page{}
+ , m_super(true)
+{
+}
+
+void sun1mmu_device::device_start()
+{
+ save_item(NAME(m_context));
+ save_item(NAME(m_segment));
+ save_item(NAME(m_page));
+ save_item(NAME(m_stall));
+ save_item(NAME(m_super));
+
+ m_space[0]->specific(m_cpu_mem);
+ m_space[1]->specific(m_cpu_spc);
+ m_space[2]->specific(m_bus_mem);
+ m_space[3]->specific(m_bus_pio);
+}
+
+void sun1mmu_device::device_reset()
+{
+ m_stall = false;
+}
+
+static bool access(bool const super, unsigned const code, unsigned const mode)
+{
+ static constexpr unsigned protection[2][16] =
+ {
+ // user mode
+ {
+ 0 , 0 , 0 , 0 , 0 , 0 , P_R , P_R ,
+ P_RW , P_RW , P_RX , P_RWX, P_RX , P_RX , P_X , P_RWX,
+ },
+ // supervisor mode
+ {
+ 0 , P_X , P_R , P_RX , P_RW , P_RWX, P_R , P_RW ,
+ P_R , P_RW , P_RW , P_RW , P_RX , P_RWX, P_RWX, P_RWX,
+ },
+ };
+
+ return protection[super][code] & mode;
+}
+
+void sun1mmu_device::context_w(u16 data)
+{
+ LOG("context 0x%04x (%s)\n", data, machine().describe_context());
+
+ m_context = data & 0xf000U;
+}
+
+u16 sun1mmu_device::segment_r(offs_t offset)
+{
+ return m_context | m_segment[m_context >> 12][BIT(offset, 14, 6)];
+}
+
+void sun1mmu_device::segment_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ LOG("segment[0x%x][0x%02x] 0x%04x (%s)\n", m_context >> 12, BIT(offset, 14, 6), data, machine().describe_context());
+
+ m_segment[m_context >> 12][BIT(offset, 14, 6)] = data & 0x0fffU;
+}
+
+u16 sun1mmu_device::page_r(offs_t offset)
+{
+ u16 const segment = m_segment[m_context >> 12][BIT(offset, 14, 6)];
+
+ return m_page[(segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4)];
+}
+
+void sun1mmu_device::page_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ u16 const segment = m_segment[m_context >> 12][BIT(offset, 14, 6)];
+
+ LOG("page[0x%03x] 0x%04x (%s)\n", (segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4), data, machine().describe_context());
+
+ m_page[(segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4)] = data;
+}
+
+std::optional<std::pair<unsigned, offs_t>> sun1mmu_device::translate(offs_t const logical, unsigned const mode)
+{
+ // check for mapped address
+ if (logical < 0x20'0000)
+ {
+ u16 const segment = m_segment[m_context >> 12][BIT(logical, 15, 6)];
+
+ // check segment map error
+ if (access(m_super, BIT(segment, 8, 4), mode) || machine().side_effects_disabled())
+ {
+ u16 &page = m_page[(segment & SEGMENT_PPTR) << 4 | BIT(logical, 11, 4)];
+
+ // update reference and modify bits
+ if (!machine().side_effects_disabled())
+ {
+ if (mode & P_W)
+ page |= PAGE_ACC | PAGE_MOD;
+ else
+ page |= PAGE_ACC;
+ }
+
+ return std::pair<unsigned, offs_t>(BIT(page, 12, 2), (page & PAGE_ADDR) << 11 | BIT(logical, 0, 11));
+ }
+ else
+ LOG("segment map error 0x%08x context %d segment 0x%04x mode %c (%s)\n",
+ logical, m_context, segment, (mode == P_R) ? 'R' : (mode == P_W) ? 'W' : 'X', machine().describe_context());
+ }
+ else if (m_super || machine().side_effects_disabled())
+ // unmapped supervisor access
+ return std::pair<unsigned, offs_t>(0, logical);
+ else
+ LOG("system space error 0x%08x (%s)\n", logical, machine().describe_context());
+
+ // protection error
+ return std::nullopt;
+}
+
+template <bool Execute> u16 sun1mmu_device::mmu_read(offs_t logical, u16 mem_mask)
+{
+ u16 data = 0;
+
+ if (m_stall)
+ {
+ m_error(MMU_DEFER);
+
+ return data;
+ }
+
+ auto const t = translate(logical, Execute ? P_X : P_R);
+
+ if (t.has_value())
+ {
+ auto const [type, physical] = t.value();
+ u16 flags = 0;
+
+ switch (type)
+ {
+ case 0:
+ // on-board memory
+ data = m_cpu_mem.read_word(physical, mem_mask);
+ break;
+ case 1:
+ // nonexistent
+ LOG("nonexistent_r 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_error(MMU_ERROR);
+ break;
+ case 2:
+ // multibus memory
+ std::tie(data, flags) = m_bus_mem.read_word_flags(physical, mem_mask);
+ if (flags)
+ {
+ LOG("multibus_r mem 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_stall = true;
+ m_error(MMU_DEFER);
+ }
+ break;
+ case 3:
+ // mutibus i/o
+ std::tie(data, flags) = m_bus_pio.read_word_flags(physical, mem_mask);
+ if (flags)
+ {
+ LOG("multibus_r i/o 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_stall = true;
+ m_error(MMU_DEFER);
+ }
+ break;
+ }
+ }
+ else
+ m_error(MMU_ERROR);
+
+ return data;
+}
+
+void sun1mmu_device::mmu_write(offs_t logical, u16 data, u16 mem_mask)
+{
+ if (m_stall)
+ {
+ m_error(MMU_DEFER);
+
+ return;
+ }
+
+ auto const t = translate(logical, P_W);
+
+ if (t.has_value())
+ {
+ auto const [type, physical] = t.value();
+
+ switch (type)
+ {
+ case 0:
+ // on-board memory
+ m_cpu_mem.write_word(physical, data, mem_mask);
+ break;
+ case 1:
+ // nonexistent
+ LOG("nonexistent write 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_error(MMU_ERROR);
+ break;
+ case 2:
+ // multibus memory
+ if (m_bus_mem.write_word_flags(physical, data, mem_mask))
+ {
+ LOG("multibus_w mem 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_stall = true;
+ m_error(MMU_DEFER);
+ }
+ break;
+ case 3:
+ // mutibus i/o
+ if (m_bus_pio.write_word_flags(physical, data, mem_mask))
+ {
+ LOG("multibus_w i/o 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
+ m_stall = true;
+ m_error(MMU_DEFER);
+ }
+ break;
+ }
+ }
+ else
+ m_error(MMU_ERROR);
+}
+
+u16 sun1mmu_device::read_program(offs_t logical, u16 mem_mask)
+{
+ return mmu_read<true>(logical, mem_mask);
+}
+
+void sun1mmu_device::write_program(offs_t logical, u16 data, u16 mem_mask)
+{
+ mmu_write(logical, data, mem_mask);
+}
+
+u16 sun1mmu_device::read_data(offs_t logical, u16 mem_mask)
+{
+ return mmu_read<false>(logical, mem_mask);
+}
+
+void sun1mmu_device::write_data(offs_t logical, u16 data, u16 mem_mask)
+{
+ mmu_write(logical, data, mem_mask);
+}
+
+u16 sun1mmu_device::read_cpu(offs_t logical, u16 mem_mask)
+{
+ return m_cpu_spc.read_word(logical, mem_mask);
+}
+
+void sun1mmu_device::set_super(bool super)
+{
+ m_super = super;
+}
diff --git a/src/mame/sun/sun1_mmu.h b/src/mame/sun/sun1_mmu.h
new file mode 100644
index 00000000000..12e0e98c06b
--- /dev/null
+++ b/src/mame/sun/sun1_mmu.h
@@ -0,0 +1,73 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_SUN_SUN1_MMU_H
+#define MAME_SUN_SUN1_MMU_H
+
+#pragma once
+
+#include "cpu/m68000/m68000.h"
+
+class sun1mmu_device
+ : public device_t
+ , public m68000_device::mmu
+{
+public:
+ template <unsigned N, typename T> void set_space(T &&tag, int spacenum) { m_space[N].set_tag(std::forward<T>(tag), spacenum); }
+ template <unsigned N> void set_space(device_t &base, char const *tag, int spacenum) { m_space[N].set_tag(base, tag, spacenum); }
+
+ enum error_type : unsigned
+ {
+ MMU_DEFER = 0, // invalid multibus access
+ MMU_ERROR = 1, // bus error (system space, segment protection, nonexistent page)
+ };
+ auto error() { return m_error.bind(); }
+
+ sun1mmu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0);
+
+ // register access
+ void context_w(u16 data);
+ u16 segment_r(offs_t offset);
+ void segment_w(offs_t offset, u16 data, u16 mem_mask);
+ u16 page_r(offs_t offset);
+ void page_w(offs_t offset, u16 data, u16 mem_mask);
+
+protected:
+ // device_t implementation
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ // m68000_device::mmu implementation
+ virtual u16 read_program(offs_t addr, u16 mem_mask) override;
+ virtual void write_program(offs_t addr, u16 data, u16 mem_mask) override;
+ virtual u16 read_data(offs_t addr, u16 mem_mask) override;
+ virtual void write_data(offs_t addr, u16 data, u16 mem_mask) override;
+ virtual u16 read_cpu(offs_t addr, u16 mem_mask) override;
+ virtual void set_super(bool super) override;
+
+ std::optional<std::pair<unsigned, offs_t>> translate(offs_t const address, unsigned const mode);
+ template <bool Execute> u16 mmu_read(offs_t logical, u16 mem_mask);
+ void mmu_write(offs_t logical, u16 data, u16 mem_mask);
+
+private:
+ required_address_space m_space[4];
+ devcb_write_line m_error;
+
+ memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_mem;
+ memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_spc;
+
+ // only 20 of the possible 24 memory address lines are connected
+ memory_access<20, 1, 0, ENDIANNESS_LITTLE>::specific m_bus_mem;
+ memory_access<16, 1, 0, ENDIANNESS_LITTLE>::specific m_bus_pio;
+
+ u16 m_context;
+ u16 m_segment[16][64];
+ u16 m_page[1024];
+
+ bool m_stall;
+ bool m_super;
+};
+
+DECLARE_DEVICE_TYPE(SUN1MMU, sun1mmu_device)
+
+#endif // MAME_SUN_SUN1_MMU_H