summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/cpu.lua16
-rw-r--r--scripts/target/mame/mess.lua16
-rw-r--r--src/devices/cpu/clipper/clipper.cpp1375
-rw-r--r--src/devices/cpu/clipper/clipper.h294
-rw-r--r--src/devices/cpu/clipper/clipperd.cpp351
-rw-r--r--src/mame/drivers/interpro.cpp345
-rw-r--r--src/mame/includes/interpro.h131
-rw-r--r--src/mame/machine/cammu.cpp180
-rw-r--r--src/mame/machine/cammu.h93
-rw-r--r--src/mame/machine/interpro_ioga.cpp691
-rw-r--r--src/mame/machine/interpro_ioga.h256
-rw-r--r--src/mame/machine/interpro_mcga.cpp89
-rw-r--r--src/mame/machine/interpro_mcga.h45
-rw-r--r--src/mame/machine/interpro_sga.cpp88
-rw-r--r--src/mame/machine/interpro_sga.h71
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/tools/unidasm.cpp2
17 files changed, 4046 insertions, 0 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index 184a9310876..72a7ffea3fa 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -2415,3 +2415,19 @@ end
if (CPUS["MB86901"]~=null or _OPTIONS["with-tools"]) then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/sparc/sparcdasm.cpp")
end
+
+--------------------------------------------------
+-- Intergraph CLIPPER (C100/C300/C400) series
+--@src/devices/cpu/clipper/clipper.h,CPUS["CLIPPER"] = true
+--------------------------------------------------
+
+if (CPUS["CLIPPER"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/cpu/clipper/clipper.cpp",
+ MAME_DIR .. "src/devices/cpu/clipper/clipper.h",
+ }
+end
+
+if (CPUS["CLIPPER"]~=null or _OPTIONS["with-tools"]) then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/clipper/clipperd.cpp")
+end \ No newline at end of file
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 7b645311826..331c1b2ddf3 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -2073,6 +2073,22 @@ files {
MAME_DIR .. "src/mame/drivers/tim100.cpp",
}
+createMESSProjects(_target, _subtarget, "interpro")
+files {
+ MAME_DIR .. "src/mame/drivers/interpro.cpp",
+ MAME_DIR .. "src/mame/includes/interpro.h",
+ MAME_DIR .. "src/mame/machine/cammu.h",
+ MAME_DIR .. "src/mame/machine/cammu.cpp",
+ MAME_DIR .. "src/mame/machine/interpro_ioga.h",
+ MAME_DIR .. "src/mame/machine/interpro_ioga.cpp",
+ MAME_DIR .. "src/mame/machine/interpro_ioga.h",
+ MAME_DIR .. "src/mame/machine/interpro_ioga.cpp",
+ MAME_DIR .. "src/mame/machine/interpro_mcga.h",
+ MAME_DIR .. "src/mame/machine/interpro_mcga.cpp",
+ MAME_DIR .. "src/mame/machine/interpro_sga.h",
+ MAME_DIR .. "src/mame/machine/interpro_sga.cpp",
+}
+
createMESSProjects(_target, _subtarget, "interton")
files {
MAME_DIR .. "src/mame/drivers/vc4000.cpp",
diff --git a/src/devices/cpu/clipper/clipper.cpp b/src/devices/cpu/clipper/clipper.cpp
new file mode 100644
index 00000000000..6e0d049ff38
--- /dev/null
+++ b/src/devices/cpu/clipper/clipper.cpp
@@ -0,0 +1,1375 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the Fairchild/Intergraph CLIPPER CPU family.
+ *
+ * Primary source: http://bitsavers.trailing-edge.com/pdf/fairchild/clipper/Clipper_Instruction_Set_Oct85.pdf
+ *
+ * TODO:
+ * - save/restore state
+ * - unimplemented instructions
+ * - C100, C300, C400 variants
+ * - correct boot logic
+ * - condition codes for multiply instructions
+ * - most cpu traps/faults
+ * - instruction timing
+ * - big endian support (not present in the wild)
+ */
+
+#include "emu.h"
+#include "debugger.h"
+#include "clipper.h"
+
+#define VERBOSE 0
+#if VERBOSE
+#define LOG_INTERRUPT(...) logerror(__VA_ARGS__)
+#else
+#define LOG_INTERRUPT(...)
+#endif
+
+const device_type CLIPPER_C100 = &device_creator<clipper_c100_device>;
+const device_type CLIPPER_C300 = &device_creator<clipper_c300_device>;
+const device_type CLIPPER_C400 = &device_creator<clipper_c400_device>;
+
+clipper_c100_device::clipper_c100_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : clipper_device(mconfig, CLIPPER_C100, "C100 CLIPPER", tag, owner, clock, "C100", __FILE__) { }
+
+clipper_c300_device::clipper_c300_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : clipper_device(mconfig, CLIPPER_C300, "C300 CLIPPER", tag, owner, clock, "C300", __FILE__) { }
+
+clipper_c400_device::clipper_c400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : clipper_device(mconfig, CLIPPER_C400, "C400 CLIPPER", tag, owner, clock, "C400", __FILE__) { }
+
+clipper_device::clipper_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, u32 clock, const char *shortname, const char *source)
+ : cpu_device(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_insn_config("insn", ENDIANNESS_LITTLE, 32, 32, 0),
+ m_data_config("data", ENDIANNESS_LITTLE, 32, 32, 0),
+ m_insn(nullptr),
+ m_data(nullptr),
+ m_pc(0),
+ m_r(m_rs),
+ m_icount(0)
+{
+}
+
+void clipper_device::device_start()
+{
+ // get our address spaces
+ m_insn = &space(AS_PROGRAM);
+ m_data = &space(AS_DATA);
+
+ // set our instruction counter
+ m_icountptr = &m_icount;
+
+ //save_item(NAME(m_pc));
+
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+ state_add(STATE_GENSP, "GENSP", m_r[15]).noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_psw).mask(0xf).formatstr("%4s").noshow();
+
+ state_add(CLIPPER_PC, "pc", m_pc);
+ state_add(CLIPPER_PSW, "psw", m_psw);
+ state_add(CLIPPER_SSW, "ssw", m_ssw);
+
+ state_add(CLIPPER_R0, "r0", m_r[0]);
+ state_add(CLIPPER_R1, "r1", m_r[1]);
+ state_add(CLIPPER_R2, "r2", m_r[2]);
+ state_add(CLIPPER_R3, "r3", m_r[3]);
+ state_add(CLIPPER_R4, "r4", m_r[4]);
+ state_add(CLIPPER_R5, "r5", m_r[5]);
+ state_add(CLIPPER_R6, "r6", m_r[6]);
+ state_add(CLIPPER_R7, "r7", m_r[7]);
+ state_add(CLIPPER_R8, "r8", m_r[8]);
+ state_add(CLIPPER_R9, "r9", m_r[9]);
+ state_add(CLIPPER_R10, "r10", m_r[10]);
+ state_add(CLIPPER_R11, "r11", m_r[11]);
+ state_add(CLIPPER_R12, "r12", m_r[12]);
+ state_add(CLIPPER_R13, "r13", m_r[13]);
+ state_add(CLIPPER_R14, "r14", m_r[14]);
+ state_add(CLIPPER_R15, "r15", m_r[15]);
+
+ state_add(CLIPPER_F0, "f0", m_f[0]);
+ state_add(CLIPPER_F1, "f1", m_f[1]);
+ state_add(CLIPPER_F2, "f2", m_f[2]);
+ state_add(CLIPPER_F3, "f3", m_f[3]);
+ state_add(CLIPPER_F4, "f4", m_f[4]);
+ state_add(CLIPPER_F5, "f5", m_f[5]);
+ state_add(CLIPPER_F6, "f6", m_f[6]);
+ state_add(CLIPPER_F7, "f7", m_f[7]);
+
+ // C400 has 8 additional floating point registers
+ if (type() == CLIPPER_C400)
+ {
+ state_add(CLIPPER_F8, "f8", m_f[8]);
+ state_add(CLIPPER_F9, "f9", m_f[9]);
+ state_add(CLIPPER_F10, "f10", m_f[10]);
+ state_add(CLIPPER_F11, "f11", m_f[11]);
+ state_add(CLIPPER_F12, "f12", m_f[12]);
+ state_add(CLIPPER_F13, "f13", m_f[13]);
+ state_add(CLIPPER_F14, "f14", m_f[14]);
+ state_add(CLIPPER_F15, "f15", m_f[15]);
+ }
+}
+
+void clipper_device::device_reset()
+{
+ /*
+ * From C300 documentation, on reset:
+ * psw: T cleared, BIG set from hardware, others undefined
+ * ssw: EI, TP, M, U, K, KU, UU, P cleared, ID set from hardware, others undefined
+ */
+ m_psw = 0;
+ m_ssw = 0;
+
+ m_r = SSW(U) ? m_ru : m_rs;
+
+ // we'll opt to clear the integer and floating point registers too
+ memset(m_r, 0, sizeof(m_r));
+ memset(m_f, 0, sizeof(m_f));
+
+ // FIXME: figure out how to branch to the boot code properly
+ m_pc = 0x7f100000;
+ m_irq = 0;
+ m_nmi = 0;
+}
+
+void clipper_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("%c%c%c%c",
+ PSW(C) ? 'C' : '.',
+ PSW(V) ? 'V' : '.',
+ PSW(Z) ? 'Z' : '.',
+ PSW(N) ? 'N' : '.');
+ break;
+ }
+}
+
+void clipper_device::execute_run()
+{
+ u16 insn;
+
+ // check for non-maskable and prioritised interrupts
+ if (m_nmi)
+ {
+ // acknowledge non-maskable interrupt
+ standard_irq_callback(INPUT_LINE_NMI);
+
+ LOG_INTERRUPT("non-maskable interrupt - current pc = 0x%08x\n", m_pc);
+ m_pc = intrap(EXCEPTION_INTERRUPT_BASE, m_pc);
+ }
+ else if (SSW(EI) && m_irq)
+ {
+ // FIXME: sample interrupt vector from the bus without acknowledging the interrupt
+ u8 ivec = standard_irq_callback(-1);
+ LOG_INTERRUPT("received prioritised interrupt with vector 0x%04x\n", ivec);
+
+ // allow equal/higher priority interrupts
+ if ((ivec >> 4) <= SSW(IL))
+ {
+ // acknowledge interrupt
+ standard_irq_callback(INPUT_LINE_IRQ0);
+
+ LOG_INTERRUPT("accepting interrupt vector 0x%04x - current pc = %08x\n", ivec, m_pc);
+ m_pc = intrap(EXCEPTION_INTERRUPT_BASE + ivec * 8, m_pc);
+ }
+ }
+
+ while (m_icount > 0) {
+
+ debugger_instruction_hook(this, m_pc);
+
+ // fetch instruction word
+ insn = m_insn->read_word(m_pc + 0);
+
+ decode_instruction(insn);
+
+ // decode and execute instruction, return next pc
+ m_pc = execute_instruction();
+
+ // FIXME: some instructions take longer (significantly) than one cycle
+ // and also the timings are often slower for the C100 and C300
+ m_icount--;
+ }
+}
+
+void clipper_device::execute_set_input(int inputnum, int state)
+{
+ switch (inputnum)
+ {
+ case INPUT_LINE_IRQ0:
+ m_irq = state;
+ break;
+
+ case INPUT_LINE_NMI:
+ m_nmi = state;
+ break;
+ }
+}
+
+/*
+ * The CLIPPER has a true Harvard architecture. In the InterPro, these are tied back together
+ * again by the MMU, which then directs the access to one of 3 address spaces: main, i/o or boot.
+ */
+const address_space_config *clipper_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_PROGRAM: return &m_insn_config;
+ case AS_DATA: return &m_data_config;
+ }
+
+ return nullptr;
+}
+
+/*
+ * This function decodes instruction operands and computes effective addresses (for
+ * instructions with addressing modes). The results are contained in the m_info
+ * structure to simplify passing between here and execute_instruction().
+ */
+void clipper_device::decode_instruction (u16 insn)
+{
+ // decode the primary parcel
+ m_info.opcode = insn >> 8;
+ m_info.subopcode = insn & 0xff;
+ m_info.r1 = (insn & 0x00f0) >> 4;
+ m_info.r2 = insn & 0x000f;
+
+ // initialise the other fields
+ m_info.imm = 0;
+ m_info.macro = 0;
+ m_info.size = 0;
+ m_info.address = 0;
+
+ if ((insn & 0xf800) == 0x3800)
+ {
+ // instruction has a 16 bit immediate operand
+
+ // fetch 16 bit immediate and sign extend
+ m_info.imm = (s16)m_insn->read_word(m_pc + 2);
+ m_info.size = 4;
+ }
+ else if ((insn & 0xd300) == 0x8300)
+ {
+ // instruction has an immediate operand, either 16 or 32 bit
+ if (insn & 0x0080)
+ {
+ // fetch 16 bit immediate and sign extend
+ m_info.imm = (s16)m_insn->read_word(m_pc + 2);
+ m_info.size = 4;
+ }
+ else
+ {
+ // fetch 32 bit immediate and sign extend
+ m_info.imm = (s32)m_insn->read_dword_unaligned(m_pc + 2);
+ m_info.size = 6;
+ }
+ }
+ else if ((insn & 0xc000) == 0x4000)
+ {
+ // instructions with addresses
+ if (insn & 0x0100)
+ {
+ // instructions with complex modes
+ u16 temp;
+
+ switch (insn & 0x00f0)
+ {
+ case ADDR_MODE_PC32:
+ m_info.address = m_pc + (s32)m_insn->read_dword_unaligned(m_pc + 2);
+ m_info.size = 6;
+ break;
+
+ case ADDR_MODE_ABS32:
+ m_info.address = m_insn->read_dword_unaligned(m_pc + 2);
+ m_info.size = 6;
+ break;
+
+ case ADDR_MODE_REL32:
+ m_info.r2 = m_insn->read_word(m_pc + 2) & 0xf;
+ m_info.address = m_r[insn & 0xf] + (s32)m_insn->read_dword_unaligned(m_pc + 4);
+ m_info.size = 8;
+ break;
+
+ case ADDR_MODE_PC16:
+ m_info.address = m_pc + (s16)m_insn->read_word(m_pc + 2);
+ m_info.size = 4;
+ break;
+
+ case ADDR_MODE_REL12:
+ temp = m_insn->read_word(m_pc + 2);
+
+ m_info.r2 = temp & 0xf;
+ m_info.address = m_r[insn & 0xf] + ((s16)temp >> 4);
+ m_info.size = 4;
+ break;
+
+ case ADDR_MODE_ABS16:
+ m_info.address = (s16)m_insn->read_word(m_pc + 2);
+ m_info.size = 4;
+ break;
+
+ case ADDR_MODE_PCX:
+ temp = m_insn->read_word(m_pc + 2);
+
+ m_info.r2 = temp & 0xf;
+ m_info.address = m_pc + m_r[(temp >> 4) & 0xf];
+ m_info.size = 4;
+ break;
+
+ case ADDR_MODE_RELX:
+ temp = m_insn->read_word(m_pc + 2);
+
+ m_info.r2 = temp & 0xf;
+ m_info.address = m_r[insn & 0xf] + m_r[(temp >> 4) & 0xf];
+ m_info.size = 4;
+ break;
+
+ default:
+ logerror("illegal addressing mode pc = 0x%08x\n", m_pc);
+ machine().debug_break();
+ break;
+ }
+ }
+ else
+ {
+ // relative addressing mode
+ m_info.address = m_r[m_info.r1];
+ m_info.size = 2;
+ }
+ }
+ else if ((insn & 0xfd00) == 0xb400)
+ {
+ // macro instructions
+ m_info.macro = m_insn->read_word(m_pc + 2);
+ m_info.size = 4;
+ }
+ else
+ // all other instruction formats are 16 bits
+ m_info.size = 2;
+}
+
+int clipper_device::execute_instruction ()
+{
+ // the address of the next instruction
+ u32 next_pc;
+
+ // next instruction follows the current one by default, but
+ // may be changed for branch, call or trap instructions
+ next_pc = m_pc + m_info.size;
+
+ switch (m_info.opcode)
+ {
+ case 0x00: // noop
+ break;
+
+ case 0x10:
+ // movwp: move word to processor register
+ // treated as a noop if target ssw in user mode
+ // R1 == 3 means "fast" mode - avoids pipeline flush
+ if (R1 == 0)
+ m_psw = m_r[R2];
+ else if (!SSW(U) && (R1 == 1 || R1 == 3))
+ {
+ m_ssw = m_r[R2];
+ m_r = SSW(U) ? m_ru : m_rs;
+ }
+ // FLAGS: CVZN
+ break;
+ case 0x11:
+ // movpw: move processor register to word
+ switch (R1)
+ {
+ case 0: m_r[R2] = m_psw; break;
+ case 1: m_r[R2] = m_ssw; break;
+ }
+ break;
+ case 0x12:
+ // calls: call supervisor
+ next_pc = intrap(EXCEPTION_SUPERVISOR_CALL_BASE + (m_info.subopcode & 0x7f) * 8, next_pc);
+ break;
+ case 0x13:
+ // ret: return from subroutine
+ next_pc = m_data->read_dword(m_r[R2]);
+ m_r[R2] += 4;
+ // TRAPS: C,U,A,P,R
+ break;
+ case 0x14:
+ // pushw: push word
+ m_r[R1] -= 4;
+ m_data->write_dword(m_r[R1], m_r[R2]);
+ // TRAPS: A,P,W
+ break;
+
+ case 0x16:
+ // popw: pop word
+ m_r[R2] = m_data->read_dword(m_r[R1]);
+ m_r[R1] += 4;
+ // TRAPS: C,U,A,P,R
+ break;
+
+ case 0x20:
+ // adds: add single floating
+ *((float *)&m_f[R2]) += *((float *)&m_f[R1]);
+ // TRAPS: F_IVUX
+ break;
+ case 0x21:
+ // subs: subtract single floating
+ *((float *)&m_f[R2]) -= *((float *)&m_f[R1]);
+ // TRAPS: F_IVUX
+ break;
+ case 0x22:
+ // addd: add double floating
+ m_f[R2] += m_f[R1];
+ // TRAPS: F_IVUX
+ break;
+ case 0x23:
+ // subd: subtract double floating
+ m_f[R2] -= m_f[R1];
+ // TRAPS: F_IVUX
+ break;
+ case 0x24:
+ // movs: move single floating
+ *((float *)&m_f[R2]) = *((float *)&m_f[R1]);
+ break;
+ case 0x25:
+ // cmps: compare single floating
+ FLAGS(0, 0, *((float *)&m_f[R2]) == *((float *)&m_f[R1]), *((float *)&m_f[R2]) < *((float *)&m_f[R1]))
+ break;
+ case 0x26:
+ // movd: move double floating
+ m_f[R2] = m_f[R1];
+ break;
+ case 0x27:
+ // cmpd: compare double floating
+ FLAGS(0, 0, m_f[R2] == m_f[R1], m_f[R2] < m_f[R1])
+ // FLAGS: 00ZN
+ break;
+ case 0x28:
+ // muls: multiply single floating
+ *((float *)&m_f[R2]) *= *((float *)&m_f[R1]);
+ // TRAPS: F_IVUX
+ break;
+ case 0x29:
+ // divs: divide single floating
+ *((float *)&m_f[R2]) /= *((float *)&m_f[R1]);
+ // TRAPS: F_IVDUX
+ break;
+ case 0x2a:
+ // muld: multiply double floating
+ m_f[R2] *= m_f[R1];
+ // TRAPS: F_IVUX
+ break;
+ case 0x2b:
+ // divd: divide double floating
+ m_f[R2] /= m_f[R1];
+ // TRAPS: F_IVDUX
+ break;
+ case 0x2c:
+ // movsw: move single floating to word
+ m_r[R2] = *((s32 *)&m_f[R1]);
+ break;
+ case 0x2d:
+ // movws: move word to single floating
+ *((s32 *)&m_f[R2]) = m_r[R1];
+ break;
+ case 0x2e:
+ // movdl: move double floating to longword
+ ((double *)m_r)[R2 >> 1] = m_f[R1];
+ break;
+ case 0x2f:
+ // movld: move longword to double floating
+ m_f[R2] = ((double *)m_r)[R1 >> 1];
+ break;
+ case 0x30:
+ // shaw: shift arithmetic word
+ if (m_r[R1] > 0)
+ {
+ // save the bits that will be shifted out plus new sign bit
+ s32 v = m_r[R2] >> (31 - m_r[R1]);
+
+ m_r[R2] <<= m_r[R1];
+
+ // overflow is set if sign changes during shift
+ FLAGS(0, v != 0 && v != -1, m_r[R2] == 0, m_r[R2] < 0)
+ }
+ else
+ {
+ m_r[R2] >>= -m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ }
+ // FLAGS: 0VZN
+ break;
+ case 0x31:
+ // shal: shift arithmetic longword
+ if (m_r[R1] > 0)
+ {
+ // save the bits that will be shifted out plus new sign bit
+ s64 v = ((s64 *)m_r)[R2 >> 1] >> (63 - m_r[R1]);
+
+ ((s64 *)m_r)[R2 >> 1] <<= m_r[R1];
+
+ // overflow is set if sign changes during shift
+ FLAGS(0, v != 0 && v != -1, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0)
+ }
+ else
+ {
+ ((s64 *)m_r)[R2 >> 1] >>= -m_r[R1];
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0)
+ }
+ // FLAGS: 0VZN
+ break;
+ case 0x32:
+ // shlw: shift logical word
+ if (m_r[R1] > 0)
+ m_r[R2] <<= m_r[R1];
+ else
+ ((u32 *)m_r)[R2] >>= -m_r[R1];
+ // FLAGS: 00ZN
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0);
+ break;
+ case 0x33:
+ // shll: shift logical longword
+ if (m_r[R1] > 0)
+ ((u64 *)m_r)[R2 >> 1] <<= m_r[R1];
+ else
+ ((u64 *)m_r)[R2 >> 1] >>= -m_r[R1];
+ // FLAGS: 00ZN
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0);
+ break;
+ case 0x34:
+ // rotw: rotate word
+ if (m_r[R1] > 0)
+ m_r[R2] = _rotl(m_r[R2], m_r[R1]);
+ else
+ m_r[R2] = _rotr(m_r[R2], -m_r[R1]);
+ // FLAGS: 00ZN
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0);
+ break;
+ case 0x35:
+ // rotl: rotate longword
+ if (m_r[R1] > 0)
+ ((u64 *)m_r)[R2 >> 1] = _rotl64(((u64 *)m_r)[R2 >> 1], m_r[R1]);
+ else
+ ((u64 *)m_r)[R2 >> 1] = _rotr64(((u64 *)m_r)[R2 >> 1], -m_r[R1]);
+ // FLAGS: 00ZN
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0);
+ break;
+
+ case 0x38:
+ // shai: shift arithmetic immediate
+ if (m_info.imm > 0)
+ {
+ // save the bits that will be shifted out plus new sign bit
+ s32 v = m_r[R2] >> (31 - m_info.imm);
+
+ m_r[R2] <<= m_info.imm;
+
+ // overflow is set if sign changes during shift
+ FLAGS(0, v != 0 && v != -1, m_r[R2] == 0, m_r[R2] < 0)
+ }
+ else
+ {
+ m_r[R2] >>= -m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ }
+ // FLAGS: 0VZN
+ // TRAPS: I
+ break;
+ case 0x39:
+ // shali: shift arithmetic longword immediate
+ if (m_info.imm > 0)
+ {
+ // save the bits that will be shifted out plus new sign bit
+ s64 v = ((s64 *)m_r)[R2 >> 1] >> (63 - m_info.imm);
+
+ ((s64 *)m_r)[R2 >> 1] <<= m_info.imm;
+
+ // overflow is set if sign changes during shift
+ FLAGS(0, v != 0 && v != -1, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0)
+ }
+ else
+ {
+ ((s64 *)m_r)[R2 >> 1] >>= -m_info.imm;
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0)
+ }
+ // FLAGS: 0VZN
+ // TRAPS: I
+ break;
+ case 0x3a:
+ // shli: shift logical immediate
+ if (m_info.imm > 0)
+ m_r[R2] <<= m_info.imm;
+ else
+ ((u32 *)m_r)[R2] >>= -m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0);
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x3b:
+ // shlli: shift logical longword immediate
+ if (m_info.imm > 0)
+ ((u64 *)m_r)[R2 >> 1] <<= m_info.imm;
+ else
+ ((u64 *)m_r)[R2 >> 1] >>= -m_info.imm;
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0);
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x3c:
+ // roti: rotate immediate
+ if (m_info.imm > 0)
+ m_r[R2] = _rotl(m_r[R2], m_info.imm);
+ else
+ m_r[R2] = _rotr(m_r[R2], -m_info.imm);
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0);
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x3d:
+ // rotli: rotate longword immediate
+ if (m_info.imm > 0)
+ ((u64 *)m_r)[R2 >> 1] = _rotl64(((u64 *)m_r)[R2 >> 1], m_info.imm);
+ else
+ ((u64 *)m_r)[R2 >> 1] = _rotr64(((u64 *)m_r)[R2 >> 1], -m_info.imm);
+ FLAGS(0, 0, ((s64 *)m_r)[R2 >> 1] == 0, ((s64 *)m_r)[R2 >> 1] < 0);
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+
+ case 0x44:
+ case 0x45:
+ // call: call subroutine
+ m_r[R2] -= 4;
+ m_data->write_dword(m_r[R2], next_pc);
+ next_pc = m_info.address;
+ // TRAPS: A,P,W
+ break;
+#ifdef UNIMPLEMENTED_C400
+ case 0x46:
+ case 0x47:
+ // loadd2:
+ break;
+#endif
+ case 0x48:
+ case 0x49:
+ // b*: branch on condition
+ if (evaluate_branch())
+ next_pc = m_info.address;
+ // TRAPS: A,I
+ break;
+#ifdef UNIMPLEMENTED_C400
+ case 0x4a:
+ case 0x4b:
+ // cdb:
+ break;
+ case 0x4c:
+ case 0x4d:
+ // cdbeq:
+ break;
+ case 0x4e:
+ case 0x4f:
+ // cdbne:
+ break;
+ case 0x50:
+ case 0x51:
+ // db*:
+ break;
+#endif
+#ifdef UNIMPLEMENTED
+ case 0x4c:
+ case 0x4d:
+ // bf*:
+ break;
+#endif
+
+ case 0x60:
+ case 0x61:
+ // loadw: load word
+ m_r[R2] = m_data->read_dword(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x62:
+ case 0x63:
+ // loada: load address
+ m_r[R2] = m_info.address;
+ // TRAPS: I
+ break;
+ case 0x64:
+ case 0x65:
+ // loads: load single floating
+ ((u64 *)&m_f)[R2] = m_data->read_dword(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x66:
+ case 0x67:
+ // loadd: load double floating
+ ((u64 *)&m_f)[R2] = m_data->read_qword(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x68:
+ case 0x69:
+ // loadb: load byte
+ m_r[R2] = (s8)m_data->read_byte(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x6a:
+ case 0x6b:
+ // loadbu: load byte unsigned
+ m_r[R2] = m_data->read_byte(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x6c:
+ case 0x6d:
+ // loadh: load halfword
+ m_r[R2] = (s16)m_data->read_word(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x6e:
+ case 0x6f:
+ // loadhu: load halfword unsigned
+ m_r[R2] = m_data->read_word(m_info.address);
+ // TRAPS: C,U,A,P,R,I
+ break;
+ case 0x70:
+ case 0x71:
+ // storw: store word
+ m_data->write_dword(m_info.address, m_r[R2]);
+ // TRAPS: A,P,W,I
+ break;
+ case 0x72:
+ case 0x73:
+ // tsts: test and set
+ m_r[R2] = m_data->read_dword(m_info.address);
+ m_data->write_dword(m_info.address, m_r[R2] | 0x80000000);
+ // TRAPS: C,U,A,P,R,W,I
+ break;
+ case 0x74:
+ case 0x75:
+ // stors: store single floating
+ m_data->write_dword(m_info.address, *((u32 *)&m_f[R2]));
+ // TRAPS: A,P,W,I
+ break;
+ case 0x76:
+ case 0x77:
+ // stord: store double floating
+ m_data->write_qword(m_info.address, *((u64 *)&m_f[R2]));
+ // TRAPS: A,P,W,I
+ break;
+ case 0x78:
+ case 0x79:
+ // storb: store byte
+ m_data->write_byte(m_info.address, (u8)m_r[R2]);
+ // TRAPS: A,P,W,I
+ break;
+
+ case 0x7c:
+ case 0x7d:
+ // storh: store halfword
+ m_data->write_word(m_info.address, (u16)m_r[R2]);
+ // TRAPS: A,P,W,I
+ break;
+
+ case 0x80:
+ // addw: add word
+ FLAGS_CV(C_ADD(m_r[R2], m_r[R1]), V_ADD(m_r[R2], m_r[R1]))
+ m_r[R2] += m_r[R1];
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+
+ case 0x82:
+ // addq: add quick
+ FLAGS_CV(C_ADD(m_r[R2], R1), V_ADD(m_r[R2], R1))
+ m_r[R2] += R1;
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+ case 0x83:
+ // addi: add immediate
+ FLAGS_CV(C_ADD(m_r[R2], m_info.imm), V_ADD(m_r[R2], m_info.imm))
+ m_r[R2] += m_info.imm;
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ // TRAPS: I
+ break;
+ case 0x84:
+ // movw: move word
+ m_r[R2] = m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ break;
+
+ case 0x86:
+ // loadq: load quick
+ m_r[R2] = R1;
+ FLAGS(0, 0, m_r[R2] == 0, 0)
+ // FLAGS: 00Z0
+ break;
+ case 0x87:
+ // loadi: load immediate
+ m_r[R2] = m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x88:
+ // andw: and word
+ m_r[R2] &= m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ break;
+
+ case 0x8b:
+ // andi: and immediate
+ m_r[R2] &= m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x8c:
+ // orw: or word
+ m_r[R2] |= m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ break;
+
+ case 0x8f:
+ // ori: or immediate
+ m_r[R2] |= m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0x90:
+ // addwc: add word with carry
+ FLAGS_CV(C_ADD(m_r[R2], (m_r[R1] + (PSW(C) ? 1 : 0))), V_ADD(m_r[R2], (m_r[R1] + (PSW(C) ? 1 : 0))))
+ m_r[R2] += m_r[R1] + (PSW(C) ? 1 : 0);
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+ case 0x91:
+ // subwc: subtract word with carry
+ FLAGS_CV(C_SUB(m_r[R2], (m_r[R1] + (PSW(C) ? 1 : 0))), V_SUB(m_r[R2], (m_r[R1] + (PSW(C) ? 1 : 0))))
+ m_r[R2] -= m_r[R1] + (PSW(C) ? 1 : 0);
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+
+ case 0x93:
+ // negw: negate word
+ FLAGS_CV(m_r[R1] != 0, m_r[R1] == INT32_MIN)
+ m_r[R2] = -m_r[R1];
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+
+ case 0x98:
+ // mulw: multiply word
+ m_r[R2] = m_r[R2] * m_r[R1];
+ // FLAGS: 0V00
+ break;
+ case 0x99:
+ // mulwx: multiply word extended
+ ((s64 *)m_r)[R2 >> 1] = (s64)m_r[R2] * (s64)m_r[R1];
+ // FLAGS: 0V00
+ break;
+ case 0x9a:
+ // mulwu: multiply word unsigned
+ m_r[R2] = (u32)m_r[R2] * (u32)m_r[R1];
+ // FLAGS: 0V00
+ break;
+ case 0x9b:
+ // mulwux: multiply word unsigned extended
+ ((u64 *)m_r)[R2 >> 1] = (u64)m_r[R2] * (u64)m_r[R1];
+ // FLAGS: 0V00
+ break;
+ case 0x9c:
+ // divw: divide word
+ if (m_r[R1] != 0)
+ {
+ FLAGS(0, m_r[R2] == INT32_MIN && m_r[R1] == -1, 0, 0)
+ m_r[R2] = m_r[R2] / m_r[R1];
+ }
+ else
+ next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
+ // FLAGS: 0V00
+ // TRAPS: D
+ break;
+ case 0x9d:
+ // modw: modulus word
+ if (m_r[R1] != 0)
+ {
+ FLAGS(0, m_r[R2] == INT32_MIN && m_r[R1] == -1, 0, 0)
+ m_r[R2] = m_r[R2] % m_r[R1];
+ }
+ else
+ next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
+ // FLAGS: 0V00
+ // TRAPS: D
+ break;
+ case 0x9e:
+ // divwu: divide word unsigned
+ if ((u32)m_r[R1] != 0)
+ m_r[R2] = (u32)m_r[R2] / (u32)m_r[R1];
+ else
+ next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
+ FLAGS(0, 0, 0, 0)
+ // FLAGS: 0000
+ // TRAPS: D
+ break;
+ case 0x9f:
+ // modwu: modulus word unsigned
+ if ((u32)m_r[R1] != 0)
+ m_r[R2] = (u32)m_r[R2] % (u32)m_r[R1];
+ else
+ next_pc = intrap(EXCEPTION_INTEGER_DIVIDE_BY_ZERO, next_pc, CTS_DIVIDE_BY_ZERO);
+ FLAGS(0, 0, 0, 0)
+ // FLAGS: 0000
+ // TRAPS: D
+ break;
+ case 0xa0:
+ // subw: subtract word
+ FLAGS_CV(C_SUB(m_r[R2], m_r[R1]), V_SUB(m_r[R2], m_r[R1]))
+ m_r[R2] -= m_r[R1];
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+
+ case 0xa2:
+ // subq: subtract quick
+ FLAGS_CV(C_SUB(m_r[R2], R1), V_SUB(m_r[R2], R1))
+ m_r[R2] -= R1;
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ break;
+ case 0xa3:
+ // subi: subtract immediate
+ FLAGS_CV(C_SUB(m_r[R2], m_info.imm), V_SUB(m_r[R2], m_info.imm))
+ m_r[R2] -= m_info.imm;
+ FLAGS_ZN(m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: CVZN
+ // TRAPS: I
+ break;
+ case 0xa4:
+ // cmpw: compare word
+ FLAGS(C_SUB(m_r[R2], m_r[R1]), V_SUB(m_r[R2], m_r[R1]), m_r[R2] == m_r[R1], m_r[R2] < m_r[R1])
+ // FLAGS: CVZN
+ break;
+
+ case 0xa6:
+ // cmpq: compare quick
+ FLAGS(C_SUB(m_r[R2], R1), V_SUB(m_r[R2], R1), m_r[R2] == (s32)R1, m_r[R2] < (s32)R1)
+ // FLAGS: CVZN
+ break;
+ case 0xa7:
+ // cmpi: compare immediate
+ FLAGS(C_SUB(m_r[R2], m_info.imm), V_SUB(m_r[R2], m_info.imm), m_r[R2] == m_info.imm, m_r[R2] < m_info.imm)
+ // FLAGS: CVZN
+ // TRAPS: I
+ break;
+ case 0xa8:
+ // xorw: exclusive or word
+ m_r[R2] ^= m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ break;
+
+ case 0xab:
+ // xori: exclusive or immediate
+ m_r[R2] ^= m_info.imm;
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: I
+ break;
+ case 0xac:
+ // notw: not word
+ m_r[R2] = ~m_r[R1];
+ FLAGS(0, 0, m_r[R2] == 0, m_r[R2] < 0)
+ // FLAGS: 00ZN
+ break;
+
+ case 0xae:
+ // notq: not quick
+ m_r[R2] = ~R1;
+ FLAGS(0, 0, 0, 1)
+ // FLAGS: 0001
+ break;
+
+#ifdef UNIMPLEMENTED_C400
+ case 0xb0:
+ // abss: absolute value single floating?
+ break;
+
+ case 0xb2:
+ // absd: absolute value double floating?
+ break;
+#endif
+
+ case 0xb4:
+ // unprivileged macro instructions
+ switch (m_info.subopcode)
+ {
+ 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:
+ // savew0..savew12: push registers rN:r14
+
+ // store ri at sp - 4 * (15 - i)
+ for (int i = R2; i < 15; i++)
+ m_data->write_dword(m_r[15] - 4 * (15 - i), m_r[i]);
+
+ // decrement sp after push to allow restart on exceptions
+ m_r[15] -= 4 * (15 - R2);
+ // TRAPS: A,P,W
+ break;
+ // NOTE: the movc, initc and cmpc macro instructions are implemented in a very basic way because
+ // at some point they will need to be improved to deal with possible exceptions (e.g. page faults)
+ // that may occur during execution. The implementation here is intended to allow the instructions
+ // to be "continued" after such exceptions.
+ case 0x0d:
+ // movc: copy r0 bytes from r1 to r2
+
+ while (m_r[0])
+ {
+ m_data->write_byte(m_r[2], m_data->read_byte(m_r[1]));
+
+ m_r[0]--;
+ m_r[1]++;
+ m_r[2]++;
+ }
+ // TRAPS: C,U,P,R,W
+ break;
+ case 0x0e:
+ // initc: initialise r0 bytes at r1 with value in r2
+ while (m_r[0])
+ {
+ m_data->write_byte(m_r[1], m_r[2] & 0xff);
+
+ m_r[0]--;
+ m_r[1]++;
+ m_r[2] = _rotr(m_r[2], 8);
+ }
+ // TRAPS: P,W
+ break;
+ case 0x0f:
+ // cmpc: compare r0 bytes at r1 with r2
+
+ // set condition codes assuming strings match
+ FLAGS(0, 0, 1, 0);
+
+ while (m_r[0])
+ {
+ // set condition codes and abort the loop if the current byte does not match
+ s32 byte1 = (s8)m_data->read_byte(m_r[1]);
+ s32 byte2 = (s8)m_data->read_byte(m_r[2]);
+ if (byte1 != byte2)
+ {
+ FLAGS(C_SUB(byte2, byte1), V_SUB(byte2, byte1), byte2 == byte1, byte2 < byte1)
+ break;
+ }
+
+ m_r[0]--;
+ m_r[1]++;
+ m_r[2]++;
+ }
+ // TRAPS: C,U,P,R
+ break;
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b:
+ case 0x1c:
+ // restwN..restw12: pop registers rN:r14
+
+ // load ri from sp + 4 * (i - N)
+ for (int i = R2; i < 15; i++)
+ m_r[i] = m_data->read_dword(m_r[15] + 4 * (i - R2));
+
+ // increment sp after pop to allow restart on exceptions
+ m_r[15] += 4 * (15 - R2);
+ // TRAPS: C,U,A,P,R
+ break;
+
+ case 0x20: case 0x21: case 0x22: case 0x23:
+ case 0x24: case 0x25: case 0x26: case 0x27:
+ // saved0..saved7: push registers fN:f7
+
+ // store fi at sp - 8 * (8 - i)
+ for (int i = R2; i < 8; i++)
+ m_data->write_qword(m_r[15] - 8 * (8 - i), m_f[i]);
+
+ // decrement sp after push to allow restart on exceptions
+ m_r[15] -= 8 * (8 - R2);
+ // TRAPS: A,P,W
+ break;
+ case 0x28: case 0x29: case 0x2a: case 0x2b:
+ case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ // restd0..restd7: pop registers fN:f7
+
+ // load fi from sp + 8 * (i - N)
+ for (int i = R2; i < 8; i++)
+ m_f[i] = m_data->read_qword(m_r[15] + 8 * (i - R2));
+
+ // increment sp after pop to allow restart on exceptions
+ m_r[15] += 8 * (8 - R2);
+ // TRAPS: C,U,A,P,R
+ break;
+#ifdef UNIMPLEMENTED
+ case 0x30:
+ // cnvsw
+ case 0x31:
+ // cnvrsw
+ // TRAPS: F_IX
+ case 0x32:
+ // cnvtsw
+ // TRAPS: F_IX
+ case 0x33:
+ // cnvws
+ // TRAPS: F_X
+ case 0x34:
+ // cnvdw
+ // TRAPS: F_IX
+ case 0x35:
+ // cnvrdw
+ // TRAPS: F_IX
+ break;
+#endif
+ case 0x36: // cnvtdw
+ m_r[m_info.macro & 0xf] = (s32)m_f[(m_info.macro >> 4) & 0xf];
+ // TRAPS: F_IX
+ break;
+ case 0x37: // cnvwd
+ m_f[m_info.macro & 0xf] = (double)m_r[(m_info.macro >> 4) & 0xf];
+ break;
+#ifdef UNIMPLEMENTED
+ case 0x38:
+ // cnvsd
+ // TRAPS: F_I
+ case 0x39:
+ // cnvds
+ // TRAPS: F_IVUX
+ case 0x3a:
+ // negs
+ case 0x3b:
+ // negds
+ case 0x3c:
+ // scalbs
+ // TRAPS: F_IVUX
+ case 0x3d:
+ // scalbd
+ // FLAGS: N
+ // TRAPS: F_IVUX
+ case 0x3e:
+ // trapfn
+ // TRAPS: I
+ case 0x3f:
+ // loadfs
+ break;
+#endif
+ default:
+ logerror("illegal unprivileged macro opcode at 0x%08x\n", m_pc);
+ next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
+ machine().debug_break();
+ break;
+ }
+
+ break;
+
+ case 0xb6:
+ // privileged macro instructions
+ if (!SSW(U))
+ {
+ switch (m_info.subopcode)
+ {
+ case 0x00:
+ // movus: move user to supervisor
+ m_rs[m_info.macro & 0xf] = m_ru[(m_info.macro >> 4) & 0xf];
+ FLAGS(0, 0, m_rs[m_info.macro & 0xf] == 0, m_rs[m_info.macro & 0xf] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: S
+ break;
+ case 0x01:
+ // movsu: move supervisor to user
+ m_ru[m_info.macro & 0xf] = m_rs[(m_info.macro >> 4) & 0xf];
+ FLAGS(0, 0, m_ru[m_info.macro & 0xf] == 0, m_ru[m_info.macro & 0xf] < 0)
+ // FLAGS: 00ZN
+ // TRAPS: S
+ break;
+ case 0x02:
+ // saveur: save user registers
+ for (int i = 0; i < 16; i++)
+ m_data->write_dword(m_rs[(m_info.macro >> 4) & 0xf] - 4 * (i + 1), m_ru[15 - i]);
+
+ m_rs[(m_info.macro >> 4) & 0xf] -= 64;
+ // TRAPS: A,P,W,S
+ break;
+ case 0x03:
+ // restur: restore user registers
+ for (int i = 0; i < 16; i++)
+ m_ru[i] = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4 * i);
+
+ m_rs[(m_info.macro >> 4) & 0xf] += 64;
+ // TRAPS: C,U,A,P,R,S
+ break;
+ case 0x04:
+ // reti: restore psw, ssw and pc from supervisor stack
+ LOG_INTERRUPT("reti r%d, ssp = %08x, pc = %08x, next_pc = %08x\n",
+ (macro >> 4) & 0xf, m_rs[(m_info.macro >> 4) & 0xf], m_pc, m_program->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 8));
+
+ m_psw = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 0);
+ m_ssw = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 4);
+ next_pc = m_data->read_dword(m_rs[(m_info.macro >> 4) & 0xf] + 8);
+
+ m_rs[(m_info.macro >> 4) & 0xf] += 12;
+
+ m_r = SSW(U) ? m_ru : m_rs;
+ // TRAPS: S
+ break;
+ case 0x05:
+ // wait: wait for interrupt
+ next_pc = m_pc;
+ // TRAPS: S
+ break;
+#ifdef UNIMPLEMENTED_C400
+ case 0x07:
+ // loadts: unknown?
+ break;
+#endif
+
+ default:
+ // illegal operation
+ logerror("illegal privileged macro opcode at 0x%08x\n", m_pc);
+ next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
+ machine().debug_break();
+ break;
+ }
+ }
+ else
+ next_pc = intrap(EXCEPTION_PRIVILEGED_INSTRUCTION, next_pc, CTS_PRIVILEGED_INSTRUCTION);
+ break;
+
+#ifdef UNIMPLEMENTED_C400
+ case 0xbc:
+ // waitd:
+ break;
+
+ case 0xc0:
+ // s*:
+ break;
+#endif
+
+ default:
+ logerror("illegal opcode at 0x%08x\n", m_pc);
+ next_pc = intrap(EXCEPTION_ILLEGAL_OPERATION, next_pc, CTS_ILLEGAL_OPERATION);
+ break;
+ }
+
+ return next_pc;
+}
+
+/*
+* Common entry point for transferring control in the event of an interrupt or exception.
+*/
+u32 clipper_device::intrap(u32 vector, u32 pc, u32 cts, u32 mts)
+{
+ LOG_INTERRUPT("intrap - vector %x, pc = 0x%08x, next_pc = 0x%08x, ssp = 0x%08x\n", vector, pc, m_program->read_dword(vector + 4), m_rs[15]);
+
+ // set cts and mts to indicate source of exception
+ m_psw = (m_psw & ~(PSW_CTS | PSW_MTS)) | mts | cts;
+
+ // push pc, psw and ssw onto supervisor stack
+ m_data->write_dword(m_rs[15] - 4, pc);
+ m_data->write_dword(m_rs[15] - 12, m_psw);
+ m_data->write_dword(m_rs[15] - 8, m_ssw);
+
+ // decrement supervisor stack pointer
+
+ // NOTE: while not explicitly stated anywhere, it seems the InterPro boot code has been
+ // developed with the assumption that the SSP is decremented by 24 bytes during an exception,
+ // rather than the 12 bytes that might otherwise be expected. This means the exception handler
+ // code must explicitly increment the SSP by 12 prior to executing the RETI instruction,
+ // as otherwise the SSP will not be pointing at a valid return frame. It's possible this
+ // behaviour might vary with some other version of the CPU, but this is all we know for now.
+ m_rs[15] -= 24;
+
+ // load ssw from trap vector and set previous mode
+ m_ssw = (m_data->read_dword(vector + 0) & ~SSW_P) | (SSW(U) << 1);
+
+ // clear psw
+ m_psw = 0;
+
+ m_r = SSW(U) ? m_ru : m_rs;
+
+ // return new pc from trap vector
+ return m_data->read_dword(vector + 4);
+}
+
+bool clipper_device::evaluate_branch ()
+{
+ switch (m_info.r2)
+ {
+ case BRANCH_T:
+ return true;
+
+ case BRANCH_LT:
+ return (!PSW(V) && !PSW(Z) && !PSW(N))
+ || (PSW(V) && !PSW(Z) && PSW(N));
+
+ case BRANCH_LE:
+ return (!PSW(V) && !PSW(N))
+ || (PSW(V) && !PSW(Z) && PSW(N));
+
+ case BRANCH_EQ:
+ return PSW(Z) && !PSW(N);
+
+ case BRANCH_GT:
+ return (!PSW(V) && !PSW(Z) && PSW(N))
+ || (PSW(V) && !PSW(N));
+
+ case BRANCH_GE:
+ return (PSW(V) && !PSW(N))
+ || (!PSW(V) && !PSW(Z) && PSW(N))
+ || (PSW(Z) && !PSW(N));
+
+ case BRANCH_NE:
+ return (!PSW(Z))
+ || (PSW(Z) && PSW(N));
+
+ case BRANCH_LTU:
+ return (!PSW(C) && !PSW(Z));
+
+ case BRANCH_LEU:
+ return !PSW(C);
+
+ case BRANCH_GTU:
+ return PSW(C);
+
+ case BRANCH_GEU:
+ return PSW(C) || PSW(Z);
+
+ case BRANCH_V:
+ return PSW(V);
+ case BRANCH_NV:
+ return !PSW(V);
+
+ case BRANCH_N:
+ return !PSW(Z) && PSW(N);
+ case BRANCH_NN:
+ return !PSW(N);
+
+ case BRANCH_FN:
+ return PSW(Z) && PSW(N);
+ }
+
+ return false;
+}
+
+offs_t clipper_device::disasm_disassemble(std::ostream &stream, offs_t pc, const u8 *oprom, const u8 *opram, u32 options)
+{
+ return CPU_DISASSEMBLE_NAME(clipper)(this, stream, pc, oprom, opram, options);
+}
diff --git a/src/devices/cpu/clipper/clipper.h b/src/devices/cpu/clipper/clipper.h
new file mode 100644
index 00000000000..be2c5e2603f
--- /dev/null
+++ b/src/devices/cpu/clipper/clipper.h
@@ -0,0 +1,294 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+#pragma once
+
+#ifndef __CLIPPER_H__
+#define __CLIPPER_H__
+
+enum clipper_registers
+{
+ CLIPPER_R0, CLIPPER_R1, CLIPPER_R2, CLIPPER_R3, CLIPPER_R4, CLIPPER_R5, CLIPPER_R6, CLIPPER_R7,
+ CLIPPER_R8, CLIPPER_R9, CLIPPER_R10, CLIPPER_R11, CLIPPER_R12, CLIPPER_R13, CLIPPER_R14, CLIPPER_R15,
+
+ CLIPPER_F0, CLIPPER_F1, CLIPPER_F2, CLIPPER_F3, CLIPPER_F4, CLIPPER_F5, CLIPPER_F6, CLIPPER_F7,
+ CLIPPER_F8, CLIPPER_F9, CLIPPER_F10, CLIPPER_F11, CLIPPER_F12, CLIPPER_F13, CLIPPER_F14, CLIPPER_F15,
+
+ CLIPPER_PSW,
+ CLIPPER_SSW,
+ CLIPPER_PC,
+};
+
+enum clipper_addressing_modes
+{
+ ADDR_MODE_PC32 = 0x10,
+ ADDR_MODE_ABS32 = 0x30,
+ ADDR_MODE_REL32 = 0x60,
+ ADDR_MODE_PC16 = 0x90,
+ ADDR_MODE_REL12 = 0xa0,
+ ADDR_MODE_ABS16 = 0xb0,
+ ADDR_MODE_PCX = 0xd0,
+ ADDR_MODE_RELX = 0xe0,
+};
+
+// branch conditions
+enum clipper_branch_conditions
+{
+ BRANCH_T = 0x0,
+ BRANCH_LT = 0x1,
+ BRANCH_LE = 0x2,
+ BRANCH_EQ = 0x3,
+ BRANCH_GT = 0x4,
+ BRANCH_GE = 0x5,
+ BRANCH_NE = 0x6,
+ BRANCH_LTU = 0x7,
+ BRANCH_LEU = 0x8,
+ BRANCH_GTU = 0x9,
+ BRANCH_GEU = 0xa,
+ BRANCH_V = 0xb,
+ BRANCH_NV = 0xc,
+ BRANCH_N = 0xd,
+ BRANCH_NN = 0xe,
+ BRANCH_FN = 0xf,
+};
+
+enum clipper_psw
+{
+ PSW_N = 0x00000001, // negative
+ PSW_Z = 0x00000002, // zero
+ PSW_V = 0x00000004, // overflow
+ PSW_C = 0x00000008, // carry out or borrow in
+ PSW_FX = 0x00000010, // floating inexact
+ PSW_FU = 0x00000020, // floating underflow
+ PSW_FD = 0x00000040, // floating divide by zero
+ PSW_FV = 0x00000080, // floating overflow
+ PSW_FI = 0x00000100, // floating invalid operation
+ PSW_EFX = 0x00000200, // enable floating inexact trap
+ PSW_EFU = 0x00000400, // enable floating underflow trap
+ PSW_EFD = 0x00000800, // enable floating divide by zero trap
+ PSW_EFV = 0x00001000, // enable floating overflow trap
+ PSW_EFI = 0x00002000, // enable floating invalid operation trap
+ PSW_EFT = 0x00004000, // enable floating trap
+ PSW_FR = 0x00018000, // floating rounding mode (2 bits)
+ // unused (3 bits)
+ PSW_DSP = 0x00300000, // c400 - delay slot pointer (2 bits)
+ PSW_BIG = 0x00400000, // c400 - big endian (hardware)
+ PSW_T = 0x00800000, // trace trap
+ PSW_CTS = 0x0f000000, // cpu trap status (4 bits)
+ PSW_MTS = 0xf0000000, // memory trap status (4 bits)
+};
+
+enum clipper_ssw
+{
+ SSW_IN = 0x0000000f, // interrupt number (4 bits)
+ SSW_IL = 0x000000f0, // interrupt level (4 bits)
+ SSW_EI = 0x00000100, // enable interrupts
+ SSW_ID = 0x0001fe00, // cpu rev # and type (8 bits)
+ // unused (5 bits)
+ SSW_FRD = 0x00400000, // floating registers dirty
+ SSW_TP = 0x00800000, // trace trap pending
+ SSW_ECM = 0x01000000, // enabled corrected memory error
+ SSW_DF = 0x02000000, // fpu disabled
+ SSW_M = 0x04000000, // mapped mode
+ SSW_KU = 0x08000000, // user protect key
+ SSW_UU = 0x10000000, // user data mode
+ SSW_K = 0x20000000, // protect key
+ SSW_U = 0x40000000, // user mode
+ SSW_P = 0x80000000, // previous mode
+};
+
+enum clipper_exception_vectors
+{
+ // data memory trap group
+ EXCEPTION_D_CORRECTED_MEMORY_ERROR = 0x108,
+ EXCEPTION_D_UNCORRECTABLE_MEMORY_ERROR = 0x110,
+ EXCEPTION_D_ALIGNMENT_FAULT = 0x120,
+ EXCEPTION_D_PAGE_FAULT = 0x128,
+ EXCEPTION_D_READ_PROTECT_FAULT = 0x130,
+ EXCEPTION_D_WRITE_PROTECT_FAULT = 0x138,
+
+ // floating-point arithmetic trap group
+ EXCEPTION_FLOATING_INEXACT = 0x180,
+ EXCEPTION_FLOATING_UNDERFLOW = 0x188,
+ EXCEPTION_FLOATING_DIVIDE_BY_ZERO = 0x190,
+ EXCEPTION_FLOATING_OVERFLOW = 0x1a0,
+ EXCEPTION_FLOATING_INVALID_OPERATION = 0x1c0,
+
+ // integer arithmetic trap group
+ EXCEPTION_INTEGER_DIVIDE_BY_ZERO = 0x208,
+
+ // instruction memory trap group
+ EXCEPTION_I_CORRECTED_MEMORY_ERROR = 0x288,
+ EXCEPTION_I_UNCORRECTABLE_MEMORY_ERROR = 0x290,
+ EXCEPTION_I_ALIGNMENT_FAULT = 0x2a0,
+ EXCEPTION_I_PAGE_FAULT = 0x2a8,
+ EXCEPTION_I_EXECUTE_PROTECT_FAULT = 0x2b0,
+
+ // illegal operation trap group
+ EXCEPTION_ILLEGAL_OPERATION = 0x300,
+ EXCEPTION_PRIVILEGED_INSTRUCTION = 0x308,
+
+ // diagnostic trap group
+ EXCEPTION_TRACE = 0x380,
+
+ // supervisor calls (0x400-0x7f8)
+ EXCEPTION_SUPERVISOR_CALL_BASE = 0x400,
+
+ // prioritized interrupts (0x800-0xff8)
+ EXCEPTION_INTERRUPT_BASE = 0x800,
+};
+
+// trap source values are shifted into the correct field in the psw
+enum clipper_cpu_trap_sources
+{
+ CTS_NO_CPU_TRAP = 0 << 24,
+ CTS_DIVIDE_BY_ZERO = 2 << 24,
+ CTS_ILLEGAL_OPERATION = 4 << 24,
+ CTS_PRIVILEGED_INSTRUCTION = 5 << 24,
+ CTS_TRACE_TRAP = 7 << 24,
+};
+
+enum clipper_memory_trap_sources
+{
+ MTS_NO_MEMORY_TRAP = 0 << 28,
+ MTS_CORRECTED_MEMORY_ERROR = 1 << 28,
+ MTS_UNCORRECTABLE_MEMORY_ERROR = 2 << 28,
+ MTS_ALIGNMENT_FAULT = 4 << 28,
+ MTS_PAGE_FAULT = 5 << 28,
+ MTS_READ_OR_EXECUTE_PROTECT_FAULT = 6 << 28,
+ MTS_WRITE_PROTECT_FAULT = 7 << 28,
+};
+
+// convenience macros for frequently used instruction fields
+#define R1 (m_info.r1)
+#define R2 (m_info.r2)
+
+// convenience macros for dealing with the psw
+#define PSW(mask) (m_psw & PSW_##mask)
+#define SSW(mask) (m_ssw & SSW_##mask)
+
+// macros for setting psw condition codes
+#define FLAGS(C,V,Z,N) \
+ m_psw = (m_psw & ~(PSW_C | PSW_V | PSW_Z | PSW_N)) | (((C) << 3) | ((V) << 2) | ((Z) << 1) | ((N) << 0));
+#define FLAGS_CV(C,V) \
+ m_psw = (m_psw & ~(PSW_C | PSW_V)) | (((C) << 3) | ((V) << 2));
+#define FLAGS_ZN(Z,N) \
+ m_psw = (m_psw & ~(PSW_Z | PSW_N)) | (((Z) << 1) | ((N) << 0));
+
+// over/underflow for addition/subtraction from here: http://stackoverflow.com/questions/199333/how-to-detect-integer-overflow-in-c-c
+#define OF_ADD(a, b) ((b > 0) && (a > INT_MAX - b))
+#define UF_ADD(a, b) ((b < 0) && (a < INT_MIN - b))
+#define OF_SUB(a, b) ((b < 0) && (a > INT_MAX + b))
+#define UF_SUB(a, b) ((b > 0) && (a < INT_MIN + b))
+
+// CLIPPER logic for carry and overflow flags
+#define C_ADD(a, b) ((u32)a + (u32)b < (u32)a)
+#define V_ADD(a, b) (OF_ADD((s32)a, (s32)b) || UF_ADD((s32)a, (s32)b))
+#define C_SUB(a, b) ((u32)a < (u32)b)
+#define V_SUB(a, b) (OF_SUB((s32)a, (s32)b) || UF_SUB((s32)a, (s32)b))
+
+class clipper_device : public cpu_device
+{
+public:
+ clipper_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, u32 clock, const char *shortname, const char *source);
+
+ DECLARE_READ_LINE_MEMBER(ssw) { return m_ssw; }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual u32 execute_min_cycles() const override { return 1; };
+ virtual u32 execute_max_cycles() const override { return 1; }; // FIXME: don't know, especially macro instructions
+ virtual u32 execute_input_lines() const override { return 2; }; // number of input/interrupt lines (irq/nmi)
+ virtual void execute_run() override;
+ virtual void execute_set_input(int inputnum, int state) override;
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const override;
+
+ // device_state_interface overrides
+#if 0
+ virtual void state_import(const device_state_entry &entry) override;
+ virtual void state_export(const device_state_entry &entry) override;
+#endif
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+ // device_disasm_interface overrides
+ virtual uint32_t disasm_min_opcode_bytes() const override { return 2; } // smallest instruction
+ virtual uint32_t disasm_max_opcode_bytes() const override { return 8; } // largest instruction
+ virtual offs_t disasm_disassemble(std::ostream &stream, offs_t pc, const u8 *oprom, const u8 *opram, u32 options) override;
+
+ // core registers
+ u32 m_pc;
+ u32 m_psw;
+ u32 m_ssw;
+
+ // integer registers
+ s32 *m_r; // active registers
+ s32 m_ru[16]; // user registers
+ s32 m_rs[16]; // supervisor registers
+
+ // floating registers
+ double m_f[16];
+
+private:
+ address_space_config m_insn_config;
+ address_space_config m_data_config;
+
+ address_space *m_insn;
+ address_space *m_data;
+
+ int m_icount;
+
+ int m_irq;
+ int m_nmi;
+
+ // decoded instruction information
+ struct
+ {
+ u8 opcode, subopcode;
+ u8 r1, r2;
+
+ s32 imm;
+ u16 macro;
+
+ // total size of instruction in bytes
+ u32 size;
+
+ // computed effective address
+ u32 address;
+ } m_info;
+
+ void clipper_device::decode_instruction(u16 insn);
+ int clipper_device::execute_instruction();
+ bool clipper_device::evaluate_branch();
+
+ uint32_t clipper_device::intrap(u32 vector, u32 pc, u32 cts = CTS_NO_CPU_TRAP, u32 mts = MTS_NO_MEMORY_TRAP);
+};
+
+class clipper_c100_device : public clipper_device
+{
+public:
+ clipper_c100_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+class clipper_c300_device : public clipper_device
+{
+public:
+ clipper_c300_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+class clipper_c400_device : public clipper_device
+{
+public:
+ clipper_c400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+extern const device_type CLIPPER_C100;
+extern const device_type CLIPPER_C300;
+extern const device_type CLIPPER_C400;
+
+extern CPU_DISASSEMBLE(clipper);
+#endif /* __CLIPPER_H__ */
diff --git a/src/devices/cpu/clipper/clipperd.cpp b/src/devices/cpu/clipper/clipperd.cpp
new file mode 100644
index 00000000000..735804b2742
--- /dev/null
+++ b/src/devices/cpu/clipper/clipperd.cpp
@@ -0,0 +1,351 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#include "emu.h"
+
+/*
+ * TODO
+ * - dynamically switch between C400 and C100/C300 instruction sets
+ * - handle failures of addressing mode decoding more elegantly
+ * - improve address decoding to use streams
+ * - detect various cases of illegal instruction encoding
+ */
+
+// enable C400 instruction decoding
+#define C400_INSTRUCTIONS 1
+
+// the CLIPPER addressing modes (unshifted)
+enum
+{
+ ADDR_MODE_PC32 = 0x10,
+ ADDR_MODE_ABS32 = 0x30,
+ ADDR_MODE_REL32 = 0x60,
+ ADDR_MODE_PC16 = 0x90,
+ ADDR_MODE_REL12 = 0xa0,
+ ADDR_MODE_ABS16 = 0xb0,
+ ADDR_MODE_PCX = 0xd0,
+ ADDR_MODE_RELX = 0xe0
+};
+
+// macros for decoding various operand fields
+#define R1 ((insn[0] & 0x00f0) >> 4)
+#define R2 (insn[0] & 0x000f)
+
+#define I16 ((int16_t)insn[1])
+#define I32 (*(int32_t *)&insn[1])
+#define IMM_VALUE (insn[0] & 0x0080 ? I16 : I32)
+#define IMM_SIZE (insn[0] & 0x0080 ? 2 : 4)
+
+#define ADDR_MODE (insn[0] & 0x00f0)
+#define ADDR_R2 ((insn[0] & 0x0050) == 0x0010 ? (insn[0] & 0x000f) : (insn[1] & 0x000f))
+#define ADDR_SIZE (ADDR_MODE > ADDR_MODE_REL32 ? 2 : ADDR_MODE == ADDR_MODE_REL32 ? 6 : 4)
+#define ADDR_RX ((insn[1] & 0xf0) >> 4)
+#define ADDR_I12 (((int16_t)insn[1]) >> 4)
+
+/*
+ * Branch condition code mnemonics - the forms beginning with 'c' are
+ * supposed to be used for branches following comparison instructions,
+ * while those beginning with 'r' are for use after move or logical
+ * instructions. We use the first form because we can't know which type
+ * should be used without some kind of dynamic information.
+ */
+static const char *const cc[] =
+{
+ "",
+ "clt", // rgt
+ "cle", // rge
+ "ceq", // req
+ "cgt", // rlt
+ "cge", // rle
+ "cne", // rne
+ "cltu", // rgtu
+ "cleu", // rgeu, nc
+ "cgtu", // rltu, c
+ "cgeu", // rleu
+ "v",
+ "nv",
+ "n",
+ "nn",
+ "fn"
+};
+
+/*
+ * Decode an addressing mode into a string.
+ */
+char *address (offs_t pc, u16 *insn)
+{
+ static char buffer[32];
+
+ switch (ADDR_MODE)
+ {
+ case ADDR_MODE_PC32: sprintf(buffer, "0x%x", pc + I32); break;
+ case ADDR_MODE_ABS32: sprintf(buffer, "0x%x", I32); break;
+ case ADDR_MODE_REL32: sprintf(buffer, "%d(r%d)", *(int32_t *)&insn[2], R2); break;
+ case ADDR_MODE_PC16: sprintf(buffer, "0x%x", pc + I16); break;
+ case ADDR_MODE_REL12: sprintf(buffer, "%d(r%d)", ADDR_I12, R2); break;
+ case ADDR_MODE_ABS16: sprintf(buffer, "0x%x", I16); break;
+ case ADDR_MODE_PCX: sprintf(buffer, "[r%d](pc)", ADDR_RX); break;
+ case ADDR_MODE_RELX: sprintf(buffer, "[r%d](r%d)", ADDR_RX, R2); break;
+ default: sprintf(buffer, "ERROR"); break;
+ }
+
+ return buffer;
+}
+
+/*
+ * CLIPPER instructions are composed of 1, 2, 3 or 4 16-bit "parcels". The first parcel contains
+ * the opcode and enough information to work out how many additional parcels might be required. The
+ * instruction set is fairly typically RISC-ish, except for these variable length instructions, the
+ * 8 addressing modes, and the "macro instructions", which are actually "subroutines" embedded in
+ * an on-CPU macro instruction ROM. It appears at least some of these macro instructions were removed
+ * from the C400 and generate traps which can be used to implement them in software instead.
+ */
+CPU_DISASSEMBLE(clipper)
+{
+ u16 *insn = (u16 *)oprom;
+ u32 flags = DASMFLAG_SUPPORTED;
+ offs_t bytes;
+
+ switch (insn[0] >> 8)
+ {
+ case 0x00:
+ if (oprom[0] == 0)
+ util::stream_format(stream, "noop");
+ else
+ util::stream_format(stream, "noop $%d", oprom[0]);
+ bytes = 2;
+ break;
+
+ case 0x10: util::stream_format(stream, "movwp r%d,%s", R2, R1 == 0 ? "psw" : R1 == 1 ? "ssw" : "sswf"); bytes = 2; break;
+ case 0x11: util::stream_format(stream, "movpw %s,r%d", R1 == 0 ? "psw" : "ssw", R2); bytes = 2; break;
+ case 0x12: util::stream_format(stream, "calls $%d", insn[0] & 0x7F); bytes = 2; flags |= DASMFLAG_STEP_OVER; break;
+ case 0x13: util::stream_format(stream, "ret r%d", R2); bytes = 2; flags |= DASMFLAG_STEP_OUT; break;
+ case 0x14: util::stream_format(stream, "pushw r%d,r%d", R2, R1); bytes = 2; break;
+
+ case 0x16: util::stream_format(stream, "popw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x20: util::stream_format(stream, "adds f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x21: util::stream_format(stream, "subs f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x22: util::stream_format(stream, "addd f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x23: util::stream_format(stream, "subd f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x24: util::stream_format(stream, "movs f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x25: util::stream_format(stream, "cmps f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x26: util::stream_format(stream, "movd f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x27: util::stream_format(stream, "cmpd f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x28: util::stream_format(stream, "muls f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x29: util::stream_format(stream, "divs f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x2a: util::stream_format(stream, "muld f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x2b: util::stream_format(stream, "divd f%d,f%d", R1, R2); bytes = 2; break;
+ case 0x2c: util::stream_format(stream, "movsw f%d,r%d", R1, R2); bytes = 2; break;
+ case 0x2d: util::stream_format(stream, "movws r%d,f%d", R1, R2); bytes = 2; break;
+ case 0x2e: util::stream_format(stream, "movdl f%d,r%d:%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+ case 0x2f: util::stream_format(stream, "movld r%d:r%d,f%d", R1 + 0, R1 + 1, R2); bytes = 2; break;
+
+ case 0x30: util::stream_format(stream, "shaw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x31: util::stream_format(stream, "shal r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+ case 0x32: util::stream_format(stream, "shlw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x33: util::stream_format(stream, "shll r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+ case 0x34: util::stream_format(stream, "rotw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x35: util::stream_format(stream, "rotl r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+
+ case 0x38: util::stream_format(stream, "shai $%d,r%d", I16, R2); bytes = 4; break;
+ case 0x39: util::stream_format(stream, "shali $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break;
+ case 0x3a: util::stream_format(stream, "shli $%d,r%d", I16, R2); bytes = 4; break;
+ case 0x3b: util::stream_format(stream, "shlli $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break;
+ case 0x3c: util::stream_format(stream, "roti $%d,r%d", I16, R2); bytes = 4; break;
+ case 0x3d: util::stream_format(stream, "rotli $%d,r%d:r%d", I16, R2 + 0, R2 + 1); bytes = 4; break;
+
+ case 0x44: util::stream_format(stream, "call r%d,(r%d)", R2, R1); bytes = 2; flags |= DASMFLAG_STEP_OVER; break;
+ case 0x45: util::stream_format(stream, "call r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; flags |= DASMFLAG_STEP_OVER; break;
+#if C400_INSTRUCTIONS
+ case 0x46: util::stream_format(stream, "loadd2 (r%d),f%d", R1, R2); bytes = 2; break;
+ case 0x47: util::stream_format(stream, "loadd2 %s,f%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+#endif
+ case 0x48: util::stream_format(stream, "b%-4s (r%d)", cc[R2], R1); bytes = 2; break;
+ case 0x49: util::stream_format(stream, "b%-4s %s", cc[ADDR_R2], address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+#if C400_INSTRUCTIONS
+ // delayed branches
+ case 0x4a: util::stream_format(stream, "cdb r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x4b: util::stream_format(stream, "cdb r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x4c: util::stream_format(stream, "cdbeq r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x4d: util::stream_format(stream, "cdbeq r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x4e: util::stream_format(stream, "cdbne r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x4f: util::stream_format(stream, "cdbne r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x50: util::stream_format(stream, "db%-4s (r%d)", cc[R2], R1); bytes = 2; break;
+ case 0x51: util::stream_format(stream, "db%-4s %s", cc[ADDR_R2], address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+#else
+ // these instructions are in the C300 documentation, but appear to be replaced in the C400
+ case 0x4c: util::stream_format(stream, "bf%s (r%d)", R2 == 0 ? "any" : "bad", R1); bytes = 2; break;
+ case 0x4d: util::stream_format(stream, "bf%s %s", ADDR_R2 == 0 ? "any" : "bad", address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+#endif
+
+ case 0x60: util::stream_format(stream, "loadw (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x61: util::stream_format(stream, "loadw %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x62: util::stream_format(stream, "loada (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x63: util::stream_format(stream, "loada %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x64: util::stream_format(stream, "loads (r%d),f%d", R1, R2); bytes = 2; break;
+ case 0x65: util::stream_format(stream, "loads %s,f%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x66: util::stream_format(stream, "loadd (r%d),f%d", R1, R2); bytes = 2; break;
+ case 0x67: util::stream_format(stream, "loadd %s,f%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x68: util::stream_format(stream, "loadb (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x69: util::stream_format(stream, "loadb %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x6a: util::stream_format(stream, "loadbu (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x6b: util::stream_format(stream, "loadbu %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x6c: util::stream_format(stream, "loadh (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x6d: util::stream_format(stream, "loadh %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x6e: util::stream_format(stream, "loadhu (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x6f: util::stream_format(stream, "loadhu %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+
+ case 0x70: util::stream_format(stream, "storw r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x71: util::stream_format(stream, "storw r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x72: util::stream_format(stream, "tsts (r%d),r%d", R1, R2); bytes = 2; break;
+ case 0x73: util::stream_format(stream, "tsts %s,r%d", address(pc, insn), ADDR_R2); bytes = 2 + ADDR_SIZE; break;
+ case 0x74: util::stream_format(stream, "stors f%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x75: util::stream_format(stream, "stors f%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x76: util::stream_format(stream, "stord f%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x77: util::stream_format(stream, "stord f%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+ case 0x78: util::stream_format(stream, "storb r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x79: util::stream_format(stream, "storb r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+
+ case 0x7c: util::stream_format(stream, "storh r%d,(r%d)", R2, R1); bytes = 2; break;
+ case 0x7d: util::stream_format(stream, "storh r%d,%s", ADDR_R2, address(pc, insn)); bytes = 2 + ADDR_SIZE; break;
+
+ case 0x80: util::stream_format(stream, "addw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x82: util::stream_format(stream, "addq $%d,r%d", R1, R2); bytes = 2; break;
+ case 0x83: util::stream_format(stream, "addi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0x84: util::stream_format(stream, "movw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x86: util::stream_format(stream, "loadq $%d,r%d", R1, R2); bytes = 2; break;
+ case 0x87: util::stream_format(stream, "loadi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0x88: util::stream_format(stream, "andw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x8b: util::stream_format(stream, "andi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0x8c: util::stream_format(stream, "orw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x8f: util::stream_format(stream, "ori $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+
+ case 0x90: util::stream_format(stream, "addwc r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x91: util::stream_format(stream, "subwc r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x93: util::stream_format(stream, "negw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0x98: util::stream_format(stream, "mulw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x99: util::stream_format(stream, "mulwx r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+ case 0x9a: util::stream_format(stream, "mulwu r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x9b: util::stream_format(stream, "mulwux r%d,r%d:r%d", R1, R2 + 0, R2 + 1); bytes = 2; break;
+ case 0x9c: util::stream_format(stream, "divw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x9d: util::stream_format(stream, "modw r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x9e: util::stream_format(stream, "divwu r%d,r%d", R1, R2); bytes = 2; break;
+ case 0x9f: util::stream_format(stream, "modwu r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0xa0: util::stream_format(stream, "subw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0xa2: util::stream_format(stream, "subq $%d,r%d", R1, R2); bytes = 2; break;
+ case 0xa3: util::stream_format(stream, "subi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0xa4: util::stream_format(stream, "cmpw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0xa6: util::stream_format(stream, "cmpq $%d,r%d", R1, R2); bytes = 2; break;
+ case 0xa7: util::stream_format(stream, "cmpi $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0xa8: util::stream_format(stream, "xorw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0xab: util::stream_format(stream, "xori $%d,r%d", IMM_VALUE, R2); bytes = 2 + IMM_SIZE; break;
+ case 0xac: util::stream_format(stream, "notw r%d,r%d", R1, R2); bytes = 2; break;
+
+ case 0xae: util::stream_format(stream, "notq $%d,r%d", R1, R2); bytes = 2; break;
+
+#if C400_INSTRUCTIONS
+ case 0xb0: util::stream_format(stream, "abss f%d,f%d", R1, R2); bytes = 2; break;
+ case 0xb2: util::stream_format(stream, "absd f%d,f%d", R1, R2); bytes = 2; break;
+#endif
+
+ case 0xb4:
+ case 0xb5:
+ // unprivileged macro instructions
+ switch (insn[0] & 0xff)
+ {
+ 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:
+ util::stream_format(stream, "savew%d", R2);
+ break;
+
+ case 0x0d: util::stream_format(stream, "movc"); break;
+ case 0x0e: util::stream_format(stream, "initc"); break;
+ case 0x0f: util::stream_format(stream, "cmpc"); break;
+
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b:
+ case 0x1c:
+ util::stream_format(stream, "restw%d", R2);
+ break;
+
+ case 0x20: case 0x21: case 0x22: case 0x23:
+ case 0x24: case 0x25: case 0x26: case 0x27:
+ util::stream_format(stream, "saved%d", R2);
+ break;
+
+ case 0x28: case 0x29: case 0x2a: case 0x2b:
+ case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ util::stream_format(stream, "restd%d", R2);
+ break;
+
+ case 0x30: util::stream_format(stream, "cnvsw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x31: util::stream_format(stream, "cnvrsw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x32: util::stream_format(stream, "cnvtsw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x33: util::stream_format(stream, "cnvws r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x34: util::stream_format(stream, "cnvdw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x35: util::stream_format(stream, "cnvrdw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x36: util::stream_format(stream, "cnvtdw f%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x37: util::stream_format(stream, "cnvwd r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x38: util::stream_format(stream, "cnvsd f%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x39: util::stream_format(stream, "cnvds f%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x3a: util::stream_format(stream, "negs f%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x3b: util::stream_format(stream, "negd f%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x3c: util::stream_format(stream, "scalbs r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x3d: util::stream_format(stream, "scalbd r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x3e: util::stream_format(stream, "trapfn"); break;
+ case 0x3f: util::stream_format(stream, "loadfs r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+
+ default:
+ util::stream_format(stream, "macro 0x%04x 0x%04x", insn[0], insn[1]);
+ break;
+ }
+ bytes = 4;
+ break;
+ case 0xb6:
+ case 0xb7:
+ // privileged macro instructions
+ switch (insn[0] & 0xff)
+ {
+ case 0x00: util::stream_format(stream, "movus r%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x01: util::stream_format(stream, "movsu r%d,r%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+ case 0x02: util::stream_format(stream, "saveur r%d", (insn[1] & 0xf0) >> 4); break;
+ case 0x03: util::stream_format(stream, "restur r%d", (insn[1] & 0xf0) >> 4); break;
+ case 0x04: util::stream_format(stream, "reti r%d", (insn[1] & 0xf0) >> 4); flags |= DASMFLAG_STEP_OUT; break;
+ case 0x05: util::stream_format(stream, "wait"); break;
+#if C400_INSTRUCTIONS
+ case 0x07: util::stream_format(stream, "loadts r%d,f%d", (insn[1] & 0xf0) >> 4, insn[1] & 0xf); break;
+#endif
+ default:
+ util::stream_format(stream, "macro 0x%04x 0x%04x", insn[0], insn[1]);
+ break;
+ }
+ bytes = 4;
+ break;
+
+#if C400_INSTRUCTIONS
+ case 0xbc: util::stream_format(stream, "waitd"); bytes = 2; break;
+ case 0xc0: util::stream_format(stream, "s%-4s r%d", cc[R2], R1); bytes = 2; break;
+#endif
+
+ default:
+ util::stream_format(stream, ".word 0x%04x", insn[0]);
+ bytes = 2;
+ break;
+ }
+
+ return bytes | flags;
+} \ No newline at end of file
diff --git a/src/mame/drivers/interpro.cpp b/src/mame/drivers/interpro.cpp
new file mode 100644
index 00000000000..03889931734
--- /dev/null
+++ b/src/mame/drivers/interpro.cpp
@@ -0,0 +1,345 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#include "includes/interpro.h"
+#include "debugger.h"
+
+#define VERBOSE 0
+#if VERBOSE
+#define LOG_SYSTEM(...) logerror(__VA_ARGS__)
+#define LOG_IDPROM(...) logerror(__VA_ARGS__)
+#else
+#define LOG_SYSTEM(...) {}
+#define LOG_IDPROM(...) {}
+#endif
+
+// machine start
+void interpro_state::machine_start()
+{
+ m_system_reg[SREG_CTRL2] = CTRL2_COLDSTART | CTRL2_PWRENA | CTRL2_PWRUP;
+}
+
+void interpro_state::machine_reset()
+{
+ // flash rom requires the following values
+ m_system_reg[SREG_ERROR] = 0x00;
+ m_system_reg[SREG_STATUS] = 0x400;
+ m_system_reg[SREG_CTRL1] = CTRL1_FLOPRDY;
+}
+
+WRITE16_MEMBER(interpro_state::system_w)
+{
+ switch (offset)
+ {
+ case SREG_LED:
+ LOG_SYSTEM("LED value %d at pc 0x%08x\n", data, space.device().safe_pc());
+ break;
+
+ case SREG_STATUS: // not sure if writable?
+ break;
+
+ case SREG_CTRL1:
+ LOG_SYSTEM("system control register 1 write data 0x%x pc 0x%08x\n", data, space.device().safe_pc());
+
+ if ((data ^ m_system_reg[offset]) & CTRL1_LEDDP)
+ LOG_SYSTEM("LED decimal point %s\n", data & CTRL1_LEDDP ? "on" : "off");
+
+ m_system_reg[offset] = data;
+ break;
+
+ case SREG_CTRL2:
+ LOG_SYSTEM("system control register 2 write data 0x%x pc 0x%08x\n", data, space.device().safe_pc());
+ if (data & CTRL2_RESET)
+ {
+ m_system_reg[SREG_CTRL2] &= ~CTRL2_COLDSTART;
+
+ machine().schedule_soft_reset();
+ }
+ else
+ m_system_reg[offset] = data & 0x0f; // top four bits are not persistent
+ break;
+ }
+}
+
+READ16_MEMBER(interpro_state::system_r)
+{
+ LOG_SYSTEM("system register read offset %d pc 0x%08x\n", offset, space.device().safe_pc());
+ switch (offset)
+ {
+ case SREG_ERROR:
+ case SREG_STATUS:
+ case SREG_CTRL1:
+ case SREG_CTRL2:
+ default:
+ return m_system_reg[offset];
+ break;
+ }
+}
+
+READ32_MEMBER(interpro_state::idprom_r)
+{
+ LOG_IDPROM("idprom read offset 0x%x mask 0x%08x at 0x%08x\n", offset, mem_mask, space.device().safe_pc());
+
+ // abitrary fake number for now, not working properly
+ uint32_t speed = 70000000;
+
+ static uint8_t idprom[] = {
+ // module type id
+ 'M', 'P', 'C', 'B',
+ '*', '*', '*', '*',
+
+ // ECO bytes
+ 0x87, 0x65, 0x43, 0x21,
+ 0xbb, 0xcc, 0xdd, 0xee,
+
+ // the following 8 bytes are "feature bytes"
+ // the feature bytes contain a 32 bit word which is divided by 40000
+ // if they're empty, a default value of 50 000 000 is used
+ // perhaps this is a system speed (50MHz)?
+ 0x2, 0x34, 0x56, 0x78,
+ (speed >> 24) & 0xff, (speed >> 16) & 0xff, (speed >> 8) & 0xff, (speed >> 0) & 0xff,
+
+ // reserved bytes
+ 0xff, 0xff,
+
+ // family
+ // boot rom tests for family == 0x41 or 0x42
+ // if so, speed read from feature bytes 2 & 3
+ // if not, read speed from feature bytes 4-7
+ //0x41, 0x00, // 2800-series CPU
+ 0x24, 0x00, // 2000-series system board
+
+ // footprint and checksum
+ 0x55, 0xaa, 0x55, 0x00
+ };
+
+ switch (offset)
+ {
+ case 0x1f:
+ {
+ uint8_t sum = 0;
+
+ // compute the checksum (sum of all bytes must be == 0x00)
+ for (int i = 0; i < 0x20; i++)
+ sum += idprom[i];
+
+ return 0x100 - (sum & 0xff);
+ }
+
+ default:
+ return idprom[offset];
+ }
+}
+
+READ32_MEMBER(interpro_state::slot0_r)
+{
+ // a known graphics board idprom
+ static uint8_t slot0[] = {
+ 0x00, 0x00, 0x00, 0x00, '9', '6', '3', 'A', // board
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // eco
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // features
+ 0xff, 0xff, // reserved
+ 0x22, 0x00, // family
+ 0x55, 0xaa, 0x55, 0x00
+ };
+
+ return ((uint8_t *)&slot0)[offset % 32];
+}
+
+WRITE8_MEMBER(interpro_state::rtc_w)
+{
+ switch (offset)
+ {
+ case 0x00:
+ // write to RTC register
+ m_rtc->write(space, 1, data);
+ break;
+
+ case 0x40:
+ // set RTC read/write address
+ m_rtc->write(space, 0, data);
+ break;
+
+ default:
+ logerror("rtc: write to unknown offset 0x%02x data 0x%02x at pc 0x%08x\n", offset, data, space.device().safe_pc());
+ break;
+ }
+}
+
+READ8_MEMBER(interpro_state::rtc_r)
+{
+ switch (offset)
+ {
+ case 0x00:
+ // read from RTC register
+ return m_rtc->read(space, 1);
+
+ // read from InterPro system ID PROM (contains MAC address)
+ case 0x40: return 0x12;
+ case 0x41: return 0x34;
+ case 0x42: return 0x56;
+
+ default:
+ logerror("rtc: read from unknown offset 0x%02x at pc 0x%08x\n", offset, space.device().safe_pc());
+ return 0xff;
+ }
+}
+
+READ8_MEMBER(interpro_state::scsi_r)
+{
+ return m_scsi->read(space, offset >> 6, mem_mask);
+}
+
+WRITE8_MEMBER(interpro_state::scsi_w)
+{
+ m_scsi->write(space, offset >> 6, data, mem_mask);
+}
+
+// driver init
+DRIVER_INIT_MEMBER(interpro_state, ip2800)
+{
+ address_space &as = m_mmu->space(AS_1);
+}
+
+// these maps point the cpu virtual addresses to the mmu
+static ADDRESS_MAP_START(clipper_insn_map, AS_PROGRAM, 32, interpro_state)
+ AM_RANGE(0x00000000, 0xffffffff) AM_DEVREAD32(INTERPRO_MMU_TAG, cammu_device, mmu_r, 0xffffffff)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START(clipper_data_map, AS_DATA, 32, interpro_state)
+AM_RANGE(0x00000000, 0xffffffff) AM_DEVREADWRITE32(INTERPRO_MMU_TAG, cammu_device, mmu_r, mmu_w, 0xffffffff)
+ADDRESS_MAP_END
+
+// these maps represent the real main, i/o and boot spaces of the system
+static ADDRESS_MAP_START(interpro_main_map, AS_0, 32, interpro_state)
+ AM_RANGE(0x00000000, 0x00ffffff) AM_RAM // 16M RAM
+
+ AM_RANGE(0x7f100000, 0x7f11ffff) AM_ROM AM_REGION(INTERPRO_ROM_TAG, 0)
+ AM_RANGE(0x7f180000, 0x7f1bffff) AM_ROM AM_REGION(INTERPRO_EEPROM_TAG, 0)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START(interpro_io_map, AS_1, 32, interpro_state)
+ AM_RANGE(0x00000000, 0x00000fff) AM_DEVICE(INTERPRO_MMU_TAG, cammu_device, map)
+ AM_RANGE(0x00001000, 0x00001fff) AM_RAM
+
+ AM_RANGE(0x40000000, 0x4000003f) AM_DEVICE16(INTERPRO_MCGA_TAG, interpro_mcga_device, map, 0xffff)
+ AM_RANGE(0x4f007e00, 0x4f007eff) AM_DEVICE(INTERPRO_SGA_TAG, interpro_sga_device, map)
+
+ AM_RANGE(0x7f000100, 0x7f00011f) AM_DEVICE8(INTERPRO_FDC_TAG, n82077aa_device, map, 0xff)
+ // this is probably the srx arbiter ga
+ AM_RANGE(0x7f000200, 0x7f0002ff) AM_RAM
+ AM_RANGE(0x7f000300, 0x7f00030f) AM_READWRITE16(system_r, system_w, 0xffff)
+ AM_RANGE(0x7f000400, 0x7f00040f) AM_DEVREADWRITE8(INTERPRO_SCC1_TAG, scc85C30_device, ba_cd_inv_r, ba_cd_inv_w, 0xff)
+ AM_RANGE(0x7f000410, 0x7f00041f) AM_DEVREADWRITE8(INTERPRO_SCC2_TAG, scc85230_device, ba_cd_inv_r, ba_cd_inv_w, 0xff)
+ AM_RANGE(0x7f000500, 0x7f0006ff) AM_READWRITE8(rtc_r, rtc_w, 0xff)
+ AM_RANGE(0x7f000700, 0x7f00077f) AM_READ(idprom_r)
+ AM_RANGE(0x7f001000, 0x7f001fff) AM_READWRITE8(scsi_r, scsi_w, 0x0000ff00)
+
+ AM_RANGE(0x7f0fff00, 0x7f0fffff) AM_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, map)
+
+ AM_RANGE(0x08000000, 0x08000fff) AM_NOP // bogus
+ AM_RANGE(0x8f000000, 0x8f0fffff) AM_READ(slot0_r)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START(interpro_boot_map, AS_2, 32, interpro_state)
+ AM_RANGE(0x00000000, 0x00001fff) AM_RAM
+ADDRESS_MAP_END
+
+FLOPPY_FORMATS_MEMBER(interpro_state::floppy_formats)
+ FLOPPY_PC_FORMAT
+FLOPPY_FORMATS_END
+
+static SLOT_INTERFACE_START(interpro_floppies)
+ SLOT_INTERFACE("525dd", FLOPPY_525_DD)
+ SLOT_INTERFACE("35hd", FLOPPY_35_HD)
+SLOT_INTERFACE_END
+
+// input ports
+static INPUT_PORTS_START(ip2800)
+INPUT_PORTS_END
+
+static MACHINE_CONFIG_START(ip2800, interpro_state)
+ MCFG_CPU_ADD(INTERPRO_CPU_TAG, CLIPPER_C400, XTAL_10MHz)
+ MCFG_CPU_PROGRAM_MAP(clipper_insn_map)
+ MCFG_CPU_DATA_MAP(clipper_data_map)
+ MCFG_CPU_IRQ_ACKNOWLEDGE_DEVICE(INTERPRO_IOGA_TAG, interpro_ioga_device, inta_cb)
+
+ MCFG_DEVICE_ADD(INTERPRO_MMU_TAG, CAMMU_C4T, 0)
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, interpro_main_map)
+ MCFG_DEVICE_ADDRESS_MAP(AS_1, interpro_io_map)
+ MCFG_DEVICE_ADDRESS_MAP(AS_2, interpro_boot_map)
+ MCFG_CAMMU_SSW_CB(DEVREADLINE(INTERPRO_CPU_TAG, clipper_device, ssw))
+
+ // serial controllers and rs232 bus
+ MCFG_SCC85C30_ADD(INTERPRO_SCC1_TAG, XTAL_4_9152MHz, 0, 0, 0, 0)
+
+ MCFG_Z80SCC_OUT_TXDA_CB(DEVWRITELINE("rs232a", rs232_port_device, write_txd))
+ MCFG_Z80SCC_OUT_TXDB_CB(DEVWRITELINE("rs232b", rs232_port_device, write_txd))
+ MCFG_Z80SCC_OUT_INT_CB(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir11_w))
+
+ MCFG_RS232_PORT_ADD("rs232a", default_rs232_devices, nullptr)
+ MCFG_RS232_RXD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, rxa_w))
+ MCFG_RS232_DCD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, dcda_w))
+ MCFG_RS232_CTS_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, ctsa_w))
+
+ // the following port is known as "port 2"
+ MCFG_RS232_PORT_ADD("rs232b", default_rs232_devices, "terminal")
+ MCFG_RS232_RXD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, rxb_w))
+ MCFG_RS232_DCD_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, dcdb_w))
+ MCFG_RS232_CTS_HANDLER(DEVWRITELINE(INTERPRO_SCC1_TAG, z80scc_device, ctsb_w))
+
+ MCFG_SCC85230_ADD(INTERPRO_SCC2_TAG, XTAL_4_9152MHz, 0, 0, 0, 0)
+
+ // real-time clock/non-volatile memory
+ MCFG_MC146818_ADD(INTERPRO_RTC_TAG, XTAL_32_768kHz)
+ MCFG_MC146818_UTC(true)
+ MCFG_MC146818_IRQ_HANDLER(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir9_w))
+
+ // floppy
+ MCFG_N82077AA_ADD(INTERPRO_FDC_TAG, n82077aa_device::MODE_PS2)
+ MCFG_UPD765_INTRQ_CALLBACK(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir1_w))
+ MCFG_UPD765_DRQ_CALLBACK(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, drq_floppy))
+ MCFG_FLOPPY_DRIVE_ADD("fdc:0", interpro_floppies, "525dd", interpro_state::floppy_formats)
+ MCFG_FLOPPY_DRIVE_ADD("fdc:1", interpro_floppies, "35hd", interpro_state::floppy_formats)
+ MCFG_FLOPPY_DRIVE_SOUND(false)
+
+ // scsi
+ MCFG_DEVICE_ADD(INTERPRO_SCSI_TAG, SCSI_PORT, 0)
+ MCFG_SCSIDEV_ADD(INTERPRO_SCSI_TAG ":" SCSI_PORT_DEVICE1, "harddisk", SCSIHD, SCSI_ID_0)
+ MCFG_SCSIDEV_ADD(INTERPRO_SCSI_TAG ":" SCSI_PORT_DEVICE2, "cdrom", SCSICD, SCSI_ID_3)
+
+ MCFG_DEVICE_ADD(INTERPRO_SCSI_ADAPTER_TAG, NCR539X, XTAL_12_5MHz)
+ MCFG_LEGACY_SCSI_PORT(INTERPRO_SCSI_TAG)
+ MCFG_NCR539X_OUT_IRQ_CB(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, ir0_w))
+ MCFG_NCR539X_OUT_DRQ_CB(DEVWRITELINE(INTERPRO_IOGA_TAG, interpro_ioga_device, drq_scsi))
+
+ // i/o gate array
+ MCFG_INTERPRO_IOGA_ADD(INTERPRO_IOGA_TAG)
+ MCFG_INTERPRO_IOGA_NMI_CB(INPUTLINE(INTERPRO_CPU_TAG, INPUT_LINE_NMI))
+ MCFG_INTERPRO_IOGA_IRQ_CB(INPUTLINE(INTERPRO_CPU_TAG, INPUT_LINE_IRQ0))
+ //MCFG_INTERPRO_IOGA_DMA_CB(IOGA_DMA_CHANNEL_PLOTTER, unknown)
+ //MCFG_INTERPRO_IOGA_DMA_CB(IOGA_DMA_SCSI, DEVREAD8(INTERPRO_SCSI_TAG, ncr539x_device, dma_read_data), DEVWRITE8(INTERPRO_SCSI_TAG, ncr539x_device, dma_write_data))
+ MCFG_INTERPRO_IOGA_DMA_CB(IOGA_DMA_FLOPPY, DEVREAD8(INTERPRO_FDC_TAG, n82077aa_device, mdma_r), DEVWRITE8(INTERPRO_FDC_TAG, n82077aa_device, mdma_w))
+ MCFG_INTERPRO_IOGA_DMA_CB(IOGA_DMA_SERIAL, DEVREAD8(INTERPRO_SCC1_TAG, z80scc_device, da_r), DEVWRITE8(INTERPRO_SCC1_TAG, z80scc_device, da_w))
+ MCFG_INTERPRO_IOGA_FDCTC_CB(DEVWRITELINE(INTERPRO_FDC_TAG, n82077aa_device, tc_line_w))
+ MCFG_INTERPRO_IOGA_DMA_BUS(INTERPRO_CAMMU_TAG, AS_0)
+
+ // memory control gate array
+ MCFG_DEVICE_ADD(INTERPRO_MCGA_TAG, INTERPRO_MCGA, 0)
+
+ // srx gate array
+ MCFG_DEVICE_ADD(INTERPRO_SGA_TAG, INTERPRO_SGA, 0)
+
+MACHINE_CONFIG_END
+
+ROM_START(ip2800)
+ ROM_REGION(0x0020000, INTERPRO_ROM_TAG, 0)
+ ROM_SYSTEM_BIOS(0, "IP2830", "IP2830")
+ ROMX_LOAD("ip2830_rom.bin", 0x00000, 0x20000, CRC(467ce7bd) SHA1(53faee40d5df311f53b24c930e434cbf94a5c4aa), ROM_BIOS(1))
+
+ ROM_REGION(0x0040000, INTERPRO_EEPROM_TAG, 0)
+ ROM_LOAD_OPTIONAL("ip2830_eeprom.bin", 0x00000, 0x40000, CRC(a0c0899f) SHA1(dda6fbca81f9885a1a76ca3c25e80463a83a0ef7))
+ROM_END
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
+COMP( 1990, ip2800, 0, 0, ip2800, ip2800, interpro_state, ip2800, "Intergraph", "InterPro 2800", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
diff --git a/src/mame/includes/interpro.h b/src/mame/includes/interpro.h
new file mode 100644
index 00000000000..6ae135b552c
--- /dev/null
+++ b/src/mame/includes/interpro.h
@@ -0,0 +1,131 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#pragma once
+
+#ifndef INTERPRO_H_
+#define INTERPRO_H_
+
+#include "emu.h"
+
+#include "cpu/clipper/clipper.h"
+#include "machine/cammu.h"
+
+#include "machine/interpro_ioga.h"
+#include "machine/interpro_mcga.h"
+#include "machine/interpro_sga.h"
+
+#include "machine/z80scc.h"
+#include "machine/mc146818.h"
+#include "machine/upd765.h"
+#include "machine/ncr539x.h"
+
+#include "bus/scsi/scsi.h"
+#include "bus/scsi/scsicd.h"
+#include "bus/scsi/scsihd.h"
+#include "bus/rs232/rs232.h"
+
+#include "formats/pc_dsk.h"
+
+#define INTERPRO_CPU_TAG "cpu"
+#define INTERPRO_MMU_TAG "mmu"
+
+#define INTERPRO_RTC_TAG "rtc"
+#define INTERPRO_SCC1_TAG "scc1"
+#define INTERPRO_SCC2_TAG "scc2"
+#define INTERPRO_ROM_TAG "rom"
+#define INTERPRO_EEPROM_TAG "eeprom"
+#define INTERPRO_TERMINAL_TAG "terminal"
+#define INTERPRO_FDC_TAG "fdc"
+#define INTERPRO_SCSI_TAG "scsi"
+#define INTERPRO_IOGA_TAG "ioga"
+#define INTERPRO_MCGA_TAG "mcga"
+#define INTERPRO_SGA_TAG "sga"
+#define INTERPRO_SCSI_ADAPTER_TAG "adapter"
+
+// system board register offsets
+#define SREG_LED 0
+#define SREG_ERROR 0
+#define SREG_STATUS 1
+#define SREG_CTRL1 2
+#define SREG_CTRL2 3
+
+// control register 1
+#define CTRL1_FLOPLOW 0x0001
+#define CTRL1_FLOPRDY 0x0002
+#define CTRL1_LEDENA 0x0004
+#define CTRL1_LEDDP 0x0008
+#define CTRL1_ETHLOOP 0x0010
+#define CTRL1_ETHDTR 0x0020
+#define CTRL1_ETHRMOD 0x0040
+#define CTRL1_CLIPRESET 0x0040
+#define CTRL1_FIFOACTIVE 0x0080
+
+// control register 2
+#define CTRL2_PWRUP 0x0001
+#define CTRL2_PWRENA 0x0002
+#define CTRL2_HOLDOFF 0x0004
+#define CTRL2_EXTNMIENA 0x0008
+#define CTRL2_COLDSTART 0x0010
+#define CTRL2_RESET 0x0020
+#define CTRL2_BUSENA 0x0040
+#define CTRL2_FRCPARITY 0x0080
+#define CTRL2_FLASHEN 0x0080
+#define CTRL2_WMASK 0x000f
+
+class interpro_state : public driver_device
+{
+public:
+ interpro_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_maincpu(*this, INTERPRO_CPU_TAG),
+ m_mmu(*this, INTERPRO_MMU_TAG),
+ m_scc1(*this, INTERPRO_SCC1_TAG),
+ m_scc2(*this, INTERPRO_SCC2_TAG),
+ m_rtc(*this, INTERPRO_RTC_TAG),
+ m_fdc(*this, INTERPRO_FDC_TAG),
+ m_scsi(*this, INTERPRO_SCSI_ADAPTER_TAG),
+ m_ioga(*this, INTERPRO_IOGA_TAG),
+ m_mcga(*this, INTERPRO_MCGA_TAG),
+ m_sga(*this, INTERPRO_SGA_TAG)
+ { }
+
+ required_device<clipper_device> m_maincpu;
+ required_device<cammu_device> m_mmu;
+
+ // FIXME: not sure which one is the escc
+ required_device<z80scc_device> m_scc1;
+ required_device<z80scc_device> m_scc2;
+ required_device<mc146818_device> m_rtc;
+ required_device<n82077aa_device> m_fdc;
+ required_device<ncr539x_device> m_scsi;
+
+ required_device<interpro_ioga_device> m_ioga;
+ required_device<interpro_mcga_device> m_mcga;
+ required_device<interpro_sga_device> m_sga;
+
+ DECLARE_DRIVER_INIT(ip2800);
+
+ DECLARE_WRITE16_MEMBER(system_w);
+ DECLARE_READ16_MEMBER(system_r);
+
+ DECLARE_WRITE8_MEMBER(rtc_w);
+ DECLARE_READ8_MEMBER(rtc_r);
+
+ DECLARE_READ32_MEMBER(idprom_r);
+ DECLARE_READ32_MEMBER(slot0_r);
+
+ DECLARE_READ8_MEMBER(scsi_r);
+ DECLARE_WRITE8_MEMBER(scsi_w);
+
+ DECLARE_FLOPPY_FORMATS(floppy_formats);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ uint16_t m_system_reg[4];
+};
+
+#endif \ No newline at end of file
diff --git a/src/mame/machine/cammu.cpp b/src/mame/machine/cammu.cpp
new file mode 100644
index 00000000000..e43896e1d0f
--- /dev/null
+++ b/src/mame/machine/cammu.cpp
@@ -0,0 +1,180 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the Fairchild/Intergraph Cache and Memory Management Unit (CAMMU) designed for use with the CLIPPER CPU family.
+ *
+ * Primary reference: http://bitsavers.trailing-edge.com/pdf/fairchild/clipper/CLIPPER%20C300%2032-Bit%20Compute%20Engine.pdf
+ * Another reference: http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-289.pdf
+ *
+ * This implementation is currently at a very early stage, and is only sufficient to handle the bare minimum of boot/diagnostic code.
+ *
+ * TODO
+ * - almost everything
+ * - map registers
+ * - refactor hardware tlb
+ * - address translation
+ * - faults
+ * - tlb
+ * - cache
+ * - bus errors
+ */
+
+#include "cammu.h"
+
+#define VERBOSE 0
+
+// each variant of the cammu has different registers and a different addressing map
+// TODO: decode the cammu registers properly
+DEVICE_ADDRESS_MAP_START(map, 32, cammu_c4t_device)
+ AM_RANGE(0x000, 0xfff) AM_READWRITE(cammu_r, cammu_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 32, cammu_c4i_device)
+ AM_RANGE(0x000, 0xfff) AM_READWRITE(cammu_r, cammu_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 32, cammu_c3_device)
+ AM_RANGE(0x000, 0xfff) AM_READWRITE(cammu_r, cammu_w)
+ADDRESS_MAP_END
+
+const device_type CAMMU_C4T = &device_creator<cammu_c4t_device>;
+const device_type CAMMU_C4I = &device_creator<cammu_c4i_device>;
+const device_type CAMMU_C3 = &device_creator<cammu_c3_device>;
+
+cammu_c4t_device::cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_device(mconfig, CAMMU_C4T, "C4E/C4T CAMMU", tag, owner, clock, "C4T", __FILE__) { }
+
+cammu_c4i_device::cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_device(mconfig, CAMMU_C4I, "C4I CAMMU", tag, owner, clock, "C4I", __FILE__) { }
+
+cammu_c3_device::cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_device(mconfig, CAMMU_C4T, "C1/C3 CAMMU", tag, owner, clock, "C3", __FILE__) { }
+
+cammu_device::cammu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source)
+ , device_memory_interface(mconfig, *this),
+ m_main_space_config("main", ENDIANNESS_LITTLE, 32, 32, 0),
+ m_io_space_config("io", ENDIANNESS_LITTLE, 32, 32, 0),
+ m_boot_space_config("boot", ENDIANNESS_LITTLE, 32, 32, 0),
+ m_main_space(nullptr),
+ m_io_space(nullptr),
+ m_boot_space(nullptr),
+ m_ssw_func(*this)
+{ }
+
+void cammu_device::device_start()
+{
+ m_ssw_func.resolve_safe(0);
+
+ m_main_space = &space(AS_0);
+ m_io_space = &space(AS_1);
+ m_boot_space = &space(AS_2);
+}
+
+void cammu_device::device_reset()
+{
+}
+
+const address_space_config *cammu_device::memory_space_config (address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_main_space_config;
+ case AS_1: return &m_io_space_config;
+ case AS_2: return &m_boot_space_config;
+ }
+
+ return nullptr;
+}
+
+READ32_MEMBER(cammu_device::mmu_r)
+{
+ u32 ssw = m_ssw_func();
+ u32 address = offset << 2;
+
+ // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
+ if ((ssw & 0x40000000) == 0 && (address & ~0x7fff) == 0)
+ {
+ switch (address & 0xf000)
+ {
+ case 0x0000:
+ case 0x1000:
+ case 0x2000:
+ case 0x3000:
+ // pages 0-3: main space pages 0-3
+ return m_main_space->read_dword(address, mem_mask);
+
+ case 0x4000:
+ case 0x5000:
+ // pages 4-5: i/o space pages 0-1
+ return m_io_space->read_dword(address & 0x1fff, mem_mask);
+
+ case 0x6000:
+ case 0x7000:
+ // pages 6-7: boot space pages 0-1
+ return m_boot_space->read_dword(address & 0x1fff, mem_mask);
+ }
+ }
+
+ // FIXME: currently maps addresses with upper bits 0x00 or 0x7f1 to main memory and everything else to I/O
+ if ((address & 0xff000000) == 0x00000000 || (address & 0xfff00000) == 0x7f100000)
+ {
+#ifdef ICACHE_ENTRIES
+ // if this is an instruction fetch, check the cache first
+ if (space.spacenum() == AS_PROGRAM)
+ {
+ if (m_icache[offset & (ICACHE_ENTRIES-1)].offset != offset)
+ {
+ m_icache[offset & (ICACHE_ENTRIES - 1)].offset = offset;
+ m_icache[offset & (ICACHE_ENTRIES - 1)].data = m_main_space->read_dword(address, mem_mask);
+ }
+
+ return m_icache[offset & (ICACHE_ENTRIES - 1)].data;
+ }
+ else
+#endif
+ return m_main_space->read_dword(address, mem_mask);
+ }
+ else
+ return m_io_space->read_dword(address, mem_mask);
+}
+
+WRITE32_MEMBER(cammu_device::mmu_w)
+{
+ u32 ssw = m_ssw_func();
+ u32 address = offset << 2;
+
+ // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
+ if ((ssw & 0x40000000) == 0 && (address & ~0x7fff) == 0)
+ {
+ switch (address & 0xf000)
+ {
+ case 0x0000:
+ case 0x1000:
+ case 0x2000:
+ case 0x3000:
+ // pages 0-3: main space pages 0-3
+ m_main_space->write_dword(address, data, mem_mask);
+ return;
+
+ case 0x4000:
+ case 0x5000:
+ // pages 4-5: i/o space pages 0-1
+ m_io_space->write_dword(address & 0x1fff, data, mem_mask);
+ return;
+
+ case 0x6000:
+ case 0x7000:
+ // pages 6-7: boot space pages 0-1
+ m_boot_space->write_dword(address & 0x1fff, data, mem_mask);
+ return;
+ }
+ }
+
+ // FIXME: currently maps addresses with upper bits 0x00 or 0x7f1 to main memory and everything else to I/O
+ if ((address & 0xff000000) == 0x00000000 || (address & 0xfff00000) == 0x7f100000)
+ m_main_space->write_dword(address, data, mem_mask);
+ else
+ m_io_space->write_dword(address, data, mem_mask);
+}
diff --git a/src/mame/machine/cammu.h b/src/mame/machine/cammu.h
new file mode 100644
index 00000000000..dfbc39884b5
--- /dev/null
+++ b/src/mame/machine/cammu.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#pragma once
+
+#ifndef CAMMU_H_
+#define CAMMU_H_
+
+#include "emu.h"
+
+// the following enables a very crude instruction cache - it has known (future)
+// problems, but speeds up cpu execution quite noticeably in the short term by
+// avoiding some of the delays in the mame memory subsystem
+#define ICACHE_ENTRIES 32768
+
+#define MCFG_CAMMU_SSW_CB(_sswcb) \
+ devcb = &cammu_device::static_set_ssw_callback(*device, DEVCB_##_sswcb);
+
+class cammu_device : public device_t, public device_memory_interface
+{
+public:
+ cammu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source);
+
+ template<class _Object> static devcb_base &static_set_ssw_callback(device_t &device, _Object object) { return downcast<cammu_device &>(device).m_ssw_func.set_callback(object); }
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) = 0;
+
+ DECLARE_READ32_MEMBER(mmu_r);
+ DECLARE_WRITE32_MEMBER(mmu_w);
+
+ DECLARE_READ32_MEMBER(cammu_r) { return m_cammu[offset]; }
+ DECLARE_WRITE32_MEMBER(cammu_w) { m_cammu[offset] = data; }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config (address_spacenum spacenum) const override;
+
+private:
+ devcb_read32 m_ssw_func;
+
+ address_space_config m_main_space_config;
+ address_space_config m_io_space_config;
+ address_space_config m_boot_space_config;
+
+ address_space *m_main_space;
+ address_space *m_io_space;
+ address_space *m_boot_space;
+
+ u32 m_cammu[1024];
+
+#ifdef ICACHE_ENTRIES
+ struct icache
+ {
+ u32 offset;
+ u32 data;
+ } m_icache[ICACHE_ENTRIES];
+#endif
+};
+
+class cammu_c4t_device : public cammu_device
+{
+public:
+ cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) override;
+};
+
+class cammu_c4i_device : public cammu_device
+{
+public:
+ cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) override;
+};
+
+class cammu_c3_device : public cammu_device
+{
+public:
+ cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 32) override;
+};
+
+// device type definitions
+extern const device_type CAMMU_C4T;
+extern const device_type CAMMU_C4I;
+extern const device_type CAMMU_C3;
+
+#endif \ No newline at end of file
diff --git a/src/mame/machine/interpro_ioga.cpp b/src/mame/machine/interpro_ioga.cpp
new file mode 100644
index 00000000000..67a47f2cfd0
--- /dev/null
+++ b/src/mame/machine/interpro_ioga.cpp
@@ -0,0 +1,691 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the IOGA device found on Intergraph InterPro family workstations. There is no
+ * public documentation on this device, so the implementation is being built to follow the logic of the
+ * system boot ROM and its diagnostic tests.
+ *
+ * The device handles most of the I/O for the system, including timers, interrupts, DMA and target device
+ * interfacing. There remains a significant amount of work to be completed before the boot diagnostics will
+ * pass without errors, let alone successfully booting CLIX.
+ *
+ * Please be aware that code in here is not only broken, it's likely wrong in many cases.
+ *
+ * TODO
+ * - too long to list
+ */
+#include "interpro_ioga.h"
+
+#define VERBOSE 0
+#if VERBOSE
+#define LOG_TIMER_MASK 0xff
+#define LOG_TIMER(timer, ...) if (LOG_TIMER_MASK & (1 << timer)) logerror(__VA_ARGS__)
+#define LOG_INTERRUPT(...) logerror(__VA_ARGS__)
+#define LOG_IOGA(...) logerror(__VA_ARGS__)
+#define LOG_DMA(...) logerror(__VA_ARGS__)
+#else
+#define LOG_TIMER_MASK 0x00
+#define LOG_TIMER(timer, ...)
+#define LOG_INTERRUPT(...)
+#define LOG_IOGA(...)
+#define LOG_DMA(...)
+#endif
+
+DEVICE_ADDRESS_MAP_START(map, 32, interpro_ioga_device)
+ AM_RANGE(0x0c, 0x1b) AM_READWRITE(dma_plotter_r, dma_plotter_w)
+ AM_RANGE(0x1c, 0x1f) AM_READWRITE(dma_plotter_eosl_r, dma_plotter_eosl_w)
+ AM_RANGE(0x20, 0x2f) AM_READWRITE(dma_scsi_r, dma_scsi_w)
+ AM_RANGE(0x30, 0x3f) AM_READWRITE(dma_floppy_r, dma_floppy_w)
+
+ AM_RANGE(0x5c, 0x7f) AM_READWRITE16(icr_r, icr_w, 0xffffffff)
+ AM_RANGE(0x80, 0x83) AM_READWRITE16(icr18_r, icr18_w, 0x0000ffff)
+ AM_RANGE(0x80, 0x83) AM_READWRITE8(softint_r, softint_w, 0x00ff0000)
+ AM_RANGE(0x80, 0x83) AM_READWRITE8(nmictrl_r, nmictrl_w, 0xff000000)
+
+ AM_RANGE(0x88, 0x8b) AM_READWRITE(timer_prescaler_r, timer_prescaler_w)
+ AM_RANGE(0x8c, 0x8f) AM_READWRITE(timer0_r, timer0_w)
+ AM_RANGE(0x90, 0x93) AM_READWRITE(timer1_r, timer1_w)
+ AM_RANGE(0x94, 0x97) AM_READ(error_address_r)
+ AM_RANGE(0x98, 0x9b) AM_READ(error_businfo_r)
+ AM_RANGE(0x9c, 0x9f) AM_READWRITE16(arbctl_r, arbctl_w, 0x0000ffff)
+
+ AM_RANGE(0xa8, 0xab) AM_READWRITE(timer3_r, timer3_w)
+
+ AM_RANGE(0xb0, 0xbf) AM_READWRITE16(softint_vector_r, softint_vector_w, 0xffffffff)
+ADDRESS_MAP_END
+
+const device_type INTERPRO_IOGA = &device_creator<interpro_ioga_device>;
+
+interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, INTERPRO_IOGA, "InterPro IOGA", tag, owner, clock, "ioga", __FILE__),
+ m_out_nmi_func(*this),
+ m_out_int_func(*this),
+ m_memory_space(nullptr),
+ m_dma_channel{
+ { 0,0,0,0,false, 0, {*this}, {*this} },
+ { 0,0,0,0,false, 0, {*this}, {*this} },
+ { 0,0,0,0,false, 0, {*this}, {*this} },
+ { 0,0,0,0,false, 0, {*this}, {*this} } },
+ m_fdc_tc_func(*this)
+{
+}
+
+void interpro_ioga_device::device_start()
+{
+ // resolve callbacks
+ m_out_nmi_func.resolve();
+ m_out_int_func.resolve();
+
+ // TODO: parameterise the cammu name and space number
+ // grab the main memory space from the mmu so we can do DMA to/from it
+ device_memory_interface *mmu;
+ siblingdevice("mmu")->interface(mmu);
+ m_memory_space = &mmu->space(AS_0);
+
+ for (int i = 0; i < IOGA_DMA_CHANNELS; i++)
+ {
+ m_dma_channel[i].device_r.resolve_safe(0xff);
+ m_dma_channel[i].device_w.resolve();
+ }
+
+ m_fdc_tc_func.resolve();
+
+ // allocate ioga timers
+ m_timer[0] = timer_alloc(IOGA_TIMER_0);
+ m_timer[1] = timer_alloc(IOGA_TIMER_1);
+ m_timer[2] = timer_alloc(IOGA_TIMER_2);
+ m_timer[3] = timer_alloc(IOGA_TIMER_3);
+
+ for (auto & elem : m_timer)
+ elem->enable(false);
+
+ // allocate timer for DMA controller
+ m_dma_timer = timer_alloc(IOGA_TIMER_DMA);
+ m_dma_timer->adjust(attotime::never);
+}
+
+void interpro_ioga_device::device_reset()
+{
+ m_nmi_pending = false;
+
+ m_interrupt_active = 0;
+ m_irq_forced = 0;
+
+ // configure timer 0 at 60Hz
+ m_timer_reg[0] = 0;
+ m_timer[0]->adjust(attotime::zero, IOGA_TIMER_0, attotime::from_hz(60));
+}
+
+/******************************************************************************
+ Timers
+******************************************************************************/
+READ32_MEMBER(interpro_ioga_device::timer1_r)
+{
+ uint32_t result = m_timer1_count & IOGA_TIMER1_VMASK;
+
+ // set the start bit if the timer is currently enabled
+ if (m_timer[1]->enabled())
+ result |= IOGA_TIMER1_START;
+ else if (m_timer[1]->param())
+ result |= IOGA_TIMER1_EXPIRED;
+
+ return result;
+}
+
+READ32_MEMBER(interpro_ioga_device::timer3_r)
+{
+ uint32_t result = m_timer3_count & IOGA_TIMER3_VMASK;
+
+ if (m_timer[3]->enabled())
+ result |= IOGA_TIMER3_START;
+ else if (m_timer[3]->param())
+ result |= IOGA_TIMER3_EXPIRED;
+
+ return result;
+}
+
+void interpro_ioga_device::write_timer(int timer, uint32_t value, device_timer_id id)
+{
+ switch (id)
+ {
+ case IOGA_TIMER_1:
+ // disable the timer
+ m_timer[timer]->enable(false);
+
+ // store the timer count value
+ m_timer1_count = value;
+
+ // start the timer if necessary
+ if (value & IOGA_TIMER1_START)
+ {
+ LOG_TIMER(1, "timer 1: started prescaler %d value %d\n", m_prescaler & 0x7fff, value & IOGA_TIMER1_VMASK);
+
+ // FIXME: this division by 50 is sufficient to pass iogadiag timer 1 tests
+ m_timer[timer]->adjust(attotime::zero, false, attotime::from_usec((m_prescaler & 0x7fff) / 50));
+ }
+ break;
+
+ case IOGA_TIMER_3:
+ // stop the timer so it won't trigger while we're fiddling with it
+ m_timer[timer]->enable(false);
+
+ // write the new value to the timer register
+ m_timer3_count = value & IOGA_TIMER3_VMASK;
+
+ // start the timer if necessary
+ if (value & IOGA_TIMER3_START)
+ {
+ LOG_TIMER(3, "timer 3: started value %d\n", value & IOGA_TIMER3_VMASK);
+
+ m_timer[timer]->adjust(attotime::zero, false, attotime::from_hz(XTAL_25MHz));
+ }
+ break;
+
+ default:
+ // save the value
+ m_timer_reg[timer] = value;
+
+ // timer_set(attotime::from_usec(500), id);
+
+ LOG_TIMER(0xf, "timer %d: set to 0x%x (%d)\n", timer, m_timer_reg[timer], m_timer_reg[timer]);
+ break;
+ }
+}
+
+void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case IOGA_TIMER_0:
+ m_timer_reg[0]++;
+ set_irq_line(IOGA_TIMER0_IRQ, ASSERT_LINE);
+ break;
+
+ case IOGA_TIMER_1:
+ // decrement timer count value
+ m_timer1_count--;
+
+ // check if timer has expired
+ if (m_timer1_count == 0)
+ {
+ LOG_TIMER(1, "timer 1: stopped\n");
+
+ // disable timer and set the zero flag
+ timer.enable(false);
+ timer.set_param(true);
+
+ // throw an interrupt
+ set_irq_line(IOGA_TIMER1_IRQ, ASSERT_LINE);
+ }
+ break;
+
+ case IOGA_TIMER_3:
+ // decrement timer count value
+ m_timer3_count--;
+
+ // check for expiry
+ if (m_timer3_count == 0)
+ {
+ LOG_TIMER(3, "timer 3: stopped\n");
+
+ // disable timer and set the zero flag
+ timer.enable(false);
+ timer.set_param(true);
+
+ // throw an interrupt
+ set_irq_line(IOGA_TIMER3_IRQ, ASSERT_LINE);
+ }
+ break;
+
+ case IOGA_TIMER_DMA:
+ // transfer data between device and main memory
+
+ // TODO: figure out what indicates dma write (memory -> device)
+ // TODO: implement multiple dma channels
+ // TODO: virtual memory?
+
+ if (!m_dma_channel[param].dma_active)
+ {
+ LOG_DMA("dma: transfer started, channel = %d, control 0x%08x, real address 0x%08x count 0x%08x\n",
+ param, m_dma_channel[param].control, m_dma_channel[param].real_address, m_dma_channel[param].transfer_count);
+ m_dma_channel[param].dma_active = true;
+ }
+
+ // while the device is requesting a data transfer and the transfer count is not zero
+ while (m_dma_channel[param].drq_state && m_dma_channel[param].transfer_count)
+ {
+ // transfer a byte between device and memory
+ if (true)
+ m_memory_space->write_byte(m_dma_channel[param].real_address, m_dma_channel[param].device_r());
+ else
+ m_dma_channel[param].device_w(m_memory_space->read_byte(m_dma_channel[param].real_address));
+
+ // increment addresses and decrement count
+ m_dma_channel[param].real_address++;
+ m_dma_channel[param].virtual_address++;
+ m_dma_channel[param].transfer_count--;
+ }
+
+ // if there are no more bytes remaining, terminate the transfer
+ if (m_dma_channel[param].transfer_count == 0)
+ {
+ LOG_DMA("dma: transfer stopped, control 0x%08x, real address 0x%08x count 0x%08x\n",
+ m_dma_channel[param].control, m_dma_channel[param].fdc_real_address, m_dma_channel[param].transfer_count);
+
+ if (param == IOGA_DMA_FLOPPY)
+ {
+ LOG_DMA("dma: asserting fdc terminal count line\n");
+
+ m_fdc_tc_func(ASSERT_LINE);
+ m_fdc_tc_func(CLEAR_LINE);
+ }
+
+ m_dma_channel[param].dma_active = false;
+ }
+ break;
+ }
+}
+
+/******************************************************************************
+ Interrupts
+******************************************************************************/
+
+static const uint16_t irq_enable_mask[IOGA_INTERRUPT_COUNT] =
+{
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 0: SCSI
+ IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 1: floppy
+ IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 2: plotter
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+
+ // internal interrupt 5: serial DMA - one interrupt enable per DMA channel
+ IOGA_INTERRUPT_ENABLE_EXTERNAL << 0 | IOGA_INTERRUPT_ENABLE_EXTERNAL << 1 | IOGA_INTERRUPT_ENABLE_EXTERNAL << 2,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL,
+ IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL // external interrupt 12: Ethernet
+};
+
+void interpro_ioga_device::set_nmi_line(int state)
+{
+ switch (state)
+ {
+ case ASSERT_LINE:
+
+ LOG_INTERRUPT("nmi: ctrl = 0x%02x\n", m_nmictrl);
+
+ if ((m_nmictrl & IOGA_NMI_ENABLE) == IOGA_NMI_ENABLE)
+ {
+ // if edge triggered mode, clear enable in
+ if (m_nmictrl & IOGA_NMI_EDGE)
+ m_nmictrl &= ~IOGA_NMI_ENABLE_IN;
+
+ m_nmi_pending = true;
+ update_interrupt(ASSERT_LINE);
+ }
+ break;
+
+ case CLEAR_LINE:
+ m_nmi_pending = false;
+ update_interrupt(ASSERT_LINE);
+ break;
+ }
+}
+
+void interpro_ioga_device::set_irq_line(int irq, int state)
+{
+ LOG_INTERRUPT("set_irq_line(%d, %d)\n", irq, state);
+ switch (state)
+ {
+ case ASSERT_LINE:
+ if (m_int_vector[irq] & irq_enable_mask[irq])
+ {
+ // set pending bit
+ m_int_vector[irq] |= IOGA_INTERRUPT_PENDING;
+
+ // update irq line state
+ update_interrupt(state);
+ }
+ else
+ LOG_INTERRUPT("received disabled interrupt irq %d vector 0x%04x\n", irq, m_int_vector[irq]);
+ break;
+
+ case CLEAR_LINE:
+ // clear pending bit
+ m_int_vector[irq] &= ~IOGA_INTERRUPT_PENDING;
+
+ // update irq line state
+ update_interrupt(state);
+ break;
+ }
+}
+
+void interpro_ioga_device::set_irq_soft(int irq, int state)
+{
+ LOG_INTERRUPT("set_irq_soft(%d, %d)\n", irq, state);
+ switch (state)
+ {
+ case ASSERT_LINE:
+ // set pending bit
+ if (irq < 8)
+ m_softint |= 1 << irq;
+ else
+ m_softint_vector[irq - 8] |= IOGA_INTERRUPT_PENDING;
+
+ update_interrupt(state);
+ break;
+
+ case CLEAR_LINE:
+ // clear pending bit
+ if (irq < 8)
+ m_softint &= ~(1 << irq);
+ else
+ m_softint_vector[irq - 8] &= ~IOGA_INTERRUPT_PENDING;
+
+ // update irq line state
+ update_interrupt(state);
+ break;
+ }
+}
+
+IRQ_CALLBACK_MEMBER(interpro_ioga_device::inta_cb)
+{
+ switch (irqline)
+ {
+ case INPUT_LINE_IRQ0:
+ // FIXME: clear pending bit - can't rely on device callbacks
+ switch (m_interrupt_active)
+ {
+ case IOGA_INTERRUPT_INTERNAL:
+ case IOGA_INTERRUPT_EXTERNAL:
+ m_int_vector[m_irq_current] &= ~IOGA_INTERRUPT_PENDING;
+ break;
+
+ case IOGA_INTERRUPT_SOFT_LO:
+ m_softint &= ~(1 << m_irq_current);
+ break;
+
+ case IOGA_INTERRUPT_SOFT_HI:
+ m_softint_vector[m_irq_current] &= ~IOGA_INTERRUPT_PENDING;
+ break;
+ }
+
+ // clear irq line
+ update_interrupt(CLEAR_LINE);
+
+ // fall through to return interrupt vector
+ case -1:
+ // return vector for current interrupt without clearing irq line
+ switch (m_interrupt_active)
+ {
+ case IOGA_INTERRUPT_EXTERNAL:
+ case IOGA_INTERRUPT_INTERNAL:
+ return m_int_vector[m_irq_current] & 0xff;
+
+ case IOGA_INTERRUPT_SOFT_LO:
+ return 0x8f + m_irq_current * 0x10;
+
+ case IOGA_INTERRUPT_SOFT_HI:
+ return m_softint_vector[m_irq_current] & 0xff;
+ }
+ break;
+
+ case INPUT_LINE_NMI:
+ // clear pending flag
+ m_nmi_pending = false;
+
+ // clear line
+ update_interrupt(CLEAR_LINE);
+
+ // return vector
+ return 0;
+ }
+
+ return 0;
+}
+
+void interpro_ioga_device::update_interrupt(int state)
+{
+ switch (state)
+ {
+ case CLEAR_LINE:
+ if (m_interrupt_active)
+ {
+ // the cpu has acknowledged the active interrupt, deassert the nmi/irq line
+ m_interrupt_active == IOGA_INTERRUPT_NMI ? m_out_nmi_func(CLEAR_LINE) : m_out_int_func(CLEAR_LINE);
+
+ // clear the active status
+ m_interrupt_active = 0;
+ }
+ // fall through to handle any pending interrupts
+
+ case ASSERT_LINE:
+ // if an interrupt is currently active, don't do anything
+ if (m_interrupt_active == 0)
+ {
+ // check for pending nmi
+ if (m_nmi_pending)
+ {
+ m_interrupt_active = IOGA_INTERRUPT_NMI;
+
+ m_out_nmi_func(ASSERT_LINE);
+ return;
+ }
+
+ // check for any pending irq
+ for (int i = 0; i < IOGA_INTERRUPT_COUNT; i++)
+ {
+ if (m_int_vector[i] & IOGA_INTERRUPT_PENDING)
+ {
+ m_interrupt_active = IOGA_INTERRUPT_INTERNAL; // TODO: flag internal/external
+ m_irq_current = i;
+
+ m_out_int_func(ASSERT_LINE);
+ return;
+ }
+ }
+
+ // check for any pending soft interrupts (low type)
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_softint & (1 << i))
+ {
+ m_interrupt_active = IOGA_INTERRUPT_SOFT_LO;
+ m_irq_current = i;
+
+ m_out_int_func(ASSERT_LINE);
+ return;
+ }
+ }
+
+ // check for any pending soft interrupts (high type)
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_softint_vector[i] & IOGA_INTERRUPT_PENDING)
+ {
+ m_interrupt_active = IOGA_INTERRUPT_SOFT_HI;
+ m_irq_current = i;
+
+ m_out_int_func(ASSERT_LINE);
+ return;
+ }
+ }
+ }
+ break;
+ }
+}
+
+WRITE16_MEMBER(interpro_ioga_device::icr_w)
+{
+ LOG_INTERRUPT("interrupt vector %d set to 0x%04x at pc 0x%08x\n", offset, data, space.device().safe_pc());
+
+ // FIXME: now that the interrupt handling only depends on IOGA_INTERRUPT_PENDING, we might be able
+ // to avoid this hack
+ if (data & IOGA_INTERRUPT_PENDING)
+ {
+ m_irq_forced |= 1 << offset;
+ m_int_vector[offset] = data & ~IOGA_INTERRUPT_PENDING;
+ }
+ else if (m_irq_forced & 1 << offset)
+ {
+ m_int_vector[offset] = data;
+
+ // clear forced flag
+ m_irq_forced &= ~(1 << offset);
+
+ // force an interrupt
+ set_irq_line(offset, ASSERT_LINE);
+ }
+ else
+ m_int_vector[offset] = data;
+}
+
+WRITE8_MEMBER(interpro_ioga_device::softint_w)
+{
+ // save the existing value
+ uint8_t previous = m_softint;
+
+ // store the written value
+ m_softint = data;
+
+ // force soft interrupt for any bit written from 1 to 0
+ for (int i = 0; i < 8; i++)
+ {
+ uint8_t mask = 1 << i;
+
+ // check for transition from 1 to 0 and force a soft interrupt
+ if (previous & mask && !(data & mask))
+ set_irq_soft(i, ASSERT_LINE);
+ }
+}
+
+WRITE8_MEMBER(interpro_ioga_device::nmictrl_w)
+{
+ // save the existing value
+ uint8_t previous = m_nmictrl;
+
+ // store the written value
+ m_nmictrl = data;
+
+ // force an nmi when pending bit is written low
+ if (previous & IOGA_NMI_PENDING && !(data & IOGA_NMI_PENDING))
+ set_nmi_line(ASSERT_LINE);
+}
+
+WRITE16_MEMBER(interpro_ioga_device::softint_vector_w)
+{
+ // save the existing value
+ uint16_t previous = m_softint_vector[offset];
+
+ // store the written value
+ m_softint_vector[offset] = data;
+
+ // check for transition from 1 to 0 and force a soft interrupt
+ if (previous & IOGA_INTERRUPT_PENDING && !(data & IOGA_INTERRUPT_PENDING))
+ set_irq_soft(offset + 8, ASSERT_LINE);
+}
+
+/******************************************************************************
+ DMA
+******************************************************************************/
+void interpro_ioga_device::drq(int state, int channel)
+{
+ // this member is called when the device has data ready for reading via dma
+ m_dma_channel[channel].drq_state = state;
+
+ if (state)
+ {
+ // TODO: check if dma is enabled
+ m_dma_timer->adjust(attotime::zero, channel);
+ }
+}
+/*
+0x94: error address reg: expect 0x7f200000 after bus error (from dma virtual address)
+0x98: error cycle type: expect 0x52f0 (after failed dma?)
+ 0x5331 - forced berr with nmi/interrupts disabled?
+ 0xc2f0
+ 0x62f0
+*/
+// TODO: 7.0266 - forced BERR not working
+
+uint32_t interpro_ioga_device::dma_r(address_space &space, offs_t offset, uint32_t mem_mask, int channel)
+{
+ switch (offset)
+ {
+ case 0:
+ return m_dma_channel[channel].real_address;
+
+ case 1:
+ return m_dma_channel[channel].virtual_address;
+
+ case 2:
+ return m_dma_channel[channel].transfer_count;
+
+ case 3:
+ return m_dma_channel[channel].control;
+ }
+
+ logerror("dma_r: unknown channel %d\n", channel);
+ return 0;
+}
+
+void interpro_ioga_device::dma_w(address_space &space, offs_t offset, uint32_t data, uint32_t mem_mask, int channel)
+{
+ switch (offset)
+ {
+ case 0:
+ m_dma_channel[channel].real_address = data;
+ break;
+
+ case 1:
+ m_dma_channel[channel].virtual_address = data & ~0x3;
+ break;
+
+ case 2:
+ m_dma_channel[channel].transfer_count = data;
+ break;
+
+ case 3:
+ m_dma_channel[channel].control = data & IOGA_DMA_CTRL_WMASK;
+
+ logerror("dma: channel = %d, control = 0x%08x, ra = 0x%08x, va = 0x%08x, tc = 0x%08x\n",
+ channel, data, m_dma_channel[channel].real_address, m_dma_channel[channel].virtual_address, m_dma_channel[channel].transfer_count);
+
+ // iogadiag test 7.0265
+ if (data == IOGA_DMA_CTRL_START)
+ {
+ uint32_t mask = 0;
+
+ switch (channel)
+ {
+ case IOGA_DMA_PLOTTER:
+ mask = IOGA_ARBCTL_BGR_PLOT;
+ break;
+
+ case IOGA_DMA_SCSI:
+ mask = IOGA_ARBCTL_BGR_SCSI;
+ break;
+
+ case IOGA_DMA_FLOPPY:
+ mask = IOGA_ARBCTL_BGR_FDC;
+ break;
+ }
+
+ // if bus grant is not enabled, set the busy flag
+ if (!(m_arbctl & mask))
+ m_dma_channel[channel].control |= IOGA_DMA_CTRL_BUSY;
+ }
+ break;
+ }
+}
diff --git a/src/mame/machine/interpro_ioga.h b/src/mame/machine/interpro_ioga.h
new file mode 100644
index 00000000000..a31f3aba9fc
--- /dev/null
+++ b/src/mame/machine/interpro_ioga.h
@@ -0,0 +1,256 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#pragma once
+
+#ifndef INTERPRO_IOGA_H_
+#define INTERPRO_IOGA_H_
+
+#include "emu.h"
+
+#define MCFG_INTERPRO_IOGA_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, INTERPRO_IOGA, 0)
+
+#define MCFG_INTERPRO_IOGA_NMI_CB(_out_nmi) \
+ devcb = &interpro_ioga_device::static_set_out_nmi_callback(*device, DEVCB_##_out_nmi);
+
+#define MCFG_INTERPRO_IOGA_IRQ_CB(_out_int) \
+ devcb = &interpro_ioga_device::static_set_out_int_callback(*device, DEVCB_##_out_int);
+
+#define MCFG_INTERPRO_IOGA_DMA_CB(_channel, _dma_r, _dma_w) \
+ devcb = &interpro_ioga_device::static_set_dma_r_callback(*device, _channel, DEVCB_##_dma_r); \
+ devcb = &interpro_ioga_device::static_set_dma_w_callback(*device, _channel, DEVCB_##_dma_w);
+
+#define MCFG_INTERPRO_IOGA_FDCTC_CB(_tc) \
+ devcb = &interpro_ioga_device::static_set_fdc_tc_callback(*device, DEVCB_##_tc);
+
+#define MCFG_INTERPRO_IOGA_DMA_BUS(_mmu, _space)
+
+// timer 0 seem to be a 60Hz cycle
+#define IOGA_TIMER0_IRQ 14
+
+// best guess for timer 1 is 10MHz based on typical prescaler value of 1000 and timer value of 100 for a delay of 100ms
+#define IOGA_TIMER1_IRQ 15
+#define IOGA_TIMER1_VMASK 0xffff
+#define IOGA_TIMER1_START 0x10000
+#define IOGA_TIMER1_EXPIRED 0x20000
+
+// best guess for timer 3 is 12.5MHz based on typical value of 12500 for a delay of 1ms
+#define IOGA_TIMER3_CLOCK XTAL_12_5MHz
+#define IOGA_TIMER3_IRQ 1
+#define IOGA_TIMER3_VMASK 0x3fffffff
+#define IOGA_TIMER3_START 0x40000000
+#define IOGA_TIMER3_EXPIRED 0x80000000
+
+#define IOGA_INTERRUPT_COUNT 19
+#define IOGA_INTERRUPT_PENDING 0x0100
+#define IOGA_INTERRUPT_ENABLE_EXTERNAL 0x0200
+#define IOGA_INTERRUPT_EDGE 0x0400
+#define IOGA_INTERRUPT_NEGPOL 0x0800
+#define IOGA_INTERRUPT_ENABLE_INTERNAL 0x1000
+
+#define IOGA_NMI_EDGE 0x02
+#define IOGA_NMI_PENDING 0x08
+#define IOGA_NMI_ENABLE_IN 0x10
+#define IOGA_NMI_ENABLE (IOGA_NMI_EDGE | IOGA_NMI_ENABLE_IN)
+
+#define IOGA_INTERRUPT_NMI 1
+#define IOGA_INTERRUPT_INTERNAL 2
+#define IOGA_INTERRUPT_EXTERNAL 3
+#define IOGA_INTERRUPT_SOFT_LO 4
+#define IOGA_INTERRUPT_SOFT_HI 5
+
+#define IOGA_DMA_CHANNELS 4
+#define IOGA_DMA_PLOTTER 0
+#define IOGA_DMA_SCSI 1
+#define IOGA_DMA_FLOPPY 2
+#define IOGA_DMA_SERIAL 3
+
+// dma write values
+#define IOGA_DMA_CTRL_WMASK 0xfd000e00
+#define IOGA_DMA_CTRL_RESET_L 0x61000000 // do not clear bus error bit
+#define IOGA_DMA_CTRL_RESET 0x60400000 // clear bus error bit
+
+#define IOGA_DMA_CTRL_START 0x63000800 // perhaps start a transfer? - maybe the 8 is the channel?
+#define IOGA_DMA_CTRL_UNK1 0x60000000 // don't know yet
+#define IOGA_DMA_CTRL_UNK2 0x67000600 // forced berr with nmi and interrupts disabled
+
+// read values
+#define IOGA_DMA_CTRL_BUSY 0x02000000
+#define IOGA_DMA_CTRL_BERR 0x00400000 // iogadiag code expects 0x60400000 on bus error
+// iogadiag expects 0x64400800 after forced berr with nmi/interrupts disabled
+
+
+// bus arbitration bus grant bits
+#define IOGA_ARBCTL_BGR_ETHC 0x0001
+#define IOGA_ARBCTL_BGR_SCSI 0x0002
+#define IOGA_ARBCTL_BGR_PLOT 0x0004
+#define IOGA_ARBCTL_BGR_FDC 0x0008
+#define IOGA_ARBCTL_BGR_SER0 0x0010
+#define IOGA_ARBCTL_BGR_SER1 0x0020
+#define IOGA_ARBCTL_BGR_SER2 0x0040
+#define IOGA_ARBCTL_BGR_ETHB 0x0080
+#define IOGA_ARBCTL_BGR_ETHA 0x0100
+
+class interpro_ioga_device : public device_t
+{
+public:
+ interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ template<class _Object> static devcb_base &static_set_out_nmi_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_out_nmi_func.set_callback(object); }
+ template<class _Object> static devcb_base &static_set_out_int_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_out_int_func.set_callback(object); }
+
+ template<class _Object> static devcb_base &static_set_dma_r_callback(device_t &device, int channel, _Object object) { return downcast<interpro_ioga_device &>(device).m_dma_channel[channel].device_r.set_callback(object); }
+ template<class _Object> static devcb_base &static_set_dma_w_callback(device_t &device, int channel, _Object object) { return downcast<interpro_ioga_device &>(device).m_dma_channel[channel].device_w.set_callback(object); }
+
+ template<class _Object> static devcb_base &static_set_fdc_tc_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_fdc_tc_func.set_callback(object); }
+
+ virtual DECLARE_ADDRESS_MAP(map, 32);
+
+ // external interrupt lines
+ DECLARE_WRITE_LINE_MEMBER(ir0_w) { set_irq_line(2, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir1_w) { set_irq_line(3, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir2_w) { set_irq_line(4, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir3_w) { set_irq_line(5, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir4_w) { set_irq_line(6, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir5_w) { set_irq_line(7, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir6_w) { set_irq_line(8, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir7_w) { set_irq_line(9, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir8_w) { set_irq_line(10, state); }
+ // FIXME: this is a workaround for the mc146818 code which inverts the normal irq state convention
+ DECLARE_WRITE_LINE_MEMBER(ir9_w) { set_irq_line(11, !state); }
+ DECLARE_WRITE_LINE_MEMBER(ir10_w) { set_irq_line(12, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir11_w) { set_irq_line(17, state); }
+ DECLARE_WRITE_LINE_MEMBER(ir12_w) { set_irq_line(18, state); }
+
+ IRQ_CALLBACK_MEMBER(inta_cb);
+
+ DECLARE_WRITE_LINE_MEMBER(drq_plotter) { drq(state, IOGA_DMA_PLOTTER); }
+ DECLARE_WRITE_LINE_MEMBER(drq_scsi) { drq(state, IOGA_DMA_SCSI); }
+ DECLARE_WRITE_LINE_MEMBER(drq_floppy) { drq(state, IOGA_DMA_FLOPPY); }
+
+ DECLARE_READ32_MEMBER(timer_prescaler_r) { return m_prescaler; }
+ DECLARE_READ32_MEMBER(timer0_r) { return m_timer_reg[0]; }
+ DECLARE_READ32_MEMBER(timer1_r);
+ DECLARE_READ16_MEMBER(arbctl_r) { return m_arbctl; }
+ DECLARE_WRITE16_MEMBER(arbctl_w) { m_arbctl = data; }
+ DECLARE_READ32_MEMBER(timer2_r) { return m_timer_reg[2]; }
+ DECLARE_READ32_MEMBER(timer3_r);
+
+ DECLARE_WRITE32_MEMBER(timer_prescaler_w) {
+ // this logic satisfies prescaler tests, but fails timer prescaler tests
+ if ((data & 0x7fff) < 0x100 && (data & 0x7fff) != 0)
+ m_prescaler = (data ^ 0xffff0000);
+ else
+ m_prescaler = (data ^ 0xffff0000) - 0x10000;
+
+ //logerror("prescaler: input 0x%08x output 0x%08x\n", data, m_prescaler);
+ }
+ DECLARE_WRITE32_MEMBER(timer0_w) { write_timer(0, data, IOGA_TIMER_0); }
+ DECLARE_WRITE32_MEMBER(timer1_w) { write_timer(1, data, IOGA_TIMER_1); }
+ DECLARE_WRITE32_MEMBER(timer2_w) { write_timer(2, data, IOGA_TIMER_2); }
+ DECLARE_WRITE32_MEMBER(timer3_w) { write_timer(3, data, IOGA_TIMER_3); }
+
+ DECLARE_READ16_MEMBER(icr_r) { return m_int_vector[offset]; }
+ DECLARE_WRITE16_MEMBER(icr_w);
+ DECLARE_READ16_MEMBER(icr18_r) { return icr_r(space, 18, mem_mask); }
+ DECLARE_WRITE16_MEMBER(icr18_w) { icr_w(space, 18, data, mem_mask); }
+
+ DECLARE_READ8_MEMBER(softint_r) { return m_softint; }
+ DECLARE_WRITE8_MEMBER(softint_w);
+ DECLARE_READ8_MEMBER(nmictrl_r) { return m_nmictrl; }
+ DECLARE_WRITE8_MEMBER(nmictrl_w);
+
+ DECLARE_READ16_MEMBER(softint_vector_r) { return m_softint_vector[offset]; }
+ DECLARE_WRITE16_MEMBER(softint_vector_w);
+
+ DECLARE_READ32_MEMBER(dma_plotter_r) { return dma_r(space, offset, mem_mask, IOGA_DMA_PLOTTER); }
+ DECLARE_WRITE32_MEMBER(dma_plotter_w) { dma_w(space, offset, data, mem_mask, IOGA_DMA_PLOTTER); }
+ DECLARE_READ32_MEMBER(dma_scsi_r) { return dma_r(space, offset, mem_mask, IOGA_DMA_SCSI); }
+ DECLARE_WRITE32_MEMBER(dma_scsi_w) { dma_w(space, offset, data, mem_mask, IOGA_DMA_SCSI); }
+ DECLARE_READ32_MEMBER(dma_floppy_r) { return dma_r(space, offset, mem_mask, IOGA_DMA_FLOPPY); }
+ DECLARE_WRITE32_MEMBER(dma_floppy_w) { dma_w(space, offset, data, mem_mask, IOGA_DMA_FLOPPY); }
+
+ DECLARE_READ32_MEMBER(dma_plotter_eosl_r) { return m_dma_plotter_eosl; }
+ DECLARE_WRITE32_MEMBER(dma_plotter_eosl_w) { m_dma_plotter_eosl = data; }
+
+ DECLARE_READ32_MEMBER(error_address_r) { return m_error_address; }
+ DECLARE_READ32_MEMBER(error_businfo_r) { return m_error_businfo; }
+ void bus_error(uint32_t address, uint32_t cycle_type) { m_error_address = address; m_error_businfo = cycle_type; }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ static const device_timer_id IOGA_TIMER_0 = 0;
+ static const device_timer_id IOGA_TIMER_1 = 1;
+ static const device_timer_id IOGA_TIMER_2 = 2;
+ static const device_timer_id IOGA_TIMER_3 = 3;
+
+ static const device_timer_id IOGA_TIMER_DMA = 4;
+
+ void set_nmi_line(int state);
+ void set_irq_line(int irq, int state);
+ void set_irq_soft(int irq, int state);
+ void write_timer(int timer, uint32_t value, device_timer_id id);
+
+ void update_interrupt(int state);
+
+ void drq(int state, int channel);
+
+ devcb_write_line m_out_nmi_func;
+ devcb_write_line m_out_int_func;
+
+ devcb_write_line m_fdc_tc_func;
+
+ bool m_nmi_pending;
+ uint32_t m_interrupt_active;
+ uint32_t m_irq_current;
+ uint32_t m_irq_forced;
+
+ uint16_t m_int_vector[IOGA_INTERRUPT_COUNT];
+ uint8_t m_softint;
+ uint8_t m_nmictrl;
+ uint16_t m_softint_vector[8];
+
+ uint32_t m_prescaler;
+ uint32_t m_timer_reg[3];
+ uint16_t m_timer1_count;
+ uint32_t m_timer3_count;
+ emu_timer *m_timer[4];
+
+ // dma state
+ address_space *m_memory_space;
+ emu_timer *m_dma_timer;
+
+ // dma channels
+ struct dma
+ {
+ uint32_t real_address;
+ uint32_t virtual_address;
+ uint32_t transfer_count;
+ uint32_t control;
+
+ bool dma_active;
+ int drq_state;
+ devcb_read8 device_r;
+ devcb_write8 device_w;
+ } m_dma_channel[IOGA_DMA_CHANNELS];
+ uint32_t m_dma_plotter_eosl;
+
+ uint32_t dma_r(address_space &space, offs_t offset, uint32_t mem_mask, int channel);
+ void dma_w(address_space &space, offs_t offset, uint32_t data, uint32_t mem_mask, int channel);
+
+ uint16_t m_arbctl;
+
+ uint32_t m_error_address;
+ uint32_t m_error_businfo;
+};
+
+// device type definition
+extern const device_type INTERPRO_IOGA;
+
+#endif \ No newline at end of file
diff --git a/src/mame/machine/interpro_mcga.cpp b/src/mame/machine/interpro_mcga.cpp
new file mode 100644
index 00000000000..d807f571bab
--- /dev/null
+++ b/src/mame/machine/interpro_mcga.cpp
@@ -0,0 +1,89 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+* An implementation of the MCGA device found on Intergraph InterPro family workstations. There is no
+* public documentation on this device, so the implementation is being built to follow the logic of the
+* system boot ROM and its diagnostic tests.
+*
+* Please be aware that code in here is not only broken, it's likely wrong in many cases.
+*
+* TODO
+* - too long to list
+*/
+#include "interpro_mcga.h"
+
+#define VERBOSE 0
+#if VERBOSE
+#define LOG_MCGA(...) logerror(__VA_ARGS__)
+#else
+#define LOG_MCGA(...) {}
+#endif
+
+DEVICE_ADDRESS_MAP_START(map, 16, interpro_mcga_device)
+ AM_RANGE(0x00, 0x3f) AM_READWRITE16(read, write, 0xffff)
+ADDRESS_MAP_END
+
+const device_type INTERPRO_MCGA = &device_creator<interpro_mcga_device>;
+
+interpro_mcga_device::interpro_mcga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, INTERPRO_MCGA, "InterPro MCGA", tag, owner, clock, "mcga", __FILE__)
+{
+}
+
+void interpro_mcga_device::device_start()
+{
+}
+
+void interpro_mcga_device::device_reset()
+{
+ m_reg[0] = 0x00ff; // 0x00
+ m_reg[2] = MCGA_CTRL_ENREFRESH | MCGA_CTRL_CBITFRCSUB | MCGA_CTRL_CBITFRCRD; // 0x08 ctrl
+ //m_mcga[4] = 0x8000; // 0x10 error
+ m_reg[10] = 0x00ff; // 0x28
+ m_reg[14] = 0x0340; // 0x38 memsize
+}
+
+WRITE16_MEMBER(interpro_mcga_device::write)
+{
+ /*
+ read MEMSIZE 0x38 mask 0xffff
+ read 0x00 mask 0x0000
+ write CBSUB 0x20 mask 0x00ff data 0
+ write FRCRD 0x18 mask 0x00ff data 0
+ read ERROR 0x10 mask 0xffff
+ read 0x00 mask 0xffff
+
+ (0x38 >> 8) & 0xF == 3?
+
+ if (0x00 != 0xFF) -> register reset error
+
+ 0x00 = 0x0055 (test value & 0xff)
+ r7 = 0x00 & 0xff
+ */
+ LOG_MCGA("mcga write offset = 0x%08x, mask = 0x%08x, data = 0x%08x, pc = 0x%08x\n", offset, mem_mask, data, space.device().safe_pc());
+ switch (offset)
+ {
+ case 0x02: // MCGA_CTRL
+ // HACK: set or clear error status depending on ENMMBE bit
+ if (data & MCGA_CTRL_ENMMBE)
+ m_reg[4] |= MCGA_ERROR_VALID;
+ // else
+ // m_reg[4] &= ~MCGA_ERROR_VALID;
+
+ default:
+ m_reg[offset] = data;
+ break;
+ }
+}
+
+READ16_MEMBER(interpro_mcga_device::read)
+{
+ LOG_MCGA("mcga read offset = 0x%08x, mask = 0x%08x, pc = 0x%08x\n", offset, mem_mask, space.device().safe_pc());
+
+ switch (offset)
+ {
+ default:
+ return m_reg[offset];
+ }
+} \ No newline at end of file
diff --git a/src/mame/machine/interpro_mcga.h b/src/mame/machine/interpro_mcga.h
new file mode 100644
index 00000000000..4f3ecde78cc
--- /dev/null
+++ b/src/mame/machine/interpro_mcga.h
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#pragma once
+
+#ifndef INTERPRO_MCGA_H_
+#define INTERPRO_MCGA_H_
+
+#include "emu.h"
+
+// mcga control register
+#define MCGA_CTRL_OPTMASK 0x00000003
+#define MCGA_CTRL_CBITFRCRD 0x00000004
+#define MCGA_CTRL_CBITFRCSUB 0x00000008
+#define MCGA_CTRL_ENREFRESH 0x00000010
+#define MCGA_CTRL_ENMSBE 0x00000100
+#define MCGA_CTRL_ENMMBE 0x00000200
+#define MCGA_CTRL_ENECC 0x00000400
+#define MCGA_CTRL_WRPROT 0x00008000
+
+// mcga error register
+#define MCGA_ERROR_VALID 0x00008000
+
+class interpro_mcga_device : public device_t
+{
+public:
+ interpro_mcga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 32);
+
+ DECLARE_WRITE16_MEMBER(write);
+ DECLARE_READ16_MEMBER(read);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ uint16_t m_reg[32];
+};
+
+// device type definition
+extern const device_type INTERPRO_MCGA;
+
+#endif \ No newline at end of file
diff --git a/src/mame/machine/interpro_sga.cpp b/src/mame/machine/interpro_sga.cpp
new file mode 100644
index 00000000000..102c58a2f0b
--- /dev/null
+++ b/src/mame/machine/interpro_sga.cpp
@@ -0,0 +1,88 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+* An implementation of the SGA device found on Intergraph InterPro family workstations. There is no
+* public documentation on this device, so the implementation is being built to follow the logic of the
+* system boot ROM and its diagnostic tests.
+*
+* Please be aware that code in here is not only broken, it's likely wrong in many cases.
+*
+* TODO
+* - too long to list
+*/
+#include "interpro_sga.h"
+
+#define VERBOSE 0
+
+DEVICE_ADDRESS_MAP_START(map, 32, interpro_sga_device)
+ AM_RANGE(0x00, 0x03) AM_READWRITE(gcs_r, gcs_w)
+ AM_RANGE(0x04, 0x07) AM_READWRITE(ipoll_r, ipoll_w)
+ AM_RANGE(0x08, 0x0b) AM_READWRITE(imask_r, imask_w)
+ AM_RANGE(0x0c, 0x0f) AM_READWRITE(range_base_r, range_base_w)
+ AM_RANGE(0x10, 0x13) AM_READWRITE(range_end_r, range_end_w)
+ AM_RANGE(0x14, 0x17) AM_READWRITE(cttag_r, cttag_w)
+ AM_RANGE(0x18, 0x1b) AM_READWRITE(address_r, address_w)
+ AM_RANGE(0x1c, 0x1f) AM_READWRITE(dmacs_r, dmacs_w)
+ AM_RANGE(0x20, 0x23) AM_READWRITE(edmacs_r, edmacs_w)
+
+ AM_RANGE(0xa4, 0xa7) AM_READWRITE(dspad1_r, dspad1_w)
+ AM_RANGE(0xa8, 0xab) AM_READWRITE(dsoff1_r, dsoff1_w)
+
+ AM_RANGE(0xb4, 0xb7) AM_READWRITE(unknown1_r, unknown1_w)
+ AM_RANGE(0xb8, 0xbb) AM_READWRITE(unknown2_r, unknown2_w)
+ AM_RANGE(0xbc, 0xbf) AM_READWRITE(ddtc1_r, ddtc1_w)
+ADDRESS_MAP_END
+
+const device_type INTERPRO_SGA = &device_creator<interpro_sga_device>;
+
+interpro_sga_device::interpro_sga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, INTERPRO_SGA, "InterPro SGA", tag, owner, clock, "sga", __FILE__)
+{
+}
+
+void interpro_sga_device::device_start()
+{
+}
+
+void interpro_sga_device::device_reset()
+{
+}
+
+WRITE32_MEMBER(interpro_sga_device::ddtc1_w)
+{
+ // we assume that when this register is written, we should start a
+ // memory to memory dma transfer
+
+ logerror(" gcs = 0x%08x dmacs = 0x%08x\n", m_gcs, m_dmacs);
+ logerror(" ipoll = 0x%08x imask = 0x%08x\n", m_ipoll, m_imask);
+ logerror("dspad1 = 0x%08x dsoff1 = 0x%08x\n", m_dspad1, m_dsoff1);
+ logerror(" unk1 = 0x%08x unk2 = 0x%08x\n", m_unknown1, m_unknown2);
+ logerror(" ddtc1 = 0x%08x\n", data);
+
+ m_ddtc1 = data;
+
+ // when complete, we indicate by setting DMAEND(2) - 2 is probably the channel
+ // we also turn off the INTBERR and INTMMBE flags
+ m_ipoll &= ~(0x20000 | 0x10000);
+ m_ipoll |= 0x200;
+
+ // if the address is invalid, fake a bus error
+ if (m_dspad1 == 0x40000000 || m_unknown1 == 0x40000000
+ || m_dspad1 == 0x40000200 || m_unknown1 == 0x40000200)
+ {
+ m_ipoll |= 0x10000;
+
+ // error cycle - bit 0x10 indicates source address error (dspad1)
+ // now expecting 0x5463?
+#if 0
+ if ((m_dspad1 & 0xfffff000) == 0x40000000)
+ m_ioga->bus_error(m_dspad1, 0x5433);
+ else
+ m_ioga->bus_error(m_unknown1, 0x5423);
+#endif
+ // 0x5423 = BERR|SNAPOK | BG(ICAMMU)? | CT(23)
+ // 0x5433 = BERR|SNAPOK | BG(ICAMMU)? | CT(33)
+ // 0x5463 = BERR|SNAPOK | BG(ICAMMU)? | TAG(1) | CT(23)
+ }
+}
diff --git a/src/mame/machine/interpro_sga.h b/src/mame/machine/interpro_sga.h
new file mode 100644
index 00000000000..06c2d1fba8f
--- /dev/null
+++ b/src/mame/machine/interpro_sga.h
@@ -0,0 +1,71 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#pragma once
+
+#ifndef INTERPRO_SGA_H_
+#define INTERPRO_SGA_H_
+
+#include "emu.h"
+
+class interpro_sga_device : public device_t
+{
+public:
+ interpro_sga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_ADDRESS_MAP(map, 32);
+
+ DECLARE_READ32_MEMBER(gcs_r) { return m_gcs; }
+ DECLARE_WRITE32_MEMBER(gcs_w) { m_gcs = data; }
+ DECLARE_READ32_MEMBER(ipoll_r) { return m_ipoll; }
+ DECLARE_WRITE32_MEMBER(ipoll_w) { m_ipoll = data; }
+ DECLARE_READ32_MEMBER(imask_r) { return m_imask; }
+ DECLARE_WRITE32_MEMBER(imask_w) { m_imask = data; }
+ DECLARE_READ32_MEMBER(range_base_r) { return m_range_base; }
+ DECLARE_WRITE32_MEMBER(range_base_w) { m_range_base = data; }
+ DECLARE_READ32_MEMBER(range_end_r) { return m_range_end; }
+ DECLARE_WRITE32_MEMBER(range_end_w) { m_range_end = data; }
+ DECLARE_READ32_MEMBER(cttag_r) { return m_cttag; }
+ DECLARE_WRITE32_MEMBER(cttag_w) { m_cttag = data; }
+ DECLARE_READ32_MEMBER(address_r) { return m_address; }
+ DECLARE_WRITE32_MEMBER(address_w) { m_address = data; }
+ DECLARE_READ32_MEMBER(dmacs_r) { return m_dmacs; }
+ DECLARE_WRITE32_MEMBER(dmacs_w) { m_dmacs = data; }
+ DECLARE_READ32_MEMBER(edmacs_r) { return m_edmacs; }
+ DECLARE_WRITE32_MEMBER(edmacs_w) { m_edmacs = data; }
+ DECLARE_READ32_MEMBER(dspad1_r) { return m_dspad1; }
+ DECLARE_WRITE32_MEMBER(dspad1_w) { m_dspad1 = data; }
+ DECLARE_READ32_MEMBER(dsoff1_r) { return m_dsoff1; }
+ DECLARE_WRITE32_MEMBER(dsoff1_w) { m_dsoff1 = data; }
+ DECLARE_READ32_MEMBER(unknown1_r) { return m_unknown1; }
+ DECLARE_WRITE32_MEMBER(unknown1_w) { m_unknown1 = data; }
+ DECLARE_READ32_MEMBER(unknown2_r) { return m_unknown2; }
+ DECLARE_WRITE32_MEMBER(unknown2_w) { m_unknown2 = data; }
+ DECLARE_READ32_MEMBER(ddtc1_r) { return m_ddtc1; }
+ DECLARE_WRITE32_MEMBER(ddtc1_w);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ uint32_t m_gcs; // general control/status
+ uint32_t m_ipoll; // interrupt poll
+ uint32_t m_imask; // interrupt mask
+ uint32_t m_range_base;
+ uint32_t m_range_end;
+ uint32_t m_cttag; // error cycletype/tag
+ uint32_t m_address;
+ uint32_t m_dmacs; // dma control/status
+ uint32_t m_edmacs; // extended dma control/status
+ uint32_t m_dspad1;
+ uint32_t m_dsoff1;
+ uint32_t m_unknown1;
+ uint32_t m_unknown2;
+ uint32_t m_ddtc1;
+};
+
+// device type definition
+extern const device_type INTERPRO_SGA;
+
+#endif \ No newline at end of file
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 20c6c94b161..8696f5c93bc 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -14781,6 +14781,9 @@ instruct //
hector1 //
interact //
+@source:interpro.cpp
+ip2800 // Intergraph InterPro 28xx workstation
+
@source:intrscti.cpp
intrscti // ????
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index e1667a9b9e7..0a8c8680438 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -79,6 +79,7 @@ CPU_DISASSEMBLE( avr8 );
CPU_DISASSEMBLE( ccpu );
CPU_DISASSEMBLE( cdp1801 );
CPU_DISASSEMBLE( cdp1802 );
+CPU_DISASSEMBLE( clipper );
CPU_DISASSEMBLE( coldfire );
CPU_DISASSEMBLE( cop410 );
CPU_DISASSEMBLE( cop420 );
@@ -248,6 +249,7 @@ static const dasm_table_entry dasm_table[] =
{ "ccpu", _8bit, 0, CPU_DISASSEMBLE_NAME(ccpu) },
{ "cdp1801", _8bit, 0, CPU_DISASSEMBLE_NAME(cdp1801) },
{ "cdp1802", _8bit, 0, CPU_DISASSEMBLE_NAME(cdp1802) },
+ { "clipper", _16le, 0, CPU_DISASSEMBLE_NAME(clipper) },
{ "coldfire", _16be, 0, CPU_DISASSEMBLE_NAME(coldfire) },
{ "cop410", _8bit, 0, CPU_DISASSEMBLE_NAME(cop410) },
{ "cop420", _8bit, 0, CPU_DISASSEMBLE_NAME(cop420) },