From 13ebcbc2c70ff48b6d4f4c72f02c1e5f318a561f Mon Sep 17 00:00:00 2001 From: Anton Kochkov Date: Thu, 28 May 2026 05:39:20 +0000 Subject: [PATCH] arch/tms320: named instruction IDs (TMS320C55_INS_*) from the disassembler Gives the C55x / C55x+ disassembler a Capstone-style per-instruction identifier and threads it through to analysis, so the analyzers dispatch on named operations instead of raw opcode bytes. What lands ========== * librz/arch/isa/tms320/tms320c55x_insn.{c,h} -- a shared TMS320C55InsID enum (TMS320C55_INS_AADD, TMS320C55_INS_B, TMS320C55_INS_CALL, ...) covering C55x and C55x+, plus tms320c55x_insn_name(), tms320c55x_insn_id_from_syntax() and tms320c55x_insn_optype(). The prefix is TMS320C55_ / tms320c55x_ rather than TMS320_ / tms320_ because the other TMS320 families (C54x, C64x, C28x) have substantially different instruction sets; the enum, the file and these helpers are C55x/C55x+ specific. * insn_head_t gains an .id field; every head entry in c55x/table.h is tagged with its TMS320C55InsID. The disassembler resolves the decoded instruction ID from the emitted mnemonic (so multi-form leading bytes such as 0x95 -> intr/trap, 0x48 -> ret/reti/rpt, 0x50 -> sftl/popboth resolve to the exact instruction, which a static byte->head map cannot). tms320_dasm_t gains an insn_id field and tms320_dasm_insn_id() accessor. * tms320_c55x_insn_id_decode() / tms320_c55x_plus_insn_id_decode() let the analyzers resolve the named ID for a byte sequence via a cached decoder instance. * The C55x analyzer's dispatch is rewritten from switch(opcode_byte) to switch(TMS320C55InsID). Both analyzers set op->id to the named ID, the same way the Capstone-based plugins (e.g. c64x) populate op->id. * The C55x+ analyzer keeps its byte-level dispatch for control flow, stack deltas and operands, but takes the final arithmetic/logical/move/ multiply/stack type from tms320c55x_insn_optype(op->id). A leading byte on C55x+ encodes several instructions (selected by operand bits), so the byte switch alone cannot tell ADD from SUB, AND from OR/XOR, or AMOV from ASUB; the decoded id can. Bugs fixed ========== Driving dispatch / typing from the decoded id fixes a number of latent mis-classifications the raw-byte switch had masked, verified against the TI dis55 reference disassembler and the C55x+ documentation: - 0x50 0x66 (psh Tx) was typed SHL; now a stack push (corrects sym._main's computed stackframe in the rel.stripped.coff test). - 0x48 0x05 (reti) was typed REP; now RET. - 0x95 0x0F / 0x8F (intr vs trap) distinguished by the decoder. - C55x+ 0x7B: byte1 bit7 selects LD (mov) vs add/sub, and within add/sub byte2 bit7 selects sub; was always typed add/mov by nibble. (TI SWPU104 Table 7-2, opcode 01111011.) - C55x+ 0xD2 mar(XDAa op k24): address-register modify, now LEA like AADD / AMOV; was typed SUB. (Table 7-2, opcode 11010010.) - 57 further C55x+ arith/logical/move/stack contradictions found by cross-referencing a Motorola Droid (Wrigley3G) C55x+ baseband dump against the decoder are resolved by the optype override. * DELAY is a memory-delay MOVE per TI SWPU104 sec.6.7.1 (Memory Delay, grouped under Move Operations): it copies Smem to Smem+1. Both analyzers now mark it as a memory access (width 2, write) and drop the spurious FAMILY_CPU (CPU is already the default family). Tests ===== New checks assert op->id carries the right TMS320C55_INS_* value on each CPU, plus regression tests for every byte/decoded-id mismatch fixed above (c55x: 0x50/0x48/0x95/0xb6; c55x+: 0x7b/0xd2). The c55x opcode- classification and stackframe expectations are updated to the corrected output. Cross-reference =============== TI SPRU374 'TMS320C55x DSP Mnemonic Instruction Set Reference Guide' TI SWPU086 'TMS320C55x+ DSP Algebraic Instruction Set Reference Guide' TI SWPU104 'TMS320C55x+ DSP Mnemonic Instruction Set Reference Guide' --- librz/arch/isa/tms320/c55x/c55x_analysis.c | 643 +++++++----------- librz/arch/isa/tms320/c55x/table.h | 143 ++++ .../isa/tms320/c55x_plus/c55plus_analysis.c | 72 +- librz/arch/isa/tms320/tms320_dasm.c | 60 ++ librz/arch/isa/tms320/tms320_dasm.h | 13 + librz/arch/isa/tms320/tms320c55x_insn.c | 270 ++++++++ librz/arch/isa/tms320/tms320c55x_insn.h | 150 ++++ librz/arch/meson.build | 1 + test/db/analysis/tms320.c55x+_32 | 69 +- test/db/analysis/tms320.c55x_32 | 95 ++- 10 files changed, 1123 insertions(+), 393 deletions(-) create mode 100644 librz/arch/isa/tms320/tms320c55x_insn.c create mode 100644 librz/arch/isa/tms320/tms320c55x_insn.h diff --git a/librz/arch/isa/tms320/c55x/c55x_analysis.c b/librz/arch/isa/tms320/c55x/c55x_analysis.c index 65cdbfedfb..c095a9a53c 100644 --- a/librz/arch/isa/tms320/c55x/c55x_analysis.c +++ b/librz/arch/isa/tms320/c55x/c55x_analysis.c @@ -9,6 +9,8 @@ #include #include "c55x_analysis.h" +#include "../tms320c55x_insn.h" +#include "../tms320_dasm.h" /** * \file c55x_analysis.c @@ -265,169 +267,94 @@ int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, op_byte &= ~0x01; } - switch (op_byte) { - /* ---- 0x00: RPTCC k8, cond (3-byte conditional repeat) -------- */ - case 0x00: + /* Resolve the named instruction ID from the disassembler (single + * source of truth) and dispatch on it, rather than on the raw opcode + * byte. The disassembler runs the operand mask lists, so multi-form + * leading bytes resolve to the exact mnemonic decoded. Second-byte + * refinements and register-field extraction still read buf[]/op_byte + * directly where the mnemonic alone is insufficient (e.g. picking the + * displacement width for the several B / CALL encodings). */ + const ut16 id = tms320c55x_insn_id_decode(buf, len); + op->id = id; + + switch (id) { + /* ---- RPTCC k8, cond (3-byte conditional repeat) -------------- */ + case TMS320C55_INS_RPTCC: op->type = RZ_ANALYSIS_OP_TYPE_REP; op->fail = addr + sz; break; - /* ---- 0x02: RETCC cond (conditional return) ------------------- */ - case 0x02: + /* ---- RETCC cond (conditional return) ------------------------- */ + case TMS320C55_INS_RETCC: set_cret(op); break; - /* ---- 0x04: BCC k8, cond (8-bit signed relative cond branch) -- */ - case 0x04: - if (sz >= 2) { + /* ---- BCC: 8-bit or 16-bit signed relative conditional branch. + * 0x04 / 0x60-0x66 carry an 8-bit displacement at byte 1; + * 0x6F carries a 16-bit BE displacement. ------------------- */ + case TMS320C55_INS_BCC: + if (op_byte == 0x6f) { + if (sz >= 4) { + const ut32 disp = rz_read_at_be16(buf, 1); + set_cjmp(op, addr + sz + sign_extend(disp, 16)); + } + } else if (sz >= 2) { set_cjmp(op, addr + sz + sign_extend(buf[1], 8)); } break; - /* ---- 0x06: B L16 (16-bit relative unconditional) ------------- */ - case 0x06: - if (sz >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_jmp(op, addr + sz + sign_extend(disp, 16)); - } - break; - - /* ---- 0x08: CALL L16 (16-bit relative call) ------------------- */ - case 0x08: - if (sz >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_call(op, addr + sz + sign_extend(disp, 16)); - } - break; - - /* ---- 0x0C: RPT k16 ------------------------------------------- */ - case 0x0c: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - - /* ---- 0x0E: RPTB pmad (block-repeat) -------------------------- */ - case 0x0e: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - - /* ---- 0x20: NOP (1 byte) -------------------------------------- */ - case 0x20: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - - /* ---- 0x21: || nop (parallel marker -- NOT a return) ---------- */ - case 0x21: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - - /* ---- 0x38: PSH (single-word push) ---------------------------- */ - case 0x38: - set_push(op, 1); - break; - - /* ---- 0x3A: POP ----------------------------------------------- */ - case 0x3a: - set_pop(op, -1); - break; - - /* ---- 0x48: 2-byte multi-form: RPT CSR / RPTADD / RPTSUB / RET / RETI - * differentiated by bits 0-2 of buf[1] (per the table.h - * INSN_MASK(8,3,value) where bit 8 of the 16-bit BE - * instruction is bit 0 of byte 1). The mapping: - * buf[1] & 0x07 == 0 -> RPT CSR - * buf[1] & 0x07 == 1 -> RPTADD CSR, TAx - * buf[1] & 0x07 == 2 -> RPTADD CSR, K4 - * buf[1] & 0x07 == 3 -> RPTSUB CSR, K4 - * buf[1] & 0x07 == 4 -> RET - * buf[1] & 0x07 == 5 -> RETI - */ - case 0x48: - if (sz >= 2) { - switch (buf[1] & 0x07) { - case 4: /* RET */ - case 5: /* RETI */ - set_ret(op); - break; - case 0: /* RPT CSR */ - case 1: /* RPTADD CSR, TAx */ - case 2: /* RPTADD CSR, K4 */ - case 3: /* RPTSUB CSR, K4 */ - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - default: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; + /* ---- B: unconditional branch. Encodings: + * 0x06 16-bit relative, 0x4A 8-bit relative, + * 0x6A 24-bit absolute, 0x91 indirect via ACx. ------------- */ + case TMS320C55_INS_B: + if (op_byte == 0x91) { + op->type = RZ_ANALYSIS_OP_TYPE_UJMP; + op->eob = true; + op->fail = addr + sz; + if (sz >= 2) { + set_ireg(op, c55x_acc_names[buf[1] & 0x03]); + } + } else if (op_byte == 0x6a || op_byte == 0x6b) { + if (sz >= 4) { + set_jmp(op, rz_read_at_be24(buf, 1)); + } + } else if (op_byte == 0x4a) { + if (sz >= 2) { + set_jmp(op, addr + sz + sign_extend(buf[1], 8)); + } + } else { /* 0x06 */ + if (sz >= 3) { + const ut32 disp = rz_read_at_be16(buf, 1); + set_jmp(op, addr + sz + sign_extend(disp, 16)); } } break; - /* ---- 0x49-0x4F miscellaneous 2-byte forms (most aren't control - * flow). 0x4A is the 2-byte short B; everything else stays - * NULL or is handled below. */ - case 0x4a: /* B k8 (8-bit signed relative B) -- 2 bytes */ - if (sz >= 2) { - set_jmp(op, addr + sz + sign_extend(buf[1], 8)); - } - break; - case 0x4c: /* RPT k8 (3-byte) */ - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - case 0x4e: - /* AADD K8, SP -- address arithmetic (frame setup). - * - * Semantic: SP = SP + K8 (signed). - * - * rizin's stack-effect convention is that op->stackptr is - * the amount by which SP *decreases* (i.e. the amount the - * stack frame grows). On c55x the stack grows downward, so - * K8 < 0 -> SP decreases -> frame grows -> stackptr = -K8 - * K8 > 0 -> SP increases -> frame shrinks -> stackptr = -K8 - * which is just stackptr = -K8 in both cases. See - * rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - if (sz >= 2) { - const st8 k8 = (st8)buf[1]; - set_imm(op, k8); - set_dst_reg(op, "sp"); - set_disp(op, k8); + /* ---- CALL: 0x08 16-bit relative, 0x6C 24-bit absolute, + * 0x92 indirect via ACx. ----------------------------------- */ + case TMS320C55_INS_CALL: + if (op_byte == 0x92 || op_byte == 0x93) { + op->type = RZ_ANALYSIS_OP_TYPE_UCALL; + op->fail = addr + sz; op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -k8; + op->stackptr = 2; + if (sz >= 2) { + set_ireg(op, c55x_acc_names[buf[1] & 0x03]); + } + } else if (op_byte == 0x6c) { + if (sz >= 4) { + set_call(op, rz_read_at_be24(buf, 1)); + } + } else { /* 0x08 */ + if (sz >= 3) { + const ut32 disp = rz_read_at_be16(buf, 1); + set_call(op, addr + sz + sign_extend(disp, 16)); + } } break; - /* ---- 0x60-0x66: BCC variants (8-bit signed relative cond branch). - * Multiple variants encode different condition register - * interpretations but all are conditional jumps with - * 8-bit signed displacement at byte 1. */ - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - if (sz >= 2) { - set_cjmp(op, addr + sz + sign_extend(buf[1], 8)); - } - break; - - /* ---- 0x6A / 0x6B: B P24 (24-bit absolute unconditional) ------ */ - case 0x6a: - case 0x6b: - if (sz >= 4) { - set_jmp(op, rz_read_at_be24(buf, 1)); - } - break; - - /* ---- 0x6C: CALL P24 (24-bit absolute call) ------------------- */ - case 0x6c: - if (sz >= 4) { - set_call(op, rz_read_at_be24(buf, 1)); - } - break; - - /* ---- 0x6E: CALLCC P24, cond (conditional 24-bit call) -------- */ - case 0x6e: + /* ---- CALLCC P24, cond (conditional 24-bit call) -------------- */ + case TMS320C55_INS_CALLCC: if (sz >= 4) { /* Format: 6E hh ll cond -- disp is BE16 at offset 1. */ const ut32 disp = rz_read_at_be16(buf, 1); @@ -435,297 +362,251 @@ int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, } break; - /* ---- 0x6F: BCC P24, cond -- alternate cond branch ------------- */ - case 0x6f: - if (sz >= 4) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_cjmp(op, addr + sz + sign_extend(disp, 16)); + /* ---- RPT / RPTB / RPTADD / RPTSUB (block/single repeat) ------ */ + case TMS320C55_INS_RPT: + case TMS320C55_INS_RPTB: + case TMS320C55_INS_RPTADD: + case TMS320C55_INS_RPTSUB: + case TMS320C55_INS_RPTBLOCAL: + op->type = RZ_ANALYSIS_OP_TYPE_REP; + break; + + /* ---- RET / RETI: both are returns with -2 stack effect. The + * disassembler resolves the 0x48 family (and any other return + * encodings) to the exact mnemonic, so no second-byte logic is + * needed here. ------------------------------------------------ */ + case TMS320C55_INS_RET: + case TMS320C55_INS_RETI: + set_ret(op); + break; + + /* ---- NOP ----------------------------------------------------- */ + case TMS320C55_INS_NOP: + op->type = RZ_ANALYSIS_OP_TYPE_NOP; + break; + + /* ---- PSH variants: 0x38 single push; 0xB5 PSH Smem, + * 0xB7 PSH dbl(Smem), 0xE4 PSH dbl(Lmem). ------------------ */ + case TMS320C55_INS_PSH: + if (op_byte == 0x38) { + set_push(op, 1); + } else { + set_push(op, (op_byte == 0xb7 || op_byte == 0xe4) ? 2 : 1); + set_dst_reg(op, "sp"); + set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); } break; - /* ---- 0x91: B ACx (indirect register branch) ------------------ */ - case 0x91: - op->type = RZ_ANALYSIS_OP_TYPE_UJMP; - op->eob = true; - op->fail = addr + sz; - if (sz >= 2) { - set_ireg(op, c55x_acc_names[buf[1] & 0x03]); + /* ---- POP variants: 0x3A single pop; 0xB8/0xB9 POP dbl(Smem), + * 0xBB POP Smem. ------------------------------------------- */ + case TMS320C55_INS_POP: + if (op_byte == 0x3a) { + set_pop(op, -1); + } else { + set_pop(op, (op_byte == 0xbb) ? -1 : -2); + set_dst_reg(op, "sp"); + set_dir(op, RZ_ANALYSIS_OP_DIR_READ); } break; - /* ---- 0x92 / 0x93: CALL ACx (indirect register call) ---------- */ - case 0x92: - case 0x93: - op->type = RZ_ANALYSIS_OP_TYPE_UCALL; - op->fail = addr + sz; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - if (sz >= 2) { - set_ireg(op, c55x_acc_names[buf[1] & 0x03]); + /* ---- PSHBOTH / POPBOTH (dual register stack ops) ------------- */ + case TMS320C55_INS_PSHBOTH: + set_push(op, 2); + set_dst_reg(op, "sp"); + set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); + break; + case TMS320C55_INS_POPBOTH: + set_pop(op, -2); + set_dst_reg(op, "sp"); + set_dir(op, RZ_ANALYSIS_OP_DIR_READ); + break; + + /* ---- AADD: address-arithmetic add. 0x4E is `AADD K8, SP` + * (frame setup); 0x14 is `AADD k16, AC/AR`. --------------- */ + case TMS320C55_INS_AADD: + if (op_byte == 0x4e) { + /* SP = SP + K8 (signed). rizin's convention: op->stackptr is + * the amount SP *decreases* (frame growth). On C55x the stack + * grows down, so stackptr = -K8 regardless of sign. See + * rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */ + op->type = RZ_ANALYSIS_OP_TYPE_ADD; + if (sz >= 2) { + const st8 k8 = (st8)buf[1]; + set_imm(op, k8); + set_dst_reg(op, "sp"); + set_disp(op, k8); + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = -k8; + } + } else { /* 0x14: AADD k16, AC/AR */ + op->type = RZ_ANALYSIS_OP_TYPE_LEA; + if (sz >= 3) { + const ut8 src_idx = (buf[1] >> 4) & 0x0f; + const ut8 dst_idx = (buf[2] >> 4) & 0x0f; + set_dst_reg(op, c55x_gpr_names[dst_idx]); + set_ireg(op, c55x_gpr_names[src_idx]); + } } break; - /* ---- 0x94: RESET --------------------------------------------- */ - case 0x94: + /* ---- RESET --------------------------------------------------- */ + case TMS320C55_INS_RESET: op->type = RZ_ANALYSIS_OP_TYPE_TRAP; op->family = RZ_ANALYSIS_OP_FAMILY_CPU; break; - /* ---- 0x95: INTR #k5 or TRAP #k5 (bit 7 of buf[1] selects) ---- */ - case 0x95: + /* ---- INTR #k5 / TRAP #k5: the disassembler distinguishes the two + * (bit 7 of buf[1]); use the resolved ID for the type and read + * the 5-bit immediate from buf[1]. ------------------------- */ + case TMS320C55_INS_INTR: + case TMS320C55_INS_TRAP: + op->type = (id == TMS320C55_INS_TRAP) ? RZ_ANALYSIS_OP_TYPE_TRAP : RZ_ANALYSIS_OP_TYPE_SWI; if (sz >= 2) { - const ut8 k5 = buf[1] & 0x1f; - if (buf[1] & 0x80) { - op->type = RZ_ANALYSIS_OP_TYPE_TRAP; - } else { - op->type = RZ_ANALYSIS_OP_TYPE_SWI; - } - set_imm(op, k5); - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; + set_imm(op, buf[1] & 0x1f); } + op->family = RZ_ANALYSIS_OP_FAMILY_CPU; break; - /* ---- 0x96 / 0x97 / 0x9E / 0x9F: XCC (predicated execute) ----- */ - case 0x96: - case 0x97: - case 0x9e: - case 0x9f: + /* ---- XCC / XCCPART (predicated execute) ---------------------- */ + case TMS320C55_INS_XCC: + case TMS320C55_INS_XCCPART: op->type = RZ_ANALYSIS_OP_TYPE_CMP; op->family = RZ_ANALYSIS_OP_FAMILY_CPU; break; - /* ---- PSH/POP variants ---------------------------------------- */ - case 0xb5: - case 0xb7: - case 0xe4: - /* PSH variants: 0xb5 PSH Smem, 0xb7 PSH dbl(Smem), - * 0xe4 PSH dbl(Lmem). All decrement SP. */ - set_push(op, (op_byte == 0xb7 || op_byte == 0xe4) ? 2 : 1); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - case 0xb8: - case 0xb9: - case 0xbb: - /* POP variants: 0xb8 / 0xb9 POP dbl(Smem), - * 0xbb POP Smem. All increment SP. */ - set_pop(op, (op_byte == 0xbb) ? -1 : -2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - break; - - /* ---- Common arithmetic/logical/move bytes ------------------- */ - case 0x10: - case 0x18: - case 0x28: - case 0x72: - case 0x7d: - case 0xd9: - case 0xf4: + /* ---- Logical AND family -------------------------------------- */ + case TMS320C55_INS_AND: + case TMS320C55_INS_BAND: + case TMS320C55_INS_BTST: + case TMS320C55_INS_BTSTSET: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; - case 0x1a: - case 0x2a: - case 0x73: - case 0x7e: - case 0xda: - case 0xf5: + + /* ---- Logical OR ---------------------------------------------- */ + case TMS320C55_INS_OR: op->type = RZ_ANALYSIS_OP_TYPE_OR; break; - case 0x1c: - case 0x2c: - case 0x74: - case 0x7f: - case 0xdb: - case 0xf6: + + /* ---- Logical XOR --------------------------------------------- */ + case TMS320C55_INS_XOR: op->type = RZ_ANALYSIS_OP_TYPE_XOR; break; - case 0x12: - case 0xf0: - case 0xf1: + + /* ---- Compare family ------------------------------------------ */ + case TMS320C55_INS_CMP: + case TMS320C55_INS_CMPAND: + case TMS320C55_INS_CMPOR: + case TMS320C55_INS_MAX: + case TMS320C55_INS_MIN: op->type = RZ_ANALYSIS_OP_TYPE_CMP; break; - case 0x14: - /* AADD k16, AC/AR -- address-arithmetic add. - * Encoding: 14 0 0. - * Both register fields are 4 bits in the high nibble of - * buf[1] / buf[2]; see c55x_gpr_names[] for the map. */ - op->type = RZ_ANALYSIS_OP_TYPE_LEA; - if (sz >= 3) { - const ut8 src_idx = (buf[1] >> 4) & 0x0f; - const ut8 dst_idx = (buf[2] >> 4) & 0x0f; - set_dst_reg(op, c55x_gpr_names[dst_idx]); - set_ireg(op, c55x_gpr_names[src_idx]); + + /* ---- MOV family ---------------------------------------------- */ + case TMS320C55_INS_MOV: + /* 0xA0-0xAF: MOV Smem, REG (LOAD); 0xC0-0xCF: MOV REG, Smem + * (STORE). Both extract the register from the low nibble and, + * for the SP-relative form (bit 0 of buf[1] clear), set a + * *sp(#disp) operand. Other MOV-encoding bytes set only type. */ + op->type = RZ_ANALYSIS_OP_TYPE_MOV; + if (op_byte >= 0xa0 && op_byte <= 0xaf) { + set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); + set_dir(op, RZ_ANALYSIS_OP_DIR_READ); + set_mem_width(op, 2); + if (sz >= 2 && !(buf[1] & 0x01)) { + set_ireg(op, "sp"); + set_disp(op, buf[1] >> 1); + } + } else if (op_byte >= 0xc0 && op_byte <= 0xcf) { + set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); + set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); + set_mem_width(op, 2); + if (sz >= 2 && !(buf[1] & 0x01)) { + set_ireg(op, "sp"); + set_disp(op, buf[1] >> 1); + } } break; - case 0x16: - case 0x22: - case 0x3c: - case 0x3e: - case 0x44: - case 0x52: - case 0x75: - case 0x78: - /* MOV-family bytes that don't have a uniform low-nibble - * register encoding -- type only. */ + + /* ---- Bit set/clear (treated as MOV-like field writes) -------- */ + case TMS320C55_INS_BCLR: + case TMS320C55_INS_BSET: + case TMS320C55_INS_BFXTR: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - /* MOV Smem, REG -- LOAD-family. Low nibble of the leading - * byte selects the destination register; for the SP-relative - * common case (bit 0 of buf[1] clear), the source memory - * operand is `*sp(#disp)` where disp = buf[1]>>1. */ + + /* ---- DELAY: memory-delay move. Per TI SWPU104 sec.6.7.1 (Memory + * Delay, grouped under "Move Operations"), delay(Smem) copies the + * content of Smem to the next-higher address Smem+1 -- a one-word + * memory-to-memory data shift used to implement delay lines in + * filters. It is a data MOV (one data read + one data write), not + * a CPU/system-control instruction, so it gets the default + * family rather than FAMILY_CPU. ----------------------------- */ + case TMS320C55_INS_DELAY: op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); set_mem_width(op, 2); - if (sz >= 2 && !(buf[1] & 0x01)) { - set_ireg(op, "sp"); - set_disp(op, buf[1] >> 1); - } - break; - case 0xc0: - case 0xc1: - case 0xc2: - case 0xc3: - case 0xc4: - case 0xc5: - case 0xc6: - case 0xc7: - case 0xc8: - case 0xc9: - case 0xca: - case 0xcb: - case 0xcc: - case 0xcd: - case 0xce: - case 0xcf: - /* MOV REG, Smem -- STORE-family. Low nibble of the leading - * byte selects the source register; for the SP-relative - * common case, the destination memory operand is - * `*sp(#disp)` where disp = buf[1]>>1. */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - set_mem_width(op, 2); - if (sz >= 2 && !(buf[1] & 0x01)) { - set_ireg(op, "sp"); - set_disp(op, buf[1] >> 1); - } break; - case 0x24: - case 0x40: - case 0x5a: - case 0x70: - case 0x7a: - case 0x7b: - case 0x81: - case 0xd6: + + /* ---- Add family ---------------------------------------------- */ + case TMS320C55_INS_ADD: + case TMS320C55_INS_ADDSUBCC: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break; - case 0x26: - case 0x42: - case 0x71: - case 0x7c: - case 0xd7: - case 0xd8: + + /* ---- Subtract / negate --------------------------------------- */ + case TMS320C55_INS_SUB: + case TMS320C55_INS_NEG: op->type = RZ_ANALYSIS_OP_TYPE_SUB; break; - case 0x2e: /* MAX */ - case 0x30: /* MIN */ - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - break; - case 0x32: /* ABS -- see comment in c55x_plus analyzer */ - op->type = RZ_ANALYSIS_OP_TYPE_NULL; - break; - case 0x34: /* NEG */ - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - break; - case 0x36: /* NOT */ + + /* ---- Bitwise NOT --------------------------------------------- */ + case TMS320C55_INS_NOT: op->type = RZ_ANALYSIS_OP_TYPE_NOT; break; - case 0x46: /* BCLR */ - case 0xec: /* BSET */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - case 0x50: - case 0x5c: /* SFTL */ + + /* ---- Shifts -------------------------------------------------- */ + case TMS320C55_INS_SFTL: op->type = RZ_ANALYSIS_OP_TYPE_SHL; break; - case 0x54: /* ADDV */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - case 0x56: - case 0x83: /* MAC */ - case 0x58: - case 0x82: - case 0xfd: /* MPY */ - case 0x1e: - case 0x79: /* MPYK */ - case 0x84: /* MAS */ - case 0x86: - case 0x87: - case 0xd1: - case 0xd3: /* MPYM */ - case 0xd0: /* MACMZ */ - case 0xd2: - case 0xd4: /* MACM */ - case 0xd5: /* MASM */ - case 0xf8: /* MPYMK */ + + /* ---- Multiply / MAC family ----------------------------------- */ + case TMS320C55_INS_MAC: + case TMS320C55_INS_MACM: + case TMS320C55_INS_MASM: + case TMS320C55_INS_MAS: + case TMS320C55_INS_MPY: + case TMS320C55_INS_MPYK: + case TMS320C55_INS_MPYM: + case TMS320C55_INS_MPYMK: + case TMS320C55_INS_SQDST: + case TMS320C55_INS_LMSF: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; - case 0x5e: /* SWAP */ + + /* ---- SWAP (register exchange) -------------------------------- */ + case TMS320C55_INS_SWAP: op->type = RZ_ANALYSIS_OP_TYPE_XCHG; break; - case 0x76: /* BFXTR */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - case 0x77: /* AMOV */ - case 0x85: - case 0xb4: /* AMAR */ + + /* ---- Address-register moves (LEA-like) ----------------------- */ + case TMS320C55_INS_AMOV: + case TMS320C55_INS_AMAR: + case TMS320C55_INS_FIRSADD: op->type = RZ_ANALYSIS_OP_TYPE_LEA; break; - case 0xb6: /* DELAY */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - case 0xdc: - case 0xe0: /* BTST */ - op->type = RZ_ANALYSIS_OP_TYPE_AND; - break; - case 0xe3: /* BTSTSET */ - op->type = RZ_ANALYSIS_OP_TYPE_AND; - break; - case 0xde: /* ADDSUBCC */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - case 0xf2: - case 0xf3: /* BAND */ - op->type = RZ_ANALYSIS_OP_TYPE_AND; + + /* ---- ABS: leave NULL (no precise rizin type; see C55x+ note) - */ + case TMS320C55_INS_ABS: + op->type = RZ_ANALYSIS_OP_TYPE_NULL; break; default: - /* Unknown leading byte. Mark as NULL so the analyzer can - * keep walking but won't merge it into a basic block. The - * size=1 default in c55x_op_size() means we advance one - * byte and try the next one -- useful for parallel-prefixed - * instructions where the leading 0x01/0x03/0x05/... bytes - * are themselves the parallel marker. */ + /* Unknown / untyped instruction. Mark as NULL so the analyzer + * keeps walking but won't merge it into a basic block. The + * size=1 default in c55x_op_size() means we advance one byte + * and re-sync -- useful for parallel-prefixed instructions + * whose leading 0x01/0x03/0x05/... bytes are the parallel + * marker. */ break; } diff --git a/librz/arch/isa/tms320/c55x/table.h b/librz/arch/isa/tms320/c55x/table.h index f8f3a6b6ad..acb97bf877 100644 --- a/librz/arch/isa/tms320/c55x/table.h +++ b/librz/arch/isa/tms320/c55x/table.h @@ -5,6 +5,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x00, .size = 0x03, + .id = TMS320C55_INS_RPTCC, .insn = { // kkkkkkkkxCCCCCCC0000000E .i_list = NULL, @@ -16,6 +17,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x02, .size = 0x03, + .id = TMS320C55_INS_RETCC, .insn = { // xxxxxxxxxCCCCCCC0000001E .i_list = NULL, @@ -27,6 +29,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x04, .size = 0x03, + .id = TMS320C55_INS_BCC, .insn = { // LLLLLLLLxCCCCCCC0000010E .i_list = NULL, @@ -38,6 +41,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x06, .size = 0x03, + .id = TMS320C55_INS_B, .insn = { // LLLLLLLLLLLLLLLL0000011E .i_list = NULL, @@ -49,6 +53,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x08, .size = 0x03, + .id = TMS320C55_INS_CALL, .insn = { // LLLLLLLLLLLLLLLL0000100E .i_list = NULL, @@ -60,6 +65,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x0c, .size = 0x03, + .id = TMS320C55_INS_RPT, .insn = { // kkkkkkkkkkkkkkkk0000110E .i_list = NULL, @@ -71,6 +77,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x0e, .size = 0x03, + .id = TMS320C55_INS_RPTB, .insn = { // llllllllllllllll0000111E .i_list = NULL, @@ -82,6 +89,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x10, .size = 0x03, + .id = TMS320C55_INS_AND, .insn = { .i_list = (insn_item_t []) { { @@ -199,6 +207,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x12, .size = 0x03, + .id = TMS320C55_INS_CMPAND, .insn = { .i_list = (insn_item_t []) { { @@ -260,6 +269,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x14, .size = 0x03, + .id = TMS320C55_INS_AADD, .insn = { .i_list = (insn_item_t []) { { @@ -398,6 +408,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x16, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -466,6 +477,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x18, .size = 0x03, + .id = TMS320C55_INS_AND, .insn = { // FDDDFSSSkkkkkkkk0001100E .i_list = NULL, @@ -477,6 +489,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x1a, .size = 0x03, + .id = TMS320C55_INS_OR, .insn = { // FDDDFSSSkkkkkkkk0001101E .i_list = NULL, @@ -488,6 +501,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x1c, .size = 0x03, + .id = TMS320C55_INS_XOR, .insn = { // FDDDFSSSkkkkkkkk0001110E .i_list = NULL, @@ -499,6 +513,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x1e, .size = 0x03, + .id = TMS320C55_INS_MPYK, .insn = { .i_list = (insn_item_t []) { { @@ -525,6 +540,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x20, .size = 0x01, + .id = TMS320C55_INS_NOP, .insn = { // 0010000E .i_list = NULL, @@ -536,6 +552,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x22, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // FSSSFDDD0010001E .i_list = NULL, @@ -547,6 +564,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x24, .size = 0x02, + .id = TMS320C55_INS_ADD, .insn = { // FSSSFDDD0010010E .i_list = NULL, @@ -558,6 +576,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x26, .size = 0x02, + .id = TMS320C55_INS_SUB, .insn = { // FSSSFDDD0010011E .i_list = NULL, @@ -569,6 +588,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x28, .size = 0x02, + .id = TMS320C55_INS_AND, .insn = { // FSSSFDDD0010100E .i_list = NULL, @@ -580,6 +600,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x2a, .size = 0x02, + .id = TMS320C55_INS_OR, .insn = { // FSSSFDDD0010101E .i_list = NULL, @@ -591,6 +612,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x2c, .size = 0x02, + .id = TMS320C55_INS_XOR, .insn = { // FSSSFDDD0010110E .i_list = NULL, @@ -602,6 +624,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x2e, .size = 0x02, + .id = TMS320C55_INS_MAX, .insn = { // FSSSFDDD0010111E .i_list = NULL, @@ -613,6 +636,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x30, .size = 0x02, + .id = TMS320C55_INS_MIN, .insn = { // FSSSFDDD0011000E .i_list = NULL, @@ -624,6 +648,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x32, .size = 0x02, + .id = TMS320C55_INS_ABS, .insn = { // FSSSFDDD0011001E .i_list = NULL, @@ -635,6 +660,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x34, .size = 0x02, + .id = TMS320C55_INS_NEG, .insn = { // FSSSFDDD0011010E .i_list = NULL, @@ -646,6 +672,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x36, .size = 0x02, + .id = TMS320C55_INS_NOT, .insn = { // FSSSFDDD0011011E .i_list = NULL, @@ -657,6 +684,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x38, .size = 0x02, + .id = TMS320C55_INS_PSH, .insn = { // FSSSFDDD0011100E .i_list = NULL, @@ -668,6 +696,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x3a, .size = 0x02, + .id = TMS320C55_INS_POP, .insn = { // FSSSFDDD0011101E .i_list = NULL, @@ -679,6 +708,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x3c, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // kkkkFDDD0011110E .i_list = NULL, @@ -690,6 +720,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x3e, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // kkkkFDDD0011111E .i_list = NULL, @@ -701,6 +732,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x40, .size = 0x02, + .id = TMS320C55_INS_ADD, .insn = { // kkkkFDDD0100000E .i_list = NULL, @@ -712,6 +744,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x42, .size = 0x02, + .id = TMS320C55_INS_SUB, .insn = { // kkkkFDDD0100001E .i_list = NULL, @@ -723,6 +756,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x44, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -798,6 +832,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x46, .size = 0x02, + .id = TMS320C55_INS_BCLR, .insn = { .i_list = (insn_item_t []) { { @@ -866,6 +901,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x48, .size = 0x02, + .id = TMS320C55_INS_RETI, .insn = { .i_list = (insn_item_t []) { { @@ -920,6 +956,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x4a, .size = 0x02, + .id = TMS320C55_INS_B, .insn = { .i_list = (insn_item_t []) { { @@ -946,6 +983,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x4c, .size = 0x02, + .id = TMS320C55_INS_RPT, .insn = { // kkkkkkkk0100110E .i_list = NULL, @@ -957,6 +995,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x4e, .size = 0x02, + .id = TMS320C55_INS_AADD, .insn = { // KKKKKKKK0100111E .i_list = NULL, @@ -968,6 +1007,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x50, .size = 0x02, + .id = TMS320C55_INS_POPBOTH, .insn = { .i_list = (insn_item_t []) { { @@ -1036,6 +1076,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x52, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -1097,6 +1138,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x54, .size = 0x02, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -1158,6 +1200,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x56, .size = 0x02, + .id = TMS320C55_INS_MAC, .insn = { .i_list = (insn_item_t []) { { @@ -1184,6 +1227,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x58, .size = 0x02, + .id = TMS320C55_INS_MPY, .insn = { .i_list = (insn_item_t []) { { @@ -1210,6 +1254,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x5a, .size = 0x02, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -1243,6 +1288,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x5c, .size = 0x02, + .id = TMS320C55_INS_SFTL, .insn = { .i_list = (insn_item_t []) { { @@ -1276,6 +1322,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x5e, .size = 0x02, + .id = TMS320C55_INS_SWAP, .insn = { // 00kkkkkk0101111E .i_list = NULL, @@ -1287,6 +1334,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x60, .size = 0x02, + .id = TMS320C55_INS_BCC, .insn = { // lCCCCCCC01100lll .i_list = NULL, @@ -1298,6 +1346,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x68, .size = 0x05, + .id = TMS320C55_INS_BCC, .insn = { // PPPPPPPPPPPPPPPPPPPPPPPPxCCCCCCC01101000 .i_list = NULL, @@ -1309,6 +1358,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x69, .size = 0x05, + .id = TMS320C55_INS_CALLCC, .insn = { // PPPPPPPPPPPPPPPPPPPPPPPPxCCCCCCC01101001 .i_list = NULL, @@ -1320,6 +1370,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x6a, .size = 0x04, + .id = TMS320C55_INS_B, .insn = { // PPPPPPPPPPPPPPPPPPPPPPPP01101010 .i_list = NULL, @@ -1331,6 +1382,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x6c, .size = 0x04, + .id = TMS320C55_INS_CALL, .insn = { // PPPPPPPPPPPPPPPPPPPPPPPP01101100 .i_list = NULL, @@ -1342,6 +1394,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x6d, .size = 0x04, + .id = TMS320C55_INS_BCC, .insn = { // LLLLLLLLLLLLLLLLxCCCCCCC01101101 .i_list = NULL, @@ -1353,6 +1406,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x6e, .size = 0x04, + .id = TMS320C55_INS_CALLCC, .insn = { // LLLLLLLLLLLLLLLLxCCCCCCC01101110 .i_list = NULL, @@ -1364,6 +1418,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x6f, .size = 0x04, + .id = TMS320C55_INS_BCC, .insn = { // LLLLLLLLKKKKKKKKFSSSccxu01101111 .i_list = NULL, @@ -1375,6 +1430,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x70, .size = 0x04, + .id = TMS320C55_INS_ADD, .insn = { // SSDDSHFTKKKKKKKKKKKKKKKK01110000 .i_list = NULL, @@ -1386,6 +1442,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x71, .size = 0x04, + .id = TMS320C55_INS_SUB, .insn = { // SSDDSHFTKKKKKKKKKKKKKKKK01110001 .i_list = NULL, @@ -1397,6 +1454,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x72, .size = 0x04, + .id = TMS320C55_INS_AND, .insn = { // SSDDSHFTkkkkkkkkkkkkkkkk01110010 .i_list = NULL, @@ -1408,6 +1466,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x73, .size = 0x04, + .id = TMS320C55_INS_OR, .insn = { // SSDDSHFTkkkkkkkkkkkkkkkk01110011 .i_list = NULL, @@ -1419,6 +1478,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x74, .size = 0x04, + .id = TMS320C55_INS_XOR, .insn = { // SSDDSHFTkkkkkkkkkkkkkkkk01110100 .i_list = NULL, @@ -1430,6 +1490,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x75, .size = 0x04, + .id = TMS320C55_INS_MOV, .insn = { // xxDDSHFTKKKKKKKKKKKKKKKK01110101 .i_list = NULL, @@ -1441,6 +1502,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x76, .size = 0x04, + .id = TMS320C55_INS_BFXTR, .insn = { .i_list = (insn_item_t []) { { @@ -1474,6 +1536,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x77, .size = 0x04, + .id = TMS320C55_INS_AMOV, .insn = { // FDDDxxxxDDDDDDDDDDDDDDDD01110111 .i_list = NULL, @@ -1485,6 +1548,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x78, .size = 0x04, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -1560,6 +1624,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x79, .size = 0x04, + .id = TMS320C55_INS_MPYK, .insn = { .i_list = (insn_item_t []) { { @@ -1586,6 +1651,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7a, .size = 0x04, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -1647,6 +1713,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7b, .size = 0x04, + .id = TMS320C55_INS_ADD, .insn = { // FDDDFSSSKKKKKKKKKKKKKKKK01111011 .i_list = NULL, @@ -1658,6 +1725,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7c, .size = 0x04, + .id = TMS320C55_INS_SUB, .insn = { // FDDDFSSSKKKKKKKKKKKKKKKK01111100 .i_list = NULL, @@ -1669,6 +1737,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7d, .size = 0x04, + .id = TMS320C55_INS_AND, .insn = { // FDDDFSSSkkkkkkkkkkkkkkkk01111101 .i_list = NULL, @@ -1680,6 +1749,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7e, .size = 0x04, + .id = TMS320C55_INS_OR, .insn = { // FDDDFSSSkkkkkkkkkkkkkkkk01111110 .i_list = NULL, @@ -1691,6 +1761,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x7f, .size = 0x04, + .id = TMS320C55_INS_XOR, .insn = { // FDDDFSSSkkkkkkkkkkkkkkkk01111111 .i_list = NULL, @@ -1702,6 +1773,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x80, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -1735,6 +1807,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x81, .size = 0x03, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -1768,6 +1841,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x82, .size = 0x04, + .id = TMS320C55_INS_MPY, .insn = { .i_list = (insn_item_t []) { { @@ -1808,6 +1882,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x83, .size = 0x04, + .id = TMS320C55_INS_MAC, .insn = { .i_list = (insn_item_t []) { { @@ -1848,6 +1923,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x84, .size = 0x04, + .id = TMS320C55_INS_MAS, .insn = { .i_list = (insn_item_t []) { { @@ -1888,6 +1964,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x85, .size = 0x04, + .id = TMS320C55_INS_FIRSADD, .insn = { .i_list = (insn_item_t []) { { @@ -1935,6 +2012,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x86, .size = 0x04, + .id = TMS320C55_INS_SQDST, .insn = { .i_list = (insn_item_t []) { { @@ -2010,6 +2088,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x87, .size = 0x04, + .id = TMS320C55_INS_LMSF, .insn = { .i_list = (insn_item_t []) { { @@ -2071,6 +2150,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x90, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // XSSSXDDD10010000 .i_list = NULL, @@ -2082,6 +2162,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x91, .size = 0x02, + .id = TMS320C55_INS_B, .insn = { // xxxxxxSS10010001 .i_list = NULL, @@ -2093,6 +2174,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x92, .size = 0x02, + .id = TMS320C55_INS_CALL, .insn = { // xxxxxxSS10010010 .i_list = NULL, @@ -2104,6 +2186,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x94, .size = 0x02, + .id = TMS320C55_INS_RESET, .insn = { // xxxxxxxx10010100 .i_list = NULL, @@ -2115,6 +2198,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x95, .size = 0x02, + .id = TMS320C55_INS_INTR, .insn = { .i_list = (insn_item_t []) { { @@ -2141,6 +2225,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x96, .size = 0x02, + .id = TMS320C55_INS_XCC, .insn = { .i_list = (insn_item_t []) { { @@ -2167,6 +2252,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x9e, .size = 0x02, + .id = TMS320C55_INS_XCC, .insn = { .i_list = (insn_item_t []) { { @@ -2193,6 +2279,7 @@ static insn_head_t c55x_list[] = { { .byte = 0x9f, .size = 0x02, + .id = TMS320C55_INS_XCC, .insn = { .i_list = (insn_item_t []) { { @@ -2219,6 +2306,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xa0, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // AAAAAAAI1010FDDD .i_list = NULL, @@ -2230,6 +2318,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb0, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // AAAAAAAI101100DD .i_list = NULL, @@ -2241,6 +2330,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb4, .size = 0x02, + .id = TMS320C55_INS_AMAR, .insn = { // AAAAAAAI10110100 .i_list = NULL, @@ -2252,6 +2342,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb5, .size = 0x02, + .id = TMS320C55_INS_PSH, .insn = { // AAAAAAAI10110101 .i_list = NULL, @@ -2263,6 +2354,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb6, .size = 0x02, + .id = TMS320C55_INS_DELAY, .insn = { // AAAAAAAI10110110 .i_list = NULL, @@ -2274,6 +2366,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb7, .size = 0x02, + .id = TMS320C55_INS_PSH, .insn = { // AAAAAAAI10110111 .i_list = NULL, @@ -2285,6 +2378,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xb8, .size = 0x02, + .id = TMS320C55_INS_POP, .insn = { // AAAAAAAI10111000 .i_list = NULL, @@ -2296,6 +2390,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xbb, .size = 0x02, + .id = TMS320C55_INS_POP, .insn = { // AAAAAAAI10111011 .i_list = NULL, @@ -2307,6 +2402,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xbc, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // AAAAAAAI101111SS .i_list = NULL, @@ -2318,6 +2414,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xc0, .size = 0x02, + .id = TMS320C55_INS_MOV, .insn = { // AAAAAAAI1100FSSS .i_list = NULL, @@ -2329,6 +2426,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd0, .size = 0x03, + .id = TMS320C55_INS_MPY, .insn = { .i_list = (insn_item_t []) { { @@ -2369,6 +2467,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd1, .size = 0x03, + .id = TMS320C55_INS_MPYM, .insn = { .i_list = (insn_item_t []) { { @@ -2402,6 +2501,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd2, .size = 0x03, + .id = TMS320C55_INS_MACM, .insn = { .i_list = (insn_item_t []) { { @@ -2442,6 +2542,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd3, .size = 0x03, + .id = TMS320C55_INS_MPYM, .insn = { .i_list = (insn_item_t []) { { @@ -2475,6 +2576,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd4, .size = 0x03, + .id = TMS320C55_INS_MACM, .insn = { // U%DDssSSAAAAAAAI11010100 .i_list = NULL, @@ -2486,6 +2588,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd5, .size = 0x03, + .id = TMS320C55_INS_MASM, .insn = { // U%DDssSSAAAAAAAI11010101 .i_list = NULL, @@ -2497,6 +2600,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd6, .size = 0x03, + .id = TMS320C55_INS_ADD, .insn = { // FDDDFSSSAAAAAAAI11010110 .i_list = NULL, @@ -2508,6 +2612,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd7, .size = 0x03, + .id = TMS320C55_INS_SUB, .insn = { // FDDDFSSSAAAAAAAI11010111 .i_list = NULL, @@ -2519,6 +2624,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd8, .size = 0x03, + .id = TMS320C55_INS_SUB, .insn = { // FDDDFSSSAAAAAAAI11011000 .i_list = NULL, @@ -2530,6 +2636,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xd9, .size = 0x03, + .id = TMS320C55_INS_AND, .insn = { // FDDDFSSSAAAAAAAI11011001 .i_list = NULL, @@ -2541,6 +2648,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xda, .size = 0x03, + .id = TMS320C55_INS_OR, .insn = { // FDDDFSSSAAAAAAAI11011010 .i_list = NULL, @@ -2552,6 +2660,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xdb, .size = 0x03, + .id = TMS320C55_INS_XOR, .insn = { // FDDDFSSSAAAAAAAI11011011 .i_list = NULL, @@ -2563,6 +2672,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xdc, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -2722,6 +2832,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xdd, .size = 0x03, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -2762,6 +2873,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xde, .size = 0x03, + .id = TMS320C55_INS_ADDSUBCC, .insn = { .i_list = (insn_item_t []) { { @@ -2837,6 +2949,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xdf, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -2898,6 +3011,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe0, .size = 0x03, + .id = TMS320C55_INS_BTST, .insn = { // FSSSxxxtAAAAAAAI11100000 .i_list = NULL, @@ -2909,6 +3023,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe1, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { // DDSHIFTWAAAAAAAI11100001 .i_list = NULL, @@ -2920,6 +3035,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe2, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { // DDSHIFTWAAAAAAAI11100010 .i_list = NULL, @@ -2931,6 +3047,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe3, .size = 0x03, + .id = TMS320C55_INS_BSET, .insn = { .i_list = (insn_item_t []) { { @@ -3006,6 +3123,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe4, .size = 0x03, + .id = TMS320C55_INS_PSH, .insn = { .i_list = (insn_item_t []) { { @@ -3032,6 +3150,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe5, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3191,6 +3310,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe6, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { // KKKKKKKKAAAAAAAI11100110 .i_list = NULL, @@ -3202,6 +3322,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe7, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3235,6 +3356,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe8, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3261,6 +3383,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xe9, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { // SSSHIFTWAAAAAAAI11101001 .i_list = NULL, @@ -3272,6 +3395,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xea, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { // SSSHIFTWAAAAAAAI11101010 .i_list = NULL, @@ -3283,6 +3407,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xeb, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3330,6 +3455,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xec, .size = 0x03, + .id = TMS320C55_INS_AMAR, .insn = { .i_list = (insn_item_t []) { { @@ -3384,6 +3510,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xed, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3480,6 +3607,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xee, .size = 0x03, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -3548,6 +3676,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xef, .size = 0x03, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3588,6 +3717,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf0, .size = 0x04, + .id = TMS320C55_INS_CMP, .insn = { // KKKKKKKKKKKKKKKKAAAAAAAI11110000 .i_list = NULL, @@ -3599,6 +3729,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf1, .size = 0x04, + .id = TMS320C55_INS_CMP, .insn = { // KKKKKKKKKKKKKKKKAAAAAAAI11110001 .i_list = NULL, @@ -3610,6 +3741,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf2, .size = 0x04, + .id = TMS320C55_INS_BAND, .insn = { // kkkkkkkkkkkkkkkkAAAAAAAI11110010 .i_list = NULL, @@ -3621,6 +3753,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf3, .size = 0x04, + .id = TMS320C55_INS_BAND, .insn = { // kkkkkkkkkkkkkkkkAAAAAAAI11110011 .i_list = NULL, @@ -3632,6 +3765,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf4, .size = 0x04, + .id = TMS320C55_INS_AND, .insn = { // kkkkkkkkkkkkkkkkAAAAAAAI11110100 .i_list = NULL, @@ -3643,6 +3777,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf5, .size = 0x04, + .id = TMS320C55_INS_OR, .insn = { // kkkkkkkkkkkkkkkkAAAAAAAI11110101 .i_list = NULL, @@ -3654,6 +3789,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf6, .size = 0x04, + .id = TMS320C55_INS_XOR, .insn = { // kkkkkkkkkkkkkkkkAAAAAAAI11110110 .i_list = NULL, @@ -3665,6 +3801,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf7, .size = 0x04, + .id = TMS320C55_INS_ADD, .insn = { // KKKKKKKKKKKKKKKKAAAAAAAI11110111 .i_list = NULL, @@ -3676,6 +3813,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf8, .size = 0x04, + .id = TMS320C55_INS_MPYMK, .insn = { .i_list = (insn_item_t []) { { @@ -3702,6 +3840,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xf9, .size = 0x04, + .id = TMS320C55_INS_ADD, .insn = { .i_list = (insn_item_t []) { { @@ -3735,6 +3874,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xfa, .size = 0x04, + .id = TMS320C55_INS_MOV, .insn = { .i_list = (insn_item_t []) { { @@ -3761,6 +3901,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xfb, .size = 0x04, + .id = TMS320C55_INS_MOV, .insn = { // KKKKKKKKKKKKKKKKAAAAAAAI11111011 .i_list = NULL, @@ -3772,6 +3913,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xfc, .size = 0x04, + .id = TMS320C55_INS_BCC, .insn = { // LLLLLLLLLLLLLLLLAAAAAAAI11111100 .i_list = NULL, @@ -3783,6 +3925,7 @@ static insn_head_t c55x_list[] = { { .byte = 0xfd, .size = 0x04, + .id = TMS320C55_INS_MPY, .insn = { .i_list = (insn_item_t []) { { diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c index cd48ec7919..462f070724 100644 --- a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c @@ -10,6 +10,8 @@ #include "c55plus_analysis.h" #include "ins.h" +#include "../tms320c55x_insn.h" +#include "../tms320_dasm.h" /** * \file c55plus_analysis.c @@ -205,6 +207,14 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, op->size = ins_len; op->type = RZ_ANALYSIS_OP_TYPE_NULL; + /* Surface the named instruction ID (Capstone-style op->id), resolved + * from the C55x+ token decoder's decoded mnemonic. The type-level + * dispatch below stays byte-driven because C55x+ packs many distinct + * instructions behind each leading byte (disambiguated by operand + * bits the analyzer already inspects); the ID gives consumers the + * exact mnemonic without re-deriving it. */ + op->id = tms320c55x_plus_insn_id_decode(buf, len); + switch (buf[0]) { /* ---- 0x00 family: NOP_16 / IDLE / RETI / to_word --------------- */ case 0x00: @@ -380,10 +390,16 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; - /* ---- 0x60: DELAY --------------------------------------------- */ + /* ---- 0x60: DELAY -- memory-delay move (TI SWPU104 sec.6.7.1, + * "Memory Delay", grouped under Move Operations). delay(Smem) + * copies the word at Smem to the next-higher address Smem+1 (a + * one-word memory-to-memory data shift used to build delay lines + * in filters): one data read, one data write. It is a data MOV, + * not a CPU/system-control op, so no FAMILY_CPU. -------------- */ case 0x60: op->type = RZ_ANALYSIS_OP_TYPE_MOV; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; + set_mem_width(op, 2); + set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); break; /* ---- 0x61: PSH dbl(mem) -------------------------------------- */ @@ -497,14 +513,31 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; - /* ---- 0x7B: ADD #k7, ACx (b1 0x00-0x0F) or MOV #k8, Tx (0xB0-0xBF) */ + /* ---- 0x7B: Ra = Ra +/- k4 | Ra <> #1 | Ra = k4 ------------- + * Per TI SWPU104 Table 7-2 (opcode 01111011): + * byte1 bit7 (0x80) set -> LD (Ra = k4) -> MOV + * byte1 bit7 (0x80) clear -> arithmetic/shift on Ra, where + * byte2 bit4 (0x10) set -> shift by 1 (SFTA) -> SHL + * byte2 bit4 (0x10) clr -> add/sub k4, with + * byte2 bit7 (0x80) set -> SUB else ADD + * The k4 immediate is the low nibble of byte2. */ case 0x7b: if (ins_len >= 3) { - set_imm(op, buf[2]); - if ((buf[1] & 0xf0) == 0xb0) { + if (buf[1] & 0x80) { + /* LD Ra, #k4 -- load immediate */ op->type = RZ_ANALYSIS_OP_TYPE_MOV; + set_imm(op, buf[2] & 0x0f); + } else if (buf[2] & 0x10) { + /* SFTA Ra, #1 -- arithmetic shift by one */ + op->type = (buf[2] & 0x80) ? RZ_ANALYSIS_OP_TYPE_SHR : RZ_ANALYSIS_OP_TYPE_SHL; + } else if (buf[2] & 0x80) { + /* SUB Ra, #k4 */ + op->type = RZ_ANALYSIS_OP_TYPE_SUB; + set_imm(op, buf[2] & 0x0f); } else { + /* ADD Ra, #k4 */ op->type = RZ_ANALYSIS_OP_TYPE_ADD; + set_imm(op, buf[2] & 0x0f); } } else { op->type = RZ_ANALYSIS_OP_TYPE_MOV; @@ -652,8 +685,18 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, case 0xcb: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; case 0xce: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* SQDST */ - case 0xd1: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* COPY */ - case 0xd2: op->type = RZ_ANALYSIS_OP_TYPE_SUB; break; + case 0xd1: + op->type = RZ_ANALYSIS_OP_TYPE_MOV; + break; /* COPY */ + /* ---- 0xD2: mar(XDAa op k24) -- modify extended address register. + * Per TI SWPU104 Table 7-2 (opcode 11010010), byte1 bits 6-5 select + * 00 -> mar(XDAa - k24) (ASUB) + * 01 -> mar(XDAa + k24) (AMOV/AADD with +) + * 10 -> mar(XDAa = k24) (AMOV load) + * All are address-register arithmetic/loads, classified LEA (the + * same family as 0x14 AADD and 0x77 AMOV). The earlier code typed + * this SUB, which mis-classified the load and add forms. */ + case 0xd2: op->type = RZ_ANALYSIS_OP_TYPE_LEA; break; case 0xd4: op->type = RZ_ANALYSIS_OP_TYPE_CMP; break; /* MAXDIFF */ @@ -802,5 +845,20 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, break; } + /* The byte-level switch above handles control flow (targets, fail + * paths), stack deltas and operand fields, but it cannot always tell + * apart instructions that share a leading byte but differ by operand + * bits (e.g. ADD vs SUB, AND vs OR vs XOR, AMOV vs ASUB, PSH vs POP). + * The decoder already resolved the exact mnemonic, so for the + * arithmetic / logical / move / multiply / stack families take the + * authoritative type from the instruction id. Control flow, repeats, + * XCC and the like map to NULL here and keep the type the byte switch + * assigned. Stack-effect bookkeeping (stackop/stackptr) set above is + * preserved. */ + const int id_type = tms320c55x_insn_optype((TMS320C55InsID)op->id); + if (id_type != RZ_ANALYSIS_OP_TYPE_NULL) { + op->type = id_type; + } + return op->size; } diff --git a/librz/arch/isa/tms320/tms320_dasm.c b/librz/arch/isa/tms320/tms320_dasm.c index ffb31dd7e1..615f3e91c8 100644 --- a/librz/arch/isa/tms320/tms320_dasm.c +++ b/librz/arch/isa/tms320/tms320_dasm.c @@ -11,6 +11,7 @@ /* private headers */ #include "tms320_dasm.h" +#include "tms320c55x_insn.h" #include "c55x_plus/c55plus.h" @@ -1168,6 +1169,20 @@ int tms320_dasm(tms320_dasm_t *dasm, const ut8 *stream, int len) { if (dasm->status & TMS320_S_INVAL) { strcpy(dasm->syntax, "invalid"), dasm->length = 1; + dasm->insn_id = TMS320C55_INS_INVALID; + } else { + /* Resolve the instruction ID from the decoded mnemonic. The + * syntax string is the disassembler's ground truth: it reflects + * the exact sub-instruction selected by the operand mask lists, + * which a static leading-byte->head mapping cannot capture (e.g. + * 0x95 0x8F decodes to TRAP, not INTR; 0x48 0x05 to RETI, not + * RPT). Falls back to the matched table head's id for C55x when + * the mnemonic isn't in the table (should not normally happen). */ + dasm->insn_id = tms320c55x_insn_id_from_syntax(dasm->syntax); + if (dasm->insn_id == TMS320C55_INS_INVALID && + tms320_f_get_cpu(dasm) != TMS320_F_CPU_C55X_PLUS && dasm->head) { + dasm->insn_id = dasm->head->id; + } } return full_insn_size(dasm); @@ -1176,6 +1191,51 @@ int tms320_dasm(tms320_dasm_t *dasm, const ut8 *stream, int len) { // insn_head_t c55x_list[] #include "c55x/table.h" +/* Resolve the C55x instruction ID for a byte sequence by running the + * disassembler and mapping its decoded mnemonic to a TMS320C55InsID. This + * is the accurate path: unlike a static leading-byte table, it honours + * the per-instruction mask lists, so multi-form heads (e.g. 0x50, which + * decodes to either POPBOTH or SFTL depending on operand bits) resolve + * to the mnemonic actually decoded. + * + * A single cached dasm instance is used to avoid per-call init/fini. The + * cache is process-wide and the disassembler is stateless across calls + * (init_dasm() resets it each time), so this is safe for the analyzer's + * single-threaded use. */ +ut16 tms320c55x_insn_id_decode(const ut8 *buf, int len) { + static tms320_dasm_t dasm; + static int initialized = 0; + if (!initialized) { + tms320_dasm_init(&dasm); + tms320_f_set_cpu(&dasm, TMS320_F_CPU_C55X); + initialized = 1; + } + if (!buf || len < 1) { + return TMS320C55_INS_INVALID; + } + tms320_dasm(&dasm, buf, len); + return dasm.insn_id; +} + +/* C55x+ counterpart of tms320c55x_insn_id_decode(): run the C55x+ + * token decoder over the byte sequence and return the TMS320C55InsID of the + * decoded mnemonic. Uses a separate cached dasm instance pinned to the + * C55x+ feature set. */ +ut16 tms320c55x_plus_insn_id_decode(const ut8 *buf, int len) { + static tms320_dasm_t dasm; + static int initialized = 0; + if (!initialized) { + tms320_dasm_init(&dasm); + tms320_f_set_cpu(&dasm, TMS320_F_CPU_C55X_PLUS); + initialized = 1; + } + if (!buf || len < 1) { + return TMS320C55_INS_INVALID; + } + tms320_dasm(&dasm, buf, len); + return dasm.insn_id; +} + int tms320_dasm_init(tms320_dasm_t *dasm) { int i = 0; diff --git a/librz/arch/isa/tms320/tms320_dasm.h b/librz/arch/isa/tms320/tms320_dasm.h index a6609c413b..968140cdb3 100644 --- a/librz/arch/isa/tms320/tms320_dasm.h +++ b/librz/arch/isa/tms320/tms320_dasm.h @@ -104,6 +104,7 @@ struct tms320_instruction_flag { struct tms320_instruction_head { ut8 byte; ut8 size; + ut16 id; ///< TMS320C55InsID for this leading-byte entry (0 = unset/invalid) insn_item_t insn; }; @@ -124,6 +125,7 @@ typedef struct { #define TMS320_S_INVAL 0x01 ut8 status; ut8 length; + ut16 insn_id; ///< TMS320C55InsID of the decoded instruction (0 if unknown) char syntax[1024]; #define def_field(name, size) \ @@ -245,6 +247,17 @@ typedef struct { extern int tms320_dasm(tms320_dasm_t *, const ut8 *, int); +/** Resolve the C55x instruction ID for a byte sequence by disassembling + * it and mapping the decoded mnemonic to a TMS320C55InsID. Honours the + * per-instruction operand masks (so multi-form leading bytes resolve to + * the mnemonic actually decoded). Returns 0 when the bytes don't decode + * to a known instruction. */ +extern ut16 tms320c55x_insn_id_decode(const ut8 *buf, int len); + +/** C55x+ counterpart of tms320c55x_insn_id_decode(). Resolves the + * TMS320C55InsID by running the C55x+ token decoder over the bytes. */ +extern ut16 tms320c55x_plus_insn_id_decode(const ut8 *buf, int len); + extern int tms320_dasm_init(tms320_dasm_t *); extern int tms320_dasm_fini(tms320_dasm_t *); diff --git a/librz/arch/isa/tms320/tms320c55x_insn.c b/librz/arch/isa/tms320/tms320c55x_insn.c new file mode 100644 index 0000000000..f5c44f1757 --- /dev/null +++ b/librz/arch/isa/tms320/tms320c55x_insn.c @@ -0,0 +1,270 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#include "tms320c55x_insn.h" + +/* Mnemonic strings indexed by TMS320C55InsID. Keep in sync with the enum + * in tms320c55x_insn.h. */ +static const char *const tms320c55x_insn_names[] = { + [TMS320C55_INS_INVALID] = "invalid", + [TMS320C55_INS_AADD] = "aadd", + [TMS320C55_INS_ABDST] = "abdst", + [TMS320C55_INS_ABS] = "abs", + [TMS320C55_INS_ADD] = "add", + [TMS320C55_INS_ADDSUB] = "addsub", + [TMS320C55_INS_ADDSUB2CC] = "addsub2cc", + [TMS320C55_INS_ADDSUBCC] = "addsubcc", + [TMS320C55_INS_AMAR] = "amar", + [TMS320C55_INS_AMOV] = "amov", + [TMS320C55_INS_AND] = "and", + [TMS320C55_INS_ASUB] = "asub", + [TMS320C55_INS_B] = "b", + [TMS320C55_INS_BAND] = "band", + [TMS320C55_INS_BCC] = "bcc", + [TMS320C55_INS_BCLR] = "bclr", + [TMS320C55_INS_BCNT] = "bcnt", + [TMS320C55_INS_BFXPA] = "bfxpa", + [TMS320C55_INS_BFXTR] = "bfxtr", + [TMS320C55_INS_BNOT] = "bnot", + [TMS320C55_INS_BSET] = "bset", + [TMS320C55_INS_BTST] = "btst", + [TMS320C55_INS_BTSTCLR] = "btstclr", + [TMS320C55_INS_BTSTNOT] = "btstnot", + [TMS320C55_INS_BTSTP] = "btstp", + [TMS320C55_INS_BTSTSET] = "btstset", + [TMS320C55_INS_CALL] = "call", + [TMS320C55_INS_CALLCC] = "callcc", + [TMS320C55_INS_CIRC] = "circ", + [TMS320C55_INS_CMP] = "cmp", + [TMS320C55_INS_CMPAND] = "cmpand", + [TMS320C55_INS_CMPOR] = "cmpor", + [TMS320C55_INS_DELAY] = "delay", + [TMS320C55_INS_DMAXDIFF] = "dmaxdiff", + [TMS320C55_INS_DMINDIFF] = "dmindiff", + [TMS320C55_INS_EXP] = "exp", + [TMS320C55_INS_FIRSADD] = "firsadd", + [TMS320C55_INS_FIRSSUB] = "firssub", + [TMS320C55_INS_IDLE] = "idle", + [TMS320C55_INS_INTR] = "intr", + [TMS320C55_INS_LMS] = "lms", + [TMS320C55_INS_LMSF] = "lmsf", + [TMS320C55_INS_MAC] = "mac", + [TMS320C55_INS_MACK] = "mack", + [TMS320C55_INS_MACM] = "macm", + [TMS320C55_INS_MACMK] = "macmk", + [TMS320C55_INS_MANT] = "mant", + [TMS320C55_INS_MAS] = "mas", + [TMS320C55_INS_MASM] = "masm", + [TMS320C55_INS_MAX] = "max", + [TMS320C55_INS_MAXDIFF] = "maxdiff", + [TMS320C55_INS_MIN] = "min", + [TMS320C55_INS_MINDIFF] = "mindiff", + [TMS320C55_INS_MOV] = "mov", + [TMS320C55_INS_MPY] = "mpy", + [TMS320C55_INS_MPYK] = "mpyk", + [TMS320C55_INS_MPYM] = "mpym", + [TMS320C55_INS_MPYMK] = "mpymk", + [TMS320C55_INS_NEG] = "neg", + [TMS320C55_INS_NOP] = "nop", + [TMS320C55_INS_NOT] = "not", + [TMS320C55_INS_OR] = "or", + [TMS320C55_INS_POP] = "pop", + [TMS320C55_INS_POPBOTH] = "popboth", + [TMS320C55_INS_PSH] = "psh", + [TMS320C55_INS_PSHBOTH] = "pshboth", + [TMS320C55_INS_RESET] = "reset", + [TMS320C55_INS_RET] = "ret", + [TMS320C55_INS_RETCC] = "retcc", + [TMS320C55_INS_RETI] = "reti", + [TMS320C55_INS_ROL] = "rol", + [TMS320C55_INS_ROR] = "ror", + [TMS320C55_INS_ROUND] = "round", + [TMS320C55_INS_RPT] = "rpt", + [TMS320C55_INS_RPTADD] = "rptadd", + [TMS320C55_INS_RPTB] = "rptb", + [TMS320C55_INS_RPTBLOCAL] = "rptblocal", + [TMS320C55_INS_RPTCC] = "rptcc", + [TMS320C55_INS_RPTSUB] = "rptsub", + [TMS320C55_INS_SAT] = "sat", + [TMS320C55_INS_SFTCC] = "sftcc", + [TMS320C55_INS_SFTL] = "sftl", + [TMS320C55_INS_SFTS] = "sfts", + [TMS320C55_INS_SFTSC] = "sftsc", + [TMS320C55_INS_SIM_TRIG] = "sim_trig", + [TMS320C55_INS_SQA] = "sqa", + [TMS320C55_INS_SQAM] = "sqam", + [TMS320C55_INS_SQDST] = "sqdst", + [TMS320C55_INS_SQR] = "sqr", + [TMS320C55_INS_SQRM] = "sqrm", + [TMS320C55_INS_SQS] = "sqs", + [TMS320C55_INS_SQSM] = "sqsm", + [TMS320C55_INS_SUB] = "sub", + [TMS320C55_INS_SUBADD] = "subadd", + [TMS320C55_INS_SUBC] = "subc", + [TMS320C55_INS_SWAP] = "swap", + [TMS320C55_INS_TRAP] = "trap", + [TMS320C55_INS_XCC] = "xcc", + [TMS320C55_INS_XCCPART] = "xccpart", + [TMS320C55_INS_XOR] = "xor", +}; + +RZ_API const char *tms320c55x_insn_name(TMS320C55InsID id) { + if ((size_t)id < (sizeof(tms320c55x_insn_names) / sizeof(tms320c55x_insn_names[0])) && tms320c55x_insn_names[id]) { + return tms320c55x_insn_names[id]; + } + return "invalid"; +} + +RZ_API TMS320C55InsID tms320c55x_insn_id_from_syntax(const char *syntax) { + if (!syntax) { + return TMS320C55_INS_INVALID; + } + /* Skip leading spaces and an optional C55x+ parallel marker. */ + const char *p = syntax; + while (*p == ' ' || *p == '\t') { + p++; + } + if (p[0] == '|' && p[1] == '|') { + p += 2; + while (*p == ' ' || *p == '\t') { + p++; + } + } + /* Extract the leading mnemonic token (lowercase letters, digits, _). */ + char tok[32]; + size_t n = 0; + while (n < sizeof(tok) - 1 && ((p[n] >= 'a' && p[n] <= 'z') || (p[n] >= '0' && p[n] <= '9') || p[n] == '_')) { + tok[n] = p[n]; + n++; + } + tok[n] = '\0'; + if (n == 0) { + return TMS320C55_INS_INVALID; + } + for (size_t i = 1; i < (sizeof(tms320c55x_insn_names) / sizeof(tms320c55x_insn_names[0])); i++) { + if (tms320c55x_insn_names[i] && !strcmp(tms320c55x_insn_names[i], tok)) { + return (TMS320C55InsID)i; + } + } + return TMS320C55_INS_INVALID; +} + +RZ_API int tms320c55x_insn_optype(TMS320C55InsID id) { + switch (id) { + case TMS320C55_INS_ADD: + case TMS320C55_INS_AADD: + case TMS320C55_INS_ADDSUB: + case TMS320C55_INS_ADDSUBCC: + case TMS320C55_INS_ADDSUB2CC: + case TMS320C55_INS_SUBADD: + return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_SUB: + case TMS320C55_INS_SUBC: + case TMS320C55_INS_NEG: + return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_AND: + case TMS320C55_INS_BAND: + case TMS320C55_INS_BTST: + case TMS320C55_INS_BTSTCLR: + case TMS320C55_INS_BTSTNOT: + case TMS320C55_INS_BTSTP: + case TMS320C55_INS_BTSTSET: + return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_OR: + case TMS320C55_INS_CMPOR: + return RZ_ANALYSIS_OP_TYPE_OR; + case TMS320C55_INS_XOR: + return RZ_ANALYSIS_OP_TYPE_XOR; + case TMS320C55_INS_NOT: + case TMS320C55_INS_BNOT: + return RZ_ANALYSIS_OP_TYPE_NOT; + case TMS320C55_INS_MOV: + case TMS320C55_INS_DELAY: + case TMS320C55_INS_BFXTR: + case TMS320C55_INS_BFXPA: + case TMS320C55_INS_BSET: + case TMS320C55_INS_BCLR: + case TMS320C55_INS_ROUND: + case TMS320C55_INS_EXP: + case TMS320C55_INS_MANT: + case TMS320C55_INS_BCNT: + case TMS320C55_INS_CIRC: + return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_MPY: + case TMS320C55_INS_MPYK: + case TMS320C55_INS_MPYM: + case TMS320C55_INS_MPYMK: + case TMS320C55_INS_MAC: + case TMS320C55_INS_MACK: + case TMS320C55_INS_MACM: + case TMS320C55_INS_MACMK: + case TMS320C55_INS_MAS: + case TMS320C55_INS_MASM: + case TMS320C55_INS_SQA: + case TMS320C55_INS_SQAM: + case TMS320C55_INS_SQR: + case TMS320C55_INS_SQRM: + case TMS320C55_INS_SQS: + case TMS320C55_INS_SQSM: + case TMS320C55_INS_SQDST: + case TMS320C55_INS_LMS: + case TMS320C55_INS_LMSF: + case TMS320C55_INS_FIRSADD: + case TMS320C55_INS_FIRSSUB: + return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SFTL: + case TMS320C55_INS_SFTS: + case TMS320C55_INS_SFTSC: + case TMS320C55_INS_SFTCC: + return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_ROL: + return RZ_ANALYSIS_OP_TYPE_ROL; + case TMS320C55_INS_ROR: + return RZ_ANALYSIS_OP_TYPE_ROR; + case TMS320C55_INS_CMP: + case TMS320C55_INS_CMPAND: + case TMS320C55_INS_MAX: + case TMS320C55_INS_MIN: + case TMS320C55_INS_MAXDIFF: + case TMS320C55_INS_MINDIFF: + case TMS320C55_INS_DMAXDIFF: + case TMS320C55_INS_DMINDIFF: + return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_AMOV: + case TMS320C55_INS_AMAR: + case TMS320C55_INS_ASUB: + return RZ_ANALYSIS_OP_TYPE_LEA; + case TMS320C55_INS_SWAP: + return RZ_ANALYSIS_OP_TYPE_XCHG; + case TMS320C55_INS_NOP: + case TMS320C55_INS_IDLE: + return RZ_ANALYSIS_OP_TYPE_NOP; + case TMS320C55_INS_PSH: + return RZ_ANALYSIS_OP_TYPE_PUSH; + case TMS320C55_INS_POP: + return RZ_ANALYSIS_OP_TYPE_POP; + /* PSHBOTH / POPBOTH push or pop a register *pair* in one op; the + * byte-level classifier types them UPUSH / UPOP (multi-register + * stack ops), which is more specific than PUSH / POP. Returning NULL + * here preserves that. */ + case TMS320C55_INS_RET: + case TMS320C55_INS_RETI: + return RZ_ANALYSIS_OP_TYPE_RET; + case TMS320C55_INS_RETCC: + return RZ_ANALYSIS_OP_TYPE_CRET; + case TMS320C55_INS_INTR: + return RZ_ANALYSIS_OP_TYPE_SWI; + case TMS320C55_INS_TRAP: + case TMS320C55_INS_RESET: + return RZ_ANALYSIS_OP_TYPE_TRAP; + default: + /* Control flow (B/BCC/CALL/CALLCC/...), repeats (RPT*), XCC, + * SAT, ABS, SIM_TRIG and anything else: leave to the byte-level + * classifier, which sets jump/call targets, stack deltas and + * operand fields. */ + return RZ_ANALYSIS_OP_TYPE_NULL; + } +} diff --git a/librz/arch/isa/tms320/tms320c55x_insn.h b/librz/arch/isa/tms320/tms320c55x_insn.h new file mode 100644 index 0000000000..52f7a1a7ba --- /dev/null +++ b/librz/arch/isa/tms320/tms320c55x_insn.h @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_TMS320C55X_INSN_H +#define RZ_TMS320C55X_INSN_H + +#include + +/** + * \file tms320c55x_insn.h + * + * Named instruction identifiers for the TMS320 C55x / C55x+ families, + * in the same spirit as Capstone's per-architecture instruction enums + * (e.g. ARM_INS_BL, X86_INS_MOV). Rather than dispatching analysis on + * raw opcode bytes, the disassembler tags each decoded instruction with + * one of these IDs and the analyzers switch on the ID. + * + * The prefix is TMS320C55_ (not just TMS320_) because the other TMS320 + * families -- C54x, C64x, C28x, ... -- have substantially different + * instruction sets; this enum is specific to the C55x / C55x+ ISA. The + * namespace is shared between C55x and C55x+ because those two share the + * bulk of their mnemonics; instructions that exist only on one of them + * (e.g. SWAP / SIM_TRIG on C55x+) are still part of the single enum. + * + * The mnemonic set was extracted from the C55x decoder opcode table + * (librz/arch/isa/tms320/c55x/table.h) and the C55x+ token decoder, and + * cross-referenced with TI SPRU374 (C55x) and SWPU086 / SWPU104 (C55x+). + */ +typedef enum { + TMS320C55_INS_INVALID = 0, + TMS320C55_INS_AADD, + TMS320C55_INS_ABDST, + TMS320C55_INS_ABS, + TMS320C55_INS_ADD, + TMS320C55_INS_ADDSUB, + TMS320C55_INS_ADDSUB2CC, + TMS320C55_INS_ADDSUBCC, + TMS320C55_INS_AMAR, + TMS320C55_INS_AMOV, + TMS320C55_INS_AND, + TMS320C55_INS_ASUB, + TMS320C55_INS_B, + TMS320C55_INS_BAND, + TMS320C55_INS_BCC, + TMS320C55_INS_BCLR, + TMS320C55_INS_BCNT, + TMS320C55_INS_BFXPA, + TMS320C55_INS_BFXTR, + TMS320C55_INS_BNOT, + TMS320C55_INS_BSET, + TMS320C55_INS_BTST, + TMS320C55_INS_BTSTCLR, + TMS320C55_INS_BTSTNOT, + TMS320C55_INS_BTSTP, + TMS320C55_INS_BTSTSET, + TMS320C55_INS_CALL, + TMS320C55_INS_CALLCC, + TMS320C55_INS_CIRC, + TMS320C55_INS_CMP, + TMS320C55_INS_CMPAND, + TMS320C55_INS_CMPOR, + TMS320C55_INS_DELAY, + TMS320C55_INS_DMAXDIFF, + TMS320C55_INS_DMINDIFF, + TMS320C55_INS_EXP, + TMS320C55_INS_FIRSADD, + TMS320C55_INS_FIRSSUB, + TMS320C55_INS_IDLE, + TMS320C55_INS_INTR, + TMS320C55_INS_LMS, + TMS320C55_INS_LMSF, + TMS320C55_INS_MAC, + TMS320C55_INS_MACK, + TMS320C55_INS_MACM, + TMS320C55_INS_MACMK, + TMS320C55_INS_MANT, + TMS320C55_INS_MAS, + TMS320C55_INS_MASM, + TMS320C55_INS_MAX, + TMS320C55_INS_MAXDIFF, + TMS320C55_INS_MIN, + TMS320C55_INS_MINDIFF, + TMS320C55_INS_MOV, + TMS320C55_INS_MPY, + TMS320C55_INS_MPYK, + TMS320C55_INS_MPYM, + TMS320C55_INS_MPYMK, + TMS320C55_INS_NEG, + TMS320C55_INS_NOP, + TMS320C55_INS_NOT, + TMS320C55_INS_OR, + TMS320C55_INS_POP, + TMS320C55_INS_POPBOTH, + TMS320C55_INS_PSH, + TMS320C55_INS_PSHBOTH, + TMS320C55_INS_RESET, + TMS320C55_INS_RET, + TMS320C55_INS_RETCC, + TMS320C55_INS_RETI, + TMS320C55_INS_ROL, + TMS320C55_INS_ROR, + TMS320C55_INS_ROUND, + TMS320C55_INS_RPT, + TMS320C55_INS_RPTADD, + TMS320C55_INS_RPTB, + TMS320C55_INS_RPTBLOCAL, + TMS320C55_INS_RPTCC, + TMS320C55_INS_RPTSUB, + TMS320C55_INS_SAT, + TMS320C55_INS_SFTCC, + TMS320C55_INS_SFTL, + TMS320C55_INS_SFTS, + TMS320C55_INS_SFTSC, + TMS320C55_INS_SIM_TRIG, + TMS320C55_INS_SQA, + TMS320C55_INS_SQAM, + TMS320C55_INS_SQDST, + TMS320C55_INS_SQR, + TMS320C55_INS_SQRM, + TMS320C55_INS_SQS, + TMS320C55_INS_SQSM, + TMS320C55_INS_SUB, + TMS320C55_INS_SUBADD, + TMS320C55_INS_SUBC, + TMS320C55_INS_SWAP, + TMS320C55_INS_TRAP, + TMS320C55_INS_XCC, + TMS320C55_INS_XCCPART, + TMS320C55_INS_XOR, +} TMS320C55InsID; + +/** Human-readable mnemonic for a TMS320C55InsID (never NULL). */ +RZ_API const char *tms320c55x_insn_name(TMS320C55InsID id); + +/** Resolve the leading mnemonic of a decoded syntax string to its + * TMS320C55InsID. Parses the first whitespace-delimited token, skipping a + * leading C55x+ parallel marker ("|| ") if present. Returns + * TMS320C55_INS_INVALID when no mnemonic matches. */ +RZ_API TMS320C55InsID tms320c55x_insn_id_from_syntax(const char *syntax); + +/** Map a TMS320C55InsID to the closest RzAnalysisOp type + * (RZ_ANALYSIS_OP_TYPE_*), returned as int to avoid pulling rz_analysis.h + * into this header. Returns RZ_ANALYSIS_OP_TYPE_NULL (0) for instructions + * with no clean scalar type (control flow, repeats, and forms whose type + * the analyzer derives from operands). This is the authoritative type for + * the arithmetic / logical / move / stack families, where a leading-byte + * switch cannot tell e.g. ADD from SUB or AND from XOR. */ +RZ_API int tms320c55x_insn_optype(TMS320C55InsID id); + +#endif /* RZ_TMS320C55X_INSN_H */ diff --git a/librz/arch/meson.build b/librz/arch/meson.build index ba38ca155f..001670a05c 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -323,6 +323,7 @@ arch_isa_sources = [ 'isa/tms320/c55x_plus/ins.c', 'isa/tms320/c64x/c64x.c', 'isa/tms320/tms320_dasm.c', + 'isa/tms320/tms320c55x_insn.c', 'isa/v810/v810_disas.c', 'isa/v810/v810_il.c', 'isa/v850/v850_disas.c', diff --git a/test/db/analysis/tms320.c55x+_32 b/test/db/analysis/tms320.c55x+_32 index 6fe867e488..cccc968b8d 100644 --- a/test/db/analysis/tms320.c55x+_32 +++ b/test/db/analysis/tms320.c55x+_32 @@ -244,7 +244,7 @@ type: push type: pop type: upush type: lea -type: sub +type: lea type: null type: sub type: cmp @@ -511,3 +511,70 @@ stackframe: 6 stackframe: 0 EOF RUN + +NAME=c55x+ analysis: named instruction id (op->id = TMS320C55_INS_*) +CMDS=<id carries the named TMS320C55InsID resolved by the C55x+ token +# decoder (positions in tms320_insn.h): AADD=1, INTR=39, NOP=59, POP=62, +# PSH=64, RET=67, RETI=69, TRAP=96. +wx 21 @ 0 +ao 1 @ 0~id +wx 0e00 @ 0 +ao 1 @ 0~id +wx 0f00 @ 0 +ao 1 @ 0~id +wx 0310 @ 0 +ao 1 @ 0~id +wx 0350 @ 0 +ao 1 @ 0~id +wx 00c0 @ 0 +ao 1 @ 0~id +wx 20 @ 0 +ao 1 @ 0~id +wx 0c10 @ 0 +ao 1 @ 0~id +EOF +EXPECT=< LD (mov); clear -> add/sub by byte2 bit7 +# (TI SWPU104 Table 7-2, opcode 01111011). +wx 7b8000 @ 0 +ao 1 @ 0~^type +wx 7b3000 @ 0 +ao 1 @ 0~^type +wx 7b3081 @ 0 +ao 1 @ 0~^type +# 0xD2 mar(XDAa op k24): address-register modify, classified lea like +# AADD/AMOV (TI SWPU104 Table 7-2, opcode 11010010). amov and asub forms +# both resolve to lea. +wx d280000000 @ 0 +ao 1 @ 0~^type +wx d200000000 @ 0 +ao 1 @ 0~^type +EOF +EXPECT=<id = TMS320C55_INS_*) +CMDS=<id (Capstone-style). These values are the +# positions in the TMS320C55_INS_* enum (see tms320_insn.h): B=12, CALL=26, +# RET=67, NOP=59, PSH=64, POP=62, AND=10, MOV=53, INTR=39, TRAP=96. +wx 060020 @ 0 +ao 1 @ 0~id +wx 080020 @ 0 +ao 1 @ 0~id +wx 4884 @ 0 +ao 1 @ 0~id +wx 20 @ 0 +ao 1 @ 0~id +wx 3800 @ 0 +ao 1 @ 0~id +wx 3a00 @ 0 +ao 1 @ 0~id +wx 1010 @ 0 +ao 1 @ 0~id +wx a000 @ 0 +ao 1 @ 0~id +wx 950f @ 0 +ao 1 @ 0~id +wx 958f @ 0 +ao 1 @ 0~id +EOF +EXPECT=<