mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
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'
This commit is contained in:
committed by
NOT XVilka
parent
6d57743bb6
commit
13ebcbc2c7
@@ -9,6 +9,8 @@
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#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 <src_high_nib>0 <dst_high_nib>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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <string.h>
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_TMS320C55X_INSN_H
|
||||
#define RZ_TMS320C55X_INSN_H
|
||||
|
||||
#include <rz_types.h>
|
||||
|
||||
/**
|
||||
* \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 */
|
||||
@@ -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',
|
||||
|
||||
@@ -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=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x+
|
||||
# op->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=<<EOF
|
||||
id: 67
|
||||
id: 64
|
||||
id: 62
|
||||
id: 39
|
||||
id: 96
|
||||
id: 69
|
||||
id: 59
|
||||
id: 1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis: regression - decoded-id type override
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x+
|
||||
# These leading bytes encode multiple instructions disambiguated by
|
||||
# operand bits; the decoded TMS320C55_INS_* id drives the final type.
|
||||
# 0x7B byte1 bit7 set -> 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=<<EOF
|
||||
type: mov
|
||||
type: add
|
||||
type: sub
|
||||
type: lea
|
||||
type: lea
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -188,7 +188,7 @@ wx c000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7a01 @ 0
|
||||
wx 7a010000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7b00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
@@ -295,7 +295,7 @@ type: cret
|
||||
type: cjmp
|
||||
type: jmp
|
||||
type: call
|
||||
type: null
|
||||
type: ccall
|
||||
type: add
|
||||
type: sub
|
||||
type: mov
|
||||
@@ -351,7 +351,7 @@ type: sub
|
||||
type: cmp
|
||||
type: cmp
|
||||
type: xchg
|
||||
type: mov
|
||||
type: null
|
||||
type: lea
|
||||
type: lea
|
||||
type: call
|
||||
@@ -548,7 +548,7 @@ cpu c55x
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
3
|
||||
stackframe: 14
|
||||
stackframe: 15
|
||||
call-refs: 0x000002bb C 0x00000364 C
|
||||
EOF
|
||||
RUN
|
||||
@@ -696,3 +696,90 @@ args: 1
|
||||
locals: 0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: named instruction id (op->id = TMS320C55_INS_*)
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x
|
||||
# The disassembler tags each instruction with a named TMS320C55InsID, which
|
||||
# the analyzer surfaces on op->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=<<EOF
|
||||
id: 12
|
||||
id: 26
|
||||
id: 67
|
||||
id: 59
|
||||
id: 64
|
||||
id: 62
|
||||
id: 10
|
||||
id: 53
|
||||
id: 39
|
||||
id: 96
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: regression - byte-vs-decoded id classification
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x
|
||||
# These cases were mis-classified by the earlier raw-opcode-byte switch
|
||||
# and are fixed by dispatching on the decoded TMS320C55_INS_* id.
|
||||
#
|
||||
# 0x50 is a multi-form leading byte: with operand 0x66 it is `psh t2`
|
||||
# (a stack push), not the `sftl` form the bare byte 0x50 maps to. The
|
||||
# old byte switch typed it SHL; it must be a stack push (stackptr=1).
|
||||
wx 5066 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
# 0x48 0x05 decodes to `reti`, a return. The old switch typed the whole
|
||||
# 0x48 family REP; reti must be RET.
|
||||
wx 4805 @ 0
|
||||
ao 1 @ 0~^type
|
||||
# 0x95 with bit7 of byte1 clear is INTR (swi); set is TRAP. The decoder
|
||||
# distinguishes them; both carry the 5-bit immediate in val.
|
||||
wx 950f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
wx 958f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
# DELAY (0xb6) is a memory-delay MOVE per TI SWPU104 6.7.1: it accesses
|
||||
# memory (refptr=2), it is not a CPU-control op.
|
||||
wx b600 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^refptr
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
type: push
|
||||
stackptr: 1
|
||||
type: ret
|
||||
type: swi
|
||||
val: 0x0000000f
|
||||
type: trap
|
||||
val: 0x0000000f
|
||||
type: mov
|
||||
refptr: 2
|
||||
EOF
|
||||
RUN
|
||||
|
||||
Reference in New Issue
Block a user