summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2021-01-08 15:10:31 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2021-01-08 15:10:31 +0700
commit89b519f58ed45d3373509f6945bed3fa25da642b (patch)
tree5e04e9525ca1eabb30949af0b7fcaa0c23eaa312
parent2ee5ac358caa6018951b7951404ec015a3f3f73b (diff)
New machines marked as NOT_WORKING
--- IBM RT PC [Bitsavers]
-rw-r--r--scripts/target/mame/mess.lua12
-rw-r--r--src/mame/drivers/rtpc.cpp768
-rw-r--r--src/mame/layout/rtpc.lay69
-rw-r--r--src/mame/machine/rosetta.cpp1202
-rw-r--r--src/mame/machine/rosetta.h181
-rw-r--r--src/mame/machine/rtpc_iocc.cpp349
-rw-r--r--src/mame/machine/rtpc_iocc.h170
-rw-r--r--src/mame/mame.lst7
-rw-r--r--src/mame/mess.flt1
9 files changed, 2759 insertions, 0 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 9c965137bec..d4693d037c6 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -165,6 +165,7 @@ CPUS["GTRON"] = true
CPUS["M88000"] = true
CPUS["XAVIX2"] = true
CPUS["UPD78K"] = true
+CPUS["ROMP"] = true
--------------------------------------------------
-- specify available sound cores; some of these are
@@ -920,6 +921,7 @@ BUSES["PSX_PARALLEL"] = true
BUSES["QL"] = true
BUSES["QBUS"] = true
BUSES["RS232"] = true
+BUSES["RTPC_KBD"] = true
BUSES["S100"] = true
BUSES["SAMCOUPE_DRIVE_PORT"] = true
BUSES["SAMCOUPE_EXPANSION"] = true
@@ -1334,6 +1336,7 @@ function linkProjects_mame_mess(_target, _subtarget)
"rockwell",
"roland",
"rolm",
+ "rtpc",
"sage",
"saitek",
"samcoupe",
@@ -3432,6 +3435,15 @@ files {
MAME_DIR .. "src/mame/drivers/aim65_40.cpp",
}
+createMESSProjects(_target, _subtarget, "rtpc")
+files {
+ MAME_DIR .. "src/mame/drivers/rtpc.cpp",
+ MAME_DIR .. "src/mame/machine/rosetta.cpp",
+ MAME_DIR .. "src/mame/machine/rosetta.h",
+ MAME_DIR .. "src/mame/machine/rtpc_iocc.cpp",
+ MAME_DIR .. "src/mame/machine/rtpc_iocc.h",
+}
+
createMESSProjects(_target, _subtarget, "sage")
files {
MAME_DIR .. "src/mame/drivers/sage2.cpp",
diff --git a/src/mame/drivers/rtpc.cpp b/src/mame/drivers/rtpc.cpp
new file mode 100644
index 00000000000..70f6046f7b4
--- /dev/null
+++ b/src/mame/drivers/rtpc.cpp
@@ -0,0 +1,768 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM RT PC.
+ *
+ * Sources:
+ * - http://bitsavers.org/pdf/ibm/pc/rt/75X0232_RT_PC_Technical_Reference_Volume_1_Jun87.pdf
+ * - http://www.cs.cmu.edu/afs/andrew.cmu.edu/usr/shadow/www/ibmrt.html
+ * - http://ps-2.kev009.com/ohlandl/6152/rt_index.html
+ *
+ * TODO:
+ * - finish refactoring iocc
+ * - additional machine variants
+ * - configurable RAM size
+ */
+/*
+ * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/6/897/ENUS186-006/index.html
+ * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/1/897/ENUS187-021/index.html
+ * https://www-01.ibm.com/common/ssi/ShowDoc.wss?docURL=/common/ssi/rep_ca/0/897/ENUS188-120/index.html
+ *
+ * Model Chassis CPU RAM HDD Release Price Notes
+ * 010 6151 032 1M/4M 40M Jan 1986 $11,700
+ * 015 6151 032 2M/4M 70M Nov 1986 $10,050
+ * 020 6150 032 1M/4M 40M Jan 1986 $14,945
+ * 025 6150 032 2M/4M 70M Jan 1986 $17,940
+ * A25 6150 032 2M/4M 70M Jan 1986 $19,510 5080 attachment/no keyboard
+ * 115 6151 Adv 4M/16M 70M Feb 1987 $10,600 AFPA
+ * 125 6150 Adv 4M/16M 70M Feb 1987 $16,100 AFPA
+ * B25 6150 Adv 4M/16M 70M Feb 1987 $17,670 AFPA, 5080 attachment/no keyboard
+ * 130 6151 Enh 16M 114M Jul 1988 $23,220 EAFPA
+ * 135 6150 Enh 16M 114M Jul 1988 $30,595 EAFPA
+ * B35 6150 Enh 16M 114M Jul 1988 $32,165 EAFPA, 5080 attachment/no keyboard
+ *
+ * 032 (aka SGP), 170ns (23.5294 MHz crystal / 4 == 5.882350 MHz == 170ns), 1MB/2MB/4MB memory boards
+ * Advanced, 100ns 4MB (6151) external (6150) (presume ~40MHz crystal/4), 4MB/8MB memory boards
+ * Enhanced, 80ns, 16MB soldered, EAFPA standard, CMOS (49.400 MHz crystal/4 == 12.350MHz == 80.971ns)
+ *
+ * AFPA is M68881 @ 20MHz
+ * EAFPA is AD90221-2 ADSP-3210 (multiplier) + AD90222-2 ADSP-3221 (fp alu) + AD90220-2 ADSP-1401 (program sequencer)
+ *
+ * system processor real memory address map
+ * 0000'0000-00ff'ffff 16MB memory management unit
+ * 0100'0000-efff'ffff -- not defined
+ *
+ * system board I/O addresses (all in segment F)
+ * f000'0000-f0ff'ffff 16MB i/o channel i/o map
+ * f100'0000-f3ff'ffff 48MB reserved
+ * f400'0000-f4ff'ffff 16MB i/o channel memory map
+ * f500'0000-f7ff'ffff reserved?
+ * f800'0000-fbff'ffff reserved
+ * fc00'0000-fdff'ffff mc68881
+ * fe00'0000-feff'ffff fpa2
+ * ff00'0000-ffff'ffff 16MB floating point accelerator
+ *
+ * system processor i/o address map
+ * 00'0000-7f'ffff 8MB not defined
+ * 80'0000-80'ffff 64KB processor channel device initialization
+ * 81'0000-81'ffff 64KB memory management unit
+ * 82'0000-ff'ffff 8064KB not defined
+ *
+ * advanced processor
+ * 80'0400 i/o interface 1 i/o base
+ * 80'0100 i/o interface 2 i/o base
+ *
+ */
+ /*
+ * WIP
+ * - diagnostic disk 1 fails with code 67
+ * - aix vrm disk 1 fails with alternating code a6/13
+ */
+
+#include "emu.h"
+
+// cpu and memory
+#include "cpu/romp/romp.h"
+#include "cpu/mcs51/mcs51.h"
+
+// various hardware
+#include "machine/rosetta.h"
+#include "machine/rtpc_iocc.h"
+#include "machine/am9517a.h"
+#include "machine/pic8259.h"
+#include "machine/i8255.h"
+#include "machine/mc146818.h"
+#include "machine/z80scc.h"
+#include "machine/timer.h"
+
+// isa bus
+#include "bus/isa/isa.h"
+#include "bus/isa/isa_cards.h"
+#include "bus/isa/mda.h"
+#include "bus/isa/fdc.h"
+#include "bus/isa/ide.h"
+
+// busses and connectors
+#include "bus/rs232/rs232.h"
+#include "bus/rtpc/kbd_con.h"
+#include "bus/rtpc/kbd.h"
+
+#include "sound/spkrdev.h"
+#include "machine/input_merger.h"
+
+#include "imagedev/floppy.h"
+#include "formats/pc_dsk.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_KLS (1U << 1)
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+#include "debugger.h"
+
+#include "rtpc.lh"
+
+namespace {
+
+class rtpc_state : public driver_device
+{
+public:
+ rtpc_state(machine_config const &mconfig, device_type type, char const *tag)
+ : driver_device(mconfig, type, tag)
+ , m_cpu(*this, "cpu")
+ , m_mcu(*this, "mcu")
+ , m_mmu(*this, "mmu")
+ , m_iocc(*this, "iocc")
+ , m_dma(*this, "dma%u", 0U)
+ , m_pic(*this, "pic%u", 0U)
+ , m_ppi(*this, "ppi")
+ , m_rtc(*this, "rtc")
+ , m_scc(*this, "scc")
+ , m_isa(*this, "isa")
+ , m_kbd_con(*this, "kbd_con")
+ , m_speaker(*this, "kbd_con:kbd:speaker")
+ , m_ipl(*this, "ipl")
+ {
+ }
+
+ void ibm6150(machine_config &config);
+ void ibm6151(machine_config &config);
+
+ void init_common();
+
+protected:
+ // driver_device overrides
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ template <bool SCC> void iocc_pio_map(address_map &map);
+
+ void common(machine_config &config);
+
+ DECLARE_FLOPPY_FORMATS(floppy_formats);
+
+ void mcu_port1_w(u8 data);
+ void mcu_port2_w(u8 data);
+ void mcu_port3_w(u8 data);
+ void ppi_portc_w(u8 data);
+
+ void kls_cmd_w(offs_t offset, u16 data, u16 mem_mask);
+ void crra_w(u8 data);
+ void crrb_w(u8 data);
+ void dia_w(u8 data);
+
+ void mcu_timer(timer_device &timer, void *ptr, s32 param)
+ {
+ m_mcu_p3 ^= 0x10;
+ m_mcu->set_input_line(MCS51_T0_LINE, BIT(m_mcu_p3, 4));
+ }
+
+ void speaker()
+ {
+ if (m_speaker)
+ m_speaker->level_w(!BIT(m_mcu_p2, 7) ? m_mcu_p1 >> 6 : 0);
+ }
+
+ // devices
+ required_device<romp_device> m_cpu;
+ required_device<i8051_device> m_mcu;
+ required_device<rosetta_device> m_mmu;
+ required_device<rtpc_iocc_device> m_iocc;
+
+ required_device_array<am9517a_device, 2> m_dma;
+ required_device_array<pic8259_device, 2> m_pic;
+ required_device<i8255_device> m_ppi;
+ required_device<mc146818_device> m_rtc;
+ optional_device<z80scc_device> m_scc;
+
+ required_device<isa16_device> m_isa;
+ required_device<rtpc_kbd_con_device> m_kbd_con;
+ optional_device<speaker_sound_device> m_speaker;
+
+ required_region_ptr<u32> m_ipl;
+
+ u8 m_mcu_p0;
+ u8 m_mcu_p1;
+ u8 m_mcu_p2;
+ u8 m_mcu_p3;
+
+ u8 m_ppi_pb;
+ u8 m_mcu_uart;
+
+ u8 m_ch8er; // dma channel 8 enable register
+ u8 m_crra; // component reset register a
+ u8 m_crrb; // component reset register b
+
+ u8 m_ext[2]; // external serial registers
+};
+
+static double const speaker_levels[4] = { 0.0, 1.0 / 3.0, 2.0 / 3.0, 1.0 };
+
+void rtpc_state::machine_start()
+{
+ m_mcu_p0 = 0;
+ m_mcu_p1 = 0;
+ m_mcu_p2 = 0;
+ m_mcu_p3 = 0;
+
+ m_ppi_pb = 0;
+
+ m_crra = 0xff;
+ m_crrb = 0xff;
+}
+
+void rtpc_state::machine_reset()
+{
+}
+
+void rtpc_state::init_common()
+{
+ if (m_speaker)
+ m_speaker->set_levels(4, speaker_levels);
+}
+
+template <bool SCC> void rtpc_state::iocc_pio_map(address_map &map)
+{
+ if (SCC)
+ {
+ map(0x00'8000, 0x00'8003).rw(m_scc, FUNC(z80scc_device::dc_ab_r), FUNC(z80scc_device::dc_ab_w));
+ map(0x00'8020, 0x00'8020).lrw8([this]() { return m_ext[0]; }, "ext_a_r", [this](u8 data) { m_ext[0] = data; }, "ext_a_w");
+ map(0x00'8040, 0x00'8040).lrw8([this]() { return m_ext[1]; }, "ext_b_r", [this](u8 data) { m_ext[1] = data; }, "ext_b_w");
+ map(0x00'8060, 0x00'8060).lr8([this]() { return u8(m_scc->m1_r()); }, "intack");
+ }
+
+ // delay 1µs per byte written
+ map(0x00'80e0, 0x00'80e3).lw8([this](u8 data) { m_cpu->eat_cycles(m_cpu->clock() / 1000000 + 1); }, "io_delay");
+
+ map(0x00'8400, 0x00'8403).mirror(0x7c).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write));
+ map(0x00'8400, 0x00'8401).mirror(0x78).w(FUNC(rtpc_state::kls_cmd_w));
+
+ map(0x00'8800, 0x00'883f).rw(m_rtc, FUNC(mc146818_device::read_direct), FUNC(mc146818_device::write_direct));
+ map(0x00'8840, 0x00'884f).mirror(0x10).rw(m_dma[0], FUNC(am9517a_device::read), FUNC(am9517a_device::write));
+ map(0x00'8860, 0x00'887f).umask16(0xff00).rw(m_dma[1], FUNC(am9517a_device::read), FUNC(am9517a_device::write));
+ map(0x00'8880, 0x00'8881).mirror(0x1e).rw(m_pic[0], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
+ map(0x00'88a0, 0x00'88a1).mirror(0x1e).rw(m_pic[1], FUNC(pic8259_device::read), FUNC(pic8259_device::write));
+ map(0x00'88c0, 0x00'88c0).mirror(0x1f).rw(m_iocc, FUNC(rtpc_iocc_device::dbr_r), FUNC(rtpc_iocc_device::dbr_w));
+ map(0x00'88e0, 0x00'88e0).mirror(0x1f).rw(m_iocc, FUNC(rtpc_iocc_device::dmr_r), FUNC(rtpc_iocc_device::dmr_w));
+
+ map(0x00'8c00, 0x00'8c00).mirror(0x03).lrw8([this]() { return m_ch8er; }, "ch8er_r", [this](u8 data) { m_ch8er = data; }, "ch8er_w");
+ map(0x00'8c20, 0x00'8c20).mirror(0x03).rw(m_iocc, FUNC(rtpc_iocc_device::ccr_r), FUNC(rtpc_iocc_device::ccr_w));
+ map(0x00'8c40, 0x00'8c40).mirror(0x03).lr8([this]() { return m_crra; }, "crra_r");
+ map(0x00'8c40, 0x00'8c40).mirror(0x03).w(FUNC(rtpc_state::crra_w));
+ map(0x00'8c60, 0x00'8c60).mirror(0x03).lr8([this]() { return m_crrb; }, "crrb_r");
+ map(0x00'8c60, 0x00'8c60).mirror(0x03).w(FUNC(rtpc_state::crrb_w));
+
+ // memory config reg (cc=2x8M, dd=2x2M, 88=2x4M)
+ map(0x00'8c80, 0x00'8c80).mirror(0x03).lr8([this]() { return 0xf8; }, "mcr");
+ map(0x00'8ca0, 0x00'8ca0).mirror(0x03).w(FUNC(rtpc_state::dia_w));
+
+ map(0x01'0000, 0x01'07ff).rw(m_iocc, FUNC(rtpc_iocc_device::tcw_r), FUNC(rtpc_iocc_device::tcw_w));
+ map(0x01'0800, 0x01'0801).mirror(0x7fc).rw(m_iocc, FUNC(rtpc_iocc_device::csr_r<1>), FUNC(rtpc_iocc_device::csr_w));
+ map(0x01'0802, 0x01'0803).mirror(0x7fc).rw(m_iocc, FUNC(rtpc_iocc_device::csr_r<0>), FUNC(rtpc_iocc_device::csr_w));
+}
+
+FLOPPY_FORMATS_MEMBER(rtpc_state::floppy_formats)
+ FLOPPY_PC_FORMAT
+FLOPPY_FORMATS_END
+
+void rtpc_state::mcu_port1_w(u8 data)
+{
+ // bit function
+ // 6 speaker volume 0
+ // 7 speaker volume 1
+ LOGMASKED(LOG_KLS, "mcu_port1_w volume %d\n", data >> 6);
+ m_mcu_p1 = (m_mcu_p1 & 0x3f) | (data & 0xc0);
+
+ // speaker volume wraps to ppi port b.6 and b.5
+ m_ppi_pb &= ~0x60;
+ m_ppi_pb |= (data >> 1) & 0x60;
+
+ speaker();
+}
+
+void rtpc_state::mcu_port2_w(u8 data)
+{
+ // bit dst
+ // 0 ppi port c.0 (iid0)
+ // 1 ppi port c.1 (iid1)
+ // 2 ppi port c.2 (iid2)
+ // 3 i/o channel system reset (active low)
+ // 4 ppi port c.4
+ // 5 (input)
+ // 6 ppi port c.6 (-ack)
+ // 7 speaker frequency
+
+ // interrupts
+ // 0 informational
+ // 1 received byte from keyboard
+ // 2 received byte from uart
+ // 3 returning byte requested by system
+ // 4 block transfer ready
+ // 5 unassigned
+ // 6 self-test performed
+ // 7 error condition
+
+ if ((data ^ m_mcu_p2) & 7)
+ LOGMASKED(LOG_KLS, "mcu_port2_w interrupt %d\n", data & 7);
+
+ if ((data ^ m_mcu_p2) & 8)
+ LOGMASKED(LOG_KLS, "mcu_port2_w system reset %d\n", data & 8);
+
+ m_ppi->pc4_w(BIT(data, 4));
+ m_ppi->pc6_w(BIT(data, 6));
+
+ m_mcu_p2 &= ~0xdf;
+ m_mcu_p2 |= data & 0xdf;
+
+ // speaker frequency wraps to ppi port b.7
+ m_ppi_pb &= ~0x80;
+ m_ppi_pb |= ~data & 0x80;
+
+ speaker();
+}
+
+void rtpc_state::mcu_port3_w(u8 data)
+{
+ // bit i/o function
+ // 0 i uart rx
+ // 1 o uart tx
+ // 2 i kbd clock in
+ // 3 i obf/int1
+ // 4 i 32 kHz
+ // 5 i kbd data in
+ // 6 o kbd data out
+ // 7 o kbd clock out
+
+ LOGMASKED(LOG_KLS, "mcu_port3_w 0x%02x\n", data);
+
+ m_kbd_con->data_write_from_mb(BIT(data, 6));
+ m_kbd_con->clock_write_from_mb(BIT(data, 7));
+
+ m_mcu_p3 = (m_mcu_p3 & ~0xc2) | (data & 0xc2);
+}
+
+void rtpc_state::ppi_portc_w(u8 data)
+{
+ LOGMASKED(LOG_KLS, "ppi_portc_w 0x%02x\n", data);
+
+ // bit 3 -> i/o channel (irq)
+ m_pic[0]->ir5_w(BIT(data, 3));
+
+ // bit 5 -> mcu p2.5 (ibf)
+ if (BIT(data, 5))
+ m_mcu_p2 |= 0x20;
+ else
+ m_mcu_p2 &= ~0x20;
+
+ // bit 7 -> mcu p3.3(-int1) (-obf)
+ if (BIT(data, 7))
+ m_mcu_p3 |= 0x08;
+ else
+ m_mcu_p3 &= ~0x08;
+ m_mcu->set_input_line(MCS51_INT1_LINE, !BIT(data, 7));
+}
+
+void rtpc_state::kls_cmd_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ LOGMASKED(LOG_KLS, "kls_cmd_w command 0x%02x data 0x%02x mask 0x%04x\n", u8(data), data >> 8, mem_mask);
+
+ // 00cc cccc dddd dddd
+ switch (mem_mask)
+ {
+ case 0xff00:
+ m_ppi->write(0, data);
+ break;
+ case 0x00ff:
+ m_ppi->write(1, data);
+ break;
+ case 0xffff:
+ m_mcu_p1 = (m_mcu_p1 & ~0x3f) | (data & 0x3f);
+ m_ppi->write(0, data >> 8);
+ break;
+ }
+}
+
+void rtpc_state::crra_w(u8 data)
+{
+ LOG("crra_w 0x%02x\n", data);
+
+ // bit function
+ // 0 i/o slot 1
+ // 1 i/o slot 2
+ // 2 i/o slot 3
+ // 3 i/o slot 4
+ // 4 i/o slot 5
+ // 5 i/o slot 6
+ // 6 i/o slot 7
+ // 7 i/o slot 8
+
+ m_crra = data;
+}
+
+void rtpc_state::crrb_w(u8 data)
+{
+ LOG("crrb_w 0x%02x\n", data);
+
+ // bit function
+ // 0 8530
+ // 1 rs232 interface
+ // 2 8051
+ // 3 dmac1
+ // 4 dmac2
+ // 5 arbitor
+ // 6 reserved
+ // 7 reserved
+
+ if (BIT(data, 0))
+ m_scc->reset();
+ // TODO: rs232 if
+ m_mcu->set_input_line(INPUT_LINE_RESET, !BIT(data, 2));
+ // TODO: dmac !ready
+ // TODO: arbitor
+
+ m_crrb = data;
+}
+
+void rtpc_state::dia_w(u8 data)
+{
+ bool const state = BIT(data, 0);
+
+ LOG("dia_w 0x%02x (%s)\n", data, machine().describe_context());
+
+ m_pic[0]->ir0_w(state);
+ m_pic[0]->ir1_w(state);
+ m_pic[0]->ir2_w(state);
+ m_pic[0]->ir3_w(state);
+ m_pic[0]->ir4_w(state);
+ m_pic[0]->ir5_w(state);
+ m_pic[0]->ir6_w(state);
+ m_pic[0]->ir7_w(state);
+
+ m_pic[1]->ir0_w(state);
+ m_pic[1]->ir1_w(state);
+ m_pic[1]->ir2_w(state);
+ m_pic[1]->ir3_w(state);
+ m_pic[1]->ir4_w(state);
+ m_pic[1]->ir5_w(state);
+ m_pic[1]->ir6_w(state);
+ m_pic[1]->ir7_w(state);
+}
+
+void rtpc_state::common(machine_config &config)
+{
+ ROMP(config, m_cpu, 23'529'400 / 4);
+ m_cpu->set_mmu(m_mmu);
+ m_cpu->set_iou(m_iocc);
+
+ input_merger_device &reqi2(INPUT_MERGER_ANY_LOW(config, "reqi2"));
+ reqi2.output_handler().set_inputline(m_cpu, INPUT_LINE_IRQ2).invert();
+
+ // TODO: hole for 1M/4M memory boards
+ // 2/2 rams 4M
+ // 2/1 rams 4M
+ // 1/1 rams 4M hole 1M
+ // 4/0 rams 4M
+ // 4/4 rams 16M hole 4M
+ ROSETTA(config, m_mmu, m_cpu->clock(), rosetta_device::RAM_4M);
+ m_mmu->set_mem(m_cpu, AS_PROGRAM);
+ m_mmu->set_rom("ipl");
+ m_mmu->out_pchk().set(reqi2, FUNC(input_merger_device::in_w<0>));
+ m_mmu->out_mchk().set_inputline(m_cpu, INPUT_LINE_NMI);
+
+ // keyboard, locator, speaker adapter
+ // P8051AH 2075
+ // 61X6310 8811
+ // (c)IBM 1986
+ // (c)INTEL '80
+ I8051(config, m_mcu, 9.216_MHz_XTAL);
+ m_mcu->port_in_cb<0>().set([this]() { return m_ppi->pa_r(); });
+ m_mcu->port_out_cb<0>().set([this](u8 data) { m_mcu_p0 = data; });
+ m_mcu->port_in_cb<1>().set([this]() { return m_mcu_p1 & 0x1f; });
+ m_mcu->port_out_cb<1>().set(FUNC(rtpc_state::mcu_port1_w));
+ m_mcu->port_in_cb<2>().set([this]() { return m_mcu_p2; });
+ m_mcu->port_out_cb<2>().set(FUNC(rtpc_state::mcu_port2_w));
+ m_mcu->port_out_cb<3>().set(FUNC(rtpc_state::mcu_port3_w));
+ m_mcu->port_in_cb<3>().set([this]() { return m_mcu_p3 & 0x3d; });
+ m_mcu->serial_tx_cb().set(
+ [this](u8 data)
+ {
+ if (BIT(m_mcu_p1, 5))
+ {
+ m_mcu_uart = data;
+ m_mcu->set_input_line(MCS51_RX_LINE, 1);
+ }
+ else
+ logerror("uart tx 0x%02x\n", data);
+ });
+ m_mcu->serial_rx_cb().set([this]() { return m_mcu_uart; });
+ //m_mcu->out_?().set_inputline(m_cpu, INPUT_LINE_IRQ0);
+
+ TIMER(config, "mcu_timer").configure_periodic(FUNC(rtpc_state::mcu_timer), attotime::from_hz(32768));
+
+ RTPC_IOCC(config, m_iocc, 0);
+ m_iocc->out_int().set(reqi2, FUNC(input_merger_device::in_w<1>));
+ m_iocc->out_rst().set_inputline(m_dma[0], INPUT_LINE_RESET);
+ m_iocc->out_rst().append_inputline(m_dma[1], INPUT_LINE_RESET);
+
+ // ISA bus
+ ISA16(config, m_isa, 14'318'180 / 3);
+ m_isa->set_memspace(m_iocc, AS_PROGRAM);
+ m_isa->set_iospace(m_iocc, AS_IO);
+ //m_isa->iochck_callback().set(FUNC(at_mb_device::iochck_w));
+
+ // NEC
+ // D8237AC-5
+ // 8903HV101
+ AM9517A(config, m_dma[0], m_isa->clock());
+ // chan usage
+ // 0 serial port A
+ // 1 serial port B
+ // 2 diskette drive, serial port A
+ // 3 pc network, serial port B
+ m_isa->drq0_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<0>));
+ m_isa->drq1_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<1>));
+ m_isa->drq2_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<2>));
+ m_isa->drq3_callback().set(m_dma[0], FUNC(am9517a_device::dreq_w<3>));
+ m_dma[0]->out_hreq_callback().set(m_dma[0], FUNC(am9517a_device::hack_w));
+ m_dma[0]->out_dack_callback<0>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<2>));
+ m_dma[0]->out_dack_callback<1>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<1>));
+ m_dma[0]->out_dack_callback<2>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<0>));
+ m_dma[0]->out_dack_callback<3>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<3>));
+ m_dma[0]->in_memr_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_b_r));
+ m_dma[0]->out_memw_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_b_w));
+
+ // NEC
+ // D8237AC-5
+ // 8903HV101
+ AM9517A(config, m_dma[1], m_isa->clock());
+ // chan usage
+ // 5 reserved
+ // 6 reserved
+ // 7 reserved
+ // 8 286 coprocessor
+ m_isa->drq5_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<1>));
+ m_isa->drq6_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<2>));
+ m_isa->drq7_callback().set(m_dma[1], FUNC(am9517a_device::dreq_w<3>));
+ m_dma[1]->out_hreq_callback().set(m_dma[1], FUNC(am9517a_device::hack_w));
+ m_dma[1]->out_dack_callback<0>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<4>));
+ m_dma[1]->out_dack_callback<1>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<5>));
+ m_dma[1]->out_dack_callback<2>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<6>));
+ m_dma[1]->out_dack_callback<3>().set(m_iocc, FUNC(rtpc_iocc_device::dack_w<7>));
+ m_dma[1]->in_memr_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_w_r));
+ m_dma[1]->out_memw_callback().set(m_iocc, FUNC(rtpc_iocc_device::dma_w_w));
+
+ // NEC
+ // D8259AC
+ PIC8259(config, m_pic[0]);
+ m_pic[0]->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ3).invert();
+ // irq source
+ // 0 iocc irq 0: dma tc
+ // 1 iocc irq 10: (multiport async)
+ // 2 iocc irq 9: pc network, (multiport async), enhanced color graphics adapter, 3278/79 emulation adapter
+ // 3 iocc irq 3: serial port 2, pc network
+ // 4 iocc irq 4: serial port 1
+ // 5 iocc irq 1: kbd
+ // 6 iocc irq 2: 8530
+ // 7 iocc irq 7: parallel port 1, monochrome/printer
+ m_isa->irq10_callback().set(m_pic[0], FUNC(pic8259_device::ir1_w));
+ m_isa->irq2_callback().set(m_pic[0], FUNC(pic8259_device::ir2_w));
+ m_isa->irq3_callback().set(m_pic[0], FUNC(pic8259_device::ir3_w));
+ m_isa->irq4_callback().set(m_pic[0], FUNC(pic8259_device::ir4_w));
+ m_isa->irq7_callback().set(m_pic[0], FUNC(pic8259_device::ir7_w));
+
+ // NEC
+ // D8259AC
+ PIC8259(config, m_pic[1]);
+ m_pic[1]->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ4).invert();
+ // irq source
+ // 0 iocc irq 8: reserved
+ // 1 iocc irq 11: (advanced monochrome graphics display) (multiport async)
+ // 2 iocc irq 14: fixed disk
+ // 3 iocc irq 12: (ibm rt pc streaming tape drive adapter)
+ // 4 iocc irq 6: diskette drive
+ // 5 iocc irq 5: parallel port 2
+ // 6 iocc irq 15: (286 coprocessor)
+ // 7 iocc irq 13: serial port ex ct1 irpt
+ m_isa->irq11_callback().set(m_pic[1], FUNC(pic8259_device::ir1_w));
+ m_isa->irq14_callback().set(m_pic[1], FUNC(pic8259_device::ir2_w));
+ m_isa->irq12_callback().set(m_pic[1], FUNC(pic8259_device::ir3_w));
+ m_isa->irq6_callback().set(m_pic[1], FUNC(pic8259_device::ir4_w));
+ m_isa->irq5_callback().set(m_pic[1], FUNC(pic8259_device::ir5_w));
+ m_isa->irq15_callback().set(m_pic[1], FUNC(pic8259_device::ir6_w));
+
+ // P8255A-5
+ // L6430434
+ // (C)INTEL '81
+ I8255A(config, m_ppi);
+ // port A: read/write 8051
+ // port B: input
+ // port C lower: input
+ // port C upper: 8051 handshake
+ // port C & 0x20 -> irq
+ m_ppi->in_pa_callback().set([this]() { return m_mcu_p0; });
+
+ // TODO: bits 4-1 "non-adapter system board signals"
+ // TODO: bit 0 "uart rxd signal"
+ m_ppi->in_pb_callback().set([this]() { return m_ppi_pb; });
+
+ m_ppi->out_pc_callback().set(FUNC(rtpc_state::ppi_portc_w));
+ m_ppi->in_pc_callback().set([this]() { return m_mcu_p2 & 0x57; });
+
+ RTPC_KBD_CON(config, m_kbd_con);
+ m_kbd_con->option_add("kbd", RTPC_KBD);
+ m_kbd_con->set_default_option("kbd");
+ m_kbd_con->out_data_cb().set([this](int state) { if (state) m_mcu_p3 |= 0x20; else m_mcu_p3 &= ~0x20; });
+ m_kbd_con->out_clock_cb().set(
+ [this](int state)
+ {
+ if (state)
+ m_mcu_p3 |= 0x04;
+ else
+ m_mcu_p3 &= ~0x04;
+ m_mcu->set_input_line(MCS51_INT0_LINE, !state);
+ });
+
+ // MC146818AP
+ // IL 0A46D8729
+ MC146818(config, m_rtc, 32.768_kHz_XTAL);
+ m_rtc->sqw().set(m_cpu, FUNC(romp_device::clk_w));
+ m_rtc->sqw().append(
+ [this](int state)
+ {
+ if (state)
+ m_ppi_pb |= 0x08;
+ else
+ m_ppi_pb &= ~0x08;
+ });
+ m_rtc->irq().set_inputline(m_cpu, INPUT_LINE_IRQ1).invert();
+ m_rtc->irq().append(
+ [this](int state)
+ {
+ if (!state)
+ m_ppi_pb |= 0x10;
+ else
+ m_ppi_pb &= ~0x10;
+ });
+
+ config.set_default_layout(layout_rtpc);
+}
+
+void rtpc_isa8_cards(device_slot_interface &device)
+{
+ device.option_add("mda", ISA8_MDA);
+}
+
+void rtpc_isa16_cards(device_slot_interface &device)
+{
+ // FIXME: need 16-bit combined hdc/fdc card
+ device.option_add("hdc", ISA16_IDE);
+ device.option_add("fdc", ISA8_FDC_AT);
+}
+
+void rtpc_state::ibm6150(machine_config &config)
+{
+ common(config);
+ m_iocc->set_addrmap(2, &rtpc_state::iocc_pio_map<true>);
+
+ SCC8530N(config, m_scc, 3'580'000);
+ m_scc->configure_channels(3'072'000, 3'072'000, 3'072'000, 3'072'000);
+ m_scc->out_int_callback().set(m_pic[0], FUNC(pic8259_device::ir6_w));
+
+ // port: TXD, DTR, RTS, RI, RXD, DSR, CTS, DCD
+ // external registers: DTR, DSR, RI
+ // scc: TXD, RTS, RXD, CTS, DCD
+ // scc: DTR/REQ, SYNC not connected
+
+ rs232_port_device &port0(RS232_PORT(config, "serial0", default_rs232_devices, nullptr));
+ port0.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w));
+ port0.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w));
+ port0.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w));
+ m_scc->out_rtsa_callback().set(port0, FUNC(rs232_port_device::write_rts));
+ m_scc->out_txda_callback().set(port0, FUNC(rs232_port_device::write_txd));
+
+ rs232_port_device &port1(RS232_PORT(config, "serial1", default_rs232_devices, nullptr));
+ // TXD, DTR, RTS, RI, RXD, DSR, CTS, DCD
+ port1.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w));
+ port1.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w));
+ port1.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w));
+ m_scc->out_rtsb_callback().set(port1, FUNC(rs232_port_device::write_rts));
+ m_scc->out_txdb_callback().set(port1, FUNC(rs232_port_device::write_txd));
+
+ // ISA slots
+ ISA16_SLOT(config, "isa1", 0, m_isa, rtpc_isa16_cards, "fdc", false); // slot 1: disk/diskette adapter
+ ISA16_SLOT(config, "isa2", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 2: option
+ ISA8_SLOT(config, "isa3", 0, m_isa, rtpc_isa8_cards, "mda", false); // slot 3: option
+ ISA16_SLOT(config, "isa4", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 4: option
+ ISA16_SLOT(config, "isa5", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 5: option
+ ISA8_SLOT(config, "isa6", 0, m_isa, rtpc_isa8_cards, nullptr, false); // slot 6: option
+ ISA16_SLOT(config, "isa7", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 7: option
+ ISA16_SLOT(config, "isa8", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 8: coprocessor/option
+}
+
+void rtpc_state::ibm6151(machine_config &config)
+{
+ common(config);
+ m_iocc->set_addrmap(2, &rtpc_state::iocc_pio_map<false>);
+
+ // ISA slots
+ ISA8_SLOT(config, "isa1", 0, m_isa, rtpc_isa8_cards, "mda", false); // slot 1: option
+ ISA16_SLOT(config, "isa2", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 2: option
+ ISA16_SLOT(config, "isa3", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 2: option
+ ISA16_SLOT(config, "isa4", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 4: option
+ ISA16_SLOT(config, "isa5", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 5: coprocessor/option
+ ISA16_SLOT(config, "isa6", 0, m_isa, rtpc_isa16_cards, nullptr, false); // slot 6: disk/diskette adapter
+}
+
+ROM_START(ibm6150)
+ ROM_REGION32_BE(0x10000, "ipl", 0)
+ ROM_SYSTEM_BIOS(0, "ipl", "IPL")
+ ROMX_LOAD("79x3456.bin", 0x00000, 0x4000, CRC(0a45a9ba) SHA1(02ca637c6a871c180dbfebf2ec68d8ec5a998c76), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3458.bin", 0x00001, 0x4000, CRC(7bd08ab6) SHA1(aabcfbb8fa1a5f8a08fb5cfd90ca6fe05258fde9), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3460.bin", 0x00002, 0x4000, CRC(897586e0) SHA1(528772635903f27235ebba2622b03386b84e4e17), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3462.bin", 0x00003, 0x4000, CRC(12aca906) SHA1(58f95b95768ef131d8d9d552506a9fe9c9c6077d), ROM_BIOS(0) | ROM_SKIP(3))
+
+ ROM_REGION(0x1000, "mcu", 0)
+ ROM_LOAD("61x6310_8051.bin", 0x0000, 0x1000, CRC(296c16c1) SHA1(83858109c39d5be37e49f24d1db4e2b15f38843e))
+ROM_END
+
+ROM_START(ibm6151)
+ ROM_REGION32_BE(0x10000, "ipl", 0)
+ ROM_SYSTEM_BIOS(0, "ipl", "IPL")
+ ROMX_LOAD("79x3456.bin", 0x00000, 0x4000, CRC(0a45a9ba) SHA1(02ca637c6a871c180dbfebf2ec68d8ec5a998c76), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3458.bin", 0x00001, 0x4000, CRC(7bd08ab6) SHA1(aabcfbb8fa1a5f8a08fb5cfd90ca6fe05258fde9), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3460.bin", 0x00002, 0x4000, CRC(897586e0) SHA1(528772635903f27235ebba2622b03386b84e4e17), ROM_BIOS(0) | ROM_SKIP(3))
+ ROMX_LOAD("79x3462.bin", 0x00003, 0x4000, CRC(12aca906) SHA1(58f95b95768ef131d8d9d552506a9fe9c9c6077d), ROM_BIOS(0) | ROM_SKIP(3))
+
+ // Version: 073
+ // Date: 85 289 (16 Oct 1985)
+ // Checksum: 0xc8 0xe8
+ ROM_REGION(0x1000, "mcu", 0)
+ ROM_LOAD("61x6310_8051.bin", 0x0000, 0x1000, CRC(296c16c1) SHA1(83858109c39d5be37e49f24d1db4e2b15f38843e))
+ROM_END
+
+#define rom_rtpc010 rom_ibm6151
+#define rom_rtpc015 rom_ibm6151
+#define rom_rtpc020 rom_ibm6150
+#define rom_rtpc025 rom_ibm6150
+#define rom_rtpca25 rom_ibm6150
+
+} // anonymous namespace
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
+COMP(1986, rtpc010, 0, 0, ibm6151, 0, rtpc_state, init_common, "International Business Machines", "IBM RT PC Model 010", MACHINE_NOT_WORKING)
+COMP(1986, rtpc015, 0, 0, ibm6151, 0, rtpc_state, init_common, "International Business Machines", "IBM RT PC Model 015", MACHINE_NOT_WORKING)
+COMP(1986, rtpc020, 0, 0, ibm6150, 0, rtpc_state, init_common, "International Business Machines", "IBM RT PC Model 020", MACHINE_NOT_WORKING)
+COMP(1986, rtpc025, 0, 0, ibm6150, 0, rtpc_state, init_common, "International Business Machines", "IBM RT PC Model 025", MACHINE_NOT_WORKING)
+COMP(1986, rtpca25, 0, 0, ibm6150, 0, rtpc_state, init_common, "International Business Machines", "IBM RT PC Model A25", MACHINE_NOT_WORKING)
diff --git a/src/mame/layout/rtpc.lay b/src/mame/layout/rtpc.lay
new file mode 100644
index 00000000000..e99ca37c05c
--- /dev/null
+++ b/src/mame/layout/rtpc.lay
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!--
+license:CC0
+copyright-holders:Patrick Mackinlay
+IBM RT PC diagnostic and keyboard LEDs
+-->
+<mamelayout version="2">
+ <element name="led" defstate="0">
+ <led7seg>
+ <color red="1.0" green="0.0" blue="0.0" />
+ </led7seg>
+ </element>
+
+ <element name="kbd_led" defstate="0">
+ <disk state="1">
+ <color red="0.0" green="0.75" blue="0.0" />
+ </disk>
+ </element>
+ <element name="num_lock">
+ <text string="Num Lock">
+ <color red="1.0" green="1.0" blue="1.0" />
+ </text>
+ </element>
+ <element name="caps_lock">
+ <text string="Caps Lock">
+ <color red="1.0" green="1.0" blue="1.0" />
+ </text>
+ </element>
+ <element name="scroll_lock">
+ <text string="Scroll Lock">
+ <color red="1.0" green="1.0" blue="1.0" />
+ </text>
+ </element>
+
+ <view name="Graphics">
+ <screen index="0">
+ <!-- assume MDA for now -->
+ <bounds x="0" y="0" width="720" height="350" />
+ </screen>
+
+ <element name="num_text" ref="num_lock">
+ <bounds x="10" y="370" width="40" height="15" />
+ </element>
+ <element name="kbd_led0" ref="kbd_led">
+ <bounds x="25" y="385" width="10" height="10" />
+ </element>
+
+ <element name="caps_text" ref="caps_lock">
+ <bounds x="60" y="370" width="40" height="15" />
+ </element>
+ <element name="kbd_led1" ref="kbd_led">
+ <bounds x="75" y="385" width="10" height="10" />
+ </element>
+
+ <element name="scroll_text" ref="scroll_lock">
+ <bounds x="110" y="370" width="40" height="15" />
+ </element>
+ <element name="kbd_led2" ref="kbd_led">
+ <bounds x="125" y="385" width="10" height="10" />
+ </element>
+
+ <element name="led0" ref="led">
+ <bounds x="700" y="375" width="15" height="20" />
+ </element>
+ <element name="led1" ref="led">
+ <bounds x="680" y="375" width="15" height="20" />
+ </element>
+ </view>
+</mamelayout>
diff --git a/src/mame/machine/rosetta.cpp b/src/mame/machine/rosetta.cpp
new file mode 100644
index 00000000000..12b6d5ec036
--- /dev/null
+++ b/src/mame/machine/rosetta.cpp
@@ -0,0 +1,1202 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM Rosetta MMU.
+ *
+ * Sources:
+ * - http://bitsavers.org/pdf/ibm/pc/rt/75X0232_RT_PC_Technical_Reference_Volume_1_Jun87.pdf
+ *
+ * TODO:
+ * - tighten error detection/reporting
+ * - external device and multiple errors
+ * - ras diagnostic modes
+ * - advanced/enhanced variants
+ * - ram holes
+ */
+
+#include "emu.h"
+#include "rosetta.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_TLB (1U << 1)
+#define LOG_RELOAD (1U << 2)
+#define LOG_ECC (1U << 3)
+#define LOG_INVALID (1U << 4)
+
+//#define VERBOSE (LOG_GENERAL|LOG_TLB|LOG_RELOAD|LOG_ECC|LOG_INVALID)
+#include "logmacro.h"
+
+enum roms_mask : u32
+{
+ ROMS_SIZE = 0x0000'000f, // rom size
+ ROMS_START = 0x0000'0ff0, // rom address
+ ROMS_P = 0x0000'1000, // parity enable
+};
+
+enum segment_mask : u32
+{
+ SEGMENT_K = 0x0000'0001, // key
+ SEGMENT_S = 0x0000'0002, // special
+ SEGMENT_ID = 0x0000'3ffc, // identifier
+ SEGMENT_I = 0x0000'4000, // i/o access protect
+ SEGMENT_R = 0x0000'8000, // system processor access protect
+ SEGMENT_P = 0x0001'0000, // present
+};
+
+enum rams_mask : u32
+{
+ RAMS_SIZE = 0x0000'000f, // ram size
+ RAMS_START = 0x0000'0ff0, // ram address
+};
+
+enum tcr_mask : u32
+{
+ TCR_HIB = 0x0000'00ff, // hat/ipt base address
+ TCR_S = 0x0000'0100, // page size (1=4k pages)
+ TCR_P = 0x0000'0200, // r/c array parity enable (advanced/enhanced only?)
+ TCR_R = 0x0000'0400, // enable interrupt on successful tlb reload
+ TCR_C = 0x0000'0800, // enable interrupt on correctable ecc error
+ TCR_I = 0x0000'1000, // terminate long ipt search
+ TCR_D = 0x0000'2000, // enable ras diagnostic mode
+ TCR_E = 0x0000'4000, // interrupt on successful parity error retry
+ TCR_V = 0x0000'8000, // segment register zero virtual equal to real
+};
+
+enum mer_mask : u32
+{
+ MER_D = 0x0000'0001, // data
+ MER_P = 0x0000'0002, // protection
+ MER_S = 0x0000'0004, // tlb specification
+ MER_F = 0x0000'0008, // page fault
+ MER_M = 0x0000'0010, // multiple exception
+ MER_E = 0x0000'0020, // external device exception
+ MER_I = 0x0000'0040, // ipt specification error
+ MER_W = 0x0000'0080, // write to rom
+ MER_R = 0x0000'0100, // r/c array parity error (advanced/enhanced only?)
+ MER_T = 0x0000'0200, // successful tlb reload
+ MER_C = 0x0000'0400, // correctable ecc error
+ MER_U = 0x0000'0800, // uncorrectable memory error
+ MER_L = 0x0000'1000, // access type (1=load)
+ MER_O = 0x0000'2000, // invalid i/o address
+ MER_B = 0x0000'4000, // invalid memory address
+ MER_N = 0x0000'8000, // processor channel nakd
+ MER_A = 0x0001'0000, // processor channel ackd
+ MER_V = 0x0002'0000, // segment protection violation
+};
+
+enum trar_mask : u32
+{
+ TRAR_A = 0x00ff'ffff, // real memory address
+ TRAR_I = 0x8000'0000, // invalid
+};
+
+enum rmdr_mask : u32
+{
+ RMDR_CHECK = 0x0000'ff00, // array check bits
+ RMDR_ALT = 0x0000'00ff, // alternate check bits
+};
+
+enum tlb_mask : u32
+{
+ // field 0
+ TLB_SEG = 0x1ffe'0000, // segment identifier
+ TLB_AT2K = 0x1fff'fff0, // address tag (2k page)
+ TLB_AT4K = 0x1fff'ffe0, // address tag (4k page)
+
+ // field 1
+ TLB_KEY = 0x0000'0003, // key bits
+ TLB_V = 0x0000'0004, // valid bit
+ TLB_RPN2K = 0x0000'fff8, // real page number (2k page)
+ TLB_RPN4K = 0x0000'fff0, // real page number (4k page)
+
+ // field 2
+ TLB_LB = 0x0000'ffff, // lock bits
+ TLB_TID = 0x00ff'0000, // transaction identifier
+ TLB_W = 0x0100'0000, // write bit
+
+};
+
+enum hat_mask : u32
+{
+ HAT_AT2K = 0x1fff'ffff, // address tag (2k page)
+ HAT_AT4K = 0x1fff'fffe, // address tag (4k page)
+ HAT_KEY = 0xc000'0000, // key
+
+ HAT_IPTP = 0x0000'1fff, // ipt pointer
+ HAT_L = 0x0000'8000, // last
+ HAT_HATP = 0x1fff'0000, // hat pointer
+ HAT_E = 0x8000'0000, // empty
+
+ HAT_LB = 0x0000'ffff, // lock bits
+ HAT_TID = 0x00ff'0000, // transaction identifier
+ HAT_W = 0x0100'0000, // write protect
+};
+
+enum rca_mask : u8
+{
+ RCA_C = 0x01, // change
+ RCA_R = 0x02, // reference
+};
+
+enum reg : unsigned
+{
+ IOBA = 0, // i/o base address
+ MER = 1, // memory exception
+ MEAR = 2, // memory exception address
+ TRAR = 3, // translated real address
+ TID = 4, // transaction identifier
+ TCR = 5, // translation control
+ RAMS = 6, // ram specification
+ ROMS = 7, // rom specification
+ RMDR = 8, // ras mode diagnostic
+};
+
+static char const *const control_names[] = { "IOBA", "MER", "MEAR", "TRAR", "TID", "TCR", "RAMS", "ROMS", "RMDR" };
+
+static u8 const ecc_bits[] =
+{
+ 0xa8, 0x68, 0xa4, 0x64, 0xa2, 0x62, 0xa1, 0x61,
+ 0x98, 0x58, 0x94, 0x54, 0x92, 0x52, 0x91, 0x51,
+ 0x8a, 0x89, 0x4a, 0x49, 0x2a, 0x29, 0x1a, 0x19,
+ 0x86, 0x85, 0x46, 0x45, 0x26, 0x25, 0x16, 0x15,
+};
+
+// 7-segment diagnostic led
+static u8 const led_pattern[16] =
+{
+ 0x3f, 0x06, 0x5b, 0x4f,
+ 0x66, 0x6d, 0x7d, 0x07,
+ 0x7f, 0x6f, 0x77, 0x7c,
+ 0x39, 0x5e, 0x79, 0x00,
+};
+
+DEFINE_DEVICE_TYPE(ROSETTA, rosetta_device, "rosetta", "IBM Rosetta")
+
+ALLOW_SAVE_TYPE(rosetta_device::mear_state)
+
+rosetta_device::rosetta_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, ram_size ram)
+ : device_t(mconfig, ROSETTA, tag, owner, clock)
+ , rsc_cpu_interface(mconfig, *this)
+ , m_mem_space(*this, finder_base::DUMMY_TAG, -1, 32)
+ , m_rom(*this, finder_base::DUMMY_TAG)
+ , m_leds(*this, "led%u", 0U)
+ , m_out_pchk(*this)
+ , m_out_mchk(*this)
+ , m_segment{}
+ , m_control{}
+ , m_mear_lock(UNLOCKED)
+ , m_rmdr_lock(false)
+ , m_led_lock(true)
+ , m_pchk_state(false)
+ , m_tlb{ {}, {} }
+ , m_tlb_lru(0)
+ , m_ram_size(ram)
+{
+}
+
+void rosetta_device::device_validity_check(validity_checker &valid) const
+{
+ if (!m_ram_size)
+ osd_printf_error("invalid ram size\n");
+}
+
+void rosetta_device::device_start()
+{
+ m_out_pchk.resolve_safe();
+ m_out_mchk.resolve_safe();
+ m_leds.resolve();
+
+ save_item(NAME(m_segment));
+ save_item(NAME(m_control));
+ save_item(NAME(m_mear_lock));
+ save_item(NAME(m_rmdr_lock));
+ save_item(NAME(m_led_lock));
+ save_item(NAME(m_pchk_state));
+
+ save_item(STRUCT_MEMBER(m_tlb, field0));
+ save_item(STRUCT_MEMBER(m_tlb, field1));
+ save_item(STRUCT_MEMBER(m_tlb, field2));
+ save_item(NAME(m_tlb_lru));
+
+ save_pointer(NAME(m_ram), m_ram_size);
+ save_pointer(NAME(m_ecc), m_ram_size);
+ save_pointer(NAME(m_rca), 2048);
+
+ config_tlb();
+
+ m_ram = std::make_unique<u32[]>(m_ram_size);
+ m_ecc = std::make_unique<u8[]>(m_ram_size);
+ m_rca = std::make_unique<u8[]>(2048);
+
+ offs_t const mask = m_rom.bytes() - 1;
+ m_mem_space->install_rom(0, mask, 0xffffff & ~mask, m_rom);
+ m_mem_space->cache(m_mem);
+}
+
+void rosetta_device::device_reset()
+{
+ m_mear_lock = UNLOCKED;
+ m_rmdr_lock = false;
+ m_led_lock = true;
+
+ set_pchk(false);
+ set_mchk(false);
+}
+
+void rosetta_device::device_post_load()
+{
+ config_tlb();
+}
+
+bool rosetta_device::ior(u32 address, u32 &data)
+{
+ if ((address >> 16) == u8(m_control[IOBA]))
+ {
+ u16 const offset = u16(address);
+
+ switch (offset)
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ data = segment_r(offset & 0xf);
+ return true;
+
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ data = control_r(offset & 0xf);
+ return true;
+
+ default:
+ if (offset - 0x20U < 0x60U)
+ {
+ data = tlb_r(offset - 0x20U);
+ return true;
+ }
+ else if (offset - 0x1000U < 0x2000U)
+ {
+ data = rca_r(offset - 0x1000U);
+ return true;
+ }
+ break;
+ }
+ }
+ else if (address == 0x80'8000U)
+ {
+ data = m_control[IOBA];
+ return true;
+ }
+
+ LOGMASKED(LOG_INVALID, "ior invalid address 0x%06x (%s)\n", address, machine().describe_context());
+ m_control[MER] |= MER_O;
+ set_mear(address, MEMORY);
+
+ return bool(m_control[TCR] & TCR_D);
+}
+
+bool rosetta_device::iow(u32 address, u32 data)
+{
+ if ((address >> 16) == u8(m_control[IOBA]))
+ {
+ u16 const offset = u16(address);
+
+ switch (offset)
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ segment_w(offset & 0xf, data);
+ return true;
+
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ control_w(offset & 0xf, data);
+ return true;
+
+ case 0x80: tlb_inv_all(data); return true;
+ case 0x81: tlb_inv_segment(data); return true;
+ case 0x82: tlb_inv_address(data); return true;
+ case 0x83: compute_address(data); return true;
+
+ default:
+ if (offset - 0x20U < 0x60U)
+ {
+ tlb_w(offset - 0x20U, data);
+ return true;
+ }
+ else if (offset - 0x1000U < 0x2000U)
+ {
+ rca_w(offset - 0x1000U, data);
+ return true;
+ }
+ break;
+ }
+ }
+ else if (address == 0x80'8000U)
+ {
+ m_control[IOBA] = data;
+ return true;
+ }
+
+ LOGMASKED(LOG_INVALID, "iow invalid address 0x%06x data 0x%08x (%s)\n", address, data, machine().describe_context());
+ m_control[MER] |= MER_O;
+ set_mear(address, MEMORY);
+ set_pchk(true);
+
+ return true;
+}
+
+bool rosetta_device::translate(u32 &address, bool system_processor, bool store)
+{
+ unsigned const segment = address >> 28;
+
+ // segment present
+ if (!(m_segment[segment] & SEGMENT_P))
+ fatalerror("rosetta_device::translate() segment %d absent (%s)\n", segment, machine().describe_context());
+
+ // segment access protection
+ if (system_processor && (m_segment[segment] & SEGMENT_R))
+ {
+ m_control[MER] |= MER_V;
+
+ return false;
+ }
+ else if (!system_processor && (m_segment[segment] & SEGMENT_I))
+ return false;
+
+ // segment zero virtual equal to real
+ if ((m_control[TCR] & TCR_V) && !segment)
+ {
+ address &= 0x00ff'ffffU;
+
+ return true;
+ }
+
+ u64 const virtual_address = (u64(m_segment[segment] & SEGMENT_ID) << 26) | (address & 0x0fff'ffffU);
+
+ tlb_entry const te = tlb_search(virtual_address, m_segment[segment] & SEGMENT_S);
+ if (!(te.field1 & TLB_V))
+ return false;
+
+ u32 const real_page = (te.field1 << 8) & m_page_mask;
+
+ // special segment lockbit processing
+ if (m_segment[segment] & SEGMENT_S)
+ {
+ // check transaction identifier
+ if ((m_control[TID] & 0xff) != ((te.field2 & TLB_TID) >> 16))
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+
+ // check write bit, lock bit and operation
+ bool const lockbit = BIT(te.field2, 15 - ((address >> (m_page_shift - 4)) & 15));
+ if (te.field2 & TLB_W)
+ {
+ if (!lockbit && store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+ }
+ else
+ {
+ if (!lockbit || store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_D;
+
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // non-special segment memory protection processing
+ switch (te.field1 & TLB_KEY)
+ {
+ case 0:
+ // key 0 fetch-protected
+ if (m_segment[segment] & SEGMENT_K)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ case 1:
+ // key 0 read/write
+ if ((m_segment[segment] & SEGMENT_K) && store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ case 2:
+ // public read/write
+ break;
+ case 3:
+ // public read-only
+ if (store)
+ {
+ if (system_processor)
+ m_control[MER] |= MER_P;
+
+ return false;
+ }
+ break;
+ }
+ }
+
+ address = real_page | (address & ~m_page_mask);
+
+ return true;
+}
+
+rosetta_device::tlb_entry rosetta_device::tlb_search(u64 const virtual_address, bool const special)
+{
+ unsigned const tlb_index = (virtual_address >> m_page_shift) & 15;
+ tlb_entry te = { 0U, 0U, 0U };
+
+ // check first set
+ if ((m_tlb[tlb_index][0].field1 & TLB_V) && !((m_tlb[tlb_index][0].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ te = m_tlb[tlb_index][0];
+
+ // tlb set 1 is least-recently used
+ m_tlb_lru |= (1U << tlb_index);
+ }
+
+ // check second set
+ if ((m_tlb[tlb_index][1].field1 & TLB_V) && !((m_tlb[tlb_index][1].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ if (!(te.field1 & TLB_V))
+ {
+ te = m_tlb[tlb_index][1];
+
+ // tlb set 0 is least-recently used
+ m_tlb_lru &= ~(1U << tlb_index);
+ }
+ else
+ {
+ // double tlb set hit
+ m_control[MER] |= MER_S;
+
+ fatalerror("rosetta_device::tlb_search() double tlb hit 0x%010x (%s)\n",
+ virtual_address, machine().describe_context());
+
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+
+ return tlb_entry{ 0U, 0U, 0U };
+ }
+ else
+ // if store, address is logical OR of both real page numbers
+ // TODO: what about load?
+ te.field1 |= m_tlb[tlb_index][1].field1;
+ }
+ }
+
+ // handle tlb miss
+ if (!(te.field1 & TLB_V))
+ {
+ unsigned const lru = BIT(m_tlb_lru, tlb_index);
+
+ u32 const mer = tlb_reload(te, virtual_address, special);
+ if (mer & (MER_T | MER_I | MER_F))
+ {
+ // FIXME: set I&F only for system processor
+ m_control[MER] |= mer;
+
+ return te;
+ }
+ else
+ m_tlb[tlb_index][lru] = te;
+ }
+
+ return te;
+}
+
+u32 rosetta_device::tlb_reload(rosetta_device::tlb_entry &tlb_entry, u64 const virtual_address, bool special)
+{
+ // compute hat index
+ u16 hat_offset = (((virtual_address >> 26) & SEGMENT_ID) ^ (virtual_address >> (m_page_shift - 2))) & m_hat_mask;
+
+ // fetch hat entry
+ u32 hat_entry = m_ram[m_hat_base + hat_offset + 1];
+
+ if (!machine().side_effects_disabled())
+ LOGMASKED(LOG_RELOAD, "reload 0x%010x hat base 0x%x index 0x%x entry 0x%08x\n",
+ virtual_address, m_hat_base, hat_offset, hat_entry);
+
+ // set reference bit
+ m_rca[(m_hat_base + hat_offset) >> 11] |= (RCA_R << (((m_hat_base + hat_offset) >> 8) & 6));
+
+ if (hat_entry & HAT_E)
+ return MER_F;
+
+ // compute ipt entry pointer
+ hat_offset = ((hat_entry & HAT_HATP) >> 14);
+ u32 const address = (virtual_address >> 11) & m_atag_mask;
+
+ // ipt search
+ for (unsigned count = 0; count < 1024; count++)
+ {
+ // fetch ipt entry
+ u32 const ipt_entry = m_ram[m_hat_base + hat_offset + 0];
+
+ // set reference bit
+ m_rca[(m_hat_base + hat_offset) >> 11] |= (RCA_R << (((m_hat_base + hat_offset) >> 8) & 6));
+
+ if ((ipt_entry & m_atag_mask) == address)
+ {
+ // reload tlb
+ tlb_entry.field0 = ipt_entry & m_atag_mask;
+ tlb_entry.field1 = (hat_offset << 1) | TLB_V | (ipt_entry >> 30);
+
+ if (special)
+ tlb_entry.field2 = m_ram[m_hat_base + hat_offset + 2];
+
+ if (!machine().side_effects_disabled())
+ LOGMASKED(LOG_RELOAD, "reload complete count %d f0 0x%08x f1 0x%08x f2 0x%08x\n",
+ count, tlb_entry.field0, tlb_entry.field1, tlb_entry.field2);
+
+ if (m_control[TCR] & TCR_R)
+ return MER_T;
+
+ return 0;
+ }
+
+ // terminate long ipt search
+ if ((m_control[TCR] & TCR_I) && (count == 127))
+ {
+ LOGMASKED(LOG_RELOAD, "reload long search abort\n");
+
+ return MER_I;
+ }
+
+ // fetch next hat entry
+ hat_entry = m_ram[m_hat_base + hat_offset + 1];
+ if (hat_entry & HAT_L)
+ {
+ LOGMASKED(LOG_RELOAD, "reload fault\n");
+
+ return MER_F;
+ }
+
+ // select next ipt entry
+ hat_offset = (hat_entry & HAT_IPTP) << 2;
+ }
+
+ fatalerror("rosetta_device::reload() endless loop detected\n");
+}
+
+void rosetta_device::set_mear(u32 const address, mear_state lock)
+{
+ if (m_mear_lock == LOCKED)
+ return;
+
+ if (m_mear_lock == MEMORY && lock == MEMORY)
+ return;
+
+ m_control[MEAR] = address;
+ m_mear_lock = lock;
+}
+
+void rosetta_device::set_rmdr(u8 const ecc, bool lock)
+{
+ if (m_rmdr_lock)
+ return;
+
+ m_control[RMDR] = (m_control[RMDR] & ~RMDR_CHECK) | (ecc << 8);
+ m_rmdr_lock = lock;
+}
+
+u32 rosetta_device::segment_r(offs_t offset)
+{
+ return m_segment[offset];
+}
+
+void rosetta_device::segment_w(offs_t offset, u32 data)
+{
+ LOG("segment_w 0x%x data 0x%x (%s)\n", offset, data, machine().describe_context());
+
+ m_segment[offset] = data;
+}
+
+u32 rosetta_device::control_r(offs_t offset)
+{
+ u32 data = 0;
+
+ switch (offset)
+ {
+ case MER:
+ data = m_control[offset];
+ set_pchk(false);
+ break;
+
+ case MEAR:
+ m_mear_lock = UNLOCKED;
+ data = m_control[offset];
+ break;
+
+ case RMDR:
+ m_mear_lock = UNLOCKED;
+ m_rmdr_lock = false;
+ data = m_control[offset];
+ break;
+
+ default:
+ data = m_control[offset];
+ break;
+ }
+
+ LOG("control_r %s data 0x%08x (%s)\n", control_names[offset], data, machine().describe_context());
+
+ return data;
+}
+
+void rosetta_device::control_w(offs_t offset, u32 data)
+{
+ bool reconfig_tlb = false;
+ bool reconfig_map = false;
+
+ LOG("control_w %s data 0x%08x (%s)\n", control_names[offset], data, machine().describe_context());
+
+ switch (offset)
+ {
+ case MEAR:
+ m_mear_lock = UNLOCKED;
+ m_control[offset] = data;
+ break;
+
+ case TCR:
+ reconfig_tlb = (m_control[offset] ^ data) & (TCR_S | TCR_HIB);
+ m_control[offset] = data;
+ break;
+
+ case RAMS:
+ reconfig_tlb = m_control[offset] ^ data;
+ reconfig_map = m_control[offset] ^ data;
+ m_control[offset] = data;
+ break;
+
+ case ROMS:
+ reconfig_map = m_control[offset] ^ data;
+ m_control[offset] = data;
+ break;
+
+ case RMDR:
+ // only alternate check bits are writeable
+ m_control[offset] = (m_control[offset] & ~RMDR_ALT) | (data & RMDR_ALT);
+ break;
+
+ default:
+ m_control[offset] = data;
+ break;
+ }
+
+ if (reconfig_map)
+ config_map();
+
+ if (reconfig_tlb)
+ config_tlb();
+}
+
+u32 rosetta_device::tlb_r(offs_t offset)
+{
+ unsigned const tlb_set = BIT(offset, 4);
+ u32 data = 0;
+
+ switch (offset & 0x60)
+ {
+ case 0x00: data = m_tlb[offset & 0xf][tlb_set].field0; break;
+ case 0x20: data = m_tlb[offset & 0xf][tlb_set].field1; break;
+ case 0x40: data = m_tlb[offset & 0xf][tlb_set].field2; break;
+ }
+
+ LOG("tlb_r offset %x data %x\n", offset, data);
+
+ return data;
+}
+
+void rosetta_device::tlb_w(offs_t offset, u32 data)
+{
+ unsigned const tlb_set = BIT(offset, 4);
+
+ switch (offset & 0x60)
+ {
+ case 0x00: m_tlb[offset & 0xf][tlb_set].field0 = data; break;
+ case 0x20: m_tlb[offset & 0xf][tlb_set].field1 = data; break;
+ case 0x40: m_tlb[offset & 0xf][tlb_set].field2 = data; break;
+ }
+
+ LOG("tlb_w offset %x data %x\n", offset, data);
+}
+
+u32 rosetta_device::rca_r(offs_t offset)
+{
+ unsigned const shift = (offset & 3) * 2;
+
+ if (!m_led_lock)
+ {
+ LOG("led 0x%02x (%s)\n", u8(offset), machine().describe_context());
+
+ m_leds[0] = led_pattern[(offset >> 0) & 15];
+ m_leds[1] = led_pattern[(offset >> 4) & 15];
+ }
+
+ return (m_rca[offset >> 2] >> shift) & 3;
+}
+
+void rosetta_device::rca_w(offs_t offset, u32 data)
+{
+ unsigned const shift = (offset & 3) * 2;
+
+ m_rca[offset >> 2] &= ~(3 << shift);
+ m_rca[offset >> 2] |= (data & 3) << shift;
+
+ m_led_lock = true;
+}
+
+u32 rosetta_device::rom_r(offs_t offset, u32 mem_mask)
+{
+ if (!machine().side_effects_disabled())
+ m_led_lock = false;
+
+ return m_rom[offset];
+}
+
+void rosetta_device::rom_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ LOGMASKED(LOG_INVALID, "rom_w invalid 0x%06x data 0x%08x mask 0x%08x (%s)\n",
+ offset << 2, data, mem_mask, machine().describe_context());
+
+ if (!machine().side_effects_disabled())
+ m_control[MER] |= MER_W;
+}
+
+void rosetta_device::ram_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ if (offset < m_ram_size)
+ {
+ m_ram[offset] = (m_ram[offset] & ~mem_mask) | (data & mem_mask);
+
+ // set reference and change bits
+ if (!machine().side_effects_disabled())
+ m_rca[offset >> 11] |= ((RCA_R | RCA_C) << ((offset >> 8) & 6));
+
+ m_ecc[offset] = (m_control[TCR] & TCR_D) ? (m_control[RMDR] & RMDR_ALT) : compute_ecc(m_ram[offset]);
+ }
+ else
+ {
+ LOGMASKED(LOG_INVALID, "ram_w invalid 0x%06x data 0x%08x mask 0x%08x (%s)\n",
+ offset << 2, data, mem_mask, machine().describe_context());
+
+ m_control[MER] |= MER_B;
+ }
+}
+
+u32 rosetta_device::ram_r(offs_t offset, u32 mem_mask)
+{
+ if (offset < m_ram_size)
+ {
+ u32 data = m_ram[offset];
+
+ if (!machine().side_effects_disabled())
+ {
+ // set reference bit
+ m_rca[offset >> 11] |= (RCA_R << ((offset >> 8) & 6));
+
+ u8 const ecc = m_ecc[offset];
+
+ switch (check_ecc(data, ecc))
+ {
+ case 1:
+ // correctable error
+ if ((m_control[TCR] & TCR_C) || (m_control[TCR] & TCR_D))
+ {
+ if (!(m_control[MER] & MER_U))
+ m_control[MER] |= MER_C;
+ set_rmdr(ecc, true);
+ }
+ break;
+ case 2:
+ // uncorrectable error
+ m_control[MER] |= MER_U;
+ set_rmdr(ecc, true);
+ break;
+ }
+ }
+
+ return data;
+ }
+ else
+ {
+ LOGMASKED(LOG_INVALID, "ram_r invalid 0x%06x mask 0x%08x (%s)\n",
+ offset << 2, mem_mask, machine().describe_context());
+
+ m_control[MER] |= MER_B;
+
+ return 0;
+ }
+}
+
+u8 rosetta_device::compute_ecc(u32 const data) const
+{
+ u8 result = 0;
+
+ for (unsigned i = 0; i < ARRAY_LENGTH(ecc_bits); i++)
+ result ^= BIT(data, 31 - i) ? ecc_bits[i] : 0;
+
+ return result;
+}
+
+unsigned rosetta_device::check_ecc(u32 &data, u8 const ecc) const
+{
+ u8 const error = compute_ecc(data) ^ ecc;
+
+ if (error)
+ {
+ for (unsigned i = 0; i < ARRAY_LENGTH(ecc_bits); i++)
+ {
+ if (error == ecc_bits[i])
+ {
+ LOGMASKED(LOG_ECC, "check_ecc single-bit error 0x%08x ecc 0x%02x error 0x%02x\n", data, ecc, error);
+
+ // correct error
+ data ^= (0x8000'0000U >> i);
+
+ return 1;
+ }
+ }
+
+ // multiple-bit error
+ LOGMASKED(LOG_ECC, "check_ecc multiple-bit error 0x%08x ecc 0x%02x error 0x%02x\n", data, ecc, error);
+ return 2;
+ }
+ else
+ return 0;
+}
+
+void rosetta_device::config_map()
+{
+ // unmap everything
+ m_mem_space->unmap_readwrite(0x000000, 0xffffff);
+
+ // map rom
+ if (m_control[ROMS] & ROMS_SIZE)
+ {
+ unsigned const shift = (m_control[ROMS] & ROMS_SIZE) > 7 ? (m_control[ROMS] & ROMS_SIZE) - 7 : 0;
+ size_t const size = 0x10000ULL << shift;
+ unsigned const factor = (m_control[ROMS] & ROMS_START) >> (4 + shift);
+
+ LOG("config rom size %dk map 0x%06x-0x%06x\n", size >> 10, size * factor, size * factor + size - 1);
+
+ /*
+ * IPL code assumes ROM is mirrored within the configured range, making it
+ * difficult to identify under what circumstances an invalid memory access to
+ * the configured ROM address range would be reported.
+ */
+ size_t const available = std::min(size, m_rom.bytes());
+ m_mem_space->install_readwrite_handler(size * factor, size * factor + available - 1, available - 1, (size - 1) & ~(available - 1), 0,
+ read32s_delegate(*this, FUNC(rosetta_device::rom_r)),
+ write32s_delegate(*this, FUNC(rosetta_device::rom_w)));
+ }
+
+ // map ram
+ if (m_control[RAMS] & RAMS_SIZE)
+ {
+ unsigned const shift = (m_control[RAMS] & RAMS_SIZE) > 7 ? (m_control[RAMS] & RAMS_SIZE) - 7 : 0;
+ unsigned const size = 0x10000U << shift;
+ unsigned const factor = (m_control[RAMS] & RAMS_START) >> (4 + shift);
+
+ LOG("config ram size %dk map 0x%06x-0x%06x\n", size >> 10, size * factor, size * factor + size - 1);
+
+ m_mem_space->install_readwrite_handler(size * factor, size * factor + size - 1,
+ read32s_delegate(*this, FUNC(rosetta_device::ram_r)),
+ write32s_delegate(*this, FUNC(rosetta_device::ram_w)));
+ }
+}
+
+void rosetta_device::config_tlb()
+{
+ unsigned const ram_size = std::max(m_control[RAMS] & RAMS_SIZE, 7U);
+
+ if (m_control[TCR] & TCR_S)
+ {
+ m_atag_mask = TLB_AT4K;
+ m_page_mask = 0xffff'f000U;
+ m_page_shift = 12;
+
+ m_hat_base = (m_control[TCR] & TCR_HIB) << (ram_size - 1);
+ m_hat_mask = (1U << (ram_size - 1)) - 4;
+ }
+ else
+ {
+ m_atag_mask = TLB_AT2K;
+ m_page_mask = 0xffff'f800U;
+ m_page_shift = 11;
+
+ m_hat_base = (m_control[TCR] & TCR_HIB) << ram_size;
+ m_hat_mask = (1U << ram_size) - 4;
+ }
+
+ LOG("config page size %d hat base 0x%08x mask 0x%04x\n", 1 << m_page_shift, m_hat_base, m_hat_mask);
+}
+
+void rosetta_device::tlb_inv_all(u32 data)
+{
+ LOGMASKED(LOG_TLB, "tlb_inv_all (%s)\n", machine().describe_context());
+
+ for (unsigned i = 0; i < 16; i++)
+ {
+ m_tlb[i][0].field1 &= ~TLB_V;
+ m_tlb[i][1].field1 &= ~TLB_V;
+ }
+}
+
+void rosetta_device::tlb_inv_segment(u32 data)
+{
+ LOGMASKED(LOG_TLB, "tlb_inv_segment %x (%s)\n", data & 15, machine().describe_context());
+
+ unsigned const identifier = (m_segment[data & 15] & SEGMENT_ID) >> 2;
+
+ for (unsigned i = 0; i < 16; i++)
+ {
+ if (((m_tlb[i][0].field0 & TLB_SEG) >> 17) == identifier)
+ m_tlb[i][0].field1 &= ~TLB_V;
+
+ if (((m_tlb[i][1].field0 & TLB_SEG) >> 17) == identifier)
+ m_tlb[i][1].field1 &= ~TLB_V;
+ }
+}
+
+void rosetta_device::tlb_inv_address(u32 data)
+{
+ u64 const virtual_address = (u64(m_segment[data >> 28] & SEGMENT_ID) << 26) | (data & 0x0fff'ffffU);
+ unsigned const tlb_index = (data >> m_page_shift) & 15;
+
+ if ((m_tlb[tlb_index][0].field1 & TLB_V) && !((m_tlb[tlb_index][0].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ LOGMASKED(LOG_TLB, "tlb_inv_address 0x%08x set 0 (%s)\n", data, machine().describe_context());
+
+ m_tlb[tlb_index][0].field1 &= ~TLB_V;
+ m_tlb_lru &= ~(1U << tlb_index);
+ }
+
+ if ((m_tlb[tlb_index][1].field1 & TLB_V) && !((m_tlb[tlb_index][1].field0 ^ (virtual_address >> 11)) & m_atag_mask))
+ {
+ LOGMASKED(LOG_TLB, "tlb_inv_address 0x%08x set 1 (%s)\n", data, machine().describe_context());
+
+ m_tlb[tlb_index][1].field1 &= ~TLB_V;
+ m_tlb_lru |= (1U << tlb_index);
+ }
+}
+
+void rosetta_device::compute_address(u32 data)
+{
+ unsigned const segment = data >> 28;
+ tlb_entry t;
+
+ m_control[TRAR] = TRAR_I;
+
+ if ((m_control[TCR] & TCR_V) && !segment)
+ m_control[TRAR] = data & 0x00ff'ffffU;
+ else if (!tlb_reload(t, (u64(m_segment[segment] & SEGMENT_ID) << 26) | (data & 0x0fff'ffffU)))
+ m_control[TRAR] = (m_control[TCR] & TCR_S)
+ ? ((t.field1 & TLB_RPN4K) << 8) | (data & 0x0fffU)
+ : ((t.field1 & TLB_RPN2K) << 8) | (data & 0x07ffU);
+}
+
+bool rosetta_device::fetch(u32 address, u16 &data, rsc_mode const mode)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ // FIXME: differences between fetch and load
+ if (!translate(address, true, false))
+ return false;
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_U | MER_C);
+ data = m_mem.read_word(address);
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_U | MER_C))
+ {
+ case MER_U:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+ return false;
+ }
+ break;
+
+ case MER_C:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ set_mchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::load(u32 address, T &data, rsc_mode const mode, bool sp)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, sp, false))
+ {
+ m_control[MER] |= MER_L;
+ if ((sp && (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))) || (m_control[MER] & (MER_S)))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_U | MER_C);
+ switch (sizeof(T))
+ {
+ case 1: data = m_mem.read_byte(address); break;
+ case 2: data = m_mem.read_word(address); break;
+ case 4: data = m_mem.read_dword(address); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_U | MER_C))
+ {
+ case MER_U:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ {
+ set_mchk(true);
+ return false;
+ }
+ break;
+
+ case MER_C:
+ set_mear(address, MEMORY);
+ if (!(m_control[TCR] & TCR_D))
+ set_mchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::store(u32 address, T data, rsc_mode const mode, bool sp)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, sp, true))
+ {
+ m_control[MER] &= ~MER_L;
+ if ((sp && (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))) || (m_control[MER] & (MER_S)))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_W);
+ switch (sizeof(T))
+ {
+ case 1: m_mem.write_byte(address, data); break;
+ case 2: m_mem.write_word(address, data); break;
+ case 4: m_mem.write_dword(address, data); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_W))
+ {
+ case MER_W:
+ set_mear(address, MEMORY);
+ set_pchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ if (mode & RSC_T)
+ return false;
+ else
+ set_pchk(true);
+ break;
+ }
+
+ return true;
+}
+
+template <typename T> bool rosetta_device::modify(u32 address, std::function<T(T)> f, rsc_mode const mode)
+{
+ if (mode & rsc_mode::RSC_T)
+ {
+ if (!translate(address, true, true))
+ {
+ m_control[MER] &= ~MER_L;
+ if (m_control[MER] & (MER_V | MER_I | MER_F | MER_S | MER_P | MER_D))
+ set_mear(address, LOCKED);
+
+ return false;
+ }
+
+ // FIXME: unconditionally access RAM
+ }
+
+ // access memory and handle errors
+ u32 const mer = m_control[MER] & (MER_B | MER_W);
+ switch (sizeof(T))
+ {
+ case 1: m_mem.write_byte(address, f(m_mem.read_byte(address))); break;
+ case 2: m_mem.write_word(address, f(m_mem.read_word(address))); break;
+ case 4: m_mem.write_dword(address, f(m_mem.read_dword(address))); break;
+ }
+
+ switch ((mer ^ m_control[MER]) & (MER_B | MER_W))
+ {
+ case MER_W:
+ set_mear(address, MEMORY);
+ set_pchk(true);
+ break;
+
+ case MER_B:
+ set_mear(address, MEMORY);
+ if (mode & RSC_T)
+ return false;
+ else
+ set_pchk(true);
+ break;
+ }
+
+ return true;
+}
diff --git a/src/mame/machine/rosetta.h b/src/mame/machine/rosetta.h
new file mode 100644
index 00000000000..a97036a01fc
--- /dev/null
+++ b/src/mame/machine/rosetta.h
@@ -0,0 +1,181 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_MACHINE_ROSETTA_H
+#define MAME_MACHINE_ROSETTA_H
+
+#pragma once
+
+#include "cpu/romp/rsc.h"
+
+class rosetta_device
+ : public device_t
+ , public rsc_cpu_interface
+{
+public:
+ // ram size in doublewords
+ enum ram_size : unsigned
+ {
+ RAM_NONE = 0,
+ RAM_1M = 0x0004'0000,
+ RAM_2M = 0x0008'0000,
+ RAM_4M = 0x0010'0000,
+ RAM_8M = 0x0020'0000,
+ RAM_16M = 0x0040'0000,
+ };
+
+ rosetta_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, ram_size ram = RAM_NONE);
+
+ template <typename T> void set_mem(T &&tag, int spacenum) { m_mem_space.set_tag(std::forward<T>(tag), spacenum); }
+ template <typename T> void set_rom(T &&tag) { m_rom.set_tag(std::forward<T>(tag)); }
+
+ auto out_pchk() { return m_out_pchk.bind(); }
+ auto out_mchk() { return m_out_mchk.bind(); }
+
+ using rsc_mode = rsc_bus_interface::rsc_mode;
+
+ // rsc_cpu_interface overrides
+ virtual bool fetch(u32 address, u16 &data, rsc_mode const mode) override;
+ virtual bool ior(u32 address, u32 &data) override;
+ virtual bool iow(u32 address, u32 data) override;
+
+ // rsc_bus_interface overrides
+ virtual bool load(u32 address, u8 &data, rsc_mode const mode, bool sp) override { return load<u8>(address, data, mode, sp); }
+ virtual bool load(u32 address, u16 &data, rsc_mode const mode, bool sp) override { return load<u16>(address, data, mode, sp); }
+ virtual bool load(u32 address, u32 &data, rsc_mode const mode, bool sp) override { return load<u32>(address, data, mode, sp); }
+ virtual bool store(u32 address, u8 data, rsc_mode const mode, bool sp) override { return store<u8>(address, data, mode, sp); }
+ virtual bool store(u32 address, u16 data, rsc_mode const mode, bool sp) override { return store<u16>(address, data, mode, sp); }
+ virtual bool store(u32 address, u32 data, rsc_mode const mode, bool sp) override { return store<u32>(address, data, mode, sp); }
+ virtual bool modify(u32 address, std::function<u8(u8)> f, rsc_mode const mode) override { return modify<u8>(address, f, mode); }
+ virtual bool modify(u32 address, std::function<u16(u16)> f, rsc_mode const mode) override { return modify<u16>(address, f, mode); }
+ virtual bool modify(u32 address, std::function<u32(u32)> f, rsc_mode const mode) override { return modify<u32>(address, f, mode); }
+
+protected:
+ // device_t overrides
+ virtual void device_validity_check(validity_checker &valid) const override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_post_load() override;
+
+ // virtual address translation
+ bool translate(u32 &address, bool system_processor, bool store);
+
+ // rsc_bus_interface implementation
+ template <typename T> bool load(u32 address, T &data, rsc_mode const mode, bool sp);
+ template <typename T> bool store(u32 address, T data, rsc_mode const mode, bool sp);
+ template <typename T> bool modify(u32 address, std::function<T(T)> f, rsc_mode const mode);
+
+ // register read handlers
+ u32 segment_r(offs_t offset);
+ u32 control_r(offs_t offset);
+ u32 tlb_r(offs_t offset);
+
+ // register write handlers
+ void segment_w(offs_t offset, u32 data);
+ void control_w(offs_t offset, u32 data);
+ void tlb_w(offs_t offset, u32 data);
+
+ // memory read handlers
+ u32 rom_r(offs_t offset, u32 mem_mask);
+ u32 ram_r(offs_t offset, u32 mem_mask);
+ u32 rca_r(offs_t offset);
+
+ // memory write handlers
+ void rom_w(offs_t offset, u32 data, u32 mem_mask);
+ void ram_w(offs_t offset, u32 data, u32 mem_mask);
+ void rca_w(offs_t offset, u32 data);
+
+ u8 compute_ecc(u32 const data) const;
+ unsigned check_ecc(u32 &data, u8 const ecc) const;
+
+ struct tlb_entry
+ {
+ u32 field0 = 0; // address tag
+ u32 field1 = 0; // real page, valid, key
+ u32 field2 = 0; // write, transaction identifier, lockbits
+ };
+ tlb_entry tlb_search(u64 const virtual_address, bool const special);
+ u32 tlb_reload(tlb_entry &tlb_entry, u64 const virtual_address, bool special = false);
+
+ void tlb_inv_all(u32 data);
+ void tlb_inv_segment(u32 data);
+ void tlb_inv_address(u32 data);
+ void compute_address(u32 data);
+
+ void config_map();
+ void config_tlb();
+
+ enum mear_state : u32
+ {
+ UNLOCKED,
+ LOCKED,
+ MEMORY,
+ };
+ void set_mear(u32 const address, mear_state lock);
+ void set_rmdr(u8 const ecc, bool lock);
+
+ void set_pchk(bool state)
+ {
+ if (state != m_pchk_state)
+ {
+ m_pchk_state = state;
+
+ // program check line is active low
+ m_out_pchk(!m_pchk_state);
+ }
+ }
+
+ void set_mchk(bool state)
+ {
+ // assume edge-triggered, active low
+ if (state)
+ {
+ m_out_mchk(0);
+ m_out_mchk(1);
+ }
+ else
+ m_out_mchk(1);
+ }
+
+private:
+ required_address_space m_mem_space;
+ required_region_ptr<u32> m_rom;
+ output_finder<2> m_leds;
+
+ devcb_write_line m_out_pchk;
+ devcb_write_line m_out_mchk;
+
+ memory_access<24, 2, 0, ENDIANNESS_BIG>::cache m_mem;
+
+ // registers
+ u32 m_segment[16];
+ u32 m_control[9];
+
+ // lock and line state
+ mear_state m_mear_lock;
+ bool m_rmdr_lock;
+ bool m_led_lock;
+ bool m_pchk_state;
+
+ // tlb state
+ tlb_entry m_tlb[16][2];
+ u16 m_tlb_lru;
+
+ // storage
+ std::unique_ptr<u32[]> m_ram;
+ std::unique_ptr<u8[]> m_ecc;
+ std::unique_ptr<u8[]> m_rca;
+
+ // address translation constants
+ u32 m_atag_mask;
+ u32 m_page_mask;
+ unsigned m_page_shift;
+ u32 m_hat_base;
+ u16 m_hat_mask;
+
+ ram_size const m_ram_size;
+};
+
+DECLARE_DEVICE_TYPE(ROSETTA, rosetta_device)
+
+#endif // MAME_MACHINE_ROSETTA_H
diff --git a/src/mame/machine/rtpc_iocc.cpp b/src/mame/machine/rtpc_iocc.cpp
new file mode 100644
index 00000000000..b06cb3f5147
--- /dev/null
+++ b/src/mame/machine/rtpc_iocc.cpp
@@ -0,0 +1,349 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM RT PC I/O Channel Converter/Controller
+ *
+ * Sources:
+ * - http://bitsavers.org/pdf/ibm/pc/rt/75X0232_RT_PC_Technical_Reference_Volume_1_Jun87.pdf
+ *
+ * TODO:
+ * - bus spaces should be little endian
+ * - alternate controllers, region mode dma
+ * - improve rsc interface
+ * - state saving
+ * - refactoring and cleanup
+ */
+
+#include "emu.h"
+#include "rtpc_iocc.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_DMA (1U << 1)
+#define LOG_WIDEPIO (1U << 2)
+
+//#define VERBOSE (LOG_GENERAL|LOG_DMA)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(RTPC_IOCC, rtpc_iocc_device, "rtpc_iocc", "RT PC I/O Channel Converter/Controller")
+
+rtpc_iocc_device::rtpc_iocc_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, RTPC_IOCC, tag, owner, clock)
+ , device_memory_interface(mconfig, *this)
+ , rsc_bus_interface(mconfig, *this)
+ , m_mem_config("mem", ENDIANNESS_BIG, 16, 24, 0)
+ , m_pio_config("pio", ENDIANNESS_BIG, 16, 24, 0)
+ , m_out_int(*this)
+ , m_out_rst(*this)
+ , m_rsc(*this, "^mmu")
+ , m_csr(0)
+ , m_ccr(0)
+ , m_dbr(0)
+ , m_dmr(0)
+ , m_tcw{}
+ , m_adc(0)
+ , m_out_int_state(false)
+{
+}
+
+device_memory_interface::space_config_vector rtpc_iocc_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(AS_PROGRAM, &m_mem_config),
+ std::make_pair(AS_IO, &m_pio_config)
+ };
+}
+
+void rtpc_iocc_device::device_start()
+{
+ m_out_int.resolve_safe();
+ m_out_rst.resolve_safe();
+
+ save_item(NAME(m_csr));
+ save_item(NAME(m_ccr));
+ save_item(NAME(m_dbr));
+ save_item(NAME(m_dmr));
+ save_item(NAME(m_tcw));
+ save_item(NAME(m_adc));
+ save_item(NAME(m_out_int_state));
+}
+
+void rtpc_iocc_device::device_reset()
+{
+ m_csr = 0;
+
+ set_int(false);
+}
+
+u8 rtpc_iocc_device::dma_b_r(offs_t offset)
+{
+ u8 data = 0;
+
+ if (!BIT(m_dmr, 7 - m_adc))
+ {
+ u16 const tcw = m_tcw[(m_adc << 6) | (offset >> 11)];
+ u32 const real = ((tcw & TCW_PFX) << 11) | (offset & 0x7ff);
+
+ LOGMASKED(LOG_DMA, "dma0 tcw 0x%04x real 0x%08x\n", tcw, real);
+ if (tcw & TCW_IOC)
+ data = space(AS_PROGRAM).read_byte(real);
+ else
+ {
+ if (!m_rsc->load(real, data, RSC_N))
+ {
+ // on dma exception
+ // - assert interrupt (level 2)
+ // - csr | DE | ~PER | INTP
+
+ m_csr &= ~CSR_PER;
+ m_csr |= (CSR_DE0 >> m_adc) | CSR_DEXK;
+ m_out_rst(1);
+ set_int(true);
+ }
+ }
+ }
+ else
+ fatalerror("rtpc_iocc_device::dma_b_r() invalid dma operation\n");
+
+ return data;
+}
+
+void rtpc_iocc_device::dma_b_w(offs_t offset, u8 data)
+{
+ LOGMASKED(LOG_DMA, "dma0 offset 0x%04x data 0x%02x\n", offset, data);
+
+ if (!BIT(m_dmr, 7 - m_adc))
+ {
+ u16 const tcw = m_tcw[(m_adc << 6) | (offset >> 11)];
+ u32 const real = ((tcw & TCW_PFX) << 11) | (offset & 0x7ff);
+
+ LOGMASKED(LOG_DMA, "dma0 tcw 0x%04x real 0x%08x\n", tcw, real);
+ if (tcw & TCW_IOC)
+ space(AS_PROGRAM).write_byte(real, data);
+ else
+ m_rsc->store(real, data, RSC_N);
+ }
+ else
+ fatalerror("rtpc_iocc_device::dma_b_w() invalid dma operation\n");
+}
+
+u8 rtpc_iocc_device::dma_w_r(offs_t offset)
+{
+ u16 data = 0;
+
+ if (!BIT(m_dmr, 7 - m_adc))
+ {
+ u16 const tcw = m_tcw[(m_adc << 6) | (offset >> 10)];
+ u32 const real = ((tcw & TCW_PFX) << 11) | (offset & 0x7ff);
+
+ LOGMASKED(LOG_DMA, "dma1 tcw 0x%04x real 0x%08x\n", tcw, real);
+ if (tcw & TCW_IOC)
+ data = space(AS_PROGRAM).read_word(real);
+ else
+ m_rsc->load(real, data, RSC_N);
+ }
+ else
+ fatalerror("rtpc_iocc_device::dma_w_r() invalid dma operation\n");
+
+ return data;
+}
+
+void rtpc_iocc_device::dma_w_w(offs_t offset, u8 data)
+{
+ LOGMASKED(LOG_DMA, "dma1 offset 0x%04x data 0x%02x\n", offset, data);
+
+ if (!BIT(m_dmr, 7 - m_adc))
+ {
+ u16 const tcw = m_tcw[(m_adc << 6) | (offset >> 10)];
+ u32 const real = ((tcw & TCW_PFX) << 11) | ((offset & 0x7ff) << 1);
+
+ LOGMASKED(LOG_DMA, "dma1 tcw 0x%04x real 0x%08x\n", tcw, real);
+ // FIXME: upper data bits
+ if (tcw & TCW_IOC)
+ space(AS_PROGRAM).write_word(real, data);
+ else
+ m_rsc->store(real, u16(data), RSC_N);
+ }
+ else
+ fatalerror("rtpc_iocc_device::dma_w_w() invalid dma operation\n");
+}
+
+template <typename T> bool rtpc_iocc_device::load(u32 address, T &data, rsc_mode const mode)
+{
+ unsigned const s = (address >> 24) & 15 ? AS_PROGRAM : AS_IO;
+
+ if (s == AS_PROGRAM && (mode & RSC_U) && !(m_ccr & CCR_MMP))
+ {
+ LOG("load mem protection violation (%s)\n", machine().describe_context());
+ m_csr |= CSR_EXR | CSR_PER | CSR_PD | CSR_PVIO;
+
+ return false;
+ }
+
+ if (s == AS_IO)
+ {
+ if ((mode & RSC_U) && (address == 0xf001'0800U || !(m_ccr & CCR_IMP)))
+ {
+ LOG("load pio protection violation (%s)\n", machine().describe_context());
+ m_csr |= CSR_EXR | CSR_PER | CSR_PD | CSR_PVIO;
+
+ return false;
+ }
+ }
+
+ switch (sizeof(T))
+ {
+ case 1: data = space(s).read_byte(address); break;
+ case 2:
+ if (s == 2 && target_size(address) < sizeof(T))
+ {
+ LOGMASKED(LOG_WIDEPIO, "load pio w<-b 0x%08x (%s)\n", address, machine().describe_context());
+ data = u16(space(s).read_byte(address)) << 8;
+ data |= space(s).read_byte(address);
+ }
+ else
+ data = space(s).read_word(address);
+ break;
+ case 4:
+ if (s == 2 && target_size(address) < sizeof(T))
+ {
+ if (target_size(address) == 1)
+ {
+ LOGMASKED(LOG_WIDEPIO, "load pio d<-b 0x%08x (%s)\n", address, machine().describe_context());
+ data = u32(space(s).read_byte(address)) << 24;
+ data |= u32(space(s).read_byte(address)) << 16;
+ data |= u32(space(s).read_byte(address)) << 8;
+ data |= space(s).read_byte(address);
+ }
+ else
+ {
+ LOGMASKED(LOG_WIDEPIO, "load pio d<-w 0x%08x (%s)\n", address, machine().describe_context());
+ data = u32(space(s).read_word(address)) << 16;
+ data |= space(s).read_word(address);
+ }
+ }
+ else
+ data = space(s).read_dword(address);
+ break;
+ }
+
+ return true;
+}
+
+#ifdef _MSC_VER
+// avoid incorrect MSVC warnings about excessive shift sizes below
+#pragma warning(disable:4333)
+#endif
+
+template <typename T> bool rtpc_iocc_device::store(u32 address, T data, rsc_mode const mode)
+{
+ unsigned const spacenum = (address >> 24) & 15 ? AS_PROGRAM : AS_IO;
+
+ if (spacenum == AS_PROGRAM && (mode & RSC_U) && !(m_ccr & CCR_MMP))
+ {
+ LOG("store mem protection violation (%s)\n", machine().describe_context());
+ m_csr |= CSR_PER | CSR_PD | CSR_PVIO;
+ if (mode & RSC_T)
+ {
+ m_csr |= CSR_EXR;
+ return false;
+ }
+ else
+ set_int(true);
+
+ return true;
+ }
+
+ if (spacenum == AS_IO && (mode & RSC_U))
+ {
+ if (address == 0xf001'0800U || !(m_ccr & CCR_IMP))
+ {
+ LOG("store pio protection violation (%s)\n", machine().describe_context());
+ m_csr |= CSR_PER | CSR_PD | CSR_PVIO;
+ if (mode & RSC_T)
+ {
+ m_csr |= CSR_EXR;
+ return false;
+ }
+ else
+ set_int(true);
+
+ return true;
+ }
+ }
+
+ switch (sizeof(T))
+ {
+ case 1: space(spacenum).write_byte(address, data); break;
+ case 2:
+ if (spacenum == AS_IO && target_size(address) < sizeof(T))
+ {
+ LOGMASKED(LOG_WIDEPIO, "store pio w->b 0x%08x data 0x%04x (%s)\n", address, data, machine().describe_context());
+ space(spacenum).write_byte(address, u8(data >> 8));
+ space(spacenum).write_byte(address, u8(data >> 0));
+ }
+ else
+ space(spacenum).write_word(address, data);
+ break;
+ case 4:
+ if (spacenum == AS_IO && target_size(address) < sizeof(T))
+ {
+ if (target_size(address) == 1)
+ {
+ LOGMASKED(LOG_WIDEPIO, "store pio d->b 0x%08x data 0x%08x (%s)\n", address, data, machine().describe_context());
+ space(spacenum).write_byte(address, u8(data >> 24));
+ space(spacenum).write_byte(address, u8(data >> 16));
+ space(spacenum).write_byte(address, u8(data >> 8));
+ space(spacenum).write_byte(address, u8(data >> 0));
+ }
+ else
+ {
+ LOGMASKED(LOG_WIDEPIO, "store pio d->w 0x%08x data 0x%08x (%s)\n", address, data, machine().describe_context());
+ space(spacenum).write_word(address, u16(data >> 16));
+ space(spacenum).write_word(address, u16(data >> 0));
+ }
+ }
+ else
+ space(spacenum).write_dword(address, data);
+ break;
+ }
+
+ return true;
+}
+
+template <typename T> bool rtpc_iocc_device::modify(u32 address, std::function<T(T)> f, rsc_mode const mode)
+{
+ unsigned const spacenum = (address >> 24) & 15 ? AS_PROGRAM : AS_IO;
+
+ if (spacenum == AS_PROGRAM && (mode & RSC_U) && !(m_ccr & CCR_MMP))
+ {
+ LOG("modify mem protection violation (%s)\n", machine().describe_context());
+ m_csr |= CSR_PER | CSR_PD | CSR_PVIO;
+ if (mode & RSC_T)
+ {
+ m_csr |= CSR_EXR;
+ return false;
+ }
+ else
+ set_int(true);
+
+ return true;
+ }
+
+ if (spacenum == AS_IO)
+ {
+ LOG("modify pio space invalid operation (%s)\n", machine().describe_context());
+ m_csr |= CSR_EXR | CSR_PER | CSR_PD | CSR_INVOP;
+
+ return false;
+ }
+
+ switch (sizeof(T))
+ {
+ case 1: space(spacenum).write_byte(address, f(space(spacenum).read_byte(address))); break;
+ case 2: space(spacenum).write_word(address, f(space(spacenum).read_word(address))); break;
+ case 4: space(spacenum).write_dword(address, f(space(spacenum).read_dword(address))); break;
+ }
+
+ return true;
+}
diff --git a/src/mame/machine/rtpc_iocc.h b/src/mame/machine/rtpc_iocc.h
new file mode 100644
index 00000000000..cf7a2052ec7
--- /dev/null
+++ b/src/mame/machine/rtpc_iocc.h
@@ -0,0 +1,170 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_MACHINE_RTPC_IOCC_H
+#define MAME_MACHINE_RTPC_IOCC_H
+
+#pragma once
+
+#include "cpu/romp/rsc.h"
+
+class rtpc_iocc_device
+ : public device_t
+ , public device_memory_interface
+ , public rsc_bus_interface
+{
+public:
+ rtpc_iocc_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ auto out_int() { return m_out_int.bind(); }
+ auto out_rst() { return m_out_rst.bind(); }
+
+ enum ccr_mask : u8
+ {
+ CCR_RFE = 0x02, // refresh enable
+ CCR_RFT = 0x04, // refresh period (0=14.9µs, 1=6.4µs)
+ CCR_MMP = 0x08, // memory map privilege
+ CCR_IMP = 0x10, // i/o map privilege
+ CCR_DKA = 0x20, // dma keyboard access
+ CCR_DSA = 0x40, // dma i/o subsystem access
+ CCR_DCA = 0x80, // dma control register access
+ };
+
+ enum csr_mask : u32
+ {
+ CSR_EXR = 0x80000000, // exception reported
+ CSR_INTP = 0x40000000, // interrupt pending
+ CSR_EPOW = 0x10000000, // early power off warning
+ CSR_SRST = 0x08000000, // soft reset
+ CSR_SAT = 0x04000000, // system attention
+ CSR_PER = 0x01000000, // pio error
+ CSR_DE0 = 0x00800000, // dma error channel 0
+ CSR_DE1 = 0x00400000, // dma error channel 1
+ CSR_DE2 = 0x00200000, // dma error channel 2
+ CSR_DE3 = 0x00100000, // dma error channel 3
+ CSR_DE5 = 0x00080000, // dma error channel 5
+ CSR_DE6 = 0x00040000, // dma error channel 6
+ CSR_DE7 = 0x00020000, // dma error channel 7
+ CSR_DE8 = 0x00010000, // dma error channel 8
+ CSR_PD = 0x00008000, // pio/dma
+ CSR_PVIO = 0x00004000, // protection violation
+ CSR_INVOP = 0x00002000, // invalid operation
+ CSR_IOCK = 0x00001000, // i/o channel check
+ CSR_DEXK = 0x00000800, // dma exception
+ CSR_CRC = 0x00000400, // channel reset captured
+ CSR_SBB = 0x00000200, // system board busy
+ CSR_PRP = 0x00000100, // pio request pending
+
+ CSR_RSV = 0x220000ff, // reserved
+ };
+
+ enum tcw_mask : u16
+ {
+ TCW_PFX = 0x1fff, // translation prefix
+ TCW_IOC = 0x4000, // i/o channel destination
+ TCW_VIR = 0x8000, // virtual access
+ };
+
+ using rsc_mode = rsc_bus_interface::rsc_mode;
+
+ // rsc_pio_interface overrides
+ virtual bool load(u32 address, u8 &data, rsc_mode const mode, bool sp) override { return load<u8>(address, data, mode); }
+ virtual bool load(u32 address, u16 &data, rsc_mode const mode, bool sp) override { return load<u16>(address, data, mode); }
+ virtual bool load(u32 address, u32 &data, rsc_mode const mode, bool sp) override { return load<u32>(address, data, mode); }
+ virtual bool store(u32 address, u8 data, rsc_mode const mode, bool sp) override { return store<u8>(address, data, mode); }
+ virtual bool store(u32 address, u16 data, rsc_mode const mode, bool sp) override { return store<u16>(address, data, mode); }
+ virtual bool store(u32 address, u32 data, rsc_mode const mode, bool sp) override { return store<u32>(address, data, mode); }
+ virtual bool modify(u32 address, std::function<u8(u8)> f, rsc_mode const mode) override { return modify<u8>(address, f, mode); }
+ virtual bool modify(u32 address, std::function<u16(u16)> f, rsc_mode const mode) override { return modify<u16>(address, f, mode); }
+ virtual bool modify(u32 address, std::function<u32(u32)> f, rsc_mode const mode) override { return modify<u32>(address, f, mode); }
+
+ u8 ccr_r() { return m_ccr; }
+ void ccr_w(u8 data) { m_ccr = data; }
+
+ template <unsigned Word> u16 csr_r() { return u16((m_csr | CSR_RSV) >> (16 * Word)); }
+ void csr_w(u16 data) { set_int(false); m_csr = 0; }
+
+ u8 dma_b_r(offs_t offset);
+ u8 dma_w_r(offs_t offset);
+ void dma_b_w(offs_t offset, u8 data);
+ void dma_w_w(offs_t offset, u8 data);
+
+ u8 dmr_r() { return m_dmr; }
+ u8 dbr_r() { return m_dbr; }
+ void dmr_w(u8 data) { m_dmr = data; }
+ void dbr_w(u8 data) { m_dbr = data; }
+
+ u16 tcw_r(offs_t offset) { return m_tcw[offset]; }
+ void tcw_w(offs_t offset, u16 data, u16 mem_mask) { COMBINE_DATA(&m_tcw[offset]); }
+
+ template <unsigned Channel> void dack_w(int state) { if (!state) m_adc = Channel; }
+
+protected:
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+ //virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
+
+ template <typename T> bool load(u32 address, T &data, rsc_mode const mode);
+ template <typename T> bool store(u32 address, T data, rsc_mode const mode);
+ template <typename T> bool modify(u32 address, std::function<T(T)> f, rsc_mode const mode);
+
+ void set_int(bool state)
+ {
+ if (state != m_out_int_state)
+ {
+ if (state)
+ m_csr |= CSR_INTP;
+ m_out_int_state = state;
+ m_out_int(!m_out_int_state);
+ }
+ }
+
+ unsigned target_size(u32 const address) const
+ {
+ if (address < 0xf000'8000U)
+ {
+ if (address == 0xf000'0110U || address == 0xf000'0112U)
+ return 2;
+ else
+ // FIXME: get size from isa bus
+ return 1;
+ }
+ else if (address == 0xf000'8400U)
+ // exception for kls
+ return 2;
+ else if (address < 0xf001'0000U)
+ return 1;
+ else if (address < 0xf001'0800U)
+ return 2;
+ else
+ return 4;
+ }
+
+private:
+ address_space_config m_mem_config;
+ address_space_config m_pio_config;
+
+ devcb_write_line m_out_int;
+ devcb_write_line m_out_rst;
+
+ required_device<rsc_bus_interface> m_rsc;
+
+ u32 m_csr; // channel status register
+ u8 m_ccr; // channel control register
+
+ u8 m_dbr; // dma buffer register
+ u8 m_dmr; // dma mode register
+ u16 m_tcw[1024]; // dma translation control words
+
+ unsigned m_adc; // active dma channel
+
+ bool m_out_int_state;
+};
+
+DECLARE_DEVICE_TYPE(RTPC_IOCC, rtpc_iocc_device)
+
+#endif // MAME_MACHINE_RTPC_IOCC_H
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index f5cea93339e..c5868c83ede 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -35828,6 +35828,13 @@ rt1715 //
rt1715lc // (latin/cyrillic)
rt1715w //
+@source:rtpc.cpp
+rtpc010 // IBM RT PC Model 010
+rtpc015 // IBM RT PC Model 015
+rtpc020 // IBM RT PC Model 020
+rtpc025 // IBM RT PC Model 025
+rtpc025 // IBM RT PC Model A25
+
@source:runaway.cpp
qwak // (proto) (c) 1982
runaway // (proto) (c) 1982
diff --git a/src/mame/mess.flt b/src/mame/mess.flt
index c724869402c..553e4000cc0 100644
--- a/src/mame/mess.flt
+++ b/src/mame/mess.flt
@@ -850,6 +850,7 @@ roland_tr707.cpp
roland_tr909.cpp
roland_u20.cpp
rt1715.cpp
+rtpc.cpp
rvoice.cpp
rx78.cpp
rz1.cpp