diff options
-rw-r--r-- | scripts/src/cpu.lua | 16 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 16 | ||||
-rw-r--r-- | src/devices/cpu/clipper/clipper.cpp | 1375 | ||||
-rw-r--r-- | src/devices/cpu/clipper/clipper.h | 294 | ||||
-rw-r--r-- | src/devices/cpu/clipper/clipperd.cpp | 351 | ||||
-rw-r--r-- | src/mame/drivers/interpro.cpp | 345 | ||||
-rw-r--r-- | src/mame/includes/interpro.h | 131 | ||||
-rw-r--r-- | src/mame/machine/cammu.cpp | 180 | ||||
-rw-r--r-- | src/mame/machine/cammu.h | 93 | ||||
-rw-r--r-- | src/mame/machine/interpro_ioga.cpp | 691 | ||||
-rw-r--r-- | src/mame/machine/interpro_ioga.h | 256 | ||||
-rw-r--r-- | src/mame/machine/interpro_mcga.cpp | 89 | ||||
-rw-r--r-- | src/mame/machine/interpro_mcga.h | 45 | ||||
-rw-r--r-- | src/mame/machine/interpro_sga.cpp | 88 | ||||
-rw-r--r-- | src/mame/machine/interpro_sga.h | 71 | ||||
-rw-r--r-- | src/mame/mame.lst | 3 | ||||
-rw-r--r-- | src/tools/unidasm.cpp | 2 |
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) }, |