From 5c79c42a56ef4e52ff3d788f3b170482d382772a Mon Sep 17 00:00:00 2001 From: Heersin Date: Sat, 17 Feb 2024 22:29:16 +0800 Subject: [PATCH] Disassembler arch for RX microcontroller (MCU) family (#4198) * Basic framework of rx implemented * Add match and parse flow * Add part of description for RxV1 instruction set * Add datasheet for rxv1 * Implement stringify op for rx * Add missing TST instruction in datasheet * Test to div instruction and fix bugs * Test known rxv1 instruction set * Fix bugs in rx disasm plugin and add SPDX * Add rz_analysis for renesas rx * Fix rz_analysis bug for renesas rx * Add rz_asm test and fix bug in datasheet * Add analysis test and fix bugs for renesas rx --- librz/analysis/d/cc-rx-32.sdb.txt | 9 + librz/analysis/meson.build | 2 + librz/analysis/p/analysis_rx.c | 256 ++++++++++++ librz/asm/arch/rx/rx.c | 166 ++++++++ librz/asm/arch/rx/rx.h | 13 + librz/asm/arch/rx/rx_inst.c | 605 +++++++++++++++++++++++++++ librz/asm/arch/rx/rx_inst.h | 100 +++++ librz/asm/arch/rx/rx_opcode_detail.c | 307 ++++++++++++++ librz/asm/arch/rx/rx_opcode_detail.h | 281 +++++++++++++ librz/asm/arch/rx/rx_str.inc | 171 ++++++++ librz/asm/meson.build | 5 + librz/asm/p/asm_rx.c | 41 ++ librz/bin/format/elf/elf_info.c | 1 + librz/include/rz_analysis.h | 1 + librz/include/rz_asm.h | 1 + test/db/analysis/rx | 367 ++++++++++++++++ test/db/asm/rx | 204 +++++++++ test/db/cmd/cmd_list | 4 +- 18 files changed, 2533 insertions(+), 1 deletion(-) create mode 100644 librz/analysis/d/cc-rx-32.sdb.txt create mode 100644 librz/analysis/p/analysis_rx.c create mode 100644 librz/asm/arch/rx/rx.c create mode 100644 librz/asm/arch/rx/rx.h create mode 100644 librz/asm/arch/rx/rx_inst.c create mode 100644 librz/asm/arch/rx/rx_inst.h create mode 100644 librz/asm/arch/rx/rx_opcode_detail.c create mode 100644 librz/asm/arch/rx/rx_opcode_detail.h create mode 100644 librz/asm/arch/rx/rx_str.inc create mode 100644 librz/asm/p/asm_rx.c create mode 100644 test/db/analysis/rx create mode 100644 test/db/asm/rx diff --git a/librz/analysis/d/cc-rx-32.sdb.txt b/librz/analysis/d/cc-rx-32.sdb.txt new file mode 100644 index 0000000000..44fda37128 --- /dev/null +++ b/librz/analysis/d/cc-rx-32.sdb.txt @@ -0,0 +1,9 @@ +default.cc=rx + +rx=cc +cc.rx.arg0=r1 +cc.rx.arg1=r2 +cc.rx.arg2=r3 +cc.rx.arg3=r4 +cc.rx.argn=stack_rev +cc.rx.ret=r1 \ No newline at end of file diff --git a/librz/analysis/meson.build b/librz/analysis/meson.build index 49a766e57f..9ef0054d40 100644 --- a/librz/analysis/meson.build +++ b/librz/analysis/meson.build @@ -30,6 +30,7 @@ analysis_plugins_list = [ 'propeller', 'pyc', 'rl78', + 'rx', 'rsp', 'snes', 'sparc_cs', @@ -142,6 +143,7 @@ rz_analysis_sources = [ 'p/analysis_pyc.c', 'p/analysis_rl78.c', 'p/analysis_rsp.c', + 'p/analysis_rx.c', 'p/analysis_snes.c', 'p/analysis_sparc_cs.c', 'p/analysis_spc700.c', diff --git a/librz/analysis/p/analysis_rx.c b/librz/analysis/p/analysis_rx.c new file mode 100644 index 0000000000..cc6c1bc191 --- /dev/null +++ b/librz/analysis/p/analysis_rx.c @@ -0,0 +1,256 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +#include +#include +#include + +static void calculate_jmp_addr(RxInst *inst, RzAnalysisOp *op) { + if (inst->v0.kind == RX_OPERAND_COND) { + ut8 pcdsp_l = inst->v0.v.cond.pc_dsp_len; + ut32 pcdsp_val = inst->v0.v.cond.pc_dsp_val; + ut64 addr_inc = pcdsp_val; + if (pcdsp_l >= 8) { + // as SIMM, use signed extend + ut8 shift = pcdsp_l - 1; + if ((1 << shift) & pcdsp_val) { + // as negative + ut32 mask = 0xffffffff << shift; + addr_inc = abs((st32)(pcdsp_val | mask)); + op->jump = op->addr - addr_inc; + return; + } + } + op->jump = op->addr + addr_inc; + } + op->fail = op->addr + op->size; +} + +static int analysis_rx_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, + const ut8 *buf, int len, RzAnalysisOpMask mask) { + op->addr = addr; + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + + RxInst inst = { 0 }; + st32 bytes_read = 0; + if (!rx_dis(&inst, &bytes_read, buf, len)) { + return bytes_read; + } else { + op->type = RZ_ANALYSIS_OP_TYPE_UNK; + } + + op->size = bytes_read; + switch (inst.op) { + // jump related instructions + case RX_OP_RTS: + op->type = RZ_ANALYSIS_OP_TYPE_RET; + break; + case RX_OP_BSR_A: + case RX_OP_BSR_L: + case RX_OP_BSR_W: + case RX_OP_JSR: + op->type = RZ_ANALYSIS_OP_TYPE_CALL; + calculate_jmp_addr(&inst, op); + break; + case RX_OP_BCND_W: + case RX_OP_BCND_B: + case RX_OP_BCND_S: + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + calculate_jmp_addr(&inst, op); + break; + case RX_OP_BRA_L: + case RX_OP_BRA_A: + case RX_OP_BRA_B: + case RX_OP_BRA_S: + case RX_OP_BRA_W: + op->type = RZ_ANALYSIS_OP_TYPE_JMP; + calculate_jmp_addr(&inst, op); + break; + case RX_OP_JMP: + // use register + op->type = RZ_ANALYSIS_OP_TYPE_JMP; + break; + // normal instruction + case RX_OP_ADD: + case RX_OP_ADD_UB: + case RX_OP_ADC: + op->type = RZ_ANALYSIS_OP_TYPE_ADD; + break; + case RX_OP_SUB: + case RX_OP_SUB_UB: + op->type = RZ_ANALYSIS_OP_TYPE_SUB; + break; + case RX_OP_DIV: + case RX_OP_DIV_UB: + case RX_OP_DIVU: + case RX_OP_DIVU_UB: + op->type = RZ_ANALYSIS_OP_TYPE_DIV; + break; + case RX_OP_MUL: + case RX_OP_MULLO: + case RX_OP_EMUL: + case RX_OP_EMULU: + case RX_OP_EMULU_UB: + case RX_OP_EMUL_UB: + case RX_OP_MUL_UB: + case RX_OP_MULHI: + case RX_OP_MACHI: + case RX_OP_MACLO: + op->type = RZ_ANALYSIS_OP_TYPE_MUL; + break; + case RX_OP_AND: + case RX_OP_AND_UB: + op->type = RZ_ANALYSIS_OP_TYPE_AND; + break; + case RX_OP_OR: + case RX_OP_OR_UB: + op->type = RZ_ANALYSIS_OP_TYPE_OR; + break; + case RX_OP_NOT: + op->type = RZ_ANALYSIS_OP_TYPE_NOT; + break; + case RX_OP_NOP: + op->type = RZ_ANALYSIS_OP_TYPE_NOP; + break; + case RX_OP_CMP: + case RX_OP_CMP_UB: + op->type = RZ_ANALYSIS_OP_TYPE_CMP; + break; + case RX_OP_PUSH: + case RX_OP_PUSHM: + case RX_OP_PUSHC: + op->type = RZ_ANALYSIS_OP_TYPE_PUSH; + break; + case RX_OP_POP: + case RX_OP_POPM: + case RX_OP_POPC: + op->type = RZ_ANALYSIS_OP_TYPE_POP; + break; + case RX_OP_ROTL: + case RX_OP_ROLC: + op->type = RZ_ANALYSIS_OP_TYPE_ROL; + break; + case RX_OP_ROTR: + case RX_OP_RORC: + op->type = RZ_ANALYSIS_OP_TYPE_ROR; + break; + case RX_OP_SHAR: + op->type = RZ_ANALYSIS_OP_TYPE_SAR; + break; + case RX_OP_SHLR: + op->type = RZ_ANALYSIS_OP_TYPE_SHR; + break; + case RX_OP_SHLL: + op->type = RZ_ANALYSIS_OP_TYPE_SHL; + break; + case RX_OP_XCHG_UB: + case RX_OP_XCHG: + op->type = RZ_ANALYSIS_OP_TYPE_XCHG; + break; + case RX_OP_XOR: + case RX_OP_XOR_UB: + op->type = RZ_ANALYSIS_OP_TYPE_XOR; + break; + case RX_OP_ITOF: + case RX_OP_FTOI: + case RX_OP_ITOF_UB: + op->type = RZ_ANALYSIS_OP_TYPE_CAST; + break; + case RX_OP_INT: + op->type = RZ_ANALYSIS_OP_TYPE_SWI; + break; + case RX_OP_MOV: + case RX_OP_MOVU: + case RX_OP_MVTIPL: + case RX_OP_MVTC: + case RX_OP_MVTACLO: + case RX_OP_MVTACHI: + case RX_OP_MVFACMI: + case RX_OP_MVFACHI: + case RX_OP_MVFC: + op->type = RZ_ANALYSIS_OP_TYPE_MOV; + break; + default: + break; + } + + return op->size; +} + +static char *analysis_rx_reg_profile(RzAnalysis *analysis) { + // check librz/reg/profile for register profile description + const char *p = + "=PC pc\n" + "=SP r0\n" + "=ZF zf\n" + "=CF cf\n" + "=SF sf\n" + "=OF of\n" + // ABI: https://www.renesas.com/us/en/document/mat/cc-rx-compiler-users-manual + "=R0 r1\n" + "=A0 r1\n" + "=A1 r2\n" + "=A2 r3\n" + "=A3 r4\n" + // general + "gpr r0 .32 0 0\n" + "gpr r1 .32 4 0\n" + "gpr r2 .32 8 0\n" + "gpr r3 .32 12 0\n" + "gpr r4 .32 16 0\n" + "gpr r5 .32 20 0\n" + "gpr r6 .32 24 0\n" + "gpr r7 .32 28 0\n" + "gpr r8 .32 32 0\n" + "gpr r9 .32 36 0\n" + "gpr r10 .32 40 0\n" + "gpr r11 .32 44 0\n" + "gpr r12 .32 48 0\n" + "gpr r13 .32 52 0\n" + "gpr r14 .32 56 0\n" + "gpr r15 .32 60 0\n" + // control register + "gpr isp .32 64 0\n" + "gpr usp .32 68 0\n" + "gpr intb .32 72 0\n" + "gpr pc .32 76 0\n" + + // psw + "gpr psw .32 80 0\n" + "flg ipl .4 .644 0\n" + "flg pm .1 .651 0\n" + "flg u .1 .654 0\n" + "flg i .1 .655 0\n" + "flg of .1 .668 0\n" + "flg sf .1 .669 0\n" + "flg zf .1 .670 0\n" + "flg cf .1 .671 0\n" + + "gpr bpc .32 84 0\n" + "gpr bpsw .32 88 0\n" + "gpr fintv .32 92 0\n" + + // fpsw, contains multiple flags but ignore now + "gpr fpsw .32 96 0\n" + "flg fsf .1 .768 0\n" + "flg fxf .1 .769 0\n" + "flg fuf .1 .770 0\n" + "flg fzf .1 .771 0\n" + "flg fof .1 .772 0\n" + "flg fvf .1 .773 0\n" + "flg rmode .2 .798 0\n"; + return strdup(p); +} + +RzAnalysisPlugin rz_analysis_plugin_rx = { + .name = "rx", + .arch = "rx", + .desc = "Renesas RX Family analysis", + .license = "LGPL3", + .bits = 32, + .op = &analysis_rx_op, + .get_reg_profile = &analysis_rx_reg_profile, +}; diff --git a/librz/asm/arch/rx/rx.c b/librz/asm/arch/rx/rx.c new file mode 100644 index 0000000000..99cf6163b0 --- /dev/null +++ b/librz/asm/arch/rx/rx.c @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rx.h" +#include "rx_str.inc" + +static ut64 prefetch_bytes(const ut8 *buf, size_t buf_len) { + ut64 result = 0; + size_t i; + size_t end = buf_len < 8 ? buf_len : 8; // Determine end based on buf_len + for (i = 0; i < end; i++) { + result |= ((ut64)buf[i]) << ((7 - i) * 8); // Shift and combine + } + return result; +} + +bool rx_operand_stringify(RxInst *inst, RxOperand *opr, RZ_OUT RzStrBuf *buf) { + // construct string output to RzStrBuf + if (opr->kind == RX_OPERAND_NULL) { + // ignore + return false; + } + + if (opr->kind == RX_OPERAND_COND) { + if (opr->v.cond.pc_dsp_len) { + rz_strbuf_appendf(buf, " #0x%" PFMT32x, opr->v.cond.pc_dsp_val); + } + return true; + } + + if (opr->kind == RX_OPERAND_FLAG) { + rz_strf(buf->buf, "%s", RxNameFlag(opr->v.flag)); + return true; + } + + if (opr->kind == RX_OPERAND_IMM) { + rz_strf(buf->buf, "#0x%" PFMT32x, + opr->v.imm.imm); + return true; + } + + if (opr->kind == RX_OPERAND_REG) { + if (opr->v.reg.dsp_width) { + // dsp value + rz_strbuf_appendf(buf, "0x%" PFMT32x, + opr->v.reg.dsp_val); + } + + if (opr->v.reg.as_indirect) { + rz_strbuf_append(buf, "["); + } + if (opr->v.reg.fix_mode == RX_FIXOP_PRE_DEC) { + rz_strbuf_append(buf, "-"); + } + + if (opr->v.reg.as_base) { + rz_strbuf_appendf(buf, "%s,%s", + RxNameReg(opr->v.reg.ri), + RxNameReg(opr->v.reg.reg)); + } else { + rz_strbuf_appendf(buf, "%s", RxNameReg(opr->v.reg.reg)); + } + + if (opr->v.reg.fix_mode == RX_FIXOP_POST_INC) { + rz_strbuf_append(buf, "+"); + } + if (opr->v.reg.as_indirect) { + rz_strbuf_append(buf, "]"); + } + + if (opr->v.reg.memex != RX_EXT_NON) { + rz_strbuf_appendf(buf, ".%s", RxNameExt(opr->v.reg.memex)); + } + } + + return true; +} + +bool rx_inst_stringify(RxInst *inst, RzStrBuf *buf) { + // construct string output to RzStrBuf + if (inst->op == RX_OP_INVALID) { + return false; + } + + RzStrBuf opr0_buf, opr1_buf, opr2_buf; + rz_strbuf_init(&opr0_buf); + rz_strbuf_init(&opr1_buf); + rz_strbuf_init(&opr2_buf); + + bool has_opr0 = inst->v0.kind != RX_OPERAND_NULL && + rx_operand_stringify(inst, &inst->v0, &opr0_buf); + bool has_opr1 = inst->v1.kind != RX_OPERAND_NULL && + rx_operand_stringify(inst, &inst->v1, &opr1_buf); + bool has_opr2 = inst->v2.kind != RX_OPERAND_NULL && + rx_operand_stringify(inst, &inst->v2, &opr2_buf); + + if (inst->op == RX_OP_BCND_W || inst->op == RX_OP_BCND_B || inst->op == RX_OP_BCND_S || + inst->op == RX_OP_BMCND || inst->op == RX_OP_SCCOND) { + if (has_opr0 && inst->v0.kind == RX_OPERAND_COND) { + // build b[cnd] + RzStrBuf cond_buf; + rz_strbuf_init(&cond_buf); + rz_strf(cond_buf.buf, "%s", RxNameCond(inst->v0.v.cond.cond)); + rz_strbuf_appendf(buf, RxNameOp(inst->op), cond_buf.buf); + + if (!inst->v0.v.cond.pc_dsp_len) { + has_opr0 = false; + } + } else { + rz_strbuf_appendf(buf, "%s[invalid]", + RxNameOp(inst->op)); + } + } else { + rz_strbuf_appendf(buf, "%s", RxNameOp(inst->op)); + } + + if (inst->sz_mark != RX_EXT_NON) { + rz_strbuf_appendf(buf, ".%s ", RxNameExt(inst->sz_mark)); + } else { + rz_strbuf_append(buf, " "); + } + + if (has_opr0) { + rz_strbuf_appendf(buf, "%s, ", opr0_buf.buf); + } + if (has_opr1) { + rz_strbuf_appendf(buf, "%s, ", opr1_buf.buf); + } + if (has_opr2) { + rz_strbuf_appendf(buf, "%s", opr2_buf.buf); + } + + return true; +} + +/** + * \brief Parse binary data to RxInst according to RxDesc + * \details RX instruction can be 1-8 Bytes, consisted by 2 main parts: [(code) (code data)], + * in rizin rx plugin, we defined an `RX instruction description` as an array of tokens, with RxEnd as the end mark. + * The parser will fetch 8Bytes, and try to match a predefined instruction description to get a valid interpretation. + * \param inst RxInst to be filled + * \param bytes_read count bytes read from buf + * \param buf rz raw binary data, provided by rizin + * \param buf_len length of buf, provided by rizin + * \return true if parse success, false otherwise + */ +RZ_API bool rx_dis(RZ_NONNULL RxInst RZ_OUT *inst, RZ_NONNULL st32 RZ_OUT *bytes_read, RZ_NONNULL const ut8 *buf, size_t buf_len) { + rz_return_val_if_fail(inst && bytes_read && buf, false); + // rx instruction length vary from 1 to 8 Bytes + ut64 prefetched_bytes = prefetch_bytes(buf, buf_len); + RxInst current_inst = { 0 }; + st32 bytes_read_real = 0; + for (ut32 desc_id = 0; desc_id < RX_DESC_SIZE; ++desc_id) { + memset(¤t_inst, 0, sizeof(RxInst)); + bool is_valid = rx_try_match_and_parse(¤t_inst, &rx_inst_descs[desc_id], + &bytes_read_real, prefetched_bytes); + if (is_valid) { + *inst = current_inst; + *bytes_read = bytes_read_real; + return true; + } + } + + // nothing matched known instruction + return false; +} diff --git a/librz/asm/arch/rx/rx.h b/librz/asm/arch/rx/rx.h new file mode 100644 index 0000000000..810c33a002 --- /dev/null +++ b/librz/asm/arch/rx/rx.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_ARCH_RX_H +#define RZ_ARCH_RX_H + +#include +#include "rx_inst.h" + +RZ_API bool rx_dis(RZ_NONNULL RxInst RZ_OUT *inst, RZ_NONNULL st32 RZ_OUT *bytes_read, RZ_NONNULL const ut8 *buf, size_t buf_len); +bool rx_inst_stringify(RxInst *inst, RzStrBuf *buf); + +#endif diff --git a/librz/asm/arch/rx/rx_inst.c b/librz/asm/arch/rx/rx_inst.c new file mode 100644 index 0000000000..f9a0dbe620 --- /dev/null +++ b/librz/asm/arch/rx/rx_inst.c @@ -0,0 +1,605 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rx_inst.h" + +#define AssignOpVar(vid, field, expr) \ + { \ + switch (vid) { \ + case 0: inst->v0.field = (expr); break; \ + case 1: inst->v1.field = (expr); break; \ + default: inst->v2.field = (expr); break; \ + } \ + } + +RxOperandFlag rx_cb_map[16] = { + RX_FLAG_C, + RX_FLAG_Z, + RX_FLAG_S, + RX_FLAG_O, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_I, + RX_FLAG_U, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, + RX_FLAG_RESERVED, +}; + +RxReg rx_cr_map[32] = { + RX_REG_PSW, + RX_REG_PC, + RX_REG_USP, + RX_REG_FPSW, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_BPSW, + RX_REG_BPC, + RX_REG_ISP, + RX_REG_FINTV, + RX_REG_INTB, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, + RX_REG_RESERVED, +}; + +static inline ut64 getbits(ut64 bytes, ut8 s, ut8 l) { + return (bytes >> (64 - s - l)) & ((1ULL << l) - 1); +} + +bool match_code(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.inst.tk_len; + ut64 bits = getbits(bytes, s, l); + if (bits == token->tk.inst.detail) { + *bits_read += l; + return true; + } + + return false; +} + +RxOpExtMark bits2mark(ut64 bits) { + // 00 - B, 01 - W, 10 - L, 11 - UW + return RX_EXT_B + bits; +} + +bool match_mi(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.mi.tk_len; + RxOpCode op = inst->op; + ut64 bits = getbits(bytes, s, l); + if (op == RX_OP_ADC) { + if (bits != 2) { + // only 10 - L allowed + return false; + } + } + + inst->v0.v.reg.memex = bits2mark(bits); + *bits_read += l; + return true; +} + +bool match_ld(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.ld.tk_len; + ut64 ld_bits = getbits(bytes, s, l); + ut8 dsp_len; + + // 11 - Rs + switch (ld_bits) { + case 3: + *bits_read += l; + return true; + case 0: + dsp_len = 0; + break; + case 1: + dsp_len = 8; + break; + case 2: + dsp_len = 16; + break; + default: + rz_warn_if_reached(); + return 0; + } + + AssignOpVar(token->tk.ld.vid, v.reg.as_indirect, true); + AssignOpVar(token->tk.ld.vid, v.reg.dsp_width, dsp_len); + + *bits_read += l; + return true; +} + +bool match_ld_part(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.ld_part.tk_len; + ut8 ldr = getbits(bytes, s, l); + ut8 dsp_width; + // 00 - None, 01 - dsp:8, 10 - dsp:16, 11 - invalid + switch (ldr) { + case 3: + // invalid 11 + return false; + case 0: + dsp_width = 0; + break; + case 1: + dsp_width = 8; + break; + case 2: + dsp_width = 16; + break; + default: + rz_warn_if_reached(); + return false; + } + + AssignOpVar(token->tk.ld_part.vid, v.reg.dsp_width, dsp_width); + AssignOpVar(token->tk.ld_part.vid, v.reg.as_indirect, true); + *bits_read += l; + return true; +} + +ut8 bits2immlen(ut64 bits) { + // 01 - SIMM: 8, 10 - SIMM: 16 + // 11 - SIMM: 24, 00 - IMM: 32 + switch (bits) { + case 0: + return 32; + case 1: + return 8; + case 2: + return 16; + case 3: + return 24; + default: + rz_warn_if_reached(); + return 0; + } +} + +bool match_li(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.li.tk_len; + AssignOpVar(token->tk.li.vid, v.imm.imm_width, bits2immlen(getbits(bytes, s, l))); + *bits_read += l; + return true; +} + +bool match_reg(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.reg.tk_len; + ut8 operand_id = token->tk.reg.vid; + AssignOpVar(operand_id, v.reg.reg, RX_REG_R0 + getbits(bytes, s, l)); + AssignOpVar(operand_id, kind, RX_OPERAND_REG); + *bits_read += l; + return true; +} + +bool match_reg_patched(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.reg.tk_len; + ut8 operand_id = token->tk.reg.vid; + ut64 reg_bits = getbits(bytes, s, l); + RxOpCode op = inst->op; + RxReg reg = RX_REG_R0 + reg_bits; + + if (op == RX_OP_PUSHM || op == RX_OP_POPM) { + if (operand_id == 0) { + // for opr0, R1 -> R14 + if (reg < RX_REG_R1 || reg > RX_REG_R14) { + return false; + } + } + if (operand_id == 1) { + // for opr1 + if (reg < RX_REG_R2 || reg > RX_REG_R15) { + return false; + } + } + } + + if (op == RX_OP_EMUL || op == RX_OP_EMULU) { + if (operand_id == 1) { + // dest reg limit: R0 -> R14 + if (reg > RX_REG_R14) { + return false; + } + } + } + + if (op == RX_OP_RTSD) { + if (reg < RX_REG_R1 || reg > RX_REG_R15) { + return false; + } + } + + AssignOpVar(operand_id, v.reg.reg, RX_REG_R0 + reg_bits); + AssignOpVar(operand_id, kind, RX_OPERAND_REG); + *bits_read += l; + return true; +} + +bool match_ri(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.reg.tk_len; + ut8 operand_id = token->tk.reg.vid; + ut64 reg_bits = getbits(bytes, s, l); + AssignOpVar(operand_id, v.reg.as_indirect, true); + AssignOpVar(operand_id, v.reg.as_base, true); + AssignOpVar(operand_id, v.reg.ri, RX_REG_R0 + reg_bits); + *bits_read += l; + return true; +} + +bool match_cr(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.cr.tk_len; + ut8 operand_id = token->tk.cr.vid; + AssignOpVar(operand_id, v.reg.reg, rx_cr_map[(getbits(bytes, s, l))]); + AssignOpVar(operand_id, kind, RX_OPERAND_REG); + *bits_read += l; + return true; +} + +bool match_imm(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.imm.tk_len; + ut8 operand_id = token->tk.imm.vid; + AssignOpVar(operand_id, v.imm.imm, getbits(bytes, s, l)); + AssignOpVar(operand_id, v.imm.imm_width, l); + AssignOpVar(operand_id, kind, RX_OPERAND_IMM); + *bits_read += l; + return true; +} + +bool match_cond(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.cond.tk_len; + ut8 operand_id = token->tk.cond.vid; + ut64 cond_bits = getbits(bytes, s, l); + RxOpCondMark cond_mark; + if (inst->op == RX_OP_BMCND) { + if (cond_bits >= 0xe) { + // reserved val for cond + return false; + } + cond_mark = RX_COND_EQ + cond_bits; + } else { + cond_mark = RX_COND_EQ + cond_bits; + } + AssignOpVar(operand_id, v.cond.cond, cond_mark); + AssignOpVar(operand_id, kind, RX_OPERAND_COND); + *bits_read += l; + return true; +} + +bool match_jump(RZ_OUT RxInst *inst, RxToken *token) { + // judge as a unconditional jump + inst->v0.kind = RX_OPERAND_COND; + inst->v0.v.cond.cond = RX_COND_JUMP; + return true; +} + +bool match_cb(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.cb.tk_len; + ut8 control_bits = getbits(bytes, s, l); + + inst->v0.v.flag = rx_cb_map[control_bits]; + inst->v0.kind = RX_OPERAND_FLAG; + + if (inst->v0.v.flag == RX_FLAG_RESERVED) { + // TODO: should we use strict policy ? + return false; + } + + *bits_read += l; + return true; +} + +bool match_dsp(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + // DSP mark should follow a jump + ut8 s = *bits_read; + ut8 l = token->tk.dsp.tk_len; + ut8 dsp_bits = getbits(bytes, s, l); + + // for condition dsp + if (dsp_bits > 10 || dsp_bits < 3) { + return false; + } + + inst->v0.v.cond.pc_dsp_len = 3; + inst->v0.v.cond.pc_dsp_val = dsp_bits; + *bits_read += l; + return true; +} + +bool match_dsp_split(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.dsp_sp.tk_len; + ut8 interval = token->tk.dsp_sp.interval; + ut8 ll = token->tk.dsp_sp.tk_len_more; + + // | dsp_A | interval_bits | dsp_B | + // dsp_bits = concat(dsp_A, dsp_B) + ut8 dsp_bits = (getbits(bytes, s, l) << ll) | getbits(bytes, s + l + interval, ll); + ut8 operand_id = token->tk.dsp_sp.vid; + AssignOpVar(operand_id, v.reg.dsp_val, dsp_bits); + AssignOpVar(operand_id, v.reg.dsp_width, l + ll); + AssignOpVar(operand_id, v.reg.as_indirect, true); + + *bits_read += l; + return true; +} + +bool match_ignore(RxToken *token, RZ_OUT ut8 *bits_read) { + ut8 l = token->tk.reserved.tk_len; + *bits_read += l; + return true; +} + +bool match_sz(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.sz.tk_len; + ut8 sz = getbits(bytes, s, l); + if (sz == 3) { + // invalid 11 + return false; + } + inst->sz_mark = RX_EXT_B + sz; + *bits_read += l; + return true; +} + +bool match_ad(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 l = token->tk.ad.tk_len; + ut8 addr_bits = getbits(bytes, s, l); + + if (inst->op == RX_OP_MOVU) { + // 10 [Rs+] and 11 [-Rs] is allowed + if (addr_bits < 2) { + return false; + } + } + + // 00 Rs, [Rd+], 01: Rs, [-Rd], inc/dec on Rd + // 10 [Rs+], Rd, 11: [-Rs], Rd, inc/dec on Rs + switch (addr_bits) { + case 0: + inst->v1.v.reg.as_indirect = true; + inst->v1.v.reg.fix_mode = RX_FIXOP_POST_INC; + break; + case 1: + inst->v1.v.reg.as_indirect = true; + inst->v1.v.reg.fix_mode = RX_FIXOP_PRE_DEC; + break; + case 2: + inst->v0.v.reg.as_indirect = true; + inst->v0.v.reg.fix_mode = RX_FIXOP_POST_INC; + break; + case 3: + inst->v0.v.reg.as_indirect = true; + inst->v0.v.reg.fix_mode = RX_FIXOP_PRE_DEC; + break; + default: + rz_warn_if_reached(); + return false; + } + + *bits_read += l; + return true; +} + +static ut32 pack_big(ut32 raw_data, ut8 len_in_bytes) { + ut32 result = 0; + for (int i = 0; i < len_in_bytes; i++) { + ut32 byte = (raw_data >> (8 * i)) & 0xFF; + result = (result << 8) | byte; + } + return result; +} + +bool pack_data(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + ut8 s = *bits_read; + ut8 vid = token->tk.data.vid; + ut8 l; + ut32 follow_data; + + RxOperand *opr = vid == 0 ? &(inst->v0) : vid == 1 ? &(inst->v1) + : &(inst->v2); + + ut8 data_type = token->tk.data.data_type; + if (data_type == 2) { + // pack dsp + l = opr->v.reg.dsp_width; + follow_data = getbits(bytes, s, l); + opr->v.reg.dsp_val = pack_big(follow_data, l / 8); + *bits_read += l; + return true; + } + + if (data_type == 1) { + // pack imm + if (token->tk.data.fixed_len) { + l = token->tk.data.fixed_len; + opr->v.imm.imm_width = l; + } else { + l = opr->v.imm.imm_width; + } + opr->kind = RX_OPERAND_IMM; + follow_data = getbits(bytes, s, l); + opr->v.imm.imm = pack_big(follow_data, l / 8); + *bits_read += l; + return true; + } + + if (data_type == 3) { + // pack pcdsp + if (token->tk.data.fixed_len) { + l = token->tk.data.fixed_len; + opr->v.cond.pc_dsp_len = l; + } else { + l = opr->v.cond.pc_dsp_len; + } + follow_data = getbits(bytes, s, l); + opr->v.cond.pc_dsp_val = pack_big(follow_data, l / 8); + *bits_read += l; + return true; + } + + rz_warn_if_reached(); + return false; +} + +bool check_some(RZ_OUT RxInst *inst, RxToken *token, RZ_OUT ut8 *bits_read, ut64 bytes) { + // a token hook for parser loop + if (inst->op == RX_OP_MOV) { + // check order of src and dest + // for ad[1:0], if 00 or 01, mov dst, src + // for ad[0:1], if 10 or 11, mov src, dst + if (inst->v1.kind == RX_OPERAND_REG) { + // parse RegBist[3:0] as dest(V1) + if (inst->v0.v.reg.fix_mode != RX_FIXOP_NON) { + // but set src fixed, parse failed + return false; + } + } + if (inst->v0.kind == RX_OPERAND_REG) { + if (inst->v1.v.reg.fix_mode != RX_FIXOP_NON) { + return false; + } + } + } + return true; +} + +/** + * \brief Parse bytes according to the given RX instruction description to see if it matches + * \param inst an empty RxInst to be filled + * \param desc an RxDesc to be matched + * \param bytes_read bytes read + * \param bytes prefetched bytes + * \return true if parse success, false otherwise + */ +bool rx_try_match_and_parse(RZ_OUT RxInst *inst, RxDesc *desc, st32 RZ_OUT *bytes_read, ut64 bytes) { + ut8 read_bits = 0; + bool is_valid = true; + inst->op = desc->op; + for (int tki = 0; tki < MAX_TOKEN; ++tki) { + if (!is_valid) { + return false; + } + RxTokenType tk_type = desc->tks[tki].type; + RxToken *token = &(desc->tks[tki]); + + if (tk_type == RX_TOKEN_NON) { + // break the loop + break; + } + + switch (tk_type) { + case RX_TOKEN_INST: + is_valid = match_code(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_LD: + is_valid = match_ld(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_LD_PART: + is_valid = match_ld_part(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_LI: + is_valid = match_li(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_MI: + is_valid = match_mi(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_DSP: + is_valid = match_dsp(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_DSP_SPLIT: + is_valid = match_dsp_split(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_SZ: + is_valid = match_sz(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_AD: + is_valid = match_ad(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_REG: + is_valid = match_reg(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_CR: + is_valid = match_cr(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_CB: + is_valid = match_cb(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_IMM: + is_valid = match_imm(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_COND: + is_valid = match_cond(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_IGNORE: + match_ignore(token, &read_bits); + break; + case RX_TOKEN_JMP: + is_valid = match_jump(inst, token); + break; + case RX_TOKEN_REG_LIMIT: + is_valid = match_reg_patched(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_HOOK: + is_valid = check_some(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_RI: + is_valid = match_ri(inst, token, &read_bits, bytes); + break; + case RX_TOKEN_DATA: + pack_data(inst, token, &read_bits, bytes); + break; + default: + rz_warn_if_reached(); + return false; + } + } + + // assume bits / 8 = bytes should be integer + if ((read_bits & 7)) { + // instruction are defined as bytes + rz_warn_if_reached(); + return false; + } + + *bytes_read = (st32)read_bits / 8; + return true; +} diff --git a/librz/asm/arch/rx/rx_inst.h b/librz/asm/arch/rx/rx_inst.h new file mode 100644 index 0000000000..de07987b9b --- /dev/null +++ b/librz/asm/arch/rx/rx_inst.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RX_ARCH_INST_H +#define RX_ARCH_INST_H + +#include "rx_opcode_detail.h" + +typedef enum rx_operand_kind_enum { + RX_OPERAND_NULL, + RX_OPERAND_IMM, + RX_OPERAND_REG, + RX_OPERAND_FLAG, + RX_OPERAND_COND, +} RxOperandKind; + +typedef enum { + RX_FIXOP_NON, + RX_FIXOP_POST_INC, + RX_FIXOP_PRE_DEC +} RxOpFixMark; + +typedef enum { + RX_FLAG_C, + RX_FLAG_Z, + RX_FLAG_S, + RX_FLAG_O, + RX_FLAG_I, + RX_FLAG_U, + RX_FLAG_RESERVED +} RxOperandFlag; + +typedef enum { + RX_COND_EQ, // BZ + RX_COND_NE, // BNZ + RX_COND_GEU, // BC + RX_COND_LTU, // BNC + RX_COND_GTU, + RX_COND_LEU, + RX_COND_PZ, + RX_COND_N, + RX_COND_GE, + RX_COND_LT, + RX_COND_GT, + RX_COND_LE, + RX_COND_O, + RX_COND_NO, + RX_COND_RA, + RX_COND_JUMP, + RX_COND_RESERVED, +} RxOpCondMark; + +typedef struct { + RxOpCondMark cond; + ut8 pc_dsp_len; + ut32 pc_dsp_val; +} RxOperandCond; + +typedef struct { + bool as_indirect; + bool as_base; + ut8 dsp_width; + RxOpExtMark memex; + RxReg ri; + RxReg reg; + RxOpFixMark fix_mode; + ut32 dsp_val; +} RxOperandReg; + +typedef struct { + ut8 imm_width; + ut32 imm; +} RxOperandImm; + +typedef struct rx_operand_t { + RxOperandKind kind; + union { + RxOperandReg reg; + RxOperandImm imm; + RxOperandFlag flag; + RxOperandCond cond; + } v; +} RxOperand; + +typedef struct rx_inst_t { + RxOpCode op; + RxOperand v0; + RxOperand v1; + RxOperand v2; + RxOpExtMark sz_mark; +} RxInst; + +// TODO make them into function, not static defined here +// TODO: call in init +extern RxOperandFlag rx_cb_map[16]; +extern RxReg rx_cr_map[32]; + +bool rx_try_match_and_parse(RZ_OUT RxInst *inst, RxDesc *desc, st32 RZ_OUT *bytes_read, ut64 bytes); + +#endif diff --git a/librz/asm/arch/rx/rx_opcode_detail.c b/librz/asm/arch/rx/rx_opcode_detail.c new file mode 100644 index 0000000000..8ab9f64ccf --- /dev/null +++ b/librz/asm/arch/rx/rx_opcode_detail.c @@ -0,0 +1,307 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rx_opcode_detail.h" +#define RxCode(x, y) \ + { \ + .type = RX_TOKEN_INST, .tk.inst = {.tk_len = (x), \ + .detail = (y) } \ + } +#define RxReg(x, v) \ + { .type = RX_TOKEN_REG, .tk.reg.tk_len = (x), .tk.reg.vid = (v) } +#define RxLi(x, v) \ + { .type = RX_TOKEN_LI, .tk.li.tk_len = (x), .tk.li.vid = (v) } +#define RxImm(x, v) \ + { .type = RX_TOKEN_IMM, .tk.imm.tk_len = (x), .tk.imm.vid = (v) } +#define RxMi(x) \ + { .type = RX_TOKEN_MI, .tk.mi.tk_len = (x) } +#define RxLd(x, v) \ + { .type = RX_TOKEN_LD, .tk.ld.tk_len = (x), .tk.ld.vid = (v) } +#define RxLdPart(x, v) \ + { .type = RX_TOKEN_LD_PART, .tk.ld_part.tk_len = (x), .tk.ld_part.vid = (v) } +#define RxCond(x, v) \ + { .type = RX_TOKEN_COND, .tk.cond.tk_len = (x), .tk.cond.vid = (v) } +#define RxDsp(x) \ + { .type = RX_TOKEN_DSP, .tk.dsp.tk_len = (x) } +#define RxSz(x) \ + { .type = RX_TOKEN_SZ, .tk.sz.tk_len = (x) } +#define RxAd(x) \ + { .type = RX_TOKEN_AD, .tk.ad.tk_len = (x) } +#define RxCr(x, v) \ + { .type = RX_TOKEN_CR, .tk.cr.tk_len = (x), .tk.cr.vid = (v) } +#define RxCb(x) \ + { .type = RX_TOKEN_CB, .tk.cb.tk_len = (x) } +#define RxDspSplit(x, v, it, xx) \ + { .type = RX_TOKEN_DSP_SPLIT, \ + .tk.dsp_sp.tk_len = (x), .tk.dsp_sp.vid = (v), .tk.dsp_sp.tk_len_more = (xx), .tk.dsp_sp.interval = (it) } +#define RxIgnore(x) \ + { .type = RX_TOKEN_IGNORE, .tk.reserved.tk_len = (x) } + +#define RxRi(x, bind_v) \ + { .type = RX_TOKEN_RI, .tk.reg.tk_len = (x), .tk.reg.vid = (bind_v) } + +// TODO: Jmp maybe removed or an empty mark +#define RxJmp \ + { .type = RX_TOKEN_JMP } + +#define RxRegLimit(x, v) \ + { .type = RX_TOKEN_REG_LIMIT, .tk.reg_li.tk_len = (x), .tk.reg_li.vid = (v) } + +#define ImmData(v) \ + { .type = RX_TOKEN_DATA, .tk.data.vid = (v), .tk.data.data_type = 1 } +#define DspData(v) \ + { .type = RX_TOKEN_DATA, .tk.data.vid = (v), .tk.data.data_type = 2 } +#define PcDspData(v, l) \ + { .type = RX_TOKEN_DATA, .tk.data.vid = (v), .tk.data.fixed_len = (l), .tk.data.data_type = 3 } +#define ImmFixedData(v, l) \ + { .type = RX_TOKEN_DATA, .tk.data.vid = (v), .tk.data.fixed_len = (l), .tk.data.data_type = 1 } +#define RxHook \ + { .type = RX_TOKEN_HOOK } +#define RxEnd \ + { .type = RX_TOKEN_NON } + +#define V0 0 +#define V1 1 +#define V2 2 + +RxDesc rx_inst_descs[RX_DESC_SIZE] = { + { .op = RX_OP_ABS, .tks = { RxCode(12, 0x07e2), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_ABS, .tks = { RxCode(16, 0xfc0f), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ADC, .tks = { RxCode(12, 0x0fd7), RxLi(2, V0), RxCode(6, 0x02), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_ADC, .tks = { RxCode(16, 0xfc0b), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ADC, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x08), RxLdPart(2, V0), RxCode(8, 0x02), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_ADD, .tks = { RxCode(8, 0x62), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + // { .op = RX_OP_ADD, .tks = { RxCode(6, 0x1c), RxLi(2, V0), RxReg(4, V1), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_ADD_UB, .tks = { RxCode(6, 0x12), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_ADD, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x02), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_ADD, .tks = { RxCode(6, 0x1c), RxLi(2, V0), RxReg(4, V1), RxReg(4, V2), ImmData(V0), RxEnd } }, + { .op = RX_OP_ADD, .tks = { RxCode(12, 0x0ff2), RxReg(4, V2), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_AND, .tks = { RxCode(8, 0x64), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_AND, .tks = { RxCode(6, 0x1d), RxLi(2, V0), RxCode(4, 0x02), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_AND_UB, .tks = { RxCode(6, 0x14), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_AND, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x04), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_AND, .tks = { RxCode(12, 0xff4), RxReg(4, V2), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_BCLR, .tks = { RxCode(6, 0x3c), RxLdPart(2, V1), RxReg(4, V1), RxCode(1, 0x1), RxImm(3, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BCLR, .tks = { RxCode(14, 0x3f19), RxLdPart(2, V1), RxReg(4, V1), RxReg(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BCLR, .tks = { RxCode(7, 0x3d), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_BCLR, .tks = { RxCode(16, 0xfc67), RxReg(4, V1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_BCND_S, .tks = { RxCode(4, 0x1), RxCond(1, V0), RxDsp(3), RxEnd } }, + { .op = RX_OP_BCND_B, .tks = { RxCode(4, 0x2), RxCond(4, V0), PcDspData(V0, 8), RxEnd } }, + { .op = RX_OP_BCND_W, .tks = { RxCode(7, 0x1d), RxCond(1, V0), PcDspData(V0, 16), RxEnd } }, + { .op = RX_OP_BMCND, .tks = { RxCode(11, 0x7e7), RxImm(3, V1), RxLdPart(2, V2), RxReg(4, V2), RxCond(4, V0), DspData(V2), RxEnd } }, + { .op = RX_OP_BMCND, .tks = { RxCode(11, 0x7ef), RxImm(5, V1), RxCond(4, V0), RxReg(4, V2), RxEnd } }, + { .op = RX_OP_BNOT, .tks = { RxCode(11, 0x7e7), RxImm(3, V0), RxLdPart(2, V1), RxReg(4, V1), RxCode(4, 0xf), DspData(V1), RxEnd } }, + { .op = RX_OP_BNOT, .tks = { RxCode(14, 0x3f1b), RxLdPart(2, V1), RxReg(4, V1), RxReg(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BNOT, .tks = { RxCode(11, 0x7ef), RxImm(5, V0), RxCode(4, 0xf), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_BNOT, .tks = { RxCode(16, 0xfc6f), RxReg(4, V1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_BRA_S, .tks = { RxCode(5, 0x1), RxJmp, RxDsp(3), RxEnd } }, + { .op = RX_OP_BRA_B, .tks = { RxCode(8, 0x2e), RxJmp, PcDspData(V0, 8), RxEnd } }, + { .op = RX_OP_BRA_W, .tks = { RxCode(8, 0x38), RxJmp, PcDspData(V0, 16), RxEnd } }, + { .op = RX_OP_BRA_A, .tks = { RxCode(8, 0x04), RxJmp, PcDspData(V0, 24), RxEnd } }, + { .op = RX_OP_BRA_L, .tks = { RxCode(12, 0x7f4), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_BRK, .tks = { RxCode(8, 0x0), RxEnd } }, + { .op = RX_OP_BSET, .tks = { RxCode(6, 0x3c), RxLdPart(2, V1), RxReg(4, V1), RxCode(1, 0x0), RxImm(3, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BSET, .tks = { RxCode(14, 0x3f18), RxLdPart(2, V1), RxReg(4, V1), RxReg(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BSET, .tks = { RxCode(7, 0x3c), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_BSET, .tks = { RxCode(16, 0xfc63), RxReg(4, V1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_BSR_W, .tks = { RxCode(8, 0x39), RxJmp, PcDspData(V0, 16), RxEnd } }, + { .op = RX_OP_BSR_A, .tks = { RxCode(8, 0x05), RxJmp, PcDspData(V0, 24), RxEnd } }, + { .op = RX_OP_BSR_L, .tks = { RxCode(12, 0x07f5), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_BTST, .tks = { RxCode(6, 0x3d), RxLdPart(2, V1), RxReg(4, V1), RxCode(1, 0x0), RxImm(3, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BTST, .tks = { RxCode(14, 0x3f1a), RxLdPart(2, V1), RxReg(4, V1), RxReg(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_BTST, .tks = { RxCode(7, 0x3e), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_BTST, .tks = { RxCode(16, 0xfc6b), RxReg(4, V1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_CLRPSW, .tks = { RxCode(12, 0x07fb), RxCb(4), RxEnd } }, + + { .op = RX_OP_CMP, .tks = { RxCode(8, 0x61), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_CMP, .tks = { RxCode(12, 0x0755), RxReg(4, V1), ImmFixedData(V0, 8), RxEnd } }, + { .op = RX_OP_CMP, .tks = { RxCode(6, 0x1d), RxLi(2, V0), RxCode(4, 0x0), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_CMP_UB, .tks = { RxCode(6, 0x11), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_CMP, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x1), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + + { .op = RX_OP_DIV, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x08), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_DIV_UB, .tks = { RxCode(14, 0x3f08), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_DIV, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x08), RxLd(2, V0), RxCode(8, 0x08), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_DIVU, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x9), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_DIVU_UB, .tks = { RxCode(14, 0x3f09), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_DIVU, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x09), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + // EMUL family special Rd Range + { .op = RX_OP_EMUL, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x06), RxRegLimit(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_EMUL_UB, .tks = { RxCode(14, 0x3f06), RxLd(2, V0), RxReg(4, V0), RxRegLimit(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_EMUL, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x08), RxLd(2, V0), RxCode(8, 0x06), RxReg(4, V0), RxRegLimit(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_EMULU, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x07), RxRegLimit(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_EMULU_UB, .tks = { RxCode(14, 0x3f07), RxLd(2, V0), RxReg(4, V0), RxRegLimit(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_EMULU, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x07), RxReg(4, V0), RxRegLimit(4, V1), DspData(V0), RxEnd } }, + + { .op = RX_OP_FADD, .tks = { RxCode(20, 0xfd722), RxReg(4, V1), ImmFixedData(V0, 32), RxEnd } }, + { .op = RX_OP_FADD, .tks = { RxCode(14, 0x3f22), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_FCMP, .tks = { RxCode(20, 0xfd721), RxReg(4, V1), ImmFixedData(V0, 32), RxEnd } }, + { .op = RX_OP_FCMP, .tks = { RxCode(14, 0x3f21), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_FDIV, .tks = { RxCode(20, 0xfd724), RxReg(4, V1), ImmFixedData(V0, 32), RxEnd } }, + { .op = RX_OP_FDIV, .tks = { RxCode(14, 0x3f24), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_FMUL, .tks = { RxCode(20, 0xfd723), RxReg(4, V1), ImmFixedData(V0, 32), RxEnd } }, + { .op = RX_OP_FMUL, .tks = { RxCode(14, 0x3f23), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_FSUB, .tks = { RxCode(20, 0xfd720), RxReg(4, V1), ImmFixedData(V0, 32), RxEnd } }, + { .op = RX_OP_FSUB, .tks = { RxCode(14, 0x3f20), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + + { .op = RX_OP_FTOI, .tks = { RxCode(14, 0x3f25), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_INT, .tks = { RxCode(16, 0x7560), ImmFixedData(V0, 8), RxEnd } }, + { .op = RX_OP_ITOF_UB, .tks = { RxCode(14, 0x3f11), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_ITOF, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x11), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_JMP, .tks = { RxCode(12, 0x07f0), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_JSR, .tks = { RxCode(12, 0x07f1), RxReg(4, V0), RxEnd } }, + + // Mov family + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x2), RxSz(2), RxCode(1, 0x0), RxDspSplit(4, V1, 3, 1), RxReg(3, V1), RxIgnore(1), RxReg(3, V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x2), RxSz(2), RxCode(1, 0x1), RxDspSplit(4, V0, 3, 1), RxReg(3, V0), RxIgnore(1), RxReg(3, V1), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(8, 0x66), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(6, 0x0f), RxSz(2), RxDspSplit(1, V1, 3, 4), RxReg(3, V1), RxIgnore(4), ImmFixedData(V0, 8), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(12, 0x0754), RxReg(4, V1), ImmFixedData(V0, 8) } }, + { .op = RX_OP_MOV, .tks = { RxCode(8, 0xfb), RxReg(4, V1), RxLi(2, V0), RxCode(2, 0x02), ImmData(V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x3), RxSz(2), RxCode(4, 0xf), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + // ld are limited to 0-2 + { .op = RX_OP_MOV, .tks = { RxCode(6, 0x3e), RxLdPart(2, V1), RxReg(4, V1), RxLi(2, V0), RxSz(2), DspData(V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x3), RxSz(2), RxCode(2, 0x3), RxLdPart(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(10, 0x03f9), RxSz(2), RxRi(4, V0), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x3), RxSz(2), RxLdPart(2, V1), RxCode(2, 0x3), RxReg(4, V1), RxReg(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(10, 0x03f8), RxSz(2), RxRi(4, V1), RxReg(4, V1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(2, 0x3), RxSz(2), RxLdPart(2, V1), RxLdPart(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), DspData(V1), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(12, 0xfd2), RxAd(2), RxSz(2), RxReg(4, V1), RxHook, RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MOV, .tks = { RxCode(12, 0xfd2), RxAd(2), RxSz(2), RxReg(4, V0), RxHook, RxReg(4, V1), RxEnd } }, + + { .op = RX_OP_MOVU, .tks = { RxCode(4, 0xb), RxSz(1), RxDspSplit(4, V0, 3, 1), RxReg(3, V0), RxIgnore(1), RxReg(3, V1), RxEnd } }, + { .op = RX_OP_MOVU, .tks = { RxCode(5, 0x0b), RxSz(1), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MOVU, .tks = { RxCode(11, 0x07f6), RxSz(1), RxRi(4, V0), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MOVU, .tks = { RxCode(12, 0x0fd3), RxAd(2), RxCode(1, 0x0), RxSz(1), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + + { .op = RX_OP_MACHI, .tks = { RxCode(16, 0xfd04), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MACLO, .tks = { RxCode(16, 0xfd05), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MAX, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x4), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_MAX_UB, .tks = { RxCode(14, 0x3f04), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MAX, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x04), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MIN, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x5), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_MIN_UB, .tks = { RxCode(14, 0x3f05), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MIN, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x05), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MUL, .tks = { RxCode(8, 0x63), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MUL, .tks = { RxCode(6, 0x1d), RxLi(2, V0), RxCode(4, 0x01), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_MUL_UB, .tks = { RxCode(6, 0x13), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MUL, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x3), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_MUL, .tks = { RxCode(12, 0xff3), RxReg(4, V2), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MULHI, .tks = { RxCode(16, 0xfd00), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MULLO, .tks = { RxCode(16, 0xfd01), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_NOP, .tks = { RxCode(8, 0x03), RxEnd } }, + { .op = RX_OP_NEG, .tks = { RxCode(12, 0x07e1), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_NEG, .tks = { RxCode(16, 0xfc07), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_NOT, .tks = { RxCode(12, 0x07e0), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_NOT, .tks = { RxCode(16, 0xfc3b), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_OR, .tks = { RxCode(8, 0x65), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_OR, .tks = { RxCode(6, 0x1d), RxLi(2, V0), RxCode(4, 0x03), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_OR_UB, .tks = { RxCode(6, 0x15), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_OR, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x5), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_OR, .tks = { RxCode(12, 0xff5), RxReg(4, V2), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MVFACHI, .tks = { RxCode(20, 0xfd1f0), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MVFACMI, .tks = { RxCode(20, 0xfd1f2), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MVFC, .tks = { RxCode(16, 0xfd6a), RxCr(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_MVTACHI, .tks = { RxCode(20, 0xfd170), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MVTACLO, .tks = { RxCode(20, 0xfd171), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_MVTC, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x30), RxCr(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_MVTC, .tks = { RxCode(16, 0xfd68), RxReg(4, V0), RxCr(4, V1), RxEnd } }, + { .op = RX_OP_MVTIPL, .tks = { RxCode(20, 0x75700), RxImm(4, V0), RxEnd } }, + { .op = RX_OP_POP, .tks = { RxCode(12, 0x7eb), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_POPC, .tks = { RxCode(12, 0x7ee), RxCr(4, V0), RxEnd } }, + { .op = RX_OP_POPM, .tks = { RxCode(8, 0x6f), RxRegLimit(4, V0), RxRegLimit(4, V1), RxEnd } }, // special rn + { .op = RX_OP_PUSH, .tks = { RxCode(10, 0x1fa), RxSz(2), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_PUSH, .tks = { RxCode(6, 0x3d), RxLdPart(2, V0), RxReg(4, V0), RxCode(2, 0x2), RxSz(2), DspData(V0), RxEnd } }, + { .op = RX_OP_PUSHC, .tks = { RxCode(12, 0x7ec), RxCr(4, V0), RxEnd } }, + { .op = RX_OP_PUSHM, .tks = { RxCode(8, 0x6e), RxRegLimit(4, V0), RxRegLimit(4, V1), RxEnd } }, // special rn + { .op = RX_OP_RACW, .tks = { RxCode(19, 0x7e8c0), RxImm(1, V0), RxCode(4, 0x0), RxEnd } }, + { .op = RX_OP_REVL, .tks = { RxCode(16, 0xfd67), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_REVW, .tks = { RxCode(16, 0xfd65), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_RMPA, .tks = { RxCode(14, 0x1fe3), RxSz(2), RxEnd } }, + { .op = RX_OP_ROLC, .tks = { RxCode(12, 0x7e5), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_RORC, .tks = { RxCode(12, 0x7e4), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_ROTL, .tks = { RxCode(15, 0x7eb7), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ROTL, .tks = { RxCode(16, 0xfd66), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ROTR, .tks = { RxCode(15, 0x7eb6), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ROTR, .tks = { RxCode(16, 0xfd64), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_ROUND, .tks = { RxCode(14, 0x3f26), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_RTE, .tks = { RxCode(16, 0x7f95), RxEnd } }, + { .op = RX_OP_RTFI, .tks = { RxCode(16, 0x7f94), RxEnd } }, + { .op = RX_OP_RTS, .tks = { RxCode(8, 0x02), RxEnd } }, + { .op = RX_OP_RTE, .tks = { RxCode(16, 0x7f94), RxEnd } }, + { .op = RX_OP_RTSD, .tks = { RxCode(8, 0x67), ImmFixedData(V0, 8), RxEnd } }, + { .op = RX_OP_RTSD, .tks = { RxCode(8, 0x3f), RxRegLimit(4, V0), RxRegLimit(4, V1), ImmFixedData(V0, 8), RxEnd } }, // special rn + { .op = RX_OP_SAT, .tks = { RxCode(12, 0x7e3), RxReg(4, V0), RxEnd } }, + { .op = RX_OP_SATR, .tks = { RxCode(16, 0x7f93), RxEnd } }, + { .op = RX_OP_WAIT, .tks = { RxCode(16, 0x7f96), RxEnd } }, + { .op = RX_OP_SCCOND, .tks = { RxCode(12, 0xfcd), RxSz(2), RxLd(2, V1), RxReg(4, V1), RxCond(4, V0), DspData(V1), RxEnd } }, + { .op = RX_OP_SCMPU, .tks = { RxCode(16, 0x7f83), RxEnd } }, + { .op = RX_OP_SETPSW, .tks = { RxCode(12, 0x7fa), RxCb(4), RxEnd } }, + { .op = RX_OP_SHAR, .tks = { RxCode(7, 0x35), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHAR, .tks = { RxCode(16, 0xfd61), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHAR, .tks = { RxCode(11, 0x07ed), RxImm(5, V0), RxReg(4, V1), RxReg(4, V2), RxEnd } }, + + { .op = RX_OP_SHLL, .tks = { RxCode(7, 0x36), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHLL, .tks = { RxCode(16, 0xfd62), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHLL, .tks = { RxCode(11, 0x07ee), RxImm(5, V0), RxReg(4, V1), RxReg(4, V2), RxEnd } }, + + { .op = RX_OP_SHLR, .tks = { RxCode(7, 0x34), RxImm(5, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHLR, .tks = { RxCode(16, 0xfd60), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SHLR, .tks = { RxCode(11, 0x07ec), RxImm(5, V0), RxReg(4, V1), RxReg(4, V2), RxEnd } }, + + { .op = RX_OP_SBB, .tks = { RxCode(16, 0xfc03), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SBB, .tks = { RxCode(14, 0x01a8), RxLdPart(2, V0), RxCode(8, 0x00), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + + { .op = RX_OP_SUB, .tks = { RxCode(8, 0x60), RxImm(4, V0), RxReg(4, V1), RxEnd } }, + { .op = RX_OP_SUB_UB, .tks = { RxCode(6, 0x10), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_SUB, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x0), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_SUB, .tks = { RxCode(12, 0xff0), RxReg(4, V2), RxReg(4, V0), RxReg(4, V1), RxEnd } }, + + { .op = RX_OP_SMOVB, .tks = { RxCode(16, 0x7f8b), RxEnd } }, + { .op = RX_OP_SMOVF, .tks = { RxCode(16, 0x7f8f), RxEnd } }, + { .op = RX_OP_SMOVU, .tks = { RxCode(16, 0x7f87), RxEnd } }, + { .op = RX_OP_SSTR, .tks = { RxCode(14, 0x1fe2), RxSz(2), RxEnd } }, + { .op = RX_OP_STNZ, .tks = { RxCode(12, 0x0fd7), RxLi(2, V0), RxCode(6, 0x0f), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_STZ, .tks = { RxCode(12, 0x0fd7), RxLi(2, V0), RxCode(6, 0x0e), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_SUNTIL, .tks = { RxCode(14, 0x1fe0), RxSz(2), RxEnd } }, + { .op = RX_OP_SWHILE, .tks = { RxCode(14, 0x1fe1), RxSz(2), RxEnd } }, + + { .op = RX_OP_TST, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x0c), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_TST_UB, .tks = { RxCode(14, 0x3f0c), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_TST, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x0c), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + + { .op = RX_OP_XCHG_UB, .tks = { RxCode(14, 0x3f10), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_XCHG, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x8), RxLd(2, V0), RxCode(8, 0x10), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_XOR, .tks = { RxCode(12, 0xfd7), RxLi(2, V0), RxCode(6, 0x0d), RxReg(4, V1), ImmData(V0), RxEnd } }, + { .op = RX_OP_XOR, .tks = { RxCode(8, 0x06), RxMi(2), RxCode(4, 0x08), RxLd(2, V0), RxCode(8, 0x0d), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, + { .op = RX_OP_XOR_UB, .tks = { RxCode(14, 0x3f0d), RxLd(2, V0), RxReg(4, V0), RxReg(4, V1), DspData(V0), RxEnd } }, +}; + +#undef RxCode +#undef RxReg +#undef RxLi +#undef RxIm +#undef RxMi +#undef RxLd +#undef RxLds +#undef RxLdd +#undef RxCond +#undef RxDsp +#undef RxSz +#undef RxAd +#undef RxCr +#undef RxCb +#undef RxDspSplit +#undef RxIgnore +#undef RxJmp +#undef RxHook +#undef RxEnd +#undef ImmData +#undef DspData +#undef PcDspData +#undef ImmFixedData +#undef RxRi + +#undef V0 +#undef V1 +#undef V2 \ No newline at end of file diff --git a/librz/asm/arch/rx/rx_opcode_detail.h b/librz/asm/arch/rx/rx_opcode_detail.h new file mode 100644 index 0000000000..6a4c868c42 --- /dev/null +++ b/librz/asm/arch/rx/rx_opcode_detail.h @@ -0,0 +1,281 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RX_OPCODE_DETAIL_H +#define RX_OPCODE_DETAIL_H + +#include + +typedef enum { + RX_OP_INVALID, + RX_OP_ABS, + RX_OP_ADC, + RX_OP_ADD, + RX_OP_ADD_UB, + RX_OP_AND, + RX_OP_AND_UB, + RX_OP_BCLR, + RX_OP_BCND_S, + RX_OP_BCND_B, + RX_OP_BCND_W, + RX_OP_BMCND, + RX_OP_BNOT, + RX_OP_BRA_S, + RX_OP_BRA_B, + RX_OP_BRA_W, + RX_OP_BRA_A, + RX_OP_BRA_L, + RX_OP_BRK, + RX_OP_BSET, + RX_OP_BSR_W, + RX_OP_BSR_A, + RX_OP_BSR_L, + RX_OP_BTST, + RX_OP_CLRPSW, + RX_OP_CMP, + RX_OP_CMP_UB, + RX_OP_DIV, + RX_OP_DIV_UB, + RX_OP_DIVU, + RX_OP_DIVU_UB, + RX_OP_EMUL, + RX_OP_EMUL_UB, + RX_OP_EMULU, + RX_OP_EMULU_UB, + RX_OP_FADD, + RX_OP_FCMP, + RX_OP_FDIV, + RX_OP_FMUL, + RX_OP_FSUB, + RX_OP_FTOI, + RX_OP_INT, + RX_OP_ITOF, + RX_OP_ITOF_UB, + RX_OP_JMP, + RX_OP_JSR, + RX_OP_MACHI, + RX_OP_MACLO, + RX_OP_MAX, + RX_OP_MAX_UB, + RX_OP_MIN, + RX_OP_MIN_UB, + RX_OP_MOV, + RX_OP_MOVU, + RX_OP_MUL, + RX_OP_MUL_UB, + RX_OP_MULHI, + RX_OP_MULLO, + RX_OP_MVFACHI, + RX_OP_MVFACMI, + RX_OP_MVFC, + RX_OP_MVTACHI, + RX_OP_MVTACLO, + RX_OP_MVTC, + RX_OP_MVTIPL, + RX_OP_NEG, + RX_OP_NOP, + RX_OP_NOT, + RX_OP_OR, + RX_OP_OR_UB, + RX_OP_POP, + RX_OP_POPC, + RX_OP_POPM, + RX_OP_PUSH, + RX_OP_PUSHC, + RX_OP_PUSHM, + RX_OP_RACW, + RX_OP_REVL, + RX_OP_REVW, + RX_OP_RMPA, + RX_OP_ROLC, + RX_OP_RORC, + RX_OP_ROTL, + RX_OP_ROTR, + RX_OP_ROUND, + RX_OP_RTE, + RX_OP_RTFI, + RX_OP_RTS, + RX_OP_RTSD, + RX_OP_SAT, + RX_OP_SATR, + RX_OP_SBB, + RX_OP_SCCOND, + RX_OP_SCMPU, + RX_OP_SETPSW, + RX_OP_SHAR, + RX_OP_SHLL, + RX_OP_SHLR, + RX_OP_SMOVB, + RX_OP_SMOVF, + RX_OP_SMOVU, + RX_OP_SSTR, + RX_OP_STNZ, + RX_OP_STZ, + RX_OP_SUB, + RX_OP_SUB_UB, + RX_OP_SUNTIL, + RX_OP_SWHILE, + RX_OP_TST, + RX_OP_TST_UB, + RX_OP_WAIT, + RX_OP_XCHG, + RX_OP_XCHG_UB, + RX_OP_XOR, + RX_OP_XOR_UB, + _RX_OP_COUNT, +} RxOpCode; + +typedef enum { + // General Purpose Register + // R0 as SP + RX_REG_R0, + RX_REG_R1, + RX_REG_R2, + RX_REG_R3, + RX_REG_R4, + RX_REG_R5, + RX_REG_R6, + RX_REG_R7, + RX_REG_R8, + RX_REG_R9, + RX_REG_R10, + RX_REG_R11, + RX_REG_R12, + RX_REG_R13, + RX_REG_R14, + RX_REG_R15, + // Control Register + RX_REG_ISP, + RX_REG_USP, + RX_REG_INTB, + RX_REG_PC, + RX_REG_PSW, + RX_REG_BPC, + RX_REG_BPSW, + RX_REG_FINTV, + RX_REG_FPSW, + RX_REG_ACC, // dsp + RX_REG_RESERVED +} RxReg; + +typedef enum { + RX_EXT_NON, + RX_EXT_UB, + RX_EXT_B, + RX_EXT_W, + RX_EXT_L, + RX_EXT_UW, + _RX_EXT_COUNT, +} RxOpExtMark; + +typedef enum { + RX_TOKEN_NON, + RX_TOKEN_INST, + RX_TOKEN_LD, + RX_TOKEN_LI, + RX_TOKEN_LD_PART, + RX_TOKEN_MI, + RX_TOKEN_IMM, + RX_TOKEN_REG, + RX_TOKEN_COND, + RX_TOKEN_DSP, + RX_TOKEN_DSP_SPLIT, + RX_TOKEN_IGNORE, + RX_TOKEN_SZ, + RX_TOKEN_AD, + RX_TOKEN_CR, + RX_TOKEN_CB, + RX_TOKEN_JMP, + RX_TOKEN_DATA, + RX_TOKEN_RI, + RX_TOKEN_REG_LIMIT, + RX_TOKEN_HOOK, // for validation at the end of token +} RxTokenType; + +struct rx_inst_token_t { + ut8 tk_len; + ut32 detail; +}; +typedef struct rx_inst_token_t RxInstToken; + +struct rx_oprand_related_token_t { + ut8 tk_len; + ut8 vid; +}; +typedef struct rx_oprand_related_token_t RxDispLenToken; +typedef struct rx_oprand_related_token_t RxImmLenToken; +typedef struct rx_oprand_related_token_t RxVarDispLenToken; +typedef struct rx_oprand_related_token_t RxRegToken; +typedef struct rx_oprand_related_token_t RxImmToken; +typedef struct rx_oprand_related_token_t RxControlRegToken; +typedef struct rx_oprand_related_token_t RxCondToken; + +typedef struct { + ut8 fixed_len; + ut8 vid; + ut8 data_type; +} RxDataToken; + +typedef struct { + ut8 tk_len; + ut8 vid; + ut8 interval; + ut8 tk_len_more; +} RxDspSplitToken; + +struct rx_simple_token_t { + ut8 tk_len; +}; +typedef struct rx_simple_token_t RxMemexToken; +typedef struct rx_simple_token_t RxSizeToken; +typedef struct rx_simple_token_t RxFlagToken; +typedef struct rx_simple_token_t RxAddrToken; +typedef struct rx_simple_token_t RxSimpleToken; +typedef struct rx_simple_token_t RxDispToken; + +typedef union rx_token_union { + RxInstToken inst; + RxDispLenToken ld; + RxImmLenToken li; + RxVarDispLenToken ld_part; // ld for partial valid bits range + RxMemexToken mi; + RxImmToken imm; + RxRegToken reg; + RxRegToken reg_li; // reg for limited range + RxRegToken ri; + RxCondToken cond; + RxDispToken dsp; + RxDspSplitToken dsp_sp; + RxSizeToken sz; + RxAddrToken ad; + RxControlRegToken cr; + RxFlagToken cb; + RxSimpleToken jmp; + RxDataToken data; + RxSimpleToken reserved; +} RxTokenUnion; + +struct rx_token_t { + RxTokenType type; + RxTokenUnion tk; +}; +typedef struct rx_token_t RxToken; + +#define MAX_TOKEN 10 +#define RX_SET_V1_SZ 90 +#define RX_SET_V2_SZ (RX_SET_V1_SZ + 19) +#define RX_SET_V3_SZ (RX_SET_V2_SZ + 4) +#define RX_SET_SIZE RX_SET_V3_SZ + +// todo: accurate num +#define RX_DESC_SIZE (RX_SET_SIZE * 3) + +struct rx_desc_t { + RxOpCode op; + RxToken tks[MAX_TOKEN]; +}; +typedef struct rx_desc_t RxDesc; + +extern RxDesc rx_inst_descs[RX_DESC_SIZE]; + +#endif \ No newline at end of file diff --git a/librz/asm/arch/rx/rx_str.inc b/librz/asm/arch/rx/rx_str.inc new file mode 100644 index 0000000000..1404cc1402 --- /dev/null +++ b/librz/asm/arch/rx/rx_str.inc @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: 2024 heersin +// SPDX-License-Identifier: LGPL-3.0-only + +const char *opnames[_RX_OP_COUNT] = { + "invalid", + "abs", + "adc", + "add", + "add.UB", + "and", + "and.UB", + "bclr", + // bcnd.s, cnd provided by cond_names + "b%s.S", + "b%s.B", + "b%s.W", + "bm%s", + "bnot", + "bra.S", + "bra.B", + "bra.W", + "bra.A", + "bra.L", + "brk", + "bset", + "bsr.W", + "bsr.A", + "bsr.L", + "btst", + "clrpsw", + "cmp", + "cmp.UB", + "div", + "div.UB", + "divu", + "divu.UB", + "emul", + "emul.UB", + "emulu", + "emulu.UB", + "fadd", + "fcmp", + "fdiv", + "fmul", + "fsub", + "ftoi", + "int", + "itof", + "itof.UB", + "jmp", + "jsr", + "machi", + "maclo", + "max", + "max.UB", + "min", + "min.UB", + "mov", + "movu", + "mul", + "mul.UB", + "mulhi", + "mullo", + "mvfachi", + "mvfacmi", + "mvfc", + "mvtachi", + "mvtaclo", + "mvtc", + "mvtipl", + "neg", + "nop", + "not", + "or", + "or.UB", + "pop", + "popc", + "popm", + "push", + "pushc", + "pushm", + "racw", + "revl", + "revw", + "rmpa", + "rolc", + "rorc", + "rotl", + "rotr", + "round", + "rte", + "rtfi", + "rts", + "rtsd", + "sat", + "satr", + "sbb", + "sc%s", + "scmpu", + "setpsw", + "shar", + "shll", + "shlr", + "smovb", + "smovf", + "smovu", + "sstr", + "stnz", + "stz", + "sub", + "sub.UB", + "suntil", + "swhile", + "tst", + "tst.UB", + "wait", + "xchg", + "xchg.UB", + "xor", + "xor.UB" +}; + +const char *extmark_names[_RX_EXT_COUNT] = { + "B", + "W", + "L", + "UW", +}; + +const char *reg_names[RX_REG_RESERVED] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "isp", "usp", "intb", "pc", + "psw", "bpc", "bpsw", "fintv", + "fpsw", "acc" +}; + +const char *flag_names[7] = { + "C", + "Z", + "S", + "O", + "I", + "U", + "", +}; + +const char *cond_names[RX_COND_RESERVED] = { + "eq", + "ne", + "geu", + "ltu", + "gtu", + "leu", + "pz", + "n", + "ge", + "lt", + "gt", + "le", + "o", + "no", + "ra", + "jump", +}; + +#define RxNameOp(op) ((op) < _RX_OP_COUNT ? opnames[(op)] : "invalid") +#define RxNameExt(ext) ((ext) < _RX_EXT_COUNT ? extmark_names[((ext)-RX_EXT_B)] : "invalid") +#define RxNameReg(reg) ((reg) < RX_REG_RESERVED ? reg_names[(reg)-RX_REG_R0] : "invalid") +#define RxNameFlag(flag) ((flag) < 7 ? flag_names[(flag)] : "invalid") +#define RxNameCond(cond) ((cond) < RX_COND_RESERVED ? cond_names[(cond)] : "invalid") \ No newline at end of file diff --git a/librz/asm/meson.build b/librz/asm/meson.build index bc3f5336da..d0f6ea0b99 100644 --- a/librz/asm/meson.build +++ b/librz/asm/meson.build @@ -37,6 +37,7 @@ asm_plugins_list = [ 'pyc', 'rl78', 'rsp', + 'rx', 'sh', 'snes', 'sparc_cs', @@ -124,6 +125,7 @@ rz_asm_sources = [ 'p/asm_propeller.c', 'p/asm_pyc.c', 'p/asm_rl78.c', + 'p/asm_rx.c', 'p/asm_rsp.c', 'p/asm_sh.c', 'p/asm_snes.c', @@ -220,6 +222,9 @@ rz_asm_sources = [ 'arch/rl78/rl78_maps.c', 'arch/rl78/rl78_operand.c', 'arch/rl78/rl78.c', + 'arch/rx/rx.c', + 'arch/rx/rx_opcode_detail.c', + 'arch/rx/rx_inst.c', 'arch/rsp/rsp_idec.c', 'arch/sh/disassembler.c', 'arch/sh/assembler.c', diff --git a/librz/asm/p/asm_rx.c b/librz/asm/p/asm_rx.c new file mode 100644 index 0000000000..586700ff98 --- /dev/null +++ b/librz/asm/p/asm_rx.c @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2024 Heersin +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include +#include +#include +#include + +static int disassemble(RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) { + RxInst inst = { 0 }; + st32 bytes_read; + + if (!rx_dis(&inst, &bytes_read, buf, len)) { + rz_asm_op_set_asm(op, "(invalid)"); + return bytes_read; + } + + rx_inst_stringify(&inst, &op->buf_asm); + op->size = bytes_read; + return bytes_read; +} + +RzAsmPlugin rz_asm_plugin_rx = { + .name = "rx", + .arch = "rx", + .desc = "Renesas RX Family disassembler", + .author = "Heersin", + .license = "LGPL3", + .bits = 32, + .endian = RZ_SYS_ENDIAN_LITTLE | RZ_SYS_ENDIAN_BIG, + .disassemble = &disassemble +}; + +#ifndef RZ_PLUGIN_INCORE +RZ_API RzLibStruct rizin_plugin = { + .type = RZ_LIB_TYPE_ASM, + .data = &rz_asm_plugin_rx, + .version = RZ_VERSION +}; +#endif \ No newline at end of file diff --git a/librz/bin/format/elf/elf_info.c b/librz/bin/format/elf/elf_info.c index 0d07844ef4..df6675c586 100644 --- a/librz/bin/format/elf/elf_info.c +++ b/librz/bin/format/elf/elf_info.c @@ -269,6 +269,7 @@ static const struct arch_translation arch_translation_table[] = { { EM_MICROBLAZE, "microblaze.gnu" }, { EM_RISCV, "riscv" }, { EM_RL78, "rl78" }, + { EM_RX, "rx" }, { EM_VAX, "vax" }, { EM_XTENSA, "xtensa" }, { EM_LANAI, "lanai" }, diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 6b6a5b1049..ad31d32d5f 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -2459,6 +2459,7 @@ extern RzAnalysisPlugin rz_analysis_plugin_riscv; extern RzAnalysisPlugin rz_analysis_plugin_riscv_cs; extern RzAnalysisPlugin rz_analysis_plugin_rl78; extern RzAnalysisPlugin rz_analysis_plugin_rsp; +extern RzAnalysisPlugin rz_analysis_plugin_rx; extern RzAnalysisPlugin rz_analysis_plugin_sh; extern RzAnalysisPlugin rz_analysis_plugin_snes; extern RzAnalysisPlugin rz_analysis_plugin_sparc_cs; diff --git a/librz/include/rz_asm.h b/librz/include/rz_asm.h index f70a370f9d..5e2db15499 100644 --- a/librz/include/rz_asm.h +++ b/librz/include/rz_asm.h @@ -261,6 +261,7 @@ extern RzAsmPlugin rz_asm_plugin_riscv; extern RzAsmPlugin rz_asm_plugin_riscv_cs; extern RzAsmPlugin rz_asm_plugin_rl78; extern RzAsmPlugin rz_asm_plugin_rsp; +extern RzAsmPlugin rz_asm_plugin_rx; extern RzAsmPlugin rz_asm_plugin_sh; extern RzAsmPlugin rz_asm_plugin_snes; extern RzAsmPlugin rz_asm_plugin_sparc_cs; diff --git a/test/db/analysis/rx b/test/db/analysis/rx new file mode 100644 index 0000000000..d71fa1d800 --- /dev/null +++ b/test/db/analysis/rx @@ -0,0 +1,367 @@ +NAME=RX morse info +FILE=bins/rx/morse +ARGS= +CMDS=< 0xfff40154 mov.L [r10], r5, +| :| 0xfff40156 mov.B [r5], r5, +| :| 0xfff40158 movu.B r5, r5, +| :| 0xfff4015a cmp #0x60, r5, +| ,===< 0xfff4015d bleu.B #0x2d, +| |:| 0xfff4015f mov.L [r10], r5, +| |:| 0xfff40161 mov.B [r5], r5, +| |:| 0xfff40163 movu.B r5, r5, +| |:| 0xfff40165 cmp #0x7a, r5, +| ,====< 0xfff40168 bgtu.B #0x22, +| ||:| 0xfff4016a mov.L [r10], r5, +| ||:| 0xfff4016c mov.B [r5], r5, +| ||:| 0xfff4016e movu.B r5, r5, +| ||:| 0xfff40170 add #0x9f, r5, r5 +| ||:| 0xfff40173 mov #0x10, r4, +| ||:| 0xfff40179 mov.L [r5,r4], r5, +| ||:| 0xfff4017c mov.L r5, 0x1[r0], +| ||:| 0xfff4017e mov.L #loc..LC26, [r0], +| ||:| 0xfff40184 bsr.A #0x14d, ; sym._printf ; int printf(const char *format) +| ,=====< 0xfff40188 bra.B #0x3e, +| |``---> 0xfff4018a mov.L [r10], r5, +| | :| 0xfff4018c mov.B [r5], r5, +| | :| 0xfff4018e movu.B r5, r5, +| | :| 0xfff40190 cmp #0x40, r5, +| | ,===< 0xfff40193 bleu.B #0x2c, +| | |:| 0xfff40195 mov.L [r10], r5, +| | |:| 0xfff40197 mov.B [r5], r5, +| | |:| 0xfff40199 movu.B r5, r5, +| | |:| 0xfff4019b cmp #0x5a, r5, +| |,====< 0xfff4019e bgtu.B #0x21, +| |||:| 0xfff401a0 mov.L [r10], r5, +| |||:| 0xfff401a2 mov.B [r5], r5, +| |||:| 0xfff401a4 movu.B r5, r5, +| |||:| 0xfff401a6 add #0xbf, r5, r5 +| |||:| 0xfff401a9 mov #0x10, r4, +| |||:| 0xfff401af mov.L [r5,r4], r5, +| |||:| 0xfff401b2 mov.L r5, 0x1[r0], +| |||:| 0xfff401b4 mov.L #loc..LC26, [r0], +| |||:| 0xfff401ba bsr.A #0x117, ; sym._printf ; int printf(const char *format) +.. +| |``---> 0xfff401bf mov #0x20, r1, +| | :| 0xfff401c2 bsr.A #0x12e, ; sym._putchar ; int putchar(int c) +| `-----> 0xfff401c6 mov.L [r10], r5, +| :| 0xfff401c8 add #0x1, r5, +| :| 0xfff401ca mov.L r5, [r10], +| :`-> 0xfff401cc mov.L [r10], r5, +| : 0xfff401ce mov.B [r5], r5, +| : 0xfff401d0 movu.B r5, r5, +| : 0xfff401d2 cmp #0x0, r5, +\ `==< 0xfff401d4 bne.B #0x80, + ; CALL XREF from sym._main @ 0xfff40296 +/ sym._morse_to_text(); +| 0xfff401e0 push.L r10, +| 0xfff401e2 add #0xf4, r0, r10 +| 0xfff401e5 mov.L r10, r0, +| 0xfff401e7 mov.L r1, 0x2[r10], +| 0xfff401ea mov #loc..LC27, r2, +| 0xfff401f0 mov.L 0x2[r10], r1, +| 0xfff401f3 bsr.A #0x178, ; sym._strtok ; char *strtok(char *s1, const char *s2) +| 0xfff401f7 mov.L r1, [r10], +| ,=< 0xfff401f9 bra.B #0x4e, +| .--> 0xfff401fb mov.L #0x0, 0x1[r10], +| ,===< 0xfff401ff bra.B #0x32, +| .----> 0xfff40201 mov #0x10, r5, +| :|:| 0xfff40207 mov.L 0x1[r10], r4, +| :|:| 0xfff4020a mov.L [r4,r5], r5, +| :|:| 0xfff4020d mov.L r5, r2, +| :|:| 0xfff4020f mov.L [r10], r1, +| :|:| 0xfff40211 bsr.A #0x14b, ; sym._strcmp ; int strcmp(const char *s1, const char *s2) +| :|:| 0xfff40215 mov.L r1, r5, +| :|:| 0xfff40217 cmp #0x0, r5, +| ,=====< 0xfff40219 bne.B #0x10, +| |:|:| 0xfff4021b mov.L 0x1[r10], r5, +| |:|:| 0xfff4021e add #0x41, r5, r5 +| |:|:| 0xfff40221 mov.L r5, r1, +| |:|:| 0xfff40223 bsr.A #0xcd, ; sym._putchar ; int putchar(int c) +| ,======< 0xfff40227 bra.B #0x12, +| |`-----> 0xfff40229 mov.L 0x1[r10], r5, +| | :|:| 0xfff4022c add #0x1, r5, +| | :|:| 0xfff4022e mov.L r5, 0x1[r10], +| | :`---> 0xfff40231 mov.L 0x1[r10], r5, +| | : :| 0xfff40234 cmp #0x19, r5, +| | `====< 0xfff40237 ble.B #0xca, +| `------> 0xfff40239 mov #loc..LC27, r2, +| :| 0xfff4023f mov #0x0, r1, +| :| 0xfff40241 bsr.A #0x12a, ; sym._strtok ; char *strtok(char *s1, const char *s2) +| :| 0xfff40245 mov.L r1, [r10], +| :`-> 0xfff40247 mov.L [r10], r5, +| : 0xfff40249 cmp #0x0, r5, +\ `==< 0xfff4024b bne.B #0xb0, +/ void sym._exit(int status); +| 0xfff402a1 push.L r7, +| 0xfff402a3 mov.L r1, r7, +| 0xfff402a5 mov #0x0, r2, +| 0xfff402a7 bsr.A #0x132b, ; sym.___call_exitprocs +| 0xfff402ab mov #obj.__global_impure_ptr, r5, +| 0xfff402b1 mov.L [r5], r1, +| 0xfff402b3 mov.L 0xf[r1], r5, +| 0xfff402b5 cmp #0x0, r5, +| ,=< 0xfff402b7 beq.S #0x3, +| | 0xfff402b8 jsr r5, +| `-> 0xfff402ba mov.L r7, r1, +\ 0xfff402bc bsr.A #0x471c, ; sym.__exit ; void _exit(int status) +EOF +RUN + +NAME=RX morse function list +FILE=bins/rx/morse +ARGS= +CMDS=< 42 sym._rx_run_preinit_array +0xfff4006e 1 18 sym._rx_run_init_array +0xfff40080 1 15 sym._rx_run_fini_array +0xfff400b0 3 32 -> 23 sym._deregister_tm_clones +0xfff400d0 4 43 sym._register_tm_clones +0xfff400fb 5 43 sym.___do_global_dtors_aux +0xfff40126 1 1 sym._call___do_global_dtors_aux +0xfff40127 3 28 sym._frame_dummy +0xfff40143 1 1 sym._call_frame_dummy +0xfff40148 10 142 -> 141 sym._text_to_morse +0xfff401e0 8 109 sym._morse_to_text +0xfff40257 1 74 sym._main +0xfff402a1 3 31 sym._exit +0xfff402c0 1 17 sym.__printf_r +0xfff402d1 1 25 sym._printf +0xfff402ea 1 6 sym.__putchar_r +0xfff402f0 1 18 sym._putchar +0xfff40302 8 74 sym.__putc_r +0xfff4034c 1 16 sym._putc +0xfff4035c 1 15 sym._strcmp +0xfff4036b 1 17 sym._strtok +0xfff4037c 13 103 -> 81 sym.___strtok_r +0xfff403e3 1 6 sym._strtok_r +0xfff403e9 35 4023 -> 411 sym.__vfprintf_r +0xfff413ee 1 18 sym._vfprintf +0xfff41400 5 119 sym.___sbprintf +0xfff41477 1 10 sym.___swbuf_r +0xfff41511 1 16 sym.___swbuf +0xfff41521 1 16 sym.___swsetup_r +0xfff415d2 11 112 -> 79 sym.___call_exitprocs +0xfff41657 11 263 -> 215 sym._quorem +0xfff4175e 33 2479 -> 400 sym.__dtoa_r +0xfff4210d 6 223 -> 53 sym.___sflush_r +0xfff42229 5 43 -> 34 sym.__fflush_r +0xfff42254 3 35 sym._fflush +0xfff42277 1 3 sym.___fp_lock +0xfff4227a 1 73 sym._std +0xfff422c3 1 10 sym.__cleanup_r +0xfff422cd 1 3 sym.___fp_unlock +0xfff422d0 3 46 sym.___sfmoreglue +0xfff422fe 1 12 sym.__cleanup +0xfff4230a 3 69 sym.___sinit +0xfff4234f 11 132 sym.___sfp +0xfff423d8 1 1 sym.___sfp_lock_acquire +0xfff423d9 1 1 sym.___sfp_lock_release +0xfff423da 1 1 sym.___sinit_lock_acquire +0xfff423db 1 1 sym.___sinit_lock_release +0xfff423dc 1 18 sym.___fp_lock_all +0xfff423ee 1 18 sym.___fp_unlock_all +0xfff42400 3 74 sym.__malloc_trim_r +0xfff424a3 19 392 -> 214 sym.__free_r +0xfff4262b 10 57 sym.__fwalk +0xfff42664 9 61 -> 52 sym.__fwalk_reent +0xfff426a1 1 5 sym.___localeconv_l +0xfff426a6 1 7 sym.__localeconv_r +0xfff426ad 1 7 sym._localeconv +0xfff426b4 4 40 sym.__setlocale_r +0xfff42700 1 11 sym.___locale_mb_cur_max +0xfff4270b 1 16 sym._setlocale +0xfff4271b 6 76 -> 51 sym.___swhatbuf_r +0xfff42767 8 123 -> 104 sym.___smakebuf_r +0xfff427e2 5 34 sym.__malloc_r +0xfff42c16 1 18 sym.__mbtowc_r +0xfff42c28 8 37 sym.___ascii_mbtowc +0xfff42c4d 1 11 sym._memchr +0xfff42c58 1 7 sym._memcpy +0xfff42c5f 1 7 sym._memset +0xfff42c66 1 1 sym.___malloc_lock +0xfff42c67 1 1 sym.___malloc_unlock +0xfff42c68 9 84 sym.__Balloc +0xfff42cbc 3 17 sym.__Bfree +0xfff42ccd 2 59 sym.___multadd +0xfff42d60 13 156 -> 151 sym.___s2b +0xfff42dfc 12 72 sym.___hi0bits +0xfff42e44 18 100 -> 85 sym.___lo0bits +0xfff42ea8 3 44 sym.___i2b +0xfff42ed4 22 291 -> 287 sym.___multiply +0xfff42ffc 13 122 -> 98 sym.___pow5mult +0xfff43076 12 179 -> 153 sym.___lshift +0xfff4312b 7 45 sym.___mcmp +0xfff4315a 12 190 sym.___mdiff +0xfff43251 3 37 -> 25 sym.___ulp +0xfff43276 7 86 -> 71 sym.___b2d +0xfff432cc 8 124 sym.___d2b +0xfff43348 4 69 sym.___ratio +0xfff4338d 4 34 sym.__mprec_log10 +0xfff433b0 6 50 sym.___copybits +0xfff433e2 10 59 sym.___any_on +0xfff4341d 1 22 sym.__sbrk_r +0xfff4343e 7 50 sym.___fpclassifyf +0xfff43470 4 31 sym.___sread +0xfff4348f 1 3 sym.___seofread +0xfff43492 3 48 sym.___swrite +0xfff434c2 1 15 sym.___sseek +0xfff434e1 1 6 sym.___sclose +0xfff434e7 1 13 sym._strlen +0xfff434f4 1 9 sym.___sprint_r +0xfff43554 139 2076 -> 1058 sym.__vfiprintf_r +0xfff43d70 1 18 sym._vfiprintf +0xfff43d82 5 119 sym.___sbprintf_0xfff43d82 +0xfff43df9 1 12 sym.__wctomb_r +0xfff43e05 5 25 sym.___ascii_wctomb +0xfff43e1e 1 26 sym.__write_r +0xfff43e43 4 60 sym.___assert_func +0xfff43e7f 1 8 sym.___assert +0xfff43e87 4 32 sym.__calloc_r +0xfff43efa 1 22 sym.__close_r +0xfff43f1b 1 9 sym.___errno +0xfff43f24 1 4 sym.__fclose_r +0xfff43fa1 1 14 sym._fclose +0xfff43faf 1 15 sym.__fiprintf_r +0xfff43fbe 1 25 sym._fiprintf +0xfff43fd7 13 115 -> 109 sym.___fputwc +0xfff4404c 3 26 sym.__fputwc_r +0xfff44066 4 41 sym._fputwc +0xfff4408f 1 24 sym.__fstat_r +0xfff440b2 39 671 -> 320 sym.___sfvwrite_r +0xfff44351 1 22 sym.__isatty_r +0xfff44372 1 26 sym.__lseek_r +0xfff44397 3 24 sym._memmove +0xfff443af 1 26 sym.__read_r +0xfff443d4 1 10 sym.__realloc_r +0xfff446f8 3 25 sym._cleanup_glue +0xfff44711 18 151 -> 129 sym.__reclaim_reent +0xfff447a8 3 29 sym.__wcrtomb_r +0xfff447d0 1 18 sym._wcrtomb +0xfff447e2 1 12 sym._abort +0xfff447ee 7 52 -> 50 sym.__init_signal_r +0xfff44822 6 50 sym.__signal_r +0xfff44856 9 81 -> 75 sym.__raise_r +0xfff448a7 12 81 -> 78 sym.___sigtramp_r +0xfff448f8 1 14 sym._raise +0xfff44906 1 16 sym._signal +0xfff44916 1 12 sym.__init_signal +0xfff44922 1 14 sym.___sigtramp +0xfff44930 1 24 sym.__kill_r +0xfff44953 1 4 sym.__getpid_r +0xfff44957 1 13 sym._close +0xfff44964 1 13 sym._fstat +0xfff44971 1 13 sym._getpid +0xfff4497e 1 12 sym._isatty +0xfff4498a 1 13 sym._kill +0xfff44997 1 13 sym._lseek +0xfff449a4 1 13 sym._read +0xfff449b1 3 26 sym._sbrk +0xfff449cb 1 13 sym._write +0xfff449d8 2 8 sym.__exit +0xfff449e0 104 971 -> 944 sym.___udivdi3 +0xfff44db0 97 938 sym.__COM_MOD64u +0xfff45548 1 4 loc..LANCHOR0_0xfff45548 +EOF +RUN \ No newline at end of file diff --git a/test/db/asm/rx b/test/db/asm/rx new file mode 100644 index 0000000000..6b3fcad69a --- /dev/null +++ b/test/db/asm/rx @@ -0,0 +1,204 @@ +d "itof.UB r1, r2," fc4712 +d "mov.W [r1], r2," dc12 +d "bclr r1, r2," fc6721 +d "itof 0x4[r1].L, r2," 06a1111204 +d "sub r1, r2, r3" ff0312 +d "xor.UB r1, r2," fc3712 +d "sub.UB r1, r2," 4312 +d "bmgeu #0x7, [r2]" fcfc22 +d "rts" 02 +d "emulu 0x4[r1].UW, r2," 06e1071204 +d "shlr #0x3, r1, r2" fd8312 +d "bset #0x7, [r2]," f027 +d "div 0x3[r1].B, r2," 0621081203 +d "divu.UB 0x3[r1], r2," fc251203 +d "divu [r1].L, r2," 06a00912 +d "mvtipl #0x2," 757002 +d "racw #0x0," fd1800 +d "mul.UB r1, r2," 4f12 +d "setpsw C," 7fa0 +d "add [r1].L, r2," 068812 +d "bltu.B #0x5," 2305 +d "mov.W [r1,r2], r3," fe5123 +d "or #0x8, r1," 6581 +d "bnot #0x1f, r2," fdfff2 +d "rmpa.W" 7f8d +d "bclr r1, [r2]," fc6421 +d "cmp #0x7, r2," 6172 +d "emul #0xa, r2," fd74620a +d "max #0xa, r2," fd74420a +d "or r1, r2, r3" ff5312 +d "revw r1, r2," fd6512 +d "shlr #0x3, r2," 6832 +d "shar #0x3, r2," 6a32 +d "rtfi" 7f94 +d "shar r1, r2," fd6112 +d "or 0x2[r1].L, r2," 06951202 +d "smovb" 7f8b +d "xor 0x4[r1].L, r2," 06a10d1204 +d "btst #0x7, [r2]," f427 +d "emul [r1].L, r2," 06a00612 +d "push.B r1," 7e81 +d "setpsw Z," 7fa1 +d "bra.L r2," 7f42 +d "mov #0x0, r2," 6602 +d "mov.W r1, [r2]," d321 +d "max 0x3[r1].B, r2," 0621041203 +d "max.UB r1, r2," fc1312 +d "fmul [r1], r2," fc8c12 +d "bra.L r1," 7f41 +d "mullo r1, r2," fd0112 +d "bsr.L r2," 7f52 +d "satr" 7f93 +d "add #0x7f, r1, r2" 71127f +d "mov.B [-r1], r2," fd2c12 +d "and [r1].UW, r2," 06d012 +d "mvfachi r1," fd1f01 +d "clrpsw C," 7fb0 +d "bmeq #0x1f, r2" fdff02 +d "round [r1], r2," fc9812 +d "rotl r1, r2," fd6612 +d "btst r1, r2," fc6b21 +d "emul 0x4[r1].W, r2," 0661061204 +d "popc psw," 7ee0 +d "fdiv [r1], r2," fc9012 +d "neg r1, r2," fc0712 +d "emul.UB r1, r2," fc1b12 +d "mov.L r1, r2," ef12 +d "bnot r1, [r2]," fc6c21 +d "or [r1].L, r2," 069412 +d "movu.W 0x1[r1], r2," b81a +d "mul #0xa, r2," 63a2 +d "bnot r1, r2," fc6f21 +d "mov.B r1, [r2+]," fd2021 +d "abs r0," 7e20 +d "neg r1," 7e11 +d "min #0xa, r2," fd74520a +d "bsr.L r1," 7f51 +d "div.UB r1, r2," fc2312 +d "add #0xf, r2," 62f2 +d "rorc r1," 7e41 +d "scgeu.L r2," fcdb22 +d "suntil.W" 7f81 +d "or.UB r1, r2," 5712 +d "tst #0x7, r2," fd74c207 +d "add r1, r2, r3" ff2312 +d "mvtc r1, usp," fd6812 +d "divu.UB r1, r2," fc2712 +d "revl r1, r2," fd6712 +d "adc [r1].L, r2," 06a00212 +d "ftoi [r1], r2," fc9412 +d "machi r1, r2," fd0412 +d "sat r1," 7e31 +d "max [r1].L, r2," 06a00412 +d "mul [r1].L, r2," 068c12 +d "fsub [r1], r2," fc8012 +d "tst.UB 0x1[r1], r2," fc311201 +d "sbb [r1], r2," 06a00012 +d "sub #0xf, r2," 60f2 +d "mul r1, r2, r3" ff3312 +d "bra.W #0xfc8," 38c80f +d "and #0xf, r2," 64f2 +d "btst r1, [r2]," fc6821 +d "mulhi r0, r0," fd0000 +d "rolc r1," 7e51 +d "bnot #0x7, [r2]," fcfc2f +d "not r1," 7e01 +d "adc r1, r2," fc0b12 +d "emulu.UB r1, r2," fc1f12 +d "add.UB [r1], r2," 4812 +d "mul 0x2[r1].W, r2," 064d1202 +d "adc #0x7f, r2," fd74227f +d "fmul r1, r2," fc8f12 +d "mvfacmi r1," fd1f21 +d "abs r1, r0," fc0f10 +d "cmp.UB r1, r2," 4712 +d "push.L [r1]," f41a +d "btst #0x1f, r2," 7df2 +d "xor #0x8, r1," fd74d108 +d "maclo r1, r2," fd0512 +d "popm r4, r8," 6f48 +d "mov.B [r1+], r2," fd2812 +d "ftoi r1, r2," fc9712 +d "mvfc usp, r1," fd6a21 +d "rotr #0x1, r1," fd6c11 +d "bra.A #0x0," 04000000 +d "rtsd #0x1," 6701 +d "not r1, r2," fc3b12 +d "stz #0x1, r2," fd74e201 +d "swhile.W" 7f85 +d "div #0xa, r2," fd74820a +d "fcmp r1, r2," fc8712 +d "bset r1, [r2]," fc6021 +d "emulu #0xa, r2," fd74720a +d "bset r1, r2," fc6321 +d "fadd r1, r2," fc8b12 +d "itof [r1].L, r2," 06a01112 +d "cmp [r1].L, r2," 068412 +d "jmp r1," 7f01 +d "add.UB r1, r2," 4b12 +d "fcmp [r1], r2," fc8412 +d "mvtachi r1," fd1701 +d "clrpsw Z," 7fb1 +d "scmpu" 7f83 +d "rte" 7f95 +d "bclr #0x7, [r2]," f02f +d "min 0x3[r1].B, r2," 0621051203 +d "mov.W r1, [r2,r3]," fe1231 +d "mvtaclo r1," fd1711 +d "round r1, r2," fc9b12 +d "bclr #0x1f, r2," 7bf2 +d "pushm r4, r8," 6e48 +d "rotr r1, r2," fd6412 +d "shar #0x3, r1, r2" fda312 +d "mov #0x80, r2," fb2680 +d "shll #0x3, r2," 6c32 +d "stnz #0x1, r2," fd74f201 +d "fsub r1, r2," fc8312 +d "pushm r1, r3," 6e13 +d "emulu [r1].L, r2," 06a00712 +d "rtsd #0x4, r7," 3f5704 +d "smovu" 7f87 +d "pop r1," 7eb1 +d "scne.W [r2]," fcd421 +d "mvtc #0xf000, intb," fd7b0c00f0 +d "mov #0x80, r2," 754280 +d "pushc psw," 7ec0 +d "rotl #0x1, r1," fd6e11 +d "and [r1].L, r2," 069012 +d "nop" 03 +d "shll #0x3, r1, r2" fdc312 +d "tst [r1].L, r2," 06a00c12 +d "brk" 00 +d "sub 0x1[r1].B, r2," 06011201 +d "wait" 7f96 +d "fadd [r1], r2," fc8812 +d "fdiv r1, r2," fc9312 +d "sstr.W" 7f89 +d "shll r1, r2," fd6212 +d "tst.UB r1, r2," fc3312 +d "sub [r1].L, r2," 068012 +d "sbb r1, r2," fc0312 +d "movu.W r1, r2," 5f12 +d "div [r1].L, r2," 06a00812 +d "mov.W [r1], [r2]," d012 +d "bset #0x1f, r2," 79f2 +d "int #0x0," 756000 +d "divu #0xa, r2," fd74920a +d "smovf" 7f8f +d "jsr r1," 7f11 +d "mulhi r1, r2," fd0012 +d "xchg.UB r1, r2," fc4312 +d "racw #0x1," fd1810 +d "movu.B [-r1], r2," fd3c12 +d "xchg [r1].W, r2," 06601012 +d "mov.L #0x0, [r2]," f82600 +d "xor [r1].L, r2," 06a00d12 +d "and.UB r1, r2," 5312 +d "and r1, r2, r3" ff4312 +d "min [r1].L, r2," 06a00512 +d "movu.B [r1+], r2," fd3812 +d "mov.B r1, [-r2]," fd2421 +d "popm r1, r3," 6f13 +d "bsr.A #0x0," 05000000 +d "min.UB r1, r2," fc1712 diff --git a/test/db/cmd/cmd_list b/test/db/cmd/cmd_list index 11cde8ca77..01ca169feb 100644 --- a/test/db/cmd/cmd_list +++ b/test/db/cmd/cmd_list @@ -420,6 +420,7 @@ _dA_ 32 propeller LGPL3 propeller disassembly plugin _dA_ 8 16 pyc LGPL3 PYC disassemble plugin adA_ 32 rl78 LGPL3 Renesas RL78 disassembler (by Bastian Engel) _dA_ 32 rsp LGPL3 Reality Signal Processor +_dA_ 32 rx LGPL3 Renesas RX Family disassembler (by Heersin) adAe 32 sh LGPL3 SuperH-4 CPU (by DMaroo) _dA_ 8 16 snes LGPL3 SuperNES CPU _dA_ 32 64 sparc BSD Capstone SPARC disassembler @@ -494,6 +495,7 @@ propeller pyc rl78 rsp +rx sh snes sparc @@ -530,7 +532,7 @@ NAME=Print the asm/analysis plugins in JSON FILE== CMDS=Laj EXPECT=<