summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/romp/romp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/romp/romp.cpp')
-rw-r--r--src/devices/cpu/romp/romp.cpp1206
1 files changed, 1206 insertions, 0 deletions
diff --git a/src/devices/cpu/romp/romp.cpp b/src/devices/cpu/romp/romp.cpp
new file mode 100644
index 00000000000..638af0efaa1
--- /dev/null
+++ b/src/devices/cpu/romp/romp.cpp
@@ -0,0 +1,1206 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM Research and Office Products Division Microprocessor (ROMP).
+ *
+ * Sources:
+ * - IBM RT PC Hardware Technical Reference Volume I, 75X0232, March 1987
+ *
+ * TODO:
+ * - configurable storage channel
+ * - multiple exceptions
+ * - check stop mask
+ * - advanced/enhanced variants
+ */
+
+#include "emu.h"
+#include "romp.h"
+#include "rompdasm.h"
+
+#define LOG_INTERRUPT (1U << 1)
+
+//#define VERBOSE (LOG_INTERRUPT)
+#include "logmacro.h"
+
+// instruction decode helpers
+#define R2 ((op >> 4) & 15)
+#define R3 (op & 15)
+
+DEFINE_DEVICE_TYPE(ROMP, romp_device, "romp", "IBM ROMP")
+
+ALLOW_SAVE_TYPE(romp_device::branch_state);
+
+romp_device::romp_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig, ROMP, tag, owner, clock)
+ , m_mem_config("memory", ENDIANNESS_BIG, 32, 32)
+ , m_mmu(*this, finder_base::DUMMY_TAG)
+ , m_iou(*this, finder_base::DUMMY_TAG)
+ , m_icount(0)
+ , m_reqi(0)
+{
+}
+
+void romp_device::device_start()
+{
+ // set instruction counter
+ set_icountptr(m_icount);
+
+ // register state for the debugger
+ state_add(STATE_GENPC, "GENPC", m_scr[IAR]).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_scr[IAR]).noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_scr[CS]).formatstr("%6s").noshow();
+
+ state_add(ROMP_SCR + IAR, "IAR", m_scr[IAR]);
+ state_add(ROMP_SCR + COUS, "COUS", m_scr[COUS]);
+ state_add(ROMP_SCR + COU, "COU", m_scr[COU]);
+ state_add(ROMP_SCR + TS, "TS", m_scr[TS]);
+ state_add(ROMP_SCR + MQ, "MQ", m_scr[MQ]);
+ state_add(ROMP_SCR + MPCS, "MPCS", m_scr[MPCS]);
+ state_add(ROMP_SCR + IRB, "IRB", m_scr[IRB]);
+ state_add(ROMP_SCR + ICS, "ICS", m_scr[ICS]);
+ state_add(ROMP_SCR + CS, "CS", m_scr[CS]);
+
+ for (unsigned i = 0; i < std::size(m_gpr); i++)
+ state_add(ROMP_GPR + i, util::string_format("R%d", i).c_str(), m_gpr[i]);
+
+ // register state for saving
+ save_item(NAME(m_scr));
+ save_item(NAME(m_gpr));
+
+ save_item(NAME(m_reqi));
+ save_item(NAME(m_trap));
+
+ save_item(NAME(m_branch_state));
+ save_item(NAME(m_branch_source));
+ save_item(NAME(m_branch_target));
+ save_item(NAME(m_defer_int));
+}
+
+void romp_device::state_string_export(device_state_entry const &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("%c%c%c%c%c%c",
+ (m_scr[CS] & CS_L) ? 'L' : '.',
+ (m_scr[CS] & CS_E) ? 'E' : '.',
+ (m_scr[CS] & CS_G) ? 'G' : '.',
+ (m_scr[CS] & CS_C) ? 'C' : '.',
+ (m_scr[CS] & CS_O) ? 'O' : '.',
+ (m_scr[CS] & CS_T) ? 'T' : '.');
+ break;
+ }
+}
+
+void romp_device::device_reset()
+{
+ for (u32 &scr : m_scr)
+ scr = 0;
+
+ for (u32 &gpr : m_gpr)
+ gpr = 0;
+
+ // initialize the state
+ m_trap = false;
+ m_branch_state = DEFAULT;
+ m_defer_int = false;
+
+ // fetch initial iar
+ load<u32>(0, [this](u32 data) { m_scr[IAR] = data; });
+}
+
+void romp_device::execute_run()
+{
+ // core execution loop
+ while (m_icount-- > 0)
+ {
+ if (m_defer_int)
+ m_defer_int = false;
+ else
+ interrupt_check();
+
+ if (m_branch_state == WAIT)
+ {
+ debugger_wait_hook();
+ m_icount = 0;
+ return;
+ }
+ else
+ debugger_instruction_hook(m_scr[IAR]);
+
+ // fetch instruction
+ u32 updated_iar = m_scr[IAR];
+ fetch(m_scr[IAR], [this, &updated_iar](u16 op)
+ {
+ updated_iar += 2;
+
+ switch (op >> 12)
+ {
+ case 0x0: // jb/jnb: jump on [not] condition bit
+ if (m_branch_state != BRANCH)
+ {
+ if (BIT(m_scr[CS], ((op >> 8) & 7) ^ 7) == BIT(op, 11))
+ {
+ m_branch_target = m_scr[IAR] + ji(op);
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ }
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0x1: // stcs: store character short
+ store<u8>(r3_0(R3) + ((op >> 8) & 15), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+ case 0x2: // sths: store half short
+ store<u16>(r3_0(R3) + ((op >> 7) & 30), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+ case 0x3: // sts: store short
+ store<u32>(r3_0(R3) + ((op >> 6) & 60), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+ case 0x4: // lcs: load character short
+ load<u8>(r3_0(R3) + ((op >> 8) & 15), [this, op](u8 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0x5: // lhas: load half algebraic short
+ load<u16>(r3_0(R3) + ((op >> 7) & 30), [this, op](u16 data) { m_gpr[R2] = s32(s16(data)); });
+ m_icount -= 4;
+ break;
+ case 0x6: // cas: compute address short
+ m_gpr[(op >> 8) & 15] = m_gpr[R2] + r3_0(R3);
+ break;
+ case 0x7: // ls: load short
+ load<u32>(r3_0(R3) + ((op >> 6) & 60), [this, op](u32 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0x8: // BI, BA format
+ fetch(updated_iar, [this, &updated_iar, op](u16 b)
+ {
+ updated_iar += 2;
+
+ if (m_branch_state == BRANCH)
+ {
+ program_check(PCS_PCK | PCS_IOC);
+ return;
+ }
+
+ switch (op >> 8)
+ {
+ case 0x88: // bnb: branch on not condition bit immediate
+ if (!BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ }
+ break;
+ case 0x89: // bnbx: branch on not condition bit immediate with execute
+ if (!BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = DELAY;
+ m_icount -= 4;
+ }
+ break;
+ case 0x8a: // bala: branch and link absolute
+ m_gpr[15] = updated_iar;
+ m_branch_target = ba(op, b);
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ break;
+ case 0x8b: // balax: branch and link absolute with execute
+ m_gpr[15] = updated_iar + 4;
+ m_branch_source = m_scr[IAR];
+ m_branch_target = ba(op, b);
+ m_branch_state = DELAY;
+ m_icount -= 4;
+ break;
+ case 0x8c: // bali: branch and link immediate
+ m_gpr[R2] = updated_iar;
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ break;
+ case 0x8d: // balix: branch and link immediate with execute
+ m_gpr[R2] = updated_iar + 4;
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = DELAY;
+ m_icount -= 4;
+ break;
+ case 0x8e: // bb: branch on condition bit immediate
+ if (BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ }
+ break;
+ case 0x8f: // bbx: branch on condition bit immediate with execute
+ if (BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_scr[IAR] + bi(op, b);
+ m_branch_state = DELAY;
+ m_icount -= 4;
+ }
+ break;
+
+ default:
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ }
+ });
+ break;
+
+ case 0xc:
+ case 0xd: // D format
+ fetch(updated_iar, [this, &updated_iar, op](u16 i)
+ {
+ updated_iar += 2;
+
+ u32 const r3 = R3 ? m_gpr[R3] : 0;
+
+ switch (op >> 8)
+ {
+ case 0xc0: // svc: supervisor call
+ if (m_branch_state != BRANCH)
+ {
+ interrupt_enter(9, updated_iar, r3 + i);
+ m_branch_state = EXCEPTION;
+
+ m_icount -= 15;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xc1: // ai: add immediate
+ flags_add(m_gpr[R3], s32(s16(i)));
+ m_gpr[R2] = m_gpr[R3] + s32(s16(i));
+ break;
+ case 0xc2: // cal16: compute address lower half 16-bit
+ m_gpr[R2] = (r3 & 0xffff'0000U) | u16(r3 + i);
+ break;
+ case 0xc3: // oiu: or immediate upper half
+ m_gpr[R2] = (u32(i) << 16) | m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xc4: // oil: or immediate lower half
+ m_gpr[R2] = u32(i) | m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xc5: // nilz: and immediate lower half extended zeroes
+ m_gpr[R2] = u32(i) & m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xc6: // nilo: and immediate lower half extended ones
+ m_gpr[R2] = (i | 0xffff'0000U) & m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xc7: // xil: exclusive or immediate lower half
+ m_gpr[R2] = u32(i) ^ m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xc8: // cal: compute address lower half
+ m_gpr[R2] = r3 + s16(i);
+ break;
+ case 0xc9: // lm: load multiple
+ for (unsigned reg = R2, offset = r3 + s16(i); (reg < 16) && m_branch_state != EXCEPTION; reg++, offset += 4)
+ {
+ load<u32>(offset, [this, reg](u32 data) { m_gpr[reg] = data; });
+ m_icount -= 2;
+ }
+ m_icount -= (m_scr[ICS] & ICS_TM) ? 3 : 1;
+ break;
+ case 0xca: // lha: load half algebraic
+ load<u16>(r3 + s16(i), [this, op](u16 data) { m_gpr[R2] = s32(s16(data)); });
+ m_icount -= 4;
+ break;
+ case 0xcb: // ior: input/output read
+ if (((r3 + i) & 0xff00'0000U) || !m_mmu->pio_load(r3 + i, m_gpr[R2]))
+ program_check(PCS_PCK | PCS_DAE);
+ break;
+ case 0xcc: // ti: trap on condition immediate
+ if (m_branch_state != BRANCH)
+ {
+ if ((BIT(op, 6) && (m_gpr[R3] < u32(s32(s16(i)))))
+ || (BIT(op, 5) && (m_gpr[R3] == u32(s32(s16(i)))))
+ || (BIT(op, 4) && (m_gpr[R3] > u32(s32(s16(i))))))
+ program_check(PCS_PCK | PCS_PT);
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xcd: // l: load
+ load<u32>(r3 + s16(i), [this, op](u32 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0xce: // lc: load character
+ load<u8>(r3 + s16(i), [this, op](u8 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0xcf: // tsh: test and set half
+ modify<u16>(r3 + s16(i), [this, op](u16 data)
+ {
+ m_gpr[R2] = data;
+
+ return 0xff00 | data;
+ });
+ m_icount -= 4;
+ break;
+
+ case 0xd0: // lps: load program status
+ if (!(m_scr[ICS] & ICS_US))
+ {
+ if (m_branch_state != BRANCH)
+ {
+ load<u32>(r3 + s16(i) + 0, [this](u32 data) { m_branch_target = data; });
+ m_branch_state = BRANCH;
+ load<u16>(r3 + s16(i) + 6, [this](u16 data) { m_scr[CS] = data; });
+ load<u16>(r3 + s16(i) + 4, [this](u16 data) { m_scr[ICS] = data; });
+ if (m_scr[MPCS] & MCS_ALL)
+ m_scr[MPCS] &= ~MCS_ALL;
+ else
+ m_scr[MPCS] &= ~PCS_ALL;
+
+ // optionally defer interrupts
+ m_defer_int = BIT(op, 4);
+
+ m_icount -= 15;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ }
+ else
+ program_check(PCS_PCK | PCS_PIE);
+ break;
+ case 0xd1: // aei: add extended immediate
+ flags_add(m_gpr[R3], s32(s16(i)) + bool(m_scr[CS] & CS_C));
+ m_gpr[R2] = m_gpr[R3] + s32(s16(i)) + bool(m_scr[CS] & CS_C);
+ break;
+ case 0xd2: // sfi: subtract from immediate
+ flags_sub(s32(s16(i)), m_gpr[R3]);
+ m_gpr[R2] = s32(s16(i)) - m_gpr[R3];
+ break;
+ case 0xd3: // cli: compare logical immediate
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+ if (m_gpr[R3] == u32(s32(s16(i))))
+ m_scr[CS] |= CS_E;
+ else if (m_gpr[R3] < u32(s32(s16(i))))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+ break;
+ case 0xd4: // ci: compare immediate
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+ if (s32(m_gpr[R3]) == s32(s16(i)))
+ m_scr[CS] |= CS_E;
+ else if (s32(m_gpr[R3]) < s32(s16(i)))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+ break;
+ case 0xd5: // niuz: and immediate upper half extended zeroes
+ m_gpr[R2] = (u32(i) << 16) & m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xd6: // niuo: and immediate upper half extended ones
+ m_gpr[R2] = ((u32(i) << 16) | 0x0000'ffffU) & m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xd7: // xiu: exclusive or immediate upper half
+ m_gpr[R2] = (u32(i) << 16) ^ m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xd8: // cau: compute address upper half
+ m_gpr[R2] = r3 + (u32(i) << 16);
+ break;
+ case 0xd9: // stm: store multiple
+ for (unsigned reg = R2, offset = r3 + s16(i); (reg < 16) && m_branch_state != EXCEPTION; reg++, offset += 4)
+ {
+ store<u32>(offset, m_gpr[reg]);
+ m_icount -= (m_scr[ICS] & ICS_TM) ? 3 : 2;
+ }
+ m_icount -= (m_scr[ICS] & ICS_TM) ? 3 : 2;
+ break;
+ case 0xda: // lh: load half
+ load<u16>(r3 + s16(i), [this, op](u16 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0xdb: // iow: input/output write
+ if (((r3 + i) & 0xff00'0000U) || !m_mmu->pio_store(r3 + i, m_gpr[R2]))
+ program_check(PCS_PCK | PCS_DAE);
+ m_icount--;
+ break;
+ case 0xdc: // sth: store half
+ store<u16>(r3 + s16(i), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+ case 0xdd: // st: store
+ store<u32>(r3 + s16(i), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+ case 0xde: // stc: store character
+ store<u8>(r3 + s16(i), m_gpr[R2]);
+ m_icount -= 4;
+ break;
+
+ default:
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ }
+ });
+ break;
+
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xe:
+ case 0xf: // R format
+ switch (op >> 8)
+ {
+ case 0x90: // ais: add immediate short
+ flags_add(m_gpr[R2], R3);
+ m_gpr[R2] += R3;
+ break;
+ case 0x91: // inc: increment
+ m_gpr[R2] += R3;
+ break;
+ case 0x92: // sis: subtract immediate short
+ flags_sub(m_gpr[R2], R3);
+ m_gpr[R2] -= R3;
+ break;
+ case 0x93: // dec: decrement
+ m_gpr[R2] -= R3;
+ break;
+ case 0x94: // cis: compare immediate short
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+ if (m_gpr[R2] == R3)
+ m_scr[CS] |= CS_E;
+ else if (s32(m_gpr[R2]) < s32(R3))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+ break;
+ case 0x95: // clrsb: clear scr bit
+ set_scr(R2, m_scr[R2] & ~(0x0000'8000U >> R3));
+ m_icount -= 3;
+ break;
+ case 0x96: // mfs: move from scr
+ if (!(m_scr[ICS] & ICS_US) || R2 == MQ || R2 == CS)
+ {
+ if (R2 == IAR)
+ m_gpr[R3] = updated_iar;
+ else
+ m_gpr[R3] = m_scr[R2];
+ }
+ else
+ program_check(PCS_PCK | PCS_PIE);
+ m_icount--;
+ break;
+ case 0x97: // setsb: set scr bit
+ set_scr(R2, m_scr[R2] | (0x0000'8000U >> R3));
+ m_icount -= 3;
+ break;
+ case 0x98: // clrbu: clear bit upper half
+ m_gpr[R2] &= ~(0x8000'0000U >> R3);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0x99: // clrbl: clear bit lower half
+ m_gpr[R2] &= ~(0x0000'8000U >> R3);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0x9a: // setbu: set bit upper half
+ m_gpr[R2] |= (0x8000'0000U >> R3);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0x9b: // setbl: set bit lower half
+ m_gpr[R2] |= (0x0000'8000U >> R3);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0x9c: // mftbiu: move from test bit immediate upper half
+ if (m_scr[CS] & CS_T)
+ m_gpr[R2] |= (0x8000'0000U >> R3);
+ else
+ m_gpr[R2] &= ~(0x8000'0000U >> R3);
+ break;
+ case 0x9d: // mftbil: move from test bit immediate lower half
+ if (m_scr[CS] & CS_T)
+ m_gpr[R2] |= (0x0000'8000U >> R3);
+ else
+ m_gpr[R2] &= ~(0x0000'8000U >> R3);
+ break;
+ case 0x9e: // mttbiu: move to test bit immediate upper half
+ if (m_gpr[R2] & (0x8000'0000U >> R3))
+ m_scr[CS] |= CS_T;
+ else
+ m_scr[CS] &= ~CS_T;
+ break;
+ case 0x9f: // mttbil: move to test bit immediate lower half
+ if (m_gpr[R2] & (0x0000'8000U >> R3))
+ m_scr[CS] |= CS_T;
+ else
+ m_scr[CS] &= ~CS_T;
+ break;
+
+ case 0xa0: // sari: shift algebraic right immediate
+ m_gpr[R2] = s32(m_gpr[R2]) >> R3;
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xa1: // sari16: shift algebraic right immediate plus sixteen
+ m_gpr[R2] = s32(m_gpr[R2]) >> (R3 + 16);
+ flags_log(m_gpr[R2]);
+ break;
+
+ case 0xa4: // lis: load immediate short
+ m_gpr[R2] = R3;
+ break;
+
+ case 0xa8: // sri: shift right immediate
+ m_gpr[R2] >>= R3;
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xa9: // sri16: shift right immediate plus sixteen
+ m_gpr[R2] >>= (R3 + 16);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xaa: // sli: shift left immediate
+ m_gpr[R2] <<= R3;
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xab: // sli16: shift left immediate plus sixteen
+ m_gpr[R2] <<= (R3 + 16);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xac: // srpi: shift right paired immediate
+ m_gpr[R2 ^ 1] = m_gpr[R2] >> R3;
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+ case 0xad: // srpi16: shift right paired immediate plus sixteen
+ m_gpr[R2 ^ 1] = m_gpr[R2] >> (R3 + 16);
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+ case 0xae: // slpi: shift left paired immediate
+ m_gpr[R2 ^ 1] = m_gpr[R2] << R3;
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+ case 0xaf: // slpi16: shift left paired immediate plus sixteen
+ m_gpr[R2 ^ 1] = m_gpr[R2] << (R3 + 16);
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+
+ case 0xb0: // sar: shift algebraic right
+ m_gpr[R2] = s32(m_gpr[R2]) >> (m_gpr[R3] & 63);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xb1: // exts: extend sign
+ m_gpr[R2] = s16(m_gpr[R3]);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xb2: // sf: subtract from
+ flags_sub(m_gpr[R3], m_gpr[R2]);
+ m_gpr[R2] = m_gpr[R3] - m_gpr[R2];
+ break;
+ case 0xb3: // cl: compare logical
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+ if (m_gpr[R2] == m_gpr[R3])
+ m_scr[CS] |= CS_E;
+ else if (m_gpr[R2] < m_gpr[R3])
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+ break;
+ case 0xb4: // c: compare
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+ if (s32(m_gpr[R2]) == s32(m_gpr[R3]))
+ m_scr[CS] |= CS_E;
+ else if (s32(m_gpr[R2]) < s32(m_gpr[R3]))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+ break;
+ case 0xb5: // mts: move to scr
+ set_scr(R2, m_gpr[R3]);
+ m_icount -= 2;
+ break;
+ case 0xb6: // d: divide step
+ {
+ m_scr[CS] &= ~(CS_C | CS_O);
+
+ s64 sum = (s64(s32(m_gpr[R2])) << 1) | (m_scr[MQ] >> 31);
+
+ if (BIT(m_gpr[R2], 31) == BIT(m_gpr[R3], 31))
+ sum -= s32(m_gpr[R3]);
+ else
+ sum += s32(m_gpr[R3]);
+
+ // update remainder
+ m_gpr[R2] = sum;
+
+ // update quotient
+ m_scr[MQ] <<= 1;
+ if (BIT(sum, 32) == BIT(m_gpr[R3], 31))
+ {
+ m_scr[MQ] |= 1;
+ m_scr[CS] |= CS_C;
+ }
+
+ // overflow test
+ if (BIT(sum, 32) == BIT(m_gpr[R2], 31))
+ m_scr[CS] |= CS_O;
+ }
+ m_icount -= 2;
+ break;
+ case 0xb8: // sr: shift right
+ m_gpr[R2] >>= (m_gpr[R3] & 63);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xb9: // srp: shift right paired
+ m_gpr[R2 ^ 1] = m_gpr[R2] >> (m_gpr[R3] & 63);
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+ case 0xba: // sl: shift left
+ m_gpr[R2] <<= (m_gpr[R3] & 63);
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xbb: // slp: shift left paired
+ m_gpr[R2 ^ 1] = m_gpr[R2] << (m_gpr[R3] & 63);
+ flags_log(m_gpr[R2 ^ 1]);
+ break;
+ case 0xbc: // mftb: move from test bit
+ if (m_scr[CS] & CS_T)
+ m_gpr[R2] |= (0x8000'0000U >> (m_gpr[R3] & 31));
+ else
+ m_gpr[R2] &= ~(0x8000'0000U >> (m_gpr[R3] & 31));
+ break;
+ case 0xbd: // tgte: trap if register greater than or equal
+ if (m_branch_state != BRANCH)
+ {
+ if (m_gpr[R2] >= m_gpr[R3])
+ {
+ program_check(PCS_PCK | PCS_PT);
+ m_icount -= 14;
+ }
+
+ m_icount--;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xbe: // tlt: trap if register less than
+ if (m_branch_state != BRANCH)
+ {
+ if (m_gpr[R2] < m_gpr[R3])
+ {
+ program_check(PCS_PCK | PCS_PT);
+ m_icount -= 14;
+ }
+
+ m_icount--;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xbf: // mttb: move to test bit
+ if (m_gpr[R2] & (0x8000'0000U >> (m_gpr[R3] & 31)))
+ m_scr[CS] |= CS_T;
+ else
+ m_scr[CS] &= ~CS_T;
+ break;
+
+ case 0xe0: // abs: absolute
+ m_scr[CS] &= ~(CS_C | CS_O);
+ if (m_gpr[R3] == 0x8000'0000U)
+ {
+ m_gpr[R2] = m_gpr[R3];
+ m_scr[CS] |= CS_O;
+ }
+ else if (s32(m_gpr[R3]) < 0)
+ m_gpr[R2] = -s32(m_gpr[R3]);
+ else
+ m_gpr[R2] = m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ m_icount--;
+ break;
+ case 0xe1: // a: add
+ flags_add(m_gpr[R2], m_gpr[R3]);
+ m_gpr[R2] += m_gpr[R3];
+ break;
+ case 0xe2: // s: subtract
+ flags_sub(m_gpr[R2], m_gpr[R3]);
+ m_gpr[R2] -= m_gpr[R3];
+ break;
+ case 0xe3: // o: or
+ m_gpr[R2] |= m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xe4: // twoc: twos complement
+ flags_sub(0, m_gpr[R3]);
+ m_gpr[R2] = -m_gpr[R3];
+ break;
+ case 0xe5: // n: and
+ m_gpr[R2] &= m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xe6: // m: multiply step
+ {
+ s64 sum = s32(m_gpr[R2]);
+
+ if (m_scr[CS] & CS_C)
+ {
+ // no carry
+ switch (m_scr[MQ] & 3)
+ {
+ case 1: sum += s32(m_gpr[R3]); break;
+ case 2: sum -= s64(s32(m_gpr[R3])) * 2; break;
+ case 3: sum -= s32(m_gpr[R3]); break;
+ }
+ }
+ else
+ {
+ // carry
+ switch (m_scr[MQ] & 3)
+ {
+ case 0: sum += s32(m_gpr[R3]); break;
+ case 1: sum += s64(s32(m_gpr[R3])) * 2; break;
+ case 2: sum -= s32(m_gpr[R3]); break;
+ }
+ }
+
+ // update carry flag
+ if (m_scr[MQ] & 2)
+ m_scr[CS] &= ~CS_C;
+ else
+ m_scr[CS] |= CS_C;
+
+ m_scr[MQ] = (sum << 30) | (m_scr[MQ] >> 2);
+ m_gpr[R2] = sum >> 2;
+ }
+ m_icount -= 3;
+ break;
+ case 0xe7: // x: exclusive or
+ m_gpr[R2] ^= m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xe8: // bnbr: branch on not condition bit
+ if (m_branch_state != BRANCH)
+ {
+ if (!BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = BRANCH;
+ }
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xe9: // bnbrx: branch on not condition bit with execute
+ if (m_branch_state != BRANCH)
+ {
+ if (!BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = DELAY;
+ }
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+
+ case 0xeb: // lhs: load half short
+ load<u16>(m_gpr[R3], [this, op](u16 data) { m_gpr[R2] = data; });
+ m_icount -= 4;
+ break;
+ case 0xec: // balr: branch and link
+ if (m_branch_state != BRANCH)
+ {
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = BRANCH;
+ m_gpr[R2] = updated_iar;
+ m_icount -= 4;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xed: // balrx: branch and link with execute
+ if (m_branch_state != BRANCH)
+ {
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = DELAY;
+ m_gpr[R2] = updated_iar + 4;
+ m_icount -= 4;
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xee: // bbr: branch on condition bit
+ if (m_branch_state != BRANCH)
+ {
+ if (BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = BRANCH;
+ m_icount -= 4;
+ }
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ case 0xef: // bbrx: branch on condition bit with execute
+ if (m_branch_state != BRANCH)
+ {
+ if (BIT(m_scr[CS], R2 ^ 15))
+ {
+ m_branch_source = m_scr[IAR];
+ m_branch_target = m_gpr[R3] & ~1;
+ m_branch_state = DELAY;
+ m_icount -= 4;
+ }
+ }
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+
+ case 0xf0: // wait: wait
+ if (!(m_scr[ICS] & ICS_US))
+ {
+ if (m_branch_state != BRANCH)
+ m_branch_state = WAIT;
+ else
+ program_check(PCS_PCK | PCS_IOC);
+ }
+ else
+ program_check(PCS_PCK | PCS_PIE);
+ break;
+ case 0xf1: // ae: add extended
+ flags_add(m_gpr[R2], m_gpr[R3] + bool(m_scr[CS] & CS_C));
+ m_gpr[R2] += m_gpr[R3] + bool(m_scr[CS] & CS_C);
+ break;
+ case 0xf2: // se: subtract extended
+ flags_add(m_gpr[R2], ~m_gpr[R3] + bool(m_scr[CS] & CS_C));
+ m_gpr[R2] += ~m_gpr[R3] + bool(m_scr[CS] & CS_C);
+ break;
+ case 0xf3: // ca16: compute address 16-bit
+ m_gpr[R2] = (m_gpr[R3] & 0xffff'0000U) | (u16(m_gpr[R2]) + u16(m_gpr[R3]));
+ break;
+ case 0xf4: // onec: ones complement
+ m_gpr[R2] = ~m_gpr[R3];
+ flags_log(m_gpr[R2]);
+ break;
+ case 0xf5: // clz: count leading zeros
+ m_gpr[R2] = count_leading_zeros_32(u16(m_gpr[R3])) - 16;
+ break;
+
+ case 0xf9: // mc03: move character zero from three
+ m_gpr[R2] = (m_gpr[R2] & 0x00ff'ffffU) | ((m_gpr[R3] & 0x0000'000ffU) << 24);
+ break;
+ case 0xfa: // mc13: move character one from three
+ m_gpr[R2] = (m_gpr[R2] & 0xff00'ffffU) | ((m_gpr[R3] & 0x0000'000ffU) << 16);
+ break;
+ case 0xfb: // mc23: move character two from three
+ m_gpr[R2] = (m_gpr[R2] & 0xffff'00ffU) | ((m_gpr[R3] & 0x0000'000ffU) << 8);
+ break;
+ case 0xfc: // mc33: move character three from three
+ m_gpr[R2] = (m_gpr[R2] & 0xffff'ff00U) | ((m_gpr[R3] & 0x0000'000ffU) << 0);
+ break;
+ case 0xfd: // mc30: move character three from zero
+ m_gpr[R2] = (m_gpr[R2] & 0xffff'ff00U) | u8(m_gpr[R3] >> 24);
+ break;
+ case 0xfe: // mc31: move character three from one
+ m_gpr[R2] = (m_gpr[R2] & 0xffff'ff00U) | u8(m_gpr[R3] >> 16);
+ break;
+ case 0xff: // mc32: move character three from two
+ m_gpr[R2] = (m_gpr[R2] & 0xffff'ff00U) | u8(m_gpr[R3] >> 8);
+ break;
+
+ default:
+ program_check(PCS_PCK | PCS_IOC);
+ break;
+ }
+ break;
+ }
+
+ });
+ // update iar and branch state
+ switch (m_branch_state)
+ {
+ case DEFAULT:
+ m_scr[IAR] = updated_iar;
+ break;
+
+ case BRANCH:
+ m_scr[IAR] = m_branch_target;
+ m_branch_state = DEFAULT;
+ break;
+
+ case DELAY:
+ m_scr[IAR] = updated_iar;
+ m_branch_state = BRANCH;
+ m_defer_int = true;
+ break;
+
+ case EXCEPTION:
+ m_branch_state = DEFAULT;
+ break;
+
+ case WAIT:
+ // TODO: assume iar is updated
+ m_scr[IAR] = updated_iar;
+ break;
+
+ }
+ }
+}
+
+void romp_device::set_scr(unsigned scr, u32 data)
+{
+ if (VERBOSE & LOG_GENERAL)
+ {
+ static char const *const scr_names[16] =
+ {
+ "scr0", "scr1", "scr2", "scr3", "scr4", "scr5", "cous", "cou",
+ "ts", "ecr", "mq", "mpcs", "irb", "iar", "ics", "cs",
+ };
+
+ LOG("set_scr %s data 0x%08x (%s)\n", scr_names[scr], data, machine().describe_context());
+ }
+
+ static u32 const scr_mask[16] =
+ {
+ 0, 0, 0, 0, 0, 0, // reserved
+ 0xffff'ffffU, 0xffff'ffffU, // counter source and counter
+ TS_E | TS_I | TS_O | TS_P,
+ 0, // reserved
+ 0xffff'ffffU, // multiplier quotient
+ MCS_ALL | PCS_ALL,
+ IRB_ALL,
+ 0xffff'fffeU, // instruction address
+ ICS_PE | ICS_MP | ICS_US | ICS_TM | ICS_IM | ICS_CS | ICS_PP,
+ CS_L | CS_E | CS_G | CS_C | CS_O | CS_T,
+ };
+
+ if (!(m_scr[ICS] & ICS_US) || scr == MQ || scr == CS)
+ m_scr[scr] = data & scr_mask[scr];
+ else
+ program_check(PCS_PCK | PCS_PIE);
+}
+
+void romp_device::execute_set_input(int irqline, int state)
+{
+ switch (irqline)
+ {
+ case INPUT_LINE_NMI:
+ if (!state)
+ m_trap = true;
+ break;
+
+ default:
+ // interrupt lines are active low
+ if (!state)
+ m_reqi |= 1U << irqline;
+ else
+ m_reqi &= ~(1U << irqline);
+ break;
+ }
+}
+
+device_memory_interface::space_config_vector romp_device::memory_space_config() const
+{
+ return space_config_vector { std::make_pair(AS_PROGRAM, &m_mem_config) };
+}
+
+bool romp_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
+{
+ target_space = &space(spacenum);
+ return true;
+}
+
+std::unique_ptr<util::disasm_interface> romp_device::create_disassembler()
+{
+ return std::make_unique<romp_disassembler>();
+}
+
+void romp_device::flags_log(u32 const data)
+{
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G);
+
+ if (data == 0)
+ m_scr[CS] |= CS_E;
+ else
+ if (BIT(data, 31))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+}
+
+void romp_device::flags_add(u32 const op1, u32 const op2)
+{
+ u32 const result = op1 + op2;
+
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G | CS_C | CS_O);
+
+ if (result == 0)
+ m_scr[CS] |= CS_E;
+ else
+ if (BIT(result, 31))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+
+ // carry
+ if ((BIT(op2, 31) && BIT(op1, 31)) || (!BIT(result, 31) && (BIT(op2, 31) || BIT(op1, 31))))
+ m_scr[CS] |= CS_C;
+
+ // overflow
+ if ((BIT(op2, 31) == BIT(op1, 31)) && (BIT(result, 31) != BIT(op2, 31)))
+ m_scr[CS] |= CS_O;
+}
+
+void romp_device::flags_sub(u32 const op1, u32 const op2)
+{
+ u32 const result = op1 - op2;
+
+ m_scr[CS] &= ~(CS_L | CS_E | CS_G | CS_O);
+
+ if (result == 0)
+ m_scr[CS] |= CS_E;
+ else
+ if (BIT(result, 31))
+ m_scr[CS] |= CS_L;
+ else
+ m_scr[CS] |= CS_G;
+
+ // borrow
+ if ((!BIT(op2, 31) && BIT(op1, 31)) || (BIT(result, 31) && (!BIT(op2, 31) || BIT(op1, 31))))
+ m_scr[CS] &= ~CS_C;
+ else
+ m_scr[CS] |= CS_C;
+
+ // overflow
+ if ((BIT(op2, 31) != BIT(op1, 31)) && (BIT(result, 31) != BIT(op2, 31)))
+ m_scr[CS] |= CS_O;
+}
+
+void romp_device::interrupt_check()
+{
+ if (m_trap)
+ {
+ // TODO: traps with check-stop mask 0
+ machine_check(MCS_IOT);
+ m_trap = false;
+ return;
+ }
+
+ // interrupts masked, machine/program check, or no interrupts
+ if ((m_scr[ICS] & ICS_IM) || m_scr[MPCS] || !(m_reqi || (m_scr[IRB] & IRB_ALL)))
+ return;
+
+ unsigned const priority = m_scr[ICS] & ICS_PP;
+ for (unsigned irl = 0; irl < priority; irl++)
+ {
+ if (BIT(m_reqi, irl) || BIT(m_scr[IRB], 15 - irl))
+ {
+ // enable debugger interrupt breakpoints
+ standard_irq_callback(irl, m_scr[IAR]);
+
+ LOGMASKED(LOG_INTERRUPT, "interrupt_check taking interrupt request level %d\n", irl);
+ interrupt_enter(irl, m_scr[IAR]);
+
+ return;
+ }
+ }
+}
+
+void romp_device::machine_check(u32 mcs)
+{
+ debugger_exception_hook(7);
+
+ LOGMASKED(LOG_INTERRUPT, "machine_check mcs 0x%08x\n", mcs);
+
+ m_scr[MPCS] &= ~MCS_ALL;
+ m_scr[MPCS] |= (mcs & MCS_ALL);
+
+ interrupt_enter(7, m_scr[IAR]);
+}
+
+void romp_device::program_check(u32 pcs)
+{
+ debugger_exception_hook(8);
+
+ LOGMASKED(LOG_INTERRUPT, "program_check pcs 0x%08x\n", pcs);
+
+ m_scr[MPCS] &= ~PCS_ALL;
+ m_scr[MPCS] |= (pcs & PCS_ALL);
+
+ /*
+ * Old program status reflects the address of a branch with execute
+ * instruction when a program-check error occurs due to either a invalid
+ * subject instruction or a data address exception caused by a subject
+ * instruction.
+ */
+ // TODO: what about program check caused by subject instruction fetch?
+ interrupt_enter(8, m_branch_state == BRANCH ? m_branch_source : m_scr[IAR]);
+
+ m_branch_state = EXCEPTION;
+}
+
+void romp_device::interrupt_enter(unsigned vector, u32 iar, u16 svc)
+{
+ // compute program status pair address
+ u32 const address = 0x100 + vector * 16;
+
+ // save old program status
+ // TODO: error handling
+ store<u32>(address + 0, iar, false);
+ store<u16>(address + 4, u16(m_scr[ICS]), false);
+ store<u16>(address + 6, u16(m_scr[CS]), false);
+ if (vector == 9)
+ store<u16>(address + 14, svc, false);
+
+ // load new program status
+ load<u32>(address + 8, [this](u32 data) { m_scr[IAR] = data; }, false);
+ load<u16>(address + 12, [this](u16 data) { m_scr[ICS] = data; }, false);
+ if (vector < 7)
+ load<u16>(address + 14, [this](u16 data) { m_scr[CS] = data; }, false);
+
+ m_branch_state = DEFAULT;
+}
+
+void romp_device::clk_w(int state)
+{
+ if (state)
+ {
+ // decrement counter
+ if (m_scr[COU])
+ m_scr[COU]--;
+
+ // check counter expiry
+ if (!m_scr[COU])
+ {
+ // check alarm enabled
+ if (m_scr[TS] & TS_E)
+ {
+ // overflow check
+ if (m_scr[TS] & TS_I)
+ m_scr[TS] |= TS_O;
+
+ // set status
+ m_scr[TS] |= TS_I;
+
+ // raise interrupt
+ if ((m_scr[TS] & TS_P) < 7)
+ m_scr[IRB] |= IRB_L0 >> (m_scr[TS] & TS_P);
+ }
+
+ // reload counter
+ m_scr[COU] = m_scr[COUS];
+ }
+ }
+}