mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
Some checks failed
Code scanning / build (CodeQL-cpp) (push) Has been cancelled
Code scanning / build (CodeQL-javascript) (push) Has been cancelled
Code scanning / build (CodeQL-python) (push) Has been cancelled
Mixed linter and checks / changes (push) Has been cancelled
Mixed linter and checks / licenses (push) Has been cancelled
Muon build / ubuntu-muon (push) Has been cancelled
TinyCC build / ubuntu-tcc-test (push) Has been cancelled
Mixed linter and checks / clang-format (push) Has been cancelled
Mixed linter and checks / prettier (push) Has been cancelled
Mixed linter and checks / cmd_descs_yaml_check (push) Has been cancelled
Mixed linter and checks / bindgen-linter (push) Has been cancelled
Mixed linter and checks / python (push) Has been cancelled
BSAR (Barrel Shift ACC Right) was grouped with the left-shift ops and reported RZ_ANALYSIS_OP_TYPE_SHL. It shifts the accumulator right (1..16 bits, sign-extended under SXM), so it belongs with the SHR group next to RORB/SFRB. The decode (shift code + 1) was already correct and continues to match the reference disassembler bit-for-bit.
292 lines
8.7 KiB
C
292 lines
8.7 KiB
C
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/**
|
|
* \file
|
|
* TMS320C5x architecture descriptor, analysis entry and RzIL VM configuration.
|
|
*
|
|
* The C5x has its own decode front-end (c5x_decode, the real C5x object
|
|
* encoding) and reuses the shared C55 consumers (c55_format, c55_fill_analysis,
|
|
* c55_lift) through the descriptor below. Shared-semantics instructions carry
|
|
* the C2x ids and route through the C2x mnemonic/op-type/lifter; the C5x-only
|
|
* ids are handled by c5x_mnemonic/c5x_op_type/c5x_lift.
|
|
*/
|
|
|
|
#include "c5x.h"
|
|
#include "../c2x/c2x.h"
|
|
|
|
/**
|
|
* \brief Map an instruction id to its C5x mnemonic.
|
|
* \param id C5X_INS_* id, or a shared C2X_INS_* id
|
|
* \return Static mnemonic string, never NULL
|
|
*
|
|
* Only the C5x-only ids and the shared ids whose canonical C5x spelling
|
|
* differs from the C2x one are handled here; the rest delegate to
|
|
* \ref c2x_mnemonic.
|
|
*/
|
|
RZ_IPI const char *c5x_mnemonic(ut16 id) {
|
|
switch (id) {
|
|
// shared ids whose canonical C5x spelling differs from the C2x mnemonic
|
|
case C2X_INS_ADDK: return "add";
|
|
case C2X_INS_SUBK: return "sub";
|
|
case C2X_INS_LARK: return "lar";
|
|
// 16-bit immediate ALU forms (decoded as the C2x long-immediate ops)
|
|
case C2X_INS_LALK: return "lacc";
|
|
case C2X_INS_ADLK: return "add";
|
|
case C2X_INS_SBLK: return "sub";
|
|
case C2X_INS_ANDK: return "and";
|
|
case C2X_INS_ORK: return "or";
|
|
case C2X_INS_XORK: return "xor";
|
|
case C2X_INS_RPTK: return "rpt";
|
|
case C2X_INS_LDPK: return "ldp";
|
|
// C5x-only mnemonics
|
|
case C5X_INS_LACC: return "lacc";
|
|
case C5X_INS_LACL: return "lacl";
|
|
case C5X_INS_LACB: return "lacb";
|
|
case C5X_INS_SACB: return "sacb";
|
|
case C5X_INS_EXAR: return "exar";
|
|
case C5X_INS_ADDB: return "addb";
|
|
case C5X_INS_SBB: return "sbb";
|
|
case C5X_INS_ADCB: return "adcb";
|
|
case C5X_INS_SBBB: return "sbbb";
|
|
case C5X_INS_CRGT: return "crgt";
|
|
case C5X_INS_CRLT: return "crlt";
|
|
case C5X_INS_ANDB: return "andb";
|
|
case C5X_INS_ORB: return "orb";
|
|
case C5X_INS_XORB: return "xorb";
|
|
case C5X_INS_ROLB: return "rolb";
|
|
case C5X_INS_RORB: return "rorb";
|
|
case C5X_INS_SFLB: return "sflb";
|
|
case C5X_INS_SFRB: return "sfrb";
|
|
case C5X_INS_SAMM: return "samm";
|
|
case C5X_INS_LAMM: return "lamm";
|
|
case C5X_INS_LMMR: return "lmmr";
|
|
case C5X_INS_SMMR: return "smmr";
|
|
case C5X_INS_BLDD: return "bldd";
|
|
case C5X_INS_BLPD: return "blpd";
|
|
case C5X_INS_BLDP: return "bldp";
|
|
case C5X_INS_MADS: return "mads";
|
|
case C5X_INS_MADD: return "madd";
|
|
case C5X_INS_SPLK: return "splk";
|
|
case C5X_INS_BCND: return "bcnd";
|
|
case C5X_INS_BCNDD: return "bcndd";
|
|
case C5X_INS_CC: return "cc";
|
|
case C5X_INS_CCD: return "ccd";
|
|
case C5X_INS_RETC: return "retc";
|
|
case C5X_INS_RETCD: return "retcd";
|
|
case C5X_INS_RETD: return "retd";
|
|
case C5X_INS_XC: return "xc";
|
|
case C5X_INS_BSAR: return "bsar";
|
|
case C5X_INS_ZAP: return "zap";
|
|
case C5X_INS_ZPR: return "zpr";
|
|
case C5X_INS_SATH: return "sath";
|
|
case C5X_INS_SATL: return "satl";
|
|
case C5X_INS_BACCD: return "baccd";
|
|
case C5X_INS_CALAD: return "calad";
|
|
case C5X_INS_APL: return "apl";
|
|
case C5X_INS_OPL: return "opl";
|
|
case C5X_INS_XPL: return "xpl";
|
|
case C5X_INS_CPL: return "cpl";
|
|
case C5X_INS_RPTB: return "rptb";
|
|
case C5X_INS_RPTZ: return "rptz";
|
|
case C5X_INS_SETC: return "setc";
|
|
case C5X_INS_CLRC: return "clrc";
|
|
case C5X_INS_IDLE2: return "idle2";
|
|
case C5X_INS_NMI: return "nmi";
|
|
case C5X_INS_RETE: return "rete";
|
|
case C5X_INS_RETI: return "reti";
|
|
case C5X_INS_INTR: return "intr";
|
|
case C5X_INS_BD: return "bd";
|
|
case C5X_INS_CALLD: return "calld";
|
|
case C5X_INS_BANZD: return "banzd";
|
|
case C5X_INS_LST: return "lst";
|
|
case C5X_INS_SST: return "sst";
|
|
default: return c2x_mnemonic(id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Map an instruction id to its RzAnalysisOp type.
|
|
* \param id C5X_INS_* id, or a shared C2X_INS_* id
|
|
* \return RZ_ANALYSIS_OP_TYPE_* value for \p id
|
|
*
|
|
* Shared ids delegate to \ref c2x_op_type.
|
|
*/
|
|
RZ_IPI ut32 c5x_op_type(ut16 id) {
|
|
switch (id) {
|
|
case C5X_INS_LACC:
|
|
case C5X_INS_LACL:
|
|
case C5X_INS_LACB:
|
|
case C5X_INS_SACB:
|
|
case C5X_INS_EXAR:
|
|
case C5X_INS_SAMM:
|
|
case C5X_INS_LAMM:
|
|
case C5X_INS_LMMR:
|
|
case C5X_INS_SMMR:
|
|
case C5X_INS_BLDD:
|
|
case C5X_INS_BLPD:
|
|
case C5X_INS_BLDP:
|
|
case C5X_INS_SPLK:
|
|
return RZ_ANALYSIS_OP_TYPE_MOV;
|
|
case C5X_INS_ADDB:
|
|
case C5X_INS_ADCB:
|
|
return RZ_ANALYSIS_OP_TYPE_ADD;
|
|
case C5X_INS_SBB:
|
|
case C5X_INS_SBBB:
|
|
return RZ_ANALYSIS_OP_TYPE_SUB;
|
|
case C5X_INS_ANDB:
|
|
return RZ_ANALYSIS_OP_TYPE_AND;
|
|
case C5X_INS_ORB:
|
|
return RZ_ANALYSIS_OP_TYPE_OR;
|
|
case C5X_INS_XORB:
|
|
return RZ_ANALYSIS_OP_TYPE_XOR;
|
|
case C5X_INS_CRGT:
|
|
case C5X_INS_CRLT:
|
|
case C5X_INS_SATH:
|
|
case C5X_INS_SATL:
|
|
case C5X_INS_APL:
|
|
case C5X_INS_OPL:
|
|
case C5X_INS_XPL:
|
|
case C5X_INS_CPL:
|
|
case C5X_INS_MADS:
|
|
case C5X_INS_MADD:
|
|
return RZ_ANALYSIS_OP_TYPE_MOV;
|
|
case C5X_INS_ROLB:
|
|
case C5X_INS_SFLB:
|
|
return RZ_ANALYSIS_OP_TYPE_SHL;
|
|
case C5X_INS_RORB:
|
|
case C5X_INS_SFRB:
|
|
case C5X_INS_BSAR:
|
|
return RZ_ANALYSIS_OP_TYPE_SHR;
|
|
case C5X_INS_ZAP:
|
|
case C5X_INS_ZPR:
|
|
return RZ_ANALYSIS_OP_TYPE_MOV;
|
|
case C5X_INS_SETC:
|
|
case C5X_INS_CLRC:
|
|
return RZ_ANALYSIS_OP_TYPE_MOV;
|
|
case C5X_INS_LST:
|
|
case C5X_INS_SST:
|
|
return RZ_ANALYSIS_OP_TYPE_MOV;
|
|
case C5X_INS_BCND:
|
|
case C5X_INS_BCNDD:
|
|
case C5X_INS_XC:
|
|
case C5X_INS_BANZD:
|
|
return RZ_ANALYSIS_OP_TYPE_CJMP;
|
|
case C5X_INS_BD:
|
|
return RZ_ANALYSIS_OP_TYPE_JMP;
|
|
case C5X_INS_CC:
|
|
case C5X_INS_CCD:
|
|
return RZ_ANALYSIS_OP_TYPE_CCALL;
|
|
case C5X_INS_CALLD:
|
|
return RZ_ANALYSIS_OP_TYPE_CALL;
|
|
case C5X_INS_BACCD:
|
|
return RZ_ANALYSIS_OP_TYPE_UJMP;
|
|
case C5X_INS_CALAD:
|
|
return RZ_ANALYSIS_OP_TYPE_UCALL;
|
|
case C5X_INS_RETC:
|
|
case C5X_INS_RETCD:
|
|
return RZ_ANALYSIS_OP_TYPE_CRET;
|
|
case C5X_INS_RETD:
|
|
return RZ_ANALYSIS_OP_TYPE_RET;
|
|
case C5X_INS_RPTB:
|
|
case C5X_INS_RPTZ:
|
|
return RZ_ANALYSIS_OP_TYPE_NOP;
|
|
case C5X_INS_IDLE2:
|
|
case C5X_INS_NMI:
|
|
case C5X_INS_INTR:
|
|
return RZ_ANALYSIS_OP_TYPE_NULL;
|
|
case C5X_INS_RETE:
|
|
case C5X_INS_RETI:
|
|
return RZ_ANALYSIS_OP_TYPE_RET;
|
|
default:
|
|
return c2x_op_type(id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief C5x architecture descriptor for the shared C55 engine.
|
|
*
|
|
* Carries the C5x's own decoder and the consumers that fall back to the C2x
|
|
* implementations for the shared instruction ids.
|
|
*/
|
|
const C55ArchDesc c5x_arch_desc = {
|
|
.arch = C55_ARCH_C5X,
|
|
.cpu_name = "c5x",
|
|
.table = NULL, // decoded by c5x_decode(), not the shared table engine
|
|
.table_len = 0,
|
|
.insn_len = NULL,
|
|
.reg_info = c2x_reg_info, // operands reference only C2x reg classes (ARn)
|
|
.mnemonic = c5x_mnemonic,
|
|
.op_type = c5x_op_type,
|
|
.lift = c5x_lift,
|
|
.mem = { .addr_unit_log2 = 0, .ptr_width = 16, .big_endian = true, .page_reg = "dp" },
|
|
.ea = c2x_ea,
|
|
.fill_dual = NULL,
|
|
.words_le = false,
|
|
.cond_exec_prefix = false,
|
|
.parallel_prefix = false,
|
|
};
|
|
|
|
/**
|
|
* \brief Analysis entry point for the "c5x" CPU.
|
|
* \param analysis Current analysis session
|
|
* \param op Operation to fill in
|
|
* \param addr Address \p buf was read from
|
|
* \param buf Instruction bytes
|
|
* \param len Number of readable bytes in \p buf
|
|
* \param mask Which parts of \p op the caller wants filled
|
|
* \return Instruction length in bytes, or -1 on an undecodable word
|
|
*/
|
|
RZ_IPI int tms320_c5x_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
|
const ut8 *buf, int len, RzAnalysisOpMask mask) {
|
|
if (!op || !buf || len < 1) {
|
|
return 0;
|
|
}
|
|
op->addr = addr;
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
|
C55Insn ci;
|
|
int n = c5x_decode(buf, len, &ci);
|
|
if (n > 0) {
|
|
c55_fill_analysis(&c5x_arch_desc, &ci, op);
|
|
c2x_fill_op_access(analysis, &c5x_arch_desc, &ci, op);
|
|
if (mask & RZ_ANALYSIS_OP_MASK_IL) {
|
|
op->il_op = c55_lift(&c5x_arch_desc, &ci, op->addr);
|
|
}
|
|
} else {
|
|
// Undecodable word: flag it and give a one-word fallback size, but
|
|
// report the failure to the caller with -1 (the common plugin convention).
|
|
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
|
|
op->size = 1;
|
|
return -1;
|
|
}
|
|
return op->size;
|
|
}
|
|
|
|
// IL register bindings: the C2x core registers the shared lifter uses, plus the
|
|
// C5x-specific registers (ACCB and friends) referenced by the C5x lifter.
|
|
static const char *c5x_il_regs[] = {
|
|
"acc", "accb", "t", "treg1", "treg2", "p",
|
|
"ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7",
|
|
"arp", "dp", "st0", "st1", "pmst",
|
|
"indx", "arcr", "cbsr1", "cber1", "cbsr2", "cber2",
|
|
"brcr", "pasr", "paer", "bmar", "dbmr", "greg",
|
|
"pc", "sp",
|
|
"c", "ov", "tc", "ovm", "sxm", "pm", "arb", "rptc",
|
|
NULL
|
|
};
|
|
|
|
/**
|
|
* \brief Build the RzIL VM configuration for the "c5x" CPU.
|
|
* \param analysis Current analysis session
|
|
* \return Newly allocated config carrying the C5x register bindings
|
|
*/
|
|
RZ_IPI RzAnalysisILConfig *tms320_c5x_il_config(RZ_NONNULL RzAnalysis *analysis) {
|
|
rz_return_val_if_fail(analysis, NULL);
|
|
// Same word-addressed memory model as the C2x (see C2X_MEM_ADDR_BITS).
|
|
RzAnalysisILConfig *cfg = rz_analysis_il_config_new(16, true, C2X_MEM_ADDR_BITS);
|
|
if (!cfg) {
|
|
return NULL;
|
|
}
|
|
cfg->reg_bindings = c5x_il_regs;
|
|
return cfg;
|
|
}
|