summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/clipper/clipper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/clipper/clipper.cpp')
-rw-r--r--src/devices/cpu/clipper/clipper.cpp1375
1 files changed, 1375 insertions, 0 deletions
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);
+}