summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/cr16b/cr16bdasm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/cr16b/cr16bdasm.cpp')
-rw-r--r--src/devices/cpu/cr16b/cr16bdasm.cpp1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/devices/cpu/cr16b/cr16bdasm.cpp b/src/devices/cpu/cr16b/cr16bdasm.cpp
new file mode 100644
index 00000000000..e52ab7e02f2
--- /dev/null
+++ b/src/devices/cpu/cr16b/cr16bdasm.cpp
@@ -0,0 +1,1051 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ National Semiconductor CompactRISC CR16B disassembler
+
+ Core architecture versions supported by this disassembler:
+ * CR16A: 17-bit PC
+ * CR16B: 21-bit PC using large memory model, enhanced instruction set
+
+ CR16C has a 24-bit PC and expands the instruction set still further.
+ Its instruction encoding is entirely different and is therefore not
+ supported here.
+
+ It should be noted that the PC is guaranteed to be always aligned to
+ even addresses. To this end, whenever the PC is stored in or loaded
+ from a register or register pair or a stack, the bits are shifted
+ right or left by one.
+
+***************************************************************************/
+
+#include "util/disasmintf.h"
+#include "cr16bdasm.h"
+
+#include "util/strformat.h"
+
+using osd::u32;
+using util::BIT;
+using offs_t = u32;
+
+cr16b_disassembler::cr16b_disassembler(cr16_arch arch)
+ : util::disasm_interface()
+ , m_arch(arch)
+{
+}
+
+cr16a_disassembler::cr16a_disassembler()
+ : cr16b_disassembler(cr16_arch::CR16A)
+{
+}
+
+cr16b_disassembler::cr16b_disassembler()
+ : cr16b_disassembler(cr16_arch::CR16B)
+{
+}
+
+u32 cr16b_disassembler::opcode_alignment() const
+{
+ return 2;
+}
+
+// Condition codes
+const char *const cr16b_disassembler::s_cc[14] =
+{
+ "eq", "ne",
+ "cs", "cc",
+ "hi", "ls",
+ "gt", "le",
+ "fs", "fc",
+ "lo", "hs",
+ "lt", "ge"
+};
+
+
+void cr16b_disassembler::format_reg(std::ostream &stream, u8 reg)
+{
+ if (reg == 15)
+ stream << "sp";
+ else if (reg == 14)
+ stream << "ra";
+ else if (reg == 13 && m_arch != cr16_arch::CR16A)
+ stream << "era"; // R13 in SMM
+ else
+ util::stream_format(stream, "r%d", reg);
+}
+
+void cr16b_disassembler::format_rpair(std::ostream &stream, u8 reg)
+{
+ if (reg == 13 && m_arch != cr16_arch::CR16A)
+ stream << "(ra,era)";
+ else
+ util::stream_format(stream, "(r%d,r%d)", reg + 1, reg);
+}
+
+void cr16a_disassembler::format_rproc(std::ostream &stream, u8 reg)
+{
+ switch (reg)
+ {
+ case 1:
+ stream << "psr";
+ break;
+
+ case 3:
+ stream << "intbase";
+ break;
+
+ case 11:
+ stream << "isp";
+ break;
+
+ default:
+ stream << "res";
+ break;
+ }
+}
+
+void cr16b_disassembler::format_rproc(std::ostream &stream, u8 reg)
+{
+ switch (reg)
+ {
+ case 1:
+ stream << "psr";
+ break;
+
+ case 3:
+ stream << "intbasel";
+ break;
+
+ case 4:
+ stream << "intbaseh";
+ break;
+
+ case 5:
+ stream << "cfg";
+ break;
+
+ case 7:
+ stream << "dsr";
+ break;
+
+ case 9:
+ stream << "dcr";
+ break;
+
+ case 11:
+ stream << "isp";
+ break;
+
+ case 13:
+ stream << "carl";
+ break;
+
+ case 14:
+ stream << "carh";
+ break;
+
+ default:
+ stream << "res";
+ break;
+ }
+}
+
+void cr16b_disassembler::format_short_imm(std::ostream &stream, u8 imm)
+{
+ // 5-bit short immediate value (0 to 15, -16, -14 to -1)
+ if (imm == 0)
+ stream << "$0";
+ else if (imm >= 0x10)
+ util::stream_format(stream, "$-0x%01X", 0x10 - (imm & 0x0f));
+ else
+ util::stream_format(stream, "$0x%01X", imm);
+}
+
+void cr16b_disassembler::format_short_imm_unsigned(std::ostream &stream, u8 imm, bool i)
+{
+ if (imm == 0)
+ stream << "$0";
+ else if (i)
+ util::stream_format(stream, "$0x%04X", (imm >= 0x10) ? 0xfff0 | (imm & 0x0f) : imm);
+ else
+ util::stream_format(stream, "$0x%02X", (imm >= 0x10) ? 0xf0 | (imm & 0x0f) : imm);
+}
+
+void cr16b_disassembler::format_short_imm_decimal(std::ostream &stream, u8 imm)
+{
+ if (imm >= 0x10)
+ util::stream_format(stream, "$-%d", 0x10 - (imm & 0x0f));
+ else
+ util::stream_format(stream, "$%d", imm);
+}
+
+void cr16b_disassembler::format_medium_imm(std::ostream &stream, u16 imm, bool i)
+{
+ if (i)
+ util::stream_format(stream, "$0x%04X", imm);
+ else
+ util::stream_format(stream, "$0x%02X", imm & 0xff);
+}
+
+void cr16b_disassembler::format_medium_imm_decimal(std::ostream &stream, u16 imm)
+{
+ util::stream_format(stream, "$%d", s16(imm));
+}
+
+void cr16b_disassembler::format_imm21(std::ostream &stream, u32 imm)
+{
+ util::stream_format(stream, "$0x%06X", imm);
+}
+
+void cr16b_disassembler::format_disp5(std::ostream &stream, u8 disp)
+{
+ // 5-bit short displacement (0 to 15, 16 to 30 even)
+ if (disp == 0)
+ stream << "0";
+ else
+ util::stream_format(stream, "0x%X", disp);
+}
+
+void cr16b_disassembler::format_disp16(std::ostream &stream, u16 disp)
+{
+ // 16-bit displacement (0 to 64K-1)
+ util::stream_format(stream, "0x%X", disp);
+}
+
+void cr16b_disassembler::format_disp18(std::ostream &stream, u32 disp)
+{
+ // 18-bit medium displacement (-128K to 128K-1 or 0 to 256K-1, result truncated to 18 bits)
+ if (disp == 0)
+ stream << "0";
+ else if (disp >= 0x20000)
+ util::stream_format(stream, "-0x%X", 0x40000 - disp);
+ else
+ util::stream_format(stream, "0x%X", disp);
+}
+
+void cr16b_disassembler::format_abs18(std::ostream &stream, u32 addr)
+{
+ // 18-bit absolute (any memory location within first 256K)
+ util::stream_format(stream, "0x%05X", addr);
+}
+
+void cr16b_disassembler::format_pc_disp5(std::ostream &stream, offs_t pc, u8 disp)
+{
+ if (m_arch == cr16_arch::CR16A)
+ util::stream_format(stream, "0x%05X", (disp >= 0x10 ? pc + 0x20 - disp : pc + disp) & 0x1ffff); // SMM
+ else
+ util::stream_format(stream, "0x%06X", (disp >= 0x10 ? pc + 0x20 - disp : pc + disp) & 0x1fffff); // LMM
+}
+
+void cr16b_disassembler::format_pc_disp9(std::ostream &stream, offs_t pc, u16 disp)
+{
+ if (m_arch == cr16_arch::CR16A)
+ util::stream_format(stream, "0x%05X", (disp >= 0x100 ? pc + 0x200 - disp : pc + disp) & 0x1ffff); // SMM
+ else
+ util::stream_format(stream, "0x%06X", (disp >= 0x100 ? pc + 0x200 - disp : pc + disp) & 0x1fffff); // LMM
+}
+
+void cr16b_disassembler::format_pc_disp17(std::ostream &stream, offs_t pc, u32 disp)
+{
+ util::stream_format(stream, "0x%05X", (disp >= 0x10000 ? pc + 0x20000 - disp : pc + disp) & 0x1ffff);
+}
+
+void cr16b_disassembler::format_pc_disp21(std::ostream &stream, offs_t pc, u32 disp)
+{
+ util::stream_format(stream, "0x%06X", (pc + ((disp & 0x0ffffe) | (disp & 0x000001) << 20)) & 0x1fffff);
+}
+
+void cr16b_disassembler::format_excp_vector(std::ostream &stream, u8 vec)
+{
+ switch (vec)
+ {
+ case 0x05: // Supervisor call
+ stream << "svc";
+ break;
+
+ case 0x06: // Division by zero
+ stream << "dvz";
+ break;
+
+ case 0x07: // Flag
+ stream << "flg";
+ break;
+
+ case 0x08: // Breakpoint
+ stream << "bpt";
+ break;
+
+ case 0x0a: // Undefined instruction
+ stream << "und";
+ break;
+
+ case 0x0e: // Debug
+ stream << (m_arch != cr16_arch::CR16A ? "dbg" : "und ; reserved");
+ break;
+
+ default:
+ stream << "und ; reserved";
+ break;
+ }
+}
+
+offs_t cr16b_disassembler::disassemble(std::ostream &stream, offs_t pc, const cr16b_disassembler::data_buffer &opcodes, const cr16b_disassembler::data_buffer &params)
+{
+ u16 opcode = opcodes.r16(pc);
+
+ if (BIT(opcode, 15))
+ {
+ // Load and store group (excluding LOADM, STORM)
+ if (BIT(opcode, 14))
+ {
+ util::stream_format(stream, "stor%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ }
+ else
+ util::stream_format(stream, "load%c ", BIT(opcode, 13) ? 'w' : 'b');
+
+ switch (opcode & 0x1801)
+ {
+ case 0x1001:
+ format_disp18(stream, u32(opcode & 0x0600) << 7 | opcodes.r16(pc + 2));
+ stream << "(";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ")";
+ if (!BIT(opcode, 14))
+ {
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ }
+ return 4 | SUPPORTED;
+
+ case 0x1801:
+ if ((opcode & 0x001e) == 0x001e)
+ format_abs18(stream, u32(opcode & 0x0600) << 7 | opcodes.r16(pc + 2));
+ else
+ {
+ format_disp18(stream, u32(opcode & 0x0600) << 7 | opcodes.r16(pc + 2));
+ format_rpair(stream, (opcode & 0x001e) >> 1);
+ }
+ if (!BIT(opcode, 14))
+ {
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ }
+ return 4 | SUPPORTED;
+
+ default:
+ format_disp5(stream, (opcode & 0x1e00) >> 8 | (opcode & 0x0001));
+ stream << "(";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ")";
+ if (!BIT(opcode, 14))
+ {
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ }
+ return 2 | SUPPORTED;
+ }
+ }
+ else switch (opcode & 0x7e01)
+ {
+ case 0x0000: case 0x0001: case 0x2000: case 0x2001:
+ util::stream_format(stream, "add%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x0200: case 0x0201: case 0x2200: case 0x2201:
+ if (opcode == 0x0200) // NOP = ADDU $0, R0
+ stream << "nop";
+ else
+ {
+ util::stream_format(stream, "addu%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+ }
+ return 2 | SUPPORTED;
+
+ case 0x0400: case 0x0401: case 0x2400: case 0x2401:
+ case 0x4401: case 0x6401:
+ // Bit manipulation and store immediate (memory operand)
+ if (m_arch == cr16_arch::CR16A)
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+ switch (opcode & 0x00c0)
+ {
+ case 0x0000:
+ util::stream_format(stream, "cbit%c $%d", BIT(opcode, 13) ? 'w' : 'b', (opcode & 0x001e) >> 1);
+ break;
+
+ case 0x0040:
+ util::stream_format(stream, "sbit%c $%d", BIT(opcode, 13) ? 'w' : 'b', (opcode & 0x001e) >> 1);
+ break;
+
+ case 0x0080:
+ util::stream_format(stream, "tbit%c $%d", BIT(opcode, 13) ? 'w' : 'b', (opcode & 0x001e) >> 1);
+ break;
+
+ case 0x00c0:
+ util::stream_format(stream, "stor%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_short_imm(stream, (opcode & 0x001e) >> 1); // unsigned 4-bit value
+ break;
+ }
+ if (BIT(opcode, 14))
+ {
+ stream << ",0(";
+ format_reg(stream, (opcode & 0x0120) >> 5);
+ stream << ")";
+ return 2 | SUPPORTED;
+ }
+ else
+ {
+ stream << ", ";
+ if (BIT(opcode, 0))
+ {
+ format_disp16(stream, opcodes.r16(pc + 2));
+ stream << "(";
+ format_reg(stream, (opcode & 0x0120) >> 5);
+ stream << ")";
+ }
+ else
+ format_abs18(stream, u32(opcode & 0x0100) << 9 | u32(opcode & 0x0020) << 11 | opcodes.r16(pc + 2));
+ return 4 | SUPPORTED;
+ }
+
+ case 0x0600: case 0x0601: case 0x2600: case 0x2601:
+ util::stream_format(stream, "mul%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x0800: case 0x0801: case 0x2800: case 0x2801:
+ util::stream_format(stream, "ashu%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm_decimal(stream, opcodes.r16(pc + 2));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_decimal(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x0a00: case 0x0a01: case 0x2a00: case 0x2a01:
+ util::stream_format(stream, "lsh%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm_decimal(stream, opcodes.r16(pc + 2));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_decimal(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x0c00: case 0x0c01: case 0x2c00: case 0x2c01:
+ util::stream_format(stream, "xor%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_unsigned(stream, opcode & 0x001f, BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x0e00: case 0x0e01: case 0x2e00: case 0x2e01:
+ util::stream_format(stream, "cmp%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1000: case 0x1001: case 0x3000: case 0x3001:
+ util::stream_format(stream, "and%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_unsigned(stream, opcode & 0x001f, BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1200: case 0x1201: case 0x3200: case 0x3201:
+ util::stream_format(stream, "addc%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1400:
+ // Conditional or unconditional branch to small address
+ if ((opcode & 0x000e) == 0x000e || (opcode & 0x01e0) != 0x01e0)
+ {
+ if ((opcode & 0x01e0) == 0x01c0)
+ stream << "br ";
+ else
+ util::stream_format(stream, "b%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_pc_disp17(stream, pc, u32(opcode & 0x0010) << 12 | opcodes.r16(pc + 2));
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1401: case 0x3401:
+ // Compare and branch group
+ if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else
+ {
+ util::stream_format(stream, "b%s%c%c ", BIT(opcode, 7) ? "ne" : "eq", BIT(opcode, 6) ? '1' : '0', BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x0120) >> 5);
+ stream << ", ";
+ format_pc_disp5(stream, pc, opcode & 0x001e);
+ }
+ return 2 | SUPPORTED;
+
+ case 0x1600:
+ // Jump and link to large address
+ if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else
+ {
+ stream << "jal ";
+ format_rpair(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ format_rpair(stream, (opcode & 0x001e) >> 1);
+ }
+ return 2 | SUPPORTED;
+
+ case 0x1601:
+ if ((opcode & 0x01e0) != 0x01e0 && m_arch != cr16_arch::CR16A)
+ {
+ if ((opcode & 0x01e0) == 0x01c0)
+ stream << "jump ";
+ else
+ util::stream_format(stream, "j%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_rpair(stream, (opcode & 0x001e) >> 1);
+ return 2 | (opcode == 0x17db ? STEP_OUT : 0) | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1800: case 0x1801: case 0x3800: case 0x3801:
+ util::stream_format(stream, "mov%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_unsigned(stream, opcode & 0x001f, BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1a00: case 0x1a01: case 0x3a00: case 0x3a01:
+ util::stream_format(stream, "subc%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1c00: case 0x1c01: case 0x3c00: case 0x3c01:
+ util::stream_format(stream, "or%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_unsigned(stream, opcode & 0x001f, BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x1e00: case 0x1e01: case 0x3e00: case 0x3e01:
+ util::stream_format(stream, "sub%c ", BIT(opcode, 13) ? 'w' : 'b');
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm(stream, opcodes.r16(pc + 2), BIT(opcode, 13));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x3400:
+ // Branch and link to small address
+ if ((opcode & 0x000e) == 0x000e)
+ {
+ stream << "bal ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ format_pc_disp17(stream, pc, u32(opcode & 0x0010) << 12 | opcodes.r16(pc + 2));
+ return 4 | STEP_OVER | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x3600: case 0x3601:
+ // TBIT imm, reg only exists as a word operation
+ stream << "tbit ";
+ if ((opcode & 0x001f) == 0x0011)
+ {
+ format_medium_imm_decimal(stream, opcodes.r16(pc + 2));
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ format_short_imm_decimal(stream, opcode & 0x001f);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+ }
+
+ case 0x4000: case 0x4200: case 0x4400: case 0x4600:
+ case 0x4800: case 0x4a00: case 0x4c00: case 0x4e00:
+ case 0x5000: case 0x5200: case 0x5400: case 0x5600:
+ case 0x5800: case 0x5a00: case 0x5c00: case 0x5e00:
+ // Conditional or unconditional branch with 9-bit displacement
+ if ((opcode & 0x01e0) != 0x01e0)
+ {
+ if ((opcode & 0x01e0) == 0x01c0)
+ stream << "br ";
+ else
+ util::stream_format(stream, "b%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_pc_disp9(stream, pc, (opcode & 0x1e00) >> 4 | (opcode & 0x001e));
+ }
+ else
+ stream << "res";
+ return 2 | SUPPORTED;
+
+ case 0x4001: case 0x6001:
+ util::stream_format(stream, "add%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4201: case 0x6201:
+ util::stream_format(stream, "addu%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4601: case 0x6601:
+ util::stream_format(stream, "mul%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4801: case 0x6801:
+ util::stream_format(stream, "ashu%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4a01: case 0x6a01:
+ util::stream_format(stream, "lsh%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4c01: case 0x6c01:
+ util::stream_format(stream, "xor%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x4e01: case 0x6e01:
+ util::stream_format(stream, "cmp%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5001: case 0x7001:
+ util::stream_format(stream, "and%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5201: case 0x7201:
+ util::stream_format(stream, "addc%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5401:
+ if ((opcode & 0x01e0) != 0x01e0)
+ {
+ if ((opcode & 0x01e0) == 0x01c0)
+ stream << "jump ";
+ else
+ util::stream_format(stream, "j%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ return 2 | (opcode == 0x55dd ? STEP_OUT : 0) | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x5801: case 0x7801:
+ util::stream_format(stream, "mov%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5a01: case 0x7a01:
+ util::stream_format(stream, "subc%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5c01: case 0x7c01:
+ util::stream_format(stream, "or%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x5e01: case 0x7e01:
+ util::stream_format(stream, "sub%c ", BIT(opcode, 13) ? 'w' : 'b');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x6000:
+ if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else
+ {
+ stream << "mulsb ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ }
+ return 2 | SUPPORTED;
+
+ case 0x6200:
+ if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else
+ {
+ stream << "mulsw ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_rpair(stream, (opcode & 0x01e0) >> 5);
+ }
+ return 2 | SUPPORTED;
+
+ case 0x6400: case 0x6600:
+ if (m_arch == cr16_arch::CR16A)
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+ else
+ {
+ stream << "movd ";
+ format_imm21(stream, u32(opcode & 0x0200) << 11 | u32(opcode & 0x000e) << 16 | u32(opcode & 0x0010) << 12 | opcodes.r16(pc + 2));
+ stream << ", ";
+ format_rpair(stream, (opcode & 0x01e0) >> 5);
+ return 4 | SUPPORTED;
+ }
+
+ case 0x6800: case 0x6a00:
+ util::stream_format(stream, "mov%cb ", BIT(opcode, 9) ? 'z' : 'x');
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x6c00:
+ // Push and pop group
+ if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else
+ {
+ if (BIT(opcode, 8))
+ stream << "popret ";
+ else if (BIT(opcode, 7))
+ stream << "pop ";
+ else
+ stream << "push ";
+ util::stream_format(stream, "$%d, ", ((opcode & 0x0060) >> 5) + 1);
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ if (BIT(opcode, 8))
+ {
+ util::stream_format(stream, " ; %cMM", BIT(opcode, 7) ? 'L' : 'S');
+ return 2 | STEP_OUT | SUPPORTED;
+ }
+ }
+ return 2 | SUPPORTED;
+
+ case 0x6e00:
+ if ((opcode & 0x01c0) != 0x01c0)
+ {
+ util::stream_format(stream, "s%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ }
+ else
+ stream << "res";
+ return 2 | SUPPORTED;
+
+ case 0x7000:
+ stream << "lpr ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_rproc(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x7200:
+ stream << "spr ";
+ format_rproc(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ return 2 | SUPPORTED;
+
+ case 0x7400:
+ // Conditional or unconditional branch to large address
+ if ((opcode & 0x01e0) != 0x01e0 && m_arch != cr16_arch::CR16A)
+ {
+ if ((opcode & 0x01e0) == 0x01c0)
+ stream << "br ";
+ else
+ util::stream_format(stream, "b%s ", s_cc[(opcode & 0x01e0) >> 5]);
+ format_pc_disp21(stream, pc, u32(opcode & 0x000e) << 16 | u32(opcode & 0x0010) << 12 | opcodes.r16(pc + 2));
+ return 4 | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x7401:
+ // Jump and link to small address
+ stream << "jal ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ return 2 | SUPPORTED;
+
+ case 0x7600:
+ // Branch and link to large address
+ if (m_arch == cr16_arch::CR16A)
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+ else
+ {
+ stream << "bal ";
+ format_rpair(stream, (opcode & 0x01e0) >> 5);
+ stream << ", ";
+ format_pc_disp21(stream, pc, u32(opcode & 0x000e) << 16 | u32(opcode & 0x0010) << 12 | opcodes.r16(pc + 2));
+ return 4 | STEP_OVER | SUPPORTED;
+ }
+
+ case 0x7601:
+ // TBIT reg, reg only exists as a word operation
+ stream << "tbit ";
+ format_reg(stream, (opcode & 0x001e) >> 1);
+ stream << ", ";
+ format_reg(stream, (opcode & 0x01e0) >> 5);
+ return 2 | SUPPORTED;
+
+ case 0x7800:
+ if (opcode == 0x79fe)
+ {
+ stream << "retx";
+ return 2 | STEP_OUT | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x7a00:
+ if ((opcode & 0x01e0) == 0x01e0)
+ {
+ stream << "excp ";
+ format_excp_vector(stream, (opcode & 0x001e) >> 1);
+ return 2 | STEP_OVER | SUPPORTED;
+ }
+ else
+ {
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+
+ case 0x7c00:
+ if (opcode == 0x7dde)
+ stream << "di";
+ else if (opcode == 0x7dfe)
+ stream << "ei";
+ else
+ stream << "res";
+ return 2 | SUPPORTED;
+
+ case 0x7e00:
+ // Various special operations
+ if (opcode == 0x7ffe)
+ stream << "wait";
+ else if (m_arch == cr16_arch::CR16A)
+ stream << "res";
+ else if ((opcode & 0x000c) == 0x0000)
+ {
+ stream << "muluw ";
+ format_reg(stream, (opcode & 0x0012) >> 1);
+ stream << ", ";
+ format_rpair(stream, (opcode & 0x01e0) >> 5);
+ }
+ else if ((opcode & 0x011e) == 0x0004)
+ {
+ if (BIT(opcode, 7))
+ stream << "storm ";
+ else
+ stream << "loadm ";
+ util::stream_format(stream, "$%d", ((opcode & 0x0060) >> 5) + 1);
+ }
+ else if (opcode == 0x7fe6)
+ stream << "eiwait";
+ else
+ stream << "res";
+ return 2 | SUPPORTED;
+
+ default:
+ stream << "res";
+ return 2 | SUPPORTED;
+ }
+}