rizin/librz/arch/isa/tms320/c5x/c5x.c
Anton Kochkov 38c3bed6c9
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
librz/arch/tms320/c5x: classify BSAR as a right shift in analysis
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.
2026-08-17 01:53:33 +08:00

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;
}