From 678465003898672024604326b4cea2e0bd2d6b75 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Sat, 12 Jan 2019 12:51:06 +0100 Subject: unsp: Created a SunPlus u'nSP recompiler. [Ryan Holtz] --- scripts/src/cpu.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 08fe95f7f68..654f5bc41ca 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -13,7 +13,7 @@ -- Dynamic recompiler objects -------------------------------------------------- -DRC_CPUS = { "E1", "SH", "MIPS3", "POWERPC", "RSP", "ARM7", "ADSP21062", "MB86235", "DSP16" } +DRC_CPUS = { "E1", "SH", "MIPS3", "POWERPC", "RSP", "ARM7", "ADSP21062", "MB86235", "DSP16", "UNSP" } CPU_INCLUDE_DRC = false for i, v in ipairs(DRC_CPUS) do if (CPUS[v]~=null) then @@ -2157,6 +2157,10 @@ if (CPUS["UNSP"]~=null) then files { MAME_DIR .. "src/devices/cpu/unsp/unsp.cpp", MAME_DIR .. "src/devices/cpu/unsp/unsp.h", + MAME_DIR .. "src/devices/cpu/unsp/unspdefs.h", + MAME_DIR .. "src/devices/cpu/unsp/unspdrc.cpp", + MAME_DIR .. "src/devices/cpu/unsp/unspfe.cpp", + MAME_DIR .. "src/devices/cpu/unsp/unspfe.h", } end -- cgit v1.2.3-70-g09d2 From bb0702d2d5f5ca6c89049ae6285c0701f2a488fb Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Thu, 24 Jan 2019 15:03:38 +0700 Subject: alpha: new cpu, disassembler only --- scripts/src/cpu.lua | 17 + scripts/target/mame/mess.lua | 1 + src/devices/cpu/alpha/alpha.cpp | 91 +++++ src/devices/cpu/alpha/alpha.h | 56 +++ src/devices/cpu/alpha/alphad.cpp | 783 +++++++++++++++++++++++++++++++++++++++ src/devices/cpu/alpha/alphad.h | 23 ++ src/tools/unidasm.cpp | 2 + 7 files changed, 973 insertions(+) create mode 100644 src/devices/cpu/alpha/alpha.cpp create mode 100644 src/devices/cpu/alpha/alpha.h create mode 100644 src/devices/cpu/alpha/alphad.cpp create mode 100644 src/devices/cpu/alpha/alphad.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 654f5bc41ca..c3218773035 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -2780,3 +2780,20 @@ if (_OPTIONS["with-tools"]) then table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nuon/nuondasm.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/nuon/nuondasm.h") end + +-------------------------------------------------- +-- DEC Alpha (EV4/EV5/EV6/EV7) series +--@src/devices/cpu/alpha/alpha.h,CPUS["ALPHA"] = true +-------------------------------------------------- + +if (CPUS["ALPHA"]~=null) then + files { + MAME_DIR .. "src/devices/cpu/alpha/alpha.cpp", + MAME_DIR .. "src/devices/cpu/alpha/alpha.h", + } +end + +if (CPUS["ALPHA"]~=null or _OPTIONS["with-tools"]) then + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/alpha/alphad.cpp") + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/alpha/alphad.h") +end diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 87239e7d969..9e8f282c9e6 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -136,6 +136,7 @@ CPUS["MB86901"] = true CPUS["NANOPROCESSOR"] = true CPUS["CLIPPER"] = true CPUS["CAPRICORN"] = true +CPUS["ALPHA"] = true --CPUS["DSPP"] = true -------------------------------------------------- diff --git a/src/devices/cpu/alpha/alpha.cpp b/src/devices/cpu/alpha/alpha.cpp new file mode 100644 index 00000000000..f4181060128 --- /dev/null +++ b/src/devices/cpu/alpha/alpha.cpp @@ -0,0 +1,91 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * An implementation of the Digital Alpha CPU family. + * + * Sources + * + * http://bitsavers.org/pdf/dec/alpha/21064-aa-RISC%20Microprocessor%20Preliminary%20Data%20Sheet-apr92.pdf + * http://bitsavers.org/pdf/dec/alpha/Sites_AlphaAXPArchitectureReferenceManual_2ed_1995.pdf + * + * TODO + * - skeleton only (wip) + */ + +#include "emu.h" +#include "debugger.h" +#include "alpha.h" +#include "alphad.h" + +#define LOG_GENERAL (1U << 0) +#define LOG_EXCEPTION (1U << 1) +#define LOG_SYSCALLS (1U << 2) + +//#define VERBOSE (LOG_GENERAL | LOG_EXCEPTION) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(DEC_21064, dec_21064_device, "21064", "DEC Alpha 21064") + +dec_21064_device::dec_21064_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : alpha_device(mconfig, DEC_21064, tag, owner, clock) +{ +} + +alpha_device::alpha_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : cpu_device(mconfig, type, tag, owner, clock) + , m_main_config("main", ENDIANNESS_LITTLE, 64, 32, 0) + , m_icount(0) +{ +} + +void alpha_device::device_start() +{ + set_icountptr(m_icount); + + save_item(NAME(m_pc)); + save_item(NAME(m_r)); + save_item(NAME(m_f)); + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + + state_add(64, "PC", m_pc); + + // integer registers + for (unsigned i = 0; i < 32; i++) + state_add(i, util::string_format("R%d", i).c_str(), m_r[i]); + + // floating point registers + for (unsigned i = 0; i < 32; i++) + state_add(i + 32, util::string_format("F%d", i).c_str(), m_f[i]); +} + +void alpha_device::device_reset() +{ +} + +void alpha_device::execute_run() +{ + m_icount = 0; +} + +void alpha_device::execute_set_input(int inputnum, int state) +{ +} + +device_memory_interface::space_config_vector alpha_device::memory_space_config() const +{ + return space_config_vector { std::make_pair(0, &m_main_config) }; +} + +bool alpha_device::memory_translate(int spacenum, int intention, offs_t &address) +{ + return true; +} + +std::unique_ptr alpha_device::create_disassembler() +{ + return std::make_unique(); +} diff --git a/src/devices/cpu/alpha/alpha.h b/src/devices/cpu/alpha/alpha.h new file mode 100644 index 00000000000..a4d66e806c7 --- /dev/null +++ b/src/devices/cpu/alpha/alpha.h @@ -0,0 +1,56 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_CPU_ALPHA_ALPHA_H +#define MAME_CPU_ALPHA_ALPHA_H + +#pragma once + +#include +#include "softfloat/milieu.h" +#include "softfloat/softfloat.h" + +class alpha_device : public cpu_device +{ +public: + +protected: + alpha_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + // 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; } + virtual u32 execute_input_lines() const override { return 6; } + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; + +private: + // emulation state + address_space_config m_main_config; + int m_icount; + + u64 m_pc; + u64 m_r[32]; // R31 is zero + u64 m_f[32]; // F31 is zero +}; + +class dec_21064_device : public alpha_device +{ +public: + dec_21064_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +DECLARE_DEVICE_TYPE(DEC_21064, dec_21064_device) + +#endif // MAME_CPU_ALPHA_ALPHA_H diff --git a/src/devices/cpu/alpha/alphad.cpp b/src/devices/cpu/alpha/alphad.cpp new file mode 100644 index 00000000000..3282036ba0a --- /dev/null +++ b/src/devices/cpu/alpha/alphad.cpp @@ -0,0 +1,783 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * TODO + * - enforce "should be zero" fields + * - enforce "Ra must be R31" + * - assembly and register naming preferences + * - operating environment dependent PALcode + */ + +#include "emu.h" +#include "alphad.h" + +// instruction field extraction +#define Ra(x) ((x >> 21) & 31) +#define Rb(x) ((x >> 16) & 31) +#define Rc(x) (x & 31) +#define Im(x) u8(x >> 13) +#define Disp_M(x) s16(x) +#define Disp_B(x) (s32(x << 11) >> 9) + +// instruction formats +#define OPERATE_RRR(opcode, ra, rb, rc) util::stream_format(stream, "%-10s %s, %s, %s", opcode, R[ra], R[rb], R[rc]) +#define OPERATE_FFF(opcode, ra, rb, rc) util::stream_format(stream, "%-10s %s, %s, %s", opcode, F[ra], F[rb], F[rc]) +#define OPERATE_RIR(opcode, ra, im, rc) util::stream_format(stream, "%-10s %s, #%d, %s", opcode, R[ra], im, R[rc]) +#define OPERATE_RR(opcode, rb, rc) util::stream_format(stream, "%-10s %s, %s", opcode, R[rb], R[rc]) +#define OPERATE_FF(opcode, rb, rc) util::stream_format(stream, "%-10s %s, %s", opcode, F[rb], F[rc]) +#define OPERATE_RF(opcode, ra, rc) util::stream_format(stream, "%-10s %s, %s", opcode, R[ra], F[rc]) +#define OPERATE_FR(opcode, ra, rc) util::stream_format(stream, "%-10s %s, %s", opcode, F[ra], R[rc]) +#define OPERATE_IR(opcode, im, rc) util::stream_format(stream, "%-10s #%d, %s", opcode, im, R[rc]) +#define OPERATE_R(opcode, rc) util::stream_format(stream, "%-10s %s", opcode, R[rc]) + +#define MEMORY_R(opcode, ra, disp, rb) util::stream_format(stream, "%-10s %s, %d(%s)", opcode, R[ra], disp, R[rb]) +#define MEMORY_F(opcode, ra, disp, rb) util::stream_format(stream, "%-10s %s, %d(%s)", opcode, F[ra], disp, R[rb]) +#define BRANCH_R(opcode, ra, offset) util::stream_format(stream, "%-10s %s, 0x%x", opcode, R[ra], pc + 4 + offset) +#define BRANCH_F(opcode, ra, offset) util::stream_format(stream, "%-10s %s, 0x%x", opcode, F[ra], pc + 4 + offset) + +#define JUMP(opcode, ra, rb) util::stream_format(stream, "%-10s %s, (%s)", opcode, R[ra], R[rb]) + +#define MISC_0(opcode) util::stream_format(stream, "%-10s", opcode) +#define MISC_R(opcode, ra) util::stream_format(stream, "%-10s %s", opcode, R[ra]) +#define MISC_M(opcode, rb) util::stream_format(stream, "%-10s (%s)", opcode, R[rb]) + +#define PAL(opcode, fnc) util::stream_format(stream, "%-10s #0x%x", opcode, fnc) +#define PAL_0(opcode) util::stream_format(stream, "%-10s", opcode) + +#define RESERVED(opcode) util::stream_format(stream, "%-10s", opcode) +#define UNKNOWN(type) util::stream_format(stream, "unknown %s", type) + +// register names +const char *const alpha_disassembler::R[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" +}; + +const char *const alpha_disassembler::F[] = +{ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" +}; + +offs_t alpha_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + offs_t const bytes = 4; + u32 flags = SUPPORTED; + + u32 const instruction = opcodes.r32(pc); + + switch (instruction >> 26) + { + // pal format + case 0x00: + switch (instruction & 0x03ffffff) + { + case 0x0000: PAL_0("halt"); break; + case 0x0002: PAL_0("draina"); break; + case 0x0086: PAL_0("imb"); break; + + default: PAL("call_pal", instruction & 0x03ffffff); break; // CALL_PAL + } + break; + + // reserved opcodes + case 0x01: RESERVED("opc01"); break; // OPC01 + case 0x02: RESERVED("opc02"); break; // OPC02 + case 0x03: RESERVED("opc03"); break; // OPC03 + case 0x04: RESERVED("opc04"); break; // OPC04 + case 0x05: RESERVED("opc05"); break; // OPC05 + case 0x06: RESERVED("opc06"); break; // OPC06 + case 0x07: RESERVED("opc07"); break; // OPC07 + + // memory format + case 0x08: MEMORY_R("lda", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDA + case 0x09: MEMORY_R("ldah", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDAH + case 0x0a: MEMORY_R("ldbu", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDBU (BWX) + case 0x0b: MEMORY_R("ldq_u", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDQ_U + case 0x0c: MEMORY_R("ldwu", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDWU (BWX) + case 0x0d: MEMORY_R("stw", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STW (BWX) + case 0x0e: MEMORY_R("stb", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STB (BWX) + case 0x0f: MEMORY_R("stq_u", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STQ_U + + // operate format + case 0x10: // INTA* (integer arithmetic) + switch ((instruction >> 5) & 0xff) + { + // register variants + case 0x00: OPERATE_RRR("addl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDL + case 0x02: OPERATE_RRR("s4addl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S4ADDL + case 0x09: OPERATE_RRR("subl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBL + case 0x0b: OPERATE_RRR("s4subl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S4SUBL + case 0x0f: OPERATE_RRR("cmpbge", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPBGE + case 0x12: OPERATE_RRR("s8addl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S8ADDL + case 0x1b: OPERATE_RRR("s8subl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S8SUBL + case 0x1d: OPERATE_RRR("cmpult", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPULT + case 0x20: OPERATE_RRR("addq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDQ + case 0x22: OPERATE_RRR("s4addq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S4ADDQ + case 0x29: OPERATE_RRR("subq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBQ + case 0x2b: OPERATE_RRR("s4subq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S4SUBQ + case 0x2d: OPERATE_RRR("cmpeq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPEQ + case 0x32: OPERATE_RRR("s8addq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S8ADDQ + case 0x3b: OPERATE_RRR("s8subq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // S8SUBQ + case 0x3d: OPERATE_RRR("cmpule", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPULE + case 0x40: OPERATE_RRR("addl/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDL/V + case 0x49: OPERATE_RRR("subl/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBL/V + case 0x4d: OPERATE_RRR("cmplt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPLT + case 0x60: OPERATE_RRR("addq/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDQ/V + case 0x69: OPERATE_RRR("subq/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBQ/V + case 0x6d: OPERATE_RRR("cmple", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPLE + + // immediate variants + case 0x80: OPERATE_RIR("addl", Ra(instruction), Im(instruction), Rc(instruction)); break; // ADDL + case 0x82: OPERATE_RIR("s4addl", Ra(instruction), Im(instruction), Rc(instruction)); break; // S4ADDL + case 0x89: OPERATE_RIR("subl", Ra(instruction), Im(instruction), Rc(instruction)); break; // SUBL + case 0x8b: OPERATE_RIR("s4subl", Ra(instruction), Im(instruction), Rc(instruction)); break; // S4SUBL + case 0x8f: OPERATE_RIR("cmpbge", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPBGE + case 0x92: OPERATE_RIR("s8addl", Ra(instruction), Im(instruction), Rc(instruction)); break; // S8ADDL + case 0x9b: OPERATE_RIR("s8subl", Ra(instruction), Im(instruction), Rc(instruction)); break; // S8SUBL + case 0x9d: OPERATE_RIR("cmpult", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPULT + case 0xa0: OPERATE_RIR("addq", Ra(instruction), Im(instruction), Rc(instruction)); break; // ADDQ + case 0xa2: OPERATE_RIR("s4addq", Ra(instruction), Im(instruction), Rc(instruction)); break; // S4ADDQ + case 0xa9: OPERATE_RIR("subq", Ra(instruction), Im(instruction), Rc(instruction)); break; // SUBQ + case 0xab: OPERATE_RIR("s4subq", Ra(instruction), Im(instruction), Rc(instruction)); break; // S4SUBQ + case 0xad: OPERATE_RIR("cmpeq", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPEQ + case 0xb2: OPERATE_RIR("s8addq", Ra(instruction), Im(instruction), Rc(instruction)); break; // S8ADDQ + case 0xbb: OPERATE_RIR("s8subq", Ra(instruction), Im(instruction), Rc(instruction)); break; // S8SUBQ + case 0xbd: OPERATE_RIR("cmpule", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPULE + case 0xc0: OPERATE_RIR("addl/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // ADDL/V + case 0xc9: OPERATE_RIR("subl/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // SUBL/V + case 0xcd: OPERATE_RIR("cmplt", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPLT + case 0xe0: OPERATE_RIR("addq/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // ADDQ/V + case 0xe9: OPERATE_RIR("subq/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // SUBQ/V + case 0xed: OPERATE_RIR("cmple", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMPLE + + default: UNKNOWN("inta*"); break; + } + break; + case 0x11: // INTL* (integer logical) + switch ((instruction >> 5) & 0xff) + { + // register variants + case 0x00: OPERATE_RRR("and", Ra(instruction), Rb(instruction), Rc(instruction)); break; // AND + case 0x08: OPERATE_RRR("bic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // BIC + case 0x14: OPERATE_RRR("cmovlbs", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVLBS + case 0x16: OPERATE_RRR("cmovlbc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVLBC + case 0x20: OPERATE_RRR("bis", Ra(instruction), Rb(instruction), Rc(instruction)); break; // BIS + case 0x24: OPERATE_RRR("cmoveq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVEQ + case 0x26: OPERATE_RRR("cmovne", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVNE + case 0x28: OPERATE_RRR("ornot", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ORNOT + case 0x40: OPERATE_RRR("xor", Ra(instruction), Rb(instruction), Rc(instruction)); break; // XOR + case 0x44: OPERATE_RRR("cmovlt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVLT + case 0x46: OPERATE_RRR("cmovge", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVGE + case 0x48: OPERATE_RRR("eqv", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EQV + case 0x61: OPERATE_RR("amask", Rb(instruction), Rc(instruction)); break; // AMASK + case 0x64: OPERATE_RRR("cmovle", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVLE + case 0x66: OPERATE_RRR("cmovgt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMOVGT + + // immediate variants + case 0x80: OPERATE_RIR("and", Ra(instruction), Im(instruction), Rc(instruction)); break; // AND + case 0x88: OPERATE_RIR("bic", Ra(instruction), Im(instruction), Rc(instruction)); break; // BIC + case 0x94: OPERATE_RIR("cmovlbs", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVLBS + case 0x96: OPERATE_RIR("cmovlbc", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVLBC + case 0xa0: OPERATE_RIR("bis", Ra(instruction), Im(instruction), Rc(instruction)); break; // BIS + case 0xa4: OPERATE_RIR("cmoveq", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVEQ + case 0xa6: OPERATE_RIR("cmovne", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVNE + case 0xa8: OPERATE_RIR("ornot", Ra(instruction), Im(instruction), Rc(instruction)); break; // ORNOT + case 0xc0: OPERATE_RIR("xor", Ra(instruction), Im(instruction), Rc(instruction)); break; // XOR + case 0xc4: OPERATE_RIR("cmovlt", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVLT + case 0xc6: OPERATE_RIR("cmovge", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVGE + case 0xc8: OPERATE_RIR("eqv", Ra(instruction), Im(instruction), Rc(instruction)); break; // EQV + case 0xe1: OPERATE_IR("amask", Im(instruction), Rc(instruction)); break; // AMASK + case 0xe4: OPERATE_RIR("cmovle", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVLE + case 0xe6: OPERATE_RIR("cmovgt", Ra(instruction), Im(instruction), Rc(instruction)); break; // CMOVGT + + // TODO: Rb must be literal #1? + case 0xec: OPERATE_R("implver", Rc(instruction)); break; // IMPLVER + + default: UNKNOWN("intl*"); break; + } + break; + case 0x12: // INTS* (integer shift) + switch ((instruction >> 5) & 0xff) + { + // register variants + case 0x02: OPERATE_RRR("mskbl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKBL + case 0x06: OPERATE_RRR("extbl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTBL + case 0x0b: OPERATE_RRR("insbl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSBL + case 0x12: OPERATE_RRR("mskwl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKWL + case 0x16: OPERATE_RRR("extwl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTWL + case 0x1b: OPERATE_RRR("inswl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSWL + case 0x22: OPERATE_RRR("mskll", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKLL + case 0x26: OPERATE_RRR("extll", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTLL + case 0x2b: OPERATE_RRR("insll", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSLL + case 0x30: OPERATE_RRR("zap", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ZAP + case 0x31: OPERATE_RRR("zapnot", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ZAPNOT + case 0x32: OPERATE_RRR("mskql", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKQL + case 0x34: OPERATE_RRR("srl", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SRL + case 0x36: OPERATE_RRR("extql", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTQL + case 0x39: OPERATE_RRR("sll", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SLL + case 0x3b: OPERATE_RRR("insql", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSQL + case 0x3c: OPERATE_RRR("sra", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SRA + case 0x52: OPERATE_RRR("mskwh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKWH + case 0x57: OPERATE_RRR("inswh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSWH + case 0x5a: OPERATE_RRR("extwh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTWH + case 0x62: OPERATE_RRR("msklh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKLH + case 0x67: OPERATE_RRR("inslh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSLH + case 0x6a: OPERATE_RRR("extlh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTLH + case 0x72: OPERATE_RRR("mskqh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MSKQH + case 0x77: OPERATE_RRR("insqh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // INSQH + case 0x7a: OPERATE_RRR("extqh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // EXTQH + + // immediate variants + case 0x82: OPERATE_RIR("mskbl", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKBL + case 0x86: OPERATE_RIR("extbl", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTBL + case 0x8b: OPERATE_RIR("insbl", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSBL + case 0x92: OPERATE_RIR("mskwl", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKWL + case 0x96: OPERATE_RIR("extwl", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTWL + case 0x9b: OPERATE_RIR("inswl", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSWL + case 0xa2: OPERATE_RIR("mskll", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKLL + case 0xa6: OPERATE_RIR("extll", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTLL + case 0xab: OPERATE_RIR("insll", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSLL + case 0xb0: OPERATE_RIR("zap", Ra(instruction), Im(instruction), Rc(instruction)); break; // ZAP + case 0xb1: OPERATE_RIR("zapnot", Ra(instruction), Im(instruction), Rc(instruction)); break; // ZAPNOT + case 0xb2: OPERATE_RIR("mskql", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKQL + case 0xb4: OPERATE_RIR("srl", Ra(instruction), Im(instruction), Rc(instruction)); break; // SRL + case 0xb6: OPERATE_RIR("extql", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTQL + case 0xb9: OPERATE_RIR("sll", Ra(instruction), Im(instruction), Rc(instruction)); break; // SLL + case 0xbb: OPERATE_RIR("insql", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSQL + case 0xbc: OPERATE_RIR("sra", Ra(instruction), Im(instruction), Rc(instruction)); break; // SRA + case 0xd2: OPERATE_RIR("mskwh", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKWH + case 0xd7: OPERATE_RIR("inswh", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSWH + case 0xda: OPERATE_RIR("extwh", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTWH + case 0xe2: OPERATE_RIR("msklh", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKLH + case 0xe7: OPERATE_RIR("inslh", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSLH + case 0xea: OPERATE_RIR("extlh", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTLH + case 0xf2: OPERATE_RIR("mskqh", Ra(instruction), Im(instruction), Rc(instruction)); break; // MSKQH + case 0xf7: OPERATE_RIR("insqh", Ra(instruction), Im(instruction), Rc(instruction)); break; // INSQH + case 0xfa: OPERATE_RIR("extqh", Ra(instruction), Im(instruction), Rc(instruction)); break; // EXTQH + + default: UNKNOWN("ints*"); break; + } + break; + case 0x13: // INTM* (integer multiply) + switch ((instruction >> 5) & 0xff) + { + // register variants + case 0x00: OPERATE_RRR("mull", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULL + case 0x20: OPERATE_RRR("mulq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULQ + case 0x30: OPERATE_RRR("umulh", Ra(instruction), Rb(instruction), Rc(instruction)); break; // UMULH + case 0x40: OPERATE_RRR("mull/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULL/V + case 0x60: OPERATE_RRR("mulq/v", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULQ/V + + // immediate variants + case 0x80: OPERATE_RIR("mull", Ra(instruction), Im(instruction), Rc(instruction)); break; // MULL + case 0xa0: OPERATE_RIR("mulq", Ra(instruction), Im(instruction), Rc(instruction)); break; // MULQ + case 0xb0: OPERATE_RIR("umulh", Ra(instruction), Im(instruction), Rc(instruction)); break; // UMULH + case 0xc0: OPERATE_RIR("mull/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // MULL/V + case 0xe0: OPERATE_RIR("mulq/v", Ra(instruction), Im(instruction), Rc(instruction)); break; // MULQ/V + + default: UNKNOWN("intm*"); break; + } + case 0x14: // ITFP* (integer to floating) + switch ((instruction >> 5) & 0x7ff) + { + case 0x004: OPERATE_RF("itofs", Ra(instruction), Rc(instruction)); break; // ITOFS + case 0x00a: OPERATE_FF("sqrtf/c", Rb(instruction), Rc(instruction)); break; // SQRTF/C + case 0x00b: OPERATE_FF("sqrts/c", Rb(instruction), Rc(instruction)); break; // SQRTS/C + case 0x014: OPERATE_RF("itoff", Ra(instruction), Rc(instruction)); break; // ITOFF + case 0x024: OPERATE_RF("itoft", Ra(instruction), Rc(instruction)); break; // ITOFT + case 0x02a: OPERATE_FF("sqrtg/c", Rb(instruction), Rc(instruction)); break; // SQRTG/C + case 0x02b: OPERATE_FF("sqrtt/c", Rb(instruction), Rc(instruction)); break; // SQRTT/C + case 0x04b: OPERATE_FF("sqrts/m", Rb(instruction), Rc(instruction)); break; // SQRTS/M + case 0x06b: OPERATE_FF("sqrtt/m", Rb(instruction), Rc(instruction)); break; // SQRTT/M + case 0x08a: OPERATE_FF("sqrtf", Rb(instruction), Rc(instruction)); break; // SQRTF + case 0x08b: OPERATE_FF("sqrts", Rb(instruction), Rc(instruction)); break; // SQRTS + case 0x0aa: OPERATE_FF("sqrtg", Rb(instruction), Rc(instruction)); break; // SQRTG + case 0x0ab: OPERATE_FF("sqrtt", Rb(instruction), Rc(instruction)); break; // SQRTT + case 0x0cb: OPERATE_FF("sqrts/d", Rb(instruction), Rc(instruction)); break; // SQRTS/D + case 0x0eb: OPERATE_FF("sqrtt/d", Rb(instruction), Rc(instruction)); break; // SQRTT/D + case 0x10a: OPERATE_FF("sqrtf/uc", Rb(instruction), Rc(instruction)); break; // SQRTF/UC + case 0x10b: OPERATE_FF("sqrts/uc", Rb(instruction), Rc(instruction)); break; // SQRTS/UC + case 0x12a: OPERATE_FF("sqrtg/uc", Rb(instruction), Rc(instruction)); break; // SQRTG/UC + case 0x12b: OPERATE_FF("sqrtt/uc", Rb(instruction), Rc(instruction)); break; // SQRTT/UC + case 0x14b: OPERATE_FF("sqrts/um", Rb(instruction), Rc(instruction)); break; // SQRTS/UM + case 0x16b: OPERATE_FF("sqrtt/um", Rb(instruction), Rc(instruction)); break; // SQRTT/UM + case 0x18a: OPERATE_FF("sqrtf/u", Rb(instruction), Rc(instruction)); break; // SQRTF/U + case 0x1aa: OPERATE_FF("sqrtg/u", Rb(instruction), Rc(instruction)); break; // SQRTG/U + case 0x1ab: OPERATE_FF("sqrtt/u", Rb(instruction), Rc(instruction)); break; // SQRTT/U + case 0x1cb: OPERATE_FF("sqrts/ud", Rb(instruction), Rc(instruction)); break; // SQRTS/UD + case 0x1eb: OPERATE_FF("sqrtt/ud", Rb(instruction), Rc(instruction)); break; // SQRTT/UD + case 0x40a: OPERATE_FF("sqrtf/sc", Rb(instruction), Rc(instruction)); break; // SQRTF/SC + case 0x42a: OPERATE_FF("sqrtg/sc", Rb(instruction), Rc(instruction)); break; // SQRTG/SC + case 0x48a: OPERATE_FF("sqrtf/s", Rb(instruction), Rc(instruction)); break; // SQRTF/S + case 0x4aa: OPERATE_FF("sqrtg/s", Rb(instruction), Rc(instruction)); break; // SQRTG/S + case 0x50a: OPERATE_FF("sqrtf/suc", Rb(instruction), Rc(instruction)); break; // SQRTF/SUC + case 0x50b: OPERATE_FF("sqrts/suc", Rb(instruction), Rc(instruction)); break; // SQRTS/SUC + case 0x52a: OPERATE_FF("sqrtg/suc", Rb(instruction), Rc(instruction)); break; // SQRTG/SUC + case 0x52b: OPERATE_FF("sqrtt/suc", Rb(instruction), Rc(instruction)); break; // SQRTT/SUC + case 0x54b: OPERATE_FF("sqrts/sum", Rb(instruction), Rc(instruction)); break; // SQRTS/SUM + case 0x56b: OPERATE_FF("sqrtt/sum", Rb(instruction), Rc(instruction)); break; // SQRTT/SUM + case 0x58a: OPERATE_FF("sqrtf/su", Rb(instruction), Rc(instruction)); break; // SQRTF/SU + case 0x58b: OPERATE_FF("sqrts/su", Rb(instruction), Rc(instruction)); break; // SQRTS/SU + case 0x5aa: OPERATE_FF("sqrtg/su", Rb(instruction), Rc(instruction)); break; // SQRTG/SU + case 0x5ab: OPERATE_FF("sqrtt/su", Rb(instruction), Rc(instruction)); break; // SQRTT/SU + case 0x5cb: OPERATE_FF("sqrts/sud", Rb(instruction), Rc(instruction)); break; // SQRTS/SUD + case 0x5eb: OPERATE_FF("sqrtt/sud", Rb(instruction), Rc(instruction)); break; // SQRTT/SUD + case 0x70b: OPERATE_FF("sqrts/suic", Rb(instruction), Rc(instruction)); break; // SQRTS/SUIC + case 0x72b: OPERATE_FF("sqrtt/suic", Rb(instruction), Rc(instruction)); break; // SQRTT/SUIC + case 0x74b: OPERATE_FF("sqrts/suim", Rb(instruction), Rc(instruction)); break; // SQRTS/SUIM + case 0x76b: OPERATE_FF("sqrtt/suim", Rb(instruction), Rc(instruction)); break; // SQRTT/SUIM + case 0x78b: OPERATE_FF("sqrts/sui", Rb(instruction), Rc(instruction)); break; // SQRTS/SUI + case 0x7ab: OPERATE_FF("sqrtt/sui", Rb(instruction), Rc(instruction)); break; // SQRTT/SUI + case 0x7cb: OPERATE_FF("sqrts/suid", Rb(instruction), Rc(instruction)); break; // SQRTS/SUID + case 0x7eb: OPERATE_FF("sqrtt/suid", Rb(instruction), Rc(instruction)); break; // SQRTT/SUID + + default: UNKNOWN("itfp*"); break; + } + break; + case 0x15: // FLTV* (vax floating) + switch ((instruction >> 5) & 0x7ff) + { + case 0x000: OPERATE_FFF("addf/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/C + case 0x001: OPERATE_FFF("subf/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/C + case 0x002: OPERATE_FFF("mulf/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/C + case 0x003: OPERATE_FFF("divf/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/C + case 0x01e: OPERATE_FF("cvtdg/c", Rb(instruction), Rc(instruction)); break; // CVTDG/C + case 0x020: OPERATE_FFF("addg/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/C + case 0x021: OPERATE_FFF("subg/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/C + case 0x022: OPERATE_FFF("mulg/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/C + case 0x023: OPERATE_FFF("divg/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/C + case 0x02c: OPERATE_FF("cvtgf/c", Rb(instruction), Rc(instruction)); break; // CVTGF/C + case 0x02d: OPERATE_FF("cvtgd/c", Rb(instruction), Rc(instruction)); break; // CVTGD/C + case 0x02f: OPERATE_FF("cvtgq/c", Rb(instruction), Rc(instruction)); break; // CVTGQ/C + case 0x03c: OPERATE_FF("cvtqf/c", Rb(instruction), Rc(instruction)); break; // CVTQF/C + case 0x03e: OPERATE_FF("cvtqg/c", Rb(instruction), Rc(instruction)); break; // CVTQG/C + case 0x080: OPERATE_FFF("addf", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF + case 0x081: OPERATE_FFF("subf", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF + case 0x082: OPERATE_FFF("mulf", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF + case 0x083: OPERATE_FFF("divf", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF + case 0x09e: OPERATE_FF("cvtdg", Rb(instruction), Rc(instruction)); break; // CVTDG + case 0x0a0: OPERATE_FFF("addg", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG + case 0x0a1: OPERATE_FFF("subg", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG + case 0x0a2: OPERATE_FFF("mulg", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG + case 0x0a3: OPERATE_FFF("divg", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG + case 0x0a5: OPERATE_FFF("cmpgeq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGEQ + case 0x0a6: OPERATE_FFF("cmpglt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGLT + case 0x0a7: OPERATE_FFF("cmpgle", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGLE + case 0x0ac: OPERATE_FF("cvtgf", Rb(instruction), Rc(instruction)); break; // CVTGF + case 0x0ad: OPERATE_FF("cvtgd", Rb(instruction), Rc(instruction)); break; // CVTGD + case 0x0af: OPERATE_FF("cvtgq", Rb(instruction), Rc(instruction)); break; // CVTGQ + case 0x0bc: OPERATE_FF("cvtqf", Rb(instruction), Rc(instruction)); break; // CVTQF + case 0x0be: OPERATE_FF("cvtqg", Rb(instruction), Rc(instruction)); break; // CVTQG + case 0x100: OPERATE_FFF("addf/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/UC + case 0x101: OPERATE_FFF("subf/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/UC + case 0x102: OPERATE_FFF("mulf/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/UC + case 0x103: OPERATE_FFF("divf/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/UC + case 0x11e: OPERATE_FF("cvtdg/uc", Rb(instruction), Rc(instruction)); break; // CVTDG/UC + case 0x120: OPERATE_FFF("addg/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/UC + case 0x121: OPERATE_FFF("subg/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/UC + case 0x122: OPERATE_FFF("mulg/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/UC + case 0x123: OPERATE_FFF("divg/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/UC + case 0x12c: OPERATE_FF("cvtgf/uc", Rb(instruction), Rc(instruction)); break; // CVTGF/UC + case 0x12d: OPERATE_FF("cvtgd/uc", Rb(instruction), Rc(instruction)); break; // CVTGD/UC + case 0x12f: OPERATE_FF("cvtgq/vc", Rb(instruction), Rc(instruction)); break; // CVTGQ/VC + case 0x180: OPERATE_FFF("addf/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/U + case 0x181: OPERATE_FFF("subf/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/U + case 0x182: OPERATE_FFF("mulf/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/U + case 0x183: OPERATE_FFF("divf/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/U + case 0x19e: OPERATE_FF("cvtdg/u", Rb(instruction), Rc(instruction)); break; // CVTDG/U + case 0x1a0: OPERATE_FFF("addg/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/U + case 0x1a1: OPERATE_FFF("subg/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/U + case 0x1a2: OPERATE_FFF("mulg/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/U + case 0x1a3: OPERATE_FFF("divg/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/U + case 0x1ac: OPERATE_FF("cvtgf/u", Rb(instruction), Rc(instruction)); break; // CVTGF/U + case 0x1ad: OPERATE_FF("cvtgd/u", Rb(instruction), Rc(instruction)); break; // CVTGD/U + case 0x1af: OPERATE_FF("cvtgq/v", Rb(instruction), Rc(instruction)); break; // CVTGQ/V + case 0x400: OPERATE_FFF("addf/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/SC + case 0x401: OPERATE_FFF("subf/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/SC + case 0x402: OPERATE_FFF("mulf/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/SC + case 0x403: OPERATE_FFF("divf/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/SC + case 0x41e: OPERATE_FF("cvtdg/sc", Rb(instruction), Rc(instruction)); break; // CVTDG/SC + case 0x420: OPERATE_FFF("addg/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/SC + case 0x421: OPERATE_FFF("subg/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/SC + case 0x422: OPERATE_FFF("mulg/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/SC + case 0x423: OPERATE_FFF("divg/sc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/SC + case 0x42c: OPERATE_FF("cvtgf/sc", Rb(instruction), Rc(instruction)); break; // CVTGF/SC + case 0x42d: OPERATE_FF("cvtgd/sc", Rb(instruction), Rc(instruction)); break; // CVTGD/SC + case 0x42f: OPERATE_FF("cvtgq/sc", Rb(instruction), Rc(instruction)); break; // CVTGQ/SC + case 0x480: OPERATE_FFF("addf/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/S + case 0x481: OPERATE_FFF("subf/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/S + case 0x482: OPERATE_FFF("mulf/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/S + case 0x483: OPERATE_FFF("divf/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/S + case 0x49e: OPERATE_FF("cvtdg/s", Rb(instruction), Rc(instruction)); break; // CVTDG/S + case 0x4a0: OPERATE_FFF("addg/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/S + case 0x4a1: OPERATE_FFF("subg/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/S + case 0x4a2: OPERATE_FFF("mulg/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/S + case 0x4a3: OPERATE_FFF("divg/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/S + case 0x4a5: OPERATE_FFF("cmpgeq/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGEQ/S + case 0x4a6: OPERATE_FFF("cmpglt/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGLT/S + case 0x4a7: OPERATE_FFF("cmpgle/s", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPGLE/S + case 0x4ac: OPERATE_FF("cvtgf/s", Rb(instruction), Rc(instruction)); break; // CVTGF/S + case 0x4ad: OPERATE_FF("cvtgd/s", Rb(instruction), Rc(instruction)); break; // CVTGD/S + case 0x4af: OPERATE_FF("cvtgq/s", Rb(instruction), Rc(instruction)); break; // CVTGQ/S + case 0x500: OPERATE_FFF("addf/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/SUC + case 0x501: OPERATE_FFF("subf/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/SUC + case 0x502: OPERATE_FFF("mulf/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/SUC + case 0x503: OPERATE_FFF("divf/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/SUC + case 0x51e: OPERATE_FF("cvtdg/suc", Rb(instruction), Rc(instruction)); break; // CVTDG/SUC + case 0x520: OPERATE_FFF("addg/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/SUC + case 0x521: OPERATE_FFF("subg/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/SUC + case 0x522: OPERATE_FFF("mulg/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/SUC + case 0x523: OPERATE_FFF("divg/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/SUC + case 0x52c: OPERATE_FF("cvtgf/suc", Rb(instruction), Rc(instruction)); break; // CVTGF/SUC + case 0x52d: OPERATE_FF("cvtgd/suc", Rb(instruction), Rc(instruction)); break; // CVTGD/SUC + case 0x52f: OPERATE_FF("cvtgq/svc", Rb(instruction), Rc(instruction)); break; // CVTGQ/SVC + case 0x580: OPERATE_FFF("addf/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDF/SU + case 0x581: OPERATE_FFF("subf/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBF/SU + case 0x582: OPERATE_FFF("mulf/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULF/SU + case 0x583: OPERATE_FFF("divf/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVF/SU + case 0x59e: OPERATE_FF("cvtdg/su", Rb(instruction), Rc(instruction)); break; // CVTDG/SU + case 0x5a0: OPERATE_FFF("addg/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDG/SU + case 0x5a1: OPERATE_FFF("subg/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBG/SU + case 0x5a2: OPERATE_FFF("mulg/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULG/SU + case 0x5a3: OPERATE_FFF("divg/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVG/SU + case 0x5ac: OPERATE_FF("cvtgf/su", Rb(instruction), Rc(instruction)); break; // CVTGF/SU + case 0x5ad: OPERATE_FF("cvtgd/su", Rb(instruction), Rc(instruction)); break; // CVTGD/SU + case 0x5af: OPERATE_FF("cvtgq/sv", Rb(instruction), Rc(instruction)); break; // CVTGQ/SV + + default: UNKNOWN("fltv*"); break; + } + break; + case 0x16: // FLTI* (ieee floating) + switch ((instruction >> 5) & 0x7ff) + { + case 0x000: OPERATE_FFF("adds/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/C + case 0x001: OPERATE_FFF("subs/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/C + case 0x002: OPERATE_FFF("muls/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/C + case 0x003: OPERATE_FFF("divs/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/C + case 0x020: OPERATE_FFF("addt/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/C + case 0x021: OPERATE_FFF("subt/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/C + case 0x022: OPERATE_FFF("mult/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/C + case 0x023: OPERATE_FFF("divt/c", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/C + case 0x02c: OPERATE_FF("cvttts/c", Rb(instruction), Rc(instruction)); break; // CVTTS/C + case 0x02f: OPERATE_FF("cvttq/c", Rb(instruction), Rc(instruction)); break; // CVTTQ/C + case 0x03c: OPERATE_FF("cvtqs/c", Rb(instruction), Rc(instruction)); break; // CVTQS/C + case 0x03e: OPERATE_FF("cvtqt/c", Rb(instruction), Rc(instruction)); break; // CVTQT/C + case 0x040: OPERATE_FFF("adds/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/M + case 0x041: OPERATE_FFF("subs/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/M + case 0x042: OPERATE_FFF("muls/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/M + case 0x043: OPERATE_FFF("divs/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/M + case 0x060: OPERATE_FFF("addt/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/M + case 0x061: OPERATE_FFF("subt/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/M + case 0x062: OPERATE_FFF("mult/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/M + case 0x063: OPERATE_FFF("divt/m", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/M + case 0x06c: OPERATE_FF("cvttts/m", Rb(instruction), Rc(instruction)); break; // CVTTS/M + case 0x06f: OPERATE_FF("cvttq/m", Rb(instruction), Rc(instruction)); break; // CVTTQ/M + case 0x07c: OPERATE_FF("cvtqs/m", Rb(instruction), Rc(instruction)); break; // CVTQS/M + case 0x07e: OPERATE_FF("cvtqt/m", Rb(instruction), Rc(instruction)); break; // CVTQT/M + case 0x080: OPERATE_FFF("adds", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS + case 0x081: OPERATE_FFF("subs", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS + case 0x082: OPERATE_FFF("muls", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS + case 0x083: OPERATE_FFF("divs", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS + case 0x0a0: OPERATE_FFF("addt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT + case 0x0a1: OPERATE_FFF("subt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT + case 0x0a2: OPERATE_FFF("mult", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT + case 0x0a3: OPERATE_FFF("divt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT + case 0x0a4: OPERATE_FFF("cmptun", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTUN + case 0x0a5: OPERATE_FFF("cmpteq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTEQ + case 0x0a6: OPERATE_FFF("cmptlt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTLT + case 0x0a7: OPERATE_FFF("cmptle", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTLE + case 0x0ac: OPERATE_FF("cvttts", Rb(instruction), Rc(instruction)); break; // CVTTS + case 0x0af: OPERATE_FF("cvttq", Rb(instruction), Rc(instruction)); break; // CVTTQ + case 0x0bc: OPERATE_FF("cvtqs", Rb(instruction), Rc(instruction)); break; // CVTQS + case 0x0be: OPERATE_FF("cvtqt", Rb(instruction), Rc(instruction)); break; // CVTQT + case 0x0c0: OPERATE_FFF("adds/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/D + case 0x0c1: OPERATE_FFF("subs/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/D + case 0x0c2: OPERATE_FFF("muls/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/D + case 0x0c3: OPERATE_FFF("divs/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/D + case 0x0e0: OPERATE_FFF("addt/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/D + case 0x0e1: OPERATE_FFF("subt/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/D + case 0x0e2: OPERATE_FFF("mult/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/D + case 0x0e3: OPERATE_FFF("divt/d", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/D + case 0x0ec: OPERATE_FF("cvttts/d", Rb(instruction), Rc(instruction)); break; // CVTTS/D + case 0x0ef: OPERATE_FF("cvttq/d", Rb(instruction), Rc(instruction)); break; // CVTTQ/D + case 0x0fc: OPERATE_FF("cvtqs/d", Rb(instruction), Rc(instruction)); break; // CVTQS/D + case 0x0fe: OPERATE_FF("cvtqt/d", Rb(instruction), Rc(instruction)); break; // CVTQT/D + case 0x100: OPERATE_FFF("adds/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/UC + case 0x101: OPERATE_FFF("subs/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/UC + case 0x102: OPERATE_FFF("muls/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/UC + case 0x103: OPERATE_FFF("divs/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/UC + case 0x120: OPERATE_FFF("addt/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/UC + case 0x121: OPERATE_FFF("subt/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/UC + case 0x122: OPERATE_FFF("mult/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/UC + case 0x123: OPERATE_FFF("divt/uc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/UC + case 0x12c: OPERATE_FF("cvttts/uc", Rb(instruction), Rc(instruction)); break; // CVTTS/UC + case 0x12f: OPERATE_FF("cvttq/vc", Rb(instruction), Rc(instruction)); break; // CVTTQ/VC + case 0x140: OPERATE_FFF("adds/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/UM + case 0x141: OPERATE_FFF("subs/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/UM + case 0x142: OPERATE_FFF("muls/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/UM + case 0x143: OPERATE_FFF("divs/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/UM + case 0x160: OPERATE_FFF("addt/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/UM + case 0x161: OPERATE_FFF("subt/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/UM + case 0x162: OPERATE_FFF("mult/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/UM + case 0x163: OPERATE_FFF("divt/um", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/UM + case 0x16c: OPERATE_FF("cvttts/um", Rb(instruction), Rc(instruction)); break; // CVTTS/UM + case 0x16f: OPERATE_FF("cvttq/vm", Rb(instruction), Rc(instruction)); break; // CVTTQ/VM + case 0x180: OPERATE_FFF("adds/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/U + case 0x181: OPERATE_FFF("subs/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/U + case 0x182: OPERATE_FFF("muls/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/U + case 0x183: OPERATE_FFF("divs/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/U + case 0x1a0: OPERATE_FFF("addt/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/U + case 0x1a1: OPERATE_FFF("subt/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/U + case 0x1a2: OPERATE_FFF("mult/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/U + case 0x1a3: OPERATE_FFF("divt/u", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/U + case 0x1ac: OPERATE_FF("cvttts/u", Rb(instruction), Rc(instruction)); break; // CVTTS/U + case 0x1af: OPERATE_FF("cvttq/v", Rb(instruction), Rc(instruction)); break; // CVTTQ/V + case 0x1c0: OPERATE_FFF("adds/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/UD + case 0x1c1: OPERATE_FFF("subs/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/UD + case 0x1c2: OPERATE_FFF("muls/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/UD + case 0x1c3: OPERATE_FFF("divs/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/UD + case 0x1e0: OPERATE_FFF("addt/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/UD + case 0x1e1: OPERATE_FFF("subs/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/UD + case 0x1e2: OPERATE_FFF("mult/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/UD + case 0x1e3: OPERATE_FFF("divt/ud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/UD + case 0x1ec: OPERATE_FF("cvttts/ud", Rb(instruction), Rc(instruction)); break; // CVTTS/UD + case 0x1ef: OPERATE_FF("cvttq/vd", Rb(instruction), Rc(instruction)); break; // CVTTQ/VD + case 0x2ac: OPERATE_FF("cvtst", Rb(instruction), Rc(instruction)); break; // CVTST + case 0x500: OPERATE_FFF("adds/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUC + case 0x501: OPERATE_FFF("subs/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUC + case 0x502: OPERATE_FFF("muls/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUC + case 0x503: OPERATE_FFF("divs/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUC + case 0x520: OPERATE_FFF("addt/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUC + case 0x521: OPERATE_FFF("subt/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUC + case 0x522: OPERATE_FFF("mult/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUC + case 0x523: OPERATE_FFF("divt/suc", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUC + case 0x52c: OPERATE_FF("cvttts/suc", Rb(instruction), Rc(instruction)); break; // CVTTS/SUC + case 0x52f: OPERATE_FF("cvttq/svc", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVC + case 0x540: OPERATE_FFF("adds/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUM + case 0x541: OPERATE_FFF("subs/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUM + case 0x542: OPERATE_FFF("muls/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUM + case 0x543: OPERATE_FFF("divs/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUM + case 0x560: OPERATE_FFF("addt/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUM + case 0x561: OPERATE_FFF("subt/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUM + case 0x562: OPERATE_FFF("mult/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUM + case 0x563: OPERATE_FFF("divt/sum", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUM + case 0x56c: OPERATE_FF("cvttts/sum", Rb(instruction), Rc(instruction)); break; // CVTTS/SUM + case 0x56f: OPERATE_FF("cvttq/svm", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVM + case 0x580: OPERATE_FFF("adds/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SU + case 0x581: OPERATE_FFF("subs/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SU + case 0x582: OPERATE_FFF("muls/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SU + case 0x583: OPERATE_FFF("divs/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SU + case 0x5a0: OPERATE_FFF("addt/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SU + case 0x5a1: OPERATE_FFF("subt/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SU + case 0x5a2: OPERATE_FFF("mult/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SU + case 0x5a3: OPERATE_FFF("divt/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SU + case 0x5a4: OPERATE_FFF("cmptun/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTUN/SU + case 0x5a5: OPERATE_FFF("cmpteq/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTEQ/SU + case 0x5a6: OPERATE_FFF("cmptlt/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTLT/SU + case 0x5a7: OPERATE_FFF("cmptle/su", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CMPTLE/SU + case 0x5ac: OPERATE_FF("cvttts/su", Rb(instruction), Rc(instruction)); break; // CVTTS/SU + case 0x5af: OPERATE_FF("cvttq/sv", Rb(instruction), Rc(instruction)); break; // CVTTQ/SV + case 0x5c0: OPERATE_FFF("adds/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUD + case 0x5c1: OPERATE_FFF("subs/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUD + case 0x5c2: OPERATE_FFF("muls/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUD + case 0x5c3: OPERATE_FFF("divs/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUD + case 0x5e0: OPERATE_FFF("addt/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUD + case 0x5e1: OPERATE_FFF("subt/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUD + case 0x5e2: OPERATE_FFF("mult/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUD + case 0x5e3: OPERATE_FFF("divt/sud", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUD + case 0x5ec: OPERATE_FF("cvttts/sud", Rb(instruction), Rc(instruction)); break; // CVTTS/SUD + case 0x5ef: OPERATE_FF("cvttq/svd", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVD + case 0x6ac: OPERATE_FF("cvtst/s", Rb(instruction), Rc(instruction)); break; // CVTST/S + case 0x700: OPERATE_FFF("adds/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUIC + case 0x701: OPERATE_FFF("subs/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUIC + case 0x702: OPERATE_FFF("muls/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUIC + case 0x703: OPERATE_FFF("divs/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUIC + case 0x720: OPERATE_FFF("addt/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUIC + case 0x721: OPERATE_FFF("subt/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUIC + case 0x722: OPERATE_FFF("mult/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUIC + case 0x723: OPERATE_FFF("divt/suic", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUIC + case 0x72c: OPERATE_FF("cvttts/suic", Rb(instruction), Rc(instruction)); break; // CVTTS/SUIC + case 0x72f: OPERATE_FF("cvttq/svic", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVIC + case 0x73c: OPERATE_FF("cvtqs/suic", Rb(instruction), Rc(instruction)); break; // CVTQS/SUIC + case 0x73e: OPERATE_FF("cvtqt/suic", Rb(instruction), Rc(instruction)); break; // CVTQT/SUIC + case 0x740: OPERATE_FFF("adds/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUIM + case 0x741: OPERATE_FFF("subs/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUIM + case 0x742: OPERATE_FFF("muls/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUIM + case 0x743: OPERATE_FFF("divs/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUIM + case 0x760: OPERATE_FFF("addt/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUIM + case 0x761: OPERATE_FFF("subt/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUIM + case 0x762: OPERATE_FFF("mult/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUIM + case 0x763: OPERATE_FFF("divt/suim", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUIM + case 0x76c: OPERATE_FF("cvttts/suim", Rb(instruction), Rc(instruction)); break; // CVTTS/SUIM + case 0x76f: OPERATE_FF("cvttq/svim", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVIM + case 0x77c: OPERATE_FF("cvtqs/suim", Rb(instruction), Rc(instruction)); break; // CVTQS/SUIM + case 0x77e: OPERATE_FF("cvtqt/suim", Rb(instruction), Rc(instruction)); break; // CVTQT/SUIM + case 0x780: OPERATE_FFF("adds/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUI + case 0x781: OPERATE_FFF("subs/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUI + case 0x782: OPERATE_FFF("muls/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUI + case 0x783: OPERATE_FFF("divs/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUI + case 0x7a0: OPERATE_FFF("addt/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUI + case 0x7a1: OPERATE_FFF("subt/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUI + case 0x7a2: OPERATE_FFF("mult/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUI + case 0x7a3: OPERATE_FFF("divt/sui", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUI + case 0x7ac: OPERATE_FF("cvttts/sui", Rb(instruction), Rc(instruction)); break; // CVTTS/SUI + case 0x7af: OPERATE_FF("cvttq/svi", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVI + case 0x7bc: OPERATE_FF("cvtqs/sui", Rb(instruction), Rc(instruction)); break; // CVTQS/SUI + case 0x7be: OPERATE_FF("cvtqt/sui", Rb(instruction), Rc(instruction)); break; // CVTQT/SUI + case 0x7c0: OPERATE_FFF("adds/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDS/SUID + case 0x7c1: OPERATE_FFF("subs/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBS/SUID + case 0x7c2: OPERATE_FFF("muls/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULS/SUID + case 0x7c3: OPERATE_FFF("divs/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVS/SUID + case 0x7e0: OPERATE_FFF("addt/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // ADDT/SUID + case 0x7e1: OPERATE_FFF("subt/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // SUBT/SUID + case 0x7e2: OPERATE_FFF("mult/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MULT/SUID + case 0x7e3: OPERATE_FFF("divt/suid", Ra(instruction), Rb(instruction), Rc(instruction)); break; // DIVT/SUID + case 0x7ec: OPERATE_FF("cvttts/suid", Rb(instruction), Rc(instruction)); break; // CVTTS/SUID + case 0x7ef: OPERATE_FF("cvttq/svid", Rb(instruction), Rc(instruction)); break; // CVTTQ/SVID + case 0x7fc: OPERATE_FF("cvtqs/suid", Rb(instruction), Rc(instruction)); break; // CVTQS/SUID + case 0x7fe: OPERATE_FF("cvtqt/suid", Rb(instruction), Rc(instruction)); break; // CVTQT/SUID + + default: UNKNOWN("flti*"); break; + } + break; + case 0x17: // FLTL* (floating) + switch ((instruction >> 5) & 0x7ff) + { + case 0x010: OPERATE_FF("cvtlq", Rb(instruction), Rc(instruction)); break; // CVTLQ + case 0x020: OPERATE_FFF("cpys", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CPYS + case 0x021: OPERATE_FFF("cpysn", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CPYSN + case 0x022: OPERATE_FFF("cpyse", Ra(instruction), Rb(instruction), Rc(instruction)); break; // CPYSE + case 0x024: OPERATE_FFF("mt_fpcr", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MT_FPCR + case 0x025: OPERATE_FFF("mf_fpcr", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MF_FPCR + case 0x02a: OPERATE_FFF("fcmoveq", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVEQ + case 0x02b: OPERATE_FFF("fcmovne", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVNE + case 0x02c: OPERATE_FFF("fcmovlt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVLT + case 0x02d: OPERATE_FFF("fcmovge", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVGE + case 0x02e: OPERATE_FFF("fcmovle", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVLE + case 0x02f: OPERATE_FFF("fcmovgt", Ra(instruction), Rb(instruction), Rc(instruction)); break; // FCMOVGT + case 0x030: OPERATE_FF("cvtql", Rb(instruction), Rc(instruction)); break; // CVTQL + case 0x130: OPERATE_FF("cvtql/v", Rb(instruction), Rc(instruction)); break; // CVTQL/V + case 0x530: OPERATE_FF("cvtql/sv", Rb(instruction), Rc(instruction)); break; // CVTQL/SV + + default: UNKNOWN("fltl*"); break; + } + break; + case 0x18: // MISC* (miscellaneous) + switch (u16(instruction)) + { + case 0x0000: MISC_0("trapb"); break; // TRAPB + case 0x0400: MISC_0("excb"); break; // EXCB + case 0x4000: MISC_0("mb"); break; // MB + case 0x4400: MISC_0("wmb"); break; // WMB + case 0x8000: MISC_M("fetch", Rb(instruction)); break; // FETCH + case 0xa000: MISC_M("fetch_m", Rb(instruction)); break; // FETCH_M + case 0xc000: MISC_R("rpcc", Ra(instruction)); break; // RPCC + case 0xe000: MISC_R("rc", Ra(instruction)); break; // RC + case 0xe800: MISC_M("ecb", Rb(instruction)); break; // ECB + case 0xf000: MISC_R("rs", Ra(instruction)); break; // RS + case 0xf800: MISC_M("wh64", Rb(instruction)); break; // WH64 + + default: UNKNOWN("misc*"); break; + } + break; + case 0x19: PAL_0("pal19"); break; // PAL19 + case 0x1a: // JSR* (jump) + switch ((instruction >> 14) & 3) + { + case 0: JUMP("jmp", Ra(instruction), Rb(instruction)); break; // JMP + case 1: JUMP("jsr", Ra(instruction), Rb(instruction)); flags |= STEP_OVER; break; // JSR + case 2: JUMP("ret", Ra(instruction), Rb(instruction)); flags |= STEP_OUT; break; // RET + case 3: JUMP("jsr_c", Ra(instruction), Rb(instruction)); break; // JSR_COROUTINE + } + break; + case 0x1b: PAL_0("pal1b"); break; // PAL1B + case 0x1c: // FPTI* (floating to integer) + switch ((instruction >> 5) & 0xff) + { + // register variants + case 0x00: OPERATE_RR("sextb", Rb(instruction), Rc(instruction)); break; // SEXTB (BWX) + case 0x01: OPERATE_RR("sextw", Rb(instruction), Rc(instruction)); break; // SEXTW (BWX) + case 0x30: OPERATE_RR("ctpop", Rb(instruction), Rc(instruction)); break; // CTPOP (CIX) + case 0x31: OPERATE_RRR("perr", Ra(instruction), Rb(instruction), Rc(instruction)); break; // PERR (MVI) + case 0x32: OPERATE_RR("ctlz", Rb(instruction), Rc(instruction)); break; // CTLZ (CIX) + case 0x33: OPERATE_RR("cttz", Rb(instruction), Rc(instruction)); break; // CTTZ (CIX) + case 0x34: OPERATE_RR("unpkbw", Rb(instruction), Rc(instruction)); break; // UNPKBW (MVI) + case 0x35: OPERATE_RR("unpkbl", Rb(instruction), Rc(instruction)); break; // UNPKBL (MVI) + case 0x36: OPERATE_RR("pkwb", Rb(instruction), Rc(instruction)); break; // PKWB (MVI) + case 0x37: OPERATE_RR("pklb", Rb(instruction), Rc(instruction)); break; // PKLB (MVI) + case 0x38: OPERATE_RRR("minsb8", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MINSB8 (MVI) + case 0x39: OPERATE_RRR("minsw4", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MINSW4 (MVI) + case 0x3a: OPERATE_RRR("minub8", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MINUB8 (MVI) + case 0x3b: OPERATE_RRR("minuw4", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MINUW4 (MVI) + case 0x3c: OPERATE_RRR("maxub8", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MAXUB8 (MVI) + case 0x3d: OPERATE_RRR("maxuw4", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MAXUW4 (MVI) + case 0x3e: OPERATE_RRR("maxsb8", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MAXSB8 (MVI) + case 0x3f: OPERATE_RRR("maxsw4", Ra(instruction), Rb(instruction), Rc(instruction)); break; // MAXSW4 (MVI) + case 0x70: OPERATE_FR("ftoit", Ra(instruction), Rc(instruction)); break; // FTOIT (FIX) + case 0x78: OPERATE_FR("ftois", Ra(instruction), Rc(instruction)); break; // FTOIS (FIX) + + // immediate variants + case 0x80: OPERATE_IR("sextb", Im(instruction), Rc(instruction)); break; // SEXTB (BWX) + case 0x81: OPERATE_IR("sextw", Im(instruction), Rc(instruction)); break; // SEXTW (BWX) + case 0xb8: OPERATE_RIR("minsb8", Ra(instruction), Im(instruction), Rc(instruction)); break; // MINSB8 (MVI) + case 0xb9: OPERATE_RIR("minsw4", Ra(instruction), Im(instruction), Rc(instruction)); break; // MINSW4 (MVI) + case 0xba: OPERATE_RIR("minub8", Ra(instruction), Im(instruction), Rc(instruction)); break; // MINUB8 (MVI) + case 0xbb: OPERATE_RIR("minuw4", Ra(instruction), Im(instruction), Rc(instruction)); break; // MINUW4 (MVI) + case 0xbc: OPERATE_RIR("maxub8", Ra(instruction), Im(instruction), Rc(instruction)); break; // MAXUB8 (MVI) + case 0xbd: OPERATE_RIR("maxuw4", Ra(instruction), Im(instruction), Rc(instruction)); break; // MAXUW4 (MVI) + case 0xbe: OPERATE_RIR("maxsb8", Ra(instruction), Im(instruction), Rc(instruction)); break; // MAXSB8 (MVI) + case 0xbf: OPERATE_RIR("maxsw4", Ra(instruction), Im(instruction), Rc(instruction)); break; // MAXSW4 (MVI) + + default: UNKNOWN("fpti*"); break; + } + break; + case 0x1d: PAL_0("pal1d"); break; // PAL1D + case 0x1e: PAL_0("pal1e"); break; // PAL1E + case 0x1f: PAL_0("pal1f"); break; // PAL1F + + // memory format + case 0x20: MEMORY_F("ldf", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDF + case 0x21: MEMORY_F("ldg", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDG + case 0x22: MEMORY_F("lds", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDS + case 0x23: MEMORY_F("ldt", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDT + case 0x24: MEMORY_F("stf", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STF + case 0x25: MEMORY_F("stg", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STG + case 0x26: MEMORY_F("sts", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STS + case 0x27: MEMORY_F("stt", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STT + case 0x28: MEMORY_R("ldl", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDL + case 0x29: MEMORY_R("ldq", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDQ + case 0x2a: MEMORY_R("ldl_l", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDL_L + case 0x2b: MEMORY_R("ldq_l", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // LDQ_L + case 0x2c: MEMORY_R("stl", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STL + case 0x2d: MEMORY_R("stq", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STQ + case 0x2e: MEMORY_R("stl_c", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STL_C + case 0x2f: MEMORY_R("stq_c", Ra(instruction), Disp_M(instruction), Rb(instruction)); break; // STQ_C + + // branch format + case 0x30: BRANCH_R("br", Ra(instruction), Disp_B(instruction)); break; // BR + case 0x31: BRANCH_F("fbeq", Ra(instruction), Disp_B(instruction)); break; // FBEQ + case 0x32: BRANCH_F("fblt", Ra(instruction), Disp_B(instruction)); break; // FBLT + case 0x33: BRANCH_F("fble", Ra(instruction), Disp_B(instruction)); break; // FBLE + case 0x34: BRANCH_R("bsr", Ra(instruction), Disp_B(instruction)); break; // BSR + case 0x35: BRANCH_F("fbne", Ra(instruction), Disp_B(instruction)); break; // FBNE + case 0x36: BRANCH_F("fbge", Ra(instruction), Disp_B(instruction)); break; // FBGE + case 0x37: BRANCH_F("fbgt", Ra(instruction), Disp_B(instruction)); break; // FBGT + case 0x38: BRANCH_R("blbc", Ra(instruction), Disp_B(instruction)); break; // BLBC + case 0x39: BRANCH_R("beq", Ra(instruction), Disp_B(instruction)); break; // BEQ + case 0x3a: BRANCH_R("blt", Ra(instruction), Disp_B(instruction)); break; // BLT + case 0x3b: BRANCH_R("ble", Ra(instruction), Disp_B(instruction)); break; // BLE + case 0x3c: BRANCH_R("blbs", Ra(instruction), Disp_B(instruction)); break; // BLBS + case 0x3d: BRANCH_R("bne", Ra(instruction), Disp_B(instruction)); break; // BNE + case 0x3e: BRANCH_R("bge", Ra(instruction), Disp_B(instruction)); break; // BGE + case 0x3f: BRANCH_R("bgt", Ra(instruction), Disp_B(instruction)); break; // BGT + } + + return bytes | flags; +} diff --git a/src/devices/cpu/alpha/alphad.h b/src/devices/cpu/alpha/alphad.h new file mode 100644 index 00000000000..09fa4b8a63a --- /dev/null +++ b/src/devices/cpu/alpha/alphad.h @@ -0,0 +1,23 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_CPU_ALPHA_ALPHAD_H +#define MAME_CPU_ALPHA_ALPHAD_H + +#pragma once + +class alpha_disassembler : public util::disasm_interface +{ +public: + alpha_disassembler() = default; + virtual ~alpha_disassembler() = default; + + virtual u32 opcode_alignment() const override { return 4; }; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + static char const *const R[]; + static char const *const F[]; +}; + +#endif // MAME_CPU_ALPHA_ALPHAD_H diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 328527c85ce..4faea8300e7 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -17,6 +17,7 @@ using util::BIT; #include "cpu/8x300/8x300dasm.h" #include "cpu/adsp2100/2100dasm.h" #include "cpu/alph8201/8201dasm.h" +#include "cpu/alpha/alphad.h" #include "cpu/alto2/alto2dsm.h" #include "cpu/am29000/am29dasm.h" #include "cpu/amis2000/amis2000d.h" @@ -301,6 +302,7 @@ static const dasm_table_entry dasm_table[] = { { "8x300", be, 0, []() -> util::disasm_interface * { return new n8x300_disassembler; } }, { "adsp21xx", le, -2, []() -> util::disasm_interface * { return new adsp21xx_disassembler; } }, + { "alpha", le, 0, []() -> util::disasm_interface * { return new alpha_disassembler; } }, { "alpha8201", le, 0, []() -> util::disasm_interface * { return new alpha8201_disassembler; } }, { "alto2", be, -2, []() -> util::disasm_interface * { return new alto2_disassembler; } }, { "am29000", be, 0, []() -> util::disasm_interface * { return new am29000_disassembler; } }, -- cgit v1.2.3-70-g09d2 From ba8d7b1c8f2e8c90b5ddc37962a32e5e2e3198eb Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 1 Feb 2019 15:27:13 +0700 Subject: r4000: experimental mips3 implementation (nw) WIP checkpoint: while I believe it's largely accurate (and very slow), neither jazz nor sgi systems can fully boot yet using this device, so it remains experimental. This implementation should go away when it has helped identify the improvements required for mips3. --- scripts/src/cpu.lua | 3 + scripts/src/main.lua | 1 + src/devices/cpu/mips/r4000.cpp | 3073 ++++++++++++++++++++++++++++++++++++++++ src/devices/cpu/mips/r4000.h | 460 ++++++ 4 files changed, 3537 insertions(+) create mode 100644 src/devices/cpu/mips/r4000.cpp create mode 100644 src/devices/cpu/mips/r4000.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index c3218773035..4095f62a4c3 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -1297,6 +1297,7 @@ end -------------------------------------------------- -- MIPS R4000 (MIPS III/IV) series --@src/devices/cpu/mips/mips3.h,CPUS["MIPS3"] = true +--@src/devices/cpu/mips/r4000.h,CPUS["MIPS3"] = true -------------------------------------------------- if (CPUS["MIPS3"]~=null) then @@ -1313,6 +1314,8 @@ if (CPUS["MIPS3"]~=null) then MAME_DIR .. "src/devices/cpu/mips/ps2vu.h", MAME_DIR .. "src/devices/cpu/mips/ps2vif1.cpp", MAME_DIR .. "src/devices/cpu/mips/ps2vif1.h", + MAME_DIR .. "src/devices/cpu/mips/r4000.cpp", + MAME_DIR .. "src/devices/cpu/mips/r4000.h", } end diff --git a/scripts/src/main.lua b/scripts/src/main.lua index bec62ff6f49..1184ff8ae9e 100644 --- a/scripts/src/main.lua +++ b/scripts/src/main.lua @@ -256,6 +256,7 @@ end "utils", ext_lib("expat"), "softfloat", + "softfloat3", ext_lib("jpeg"), "7z", } diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp new file mode 100644 index 00000000000..7fb28b10dce --- /dev/null +++ b/src/devices/cpu/mips/r4000.cpp @@ -0,0 +1,3073 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * This is a stripped-down MIPS-III CPU derived from the main mips3 code. Its + * primary purpose is to act as a test-bed to aid in debugging MIPS-based + * systems, after which the changes/improvements from here are expected to + * be back-ported and incorporated into the original mips3 device. + * + * Because of this specific approach, no attempt is made to support many of the + * current features of the mips3 device at this time. Key differences bewteen + * this implementation and mips3 include: + * + * - only supports MIPS R4000/R4400 and QED R4600 + * - no dynamic recompilation + * - reworked address translation logic, including 64-bit modes + * - reworked softfloat3-based floating point + * - experimental primary instruction cache + * - memory tap based ll/sc + * - configurable endianness + * - it's very very very slow + * + * TODO + * - try to eliminate mode check in address calculations + * - find a better way to deal with software interrupts + * - enforce mode checks for cp1 + * - cache instructions + * - check/improve instruction timing + * + */ + +#include "emu.h" +#include "debugger.h" +#include "r4000.h" +#include "mips3dsm.h" + +#include "softfloat3/source/include/softfloat.h" + +#define LOG_GENERAL (1U << 0) +#define LOG_TLB (1U << 1) +#define LOG_CACHE (1U << 2) +#define LOG_EXCEPTION (1U << 3) +#define LOG_SYSCALL (1U << 4) + +#define VERBOSE (LOG_GENERAL) + +// operating system specific system call logging +#define SYSCALL_IRIX53 (1U << 0) +#define SYSCALL_WINNT4 (1U << 1) +#if VERBOSE & LOG_SYSCALL +#define SYSCALL_MASK (SYSCALL_IRIX53) +#else +#define SYSCALL_MASK (0) +#endif + +// experimental primary instruction cache +#define ICACHE 0 + +#include "logmacro.h" + +#define USE_ABI_REG_NAMES 1 + +// cpu instruction fiels +#define RSREG ((op >> 21) & 31) +#define RTREG ((op >> 16) & 31) +#define RDREG ((op >> 11) & 31) +#define SHIFT ((op >> 6) & 31) + +// cop1 instruction fields +#define FRREG ((op >> 21) & 31) +#define FTREG ((op >> 16) & 31) +#define FSREG ((op >> 11) & 31) +#define FDREG ((op >> 6) & 31) + +#define R4000_ENDIAN_LE_BE(le, be) ((m_cp0[CP0_Config] & CONFIG_BE) ? (be) : (le)) + +// identify odd-numbered cop1 registers +#define ODD_REGS 0x00010840U + +// address computation +#define ADDR(r, o) (m_64 ? (r + s16(o)) : s64(s32(u32(r) + s16(o)))) + +#define SR m_cp0[CP0_Status] +#define CAUSE m_cp0[CP0_Cause] + +DEFINE_DEVICE_TYPE(R4000, r4000_device, "r4000", "MIPS R4000") +DEFINE_DEVICE_TYPE(R4400, r4400_device, "r4400", "MIPS R4000") +DEFINE_DEVICE_TYPE(R4600, r4600_device, "r4600", "QED R4000") + +r4000_base_device::r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size) + : cpu_device(mconfig, type, tag, owner, clock) + , m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32) + , m_program_config_be("program", ENDIANNESS_BIG, 64, 32) + , m_ll_watch(nullptr) + , m_fcr0(0x00000500U) +{ + m_cp0[CP0_PRId] = prid; + + // default configuration + m_cp0[CP0_Config] = CONFIG_BE | (icache_size << 9) | (dcache_size << 6); +} + +void r4000_base_device::device_start() +{ + // TODO: save state + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + state_add(MIPS3_PC, "PC", m_pc).formatstr("%016X"); + + // exception processing + state_add(MIPS3_CP0 + CP0_Status, "SR", m_cp0[CP0_Status]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_EPC, "EPC", m_cp0[CP0_EPC]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_Cause, "Cause", m_cp0[CP0_Cause]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_Context, "Context", m_cp0[CP0_Context]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_BadVAddr, "BadVAddr", m_cp0[CP0_BadVAddr]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_Compare, "Compare", m_cp0[CP0_Compare]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_WatchLo, "WatchLo", m_cp0[CP0_WatchLo]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_WatchHi, "WatchHi", m_cp0[CP0_WatchHi]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_XContext, "XContext", m_cp0[CP0_XContext]).formatstr("%016X"); + + // memory management + state_add(MIPS3_CP0 + CP0_Index, "Index", m_cp0[CP0_Index]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_EntryLo0, "EntryLo0", m_cp0[CP0_EntryLo0]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_EntryLo1, "EntryLo1", m_cp0[CP0_EntryLo1]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_PageMask, "PageMask", m_cp0[CP0_PageMask]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_Wired, "Wired", m_cp0[CP0_Wired]).formatstr("%08X"); + state_add(MIPS3_CP0 + CP0_EntryHi, "EntryHi", m_cp0[CP0_EntryHi]).formatstr("%016X"); + state_add(MIPS3_CP0 + CP0_LLAddr, "LLAddr", m_cp0[CP0_LLAddr]).formatstr("%08X"); + +#if USE_ABI_REG_NAMES + state_add(MIPS3_R0 + 0, "zero", m_r[0]).callimport().formatstr("%016X"); // Can't change R0 + state_add(MIPS3_R0 + 1, "at", m_r[1]).formatstr("%016X"); + state_add(MIPS3_R0 + 2, "v0", m_r[2]).formatstr("%016X"); + state_add(MIPS3_R0 + 3, "v1", m_r[3]).formatstr("%016X"); + state_add(MIPS3_R0 + 4, "a0", m_r[4]).formatstr("%016X"); + state_add(MIPS3_R0 + 5, "a1", m_r[5]).formatstr("%016X"); + state_add(MIPS3_R0 + 6, "a2", m_r[6]).formatstr("%016X"); + state_add(MIPS3_R0 + 7, "a3", m_r[7]).formatstr("%016X"); + state_add(MIPS3_R0 + 8, "t0", m_r[8]).formatstr("%016X"); + state_add(MIPS3_R0 + 9, "t1", m_r[9]).formatstr("%016X"); + state_add(MIPS3_R0 + 10, "t2", m_r[10]).formatstr("%016X"); + state_add(MIPS3_R0 + 11, "t3", m_r[11]).formatstr("%016X"); + state_add(MIPS3_R0 + 12, "t4", m_r[12]).formatstr("%016X"); + state_add(MIPS3_R0 + 13, "t5", m_r[13]).formatstr("%016X"); + state_add(MIPS3_R0 + 14, "t6", m_r[14]).formatstr("%016X"); + state_add(MIPS3_R0 + 15, "t7", m_r[15]).formatstr("%016X"); + state_add(MIPS3_R0 + 16, "s0", m_r[16]).formatstr("%016X"); + state_add(MIPS3_R0 + 17, "s1", m_r[17]).formatstr("%016X"); + state_add(MIPS3_R0 + 18, "s2", m_r[18]).formatstr("%016X"); + state_add(MIPS3_R0 + 19, "s3", m_r[19]).formatstr("%016X"); + state_add(MIPS3_R0 + 20, "s4", m_r[20]).formatstr("%016X"); + state_add(MIPS3_R0 + 21, "s5", m_r[21]).formatstr("%016X"); + state_add(MIPS3_R0 + 22, "s6", m_r[22]).formatstr("%016X"); + state_add(MIPS3_R0 + 23, "s7", m_r[23]).formatstr("%016X"); + state_add(MIPS3_R0 + 24, "t8", m_r[24]).formatstr("%016X"); + state_add(MIPS3_R0 + 25, "t9", m_r[25]).formatstr("%016X"); + state_add(MIPS3_R0 + 26, "k0", m_r[26]).formatstr("%016X"); + state_add(MIPS3_R0 + 27, "k1", m_r[27]).formatstr("%016X"); + state_add(MIPS3_R0 + 28, "gp", m_r[28]).formatstr("%016X"); + state_add(MIPS3_R0 + 29, "sp", m_r[29]).formatstr("%016X"); + state_add(MIPS3_R0 + 30, "fp", m_r[30]).formatstr("%016X"); + state_add(MIPS3_R0 + 31, "ra", m_r[31]).formatstr("%016X"); +#else + state_add(MIPS3_R0, "R0", m_r[0]).callimport().formatstr("%016X"); + for (unsigned i = 1; i < 32; i++) + state_add(MIPS3_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]); +#endif + + state_add(MIPS3_HI, "HI", m_hi).formatstr("%016X"); + state_add(MIPS3_LO, "LO", m_lo).formatstr("%016X"); + + // floating point registers + state_add(MIPS3_FCR31, "FCR31", m_fcr31).formatstr("%08X"); + for (unsigned i = 0; i < 32; i++) + state_add(MIPS3_F0 + i, util::string_format("F%d", i).c_str(), m_f[i]); + + set_icountptr(m_icount); + + m_cp0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(r4000_base_device::cp0_timer_callback), this)); + + // compute icache line selection mask and allocate tag and data + unsigned const config_ic = (m_cp0[CP0_Config] & CONFIG_IC) >> 9; + + m_icache_mask_hi = (0x1000U << config_ic) - 1; + m_icache_tag = std::make_unique(0x100U << config_ic); + m_icache_data = std::make_unique((0x1000U << config_ic) >> 2); +} + +void r4000_base_device::device_reset() +{ + m_branch_state = NONE; + m_pc = s64(s32(0xbfc00000)); + + m_cp0[CP0_Status] = SR_BEV | SR_ERL; + m_cp0[CP0_Wired] = 0; + m_cp0[CP0_Compare] = 0; + m_cp0[CP0_Count] = 0; + + m_cp0_timer_zero = total_cycles(); + m_64 = false; + + if (m_ll_watch) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + } + + m_cp0[CP0_WatchLo] = 0; + m_cp0[CP0_WatchHi] = 0; + + m_icache_hit = 0; + m_icache_miss = 0; +} + +void r4000_base_device::device_stop() +{ + if (ICACHE) + LOGMASKED(LOG_CACHE, "icache hit ratio %.3f%% (%d hits %d misses)\n", + double(m_icache_hit) / double(m_icache_hit + m_icache_miss) * 100.0, m_icache_hit, m_icache_miss); +} + +device_memory_interface::space_config_vector r4000_base_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(AS_PROGRAM, R4000_ENDIAN_LE_BE(&m_program_config_le, &m_program_config_be)) + }; +} + +bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address) +{ + // FIXME: address truncation + u64 placeholder = s32(address); + + translate_t const t = translate(intention, placeholder); + + if (t == ERROR || t == MISS) + return false; + + address = placeholder; + return true; +} + +std::unique_ptr r4000_base_device::create_disassembler() +{ + return std::make_unique(); +} + +void r4000_base_device::execute_run() +{ + // check interrupts + if ((CAUSE & SR & CAUSE_IP) && (SR & SR_IE) && !(SR & (SR_EXL | SR_ERL))) + cpu_exception(EXCEPTION_INT); + + while (m_icount > 0) + { + debugger_instruction_hook(m_pc); + + fetch(m_pc, + [this](u32 const op) + { + cpu_execute(op); + + // zero register zero + m_r[0] = 0; + }); + + // update pc and branch state + switch (m_branch_state) + { + case NONE: + m_pc += 4; + break; + + case DELAY: + m_branch_state = NONE; + m_pc = m_branch_target; + break; + + case BRANCH: + m_branch_state = DELAY; + m_pc += 4; + break; + + case EXCEPTION: + m_branch_state = NONE; + break; + + case NULLIFY: + m_branch_state = NONE; + m_pc += 8; + break; + } + + m_icount--; + } +} + +void r4000_base_device::execute_set_input(int inputnum, int state) +{ + if (state) + m_cp0[CP0_Cause] |= (CAUSE_IPEX0 << inputnum); + else + m_cp0[CP0_Cause] &= ~(CAUSE_IPEX0 << inputnum); +} + +void r4000_base_device::cpu_execute(u32 const op) +{ + switch (op >> 26) + { + case 0x00: // SPECIAL + switch (op & 0x3f) + { + case 0x00: // SLL + m_r[RDREG] = s64(s32(m_r[RTREG] << SHIFT)); + break; + //case 0x01: // * + case 0x02: // SRL + m_r[RDREG] = u32(m_r[RTREG]) >> SHIFT; + break; + case 0x03: // SRA + m_r[RDREG] = s64(s32(m_r[RTREG]) >> SHIFT); + break; + case 0x04: // SLLV + m_r[RDREG] = s64(s32(m_r[RTREG] << (m_r[RSREG] & 31))); + break; + //case 0x05: // * + case 0x06: // SRLV + m_r[RDREG] = u32(m_r[RTREG]) >> (m_r[RSREG] & 31); + break; + case 0x07: // SRAV + m_r[RDREG] = s64(s32(m_r[RTREG]) >> (m_r[RSREG] & 31)); + break; + case 0x08: // JR + m_branch_state = BRANCH; + m_branch_target = ADDR(m_r[RSREG], 0); + break; + case 0x09: // JALR + m_branch_state = BRANCH; + m_branch_target = ADDR(m_r[RSREG], 0); + m_r[RDREG] = ADDR(m_pc, 8); + break; + //case 0x0a: // * + //case 0x0b: // * + case 0x0c: // SYSCALL + if (VERBOSE & LOG_SYSCALL) + { + if (SYSCALL_MASK & SYSCALL_IRIX53) + { + switch (m_r[2]) + { + case 0x3e9: // 1001 = exit + LOGMASKED(LOG_SYSCALL, "exit(%d) (%s)\n", m_r[4], machine().describe_context()); + break; + + case 0x3ea: // 1002 = fork + LOGMASKED(LOG_SYSCALL, "fork() (%s)\n", machine().describe_context()); + break; + + case 0x3eb: // 1003 = read + LOGMASKED(LOG_SYSCALL, "read(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context()); + break; + + case 0x3ec: // 1004 = write + LOGMASKED(LOG_SYSCALL, "write(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context()); + if (m_r[4] == 1 || m_r[4] == 2) + printf("%s", debug_string(m_r[5], m_r[6]).c_str()); + break; + + case 0x3ed: // 1005 = open + LOGMASKED(LOG_SYSCALL, "open(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context()); + break; + + case 0x3ee: // 1006 = close + LOGMASKED(LOG_SYSCALL, "close(%d) (%s)\n", m_r[4], machine().describe_context()); + break; + + case 0x3ef: // 1007 = creat + LOGMASKED(LOG_SYSCALL, "creat(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context()); + break; + + case 0x423: // 1059 = exece + LOGMASKED(LOG_SYSCALL, "exece(\"%s\", [ %s ], [ %s ]) (%s)\n", debug_string(m_r[4]), debug_string_array(m_r[5]), debug_string_array(m_r[6]), machine().describe_context()); + break; + + default: + LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context()); + break; + } + } + else if (SYSCALL_MASK & SYSCALL_WINNT4) + { + switch (m_r[2]) + { + case 0x4f: + load(m_r[7] + 8, + [this](s64 string_pointer) + { + LOGMASKED(LOG_SYSCALL, "NtOpenFile(%s) (%s)\n", debug_string(string_pointer), machine().describe_context()); + }); + break; + + default: + LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context()); + break; + } + } + } + cpu_exception(EXCEPTION_SYS); + break; + case 0x0d: // BREAK + cpu_exception(EXCEPTION_BP); + break; + //case 0x0e: // * + case 0x0f: // SYNC + break; + case 0x10: // MFHI + m_r[RDREG] = m_hi; + break; + case 0x11: // MTHI + m_hi = m_r[RSREG]; + break; + case 0x12: // MFLO + m_r[RDREG] = m_lo; + break; + case 0x13: // MTLO + m_lo = m_r[RSREG]; + break; + case 0x14: // DSLLV + m_r[RDREG] = m_r[RTREG] << (m_r[RSREG] & 63); + break; + //case 0x15: // * + case 0x16: // DSRLV + m_r[RDREG] = m_r[RTREG] >> (m_r[RSREG] & 63); + break; + case 0x17: // DSRAV + m_r[RDREG] = s64(m_r[RTREG]) >> (m_r[RSREG] & 63); + break; + case 0x18: // MULT + { + u64 const product = mul_32x32(s32(m_r[RSREG]), s32(m_r[RTREG])); + + m_lo = s64(s32(product)); + m_hi = s64(s32(product >> 32)); + m_icount -= 3; + } + break; + case 0x19: // MULTU + { + u64 const product = mulu_32x32(u32(m_r[RSREG]), u32(m_r[RTREG])); + + m_lo = s64(s32(product)); + m_hi = s64(s32(product >> 32)); + m_icount -= 3; + } + break; + case 0x1a: // DIV + if (m_r[RTREG]) + { + m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG])); + m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG])); + } + m_icount -= 35; + break; + case 0x1b: // DIVU + if (m_r[RTREG]) + { + m_lo = s64(u32(m_r[RSREG]) / u32(m_r[RTREG])); + m_hi = s64(u32(m_r[RSREG]) % u32(m_r[RTREG])); + } + m_icount -= 35; + break; + case 0x1c: // DMULT + { + u64 const a_hi = u32(m_r[RSREG] >> 32); + u64 const b_hi = u32(m_r[RTREG] >> 32); + u64 const a_lo = u32(m_r[RSREG]); + u64 const b_lo = u32(m_r[RTREG]); + + u64 const p1 = a_lo * b_lo; + u64 const p2 = a_hi * b_lo; + u64 const p3 = a_lo * b_hi; + u64 const p4 = a_hi * b_hi; + u64 const carry = u32(((p1 >> 32) + u32(p2) + u32(p3)) >> 32); + + m_lo = p1 + (p2 << 32) + (p3 << 32); + m_hi = p4 + (p2 >> 32) + (p3 >> 32) + carry; + + // adjust for sign + if (m_r[RSREG] < 0) + m_hi -= m_r[RTREG]; + if (m_r[RTREG] < 0) + m_hi -= m_r[RSREG]; + + m_icount -= 7; + } + break; + case 0x1d: // DMULTU + { + u64 const a_hi = u32(m_r[RSREG] >> 32); + u64 const b_hi = u32(m_r[RTREG] >> 32); + u64 const a_lo = u32(m_r[RSREG]); + u64 const b_lo = u32(m_r[RTREG]); + + u64 const p1 = a_lo * b_lo; + u64 const p2 = a_hi * b_lo; + u64 const p3 = a_lo * b_hi; + u64 const p4 = a_hi * b_hi; + u64 const carry = u32(((p1 >> 32) + u32(p2) + u32(p3)) >> 32); + + m_lo = p1 + (p2 << 32) + (p3 << 32); + m_hi = p4 + (p2 >> 32) + (p3 >> 32) + carry; + + m_icount -= 7; + } + break; + case 0x1e: // DDIV + if (m_r[RTREG]) + { + m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]); + m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]); + } + m_icount -= 67; + break; + case 0x1f: // DDIVU + if (m_r[RTREG]) + { + m_lo = m_r[RSREG] / m_r[RTREG]; + m_hi = m_r[RSREG] % m_r[RTREG]; + } + m_icount -= 67; + break; + case 0x20: // ADD + { + u32 const sum = u32(m_r[RSREG]) + u32(m_r[RTREG]); + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31)) + cpu_exception(EXCEPTION_OV); + else + m_r[RDREG] = s64(s32(sum)); + } + break; + case 0x21: // ADDU + m_r[RDREG] = s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))); + break; + case 0x22: // SUB + { + u32 const difference = u32(m_r[RSREG]) - u32(m_r[RTREG]); + + // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) + if (BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ difference, 31)) + cpu_exception(EXCEPTION_OV); + else + m_r[RDREG] = s64(s32(difference)); + } + break; + case 0x23: // SUBU + m_r[RDREG] = s64(s32(u32(m_r[RSREG]) - u32(m_r[RTREG]))); + break; + case 0x24: // AND + m_r[RDREG] = m_r[RSREG] & m_r[RTREG]; + break; + case 0x25: // OR + m_r[RDREG] = m_r[RSREG] | m_r[RTREG]; + break; + case 0x26: // XOR + m_r[RDREG] = m_r[RSREG] ^ m_r[RTREG]; + break; + case 0x27: // NOR + m_r[RDREG] = ~(m_r[RSREG] | m_r[RTREG]); + break; + //case 0x28: // * + //case 0x29: // * + case 0x2a: // SLT + m_r[RDREG] = s64(m_r[RSREG]) < s64(m_r[RTREG]); + break; + case 0x2b: // SLTU + m_r[RDREG] = m_r[RSREG] < m_r[RTREG]; + break; + case 0x2c: // DADD + { + u64 const sum = m_r[RSREG] + m_r[RTREG]; + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ m_r[RTREG], 63) && BIT(m_r[RSREG] ^ sum, 63)) + cpu_exception(EXCEPTION_OV); + else + m_r[RDREG] = sum; + } + break; + case 0x2d: // DADDU + m_r[RDREG] = m_r[RSREG] + m_r[RTREG]; + break; + case 0x2e: // DSUB + { + u64 const difference = m_r[RSREG] - m_r[RTREG]; + + // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) + if (BIT(m_r[RSREG] ^ m_r[RTREG], 63) && BIT(m_r[RSREG] ^ difference, 63)) + cpu_exception(EXCEPTION_OV); + else + m_r[RDREG] = difference; + } + break; + case 0x2f: // DSUBU + m_r[RDREG] = m_r[RSREG] - m_r[RTREG]; + break; + case 0x30: // TGE + if (s64(m_r[RSREG]) >= s64(m_r[RTREG])) + cpu_exception(EXCEPTION_TR); + break; + case 0x31: // TGEU + if (m_r[RSREG] >= m_r[RTREG]) + cpu_exception(EXCEPTION_TR); + break; + case 0x32: // TLT + if (s64(m_r[RSREG]) < s64(m_r[RTREG])) + cpu_exception(EXCEPTION_TR); + break; + case 0x33: // TLTU + if (m_r[RSREG] < m_r[RTREG]) + cpu_exception(EXCEPTION_TR); + break; + case 0x34: // TEQ + if (m_r[RSREG] == m_r[RTREG]) + cpu_exception(EXCEPTION_TR); + break; + //case 0x35: // * + case 0x36: // TNE + if (m_r[RSREG] != m_r[RTREG]) + cpu_exception(EXCEPTION_TR); + break; + //case 0x37: // * + case 0x38: // DSLL + m_r[RDREG] = m_r[RTREG] << SHIFT; + break; + //case 0x39: // * + case 0x3a: // DSRL + m_r[RDREG] = m_r[RTREG] >> SHIFT; + break; + case 0x3b: // DSRA + m_r[RDREG] = s64(m_r[RTREG]) >> SHIFT; + break; + case 0x3c: // DSLL32 + m_r[RDREG] = m_r[RTREG] << (SHIFT + 32); + break; + //case 0x3d: // * + case 0x3e: // DSRL32 + m_r[RDREG] = m_r[RTREG] >> (SHIFT + 32); + break; + case 0x3f: // DSRA32 + m_r[RDREG] = s64(m_r[RTREG]) >> (SHIFT + 32); + break; + + default: + // * Operation codes marked with an asterisk cause reserved + // instruction exceptions in all current implementations and are + // reserved for future versions of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } + break; + case 0x01: // REGIMM + switch ((op >> 16) & 0x1f) + { + case 0x00: // BLTZ + if (s64(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x01: // BGEZ + if (s64(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x02: // BLTZL + if (s64(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x03: // BGEZL + if (s64(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + //case 0x04: // * + //case 0x05: // * + //case 0x06: // * + //case 0x07: // * + case 0x08: // TGEI + if (s64(m_r[RSREG]) >= s16(op)) + cpu_exception(EXCEPTION_TR); + break; + case 0x09: // TGEIU + if (m_r[RSREG] >= u16(op)) + cpu_exception(EXCEPTION_TR); + break; + case 0x0a: // TLTI + if (s64(m_r[RSREG]) < s16(op)) + cpu_exception(EXCEPTION_TR); + break; + case 0x0b: // TLTIU + if (m_r[RSREG] >= u16(op)) + cpu_exception(EXCEPTION_TR); + break; + case 0x0c: // TEQI + if (m_r[RSREG] == u16(op)) + cpu_exception(EXCEPTION_TR); + break; + //case 0x0d: // * + case 0x0e: // TNEI + if (m_r[RSREG] != u16(op)) + cpu_exception(EXCEPTION_TR); + break; + //case 0x0f: // * + case 0x10: // BLTZAL + if (s64(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + m_r[31] = ADDR(m_pc, 8); + break; + case 0x11: // BGEZAL + if (s64(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + m_r[31] = ADDR(m_pc, 8); + break; + case 0x12: // BLTZALL + if (s64(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + m_r[31] = ADDR(m_pc, 8); + break; + case 0x13: // BGEZALL + if (s64(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + m_r[31] = ADDR(m_pc, 8); + break; + //case 0x14: // * + //case 0x15: // * + //case 0x16: // * + //case 0x17: // * + //case 0x18: // * + //case 0x19: // * + //case 0x1a: // * + //case 0x1b: // * + //case 0x1c: // * + //case 0x1d: // * + //case 0x1e: // * + //case 0x1f: // * + + default: + // * Operation codes marked with an asterisk cause reserved + // instruction exceptions in all current implementations and are + // reserved for future versions of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } + break; + case 0x02: // J + m_branch_state = BRANCH; + m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2); + break; + case 0x03: // JAL + m_branch_state = BRANCH; + m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2); + m_r[31] = ADDR(m_pc, 8); + break; + case 0x04: // BEQ + if (m_r[RSREG] == m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x05: // BNE + if (m_r[RSREG] != m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x06: // BLEZ + if (s64(m_r[RSREG]) <= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x07: // BGTZ + if (s64(m_r[RSREG]) > 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x08: // ADDI + { + u32 const sum = u32(m_r[RSREG]) + s16(op); + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(u32(m_r[RSREG]) ^ s32(s16(op)), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31)) + cpu_exception(EXCEPTION_OV); + else + m_r[RTREG] = s64(s32(sum)); + } + break; + case 0x09: // ADDIU + m_r[RTREG] = s64(s32(u32(m_r[RSREG]) + s16(op))); + break; + case 0x0a: // SLTI + m_r[RTREG] = s64(m_r[RSREG]) < s64(s16(op)); + break; + case 0x0b: // SLTIU + m_r[RTREG] = u64(m_r[RSREG]) < u64(s16(op)); + break; + case 0x0c: // ANDI + m_r[RTREG] = m_r[RSREG] & u16(op); + break; + case 0x0d: // ORI + m_r[RTREG] = m_r[RSREG] | u16(op); + break; + case 0x0e: // XORI + m_r[RTREG] = m_r[RSREG] ^ u16(op); + break; + case 0x0f: // LUI + m_r[RTREG] = s64(s32(u16(op) << 16)); + break; + case 0x10: // COP0 + cp0_execute(op); + break; + case 0x11: // COP1 + cp1_execute(op); + break; + case 0x12: // COP2 + cp2_execute(op); + break; + //case 0x13: // * + case 0x14: // BEQL + if (m_r[RSREG] == m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x15: // BNEL + if (m_r[RSREG] != m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x16: // BLEZL + if (s64(m_r[RSREG]) <= 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x17: // BGTZL + if (s64(m_r[RSREG]) > 0) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x18: // DADDI + { + u64 const sum = m_r[RSREG] + s64(s16(op)); + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ s64(s16(op)), 63) && BIT(m_r[RSREG] ^ sum, 63)) + cpu_exception(EXCEPTION_OV); + else + m_r[RTREG] = sum; + } + break; + case 0x19: // DADDIU + m_r[RTREG] = m_r[RSREG] + s64(s16(op)); + break; + case 0x1a: // LDL + if (m_64 || !(SR & SR_KSU) || (SR & SR_EXL) || (SR & SR_ERL)) + cpu_ldl(op); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x1b: // LDR + if (m_64 || !(SR & SR_KSU) || (SR & SR_EXL) || (SR & SR_ERL)) + cpu_ldr(op); + else + cpu_exception(EXCEPTION_RI); + break; + //case 0x1c: // * + //case 0x1d: // * + //case 0x1e: // * + //case 0x1f: // * + case 0x20: // LB + load(ADDR(m_r[RSREG], op), + [this, op](s8 data) + { + m_r[RTREG] = data; + }); + break; + case 0x21: // LH + load(ADDR(m_r[RSREG], op), + [this, op](s16 data) + { + m_r[RTREG] = data; + }); + break; + case 0x22: // LWL + cpu_lwl(op); + break; + case 0x23: // LW + load(ADDR(m_r[RSREG], op), + [this, op](s32 data) + { + m_r[RTREG] = data; + }); + break; + case 0x24: // LBU + load(ADDR(m_r[RSREG], op), + [this, op](u8 data) + { + m_r[RTREG] = data; + }); + break; + case 0x25: // LHU + load(ADDR(m_r[RSREG], op), + [this, op](u16 data) + { + m_r[RTREG] = data; + }); + break; + case 0x26: // LWR + cpu_lwr(op); + break; + case 0x27: // LWU + load(ADDR(m_r[RSREG], op), + [this, op](u32 data) + { + m_r[RTREG] = data; + }); + break; + case 0x28: // SB + store(ADDR(m_r[RSREG], op), u8(m_r[RTREG])); + break; + case 0x29: // SH + store(ADDR(m_r[RSREG], op), u16(m_r[RTREG])); + break; + case 0x2a: // SWL + cpu_swl(op); + break; + case 0x2b: // SW + store(ADDR(m_r[RSREG], op), u32(m_r[RTREG])); + break; + case 0x2c: // SDL + if (m_64 || !(SR & SR_KSU) || (SR & SR_EXL) || (SR & SR_ERL)) + cpu_sdl(op); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x2d: // SDR + if (m_64 || !(SR & SR_KSU) || (SR & SR_EXL) || (SR & SR_ERL)) + cpu_sdr(op); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x2e: // SWR + cpu_swr(op); + break; + case 0x2f: // CACHE + if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) + { + cpu_exception(EXCEPTION_CP0); + break; + } + + switch ((op >> 16) & 0x1f) + { + case 0x00: // index invalidate (I) + if (ICACHE) + { + m_icache_tag[(ADDR(m_r[RSREG], op) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V; + break; + } + + case 0x04: // index load tag (I) + if (ICACHE) + { + u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], op) & m_icache_mask_hi) >> m_icache_shift]; + + m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25); + m_cp0[CP0_ECC] = 0; // data ecc or parity + + break; + } + + case 0x08: // index store tag (I) + if (ICACHE) + { + // FIXME: compute parity + m_icache_tag[(ADDR(m_r[RSREG], op) & m_icache_mask_hi) >> m_icache_shift] = + (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18; + + break; + } + + case 0x01: // index writeback invalidate (D) + case 0x02: // index invalidate (SI) + case 0x03: // index writeback invalidate (SD) + + case 0x05: // index load tag (D) + case 0x06: // index load tag (SI) + case 0x07: // index load tag (SI) + + case 0x09: // index store tag (D) + case 0x0a: // index store tag (SI) + case 0x0b: // index store tag (SD) + + case 0x0d: // create dirty exclusive (D) + case 0x0f: // create dirty exclusive (SD) + + case 0x10: // hit invalidate (I) + case 0x11: // hit invalidate (D) + case 0x12: // hit invalidate (SI) + case 0x13: // hit invalidate (SD) + + case 0x14: // fill (I) + case 0x15: // hit writeback invalidate (D) + case 0x17: // hit writeback invalidate (SD) + + case 0x18: // hit writeback (I) + case 0x19: // hit writeback (D) + case 0x1b: // hit writeback (SD) + + case 0x1e: // hit set virtual (SI) + case 0x1f: // hit set virtual (SD) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + } + break; + case 0x30: // LL + load_linked(ADDR(m_r[RSREG], op), + [this, op](u64 address, s32 data) + { + // remove existing tap + if (m_ll_watch) + m_ll_watch->remove(); + + m_r[RTREG] = data; + m_cp0[CP0_LLAddr] = u32(address >> 4); + + // install write tap + // FIXME: physical address truncation + m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "ll", + [this, hi(bool(BIT(address, 2)))](offs_t offset, u64 &data, u64 mem_mask) + { + if (hi ? ACCESSING_BITS_32_63 : ACCESSING_BITS_0_31) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + } + }); + }); + break; + case 0x31: // LWC1 + cp1_execute(op); + break; + case 0x32: // LWC2 + cp2_execute(op); + break; + //case 0x33: // * + case 0x34: // LLD + load_linked(ADDR(m_r[RSREG], op), + [this, op](u64 address, u64 data) + { + // remove existing tap + if (m_ll_watch) + m_ll_watch->remove(); + + m_r[RTREG] = data; + m_cp0[CP0_LLAddr] = u32(address >> 4); + + // install write tap + // FIXME: address truncation + m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "lld", + [this](offs_t offset, u64 &data, u64 mem_mask) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + }); + }); + break; + case 0x35: // LDC1 + cp1_execute(op); + break; + case 0x36: // LDC2 + cp2_execute(op); + break; + case 0x37: // LD + load(ADDR(m_r[RSREG], op), + [this, op](u64 data) + { + m_r[RTREG] = data; + }); + break; + case 0x38: // SC + if (m_ll_watch) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + + store(ADDR(m_r[RSREG], op), u32(m_r[RTREG])); + m_r[RTREG] = 1; + } + else + m_r[RTREG] = 0; + break; + case 0x39: // SWC1 + cp1_execute(op); + break; + case 0x3a: // SWC2 + cp2_execute(op); + break; + //case 0x3b: // * + case 0x3c: // SCD + if (m_ll_watch) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + + store(ADDR(m_r[RSREG], op), m_r[RTREG]); + m_r[RTREG] = 1; + } + else + m_r[RTREG] = 0; + break; + case 0x3d: // SDC1 + cp1_execute(op); + break; + case 0x3e: // SDC2 + cp2_execute(op); + break; + case 0x3f: // SD + store(ADDR(m_r[RSREG], op), m_r[RTREG]); + break; + + default: + // * Operation codes marked with an asterisk cause reserved instruction + // exceptions in all current implementations and are reserved for future + // versions of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } +} + +void r4000_base_device::cpu_exception(u32 exception, u16 const vector) +{ + if (exception != EXCEPTION_INT) + LOGMASKED(LOG_EXCEPTION, "exception 0x%08x\n", exception); + + if (!(SR & SR_EXL)) + { + m_cp0[CP0_EPC] = m_pc; + + CAUSE = (CAUSE & CAUSE_IP) | exception; + + // if in a branch delay slot, restart at the branch instruction + if (m_branch_state == DELAY) + { + m_cp0[CP0_EPC] -= 4; + CAUSE |= CAUSE_BD; + } + + SR |= SR_EXL; + + m_64 = m_cp0[CP0_Status] & SR_KX; + } + else + CAUSE = (CAUSE & (CAUSE_BD | CAUSE_IP)) | exception; + + m_branch_state = EXCEPTION; + m_pc = ((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector; + + if (exception != EXCEPTION_INT) + debugger_exception_hook(exception); +} + +void r4000_base_device::cpu_lwl(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3; + + load(offset & ~3, + [this, op, shift](u32 const data) + { + m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) << shift)) | (data << shift)); + }); +} + +void r4000_base_device::cpu_lwr(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 0x3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3; + + load(offset & ~3, + [this, op, shift](u32 const data) + { + m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) >> shift)) | (data >> shift)); + }); +} + +void r4000_base_device::cpu_swl(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3; + + store(offset & ~3, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift); +} + +void r4000_base_device::cpu_swr(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3; + + store(offset & ~3, u32(m_r[RTREG]) << shift, ~u32(0) << shift); +} + +void r4000_base_device::cpu_ldl(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3; + + load(offset & ~7, + [this, op, shift](u64 const data) + { + m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) << shift)) | (data << shift); + }); +} + +void r4000_base_device::cpu_ldr(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3; + + load(offset & ~7, + [this, op, shift](u64 const data) + { + m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) >> shift)) | (data >> shift); + }); +} + +void r4000_base_device::cpu_sdl(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3; + + store(offset & ~7, m_r[RTREG] >> shift, ~u64(0) >> shift); +} + +void r4000_base_device::cpu_sdr(u32 const op) +{ + unsigned const reverse = (SR & SR_RE) && ((SR & SR_KSU) == SR_KSU_U) ? 7 : 0; + u64 const offset = u64(ADDR(m_r[RSREG], op)) ^ reverse; + unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3; + + store(offset & ~7, m_r[RTREG] << shift, ~u64(0) << shift); +} + +void r4000_base_device::cp0_execute(u32 const op) +{ + if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) + { + cpu_exception(EXCEPTION_CP0); + return; + } + + switch ((op >> 21) & 0x1f) + { + case 0x00: // MFC0 + m_r[RTREG] = s32(cp0_get(RDREG)); + break; + case 0x01: // DMFC0 + // ε Operation codes marked with epsilon are valid when the + // processor is operating either in the Kernel mode or in the + // 64-bit non-Kernel (User or Supervisor) mode. These instructions + // cause a reserved instruction exception if 64-bit operation is + // not enabled in User or Supervisor mode. + if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || m_64) + m_r[RTREG] = cp0_get(RDREG); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x02: // CFC0 + break; + + case 0x04: // MTC0 + cp0_set(RDREG, s64(s32(m_r[RTREG]))); + break; + case 0x05: // DMTC0 + // ε Operation codes marked with epsilon are valid when the + // processor is operating either in the Kernel mode or in the + // 64-bit non-Kernel (User or Supervisor) mode. These instructions + // cause a reserved instruction exception if 64-bit operation is + // not enabled in User or Supervisor mode. + if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || m_64) + cp0_set(RDREG, m_r[RTREG]); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x06: // CTC0 + break; + + case 0x08: // BC0 + switch ((op >> 16) & 0x1f) + { + case 0x00: // BC0F + case 0x01: // BC0T + case 0x02: // BC0FL + case 0x03: // BC0TL + // fall through + + default: + // γ Operation codes marked with a gamma cause a reserved + // instruction exception. They are reserved for future versions + // of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } + 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: case 0x1d: case 0x1e: case 0x1f: + // CP0 function + switch (op & 0x3f) + { + case 0x01: // TLBR + cp0_tlbr(); + break; + case 0x02: // TLBWI + cp0_tlbwi(m_cp0[CP0_Index] & 0x3f); + break; + + case 0x06: // TLBWR + cp0_tlbwr(); + break; + + case 0x08: // TLBP + cp0_tlbp(); + break; + + case 0x10: // RFE + // ξ Operation codes marked with a xi cause a reserved + // instruction exception on R4000 processors. + cpu_exception(EXCEPTION_RI); + break; + + case 0x18: // ERET + if (SR & SR_ERL) + { + logerror("eret from error\n"); + m_branch_state = EXCEPTION; + m_pc = m_cp0[CP0_ErrorEPC]; + SR &= ~SR_ERL; + } + else + { + m_branch_state = EXCEPTION; + m_pc = m_cp0[CP0_EPC]; + SR &= ~SR_EXL; + } + cp0_mode_check(); + + if (m_ll_watch) + { + m_ll_watch->remove(); + m_ll_watch = nullptr; + } + break; + + default: + // Φ Operation codes marked with a phi are invalid but do not + // cause reserved instruction exceptions in R4000 implementations. + break; + } + break; + + default: + // γ Operation codes marked with a gamma cause a reserved + // instruction exception. They are reserved for future versions + // of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } +} + +u64 r4000_base_device::cp0_get(unsigned const reg) +{ + switch (reg) + { + case CP0_Count: + return u32((total_cycles() - m_cp0_timer_zero) / 2); + + case CP0_Random: + { + u8 const wired = m_cp0[CP0_Wired] & 0x3f; + + if (wired < ARRAY_LENGTH(m_tlb)) + return ((total_cycles() - m_cp0_timer_zero) % (ARRAY_LENGTH(m_tlb) - wired) + wired) & 0x3f; + else + return ARRAY_LENGTH(m_tlb) - 1; + } + break; + + default: + return m_cp0[reg]; + } +} + +void r4000_base_device::cp0_set(unsigned const reg, u64 const data) +{ + switch (reg) + { + case CP0_Count: + m_cp0[CP0_Count] = u32(data); + m_cp0_timer_zero = total_cycles() - m_cp0[CP0_Count] * 2; + + cp0_update_timer(); + break; + + case CP0_EntryHi: + m_cp0[CP0_EntryHi] = data & (EH_R | EH_VPN2_64 | EH_ASID); + break; + + case CP0_Compare: + m_cp0[CP0_Compare] = u32(data); + CAUSE &= ~CAUSE_IPEX5; + + cp0_update_timer(true); + break; + + case CP0_Status: + m_cp0[CP0_Status] = u32(data); + + // reevaluate operating mode + cp0_mode_check(); + + // FIXME: software interrupt check + if (CAUSE & SR & SR_IMSW) + m_icount = 0; + + if (data & SR_RE) + fatalerror("unimplemented reverse endian mode enabled (%s)\n", machine().describe_context().c_str()); + break; + + case CP0_Cause: + CAUSE = (CAUSE & ~CAUSE_IPSW) | (data & CAUSE_IPSW); + + // FIXME: software interrupt check + if (CAUSE & SR & SR_IMSW) + m_icount = 0; + break; + + case CP0_PRId: + // read-only register + break; + + case CP0_Config: + m_cp0[CP0_Config] = (m_cp0[CP0_Config] & ~CONFIG_WM) | (data & CONFIG_WM); + + if (m_cp0[CP0_Config] & CONFIG_IB) + { + m_icache_line_size = 32; + m_icache_shift = 5; + m_icache_mask_lo = ~u32(0x1f); + } + else + { + m_icache_line_size = 16; + m_icache_shift = 4; + m_icache_mask_lo = ~u32(0xf); + } + + LOGMASKED(LOG_CACHE, "icache/dcache line sizes %d/%d bytes\n", + m_icache_line_size, m_cp0[CP0_Config] & CONFIG_DB ? 32 : 16); + break; + + default: + m_cp0[reg] = data; + break; + } +} + +void r4000_base_device::cp0_tlbr() +{ + u8 const index = m_cp0[CP0_Index] & 0x3f; + + if (index < ARRAY_LENGTH(m_tlb)) + { + tlb_entry_t const &entry = m_tlb[index]; + + m_cp0[CP0_PageMask] = entry.mask; + m_cp0[CP0_EntryHi] = entry.vpn; + m_cp0[CP0_EntryLo0] = entry.pfn[0]; + m_cp0[CP0_EntryLo1] = entry.pfn[1]; + } +} + +void r4000_base_device::cp0_tlbwi(u8 const index) +{ + if (index < ARRAY_LENGTH(m_tlb)) + { + tlb_entry_t &entry = m_tlb[index]; + + entry.mask = m_cp0[CP0_PageMask]; + entry.vpn = m_cp0[CP0_EntryHi]; + if ((m_cp0[CP0_EntryLo0] & EL_G) && (m_cp0[CP0_EntryLo1] & EL_G)) + entry.vpn |= EH_G; + entry.pfn[0] = m_cp0[CP0_EntryLo0]; + entry.pfn[1] = m_cp0[CP0_EntryLo1]; + + entry.low_bit = 32 - count_leading_zeros((entry.mask >> 1) | 0xfff); + + LOGMASKED(LOG_TLB, "tlb write index %02d mask 0x%016x vpn2 0x%016x %c asid 0x%02x pfn0 0x%016x %c%c pfn1 0x%016x %c%c (%s)\n", + index, entry.mask, + entry.vpn, entry.vpn & EH_G ? 'G' : '-', entry.vpn & EH_ASID, + entry.pfn[0] & EL_PFN, entry.pfn[0] & EL_D ? 'D' : '-', entry.pfn[0] & EL_V ? 'V' : '-', + entry.pfn[1] & EL_PFN, entry.pfn[1] & EL_D ? 'D' : '-', entry.pfn[1] & EL_V ? 'V' : '-', + machine().describe_context()); + } +} + +void r4000_base_device::cp0_tlbwr() +{ + u8 const wired = m_cp0[CP0_Wired] & 0x3f; + u8 const unwired = ARRAY_LENGTH(m_tlb) - wired; + + u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (ARRAY_LENGTH(m_tlb) - 1); + + cp0_tlbwi(index); +} + +void r4000_base_device::cp0_tlbp() +{ + m_cp0[CP0_Index] = 0x80000000; + for (u8 index = 0; index < ARRAY_LENGTH(m_tlb); index++) + { + tlb_entry_t const &entry = m_tlb[index]; + + u64 const mask = (m_64 ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask)) + | ((entry.vpn & EH_G) ? 0 : EH_ASID); + + if ((entry.vpn & mask) == (m_cp0[CP0_EntryHi] & mask)) + { + m_cp0[CP0_Index] = index; + break; + } + } + + if (m_cp0[CP0_Index] == 0x80000000) + LOGMASKED(LOG_TLB, "tlbp miss 0x%08x\n", m_cp0[CP0_EntryHi]); + else + LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index]); +} + +void r4000_base_device::cp0_update_timer(bool start) +{ + if (start || m_cp0_timer->enabled()) + { + u32 const count = (total_cycles() - m_cp0_timer_zero) / 2; + u64 const delta = m_cp0[CP0_Compare] - count; + + m_cp0_timer->adjust(cycles_to_attotime(delta * 2)); + } +} + +TIMER_CALLBACK_MEMBER(r4000_base_device::cp0_timer_callback) +{ + set_input_line(5, ASSERT_LINE); +} + +void r4000_base_device::cp0_mode_check() +{ + if (!(m_cp0[CP0_Status] & (SR_EXL | SR_ERL))) + switch (m_cp0[CP0_Status] & SR_KSU) + { + case SR_KSU_K: m_64 = m_cp0[CP0_Status] & SR_KX; break; + case SR_KSU_S: m_64 = m_cp0[CP0_Status] & SR_SX; break; + case SR_KSU_U: m_64 = m_cp0[CP0_Status] & SR_UX; break; + } + else + m_64 = m_cp0[CP0_Status] & SR_KX; +} + +void r4000_base_device::cp1_execute(u32 const op) +{ + if (!(SR & SR_CU1)) + { + cpu_exception(EXCEPTION_CP1); + return; + } + + softfloat_exceptionFlags = 0; + switch (op >> 26) + { + case 0x11: // COP1 + switch ((op >> 21) & 0x1f) + { + case 0x00: // MFC1 + if (SR & SR_FR) + m_r[RTREG] = s64(s32(m_f[RDREG])); + else + if (RDREG & 1) + // move the high half of the even floating point register + m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 32)); + else + // move the low half of the even floating point register + m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 0)); + break; + case 0x01: // DMFC1 + // TODO: MIPS3 only + if ((SR & SR_FR) || !(RDREG & 1)) + m_r[RTREG] = m_f[RDREG]; + break; + case 0x02: // CFC1 + switch (RDREG) + { + case 0: m_r[RTREG] = m_fcr0; break; + case 31: m_r[RTREG] = m_fcr31; break; + + default: + logerror("cfc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context()); + break; + } + break; + case 0x04: // MTC1 + if (SR & SR_FR) + m_f[RDREG] = u32(m_r[RTREG]); + else + if (RDREG & 1) + // load the high half of the even floating point register + m_f[RDREG & ~1] = (m_r[RTREG] << 32) | u32(m_f[RDREG & ~1]); + else + // load the low half of the even floating point register + m_f[RDREG & ~1] = (m_f[RDREG & ~1] & ~0xffffffffULL) | u32(m_r[RTREG]); + break; + case 0x05: // DMTC1 + // TODO: MIPS3 only + if ((SR & SR_FR) || !(RDREG & 1)) + m_f[RDREG] = m_r[RTREG]; + break; + case 0x06: // CTC1 + switch (RDREG) + { + case 0: // register is read-only + break; + + case 31: + m_fcr31 = u32(m_r[RTREG]); + + // update rounding mode + switch (m_fcr31 & FCR31_RM) + { + case 0: softfloat_roundingMode = softfloat_round_near_even; break; + case 1: softfloat_roundingMode = softfloat_round_minMag; break; + case 2: softfloat_roundingMode = softfloat_round_max; break; + case 3: softfloat_roundingMode = softfloat_round_min; break; + } + + // exception check + if ((m_fcr31 & FCR31_CE) || ((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + cpu_exception(EXCEPTION_FPE); + + break; + + default: + logerror("ctc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context()); + break; + } + break; + case 0x08: // BC + switch ((op >> 16) & 0x1f) + { + case 0x00: // BC1F + if (!(m_fcr31 & FCR31_C)) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x01: // BC1T + if (m_fcr31 & FCR31_C) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + break; + case 0x02: // BC1FL + if (!(m_fcr31 & FCR31_C)) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + case 0x03: // BC1TL + if (m_fcr31 & FCR31_C) + { + m_branch_state = BRANCH; + m_branch_target = ADDR(m_pc, (s16(op) << 2) + 4); + } + else + m_branch_state = NULLIFY; + break; + + default: + // reserved instructions + cpu_exception(EXCEPTION_RI); + break; + } + break; + + case 0x10: // S + switch (op & 0x3f) + { + case 0x00: // ADD.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_add(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v); + break; + case 0x01: // SUB.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_sub(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v); + break; + case 0x02: // MUL.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v); + break; + case 0x03: // DIV.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_div(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v); + break; + case 0x04: // SQRT.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_sqrt(float32_t{ u32(m_f[FSREG]) }).v); + break; + case 0x05: // ABS.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ 0 })) + cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v); + break; + case 0x06: // MOV.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_f[FDREG] = m_f[FSREG]; + break; + case 0x07: // NEG.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v); + break; + case 0x08: // ROUND.L.S + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true)); + break; + case 0x09: // TRUNC.L.S + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true)); + break; + case 0x0a: // CEIL.L.S + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true)); + break; + case 0x0b: // FLOOR.L.S + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true)); + break; + case 0x0c: // ROUND.W.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true)); + break; + case 0x0d: // TRUNC.W.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true)); + break; + case 0x0e: // CEIL.W.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true)); + break; + case 0x0f: // FLOOR.W.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true)); + break; + + case 0x21: // CVT.D.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_f64(float32_t{ u32(m_f[FSREG]) }).v); + break; + case 0x24: // CVT.W.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true)); + break; + case 0x25: // CVT.L.S + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true)); + break; + + case 0x30: // C.F.S (false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.S (unordered) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x32: // C.EQ.S (equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x33: // C.UEQ.S (unordered equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x34: // C.OLT.S (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x35: // C.ULT.S (unordered less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x36: // C.OLE.S (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x37: // C.ULE.S (unordered less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + + case 0x38: // C.SF.S (signalling false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x39: // C.NGLE.S (not greater, less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x3a: // C.SEQ.S (signalling equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3b: // C.NGL.S (not greater or less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3c: // C.LT.S (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3d: // C.NGE.S (not greater or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3e: // C.LE.S (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3f: // C.NGT.S (not greater than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + + default: // unimplemented operations + m_fcr31 |= FCR31_CE; + cpu_exception(EXCEPTION_FPE); + break; + } + break; + case 0x11: // D + switch (op & 0x3f) + { + case 0x00: // ADD.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_add(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v); + break; + case 0x01: // SUB.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_sub(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v); + break; + case 0x02: // MUL.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v); + break; + case 0x03: // DIV.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_div(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v); + break; + case 0x04: // SQRT.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_sqrt(float64_t{ m_f[FSREG] }).v); + break; + case 0x05: // ABS.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ 0 })) + cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v); + break; + case 0x06: // MOV.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_f[FDREG] = m_f[FSREG]; + break; + case 0x07: // NEG.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v); + break; + case 0x08: // ROUND.L.D + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true)); + break; + case 0x09: // TRUNC.L.D + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true)); + break; + case 0x0a: // CEIL.L.D + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_max, true)); + break; + case 0x0b: // FLOOR.L.D + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_min, true)); + break; + case 0x0c: // ROUND.W.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true)); + break; + case 0x0d: // TRUNC.W.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true)); + break; + case 0x0e: // CEIL.W.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_max, true)); + break; + case 0x0f: // FLOOR.W.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_min, true)); + break; + + case 0x20: // CVT.S.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_f32(float64_t{ m_f[FSREG] }).v); + break; + case 0x24: // CVT.W.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true)); + break; + case 0x25: // CVT.L.D + // TODO: MIPS3 only + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true)); + break; + + case 0x30: // C.F.D (false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.D (unordered) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x32: // C.EQ.D (equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x33: // C.UEQ.D (unordered equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x34: // C.OLT.D (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x35: // C.ULT.D (unordered less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x36: // C.OLE.D (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x37: // C.ULE.D (unordered less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + } + break; + + case 0x38: // C.SF.D (signalling false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x39: // C.NGLE.D (not greater, less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + else + m_fcr31 &= ~FCR31_C; + } + break; + case 0x3a: // C.SEQ.D (signalling equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3b: // C.NGL.D (not greater or less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3c: // C.LT.D (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3d: // C.NGE.D (not greater or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3e: // C.LE.D (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + case 0x3f: // C.NGT.D (not greater than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + break; + + default: // unimplemented operations + m_fcr31 |= FCR31_CE; + cpu_exception(EXCEPTION_FPE); + break; + } + break; + case 0x14: // W + switch (op & 0x3f) + { + case 0x20: // CVT.S.W + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, i32_to_f32(s32(m_f[FSREG])).v); + break; + case 0x21: // CVT.D.W + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, i32_to_f64(s32(m_f[FSREG])).v); + break; + + default: // unimplemented operations + m_fcr31 |= FCR31_CE; + cpu_exception(EXCEPTION_FPE); + break; + } + break; + case 0x15: // L + // TODO: MIPS3 only + switch (op & 0x3f) + { + case 0x02a00020: // CVT.S.L + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, i64_to_f32(s64(m_f[FSREG])).v); + break; + case 0x02a00021: // CVT.D.L + if ((SR & SR_FR) || !(op & ODD_REGS)) + cp1_set(FDREG, i64_to_f64(s64(m_f[FSREG])).v); + break; + + default: // unimplemented operations + m_fcr31 |= FCR31_CE; + cpu_exception(EXCEPTION_FPE); + break; + } + break; + + default: // unimplemented operations + m_fcr31 |= FCR31_CE; + cpu_exception(EXCEPTION_FPE); + break; + } + break; + + case 0x31: // LWC1 + load(ADDR(m_r[RSREG], op), + [this, op](u32 data) + { + if (SR & SR_FR) + m_f[RTREG] = data; + else + if (RTREG & 1) + // load the high half of the even floating point register + m_f[RTREG & ~1] = (u64(data) << 32) | u32(m_f[RTREG & ~1]); + else + // load the low half of the even floating point register + m_f[RTREG & ~1] = (m_f[RTREG & ~1] & ~0xffffffffULL) | data; + }); + break; + + case 0x35: // LDC1 + load(ADDR(m_r[RSREG], op), + [this, op](u64 data) + { + if ((SR & SR_FR) || !(RTREG & 1)) + m_f[RTREG] = data; + }); + break; + + case 0x39: // SWC1 + if (SR & SR_FR) + store(ADDR(m_r[RSREG], op), u32(m_f[RTREG])); + else + if (RTREG & 1) + // store the high half of the even floating point register + store(ADDR(m_r[RSREG], op), u32(m_f[RTREG & ~1] >> 32)); + else + // store the low half of the even floating point register + store(ADDR(m_r[RSREG], op), u32(m_f[RTREG & ~1])); + break; + + case 0x3d: // SDC1 + if ((SR & SR_FR) || !(RTREG & 1)) + store(ADDR(m_r[RSREG], op), m_f[RTREG]); + break; + } +} + +void r4000_base_device::cp1_set(unsigned const reg, u64 const data) +{ + // translate softfloat exception flags to cause register + if (softfloat_exceptionFlags) + { + if (softfloat_exceptionFlags & softfloat_flag_inexact) + m_fcr31 |= FCR31_CI; + if (softfloat_exceptionFlags & softfloat_flag_underflow) + m_fcr31 |= FCR31_CU; + if (softfloat_exceptionFlags & softfloat_flag_overflow) + m_fcr31 |= FCR31_CO; + if (softfloat_exceptionFlags & softfloat_flag_infinite) + m_fcr31 |= FCR31_CZ; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_CV; + + // check if exception is enabled + if (((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + { + cpu_exception(EXCEPTION_FPE); + return; + } + + // set flags + m_fcr31 |= ((m_fcr31 & FCR31_CM) >> 10); + } + + m_f[reg] = data; +} + +void r4000_base_device::cp2_execute(u32 const op) +{ + if (!(SR & SR_CU2)) + { + cpu_exception(EXCEPTION_CP2); + return; + } + + switch (op >> 26) + { + case 0x12: // COP2 + switch ((op >> 21) & 0x1f) + { + case 0x00: // MFC2 + logerror("mfc2 unimplemented (%s)\n", machine().describe_context()); + break; + case 0x01: // DMFC2 + // ε Operation codes marked with epsilon are valid when the + // processor is operating either in the Kernel mode or in the + // 64-bit non-Kernel (User or Supervisor) mode. These instructions + // cause a reserved instruction exception if 64-bit operation is + // not enabled in User or Supervisor mode. + if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || m_64) + logerror("dmfc2 unimplemented (%s)\n", machine().describe_context()); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x02: // CFC2 + logerror("cfc2 unimplemented (%s)\n", machine().describe_context()); + break; + + case 0x04: // MTC2 + logerror("mtc2 unimplemented (%s)\n", machine().describe_context()); + break; + case 0x05: // DMTC2 + // ε Operation codes marked with epsilon are valid when the + // processor is operating either in the Kernel mode or in the + // 64-bit non-Kernel (User or Supervisor) mode. These instructions + // cause a reserved instruction exception if 64-bit operation is + // not enabled in User or Supervisor mode. + if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || m_64) + logerror("dmtc2 unimplemented (%s)\n", machine().describe_context()); + else + cpu_exception(EXCEPTION_RI); + break; + case 0x06: // CTC2 + logerror("ctc2 unimplemented (%s)\n", machine().describe_context()); + break; + + case 0x08: // BC2 + switch ((op >> 16) & 0x1f) + { + case 0x00: // BC2F + case 0x01: // BC2F + case 0x02: // BC2FL + case 0x03: // BC2TL + logerror("bc2 unimplemented (%s)\n", machine().describe_context()); + break; + + default: + // γ Operation codes marked with a gamma cause a reserved + // instruction exception. They are reserved for future versions + // of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } + 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: case 0x1d: case 0x1e: case 0x1f: + // CP2 function + logerror("function unimplemented (%s)\n", machine().describe_context()); + break; + + default: + // γ Operation codes marked with a gamma cause a reserved + // instruction exception. They are reserved for future versions + // of the architecture. + cpu_exception(EXCEPTION_RI); + break; + } + break; + + case 0x32: // LWC2 + logerror("lwc2 unimplemented (%s)\n", machine().describe_context()); + break; + + case 0x36: // LDC2 + logerror("ldc2 unimplemented (%s)\n", machine().describe_context()); + break; + + case 0x3a: // SWC2 + logerror("swc2 unimplemented (%s)\n", machine().describe_context()); + break; + + case 0x3e: // SDC2 + logerror("sdc2 unimplemented (%s)\n", machine().describe_context()); + break; + } +} + +r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &address) +{ + /* + * Decode the program address into one of the following ranges depending on + * the active status register bits. + * + * 32-bit modes + * user: 0x0000'0000-0x7fff'ffff (useg, mapped) + * + * super: 0x0000'0000-0x7fff'ffff (suseg, mapped) + * 0xc000'0000-0xdfff'ffff (ssseg, mapped) + * + * kernel: 0x0000'0000-0x7fff'ffff (kuseg, mapped) + * 0x8000'0000-0x9fff'ffff (kseg0, unmapped, cached) + * 0xa000'0000-0xbfff'ffff (kseg1, unmapped, uncached) + * 0xc000'0000-0xdfff'ffff (ksseg, mapped) + * 0xe000'0000-0xffff'ffff (kseg3, mapped) + * + * 64-bit modes + * user: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xuseg, mapped) + * + * super: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xsuseg, mapped) + * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xsseg, mapped) + * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (csseg, mapped) + * + * kernel: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xkuseg, mapped) + * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xksseg, mapped) + * 0x8000'0000'0000'0000-0xbfff'ffff'ffff'ffff (xkphys, unmapped) + * 0xc000'0000'0000'0000-0xc000'00ff'7fff'ffff (xkseg, mapped) + * 0xffff'ffff'8000'0000-0xffff'ffff'9fff'ffff (ckseg0, unmapped, cached) + * 0xffff'ffff'a000'0000-0xffff'ffff'bfff'ffff (ckseg1, unmapped, uncached) + * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (cksseg, mapped) + * 0xffff'ffff'e000'0000-0xffff'ffff'ffff'ffff (ckseg3, mapped) + */ + + bool extended = false; + + if (!(SR & SR_KSU) || (SR & SR_EXL) || (SR & SR_ERL)) + { + // kernel mode + if (SR & SR_KX) + { + // 64-bit kernel mode + if (address & 0xffff'ff00'0000'0000) + if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000) + extended = true; // xksseg + else + if ((address & 0xc000'0000'0000'0000) == 0x8000'0000'0000'0000) + { + address &= 0x0000'000f'ffff'ffff; // xkphys + + // FIXME: caching depends on top three bits + return CACHED; + } + else + if ((address & 0xffff'ff00'0000'0000) == 0xc000'0000'0000'0000) + if ((address & 0x0000'00ff'8000'0000) == 0x0000'00ff'8000'0000) + return ERROR; // exception + else + extended = true; // xkseg + else + // FIXME: ckseg0 caching depends on config regiter + switch (address & 0xffff'ffff'e000'0000) + { + case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // ckseg0 + case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // ckseg1 + case 0xffff'ffff'c000'0000: extended = true; break; // cksseg + case 0xffff'ffff'e000'0000: extended = true; break; // ckseg3 + default: return ERROR; // exception + } + else + if (SR & SR_ERL) + // FIXME: documentation says 2^31, but assume it should be 2^40 + return UNCACHED; // xkuseg (unmapped, uncached) + else + extended = true; // xkuseg + } + else + { + // 32-bit kernel mode + if (address & 0xffff'ffff'8000'0000) + switch (address & 0xffff'ffff'e000'0000) + { + case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // kseg0 + case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // kseg1 + case 0xffff'ffff'c000'0000: extended = false; break; // ksseg + case 0xffff'ffff'e000'0000: extended = false; break; // kseg3 + default: return ERROR; // exception + } + else + if (SR & SR_ERL) + return UNCACHED; // kuseg (unmapped, uncached) + else + extended = false; // kuseg + } + } + else if ((SR & SR_KSU) == SR_KSU_S) + { + // supervisor mode + if (SR & SR_SX) + { + // 64-bit supervisor mode + if (address & 0xffff'ff00'0000'0000) + if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000) + extended = true; // xsseg + else + if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000) + extended = true; // csseg + else + return ERROR; // exception + else + extended = true; // xsuseg + } + else + { + // 32-bit supervisor mode + if (address & 0xffff'ffff'8000'0000) + if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000) + extended = false; // sseg + else + return ERROR; // exception + else + extended = false; // suseg + } + } + else + { + // user mode + if (SR & SR_UX) + { + // 64-bit user mode + if (address & 0xffff'ff00'0000'0000) + return ERROR; // exception + else + extended = true; // xuseg + } + else + { + // 32-bit user mode + if (address & 0xffff'ffff'8000'0000) + return ERROR; // exception + else + extended = false; // useg + } + } + + // address needs translation, using a combination of VPN2 and ASID + u64 const key = (address & (extended ? (EH_R | EH_VPN2_64) : EH_VPN2_32)) | (m_cp0[CP0_EntryHi] & EH_ASID); + + bool invalid = false; + bool modify = false; + for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) + { + unsigned const index = (m_last[intention & TRANSLATE_TYPE_MASK] + i) % ARRAY_LENGTH(m_tlb); + tlb_entry_t const &entry = m_tlb[index]; + + // test vpn and asid + u64 const mask = (extended ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask)) + | ((entry.vpn & EH_G) ? 0 : EH_ASID); + + if ((entry.vpn & mask) != (key & mask)) + continue; + + u64 const pfn = entry.pfn[BIT(address, entry.low_bit)]; + + // test valid + if (!(pfn & EL_V)) + { + invalid = true; + break; + } + + // test dirty + if ((intention & TRANSLATE_WRITE) && !(pfn & EL_D)) + { + modify = true; + break; + } + + // translate the address + address &= (entry.mask >> 1) | 0xfff; + address |= ((pfn & EL_PFN) << 6) & ~(entry.mask >> 1); + + // remember the last-used tlb entry + m_last[intention & TRANSLATE_TYPE_MASK] = index; + + return (pfn & EL_C) == C_2 ? UNCACHED : CACHED; + } + + // tlb miss, invalid entry, or a store to a non-dirty entry + if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK)) + { + if (VERBOSE & LOG_TLB) + { + char const mode[] = { 'r', 'w', 'x' }; + + if (modify) + LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%016x (%s)\n", + m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); + else + LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%016x (%s)\n", + mode[intention & TRANSLATE_TYPE_MASK], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); + } + + // load tlb exception registers + m_cp0[CP0_BadVAddr] = address; + m_cp0[CP0_EntryHi] = key; + m_cp0[CP0_Context] = (m_cp0[CP0_Context] & CONTEXT_PTEBASE) | ((address >> 9) & CONTEXT_BADVPN2); + m_cp0[CP0_XContext] = (m_cp0[CP0_XContext] & XCONTEXT_PTEBASE) | ((address >> 31) & XCONTEXT_R) | ((address >> 9) & XCONTEXT_BADVPN2); + + if (invalid || modify || (SR & SR_EXL)) + cpu_exception(modify ? EXCEPTION_MOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL); + else + cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x000 : 0x080); + } + + return MISS; +} + +void r4000_base_device::address_error(int intention, u64 const address) +{ + if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK)) + { + logerror("address_error 0x%016x (%s)\n", address, machine().describe_context()); + + // TODO: check this + if (!(SR & SR_EXL)) + m_cp0[CP0_BadVAddr] = address; + + cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL); + } +} + +template std::enable_if_t>::value, bool> r4000_base_device::load(u64 address, U &&apply) +{ + // alignment error + if (address & (sizeof(T) - 1)) + { + address_error(TRANSLATE_READ, address); + return false; + } + + translate_t const t = translate(TRANSLATE_READ, address); + + // address error + if (t == ERROR) + { + address_error(TRANSLATE_READ, address); + + return false; + } + + // tlb miss + if (t == MISS) + return false; + + // watchpoint + if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL)) + { + u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0); + + if ((address & ~7) == watch_address) + { + cpu_exception(EXCEPTION_WATCH); + return false; + } + } + + // TODO: cache lookup + + switch (sizeof(T)) + { + case 1: apply(T(space(0).read_byte(address))); break; + case 2: apply(T(space(0).read_word(address))); break; + case 4: apply(T(space(0).read_dword(address))); break; + case 8: apply(T(space(0).read_qword(address))); break; + } + + return true; +} + +template std::enable_if_t>::value, bool> r4000_base_device::load_linked(u64 address, U &&apply) +{ + // alignment error + if (address & (sizeof(T) - 1)) + { + address_error(TRANSLATE_READ, address); + return false; + } + + translate_t const t = translate(TRANSLATE_READ, address); + + // address error + if (t == ERROR) + { + address_error(TRANSLATE_READ, address); + return false; + } + + // tlb miss + if (t == MISS) + return false; + + // watchpoint + if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL)) + { + u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0); + + if ((address & ~7) == watch_address) + { + cpu_exception(EXCEPTION_WATCH); + return false; + } + } + + // TODO: cache lookup + + switch (sizeof(T)) + { + case 4: apply(address, T(space(0).read_dword(address))); break; + case 8: apply(address, T(space(0).read_qword(address))); break; + } + + return true; +} + +template std::enable_if_t::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask) +{ + // alignment error + if (address & (sizeof(T) - 1)) + { + address_error(TRANSLATE_READ, address); + return false; + } + + translate_t const t = translate(TRANSLATE_WRITE, address); + + // address error + if (t == ERROR) + { + address_error(TRANSLATE_WRITE, address); + return false; + } + + // tlb miss + if (t == MISS) + return false; + + // watchpoint + if ((m_cp0[CP0_WatchLo] & WATCHLO_W) && !(SR & SR_EXL)) + { + u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0); + + if ((address & ~7) == watch_address) + { + cpu_exception(EXCEPTION_WATCH); + return false; + } + } + + // TODO: cache lookup + + switch (sizeof(T)) + { + case 1: space(0).write_byte(address, T(data)); break; + case 2: space(0).write_word(address, T(data), mem_mask); break; + case 4: space(0).write_dword(address, T(data), mem_mask); break; + case 8: space(0).write_qword(address, T(data), mem_mask); break; + } + + return true; +} + +bool r4000_base_device::fetch(u64 address, std::function &&apply) +{ + u64 const program_address = address; + + // alignment error + if (address & 3) + { + address_error(TRANSLATE_FETCH, address); + + return false; + } + + translate_t const t = translate(TRANSLATE_FETCH, address); + + // address error + if (t == ERROR) + { + address_error(TRANSLATE_FETCH, address); + + return false; + } + + // tlb miss + if (t == MISS) + return false; + + if (ICACHE) + { + if (t == UNCACHED) + { + apply(space(0).read_dword(address)); + + return true; + } + + // look up the tag + u32 const cache_address = (program_address & m_icache_mask_hi); + u32 &tag = m_icache_tag[cache_address >> m_icache_shift]; + + // check for cache miss + if (!(tag & ICACHE_V) || (tag & ICACHE_PTAG) != (address >> 12)) + { + // cache miss + m_icache_miss++; + + // reload the cache line + tag = ICACHE_V | (address >> 12); + for (unsigned i = 0; i < m_icache_line_size; i += 8) + { + u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i); + + m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data); + m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32; + } + } + else + m_icache_hit++; + + // apply the result + apply(m_icache_data[cache_address >> 2]); + } + else + apply(space(0).read_dword(address)); + + return true; +} + +std::string r4000_base_device::debug_string(u64 string_pointer, unsigned limit) +{ + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + bool mapped = false; + std::string result(""); + + while (!done) + { + done = true; + load(string_pointer++, [limit, &done, &mapped, &result](u8 byte) + { + mapped = true; + if (byte != 0) + { + result += byte; + + done = result.length() == limit; + } + }); + } + + if (!mapped) + result.assign("[unmapped]"); + + return result; +} + +std::string r4000_base_device::debug_string_array(u64 array_pointer) +{ + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + std::string result(""); + + while (!done) + { + done = true; + load(array_pointer, [this, &done, &result](u64 string_pointer) + { + if (string_pointer != 0) + { + if (!result.empty()) + result += ", "; + + result += '\"' + debug_string(string_pointer) + '\"'; + + done = false; + } + }); + + array_pointer += 4; + } + + return result; +} + +std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer) +{ + auto const suppressor(machine().disable_side_effects()); + + std::wstring result(L""); + + if (!load(unicode_string_pointer, + [this, unicode_string_pointer, &result](u16 const length) + { + if (length) + if (!load(unicode_string_pointer + 4, + [this, length, &result](s32 buffer) + { + for (int i = 0; i < length; i += 2) + load(buffer + i, [&result](wchar_t const character) { result += character; }); + })) + result.assign(L"[unmapped]"); + })) + result.assign(L"[unmapped]"); + + return utf8_from_wstring(result); +} diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h new file mode 100644 index 00000000000..d067d305a5f --- /dev/null +++ b/src/devices/cpu/mips/r4000.h @@ -0,0 +1,460 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_CPU_MIPS_R4000_H +#define MAME_CPU_MIPS_R4000_H + +#pragma once + +DECLARE_DEVICE_TYPE(R4000, r4000_device) +DECLARE_DEVICE_TYPE(R4400, r4400_device) +DECLARE_DEVICE_TYPE(R4600, r4600_device) + +class r4000_base_device : public cpu_device +{ +public: + enum config_mask : u32 + { + CONFIG_K0 = 0x00000007, // kseg0 cache coherency + CONFIG_CU = 0x00000080, // store conditional cache coherent + CONFIG_DB = 0x00000010, // primary d-cache line 32 bytes + CONFIG_IB = 0x00000020, // primary i-cache line 32 bytes + CONFIG_DC = 0x000001c0, // primary d-cache size + CONFIG_IC = 0x00000e00, // primary i-cache size + CONFIG_EB = 0x00002000, // sub-block ordering + CONFIG_EM = 0x00004000, // parity mode enable + CONFIG_BE = 0x00008000, // big endian + CONFIG_SM = 0x00010000, // dirty shared disable + CONFIG_SC = 0x00020000, // secondary cache absent + CONFIG_EW = 0x000c0000, // system port width + CONFIG_SW = 0x00100000, // secondary cache port width + CONFIG_SS = 0x00200000, // split secondary cache mode + CONFIG_SB = 0x00c00000, // secondary cache line size + CONFIG_EP = 0x0f000000, // transmit data pattern + CONFIG_EC = 0x70000000, // system clock ratio + CONFIG_CM = 0x80000000, // master/checker enable + + CONFIG_WM = 0x0000003f, // runtime-writable bits + }; + void set_config(u32 data, u32 mem_mask = ~u32(0)) + { + if (!configured()) + COMBINE_DATA(&m_cp0[CP0_Config]); + } + +protected: + enum cache_size_t + { + CACHE_4K = 0, + CACHE_8K = 1, + CACHE_16K = 2, + CACHE_32K = 3, + CACHE_64K = 4, + CACHE_128K = 5, + CACHE_256K = 6, + CACHE_512K = 7, + }; + r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size); + + enum cp0_reg : int + { + CP0_Index = 0, + CP0_Random = 1, + CP0_EntryLo0 = 2, + CP0_EntryLo1 = 3, + CP0_Context = 4, + CP0_PageMask = 5, + CP0_Wired = 6, + CP0_BadVAddr = 8, + CP0_Count = 9, + CP0_EntryHi = 10, + CP0_Compare = 11, + CP0_Status = 12, + CP0_Cause = 13, + CP0_EPC = 14, + CP0_PRId = 15, + CP0_Config = 16, + CP0_LLAddr = 17, + CP0_WatchLo = 18, + CP0_WatchHi = 19, + CP0_XContext = 20, + CP0_ECC = 26, + CP0_CacheErr = 27, + CP0_TagLo = 28, + CP0_TagHi = 29, + CP0_ErrorEPC = 30, + }; + + enum cp0_sr_mask : u32 + { + SR_IE = 0x00000001, // interrupt enable + SR_EXL = 0x00000002, // exception level + SR_ERL = 0x00000004, // error level + SR_KSU = 0x00000018, // kernel/supervisor/user mode + SR_UX = 0x00000020, // 64-bit addressing user mode + SR_SX = 0x00000040, // 64-bit addressing supervisor mode + SR_KX = 0x00000080, // 64-bit addressing kernel mode + SR_IMSW0 = 0x00000100, // software interrupt 0 enable + SR_IMSW1 = 0x00000200, // software interrupt 1 enable + SR_IMEX0 = 0x00000400, // external interrupt 0 enable + SR_IMEX1 = 0x00000800, // external interrupt 1 enable + SR_IMEX2 = 0x00001000, // external interrupt 2 enable + SR_IMEX3 = 0x00002000, // external interrupt 3 enable + SR_IMEX4 = 0x00004000, // external interrupt 4 enable + SR_IMEX5 = 0x00008000, // external interrupt 5 enable + SR_DE = 0x00010000, // disable cache parity/ecc exceptions + SR_CE = 0x00020000, // cache ecc check enable + SR_CH = 0x00040000, // cache hit + SR_SR = 0x00100000, // soft reset + SR_TS = 0x00200000, // tlb shutdown (read only) + SR_BEV = 0x00400000, // bootstrap exception vectors + SR_RE = 0x02000000, // reverse endian + SR_FR = 0x04000000, // enable additional floating-point registers + SU_RP = 0x08000000, // reduced power + SR_CU0 = 0x10000000, // coprocessor usable 0 + SR_CU1 = 0x20000000, // coprocessor usable 1 + SR_CU2 = 0x40000000, // coprocessor usable 2 + SR_CU3 = 0x80000000, // coprocessor usable 3 + + SR_IMSW = 0x00000300, + SR_IM = 0x0000ff00, // interrupt mask + SR_DS = 0x01ff0000, // diagnostic status + }; + + enum cp0_sr_ksu_mode : u32 + { + SR_KSU_K = 0x00000000, // kernel mode + SR_KSU_S = 0x00000008, // supervisor mode + SR_KSU_U = 0x00000010, // user mode + }; + + enum cp0_cause_mask : u32 + { + CAUSE_EXCCODE = 0x0000007c, // exception code + CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending + CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending + CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending + CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending + CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending + CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending + CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending + CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending + CAUSE_CE = 0x30000000, // coprocessor unit + CAUSE_BD = 0x80000000, // branch delay slot + + CAUSE_IPSW = 0x00000300, + CAUSE_IPHW = 0x0000fc00, + CAUSE_IP = 0x0000ff00, + }; + + enum cp0_watchlo_mask : u32 + { + WATCHLO_W = 0x00000001, // trap on store + WATCHLO_R = 0x00000002, // trap on load + WATCHLO_PADDR0 = 0xfffffff8, // physical address bits 31:3 + }; + enum cp0_watchhi_mask : u32 + { + WATCHHI_PADDR1 = 0x0000000f, // physical address bits 35:32 + }; + + enum cp0_tlb_mask : u64 + { + TLB_MASK = 0x0000'0000'01ff'e000, + }; + enum cp0_tlb_eh : u64 + { + EH_ASID = 0x0000'0000'0000'00ff, // address space id + EH_G = 0x0000'0000'0000'1000, // global (tlb only) + EH_VPN2_32 = 0x0000'0000'ffff'e000, // virtual page number (32-bit mode) + EH_VPN2_64 = 0x0000'00ff'ffff'e000, // virtual page number (64-bit mode) + EH_R = 0xc000'0000'0000'0000, // region (64-bit mode) + }; + enum cp0_tlb_el : u64 + { + EL_G = 0x0000'0000'0000'0001, // global (entrylo only) + EL_V = 0x0000'0000'0000'0002, // valid + EL_D = 0x0000'0000'0000'0004, // dirty + EL_C = 0x0000'0000'0000'0038, // coherency + EL_PFN = 0x0000'0000'3fff'ffc0, // page frame number + }; + enum cp0_tlb_el_c : u64 + { + C_0 = 0x00, // reserved + C_1 = 0x08, // reserved + C_2 = 0x10, // uncached + C_3 = 0x18, // cacheable noncoherent (noncoherent) + C_4 = 0x20, // cacheable coherent exclusive (exclusive) + C_5 = 0x28, // cacheable coherent exclusive on write (sharable) + C_6 = 0x30, // cacheable coherent update on write (update) + C_7 = 0x38, // reserved + }; + + enum cp0_context_mask : u64 + { + CONTEXT_PTEBASE = 0xffff'ffff'ff80'0000, + CONTEXT_BADVPN2 = 0x0000'0000'007f'fff0, + }; + + enum cp0_xcontext_mask : u64 + { + XCONTEXT_PTEBASE = 0xffff'fffe'0000'0000, // page table entry base + XCONTEXT_R = 0x0000'0001'8000'0000, // region + XCONTEXT_BADVPN2 = 0x0000'0000'7fff'fff0, // bad virtual page number / 2 + }; + + enum cp0_pagemask_mask : u32 + { + PAGEMASK = 0x01ff'e000, + }; + + enum exception_mask : u32 + { + EXCEPTION_INT = 0x00000000, // interrupt + EXCEPTION_MOD = 0x00000004, // tlb modification + EXCEPTION_TLBL = 0x00000008, // tlb load + EXCEPTION_TLBS = 0x0000000c, // tlb store + EXCEPTION_ADEL = 0x00000010, // address error load + EXCEPTION_ADES = 0x00000014, // address error store + EXCEPTION_IBE = 0x00000018, // bus error (instruction fetch) + EXCEPTION_DBE = 0x0000001c, // bus error (data reference: load or store) + EXCEPTION_SYS = 0x00000020, // syscall + EXCEPTION_BP = 0x00000024, // breakpoint + EXCEPTION_RI = 0x00000028, // reserved instruction + EXCEPTION_CPU = 0x0000002c, // coprocessor unusable + EXCEPTION_OV = 0x00000030, // arithmetic overflow + EXCEPTION_TR = 0x00000034, // trap + EXCEPTION_VCEI = 0x00000038, // virtual coherency exception instruction + EXCEPTION_FPE = 0x0000003c, // floating point + EXCEPTION_WATCH = 0x0000005c, // reference to watchhi/watchlo address + EXCEPTION_VCED = 0x0000007c, // virtual coherency exception data + + EXCEPTION_CP0 = 0x0000002c, // coprocessor 0 unusable + EXCEPTION_CP1 = 0x1000002c, // coprocessor 1 unusable + EXCEPTION_CP2 = 0x2000002c, // coprocessor 2 unusable + EXCEPTION_CP3 = 0x3000002c, // coprocessor 3 unusable + }; + + enum cp1_fcr31_mask : u32 + { + FCR31_RM = 0x00000003, // rounding mode + + FCR31_FI = 0x00000004, // inexact operation flag + FCR31_FU = 0x00000008, // underflow flag + FCR31_FO = 0x00000010, // overflow flag + FCR31_FZ = 0x00000020, // divide by zero flag + FCR31_FV = 0x00000040, // invalid operation flag + + FCR31_EI = 0x00000080, // inexact operation enable + FCR31_EU = 0x00000100, // underflow enable + FCR31_EO = 0x00000200, // overflow enable + FCR31_EZ = 0x00000400, // divide by zero enable + FCR31_EV = 0x00000800, // invalid operation enable + + FCR31_CI = 0x00001000, // inexact operation cause + FCR31_CU = 0x00002000, // underflow cause + FCR31_CO = 0x00004000, // overflow cause + FCR31_CZ = 0x00008000, // divide by zero cause + FCR31_CV = 0x00010000, // invalid operation cause + FCR31_CE = 0x00020000, // unimplemented operation cause + + FCR31_C = 0x00800000, // condition + FCR31_FS = 0x01000000, // flush denormalized results + + FCR31_FM = 0x0000007c, // flag mask + FCR31_EM = 0x00000f80, // enable mask + FCR31_CM = 0x0001f000, // cause mask (except unimplemented) + }; + + enum mips3_registers : unsigned + { + MIPS3_R0 = 0, + MIPS3_CP0 = 32, + MIPS3_F0 = 64, + + MIPS3_PC = 96, + MIPS3_HI, + MIPS3_LO, + MIPS3_FCR31, + }; + + enum cp0_taglo_mask : u32 + { + TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12 + TAGLO_PSTATE = 0x000000c0, // primary cache state + TAGLO_P = 0x00000001, // primary tag even parity + }; + enum icache_mask : u32 + { + ICACHE_PTAG = 0x00ffffff, // physical tag + ICACHE_V = 0x01000000, // valid + ICACHE_P = 0x02000000, // even parity + }; + enum dcache_mask : u32 + { + DCACHE_PTAG = 0x00ffffff, // physical tag + DCACHE_CS = 0x01000000, // primary cache state + DCACHE_P = 0x02000000, // even parity for ptag and cs + DCACHE_W = 0x02000000, // write-back + DCACHE_WP = 0x02000000, // even parity for write-back + }; + + // device_t overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_stop() override; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; + + // device_execute_interface overrides + virtual u32 execute_min_cycles() const override { return 1; } + virtual u32 execute_max_cycles() const override { return 40; } + virtual u32 execute_input_lines() const override { return 6; } + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + + // cpu implementation + void cpu_execute(u32 const op); + void cpu_exception(u32 exception, u16 const vector = 0x180); + void cpu_lwl(u32 const op); + void cpu_lwr(u32 const op); + void cpu_ldl(u32 const op); + void cpu_ldr(u32 const op); + void cpu_swl(u32 const op); + void cpu_swr(u32 const op); + void cpu_sdl(u32 const op); + void cpu_sdr(u32 const op); + + // cp0 implementation + void cp0_execute(u32 const op); + u64 cp0_get(unsigned const reg); + void cp0_set(unsigned const reg, u64 const data); + void cp0_tlbr(); + void cp0_tlbwi(u8 const index); + void cp0_tlbwr(); + void cp0_tlbp(); + + // cp0 helpers + TIMER_CALLBACK_MEMBER(cp0_timer_callback); + void cp0_update_timer(bool start = false); + void cp0_mode_check(); + + // cp1 implementation + void cp1_execute(u32 const op); + void cp1_set(unsigned const reg, u64 const data); + + // cp2 implementation + void cp2_execute(u32 const op); + + // address and memory handling + enum translate_t { ERROR, MISS, UNCACHED, CACHED }; + translate_t translate(int intention, u64 &address); + void address_error(int intention, u64 const address); + + template std::enable_if_t>::value, bool> load(u64 program_address, U &&apply); + template std::enable_if_t>::value, bool> load_linked(u64 program_address, U &&apply); + template std::enable_if_t::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0)); + bool fetch(u64 address, std::function &&apply); + + // debugging helpers + std::string debug_string(u64 string_pointer, unsigned limit = 0); + std::string debug_string_array(u64 array_pointer); + std::string debug_unicode_string(u64 unicode_string_pointer); + + // device configuration state + address_space_config m_program_config_le; + address_space_config m_program_config_be; + + // runtime state + int m_icount; + + // cpu state + u64 m_pc; + u64 m_r[32]; + u64 m_hi; + u64 m_lo; + enum branch_state_t : unsigned + { + NONE = 0, + DELAY = 1, // delay slot instruction active + BRANCH = 2, // branch instruction active + EXCEPTION = 3, // exception triggered + NULLIFY = 4, // next instruction nullified + } + m_branch_state; + u64 m_branch_target; + + // cp0 state + u64 m_cp0[32]; + u64 m_cp0_timer_zero; + emu_timer *m_cp0_timer; + memory_passthrough_handler *m_ll_watch; + struct tlb_entry_t + { + u64 mask; + u64 vpn; + u64 pfn[2]; + + u8 low_bit; + } + m_tlb[48]; + unsigned m_last[3]; + bool m_64; + + // cp1 state + u64 m_f[32]; // floating point registers + u32 m_fcr0; // implementation and revision register + u32 m_fcr31; // control/status register + + // experimental icache state + u32 m_icache_mask_hi; + u32 m_icache_mask_lo; + unsigned m_icache_line_size; + unsigned m_icache_shift; + std::unique_ptr m_icache_tag; + std::unique_ptr m_icache_data; + + // experimental icache statistics + u64 m_icache_hit; + u64 m_icache_miss; +}; + +class r4000_device : public r4000_base_device +{ +public: + // NOTE: R4000 chips prior to 3.0 have an xtlb bug + r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, CACHE_8K, CACHE_8K) + { + // no secondary cache + m_cp0[CP0_Config] |= CONFIG_SC; + } +}; + +class r4400_device : public r4000_base_device +{ +public: + r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, CACHE_16K, CACHE_16K) + { + // no secondary cache + m_cp0[CP0_Config] |= CONFIG_SC; + } +}; + +class r4600_device : public r4000_base_device +{ +public: + r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2000, CACHE_16K, CACHE_16K) + { + // no secondary cache + m_cp0[CP0_Config] |= CONFIG_SC; + } +}; +#endif // MAME_CPU_MIPS_R4000_H -- cgit v1.2.3-70-g09d2 From 156619b401e85bfe14f771a59bd8d0272fa257a4 Mon Sep 17 00:00:00 2001 From: AJR Date: Sun, 3 Mar 2019 16:56:13 -0500 Subject: Add skeleton CPU device and disassembler for HPC architecture --- scripts/src/cpu.lua | 17 + scripts/target/mame/arcade.lua | 1 + scripts/target/mame/mess.lua | 1 + src/devices/cpu/hpc/hpc.cpp | 102 ++++++ src/devices/cpu/hpc/hpc.h | 73 ++++ src/devices/cpu/hpc/hpcdasm.cpp | 739 ++++++++++++++++++++++++++++++++++++++++ src/devices/cpu/hpc/hpcdasm.h | 49 +++ src/mame/drivers/age_candy.cpp | 22 +- src/tools/unidasm.cpp | 2 + 9 files changed, 993 insertions(+), 13 deletions(-) create mode 100644 src/devices/cpu/hpc/hpc.cpp create mode 100644 src/devices/cpu/hpc/hpc.h create mode 100644 src/devices/cpu/hpc/hpcdasm.cpp create mode 100644 src/devices/cpu/hpc/hpcdasm.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 4095f62a4c3..631af0dbcff 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -2800,3 +2800,20 @@ if (CPUS["ALPHA"]~=null or _OPTIONS["with-tools"]) then table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/alpha/alphad.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/alpha/alphad.h") end + +-------------------------------------------------- +-- National Semiconductor HPC +--@src/devices/cpu/hpc/hpc.h,CPUS["HPC"] = true +-------------------------------------------------- + +if (CPUS["HPC"]~=null) then + files { + MAME_DIR .. "src/devices/cpu/hpc/hpc.cpp", + MAME_DIR .. "src/devices/cpu/hpc/hpc.h", + } +end + +if (CPUS["HPC"]~=null or _OPTIONS["with-tools"]) then + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/hpc/hpcdasm.cpp") + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/hpc/hpcdasm.h") +end diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index d5c8b579f1f..41c6d9ce8f9 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -133,6 +133,7 @@ CPUS["HMCS40"] = true --CPUS["SM510"] = true CPUS["ST62XX"] = true CPUS["DSPP"] = true +CPUS["HPC"] = true -------------------------------------------------- -- specify available sound cores diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 29f067ce502..ffab589c5b6 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -138,6 +138,7 @@ CPUS["CLIPPER"] = true CPUS["CAPRICORN"] = true CPUS["ALPHA"] = true --CPUS["DSPP"] = true +--CPUS["HPC"] = true -------------------------------------------------- -- specify available sound cores; some of these are diff --git a/src/devices/cpu/hpc/hpc.cpp b/src/devices/cpu/hpc/hpc.cpp new file mode 100644 index 00000000000..6cf5f30eb37 --- /dev/null +++ b/src/devices/cpu/hpc/hpc.cpp @@ -0,0 +1,102 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/*************************************************************************** + + National Semiconductor High-Performance microController (HPC) + + Currently this device is just a stub with no actual execution core. + +***************************************************************************/ + +#include "emu.h" +#include "hpc.h" +#include "hpcdasm.h" + +// device type definitions +DEFINE_DEVICE_TYPE(HPC46104, hpc46104_device, "hpc46104", "HPC46104") + + +void hpc46104_device::internal_map(address_map &map) +{ + map(0x0000, 0x00bf).ram(); + map(0x00c0, 0x00c0).rw(FUNC(hpc46104_device::psw_r), FUNC(hpc46104_device::psw_w)); + map(0x00c4, 0x00cf).ram().share("core_regs"); + // TODO: many other internal registers + map(0x01c0, 0x02ff).ram(); +} + +std::unique_ptr hpc46104_device::create_disassembler() +{ + return std::make_unique(); +} + + +hpc_device::hpc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor map) + : cpu_device(mconfig, type, tag, owner, clock) + , m_program_config("program", ENDIANNESS_LITTLE, 16, 16, 0, map) + , m_program(nullptr) + , m_core_regs(*this, "core_regs") + , m_psw(0) + , m_icount(0) +{ +} + +hpc46104_device::hpc46104_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : hpc_device(mconfig, HPC46104, tag, owner, clock, address_map_constructor(FUNC(hpc46104_device::internal_map), this)) +{ +} + +device_memory_interface::space_config_vector hpc_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_PROGRAM, &m_program_config), + }; +} + + +void hpc_device::device_start() +{ + m_program = &space(AS_PROGRAM); + + set_icountptr(m_icount); + + state_add(HPC_PSW, "PSW", m_psw); + state_add(HPC_SP, "SP", m_core_regs[0]); + state_add(HPC_PC, "PC", m_core_regs[1]); + state_add(STATE_GENPC, "GENPC", m_core_regs[1]).callimport().noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_core_regs[1]).callimport().noshow(); + state_add(HPC_A, "A", m_core_regs[2]); + state_add(HPC_K, "K", m_core_regs[3]); + state_add(HPC_B, "B", m_core_regs[4]); + state_add(HPC_X, "X", m_core_regs[5]); + + save_item(NAME(m_psw)); +} + +void hpc_device::device_reset() +{ + m_psw = 0x00; + std::fill_n(&m_core_regs[0], 6, 0x0000); +} + + +u8 hpc_device::psw_r() +{ + return m_psw; +} + +void hpc_device::psw_w(u8 data) +{ + m_psw = data; +} + + +void hpc_device::execute_run() +{ + m_icount = 0; +} + +void hpc_device::execute_set_input(int inputnum, int state) +{ + // TODO +} diff --git a/src/devices/cpu/hpc/hpc.h b/src/devices/cpu/hpc/hpc.h new file mode 100644 index 00000000000..3f1dad6be3e --- /dev/null +++ b/src/devices/cpu/hpc/hpc.h @@ -0,0 +1,73 @@ +// license:BSD-3-Clause +// copyright-holders:AJR + +#ifndef MAME_CPU_HPC_HPC_H +#define MAME_CPU_HPC_HPC_H + +#pragma once + + +class hpc_device : public cpu_device +{ +public: + enum { + HPC_PSW, + HPC_SP, + HPC_PC, + HPC_A, + HPC_K, + HPC_B, + HPC_X + }; + +protected: + // construction/destruction + hpc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor map); + + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // device_execute_interface overrides + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + + // internal register access + u8 psw_r(); + void psw_w(u8 data); + +private: + // address space + address_space_config m_program_config; + address_space *m_program; + + // internal state + required_shared_ptr m_core_regs; + u8 m_psw; + + // execution sequencing + s32 m_icount; +}; + +class hpc46104_device : public hpc_device +{ +public: + // construction/destruction + hpc46104_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; + +private: + void internal_map(address_map &map); +}; + + +// device type declarations +DECLARE_DEVICE_TYPE(HPC46104, hpc46104_device) + +#endif // MAME_CPU_HPC_HPC_H diff --git a/src/devices/cpu/hpc/hpcdasm.cpp b/src/devices/cpu/hpc/hpcdasm.cpp new file mode 100644 index 00000000000..aee29751838 --- /dev/null +++ b/src/devices/cpu/hpc/hpcdasm.cpp @@ -0,0 +1,739 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/*************************************************************************** + + National Semiconductor HPC disassembler + + Note that though all 16-bit fields in instructions have the MSB first, + the HPC's memory organization is in fact little-endian. This is why r16 + is not used to read them. + +***************************************************************************/ + +#include "util/disasmintf.h" +#include "hpcdasm.h" + +#include "util/strformat.h" +#include + +const char *const hpc16164_disassembler::s_regs[128] = +{ + "psw", nullptr, "SP", "PC", "A", "K", "B", "X", + "enir", "irpd", "ircd", "sio", "porti", nullptr, "halten", "romdump", + "porta", "portb", nullptr, "upic", nullptr, nullptr, nullptr, nullptr, + "dira", "dirb", "bfun", nullptr, nullptr, nullptr, nullptr, nullptr, + "adcr1", "adcr2", "portd", "adcr3", nullptr, nullptr, nullptr, nullptr, + "ad0", "ad1", "ad2", "ad3", "ad4", "ad5", "ad6", "ad7", + "enu", "enui", "rbuf", "tbuf", "enur", nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "t4", "r4", "t5", "r5", "t6", "r6", "t7", "r7", + "pwmode", "portp", nullptr, nullptr, nullptr, nullptr, "eicon", "eicr", + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "i4cr", "i3cr", "i2cr", "r2", "t2", "r3", "t3", "divby", + "tmmode", "t0con", "watchdog", nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr +}; + +hpc_disassembler::hpc_disassembler(const char *const regs[]) + : util::disasm_interface() + , m_regs(regs) +{ +} + +u32 hpc_disassembler::opcode_alignment() const +{ + return 1; +} + +void hpc_disassembler::format_register(std::ostream &stream, u16 reg) const +{ + if (reg >= 0x00c0 && reg < 0x01c0) + { + const char *name = m_regs[(reg - 0x00c0) >> 1]; + if (name != nullptr) + { + stream << name; + if (BIT(reg, 0)) + stream << "+1"; + return; + } + } + + util::stream_format(stream, "0%X", reg); +} + +void hpc_disassembler::format_immediate_byte(std::ostream &stream, u8 data) const +{ + stream << "#"; + if (data >= 0x10) + stream << "0"; + util::stream_format(stream, "%02X", data); +} + +void hpc_disassembler::format_immediate_word(std::ostream &stream, u16 data) const +{ + stream << "#"; + if (data >= 0x1000) + stream << "0"; + util::stream_format(stream, "%04X", data); +} + +void hpc_disassembler::disassemble_op(std::ostream &stream, const char *op, u16 reg, u16 src, bool imm, bool indir, bool idx, bool w) const +{ + util::stream_format(stream, "%-8s", op); + if (idx) + stream << "A"; + else + format_register(stream, reg); + stream << ","; + if (imm) + { + if (w) + format_immediate_word(stream, src); + else + format_immediate_byte(stream, src); + } + else + { + if (idx) + util::stream_format(stream, "0%X", u16(reg)); + + if (indir) + stream << "["; + format_register(stream, src); + if (indir) + stream << "]"; + + if (w) + stream << ".w"; + else + stream << ".b"; + } +} + +void hpc_disassembler::disassemble_unary_op(std::ostream &stream, const char *op, u16 offset, u16 src, bool indir, bool idx, bool w) const +{ + util::stream_format(stream, "%-8s", op); + if (idx) + util::stream_format(stream, "0%X", offset); + + if (indir) + stream << "["; + format_register(stream, src); + if (indir) + stream << "]"; + + if (w) + { + if (src < 0x00c4 || src >= 0x00d0 || indir) + stream << ".w"; + } + else + stream << ".b"; +} + +void hpc_disassembler::disassemble_bit_op(std::ostream &stream, const char *op, u8 bit, u16 offset, u16 src, bool indir, bool idx) const +{ + if (src >= 0x00c0 && src < 0x01c0 && BIT(src, 0) && !indir && m_regs[(src - 0x00c0) >> 1] != nullptr) + { + src &= 0xfffe; + bit += 8; + } + + util::stream_format(stream, "%-8s%d,", op, bit); + + if (idx) + util::stream_format(stream, "0%X", offset); + + if (indir) + stream << "["; + format_register(stream, src); + if (indir) + stream << "]"; +} + +offs_t hpc_disassembler::disassemble(std::ostream &stream, offs_t pc, const hpc_disassembler::data_buffer &opcodes, const hpc_disassembler::data_buffer ¶ms) +{ + u8 opcode = opcodes.r8(pc); + u16 reg = REGISTER_A; + u16 src = REGISTER_B; + bool imm = false; + bool dmode = false; + bool indir = true; + bool idx = false; + bool jmp = false; + offs_t bytes = 1; + + switch (opcode) + { + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + jmp = true; + src = 0xffd0 + (opcode & 0x0f) * 2; + bytes = 1; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + jmp = true; + src = pc + 2 + ((opcode & 0x03) << 8 | opcodes.r8(pc + 1)); + bytes = 2; + break; + + case 0x34: case 0x35: case 0x36: case 0x37: + jmp = true; + src = pc - ((opcode & 0x03) << 8 | opcodes.r8(pc + 1)); + bytes = 2; + break; + + case 0x3f: + case 0x89: case 0x8a: + case 0xa9: case 0xaa: case 0xaf: + indir = false; + src = opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + case 0x58: case 0x59: case 0x5a: case 0x5b: + case 0x5c: case 0x5d: case 0x5e: case 0x5f: + jmp = true; + src = pc + 1 + (opcode & 0x1f); + break; + + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + jmp = true; + src = pc - (opcode & 0x1f); + break; + + case 0x80: case 0x82: + case 0xa0: + dmode = true; + indir = false; + if (BIT(opcode, 1)) + imm = true; + + src = opcodes.r8(pc + 1); + reg = opcodes.r8(pc + 2); + opcode = opcodes.r8(pc + 3); + bytes = 4; + break; + + case 0x84: case 0x86: + case 0xa4: + dmode = true; + indir = false; + if (BIT(opcode, 1)) + imm = true; + + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + reg = opcodes.r8(pc + 3); + opcode = opcodes.r8(pc + 4); + bytes = 5; + break; + + case 0x81: case 0x83: + case 0xa1: + dmode = true; + indir = false; + if (BIT(opcode, 1)) + imm = true; + + src = opcodes.r8(pc + 1); + reg = (opcodes.r8(pc + 2) << 8) | opcodes.r8(pc + 3); + opcode = opcodes.r8(pc + 4); + bytes = 5; + break; + + case 0xa2: + idx = true; + reg = opcodes.r8(pc + 1); + src = opcodes.r8(pc + 2); + opcode = opcodes.r8(pc + 3); + bytes = 4; + break; + + case 0x85: case 0x87: + case 0xa5: + dmode = true; + indir = false; + if (BIT(opcode, 1)) + imm = true; + + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + reg = (opcodes.r8(pc + 3) << 8) | opcodes.r8(pc + 4); + opcode = opcodes.r8(pc + 5); + bytes = 6; + break; + + case 0xa6: + idx = true; + reg = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + src = opcodes.r8(pc + 3); + opcode = opcodes.r8(pc + 4); + bytes = 5; + break; + + case 0x88: case 0x8b: case 0x8e: + case 0xa8: case 0xab: case 0xae: + indir = false; + src = opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x8c: + case 0xac: + indir = false; + src = opcodes.r8(pc + 1); + reg = opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0x8d: + imm = true; + src = opcodes.r8(pc + 1); + reg = opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0x8f: + src = REGISTER_X; + opcode = opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x90: case 0x91: case 0x92: case 0x93: + imm = true; + reg = 0x00c8 | ((opcode & 0x03) << 1); + src = opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + imm = true; + src = opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x94: + jmp = true; + src = pc + 2 + opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x95: + jmp = true; + src = pc - opcodes.r8(pc + 1); + bytes = 2; + break; + + case 0x96: + indir = false; + src = opcodes.r8(pc + 1); + opcode = opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0x97: + indir = false; + imm = true; + src = opcodes.r8(pc + 1); + reg = opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0xa7: + imm = true; + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + reg = (opcodes.r8(pc + 3) << 8) | opcodes.r8(pc + 4); + bytes = 5; + break; + + case 0xad: + src = opcodes.r8(pc + 1); + opcode = opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + imm = true; + reg = 0x00c8 | ((opcode & 0x03) << 1); + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + imm = true; + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + bytes = 3; + break; + + case 0xb4: case 0xb5: + jmp = true; + src = pc + 3 + ((opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2)); + bytes = 3; + break; + + case 0xb6: + indir = false; + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + opcode = opcodes.r8(pc + 3); + bytes = 4; + break; + + case 0xb7: + imm = true; + src = (opcodes.r8(pc + 1) << 8) | opcodes.r8(pc + 2); + reg = opcodes.r8(pc + 3); + bytes = 4; + break; + + case 0xd4: case 0xd5: case 0xd6: + case 0xf4: case 0xf5: case 0xf6: + src = REGISTER_X; + break; + } + + switch (opcode) + { + case 0x00: + util::stream_format(stream, "%-8sA", "clr"); + break; + + case 0x01: + util::stream_format(stream, "%-8sA", "comp"); + break; + + case 0x02: + stream << "sc"; + break; + + case 0x03: + stream << "rc"; + break; + + case 0x04: + util::stream_format(stream, "%-8sA", "inc"); + break; + + case 0x05: + util::stream_format(stream, "%-8sA", "dec"); + break; + + case 0x06: + stream << "ifnc"; + break; + + case 0x07: + stream << "ifc"; + break; + + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + disassemble_bit_op(stream, "sbit", opcode & 0x07, reg, src, indir, idx); + break; + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + disassemble_bit_op(stream, "ifbit", opcode & 0x07, reg, src, indir, idx); + break; + + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + disassemble_bit_op(stream, "rbit", opcode & 0x07, reg, src, indir, idx); + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + util::stream_format(stream, "%-8s", "jsrp"); + if (jmp) + util::stream_format(stream, "[0%X]", src); + else + stream << "???"; + bytes |= STEP_OVER; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + util::stream_format(stream, "%-8s", "jsr"); + if (jmp) + util::stream_format(stream, "0%X", src); + else + stream << "???"; + bytes |= STEP_OVER; + break; + + case 0x38: + util::stream_format(stream, "%-8sX,", "rbit"); + format_register(stream, reg); + break; + + case 0x39: + util::stream_format(stream, "%-8sX,", "sbit"); + format_register(stream, reg); + break; + + case 0x3a: + util::stream_format(stream, "%-8sX,", "ifbit"); + format_register(stream, reg); + break; + + case 0x3b: + util::stream_format(stream, "%-8sA", "swap"); + break; + + case 0x3c: + stream << "ret"; + bytes |= STEP_OUT; + break; + + case 0x3d: + stream << "retsk"; + bytes |= STEP_OUT; + break; + + case 0x3e: + stream << "reti"; + bytes |= STEP_OUT; + break; + + case 0x3f: + util::stream_format(stream, "%-8s", "pop"); + format_register(stream, src); + break; + + case 0x40: + stream << "nop"; + break; + + case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + case 0x58: case 0x59: case 0x5a: case 0x5b: + case 0x5c: case 0x5d: case 0x5e: case 0x5f: + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + util::stream_format(stream, "%-8s", "jp"); + if (jmp) + util::stream_format(stream, "0%X", src); + else + stream << "???"; + break; + + case 0x88: case 0x8c: + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x97: + case 0xa8: case 0xac: + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb7: + case 0xc4: + case 0xd4: + case 0xe4: + case 0xf4: + disassemble_op(stream, "ld", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x89: + case 0xa9: + disassemble_unary_op(stream, "inc", reg, src, indir, idx, BIT(opcode, 5)); + break; + + case 0x8a: + case 0xaa: + disassemble_unary_op(stream, "decsz", reg, src, indir, idx, BIT(opcode, 5)); + bytes |= STEP_OVER | (1 << OVERINSTSHIFT); + break; + + case 0x8b: + case 0xab: + case 0xc6: + case 0xd6: + case 0xe6: + case 0xf6: + disassemble_op(stream, dmode ? "ld" : "st", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x8d: + util::stream_format(stream, "%-8sBK,", "ld"); + format_immediate_byte(stream, src); + stream << ","; + format_immediate_byte(stream, reg); + break; + + case 0x8e: + case 0xae: + case 0xc5: + case 0xd5: + case 0xe5: + case 0xf5: + disassemble_op(stream, "x", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x94: case 0x95: + util::stream_format(stream, "%-8s", "jp"); + if (jmp) + util::stream_format(stream, "0%X", src); + else + stream << "???"; + break; + + case 0x98: case 0xb8: case 0xd8: case 0xf8: + disassemble_op(stream, "add", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x99: case 0xb9: case 0xd9: case 0xf9: + disassemble_op(stream, "and", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9a: case 0xba: case 0xda: case 0xfa: + disassemble_op(stream, "or", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9b: case 0xbb: case 0xdb: case 0xfb: + disassemble_op(stream, "xor", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9c: case 0xbc: case 0xdc: case 0xfc: + disassemble_op(stream, "ifeq", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9d: case 0xbd: case 0xdd: case 0xfd: + disassemble_op(stream, "ifgt", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9e: case 0xbe: case 0xde: case 0xfe: + disassemble_op(stream, "mult", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0x9f: case 0xbf: case 0xdf: case 0xff: + disassemble_op(stream, "div", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0xa7: + util::stream_format(stream, "%-8sBK,", "ld"); + format_immediate_word(stream, src); + stream << ","; + format_immediate_word(stream, reg); + break; + + case 0xaf: + util::stream_format(stream, "%-8s", "push"); + format_register(stream, src); + break; + + case 0xb4: + util::stream_format(stream, "%-8s", "jmpl"); + if (jmp) + util::stream_format(stream, "0%X", src); + else + stream << "???"; + break; + + case 0xb5: + util::stream_format(stream, "%-8s", "jsrl"); + if (jmp) + util::stream_format(stream, "0%X", src); + else + stream << "???"; + bytes |= STEP_OVER; + break; + + case 0xc0: case 0xc2: + case 0xe0: case 0xe2: + util::stream_format(stream, "%-8sA,[B%c].%c", "lds", + BIT(opcode, 1) ? '-' : '+', + BIT(opcode, 5) ? 'w' : 'b'); + bytes |= STEP_OVER | (1 << OVERINSTSHIFT); + break; + + case 0xd0: case 0xd2: + case 0xf0: case 0xf2: + util::stream_format(stream, "%-8sA,[X%c].%c", "ld", + BIT(opcode, 1) ? '-' : '+', + BIT(opcode, 5) ? 'w' : 'b'); + break; + + case 0xc1: case 0xc3: + case 0xe1: case 0xe3: + util::stream_format(stream, "%-8sA,[B%c].%c", "xs", + BIT(opcode, 1) ? '-' : '+', + BIT(opcode, 5) ? 'w' : 'b'); + bytes |= STEP_OVER | (1 << OVERINSTSHIFT); + break; + + case 0xd1: case 0xd3: + case 0xf1: case 0xf3: + util::stream_format(stream, "%-8sA,[X%c].%c", "x", + BIT(opcode, 1) ? '-' : '+', + BIT(opcode, 5) ? 'w' : 'b'); + break; + + case 0xc7: case 0xe7: + util::stream_format(stream, "%-8sA", BIT(opcode, 5) ? "shl" : "shr"); + break; + + case 0xd7: case 0xf7: + util::stream_format(stream, "%-8sA", BIT(opcode, 5) ? "rlc" : "rrc"); + break; + + case 0xc8: case 0xe8: + disassemble_op(stream, "adc", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0xc9: case 0xe9: + disassemble_op(stream, "dadc", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0xca: case 0xea: + disassemble_op(stream, "dsubc", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0xcb: case 0xeb: + disassemble_op(stream, "subc", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + case 0xcc: case 0xec: + stream << "jid"; + if (BIT(opcode, 5)) + stream << "w"; + break; + + case 0xcf: case 0xef: + disassemble_op(stream, "divd", reg, src, imm, indir, idx, BIT(opcode, 5)); + break; + + default: + stream << "???"; + break; + } + + return bytes | SUPPORTED; +} diff --git a/src/devices/cpu/hpc/hpcdasm.h b/src/devices/cpu/hpc/hpcdasm.h new file mode 100644 index 00000000000..85aa4f550f1 --- /dev/null +++ b/src/devices/cpu/hpc/hpcdasm.h @@ -0,0 +1,49 @@ +// license:BSD-3-Clause +// copyright-holders:AJR + +#ifndef MAME_CPU_HPC_HPCDASM_H +#define MAME_CPU_HPC_HPCDASM_H + +#pragma once + +class hpc_disassembler : public util::disasm_interface +{ +protected: + enum : u16 + { + REGISTER_A = 0x00c8, + REGISTER_K = 0x00ca, + REGISTER_B = 0x00cc, + REGISTER_X = 0x00ce + }; + + // construction/destruction + hpc_disassembler(const char *const regs[]); + + // disassembler overrides + virtual u32 opcode_alignment() const override; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + // internal helpers + void format_register(std::ostream &stream, u16 reg) const; + void format_immediate_byte(std::ostream &stream, u8 data) const; + void format_immediate_word(std::ostream &stream, u16 data) const; + void disassemble_op(std::ostream &stream, const char *op, u16 reg, u16 src, bool imm, bool indir, bool idx, bool w) const; + void disassemble_unary_op(std::ostream &stream, const char *op, u16 offset, u16 src, bool indir, bool idx, bool w) const; + void disassemble_bit_op(std::ostream &stream, const char *op, u8 bit, u16 offset, u16 src, bool indir, bool idx) const; + + // register names + const char *const *m_regs; +}; + +class hpc16164_disassembler : public hpc_disassembler +{ +public: + hpc16164_disassembler() : hpc_disassembler(s_regs) { } + +private: + static const char *const s_regs[128]; +}; + +#endif // MAME_CPU_HPC_HPCDASM_H diff --git a/src/mame/drivers/age_candy.cpp b/src/mame/drivers/age_candy.cpp index 331a421c7f7..5731611c7cd 100644 --- a/src/mame/drivers/age_candy.cpp +++ b/src/mame/drivers/age_candy.cpp @@ -13,6 +13,7 @@ Device is a 27c256 location U3 */ #include "emu.h" +#include "cpu/hpc/hpc.h" #include "speaker.h" @@ -21,7 +22,7 @@ class age_candy_state : public driver_device public: age_candy_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) - // ,m_maincpu(*this, "maincpu") + , m_maincpu(*this, "maincpu") { } void age_candy(machine_config &config); @@ -31,7 +32,7 @@ private: virtual void machine_reset() override; void age_candy_map(address_map &map); -// required_device m_maincpu; + required_device m_maincpu; }; static INPUT_PORTS_START( age_candy ) @@ -48,24 +49,19 @@ void age_candy_state::machine_reset() } -#ifdef UNUSED_DEFINITION void age_candy_state::age_candy_map(address_map &map) { - map(0xc000, 0xffff).rom().region("maincpu", 0x4000); + map(0x8000, 0xffff).rom().region("maincpu", 0); } -#endif -MACHINE_CONFIG_START(age_candy_state::age_candy) - - /* basic machine hardware */ -// MCFG_DEVICE_ADD("maincpu", HPC46104, 8000000) // unknown clock; HPC emulation needed -// MCFG_DEVICE_PROGRAM_MAP(age_candy_map) -// MCFG_DEVICE_IO_MAP(age_candy_io) +void age_candy_state::age_candy(machine_config &config) +{ + HPC46104(config, m_maincpu, 8000000); // unknown clock; HPC still not actually emulated + m_maincpu->set_addrmap(AS_PROGRAM, &age_candy_state::age_candy_map); /* sound hardware */ SPEAKER(config, "mono").front_center(); -MACHINE_CONFIG_END - +} ROM_START( age_cand ) diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 432a762c028..e8bb305bfa1 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -55,6 +55,7 @@ using util::BIT; #include "cpu/hcd62121/hcd62121d.h" #include "cpu/hd61700/hd61700d.h" #include "cpu/hmcs40/hmcs40d.h" +#include "cpu/hpc/hpcdasm.h" #include "cpu/hphybrid/hphybrid_dasm.h" #include "cpu/i386/i386dasm.h" #include "cpu/i8008/8008dasm.h" @@ -360,6 +361,7 @@ static const dasm_table_entry dasm_table[] = { "hp_5061_3001", be, -1, []() -> util::disasm_interface * { return new hp_5061_3001_disassembler; } }, { "hp_5061_3011", be, -1, []() -> util::disasm_interface * { return new hp_5061_3011_disassembler; } }, { "hp_09825_67907", be, -1, []() -> util::disasm_interface * { return new hp_09825_67907_disassembler; } }, + { "hpc16164", le, 0, []() -> util::disasm_interface * { return new hpc16164_disassembler; } }, { "hyperstone", be, 0, []() -> util::disasm_interface * { return new hyperstone_disassembler(&hyperstone_unidasm); } }, { "i4004", le, 0, []() -> util::disasm_interface * { return new i4004_disassembler; } }, { "i4040", le, 0, []() -> util::disasm_interface * { return new i4040_disassembler; } }, -- cgit v1.2.3-70-g09d2 From a81cb8469e8f3548205dc0add226365fdddce59c Mon Sep 17 00:00:00 2001 From: yz70s Date: Thu, 14 Mar 2019 20:29:23 +0100 Subject: i386: add a simple cache to the athlon xp processor (nw) . A single 64K 2-way set associative cache Used for both instructions and data Enabled only for addresses in the first megabyte Works always in writeback mode . It is needed by the nforce motherboard bios that uses it to simulate a block of ram at address d0000 before ddr ram is configured --- scripts/src/cpu.lua | 1 + src/devices/cpu/i386/cache.h | 258 +++++++++++++++++++++++++++++++++++++ src/devices/cpu/i386/cpuidmsrs.hxx | 12 ++ src/devices/cpu/i386/i386.cpp | 225 ++++++++++++++++++++++++++++++++ src/devices/cpu/i386/i386.h | 25 ++++ 5 files changed, 521 insertions(+) create mode 100644 src/devices/cpu/i386/cache.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 631af0dbcff..090093df856 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -1087,6 +1087,7 @@ if (CPUS["I386"]~=null) then files { MAME_DIR .. "src/devices/cpu/i386/i386.cpp", MAME_DIR .. "src/devices/cpu/i386/i386.h", + MAME_DIR .. "src/devices/cpu/i386/cache.h", MAME_DIR .. "src/devices/cpu/i386/cycles.h", MAME_DIR .. "src/devices/cpu/i386/i386op16.hxx", MAME_DIR .. "src/devices/cpu/i386/i386op32.hxx", diff --git a/src/devices/cpu/i386/cache.h b/src/devices/cpu/i386/cache.h new file mode 100644 index 00000000000..548a1fb9f85 --- /dev/null +++ b/src/devices/cpu/i386/cache.h @@ -0,0 +1,258 @@ +// license:BSD-3-Clause +// copyright-holders:Samuele Zannoli + +#ifndef MAME_CPU_I386_CACHE_H +#define MAME_CPU_I386_CACHE_H + +#pragma once + +/* To test it outside of Mame +#include + +typedef unsigned char u8; +typedef unsigned int u32; +*/ + +enum { + CacheLineBytes16 = 4, + CacheLineBytes32 = 5, + CacheLineBytes64 = 6, + CacheLineBytes128 = 7, + CacheLineBytes256 = 8, +}; + +enum { + CacheDirectMapped = 0, + Cache2Way = 1, + Cache4Way = 2, + Cache8Way = 3, + Cache16Way = 4 +}; + +enum { + CacheRead = 0, + CacheWrite = 1 +}; + +template +class cpucache { +public: + // Constructor + cpucache(); + // Reset the cache + void reset(); + // Find the cacheline containing data at address + template u8* search(u32 address); + // Allocate a cacheline for data at address + template bool allocate(u32 address, u8 **data); + // Get the address where the cacheline data should be written back to + u32 old(); + // Get the address of the first byte of the cacheline that contains data at address + u32 base(u32 address); + // Compose the cacheline parameters into an address + u32 address(u32 tag, u32 set, u32 offset); + // Get the data of the first cacheline marked as dirty + u8* first_dirty(u32 &base, bool clean); + // Get the data of the next cacheline marked as dirty + u8* next_dirty(u32 &base, bool clean); + +private: + static const int Ways = 1 << WayBits; + static const int LineBytes = 1 << LineBits; + static const int Sets = 1 << SetBits; + static const u32 LineMask = (1 << LineBits) - 1; + static const u32 SetMask = ((1 << SetBits) - 1) << LineBits; + static const u32 WayMask = (1 << WayBits) - 1; + static const int TagShift = LineBits + SetBits; + + struct cacheline { + u8 data[LineBytes]; + bool allocated; + bool dirty; + u32 tag; + u32 debug_address; + }; + + struct cacheset { + cacheline lines[Ways]; + int nextway; + }; + + cacheset sets[Sets]; + u32 writeback_base; + int last_set; + int last_way; +}; + +template +cpucache::cpucache() +{ + reset(); +} + +template +void cpucache::reset() +{ + for (int s = 0; s < Sets; s++) + for (int w = 0; w < Ways; w++) + { + sets[s].nextway = 0; + sets[s].lines[w].allocated = false; + sets[s].lines[w].dirty = false; + sets[s].lines[w].debug_address = 0; + } + last_set = -1; + last_way = -1; +} + +template +template +u8* cpucache::search(u32 address) +{ + const int addresset = (address & SetMask) >> LineBits; + const int addrestag = address >> TagShift; + + for (int w = 0; w < Ways; w++) + if ((sets[addresset].lines[w].allocated) && (sets[addresset].lines[w].tag == addrestag)) + { + if (ReadWrite != 0) + sets[addresset].lines[w].dirty = true; + return sets[addresset].lines[w].data; + } + return nullptr; +} + +template +template +bool cpucache::allocate(u32 address, u8 **data) +{ + const int addresset = (address & SetMask) >> LineBits; + const int addrestag = address >> TagShift; + const int victimway = sets[addresset].nextway; + bool old_allocated, old_dirty; + bool ret; + + sets[addresset].nextway = (victimway + 1) & WayMask; // decide wich way will be allocated next + old_allocated = sets[addresset].lines[victimway].allocated; + old_dirty = sets[addresset].lines[victimway].dirty; + writeback_base = (sets[addresset].lines[victimway].tag << TagShift) | (address & SetMask); + sets[addresset].lines[victimway].tag = addrestag; + sets[addresset].lines[victimway].allocated = true; + if (ReadWrite == 0) + sets[addresset].lines[victimway].dirty = false; // caller must write back the cacheline if told so + else + sets[addresset].lines[victimway].dirty = true; // line is allocated to write into it + *data = sets[addresset].lines[victimway].data; + sets[addresset].lines[victimway].debug_address = address; + ret = old_allocated; // ret = old_allocated && old_dirty + if (!old_dirty) + ret = false; + return ret; // true if caller must write back the cacheline +} + +template +u32 cpucache::old() +{ + return writeback_base; +} + +template +u32 cpucache::base(u32 address) +{ + return address & ~LineMask; +} + +template +u32 cpucache::address(u32 tag, u32 set, u32 offset) +{ + return (tag << TagShift) | (set << LineBits) | offset; +} + +template +u8* cpucache::first_dirty(u32 &base, bool clean) +{ + for (int s = 0; s < Sets; s++) + for (int w = 0; w < Ways; w++) + if (sets[s].lines[w].dirty == true) + { + if (clean) + sets[s].lines[w].dirty = false; + last_set = s; + last_way = w; + base = address(sets[s].lines[w].tag, s, 0); + return sets[s].lines[w].data; + } + return nullptr; +} + +template +u8* cpucache::next_dirty(u32 &base, bool clean) +{ + if (last_set < 0) + return nullptr; + while (true) + { + last_way++; + if (last_way == Ways) + { + last_way = 0; + last_set++; + if (last_set == Sets) + { + last_set = -1; + last_way = -1; + return nullptr; + } + } + if (sets[last_set].lines[last_way].dirty == true) + { + if (clean) + sets[last_set].lines[last_way].dirty = false; + base = address(sets[last_set].lines[last_way].tag, last_set, 0); + return sets[last_set].lines[last_way].data; + } + } +} + +#endif + +/* To test it outside of Mame +const int memorysize = 256 * 1024; +u8 memory[memorysize]; + +void readline(u8 *data, u32 address) +{ + for (int n = 0; n < 64; n++) + data[n] = memory[address + n]; +} + +void writeline(u8 *data, u32 address) +{ + for (int n = 0; n < 64; n++) + memory[address + n] = data[n]; +} + +void cache_tester() +{ + cpucache<18, 8, 6, 2> cache; + bool r; + u8 *data; + int address; + u8 value; + + for (int n = 0; n < memorysize; n++) + memory[n] = 0xaa ^ n; + address = std::rand() & (memorysize - 1); + r = cache.search(address, &data); + if (r == false) + { + r = cache.allocate(address, &data); + if (r == true) + writeline(data, cache.base(address)); + readline(data, cache.base(address)); + } + value = data[address & 63]; + if (value != memory[address]) + printf("Error reading address %d\n\r", address); +} +*/ diff --git a/src/devices/cpu/i386/cpuidmsrs.hxx b/src/devices/cpu/i386/cpuidmsrs.hxx index 827050174e3..842ff85b4b2 100644 --- a/src/devices/cpu/i386/cpuidmsrs.hxx +++ b/src/devices/cpu/i386/cpuidmsrs.hxx @@ -314,12 +314,15 @@ uint64_t athlonxp_device::opcode_rdmsr(bool &valid_msr) break; case 0x250: // MTRRfix64K_00000 // 8 bits for each 64k block starting at address 0 + ret = m_msr_mtrrfix[0]; break; case 0x258: // MTRRfix16K_80000 // 8 bits for each 16k block starting at address 0x80000 + ret = m_msr_mtrrfix[1]; break; case 0x259: // MTRRfix16K_A0000 // 8 bits for each 16k block starting at address 0xa0000 + ret = m_msr_mtrrfix[2]; break; case 0x268: // MTRRfix4K_C0000 case 0x269: // MTRRfix4K_C8000 @@ -330,6 +333,7 @@ uint64_t athlonxp_device::opcode_rdmsr(bool &valid_msr) case 0x26e: // MTRRfix4K_F0000 case 0x26f: // MTRRfix4K_F8000 // 8 bits for each 4k block + ret = m_msr_mtrrfix[3 + offset - 0x268]; break; case 0x400: // MC0_CTL break; @@ -399,10 +403,16 @@ void athlonxp_device::opcode_wrmsr(uint64_t data, bool &valid_msr) case 0x2ff: // MTRRdefType break; case 0x250: // MTRRfix64K_00000 + m_msr_mtrrfix[0] = data; + parse_mtrrfix(data, 0, 64); break; case 0x258: // MTRRfix16K_80000 + m_msr_mtrrfix[1] = data; + parse_mtrrfix(data, 0x80000, 16); break; case 0x259: // MTRRfix16K_A0000 + m_msr_mtrrfix[2] = data; + parse_mtrrfix(data, 0xa0000, 16); break; case 0x268: // MTRRfix4K_C0000-F8000 case 0x269: @@ -412,6 +422,8 @@ void athlonxp_device::opcode_wrmsr(uint64_t data, bool &valid_msr) case 0x26d: case 0x26e: case 0x26f: + m_msr_mtrrfix[3 + offset - 0x268] = data; + parse_mtrrfix(data, 0xc0000 + (offset - 0x268) * 0x8000, 4); break; case 0x400: // MC0_CTL break; diff --git a/src/devices/cpu/i386/i386.cpp b/src/devices/cpu/i386/i386.cpp index fdc0561a1ab..f372dba1239 100644 --- a/src/devices/cpu/i386/i386.cpp +++ b/src/devices/cpu/i386/i386.cpp @@ -4721,6 +4721,10 @@ void athlonxp_device::device_reset() m_cpuid_id2 = ('D' << 24) | ('M' << 16) | ('A' << 8) | 'c'; // cAMD memset(m_processor_name_string, 0, 48); strcpy((char *)m_processor_name_string, "AMD Athlon(tm) Processor"); + for (int n = 0; n < 11; n++) + m_msr_mtrrfix[n] = 0; + for (int n = 0; n < (1024 / 4); n++) + m_memory_ranges_1m[n] = 0; // change the 0 to 6 to test the cache just after reset m_cpuid_max_input_value_eax = 0x01; m_cpu_version = REG32(EDX); @@ -4731,6 +4735,227 @@ void athlonxp_device::device_reset() CHANGE_PC(m_eip); } +void athlonxp_device::parse_mtrrfix(u64 mtrr, offs_t base, int kblock) +{ + int nb = kblock / 4; + int range = (int)(base >> 12); // base must never be higher than 1 megabyte + + for (int n = 0; n < 8; n++) + { + uint8_t type = mtrr & 0xff; + + for (int b = 0; b < nb; b++) + { + m_memory_ranges_1m[range] = type; + range++; + } + mtrr = mtrr >> 8; + } +} + +int athlonxp_device::check_cacheable(offs_t address) +{ + offs_t block; + int disabled; + + disabled = 0; + if (m_cr[0] & (1 << 30)) + disabled = 128; + if (address >= 0x100000) + return disabled; + block = address >> 12; + return m_memory_ranges_1m[block] | disabled; +} + +template +dt athlonxp_device::opcode_read_cache(offs_t address) +{ + int mode = check_cacheable(address); + bool nocache = false; + u8 *data; + + if ((mode & 7) == 0) + nocache = true; + if (mode & 1) + nocache = true; + if (nocache == false) + { + int offset = (address & 63) ^ xorle; + data = cache.search(address); + if (data) + return *(dt *)(data + offset); + if (!(mode & 128)) + { + bool dirty = cache.allocate(address, &data); + address = cache.base(address); + if (dirty) + { + offs_t old_address = cache.old(); + + for (int w = 0; w < 64; w += 4) + macache32->write_dword(old_address + w, *(u32 *)(data + w)); + } + for (int r = 0; r < 64; r += 4) + *(u32 *)(data + r) = macache32->read_dword(address + r); + return *(dt *)(data + offset); + } + else + { + if (sizeof(dt) == 1) + return macache32->read_byte(address); + else if (sizeof(dt) == 2) + return macache32->read_word(address); + else + return macache32->read_dword(address); + } + } + else + { + if (sizeof(dt) == 1) + return macache32->read_byte(address); + else if (sizeof(dt) == 2) + return macache32->read_word(address); + else + return macache32->read_dword(address); + } +} + +template +dt athlonxp_device::program_read_cache(offs_t address) +{ + int mode = check_cacheable(address); + bool nocache = false; + u8 *data; + + if ((mode & 7) == 0) + nocache = true; + if (mode & 1) + nocache = true; + if (nocache == false) + { + int offset = (address & 63) ^ xorle; + data = cache.search(address); + if (data) + return *(dt *)(data + offset); + if (!(mode & 128)) + { + bool dirty = cache.allocate(address, &data); + address = cache.base(address); + if (dirty) + { + offs_t old_address = cache.old(); + + for (int w = 0; w < 64; w += 4) + m_program->write_dword(old_address + w, *(u32 *)(data + w)); + } + for (int r = 0; r < 64; r += 4) + *(u32 *)(data + r) = m_program->read_dword(address + r); + return *(dt *)(data + offset); + } + else + { + if (sizeof(dt) == 1) + return m_program->read_byte(address); + else if (sizeof(dt) == 2) + return m_program->read_word(address); + else + return m_program->read_dword(address); + } + } + else + { + if (sizeof(dt) == 1) + return m_program->read_byte(address); + else if (sizeof(dt) == 2) + return m_program->read_word(address); + else + return m_program->read_dword(address); + } +} + +template +void athlonxp_device::program_write_cache(offs_t address, dt data) +{ + int mode = check_cacheable(address); + bool nocache = false; + u8 *dataw; + + if ((mode & 7) == 0) + nocache = true; + if (mode & 1) + nocache = true; + if (nocache == false) + { + int offset = (address & 63) ^ xorle; + dataw = cache.search(address); + if (dataw) + { + *(dt *)(dataw + offset) = data; + return; + } + if (!(mode & 128)) + { + bool dirty = cache.allocate(address, &dataw); + address = cache.base(address); + if (dirty) + { + offs_t old_address = cache.old(); + + for (int w = 0; w < 64; w += 4) + m_program->write_dword(old_address + w, *(u32 *)(dataw + w)); + } + for (int r = 0; r < 64; r += 4) + *(u32 *)(dataw + r) = m_program->read_dword(address + r); + *(dt *)(dataw + offset) = data; + } + else + { + if (sizeof(dt) == 1) + m_program->write_byte(address, data); + else if (sizeof(dt) == 2) + m_program->write_word(address, data); + else + m_program->write_dword(address, data); + } + } + else + { + if (sizeof(dt) == 1) + m_program->write_byte(address, data); + else if (sizeof(dt) == 2) + m_program->write_word(address, data); + else + m_program->write_dword(address, data); + } +} + +void athlonxp_device::invalidate_cache(bool writeback) +{ + u32 base; + u8 *data; + + data = cache.first_dirty(base, true); + while (data != nullptr) + { + if (writeback) + for (int w = 0; w < 64; w += 4) + m_program->write_dword(base + w, *(u32 *)(data + w)); + data = cache.next_dirty(base, true); + } + cache.reset(); +} + +void athlonxp_device::opcode_invd() +{ + invalidate_cache(false); +} + +void athlonxp_device::opcode_wbinvd() +{ + invalidate_cache(true); +} + + /*****************************************************************************/ /* Intel Pentium 4 */ diff --git a/src/devices/cpu/i386/i386.h b/src/devices/cpu/i386/i386.h index 52e3c4fec8f..d8422780710 100644 --- a/src/devices/cpu/i386/i386.h +++ b/src/devices/cpu/i386/i386.h @@ -16,6 +16,7 @@ #include "divtlb.h" #include "i386dasm.h" +#include "cache.h" #define INPUT_LINE_A20 1 #define INPUT_LINE_SMI 2 @@ -1631,10 +1632,34 @@ protected: virtual void opcode_cpuid() override; virtual uint64_t opcode_rdmsr(bool &valid_msr) override; virtual void opcode_wrmsr(uint64_t data, bool &valid_msr) override; + virtual void opcode_invd() override; + virtual void opcode_wbinvd() override; virtual void device_start() override; virtual void device_reset() override; + virtual u8 mem_pr8(offs_t address) override { return opcode_read_cache(address); } + virtual u16 mem_pr16(offs_t address) override { return opcode_read_cache(address); } + virtual u32 mem_pr32(offs_t address) override { return opcode_read_cache(address); } + virtual u8 mem_prd8(offs_t address) override { return program_read_cache(address); } + virtual u16 mem_prd16(offs_t address) override { return program_read_cache(address); } + virtual u32 mem_prd32(offs_t address) override { return program_read_cache(address); } + virtual void mem_pwd8(offs_t address, u8 data) override { program_write_cache(address, data); } + virtual void mem_pwd16(offs_t address, u16 data) override { program_write_cache(address, data); } + virtual void mem_pwd32(offs_t address, u32 data) override { program_write_cache(address, data); } + +private: + void parse_mtrrfix(u64 mtrr, offs_t base, int kblock); + int check_cacheable(offs_t address); + void invalidate_cache(bool writeback); + + template dt opcode_read_cache(offs_t address); + template dt program_read_cache(offs_t address); + template void program_write_cache(offs_t address, dt data); + uint8_t m_processor_name_string[48]; + uint64_t m_msr_mtrrfix[11]; + uint8_t m_memory_ranges_1m[1024 / 4]; + cpucache<17, 9, Cache2Way, CacheLineBytes64> cache; // 512 sets, 2 ways (cachelines per set), 64 bytes per cacheline }; -- cgit v1.2.3-70-g09d2 From b445d101357be7d62f3dee88d29e66bfc59a5bc3 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Sat, 23 Mar 2019 12:07:46 +0100 Subject: ymmu100: Start adding the MEG [O. Galibert] --- scripts/src/cpu.lua | 17 ++ scripts/target/mame/mess.lua | 1 + src/devices/sound/meg.cpp | 384 +++++++++++++++++++++++++++++++++++++++++++ src/devices/sound/meg.h | 122 ++++++++++++++ src/devices/sound/megd.cpp | 118 +++++++++++++ src/devices/sound/megd.h | 39 +++++ src/devices/sound/swp30.cpp | 60 +++---- src/devices/sound/swp30.h | 9 +- src/mame/drivers/ymmu100.cpp | 9 + 9 files changed, 720 insertions(+), 39 deletions(-) create mode 100644 src/devices/sound/meg.cpp create mode 100644 src/devices/sound/meg.h create mode 100644 src/devices/sound/megd.cpp create mode 100644 src/devices/sound/megd.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 090093df856..e8084e2a362 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -2818,3 +2818,20 @@ if (CPUS["HPC"]~=null or _OPTIONS["with-tools"]) then table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/hpc/hpcdasm.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/hpc/hpcdasm.h") end + +-------------------------------------------------- +-- Yamaha Multiple Effects Generator +--@src/devices/sound/meg.h,CPUS["MEG"] = true +-------------------------------------------------- + +if (CPUS["MEG"]~=null) then + files { + MAME_DIR .. "src/devices/sound/meg.cpp", + MAME_DIR .. "src/devices/sound/meg.h", + } +end + +if (CPUS["MEG"]~=null or _OPTIONS["with-tools"]) then + table.insert(disasm_files , MAME_DIR .. "src/devices/sound/megd.cpp") + table.insert(disasm_files , MAME_DIR .. "src/devices/sound/megd.h") +end diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 032e5d6f474..bfe03e65404 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -139,6 +139,7 @@ CPUS["CAPRICORN"] = true CPUS["ALPHA"] = true --CPUS["DSPP"] = true CPUS["HPC"] = true +CPUS["MEG"] = true -------------------------------------------------- -- specify available sound cores; some of these are diff --git a/src/devices/sound/meg.cpp b/src/devices/sound/meg.cpp new file mode 100644 index 00000000000..a1f9d67b293 --- /dev/null +++ b/src/devices/sound/meg.cpp @@ -0,0 +1,384 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation + +#include "emu.h" +#include "debugger.h" +#include "meg.h" + +DEFINE_DEVICE_TYPE(MEG, meg_device, "meg", "Multiple Effects Generator (HD62098 / XM309A00)") +DEFINE_DEVICE_TYPE(MEGEMB, meg_embedded_device, "megemb", "Multiple Effects Generator (embedded)") + +void meg_base_device::prg_map(address_map &map) +{ + map(0, m_prg_size - 1).ram(); +} + +void meg_base_device::fp_map(address_map &map) +{ + map(0, m_prg_size - 1).ram(); +} + +void meg_base_device::offsets_map(address_map &map) +{ + map(0, 0x7f).ram(); +} + +meg_base_device::meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size) : + cpu_device(mconfig, type, tag, owner, clock), + m_program_config("program", ENDIANNESS_BIG, 64, prg_size > 256 ? 9 : 8, -3, address_map_constructor(FUNC(meg_base_device::prg_map), this)), + m_fp_config("fp", ENDIANNESS_BIG, 16, prg_size > 256 ? 9 : 8, -1, address_map_constructor(FUNC(meg_base_device::fp_map), this)), + m_offsets_config("offsets", ENDIANNESS_BIG, 16, prg_size > 256 ? 7 : 7, -1, address_map_constructor(FUNC(meg_base_device::offsets_map), this)), + m_prg_size(prg_size) +{ +} + + +void meg_base_device::prg_w(u16 address, u64 opcode) +{ + m_program->write_qword(address, opcode); +} + +void meg_base_device::fp_w(u16 address, u16 value) +{ + m_fp->write_word(address, value); +} + +void meg_base_device::offset_w(u16 address, u16 value) +{ + m_offsets->write_word(address, value); +} + +void meg_base_device::lfo_w(u8 reg, u16 value) +{ + m_lfo[reg] = value; + + static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 }; + static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 }; + + int scale = (value >> 5) & 7; + int step = ((value & 31) << sh[scale]) + dt[scale]; + logerror("lfo_w %02x freq=%5.2f phase=%6.4f\n", reg, step * 44100.0/4194304, (value >> 8)/256.0); +} + +void meg_base_device::map_w(u8 reg, u16 value) +{ + m_map[reg] = value; +} + +u64 meg_base_device::prg_r(u16 address) const +{ + return m_program->read_qword(address); +} + +u16 meg_base_device::fp_r(u16 address) const +{ + return m_fp->read_word(address); +} + +u16 meg_base_device::offset_r(u16 address) const +{ + return m_offsets->read_word(address); +} + +u16 meg_base_device::lfo_r(u8 reg) const +{ + return m_lfo[reg]; +} + +u16 meg_base_device::map_r(u8 reg) const +{ + return m_map[reg]; +} + + +void meg_base_device::device_start() +{ + m_program = &space(AS_PROGRAM); + m_fp = &space(AS_FP); + m_offsets = &space(AS_OFFSETS); + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + state_add(0, "PC", m_pc); + + set_icountptr(m_icount); + + save_item(NAME(m_lfo)); + save_item(NAME(m_map)); + save_item(NAME(m_pc)); +} + +void meg_base_device::device_reset() +{ + memset(m_lfo, 0, sizeof(m_lfo)); + memset(m_map, 0, sizeof(m_map)); + m_pc = 0; +} + +uint32_t meg_base_device::execute_min_cycles() const +{ + return 1; +} + +uint32_t meg_base_device::execute_max_cycles() const +{ + return 1; +} + +uint32_t meg_base_device::execute_input_lines() const +{ + return 0; +} + +void meg_base_device::execute_run() +{ + if(machine().debug_flags & DEBUG_FLAG_ENABLED) + debugger_instruction_hook(m_pc); + m_icount = 0; +} + +device_memory_interface::space_config_vector meg_base_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_PROGRAM, &m_program_config), + std::make_pair(AS_FP, &m_fp_config), + std::make_pair(AS_OFFSETS, &m_offsets_config) + }; +} + +void meg_base_device::state_import(const device_state_entry &entry) +{ +} + +void meg_base_device::state_export(const device_state_entry &entry) +{ +} + +void meg_base_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ +} + +std::unique_ptr meg_base_device::create_disassembler() +{ + return std::make_unique(this); +} + +meg_embedded_device::meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + meg_base_device(mconfig, MEGEMB, tag, owner, clock, 384) +{ +} + +meg_device::meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + meg_base_device(mconfig, MEG, tag, owner, clock, 256) +{ +} + + +// vl70: +// 6d1e: write 1, r0l +// 6d26: write 2, r0l +// 6d2e: read 2 +// 6d36: write 3, r0l +// 6d3e: write reg 4:r0h, r0l +// 6d52: write reg 5:r0h, r0l-1 +// 6d68: write 7, r0l +// 6d70: write reg 8:r0h, r0l +// 6d84: write reg 9:r0h, r0l +// 6dac: write a, r0l +// 6db4: write reg cd:r1l, r0 +// 6dd4: write reg e:r0h, r0l +// 6dee: write reg f:r0h, r0l +// 6e08: read 10,11 +// 6e1c: write reg 1213:r1l, r0 +// 6e3c: write reg 14:r0h, r0l +// 6e50: write 15, r0l +// 6e58: write reg 16:r0h, r0l +// 6e6c: write reg 17:r0h, r0l +// 6e80: write reg 18:e0h, e0l + +void meg_device::map(address_map &map) +{ + map(0x00, 0x00).w(FUNC(meg_device::select_w)); + map(0x01, 0x01).w(FUNC(meg_device::s1_w)); + map(0x02, 0x02).rw(FUNC(meg_device::s2_r), FUNC(meg_device::s2_w)); + map(0x03, 0x03).w(FUNC(meg_device::s3_w)); + map(0x04, 0x04).w(FUNC(meg_device::s4_w)); + map(0x05, 0x05).w(FUNC(meg_device::s5_w)); + map(0x07, 0x07).w(FUNC(meg_device::s7_w)); + map(0x08, 0x08).w(FUNC(meg_device::s8_w)); + map(0x09, 0x09).w(FUNC(meg_device::s9_w)); + map(0x0a, 0x0a).w(FUNC(meg_device::sa_w)); + map(0x0c, 0x0c).w(FUNC(meg_device::fph_w)); + map(0x0d, 0x0d).w(FUNC(meg_device::fpl_w)); + map(0x0e, 0x0e).w(FUNC(meg_device::se_w)); + map(0x0f, 0x0f).w(FUNC(meg_device::sf_w)); + map(0x10, 0x10).r(FUNC(meg_device::s10_r)); + map(0x11, 0x11).r(FUNC(meg_device::s11_r)); + map(0x12, 0x12).w(FUNC(meg_device::offseth_w)); + map(0x13, 0x13).w(FUNC(meg_device::offsetl_w)); + map(0x14, 0x14).w(FUNC(meg_device::s14_w)); + map(0x15, 0x15).w(FUNC(meg_device::s15_w)); + map(0x16, 0x16).w(FUNC(meg_device::s16_w)); + map(0x17, 0x17).w(FUNC(meg_device::s17_w)); + map(0x18, 0x18).w(FUNC(meg_device::s18_w)); +} + +u8 meg_device::s2_r() +{ + logerror("read r2 %s\n", machine().describe_context()); + return 0x00; +} + +void meg_device::select_w(u8 data) +{ + m_reg = data; +} + +void meg_device::s1_w(u8 data) +{ + logerror("r1 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s2_w(u8 data) +{ + logerror("r2 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s3_w(u8 data) +{ + logerror("r3 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s4_w(u8 data) +{ + if(m_r4[m_reg] != data) { + m_r4[m_reg] = data; + logerror("r4[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s5_w(u8 data) +{ + if(m_r5[m_reg] != data) { + m_r5[m_reg] = data; + logerror("r5[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s7_w(u8 data) +{ + logerror("r7 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s8_w(u8 data) +{ + if(m_r8[m_reg] != data) { + m_r8[m_reg] = data; + logerror("r8[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + + +void meg_device::s9_w(u8 data) +{ + if(m_r9[m_reg] != data) { + m_r9[m_reg] = data; + logerror("r9[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::sa_w(u8 data) +{ + logerror("ra %02x %s\n", data, machine().describe_context()); +} + +void meg_device::fph_w(u8 data) +{ + fp_w(m_reg, (fp_r(m_reg) & 0x00ff) | (data << 8)); +} + + +void meg_device::fpl_w(u8 data) +{ + fp_w(m_reg, (fp_r(m_reg) & 0xff00) | data); +} + +void meg_device::se_w(u8 data) +{ + if(m_re[m_reg] != data) { + m_re[m_reg] = data; + logerror("re[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + + +void meg_device::sf_w(u8 data) +{ + if(m_rf[m_reg] != data) { + m_rf[m_reg] = data; + logerror("rf[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +u8 meg_device::s10_r() +{ + logerror("read r10 %s\n", machine().describe_context()); + return 0x00; +} + +u8 meg_device::s11_r() +{ + logerror("read r11 %s\n", machine().describe_context()); + return 0x00; +} + +void meg_device::offseth_w(u8 data) +{ + offset_w(m_reg, (offset_r(m_reg) & 0x00ff) | (data << 8)); +} + +void meg_device::offsetl_w(u8 data) +{ + offset_w(m_reg, (offset_r(m_reg) & 0xff00) | data); +} + +void meg_device::s14_w(u8 data) +{ + if(m_r14[m_reg] != data) { + m_r14[m_reg] = data; + logerror("r14[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s15_w(u8 data) +{ + logerror("r15 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s16_w(u8 data) +{ + if(m_r16[m_reg] != data) { + m_r16[m_reg] = data; + logerror("r16[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s17_w(u8 data) +{ + if(m_r17[m_reg] != data) { + m_r17[m_reg] = data; + logerror("r17[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s18_w(u8 data) +{ + if(m_r18[m_reg] != data) { + m_r18[m_reg] = data; + logerror("r18[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} diff --git a/src/devices/sound/meg.h b/src/devices/sound/meg.h new file mode 100644 index 00000000000..bc403e9eabf --- /dev/null +++ b/src/devices/sound/meg.h @@ -0,0 +1,122 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation + +#ifndef DEVICES_SOUND_MEG_H +#define DEVICES_SOUND_MEG_H + +#pragma once + +#include "megd.h" + + +class meg_base_device : public cpu_device, public meg_disassembler::info +{ +public: + enum { + AS_FP = 1, + AS_OFFSETS = 2 + }; + + meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size); + + void prg_w(u16 address, u64 opcode); + void fp_w(u16 address, u16 value); + void offset_w(u16 address, u16 value); + void lfo_w(u8 reg, u16 value); + void map_w(u8 reg, u16 value); + u64 prg_r(u16 address) const; + virtual u16 fp_r(u16 address) const override; + virtual u16 offset_r(u16 address) const override; + u16 lfo_r(u8 reg) const; + u16 map_r(u8 reg) const; + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual uint32_t execute_min_cycles() const override; + virtual uint32_t execute_max_cycles() const override; + virtual uint32_t execute_input_lines() const override; + virtual void execute_run() override; + virtual space_config_vector memory_space_config() const override; + virtual void state_import(const device_state_entry &entry) override; + virtual void state_export(const device_state_entry &entry) override; + virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; + virtual std::unique_ptr create_disassembler() override; + +private: + address_space_config m_program_config, m_fp_config, m_offsets_config; + address_space *m_program, *m_fp, *m_offsets; + + u32 m_prg_size, m_pc; + int m_icount; + + u16 m_lfo[0x18], m_map[8]; + + void prg_map(address_map &map); + void fp_map(address_map &map); + void offsets_map(address_map &map); +}; + +class meg_embedded_device : public meg_base_device +{ +public: + meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*384); +}; + +class meg_device : public meg_base_device +{ +public: + meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*256); + void map(address_map &map); + +private: + u8 m_r4[256]; + u8 m_r5[256]; + u8 m_r8[256]; + u8 m_r9[256]; + u8 m_re[256]; + u8 m_rf[256]; + u8 m_r12[256]; + u8 m_r13[256]; + u8 m_r14[256]; + u8 m_r16[256]; + u8 m_r17[256]; + u8 m_r18[256]; + u8 m_reg; + u8 s2_r(); + u8 s10_r(); + u8 s11_r(); + void select_w(u8 reg); + void s1_w(u8 data); + void s2_w(u8 data); + void s3_w(u8 data); + void s4_w(u8 data); + void s5_w(u8 data); + void s7_w(u8 data); + void s8_w(u8 data); + void s9_w(u8 data); + void sa_w(u8 data); + void fph_w(u8 data); + void fpl_w(u8 data); + void se_w(u8 data); + void sf_w(u8 data); + void s10_w(u8 data); + void s11_w(u8 data); + void offseth_w(u8 data); + void offsetl_w(u8 data); + void s14_w(u8 data); + void s15_w(u8 data); + void s16_w(u8 data); + void s17_w(u8 data); + void s18_w(u8 data); +}; + + +DECLARE_DEVICE_TYPE(MEG, meg_device) +DECLARE_DEVICE_TYPE(MEGEMB, meg_embedded_device) + +#endif diff --git a/src/devices/sound/megd.cpp b/src/devices/sound/megd.cpp new file mode 100644 index 00000000000..ced1880a697 --- /dev/null +++ b/src/devices/sound/megd.cpp @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation +// +// Disassembler + +#include "emu.h" +#include "megd.h" + +meg_disassembler::meg_disassembler(info *inf) : m_info(inf) +{ +} + +u32 meg_disassembler::opcode_alignment() const +{ + return 1; +} + +std::string meg_disassembler::gfp(offs_t address) const +{ + if(!m_info) + return util::string_format("fp%03x", address); + s16 fp = m_info->fp_r(address); + return util::string_format("%g", fp / 16384.0); +} + +std::string meg_disassembler::goffset(offs_t address) const +{ + return m_info ? util::string_format("%x", m_info->offset_r(address)) : util::string_format("of%02x", address); +} + +u32 meg_disassembler::b(u64 opc, u32 start, u32 count) +{ + return (opc >> start) & ((1 << count) - 1); +} + +void meg_disassembler::append(std::string &r, std::string e) +{ + if(r != "") + r += " ; "; + r += e; +} + +// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000 +// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 + +// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 +// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 +// XLB----- -rrrrrrr r--mmmmm m-MM---- -P-----* -----Arr rrrrrrmm mmmm---- + +// m = low is read port, high is write port, memory register +// r = low is read port, high is high port, rotating register + +// X = used for lo-fi variation only +// L = lfo read +// * = compute mul +// A = mul input = m or r +// P = P sent for register write +// B = register write to mbuf +// M = memory mode, none/read/write/read+1 + +offs_t meg_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + u64 opc = opcodes.r64(pc); + + std::string r; + + r = util::string_format("[m%02x]", b(opc, 39, 6)); + + if(b(opc, 62, 1)) + append(r, "lfo"); + + if(b(opc, 23, 1)) + switch(b(opc, 24, 2)) { + case 0: + if(b(opc, 18, 1)) + append(r, util::string_format("p += %s*m%02x", gfp(pc), b(opc, 4, 6))); + else + append(r, util::string_format("p += %s*r%02x", gfp(pc), b(opc, 10, 8))); + break; + case 1: + append(r, util::string_format("p = %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6))); + break; + case 2: + append(r, util::string_format("p ?= %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6))); + break; + case 3: + if(b(opc, 18, 1)) + append(r, util::string_format("p = %s*m%02x", gfp(pc), b(opc, 4, 6))); + else + append(r, util::string_format("p = %s*r%02x", gfp(pc), b(opc, 10, 8))); + break; + } + + if(b(opc, 30, 1)) { + if(b(opc, 61, 1)) + append(r, "mb = p"); + else if(b(opc, 46, 1) == 1) + append(r, util::string_format("m%02x = p", b(opc, 39, 6))); + else + append(r, util::string_format("r%02x = p", b(opc, 47, 8))); + } + + u32 memmode = b(opc, 36, 2); + if(memmode) { + static const char *modes[4] = { nullptr, "w", "r", "rw" }; + + append(r, util::string_format("mem_%s %x +%s", modes[memmode], b(opc, 33, 3), goffset(pc/3))); + r += util::string_format("-> m%02x", b(opcodes.r64(pc+2), 39, 6)); + } + + stream << r; + + return 1 | SUPPORTED; +} diff --git a/src/devices/sound/megd.h b/src/devices/sound/megd.h new file mode 100644 index 00000000000..59a13aa0ea4 --- /dev/null +++ b/src/devices/sound/megd.h @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation +// +// Disassembler + +#ifndef DEVICES_SOUND_MEGD_H +#define DEVICES_SOUND_MEGD_H + +#pragma once + +class meg_disassembler : public util::disasm_interface +{ +public: + class info { + public: + virtual u16 fp_r(u16 address) const = 0; + virtual u16 offset_r(u16 address) const = 0; + }; + + meg_disassembler(info *inf = nullptr); + + virtual u32 opcode_alignment() const override; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + info *m_info; + + std::string gfp(offs_t address) const; + std::string goffset(offs_t address) const; + + static inline u32 b(u64 opc, u32 start, u32 count); + static inline void append(std::string &r, std::string e); +}; + +#endif diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp index 56870df5ec7..8b22af0975d 100644 --- a/src/devices/sound/swp30.cpp +++ b/src/devices/sound/swp30.cpp @@ -150,17 +150,23 @@ DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip") swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SWP30, tag, owner, clock), device_sound_interface(mconfig, *this), - device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32) + device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32), + m_meg(*this, "meg") { } +void swp30_device::device_add_mconfig(machine_config &config) +{ + MEGEMB(config, m_meg); +} + void swp30_device::device_start() { m_stream = stream_alloc(0, 2, 44100); // Attenuantion for panning is 4.4 floating point. That means 0 // to -96.3dB. Since it's a nice range, we assume it's the same - // for other attenuation values. Computed value is is 1.16 + // for other attenuation values. Computed value is 1.16 // format, to avoid overflow for(int i=0; i<256; i++) @@ -328,8 +334,8 @@ void swp30_device::map(address_map &map) rchan(map, 0x27).rw(FUNC(swp30_device::prg_fp_r<3>), FUNC(swp30_device::prg_fp_w<3>)); rchan(map, 0x29).rw(FUNC(swp30_device::prg_fp_r<4>), FUNC(swp30_device::prg_fp_w<4>)); rchan(map, 0x2b).rw(FUNC(swp30_device::prg_fp_r<5>), FUNC(swp30_device::prg_fp_w<5>)); - rchan(map, 0x30).rw(FUNC(swp30_device::prg_int_r<0>), FUNC(swp30_device::prg_int_w<0>)); - rchan(map, 0x31).rw(FUNC(swp30_device::prg_int_r<1>), FUNC(swp30_device::prg_int_w<1>)); + rchan(map, 0x30).rw(FUNC(swp30_device::prg_off_r<0>), FUNC(swp30_device::prg_off_w<0>)); + rchan(map, 0x31).rw(FUNC(swp30_device::prg_off_r<1>), FUNC(swp30_device::prg_off_w<1>)); rchan(map, 0x3e).rw(FUNC(swp30_device::prg_lfo_r<0>), FUNC(swp30_device::prg_lfo_w<0>)); rchan(map, 0x3f).rw(FUNC(swp30_device::prg_lfo_r<1>), FUNC(swp30_device::prg_lfo_w<1>)); } @@ -381,14 +387,14 @@ void swp30_device::prg_address_w(u16 data) template u16 swp30_device::prg_r() { constexpr offs_t shift = 48-16*sel; - return m_program[m_program_address] >> shift; + return m_meg->prg_r(m_program_address) >> shift; } template void swp30_device::prg_w(u16 data) { constexpr offs_t shift = 48-16*sel; constexpr u64 mask = ~(u64(0xffff) << shift); - m_program[m_program_address] = (m_program[m_program_address] & mask) | (u64(data) << shift); + m_meg->prg_w(m_program_address, (m_meg->prg_r(m_program_address) & mask) | (u64(data) << shift)); if(sel == 3) { if(0) @@ -402,14 +408,12 @@ template void swp30_device::prg_w(u16 data) template u16 swp30_device::map_r() { - return m_map[sel]; + return m_meg->map_r(sel); } template void swp30_device::map_w(u16 data) { - m_map[sel] = data; - if(0) - logerror("map %d: type=%02x offset=%05x size=%05x\n", sel, data >> 11, (data & 0xff) << 10, 0x400 << ((data >> 8) & 7)); + m_meg->map_w(sel, data); } @@ -656,54 +660,36 @@ void swp30_device::address_l_w(offs_t offset, u16 data) } -// MEG registers (Multiple Effects Generator) +// MEG registers forwarding template u16 swp30_device::prg_fp_r(offs_t offset) { - offs_t adr = (offset >> 6)*6 + sel; - return m_program_pfp[adr]; + return m_meg->fp_r((offset >> 6)*6 + sel); } template void swp30_device::prg_fp_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*6 + sel; - m_program_pfp[adr] = data; - if(0) - logerror("prg_fp_w %03x, %04x\n", adr, data); + m_meg->fp_w((offset >> 6)*6 + sel, data); } -template u16 swp30_device::prg_int_r(offs_t offset) +template u16 swp30_device::prg_off_r(offs_t offset) { - offs_t adr = (offset >> 6)*2 + sel; - return m_program_pint[adr]; + return m_meg->offset_r((offset >> 6)*2 + sel); } -template void swp30_device::prg_int_w(offs_t offset, u16 data) +template void swp30_device::prg_off_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*2 + sel; - m_program_pint[adr] = data; - if(0) - logerror("prg_int_w %02x, %04x\n", adr, data); + m_meg->offset_w((offset >> 6)*2 + sel, data); } template u16 swp30_device::prg_lfo_r(offs_t offset) { - offs_t adr = (offset >> 6)*2 + sel; - return m_program_plfo[adr]; + return m_meg->lfo_r((offset >> 6)*2 + sel); } template void swp30_device::prg_lfo_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*2 + sel; - m_program_plfo[adr] = data; - - static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 }; - static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 }; - - int scale = (data >> 5) & 7; - int step = ((data & 31) << sh[scale]) + dt[scale]; - if(0) - logerror("prg_lfo_w %02x freq=%5.2f phase=%6.4f\n", adr, step * 44100.0/4194304, (data >> 8)/256.0); + m_meg->lfo_w((offset >> 6)*2 + sel, data); } diff --git a/src/devices/sound/swp30.h b/src/devices/sound/swp30.h index 56e24b453f0..dd95a8dd85f 100644 --- a/src/devices/sound/swp30.h +++ b/src/devices/sound/swp30.h @@ -8,6 +8,8 @@ #pragma once +#include "meg.h" + class swp30_device : public device_t, public device_sound_interface, public device_rom_interface { public: @@ -20,8 +22,11 @@ protected: virtual void device_reset() override; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; virtual void rom_bank_updated() override; + virtual void device_add_mconfig(machine_config &config) override; private: + required_device m_meg; + sound_stream *m_stream; s32 m_sample_increment[0x4000]; @@ -99,8 +104,8 @@ private: // MEG registers template u16 prg_fp_r(offs_t offset); template void prg_fp_w(offs_t offset, u16 data); - template u16 prg_int_r(offs_t offset); - template void prg_int_w(offs_t offset, u16 data); + template u16 prg_off_r(offs_t offset); + template void prg_off_w(offs_t offset, u16 data); template u16 prg_lfo_r(offs_t offset); template void prg_lfo_w(offs_t offset, u16 data); diff --git a/src/mame/drivers/ymmu100.cpp b/src/mame/drivers/ymmu100.cpp index 4d89242b061..e45ccbb1b06 100644 --- a/src/mame/drivers/ymmu100.cpp +++ b/src/mame/drivers/ymmu100.cpp @@ -136,6 +136,7 @@ #include "cpu/h8/h8s2655.h" #include "video/hd44780.h" #include "sound/swp30.h" +#include "sound/meg.h" #include "debugger.h" #include "screen.h" @@ -199,6 +200,7 @@ public: , m_mu80cpu(*this, "mu80cpu") , m_vl70cpu(*this, "vl70cpu") , m_swp30(*this, "swp30") + , m_meg(*this, "meg") , m_lcd(*this, "lcd") , m_ioport_p7(*this, "P7") , m_ioport_p8(*this, "P8") @@ -316,6 +318,7 @@ private: optional_device m_mu80cpu; optional_device m_vl70cpu; optional_device m_swp30; + optional_device m_meg; required_device m_lcd; optional_ioport m_ioport_p7; optional_ioport m_ioport_p8; @@ -597,12 +600,14 @@ void mu100_state::mu80_map(address_map &map) { map(0x000000, 0x07ffff).rom().region("mu80cpu", 0); map(0x200000, 0x20ffff).ram(); // 64K work RAM + map(0x440000, 0x44001f).m(m_meg, FUNC(meg_device::map)); } void mu100_state::vl70_map(address_map &map) { map(0x000000, 0x1fffff).rom().region("vl70cpu", 0); map(0x200000, 0x20ffff).ram(); // 64K work RAM + map(0x600000, 0x60001f).m(m_meg, FUNC(meg_device::map)); } void mu100_state::mu100_map(address_map &map) @@ -991,6 +996,8 @@ void mu100_state::mu80(machine_config &config) m_swp30->add_route(0, "lspeaker", 1.0); m_swp30->add_route(1, "rspeaker", 1.0); + MEG(config, m_meg); + auto &mdin_a(MIDI_PORT(config, "mdin_a")); midiin_slot(mdin_a); mdin_a.rxd_handler().set("mu80cpu:sci1", FUNC(h8_sci_device::rx_w)); @@ -1023,6 +1030,8 @@ void mu100_state::vl70(machine_config &config) SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); + MEG(config, m_meg); + auto &mdin_a(MIDI_PORT(config, "mdin_a")); midiin_slot(mdin_a); mdin_a.rxd_handler().set("vl70cpu:sci1", FUNC(h8_sci_device::rx_w)); -- cgit v1.2.3-70-g09d2 From 9e122168ef4fdbb9158bc8a4fd0e5e86900496f0 Mon Sep 17 00:00:00 2001 From: Nigel Barnes Date: Sat, 23 Mar 2019 11:55:09 +0000 Subject: ns32000: Skeleton CPU device with disassembler for NS32000 family. bbc: Added preliminary Acorn 32016 2nd Processor, incomplete CPU emulation. --- scripts/src/bus.lua | 2 + scripts/src/cpu.lua | 17 + scripts/target/mame/mess.lua | 1 + src/devices/bus/bbc/tube/tube.cpp | 12 +- src/devices/bus/bbc/tube/tube_32016.cpp | 210 ++++++++ src/devices/bus/bbc/tube/tube_32016.h | 71 +++ src/devices/cpu/ns32000/ns32000.cpp | 160 ++++++ src/devices/cpu/ns32000/ns32000.h | 149 ++++++ src/devices/cpu/ns32000/ns32000dasm.cpp | 834 ++++++++++++++++++++++++++++++++ src/devices/cpu/ns32000/ns32000dasm.h | 118 +++++ src/tools/unidasm.cpp | 2 + 11 files changed, 1571 insertions(+), 5 deletions(-) create mode 100644 src/devices/bus/bbc/tube/tube_32016.cpp create mode 100644 src/devices/bus/bbc/tube/tube_32016.h create mode 100644 src/devices/cpu/ns32000/ns32000.cpp create mode 100644 src/devices/cpu/ns32000/ns32000.h create mode 100644 src/devices/cpu/ns32000/ns32000dasm.cpp create mode 100644 src/devices/cpu/ns32000/ns32000dasm.h (limited to 'scripts/src/cpu.lua') diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index d846e8b6abd..14130a37ae7 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -409,6 +409,8 @@ if (BUSES["BBC_TUBE"]~=null) then files { MAME_DIR .. "src/devices/bus/bbc/tube/tube.cpp", MAME_DIR .. "src/devices/bus/bbc/tube/tube.h", + MAME_DIR .. "src/devices/bus/bbc/tube/tube_32016.cpp", + MAME_DIR .. "src/devices/bus/bbc/tube/tube_32016.h", MAME_DIR .. "src/devices/bus/bbc/tube/tube_6502.cpp", MAME_DIR .. "src/devices/bus/bbc/tube/tube_6502.h", MAME_DIR .. "src/devices/bus/bbc/tube/tube_80186.cpp", diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index e8084e2a362..8c461e0ce16 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -2835,3 +2835,20 @@ if (CPUS["MEG"]~=null or _OPTIONS["with-tools"]) then table.insert(disasm_files , MAME_DIR .. "src/devices/sound/megd.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/sound/megd.h") end + +-------------------------------------------------- +-- National Semiconductor NS32000 series +--@src/devices/cpu/ns32000/ns32000.h,CPUS["NS32000"] = true +-------------------------------------------------- + +if (CPUS["NS32000"]~=null) then + files { + MAME_DIR .. "src/devices/cpu/ns32000/ns32000.cpp", + MAME_DIR .. "src/devices/cpu/ns32000/ns32000.h", + } +end + +if (CPUS["NS32000"]~=null or _OPTIONS["with-tools"]) then + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/ns32000/ns32000dasm.cpp") + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/ns32000/ns32000dasm.h") +end diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index bfe03e65404..d957b76c9b9 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -137,6 +137,7 @@ CPUS["NANOPROCESSOR"] = true CPUS["CLIPPER"] = true CPUS["CAPRICORN"] = true CPUS["ALPHA"] = true +CPUS["NS32000"] = true --CPUS["DSPP"] = true CPUS["HPC"] = true CPUS["MEG"] = true diff --git a/src/devices/bus/bbc/tube/tube.cpp b/src/devices/bus/bbc/tube/tube.cpp index c0ff49915a6..602ea3c845a 100644 --- a/src/devices/bus/bbc/tube/tube.cpp +++ b/src/devices/bus/bbc/tube/tube.cpp @@ -112,6 +112,7 @@ void bbc_tube_slot_device::host_w(offs_t offset, uint8_t data) // slot devices +#include "tube_32016.h" #include "tube_6502.h" #include "tube_80186.h" #include "tube_80286.h" @@ -133,7 +134,7 @@ void bbc_tube_devices(device_slot_interface &device) { device.option_add("6502", BBC_TUBE_6502); /* Acorn ANC01 6502 2nd processor */ device.option_add("z80", BBC_TUBE_Z80); /* Acorn ANC04 Z80 2nd processor */ - //device.option_add("32016", BBC_TUBE_32016); /* Acorn ANC05 32016 2nd processor */ + device.option_add("32016", BBC_TUBE_32016); /* Acorn ANC05 32016 2nd processor */ device.option_add("arm", BBC_TUBE_ARM); /* Acorn ANC13 ARM Evaluation System */ device.option_add("80286", BBC_TUBE_80286); /* Acorn 80286 2nd Processor */ //device.option_add("a500", BBC_TUBE_A500); /* Acorn A500 2nd Processor */ @@ -142,8 +143,8 @@ void bbc_tube_devices(device_slot_interface &device) //device.option_add("hdp68k", BBC_TUBE_HDP68K); /* Torch Unicorn (HDP68K) */ //device.option_add("x25", BBC_TUBE_X25); /* Econet X25 Gateway */ device.option_add("zep100", BBC_TUBE_ZEP100); /* Torch Z80 Communicator (ZEP100) (Torch) */ - //device.option_add("zep100l", BBC_TUBE_ZEP100L); /* Torch Z80 Communicator (ZEP100) (Acorn 8271) */ - //device.option_add("zep100w", BBC_TUBE_ZEP100W); /* Torch Z80 Communicator (ZEP100) (Acorn 1770) */ + //device.option_add("zep100l", BBC_TUBE_ZEP100L); /* Torch Z80 Communicator (ZEP100) (Model B) */ + //device.option_add("zep100w", BBC_TUBE_ZEP100W); /* Torch Z80 Communicator (ZEP100) (Model B+) */ /* Acorn ANC21 Universal 2nd Processor Unit */ device.option_add("65c102", BBC_TUBE_65C102); /* Acorn ADC06 65C102 co-processor */ device.option_add("80186", BBC_TUBE_80186); /* Acorn ADC08 80186 co-processor */ @@ -160,11 +161,12 @@ void bbc_extube_devices(device_slot_interface &device) { device.option_add("6502", BBC_TUBE_6502); /* Acorn ANC01 6502 2nd processor */ device.option_add("z80", BBC_TUBE_Z80); /* Acorn ANC04 Z80 2nd processor */ - //device.option_add("32016", BBC_TUBE_32016); /* Acorn ANC05 32016 2nd processor */ + device.option_add("32016", BBC_TUBE_32016); /* Acorn ANC05 32016 2nd processor */ device.option_add("arm", BBC_TUBE_ARM); /* Acorn ANC13 ARM Evaluation System */ device.option_add("80286", BBC_TUBE_80286); /* Acorn 80286 2nd Processor */ //device.option_add("a500", BBC_TUBE_A500); /* Acorn A500 2nd Processor */ //device.option_add("pmsb2p", BBC_TUBE_PMSB2P); /* PMS B2P-6502 */ + //device.option_add("zep100m", BBC_TUBE_ZEP100M); /* Torch Z80 Communicator (ZEP100) (Master) */ /* Acorn ANC21 Universal 2nd Processor Unit */ device.option_add("65c102", BBC_TUBE_65C102); /* Acorn ADC06 65C102 co-processor */ device.option_add("80186", BBC_TUBE_80186); /* Acorn ADC08 80186 co-processor */ @@ -181,7 +183,6 @@ void bbc_intube_devices(device_slot_interface &device) { device.option_add("65c102", BBC_TUBE_65C102); /* Acorn ADC06 65C102 co-processor */ device.option_add("80186", BBC_TUBE_80186); /* Acorn ADC08 80186 co-processor */ - //device.option_add("zep100m", BBC_TUBE_ZEP100M); /* Torch Z80 Communicator (ZEP100) (Master) */ //device.option_add("arm7", BBC_TUBE_ARM7); /* Sprow ARM7 co-processor */ device.option_add("rc6502", BBC_TUBE_RC6502); /* ReCo6502 (6502) */ device.option_add("rc65816", BBC_TUBE_RC65816); /* ReCo6502 (65816) */ @@ -196,6 +197,7 @@ void electron_tube_devices(device_slot_interface &device) { device.option_add("6502", BBC_TUBE_6502); /* Acorn ANC01 6502 2nd processor */ device.option_add("z80", BBC_TUBE_Z80); /* Acorn ANC04 Z80 2nd processor */ + device.option_add("32016", BBC_TUBE_32016); /* Acorn ANC05 32016 2nd processor */ device.option_add("arm", BBC_TUBE_ARM); /* Acorn ANC13 ARM Evaluation System */ device.option_add("65c102", BBC_TUBE_65C102); /* Acorn ADC06 65C102 co-processor */ device.option_add("80186", BBC_TUBE_80186); /* Acorn ADC08 80186 co-processor */ diff --git a/src/devices/bus/bbc/tube/tube_32016.cpp b/src/devices/bus/bbc/tube/tube_32016.cpp new file mode 100644 index 00000000000..7b6ac355e42 --- /dev/null +++ b/src/devices/bus/bbc/tube/tube_32016.cpp @@ -0,0 +1,210 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/********************************************************************** + + Acorn ANC05 32016 2nd processor + + http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANC05_320162ndproc.html + + Acorn ANC06 Cambridge Co-Processor + + http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANC06_CamCoPro.html + + IC1 (ULA) TUBE + IC2 (MMU) NS32082 Not fitted + IC3 (CPU) NS32016 + IC4 (TCU) NS32201 + IC20 (FPU) NS32081 + +**********************************************************************/ + + +#include "emu.h" +#include "tube_32016.h" +#include "softlist_dev.h" + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(BBC_TUBE_32016, bbc_tube_32016_device, "bbc_tube_32016", "Acorn 32016 2nd processor") + + +//------------------------------------------------- +// ADDRESS_MAP( tube_32016_mem ) +//------------------------------------------------- + +void bbc_tube_32016_device::tube_32016_mem(address_map &map) +{ + map(0x000000, 0xffffff).rw(FUNC(bbc_tube_32016_device::read), FUNC(bbc_tube_32016_device::write)); + map(0xf90000, 0xf90001).portr("CONFIG"); + map(0xfffff0, 0xffffff).rw("ula", FUNC(tube_device::parasite_r), FUNC(tube_device::parasite_w)).umask32(0x00ff); +} + +//------------------------------------------------- +// ROM( tube_32016 ) +//------------------------------------------------- + +ROM_START(tube_32016) + ROM_REGION(0x8000, "rom", 0) + ROM_DEFAULT_BIOS("200") + ROM_SYSTEM_BIOS(0, "200", "Pandora v2.00") + ROMX_LOAD("pan200lo.rom", 0x0000, 0x4000, CRC(b1980fd0) SHA1(8084f8896cd22953abefbd43c51e1a422b30e28d), ROM_SKIP(1) | ROM_BIOS(0)) // 0201-764-02 Pandora Lo + ROMX_LOAD("pan200hi.rom", 0x0001, 0x4000, CRC(cab98d6b) SHA1(dfad1f4180c50757a74fcfe3a0ee7d7b48eb1bee), ROM_SKIP(1) | ROM_BIOS(0)) // 0201-763-02 Pandora Hi + ROM_SYSTEM_BIOS(1, "100", "Pandora v1.00") + ROMX_LOAD("pan100.rom", 0x0000, 0x8000, BAD_DUMP CRC(75333006) SHA1(996cd120103039390c9b979b16c327bb95da72e4), ROM_BIOS(1)) // 0201-763-01, 0201-764-01 Pandora + ROM_SYSTEM_BIOS(2, "061", "Pandora v0.61") + ROMX_LOAD("pan061lo.rom", 0x0000, 0x4000, CRC(6f801b35) SHA1(ce31f7c10603f3d15a06a8e32bde40df0639e446), ROM_SKIP(1) | ROM_BIOS(2)) + ROMX_LOAD("pan061hi.rom", 0x0001, 0x4000, CRC(c00b1ab0) SHA1(e6a705232278c518340ddc69ea51af91965fa332), ROM_SKIP(1) | ROM_BIOS(2)) +ROM_END + +//------------------------------------------------- +// INPUT_PORTS( tube_32016 ) +//------------------------------------------------- + +static INPUT_PORTS_START(tube_32016) + PORT_START("CONFIG") + PORT_DIPNAME(0x80, 0x80, "H") PORT_DIPLOCATION("LKS:1") + PORT_DIPSETTING(0x80, "FPU") + PORT_DIPSETTING(0x00, "No FPU") + + PORT_DIPNAME(0x40, 0x00, "G") PORT_DIPLOCATION("LKS:2") + PORT_DIPSETTING(0x40, "MMU") + PORT_DIPSETTING(0x00, "No MMU") + + PORT_DIPNAME(0x20, 0x00, "F") PORT_DIPLOCATION("LKS:3") + PORT_DIPSETTING(0x20, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") + + PORT_DIPNAME(0x10, 0x00, "E") PORT_DIPLOCATION("LKS:4") + PORT_DIPSETTING(0x10, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") + + PORT_DIPNAME(0x08, 0x00, "D") PORT_DIPLOCATION("LKS:5") + PORT_DIPSETTING(0x08, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") + + PORT_DIPNAME(0x04, 0x00, "C") PORT_DIPLOCATION("LKS:6") + PORT_DIPSETTING(0x04, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") + + PORT_DIPNAME(0x02, 0x00, "B") PORT_DIPLOCATION("LKS:7") + PORT_DIPSETTING(0x02, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") + + PORT_DIPNAME(0x01, 0x00, "A") PORT_DIPLOCATION("LKS:8") + PORT_DIPSETTING(0x01, "Reserved") + PORT_DIPSETTING(0x00, "Reserved") +INPUT_PORTS_END + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void bbc_tube_32016_device::device_add_mconfig(machine_config &config) +{ + NS32016(config, m_maincpu, 12_MHz_XTAL / 2); + m_maincpu->set_addrmap(AS_PROGRAM, &bbc_tube_32016_device::tube_32016_mem); + + TUBE(config, m_ula); + m_ula->pnmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI); + m_ula->pirq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0); + + /* internal ram */ + RAM(config, m_ram).set_default_size("1M").set_default_value(0); + + /* software lists */ + SOFTWARE_LIST(config, "flop_ls_32016").set_original("bbc_flop_32016"); +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const tiny_rom_entry *bbc_tube_32016_device::device_rom_region() const +{ + return ROM_NAME( tube_32016 ); +} + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor bbc_tube_32016_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( tube_32016 ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// bbc_tube_32016_device - constructor +//------------------------------------------------- + +bbc_tube_32016_device::bbc_tube_32016_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, BBC_TUBE_32016, tag, owner, clock) + , device_bbc_tube_interface(mconfig, *this) + , m_maincpu(*this, "maincpu") + , m_ula(*this, "ula") + , m_ram(*this, "ram") + , m_rom(*this, "rom") + , m_rom_enabled(true) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void bbc_tube_32016_device::device_start() +{ +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void bbc_tube_32016_device::device_reset() +{ + m_rom_enabled = true; +} + + +//************************************************************************** +// IMPLEMENTATION +//************************************************************************** + +uint8_t bbc_tube_32016_device::host_r(offs_t offset) +{ + return m_ula->host_r(offset); +} + +void bbc_tube_32016_device::host_w(offs_t offset, uint8_t data) +{ + m_ula->host_w(offset, data); +} + + +READ8_MEMBER(bbc_tube_32016_device::read) +{ + uint16_t data = 0xffff; + + if (m_rom_enabled) + data = m_rom->base()[offset & 0x3fff]; + else if (offset < m_ram->size()) + data = m_ram->pointer()[offset]; + + return data; +} + +WRITE8_MEMBER(bbc_tube_32016_device::write) +{ + /* clear ROM select on first write */ + if (!machine().side_effects_disabled()) m_rom_enabled = false; + + if (offset < m_ram->size()) + m_ram->pointer()[offset] = data; +} diff --git a/src/devices/bus/bbc/tube/tube_32016.h b/src/devices/bus/bbc/tube/tube_32016.h new file mode 100644 index 00000000000..864b8173703 --- /dev/null +++ b/src/devices/bus/bbc/tube/tube_32016.h @@ -0,0 +1,71 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/********************************************************************** + + Acorn ANC05 32016 2nd processor + + http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANC05_320162ndproc.html + + Acorn ANC06 Cambridge Co-Processor + + http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_ANC06_CamCoPro.html + +**********************************************************************/ + + +#ifndef MAME_BUS_BBC_TUBE_32016_H +#define MAME_BUS_BBC_TUBE_32016_H + +#include "tube.h" +#include "cpu/ns32000/ns32000.h" +#include "machine/ram.h" +#include "machine/tube.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> bbc_tube_32016_device + +class bbc_tube_32016_device : + public device_t, + public device_bbc_tube_interface +{ +public: + // construction/destruction + bbc_tube_32016_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // optional information overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual const tiny_rom_entry *device_rom_region() const override; + virtual ioport_constructor device_input_ports() const override; + + virtual uint8_t host_r(offs_t offset) override; + virtual void host_w(offs_t offset, uint8_t data) override; + +private: + required_device m_maincpu; + required_device m_ula; + required_device m_ram; + required_memory_region m_rom; + + DECLARE_READ8_MEMBER(read); + DECLARE_WRITE8_MEMBER(write); + + void tube_32016_mem(address_map &map); + + bool m_rom_enabled; +}; + + + +// device type definition +DECLARE_DEVICE_TYPE(BBC_TUBE_32016, bbc_tube_32016_device) + + +#endif /* MAME_BUS_BBC_TUBE_32016_H */ diff --git a/src/devices/cpu/ns32000/ns32000.cpp b/src/devices/cpu/ns32000/ns32000.cpp new file mode 100644 index 00000000000..6596d28f8dd --- /dev/null +++ b/src/devices/cpu/ns32000/ns32000.cpp @@ -0,0 +1,160 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/***************************************************************************** +* +* ns32000.cpp +* +* NS32000 CPU family +* +*****************************************************************************/ + +#include "emu.h" +#include "ns32000.h" +#include "ns32000dasm.h" +#include "debugger.h" + +//************************************************************************** +// DEVICE INTERFACE +//************************************************************************** + +DEFINE_DEVICE_TYPE(NS32008, ns32008_cpu_device, "ns32008", "National Semiconductor NS32008") +DEFINE_DEVICE_TYPE(NS32016, ns32016_cpu_device, "ns32016", "National Semiconductor NS32016") +DEFINE_DEVICE_TYPE(NS32032, ns32032_cpu_device, "ns32032", "National Semiconductor NS32032") + + +//------------------------------------------------- +// ns32000_cpu_device - constructor +//------------------------------------------------- + +ns32000_cpu_device::ns32000_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int databits, int addrbits) + : cpu_device(mconfig, type, tag, owner, clock) + , m_program_config("program", ENDIANNESS_LITTLE, databits, addrbits, 0) + , m_icount(0) +{ +} + +ns32008_cpu_device::ns32008_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : ns32000_cpu_device(mconfig, NS32008, tag, owner, clock, 8, 24) +{ +} + +ns32016_cpu_device::ns32016_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : ns32000_cpu_device(mconfig, NS32016, tag, owner, clock, 16, 24) +{ +} + +ns32032_cpu_device::ns32032_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : ns32000_cpu_device(mconfig, NS32032, tag, owner, clock, 32, 24) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ns32000_cpu_device::device_start() +{ + set_icountptr(m_icount); + + save_item(NAME(m_pc)); + save_item(NAME(m_r)); + save_item(NAME(m_f)); + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + + // dedicated registers + state_add(NS32000_PC, "PC", m_pc); + state_add(NS32000_SB, "SB", m_sb); + state_add(NS32000_FP, "FP", m_fp); + state_add(NS32000_SP1, "SP1", m_sp1); + state_add(NS32000_SP0, "SP0", m_sp0); + state_add(NS32000_INTBASE, "INTBASE", m_intbase); + state_add(NS32000_PSR, "PSR", m_psr); + state_add(NS32000_MOD, "MOD", m_mod); + + // general registers + for (unsigned i = 0; i < 8; i++) + state_add(NS32000_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]); + + // floating point registers + //for (unsigned i = 0; i < 8; i++) + // state_add(NS32000_R7 + i, util::string_format("F%d", i).c_str(), m_f[i]); + + // set our instruction counter + //set_icountptr(m_icount); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ns32000_cpu_device::device_reset() +{ + m_nmi_line = false; + m_irq_line = false; + + m_pc = 0; +} + +//------------------------------------------------- +// execute_run - execute a timeslice's worth of +// opcodes +//------------------------------------------------- + +void ns32000_cpu_device::execute_run() +{ + while (m_icount > 0) + { + debugger_instruction_hook(m_pc); + + m_icount--; + } +} + +//------------------------------------------------- +// execute_set_input - act on a changed input/ +// interrupt line +//------------------------------------------------- + +void ns32000_cpu_device::execute_set_input(int inputnum, int state) +{ + switch (inputnum) + { + case INPUT_LINE_NMI: + // NMI is edge triggered + m_nmi_line = m_nmi_line || (state == ASSERT_LINE); + break; + + case INPUT_LINE_IRQ0: + // IRQ is line triggered + m_irq_line = state == ASSERT_LINE; + break; + } +} + +//------------------------------------------------- +// memory_space_config - return the configuration +// of the specified address space, or nullptr if +// the space doesn't exist +//------------------------------------------------- + +device_memory_interface::space_config_vector ns32000_cpu_device::memory_space_config() const +{ + return space_config_vector{ std::make_pair(AS_PROGRAM, &m_program_config) }; +} + +bool ns32000_cpu_device::memory_translate(int spacenum, int intention, offs_t &address) +{ + return true; +} + +//------------------------------------------------- +// disassemble - call the disassembly +// helper function +//------------------------------------------------- + +std::unique_ptr ns32000_cpu_device::create_disassembler() +{ + return std::make_unique(); +} diff --git a/src/devices/cpu/ns32000/ns32000.h b/src/devices/cpu/ns32000/ns32000.h new file mode 100644 index 00000000000..7b17cafba99 --- /dev/null +++ b/src/devices/cpu/ns32000/ns32000.h @@ -0,0 +1,149 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/***************************************************************************** +* +* ns32000.h +* +* NS32000 CPU family +* +*****************************************************************************/ + +#ifndef MAME_CPU_NS32016_NS32016_H +#define MAME_CPU_NS32016_NS32016_H + +#pragma once + + +/*********************************************************************** + CONSTANTS +***********************************************************************/ + + + +/*********************************************************************** + TYPE DEFINITIONS +***********************************************************************/ + +class ns32000_cpu_device : public cpu_device +{ +public: + // construction/destruction + ns32000_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + +protected: + ns32000_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int databits, int addrbits); + + // 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 6; } + virtual u32 execute_input_lines() const override { return 2; } + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + virtual bool execute_input_edge_triggered(int inputnum) const override { return inputnum == INPUT_LINE_NMI; } + + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; + + // addressing modes + enum + { + ADDRESSING_MODE_REGISTER_0 = 0, + ADDRESSING_MODE_REGISTER_1, + ADDRESSING_MODE_REGISTER_2, + ADDRESSING_MODE_REGISTER_3, + ADDRESSING_MODE_REGISTER_4, + ADDRESSING_MODE_REGISTER_5, + ADDRESSING_MODE_REGISTER_6, + ADDRESSING_MODE_REGISTER_7, + ADDRESSING_MODE_REGISTER_0_RELATIVE, + ADDRESSING_MODE_REGISTER_1_RELATIVE, + ADDRESSING_MODE_REGISTER_2_RELATIVE, + ADDRESSING_MODE_REGISTER_3_RELATIVE, + ADDRESSING_MODE_REGISTER_4_RELATIVE, + ADDRESSING_MODE_REGISTER_5_RELATIVE, + ADDRESSING_MODE_REGISTER_6_RELATIVE, + ADDRESSING_MODE_REGISTER_7_RELATIVE, + ADDRESSING_MODE_FRAME_MEMORY_RELATIVE, + ADDRESSING_MODE_STACK_MEMORY_RELATIVE, + ADDRESSING_MODE_STATIC_MEMORY_RELATIVE, + ADDRESSING_MODE_RESERVED, + ADDRESSING_MODE_IMMEDIATE, + ADDRESSING_MODE_ABSOLUTE, + ADDRESSING_MODE_EXTERNAL, + ADDRESSING_MODE_TOP_OF_STACK, + ADDRESSING_MODE_FRAME_MEMORY, + ADDRESSING_MODE_STACK_MEMORY, + ADDRESSING_MODE_STATIC_MEMORY, + ADDRESSING_MODE_PROGRAM_MEMORY, + ADDRESSING_MODE_INDEX_BYTES, + ADDRESSING_MODE_INDEX_WORDS, + ADDRESSING_MODE_INDEX_DOUBLE_WORDS, + ADDRESSING_MODE_INDEX_QUAD_WORDS, + }; + + // registers + enum + { + NS32000_PC = 1, + NS32000_SB, NS32000_FP, NS32000_SP1, NS32000_SP0, NS32000_INTBASE, NS32000_PSR, NS32000_MOD, + NS32000_R0, NS32000_R1, NS32000_R2, NS32000_R3, NS32000_R4, NS32000_R5, NS32000_R6, NS32000_R7, + }; + +private: + // configuration + address_space_config m_program_config; + + // emulation state + int m_icount; + + u32 m_pc; + u32 m_sb; + u32 m_fp; + u32 m_sp1; + u32 m_sp0; + u32 m_intbase; + u32 m_psr; + u32 m_mod; + u32 m_r[8]; + u32 m_f[8]; + + u8 m_irq_line; + u8 m_nmi_line; +}; + +class ns32008_cpu_device : public ns32000_cpu_device +{ +public: + ns32008_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +class ns32016_cpu_device : public ns32000_cpu_device +{ +public: + ns32016_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +class ns32032_cpu_device : public ns32000_cpu_device +{ +public: + ns32032_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +/*********************************************************************** + DEVICE TYPE DECLARATIONS +***********************************************************************/ + +DECLARE_DEVICE_TYPE(NS32008, ns32008_cpu_device) +DECLARE_DEVICE_TYPE(NS32016, ns32016_cpu_device) +DECLARE_DEVICE_TYPE(NS32032, ns32032_cpu_device) + +#endif // MAME_CPU_NS32016_NS32016_H diff --git a/src/devices/cpu/ns32000/ns32000dasm.cpp b/src/devices/cpu/ns32000/ns32000dasm.cpp new file mode 100644 index 00000000000..983abefdd3e --- /dev/null +++ b/src/devices/cpu/ns32000/ns32000dasm.cpp @@ -0,0 +1,834 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/***************************************************************************** +* +* ns32000dasm.cpp +* +* NS32000 CPU Disassembly +* +*****************************************************************************/ + +#include "emu.h" +#include "ns32000dasm.h" + +// instruction field extraction +#define Format0cond(x) ((x >> 4) & 0x0f) + +#define Format1op(x) ((x >> 4) & 0x0f) + +#define Format2i(x) ((x >> 0) & 0x03) +#define Format2op(x) ((x >> 4) & 0x07) +#define Format2short(x) ((x >> 7) & 0x0f) +#define Format2gen(x) ((x >> 11) & 0x1f) + +#define Format3i(x) ((x >> 0) & 0x03) +#define Format3op(x) ((x >> 7) & 0x0f) +#define Format3gen(x) ((x >> 11) & 0x1f) + +#define Format4i(x) ((x >> 0) & 0x03) +#define Format4op(x) ((x >> 2) & 0x0f) +#define Format4gen2(x) ((x >> 6) & 0x1f) +#define Format4gen1(x) ((x >> 11) & 0x1f) + +#define Format5i(x) ((x >> 8) & 0x03) +#define Format5op(x) ((x >> 10) & 0x07) +#define Format5short(x) ((x >> 15) & 0x0f) + +#define Format6i(x) ((x >> 8) & 0x03) +#define Format6op(x) ((x >> 10) & 0x0f) +#define Format6gen2(x) ((x >> 14) & 0x1f) +#define Format6gen1(x) ((x >> 19) & 0x1f) + +#define Format7i(x) ((x >> 8) & 0x03) +#define Format7op(x) ((x >> 10) & 0x0f) +#define Format7gen2(x) ((x >> 14) & 0x1f) +#define Format7gen1(x) ((x >> 19) & 0x1f) + +#define Format8i(x) ((x >> 8) & 0x03) +#define Format8op(x) (((x >> 8) & 0x04) | ((x >> 6) & 0x03)) +#define Format8reg(x) ((x >> 11) & 0x07) +#define Format8gen2(x) ((x >> 14) & 0x1f) +#define Format8gen1(x) ((x >> 19) & 0x1f) + +#define Format9i(x) ((x >> 8) & 0x03) +#define Format9f(x) ((x >> 10) & 0x01) +#define Format9op(x) ((x >> 11) & 0x07) +#define Format9gen2(x) ((x >> 14) & 0x1f) +#define Format9gen1(x) ((x >> 19) & 0x1f) + +#define Format11f(x) ((x >> 8) & 0x01) +#define Format11op(x) ((x >> 10) & 0x0f) +#define Format11gen2(x) ((x >> 14) & 0x1f) +#define Format11gen1(x) ((x >> 19) & 0x1f) + +#define Format14i(x) ((x >> 8) & 0x01) +#define Format14op(x) ((x >> 10) & 0x0f) +#define Format14short(x) ((x >> 15) & 0x1f) +#define Format14gen1(x) ((x >> 19) & 0x1f) + +// instructions +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format0_op[1] = +{ + { "Bcond", DISP, 0, 0, 0, 0 } +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format1_op[16] = +{ + { "BSR", DISP, 0, 0, 0, STEP_OVER }, + { "RET", DISP, 0, 0, 0, STEP_OUT }, + { "CXP", DISP, 0, 0, 0, STEP_OVER }, + { "RXP", DISP, 0, 0, 0, STEP_OUT }, + { "RETT", 0, 0, 0, 0, STEP_OUT }, + { "RETI", DISP, 0, 0, 0, STEP_OUT }, + { "SAVE", IMM, 0, 0, 0, 0 }, + { "RESTORE", IMM, 0, 0, 0, 0 }, + { "ENTER", IMM, DISP, 0, 0, 0 }, + { "EXIT", IMM, 0, 0, 0, 0 }, + { "NOP", 0, 0, 0, 0, 0 }, + { "WAIT", 0, 0, 0, 0, 0 }, + { "DIA", 0, 0, 0, 0, 0 }, + { "FLAG", 0, 0, 0, 0, 0 }, + { "SVC", 0, 0, 0, 0, 0 }, + { "BPT", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format2_op[8] = +{ + { "ADDQi", QUICK, GEN | RMW | I, 0, 0, 0 }, + { "CMPQi", QUICK, GEN | READ | I, 0, 0, 0 }, + { "SPRi", SHORT, GEN | WRITE | I, 0, 0, 0 }, + { "Scondi", GEN | WRITE | I, 0, 0, 0, 0 }, + { "ACBi", QUICK, GEN | RMW | I, DISP, 0, 0 }, + { "MOVQi", QUICK, GEN | WRITE | I, 0, 0, 0 }, + { "LPRi", SHORT, GEN | READ | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format3_op[16] = +{ + { "CXPD", GEN | ADDR, 0, 0, 0, STEP_OVER }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "BICPSRi", GEN | READ | I, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "JUMP", GEN | ADDR, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "BISPSRB", GEN | READ | B, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "ADJSPi", GEN | READ | I, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "JSR", GEN | ADDR, 0, 0, 0, STEP_OVER }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "CASEi", GEN | READ | I, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format4_op[16] = +{ + { "ADDi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "CMPi", GEN | READ | I, GEN | READ | I, 0, 0, 0 }, + { "BICi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "ADDCi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "MOVi", GEN | READ | I, GEN | WRITE | I, 0, 0, 0 }, + { "ORi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "SUBi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "ADDR", GEN | ADDR, GEN | WRITE | D, 0, 0, 0 }, + { "ANDi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "SUBCi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "TBITi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "XORi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format5_op[16] = +{ + { "MOVSi", OPTIONS, 0, 0, 0, 0 }, + { "CMPSi", OPTIONS, 0, 0, 0, 0 }, + { "SETCFG", SHORT, 0, 0, 0, 0 }, + { "SKPSi", OPTIONS, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format6_op[16] = +{ + { "ROTi", GEN | READ | B, GEN | RMW | I, 0, 0, 0 }, + { "ASHi", GEN | READ | B, GEN | RMW | I, 0, 0, 0 }, + { "CBITi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "CBITIi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "LSHi", GEN | READ | B, GEN | RMW | I, 0, 0, 0 }, + { "SBITi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "SBITIi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "NEGi", GEN | READ | I, GEN | WRITE | I, 0, 0, 0 }, + { "NOTi", GEN | READ | I, GEN | WRITE | I, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "SUBPi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "ABSi", GEN | READ | I, GEN | READ | I, 0, 0, 0 }, + { "COM", GEN | READ | I, GEN | WRITE | I, 0, 0, 0 }, + { "IBITi", GEN | READ | I, GEN | REGADDR, 0, 0, 0 }, + { "ADDPi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format7_op[16] = +{ + { "MOVMi", GEN | ADDR, GEN | ADDR, DISP, 0, 0 }, + { "CMPMi", GEN | ADDR, GEN | ADDR, DISP, 0, 0 }, + { "INSSi", GEN | READ | I, GEN | REGADDR, IMM, 0, 0 }, + { "EXTSi", GEN | REGADDR, GEN | WRITE | I, IMM, 0, 0 }, + { "MOVXBW", GEN | READ | B, GEN | WRITE | W, 0, 0, 0 }, + { "MOVZBW", GEN | READ | B, GEN | WRITE | W, 0, 0, 0 }, + { "MOVZiD", GEN | READ | I, GEN | WRITE | D, 0, 0, 0 }, + { "MOVXiD", GEN | READ | I, GEN | WRITE | D, 0, 0, 0 }, + { "MULi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "MEIi", GEN | READ | I, GEN | RMW | I2, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "DEIi", GEN | READ | I, GEN | RMW | I2, 0, 0, 0 }, + { "QUOi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "REMi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "MODi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, + { "DIVi", GEN | READ | I, GEN | RMW | I, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format8_op[] = +{ + { "EXTi", REG, GEN | REGADDR, GEN | WRITE | I, DISP, 0 }, + { "CVTP", REG, GEN | ADDR, GEN | WRITE | D, 0, 0 }, + { "INSi", REG, GEN | READ | I, GEN | REGADDR, DISP, 0 }, + { "CHECKi", REG, GEN | ADDR, GEN | READ | I, 0, 0 }, + { "INDEXi", REG, GEN | READ | I, GEN | READ | I, 0, 0 }, + { "FFSi", GEN | READ | I, GEN | RMW | B, 0, 0, 0 }, + { "MOVSUi", GEN | ADDR, GEN | ADDR, 0, 0, 0 }, + { "MOVUSi", GEN | ADDR, GEN | ADDR, 0, 0, 0 } +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format9_op[] = +{ + { "MOVif", GEN | READ | I, GEN | WRITE | F, 0, 0, 0 }, + { "LFSR", GEN | READ | D, 0, 0, 0, 0 }, + { "MOVLF", GEN | READ | L, GEN | WRITE | F, 0, 0, 0 }, + { "MOVFL", GEN | READ | F, GEN | WRITE | L, 0, 0, 0 }, + { "ROUNDfi", GEN | READ | F, GEN | WRITE | I, 0, 0, 0 }, + { "TRUNCfi", GEN | READ | F, GEN | WRITE | I, 0, 0, 0 }, + { "SFSR", GEN | WRITE | D, 0, 0, 0, 0 }, + { "FLOORfi", GEN | READ | F, GEN | WRITE | I, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format11_op[] = +{ + { "ADDf", GEN | READ | F, GEN | RMW | F, 0, 0, 0 }, + { "MOVf", GEN | READ | F, GEN | WRITE | F, 0, 0, 0 }, + { "CMPf", GEN | READ | F, GEN | READ | F, 0, 0, 0 }, // POLYf + { "Trap (SLAVE)", 0, 0, 0, 0, 0 }, // DOTf + { "SUBf", GEN | READ | F, GEN | RMW | F, 0, 0, 0 }, // SCALBf + { "NEGf", GEN | READ | F, GEN | WRITE | F, 0, 0, 0 }, // LOGBf + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "DIVf", GEN | READ | F, GEN | RMW | F, 0, 0, 0 }, + { "Trap (SLAVE)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "MULf", GEN | READ | F, GEN | RMW | F, 0, 0, 0 }, + { "ABSf", GEN | READ | F, GEN | READ | F, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; +const ns32000_disassembler::NS32000_OPCODE ns32000_disassembler::format14_op[] = +{ + { "RDVAL", GEN | ADDR, 0, 0, 0, 0 }, + { "WRVAL", GEN | ADDR, 0, 0, 0, 0 }, + { "LMR", SHORT, GEN | READ | D, 0, 0, 0 }, + { "SMR", SHORT, GEN | WRITE | D, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, // CINV + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, + { "Trap (UND)", 0, 0, 0, 0, 0 }, +}; + + +const char *const ns32000_disassembler::Format0[] = +{ + "Bcond" +}; +const char *const ns32000_disassembler::Format1[] = +{ + "BSR", "RET", "CXP", "RXP", "RETT", "RETI", "SAVE", "RESTORE", "ENTER", "EXIT", "NOP", "WAIT", "DIA", "FLAG", "SVC", "BPT" +}; +const char *const ns32000_disassembler::Format2[] = +{ + "ADDQi", "CMPQi", "SPRi", "Scondi", "ACBi", "MOVQi", "LPRi" +}; +const char *const ns32000_disassembler::Format3[] = +{ + "CXPD", "Trap (UND)", "BICPSR", "Trap (UND)", "JUMP", "Trap (UND)", "BISPSR", "Trap (UND)", "Trap (UND)", "Trap (UND)", "ADJSPi", "Trap (UND)", "JSR", "Trap (UND)", "CASEi", "Trap (UND)" +}; +const char *const ns32000_disassembler::Format4[] = +{ + "ADDi", "CMPi", "BICi", "Trap (UND)", "ADDCi", "MOVi", "ORi", "Trap (UND)", "SUBi", "ADDR", "ANDi", "Trap (UND)", "SUBCi", "TBITi", "XORi", "Trap (UND)" +}; +const char *const ns32000_disassembler::Format5[] = +{ + "MOVSi", "CMPSi", "SETCFG", "SKPSi", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)" +}; +const char *const ns32000_disassembler::Format6[] = +{ + "ROTi", "ASHi", "CBITi", "CBITIi", "Trap (UND)", "LSHi", "SBITi", "SBITIi", "NEGi", "NOTi", "Trap (UND)", "SUBPi", "ABSi", "COM", "IBITi", "ADDPi" +}; +const char *const ns32000_disassembler::Format7[] = +{ + "MOVMi", "CMPMi", "INSSi", "EXTSi", "MOVXBW", "MOVZBW", "MOVZiD", "MOVXiD", "MULi", "MEIi", "Trap (UND)", "DEIi", "QUOi", "REMi", "MODi", "DIVi" +}; +const char *const ns32000_disassembler::Format8[] = +{ + "EXTi", "CVTP", "INSi", "CHECKi", "INDEXi", "FFSi", "MOVSUi", "MOVUSi" +}; +const char *const ns32000_disassembler::Format9[] = +{ + "MOVif", "LFSR", "MOVLF", "MOVFL", "ROUNDfi", "TRUNCfi", "SFSR", "FLOORfi" +}; +const char *const ns32000_disassembler::Format11[] = +{ + "ADDf", "MOVf", "CMPf", "Trap (SLAVE)", "SUBf", "NEGf", "Trap (UND)", "Trap (UND)", "DIVf", "Trap (SLAVE)", "Trap (UND)", "Trap (UND)", "MULf", "ABSf", "Trap (UND)", "Trap (UND)" +}; +const char *const ns32000_disassembler::Format14[] = +{ + "RDVAL", "WRVAL", "LMR", "SMR", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)", "Trap (UND)" +}; + +// types +const char *const ns32000_disassembler::iType[] = +{ + "B", "W", " ", "D" +}; +const char *const ns32000_disassembler::fType[] = +{ + "L", "F" +}; +const char *const ns32000_disassembler::cType[] = +{ + "Q", "D" +}; + +// index byte sizes +const char *const ns32000_disassembler::indexSize[] = +{ + "B", "W", "D", "Q" +}; + +// short codes +const char *const ns32000_disassembler::cond[] = +{ + "EQ", "NE", "CS", "CC", "HI", "LS", "GT", "LE", "FS", "FC", "LO", "HS", "LT", "GE", "R", "N" +}; +const char *const ns32000_disassembler::areg[] = +{ + "US", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "FP", "SP", "SB", "(reserved)", "(reserved)", "PSR", "INTBASE", "MOD" +}; +const char *const ns32000_disassembler::mreg[] = +{ + "BPR0", "BPR1", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "(reserved)", "MSR", "BCNT", "PTB0", "PTB1", "(reserved)", "EIA" +}; + +// register names +const char *const ns32000_disassembler::R[] = +{ + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7" +}; +const char *const ns32000_disassembler::M[] = +{ + "FP", "SP", "SB" +}; +const char *const ns32000_disassembler::PR[] = +{ + "UPSR", "DCR", "BPC", "DSR", "CAR", "", "", "", "FP", "SP", "SB", "USP", "CFG", "PSR", "INTBASE", "MOD" +}; + +int8_t ns32000_disassembler::short2int(uint8_t val) +{ + return (val & 0x08) ? val | 0xf0 : val; +} + +std::string ns32000_disassembler::mnemonic_index(std::string form, std::string itype, std::string ftype) +{ + if (itype.size() && form.find('i') != std::string::npos) + form.replace(form.find('i'), 1, itype); + if (ftype.size() && form.find('f') != std::string::npos) + form.replace(form.find('f'), 1, ftype); + return form; +} + +uint8_t ns32000_disassembler::opcode_format(uint8_t byte) +{ + switch (byte & 0x0f) + { + case 0x0a: return 0; + case 0x02: return 1; + case 0x0c: + case 0x0d: + case 0x0f: + if ((byte & 0x70) != 0x70) + return 2; + else + return 3; + } + + if ((byte & 0x03) != 0x02) return 4; + + switch (byte) + { + case 0x0e: return 5; + case 0x4e: return 6; + case 0xce: return 7; + case 0x2e: + case 0x6e: + case 0xae: + case 0xee: return 8; + case 0x3e: return 9; + case 0x7e: return 10; + case 0xbe: return 11; + case 0xfe: return 12; + case 0x9e: return 13; + case 0x1e: return 14; + } + + return 99; /* unknown format */ +} + +inline std::string ns32000_disassembler::get_option_list(uint8_t cfg) +{ + std::string option_list; + + option_list.append("["); + if (BIT(cfg, 0)) option_list.append("I,"); + if (BIT(cfg, 1)) option_list.append("F,"); + if (BIT(cfg, 2)) option_list.append("M,"); + if (BIT(cfg, 3)) option_list.append("C,"); + if (option_list.back() == ',') option_list.pop_back(); + option_list.append("]"); + + return option_list; +} + +inline std::string ns32000_disassembler::get_options(uint8_t opts) +{ + std::string options; + + options.append(" "); + if ((opts & 0x02) == 0x02) options.append("B,"); + if ((opts & 0x04) == 0x04) options.append("W,"); + if ((opts & 0x0c) == 0x0c) options.append("U,"); + if (options.back() == ',') options.pop_back(); + + return options; +} + +inline int32_t ns32000_disassembler::get_disp(offs_t &pc, const data_buffer &opcodes) +{ + /* displacement can be upto 3 bytes */ + uint32_t disp = bitswap<32>(opcodes.r32(pc), 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24); + + switch ((disp >> 29) & 0x07) + { + case 0: case 1: /* 7 bit positive */ + disp = (disp >> 24); + pc += 1; + break; + + case 2: case 3: /* 7 bit negative */ + disp = (disp >> 24) | 0xffffff80; + pc += 1; + break; + + case 4: /* 14 bit positive */ + disp = (disp >> 16) & 0x3fff; + pc += 2; + break; + + case 5: /* 14 bit negative */ + disp = (disp >> 16) | 0xffffc000; + pc += 2; + break; + + case 6: /* 30 bit positive */ + disp = disp & 0x3fffffff; + pc += 4; + break; + + case 7: /* 30 bit negative */ + pc += 4; + break; + } + + return disp; +} + +inline std::string ns32000_disassembler::get_reg_list(offs_t &pc, const data_buffer &opcodes, bool reverse) +{ + std::string reg_list; + + uint8_t byte = opcodes.r8(pc++); + + reg_list.append("["); + for (int i = 0; i < 8; i++) + { + if (BIT(byte, i)) reg_list.append(R[reverse ? (~i & 7) : (i & 7)]).append(","); + } + if (reg_list.back() == ',') reg_list.pop_back(); + reg_list.append("]"); + + return reg_list; +} + +void ns32000_disassembler::stream_gen(std::ostream &stream, u8 gen_addr, u8 op_len, offs_t &pc, const data_buffer &opcodes) +{ + uint8_t index_byte; + int32_t disp1, disp2; + + switch (gen_addr) + { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: + /* Register */ + util::stream_format(stream, "%s", R[gen_addr & 0x07]); + break; + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: + /* Register Relative */ + disp1 = get_disp(pc, opcodes); + util::stream_format(stream, "%+d(%s)", disp1, R[gen_addr & 0x07]); + break; + case 0x10: case 0x11: case 0x12: + /* Memory Relative */ + disp1 = get_disp(pc, opcodes); + disp2 = get_disp(pc, opcodes); + util::stream_format(stream, "#X%02X(#X%02X(%s))", disp2, disp1, M[gen_addr & 0x03]); + break; + case 0x13: + /* Reserved */ + util::stream_format(stream, "(reserved)"); + break; + case 0x14: + /* Immediate */ + disp1 = bitswap<32>(opcodes.r32(pc), 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24); + util::stream_format(stream, "#X%02X", (op_len == 0 ? (disp1 >> 24) : (op_len == 1 ? (disp1 >> 16) : disp1))); + pc += op_len + 1; + break; + case 0x15: + /* Absolute */ + disp1 = get_disp(pc, opcodes); + util::stream_format(stream, "@#X%02X", disp1); + break; + case 0x16: + /* External */ + disp1 = get_disp(pc, opcodes); + disp2 = get_disp(pc, opcodes); + util::stream_format(stream, "EXT(%d)+%d", disp1, disp2); + break; + case 0x17: + /* Top Of Stack */ + util::stream_format(stream, "TOS"); + break; + case 0x18: case 0x19: case 0x1a: + /* Memory Space */ + disp1 = get_disp(pc, opcodes); + util::stream_format(stream, "#X%02X(%s)", disp1, M[gen_addr & 0x03]); + break; + case 0x1b: + /* Memory Space */ + disp1 = get_disp(pc, opcodes); + util::stream_format(stream, "#X%06X", m_base_pc + disp1); + break; + case 0x1c: + /* Scaled Index */ + index_byte = opcodes.r8(pc++); + stream_gen(stream, index_byte >> 3, op_len, pc, opcodes); + util::stream_format(stream, "[%s:%c]", R[index_byte & 0x07], indexSize[gen_addr & 0x03]); + break; + } +} + +offs_t ns32000_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + uint32_t flags = SUPPORTED; + + uint32_t opcode; + std::string mnemonic; + uint8_t temp8; + + /* opcode can be upto 3 bytes */ + opcode = opcodes.r32(pc); + m_base_pc = pc; + + switch (opcode_format(opcode)) + { + case 0: /* Format 0 */ + pc += 1; + util::stream_format(stream, "%-8s #X%06X", std::string("B").append(cond[Format0cond(opcode)]), m_base_pc + get_disp(pc, opcodes)); + break; + + case 0x01: /* Format 1 */ + pc += 1; + switch (Format1op(opcode)) + { + case 0x00: + util::stream_format(stream, "%-8s #X%06X", Format1[Format1op(opcode)], m_base_pc + get_disp(pc, opcodes)); + break; + case 0x01: + util::stream_format(stream, "%-8s #X%02X", Format1[Format1op(opcode)], get_disp(pc, opcodes) & 0xffffff); + break; + case 0x02: + util::stream_format(stream, "%-8s EXT(%d)", Format1[Format1op(opcode)], get_disp(pc, opcodes)); + break; + case 0x03: case 0x04: + util::stream_format(stream, "%-8s #X%02X", Format1[Format1op(opcode)], get_disp(pc, opcodes) & 0xffffff); + break; + case 0x05: + util::stream_format(stream, "%-8s", Format1[Format1op(opcode)]); + break; + case 0x06: + util::stream_format(stream, "%-8s %s", Format1[Format1op(opcode)], get_reg_list(pc, opcodes, false)); + break; + case 0x07: case 0x09: + util::stream_format(stream, "%-8s %s", Format1[Format1op(opcode)], get_reg_list(pc, opcodes, true)); + break; + case 0x08: + util::stream_format(stream, "%-8s %s, %d", Format1[Format1op(opcode)], get_reg_list(pc, opcodes, false), get_disp(pc, opcodes)); + break; + case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: + util::stream_format(stream, "%-8s", Format1[Format1op(opcode)]); + break; + } + break; + + case 0x02: /* Format 2 */ + pc += 2; + mnemonic = mnemonic_index(Format2[Format2op(opcode)], iType[Format2i(opcode)], ""); + switch (Format2op(opcode)) + { + case 0x00: case 0x01: case 0x05: + util::stream_format(stream, "%-8s %+d, ", mnemonic, short2int(Format2short(opcode))); + stream_gen(stream, Format2gen(opcode), Format2i(opcode), pc, opcodes); + break; + case 0x02: case 0x06: + util::stream_format(stream, "%-8s %s, ", mnemonic, areg[Format2short(opcode)]); + stream_gen(stream, Format2gen(opcode), Format2i(opcode), pc, opcodes); + break; + case 0x03: + util::stream_format(stream, "%-8s ", std::string("S").append(cond[Format2short(opcode)]).append(iType[Format2i(opcode)])); + stream_gen(stream, Format2gen(opcode), Format2i(opcode), pc, opcodes); + break; + case 0x04: + util::stream_format(stream, "%-8s %+d, ", mnemonic, short2int(Format2short(opcode))); + stream_gen(stream, Format2gen(opcode), Format2i(opcode), pc, opcodes); + util::stream_format(stream, ", #X%06x", m_base_pc + get_disp(pc, opcodes)); + break; + } + break; + + case 0x03: /* Format 3 */ + pc += 2; + mnemonic = mnemonic_index(Format3[Format3op(opcode)], iType[Format3i(opcode)], ""); + switch (Format3op(opcode)) + { + case 0x00: case 0x02: case 0x04: case 0x06: case 0x0a: case 0x0c: case 0x0e: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format3gen(opcode), Format3i(opcode), pc, opcodes); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + case 0x04: /* Format 4 */ + pc += 2; + mnemonic = mnemonic_index(Format4[Format4op(opcode)], iType[Format4i(opcode)], ""); + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format4gen1(opcode), Format4i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format4gen2(opcode), Format4i(opcode), pc, opcodes); + break; + + case 0x05: /* Format 5 */ + pc += 3; + mnemonic = mnemonic_index(Format5[Format5op(opcode)], iType[Format5i(opcode)], ""); + switch ((opcode >> 10) & 0x0f) + { + case 0x00: case 0x01: case 0x03: + if (Format5short(opcode) & 0x01) + util::stream_format(stream, "%-8s %s", std::string(Format5[Format5op(opcode)]).append("T"), get_options(Format5short(opcode))); + else + util::stream_format(stream, "%-8s %s", mnemonic, get_options(Format5short(opcode))); + break; + case 0x02: + util::stream_format(stream, "%-8s %s", Format5[Format5op(opcode)], get_option_list(Format5short(opcode))); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + case 0x06: /* Format 6 */ + pc += 3; + mnemonic = mnemonic_index(Format6[Format6op(opcode)], iType[Format6i(opcode)], ""); + switch ((opcode >> 10) & 0x0f) + { + case 0x00: case 0x01: case 0x02: case 0x03: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format6gen1(opcode), Format6i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format6gen2(opcode), Format6i(opcode), pc, opcodes); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + case 0x07: /* Format 7 */ + pc += 3; + mnemonic = mnemonic_index(Format7[Format7op(opcode)], iType[Format7i(opcode)], ""); + switch (Format7op(opcode)) + { + case 0x00: case 0x01: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format7gen1(opcode), Format7i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format7gen2(opcode), Format7i(opcode), pc, opcodes); + util::stream_format(stream, ", %d", get_disp(pc, opcodes)); + break; + case 0x02: case 0x03: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format7gen1(opcode), Format7i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format7gen2(opcode), Format7i(opcode), pc, opcodes); + temp8 = opcodes.r8(pc++); + util::stream_format(stream, ", %d, %d", temp8 >> 5, temp8 + 1); + break; + case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format7gen1(opcode), Format7i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format7gen2(opcode), Format7i(opcode), pc, opcodes); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + case 0x08: /* Format 8 */ + pc += 3; + mnemonic = mnemonic_index(Format8[Format8op(opcode)], iType[Format8i(opcode)], ""); + switch (Format8op(opcode)) + { + case 0x00: case 0x02: + util::stream_format(stream, "%-8s %s, ", mnemonic, R[Format8reg(opcode)]); + stream_gen(stream, Format8gen2(opcode), Format8i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format8gen1(opcode), Format8i(opcode), pc, opcodes); + util::stream_format(stream, ", %d", get_disp(pc, opcodes)); + break; + case 0x01: case 0x03: case 0x04: + util::stream_format(stream, "%-8s %s, ", mnemonic, R[Format8reg(opcode)]); + stream_gen(stream, Format8gen2(opcode), Format8i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format8gen1(opcode), Format8i(opcode), pc, opcodes); + break; + case 0x05: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format8gen2(opcode), Format8i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format8gen1(opcode), Format8i(opcode), pc, opcodes); + break; + case 0x06: + if (Format8reg(opcode) == 0x01) + util::stream_format(stream, "%-8s ", std::string(Format8[Format8op(opcode)]).append(iType[Format8i(opcode)])); + else + util::stream_format(stream, "%-8s ", std::string(Format8[Format8op(opcode) + 1]).append(iType[Format8i(opcode)])); + stream_gen(stream, Format8gen2(opcode), Format8i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format8gen1(opcode), Format8i(opcode), pc, opcodes); + break; + } + break; + + case 0x09: /* Format 9 */ + pc += 3; + mnemonic = mnemonic_index(Format9[Format9op(opcode)], iType[Format9i(opcode)], fType[Format9f(opcode)]); + switch (Format9op(opcode)) + { + case 0x00: case 0x02: case 0x04: case 0x05: case 0x07: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format9gen1(opcode), Format9i(opcode), pc, opcodes); + util::stream_format(stream, ", "); + stream_gen(stream, Format9gen2(opcode), Format9i(opcode), pc, opcodes); + break; + case 0x01: case 0x06: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format9gen1(opcode), Format9i(opcode), pc, opcodes); + break; + } + + case 0x0b: /* Format 11 */ + pc += 3; + mnemonic = mnemonic_index(Format11[Format11op(opcode)], nullptr, fType[Format11f(opcode)]); + switch (Format11op(opcode)) + { + case 0x00: case 0x01: case 0x02: case 0x04: case 0x05 : case 0x08: case 0x0c : case 0x0d: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format11gen1(opcode), Format11f(opcode), pc, opcodes); + util::stream_format(stream, ","); + stream_gen(stream, Format11gen2(opcode), Format11f(opcode), pc, opcodes); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + case 0x0a: /* Format 10 */ + case 0x0c: /* Format 12 */ + case 0x0d: /* Format 13 */ + pc += 3; + util::stream_format(stream, "Trap (UND)"); + break; + + case 0x0e: /* Format 14 */ + pc += 3; + mnemonic = mnemonic_index(Format14[Format14op(opcode)], iType[Format14i(opcode)], ""); + switch (Format14op(opcode)) + { + case 0x00: case 0x01: + util::stream_format(stream, "%-8s ", mnemonic); + stream_gen(stream, Format14gen1(opcode), Format14i(opcode), pc, opcodes); + break; + case 0x02: case 0x03: + util::stream_format(stream, "%-8s %s, ", mnemonic, mreg[Format14short(opcode)]); + stream_gen(stream, Format14gen1(opcode), Format14i(opcode), pc, opcodes); + break; + default: /* Trap */ + util::stream_format(stream, "%-8s ", mnemonic); + break; + } + break; + + default: + pc += 1; + util::stream_format(stream, "unknown instruction format"); + break; + } + + return (pc - m_base_pc) | flags; +} diff --git a/src/devices/cpu/ns32000/ns32000dasm.h b/src/devices/cpu/ns32000/ns32000dasm.h new file mode 100644 index 00000000000..e9c970c9829 --- /dev/null +++ b/src/devices/cpu/ns32000/ns32000dasm.h @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/***************************************************************************** +* +* ns32000dasm.cpp +* +* NS32000 CPU Disassembly +* +*****************************************************************************/ + +#ifndef MAME_CPU_NS32000_NS32000DASM_H +#define MAME_CPU_NS32000_NS32000DASM_H + +#pragma once + +class ns32000_disassembler : public util::disasm_interface +{ +public: + + ns32000_disassembler() = default; + virtual ~ns32000_disassembler() = default; + + virtual u32 opcode_alignment() const override { return 1; } + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + /* implied operand attributes */ + enum + { + REG = 16, + QUICK, + SHORT, + IMM, + DISP, + GEN, + OPTIONS, + }; + /* access classes */ + enum + { + READ = 8, + WRITE, + RMW, + ADDR, + REGADDR + }; + /* length attributes */ + enum + { + B = 0, + W = 1, + D = 3, + I, + I2, + F, + L + }; + struct NS32000_OPCODE { + const char *mnemonic; + u32 operand1; + u32 operand; + u32 operand3; + u32 operand4; + offs_t dasm_flags; + }; + + static const NS32000_OPCODE format0_op[1]; + static const NS32000_OPCODE format1_op[16]; + static const NS32000_OPCODE format2_op[8]; + static const NS32000_OPCODE format3_op[16]; + static const NS32000_OPCODE format4_op[16]; + static const NS32000_OPCODE format5_op[16]; + static const NS32000_OPCODE format6_op[16]; + static const NS32000_OPCODE format7_op[16]; + static const NS32000_OPCODE format8_op[16]; + static const NS32000_OPCODE format9_op[16]; + static const NS32000_OPCODE format11_op[16]; + static const NS32000_OPCODE format14_op[16]; + + + static char const *const Format0[]; + static char const *const Format1[]; + static char const *const Format2[]; + static char const *const Format3[]; + static char const *const Format4[]; + static char const *const Format5[]; + static char const *const Format6[]; + static char const *const Format7[]; + static char const *const Format8[]; + static char const *const Format9[]; + static char const *const Format11[]; + static char const *const Format14[]; + static char const *const iType[]; + static char const *const fType[]; + static char const *const cType[]; + static char const *const indexSize[]; + static char const *const cond[]; + static char const *const areg[]; + static char const *const mreg[]; + static char const *const R[]; + static char const *const M[]; + static char const *const PR[]; + + std::string mnemonic_index(std::string form, std::string itype, std::string ftype); + uint8_t opcode_format(uint8_t byte); + int8_t short2int(uint8_t val); + static inline int32_t get_disp(offs_t &pc, const data_buffer &opcodes); + static inline std::string get_option_list(uint8_t cfg); + static inline std::string get_options(uint8_t opts); + static inline std::string get_reg_list(offs_t &pc, const data_buffer &opcodes, bool reverse); + + void stream_gen(std::ostream &stream, u8 gen_addr, u8 op_len, offs_t &pc, const data_buffer &opcodes); + + u32 m_base_pc; + unsigned m_pcmask; +}; + +#endif diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 81b9f448140..c668fcba82d 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -99,6 +99,7 @@ using util::BIT; #include "cpu/mn10200/mn102dis.h" #include "cpu/nanoprocessor/nanoprocessor_dasm.h" #include "cpu/nec/necdasm.h" +#include "cpu/ns32000/ns32000dasm.h" #include "cpu/nuon/nuondasm.h" #include "cpu/patinhofeio/patinho_feio_dasm.h" #include "cpu/pdp1/pdp1dasm.h" @@ -420,6 +421,7 @@ static const dasm_table_entry dasm_table[] = { "mn10200", le, 0, []() -> util::disasm_interface * { return new mn10200_disassembler; } }, { "nanoprocessor", le, 0, []() -> util::disasm_interface * { return new hp_nanoprocessor_disassembler; } }, { "nec", le, 0, []() -> util::disasm_interface * { return new nec_disassembler; } }, + { "ns32000", le, 0, []() -> util::disasm_interface * { return new ns32000_disassembler; } }, { "nuon", be, 0, []() -> util::disasm_interface * { return new nuon_disassembler; } }, { "nsc8105", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(8105); } }, { "patinho_feio", le, 0, []() -> util::disasm_interface * { return new patinho_feio_disassembler; } }, -- cgit v1.2.3-70-g09d2