mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
librz/arch/tms320: add TMS320C5x disassembler and analysis
Add the real TMS320C5x (C50/C51/C53) object encoding. The C5x is source-compatible with the C2x but encodes instructions differently, so it cannot reuse the C2x decode table: a dedicated C5x decode front-end fills the shared C55 instruction representation, carrying the C2x ids for shared-semantics instructions (so the common consumers apply unchanged) and new C5x-only ids for the C5x additions (ACCB ops, parallel-logic, memory-mapped register access, conditional execute/call/return, block moves, ...). Wired into the tms320 asm and analysis plugins under cpu "c5x", with the C5x mnemonic and op-type tables and register profile. Includes disassembly and opcode classification tests.
This commit is contained in:
parent
7b36a031ca
commit
148b54a9fc
12 changed files with 1544 additions and 12 deletions
263
librz/arch/isa/tms320/c5x/c5x.c
Normal file
263
librz/arch/isa/tms320/c5x/c5x.c
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
// 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:
|
||||
case C5X_INS_BSAR:
|
||||
return RZ_ANALYSIS_OP_TYPE_SHL;
|
||||
case C5X_INS_RORB:
|
||||
case C5X_INS_SFRB:
|
||||
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 = NULL,
|
||||
.mem = { .addr_unit_log2 = 0, .ptr_width = 16, .big_endian = true, .page_reg = "dp" },
|
||||
.ea = NULL,
|
||||
.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;
|
||||
}
|
||||
112
librz/arch/isa/tms320/c5x/c5x.h
Normal file
112
librz/arch/isa/tms320/c5x/c5x.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file
|
||||
* TMS320C5x (second-generation fixed-point DSP, e.g. TMS320C50/C51/C53).
|
||||
*
|
||||
* The C5x is source-compatible with the C2x (same assembly mnemonics) but uses
|
||||
* a completely different object encoding, so it has its own decode front-end
|
||||
* (c5x_decode, ported from the authoritative TMS320C5x opcode table) rather than
|
||||
* the C2x decode table. The decoder fills the shared C55Insn; shared-semantics
|
||||
* instructions reuse the C2x instruction ids and the C2x lifter, while the
|
||||
* C5x-only instructions (ACCB buffer ops, parallel-logic, memory-mapped register
|
||||
* access, conditional execute/call/return, block moves, ...) carry the C5X_INS_*
|
||||
* ids below and are handled by c5x_mnemonic/c5x_op_type/c5x_lift, which delegate
|
||||
* to the C2x consumers for the shared ids.
|
||||
*/
|
||||
|
||||
#ifndef RZ_TMS320_C5X_H
|
||||
#define RZ_TMS320_C5X_H
|
||||
|
||||
#include "../c2x/c2x.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// C5x-only instruction ids (shared-semantics ops reuse the C2X_INS_* ids). The
|
||||
/// base sits above the C2x enum so the two id spaces never collide.
|
||||
enum {
|
||||
C5X_INS_BASE = 0x200,
|
||||
C5X_INS_LACC, ///< load ACC with shift (C2x LAC)
|
||||
C5X_INS_LACL, ///< load ACC low, zero-extended
|
||||
C5X_INS_LACB, ///< load ACC from ACCB
|
||||
C5X_INS_SACB, ///< store ACC to ACCB
|
||||
C5X_INS_EXAR, ///< exchange ACC and ACCB
|
||||
C5X_INS_ADDB, ///< ACC += ACCB
|
||||
C5X_INS_SBB, ///< ACC -= ACCB
|
||||
C5X_INS_ADCB, ///< ACC += ACCB + carry
|
||||
C5X_INS_SBBB, ///< ACC -= ACCB + borrow
|
||||
C5X_INS_CRGT, ///< ACC = max(ACC, ACCB)
|
||||
C5X_INS_CRLT, ///< ACC = min(ACC, ACCB)
|
||||
C5X_INS_ANDB, ///< ACC &= ACCB
|
||||
C5X_INS_ORB, ///< ACC |= ACCB
|
||||
C5X_INS_XORB, ///< ACC ^= ACCB
|
||||
C5X_INS_ROLB, ///< rotate ACC:ACCB left through carry
|
||||
C5X_INS_RORB, ///< rotate ACC:ACCB right through carry
|
||||
C5X_INS_SFLB, ///< shift ACC:ACCB left
|
||||
C5X_INS_SFRB, ///< shift ACC:ACCB right
|
||||
C5X_INS_SAMM, ///< store ACC to a memory-mapped register
|
||||
C5X_INS_LAMM, ///< load ACC from a memory-mapped register
|
||||
C5X_INS_LMMR, ///< load a memory-mapped register (long immediate addr)
|
||||
C5X_INS_SMMR, ///< store a memory-mapped register (long immediate addr)
|
||||
C5X_INS_BLDD, ///< block move data to data
|
||||
C5X_INS_BLPD, ///< block move program to data
|
||||
C5X_INS_BLDP, ///< block move data to program
|
||||
C5X_INS_MADS, ///< multiply-accumulate with data move (BMAR address)
|
||||
C5X_INS_MADD, ///< multiply-accumulate with data move and delay
|
||||
C5X_INS_SPLK, ///< store parallel long immediate constant
|
||||
C5X_INS_BCND, ///< conditional branch
|
||||
C5X_INS_BCNDD, ///< delayed conditional branch
|
||||
C5X_INS_CC, ///< conditional call
|
||||
C5X_INS_CCD, ///< delayed conditional call
|
||||
C5X_INS_RETC, ///< conditional return
|
||||
C5X_INS_RETCD, ///< delayed conditional return
|
||||
C5X_INS_RETD, ///< unconditional delayed return
|
||||
C5X_INS_XC, ///< conditionally execute next n words
|
||||
C5X_INS_BSAR, ///< barrel shift ACC right
|
||||
C5X_INS_ZAP, ///< clear ACC and PREG
|
||||
C5X_INS_ZPR, ///< clear PREG
|
||||
C5X_INS_SATH, ///< saturate ACC high
|
||||
C5X_INS_SATL, ///< saturate ACC low
|
||||
C5X_INS_BACCD, ///< delayed branch to ACC address
|
||||
C5X_INS_CALAD, ///< delayed call to ACC address
|
||||
C5X_INS_APL, ///< AND data with DBMR / long immediate
|
||||
C5X_INS_OPL, ///< OR data with DBMR / long immediate
|
||||
C5X_INS_XPL, ///< XOR data with DBMR / long immediate
|
||||
C5X_INS_CPL, ///< compare data with DBMR / long immediate
|
||||
C5X_INS_RPTB, ///< repeat block
|
||||
C5X_INS_RPTZ, ///< repeat next instruction, clearing ACC and PREG
|
||||
C5X_INS_SETC, ///< set a control bit
|
||||
C5X_INS_CLRC, ///< clear a control bit
|
||||
C5X_INS_IDLE2, ///< idle until interrupt (low-power)
|
||||
C5X_INS_NMI, ///< non-maskable interrupt
|
||||
C5X_INS_RETE, ///< return from interrupt with enable
|
||||
C5X_INS_RETI, ///< return from interrupt
|
||||
C5X_INS_INTR, ///< software interrupt
|
||||
C5X_INS_BD, ///< delayed branch
|
||||
C5X_INS_CALLD, ///< delayed call
|
||||
C5X_INS_BANZD, ///< delayed branch on AR not zero
|
||||
C5X_INS_LST, ///< load status register STn (C5x #n, mem form)
|
||||
C5X_INS_SST, ///< store status register STn (C5x #n, mem form)
|
||||
};
|
||||
|
||||
RZ_IPI int c5x_decode(const ut8 *buf, int len, C55Insn *out);
|
||||
|
||||
RZ_IPI const char *c5x_mnemonic(ut16 id);
|
||||
RZ_IPI ut32 c5x_op_type(ut16 id);
|
||||
RZ_IPI RzILOpEffect *c5x_lift(const C55Insn *insn, ut64 pc);
|
||||
|
||||
extern const C55ArchDesc c5x_arch_desc;
|
||||
|
||||
RZ_IPI int tms320_c5x_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len, RzAnalysisOpMask mask);
|
||||
|
||||
RZ_IPI RzAnalysisILConfig *tms320_c5x_il_config(RZ_NONNULL RzAnalysis *analysis);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RZ_TMS320_C5X_H */
|
||||
681
librz/arch/isa/tms320/c5x/c5x_decode.c
Normal file
681
librz/arch/isa/tms320/c5x/c5x_decode.c
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file
|
||||
* TMS320C5x decode front-end (the real C5x object encoding).
|
||||
*
|
||||
* The C5x is source-compatible with the C2x but encodes instructions
|
||||
* differently, so it cannot reuse the C2x decode table. c5x_decode() ports the
|
||||
* authoritative TMS320C5x opcode table directly and fills the shared C55Insn:
|
||||
* shared-semantics instructions carry the C2X_INS_* ids (so the C2x lifter,
|
||||
* op-type and mnemonic tables apply unchanged) and C5x-only instructions carry
|
||||
* the C5X_INS_* ids handled by c5x.c.
|
||||
*
|
||||
* Memory operands use one addressing byte (the low 8 bits of the opcode word):
|
||||
* bit 7 selects direct (0) or indirect (1). For indirect, bits 6:3 are a 4-bit
|
||||
* sub-mode (the LSB of which requests a next-ARP load from bits 2:0): 0=*, 2=*-,
|
||||
* 4=*+, 8=*BR0-, A=*0-, C=*0+, E=*BR0+. Operand order matches the C2x
|
||||
* convention so the shared consumers render and lift them identically.
|
||||
*/
|
||||
|
||||
#include "c5x.h"
|
||||
#include <rz_util.h>
|
||||
|
||||
static const char *const c5x_indir_raw[16] = {
|
||||
"*", "*", "*-", "*-", "*+", "*+", "*?", "*?",
|
||||
"*br0-", "*br0-", "*0-", "*0-", "*0+", "*0+", "*br0+", "*br0+"
|
||||
};
|
||||
static const C55AddrMode c5x_indir_amode[16] = {
|
||||
C55_AM_INDIRECT, C55_AM_INDIRECT, C55_AM_POSTDEC, C55_AM_POSTDEC,
|
||||
C55_AM_POSTINC, C55_AM_POSTINC, C55_AM_INDIRECT, C55_AM_INDIRECT,
|
||||
C55_AM_BITREV_SUB, C55_AM_BITREV_SUB, C55_AM_POSTSUB, C55_AM_POSTSUB,
|
||||
C55_AM_POSTADD, C55_AM_POSTADD, C55_AM_BITREV, C55_AM_BITREV
|
||||
};
|
||||
static const char *const c5x_arx_raw[8] = {
|
||||
"ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7"
|
||||
};
|
||||
|
||||
// A data-memory operand from one addressing byte: direct (DP-relative, in disp)
|
||||
// or indirect (ARP-relative, rendered via raw).
|
||||
static void mem_op(C55Operand *out, ut8 low) {
|
||||
out->kind = C55_OP_MEM;
|
||||
out->access = 16;
|
||||
if (low & 0x80) {
|
||||
ut8 sub = (low >> 3) & 0xf;
|
||||
out->amode = c5x_indir_amode[sub];
|
||||
out->reg.cls = C55_RC_AR;
|
||||
out->raw = c5x_indir_raw[sub];
|
||||
} else {
|
||||
out->amode = C55_AM_DIRECT;
|
||||
out->disp = (st32)(low & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
// The next-ARP operand (rendered ",arN") when an indirect sub-mode is odd.
|
||||
static bool narp_op(C55Operand *out, ut8 low) {
|
||||
if ((low & 0x80) && ((low >> 3) & 1)) {
|
||||
out->kind = C55_OP_REG;
|
||||
out->reg.cls = C55_RC_AR;
|
||||
out->reg.num = low & 7;
|
||||
out->width = 16;
|
||||
out->raw = c5x_arx_raw[low & 7];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void imm_op(C55Operand *out, ut64 v, ut8 width, bool is_signed) {
|
||||
out->kind = C55_OP_IMM;
|
||||
out->imm = v;
|
||||
out->width = width;
|
||||
out->imm_signed = is_signed;
|
||||
}
|
||||
|
||||
static void shift_op(C55Operand *out, ut8 sh) {
|
||||
out->kind = C55_OP_IMM;
|
||||
out->imm = sh;
|
||||
out->width = 16;
|
||||
}
|
||||
|
||||
static void ar_op(C55Operand *out, ut8 n) {
|
||||
out->kind = C55_OP_REG;
|
||||
out->reg.cls = C55_RC_AR;
|
||||
out->reg.num = n & 7;
|
||||
out->width = 16;
|
||||
out->raw = c5x_arx_raw[n & 7];
|
||||
}
|
||||
|
||||
// 16-bit absolute branch/call target carried in the trailing word.
|
||||
static void target_op(C55Operand *out, ut16 t) {
|
||||
out->kind = C55_OP_IMM;
|
||||
out->imm = t;
|
||||
out->width = 16;
|
||||
out->addr = true;
|
||||
out->abs_target = true;
|
||||
}
|
||||
|
||||
// Condition fragments for the 0xE0-0xFF group, rendered verbatim. zl/cv/tp are
|
||||
// packed across the opcode's high and low nibbles (see the C5x opcode table).
|
||||
static const char *const c5x_zl[16] = {
|
||||
"", "gt", "neq", "gt", "", "lt", "neq", "lt", "", "gt", "eq", "geq", "", "lt", "eq", "leq"
|
||||
};
|
||||
static const char *const c5x_cv[16] = {
|
||||
"", "nc", "nov", "nc nov", "", "c", "nov", "c nov", "", "nc", "ov", "nc ov", "", "c", "ov", "c ov"
|
||||
};
|
||||
static const char *const c5x_tp[4] = { "bio", "tc", "ntc", "" };
|
||||
|
||||
// Control bits addressed by SETC/CLRC (sub 0x40-0x4f), selected by (sub >> 1).
|
||||
static const char *const c5x_ctrl_bits[8] = {
|
||||
"intm", "ovm", "cnf", "sxm", "hold", "tc", "xf", "carry"
|
||||
};
|
||||
|
||||
// 16-bit long-immediate ALU forms in the 0xB<sub> group, indexed by the low
|
||||
// nibble; the zero entries are not long-immediate ops.
|
||||
static const ut16 c5x_long_imm_alu[16] = {
|
||||
[0x8] = C2X_INS_LALK, [0x9] = C2X_INS_ADLK, [0xa] = C2X_INS_SBLK, [0xb] = C2X_INS_ANDK, [0xc] = C2X_INS_ORK, [0xd] = C2X_INS_XORK
|
||||
};
|
||||
|
||||
// Append the active zl/cv/tp condition fragments as verbatim operands.
|
||||
static void cond_ops(C55Insn *out, ut16 op) {
|
||||
ut8 zlcvmask = op & 0xf;
|
||||
ut8 zlcv = (op >> 4) & 0xf;
|
||||
ut8 zl = (zlcv & 0xc) | ((zlcvmask >> 2) & 3);
|
||||
ut8 cv = ((zlcv << 2) & 0xc) | (zlcvmask & 3);
|
||||
ut8 tp = (op >> 8) & 3;
|
||||
const char *frag[3] = { c5x_zl[zl], c5x_cv[cv], c5x_tp[tp] };
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (frag[i][0]) {
|
||||
out->ops[out->n_ops].kind = C55_OP_NONE;
|
||||
out->ops[out->n_ops].raw = frag[i];
|
||||
out->n_ops++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ID(_id) (out->id = (ut16)(_id))
|
||||
|
||||
/**
|
||||
* \brief Fill a "mem [narp]" instruction form.
|
||||
* \param out Instruction to fill in
|
||||
* \param id Instruction id to set
|
||||
* \param low Addressing byte (low 8 bits of the opcode word)
|
||||
* \return Instruction length in bytes
|
||||
*/
|
||||
static int mem_plain_insn(C55Insn *out, ut16 id, ut8 low) {
|
||||
out->id = id;
|
||||
mem_op(&out->ops[0], low);
|
||||
out->n_ops = 1;
|
||||
if (narp_op(&out->ops[1], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Fill a "mem shift [narp]" instruction form.
|
||||
* \param out Instruction to fill in
|
||||
* \param id Instruction id to set
|
||||
* \param low Addressing byte (low 8 bits of the opcode word)
|
||||
* \param shift Shift amount encoded in the opcode
|
||||
* \return Instruction length in bytes
|
||||
*/
|
||||
static int mem_shift_insn(C55Insn *out, ut16 id, ut8 low, ut8 shift) {
|
||||
out->id = id;
|
||||
mem_op(&out->ops[0], low);
|
||||
shift_op(&out->ops[1], shift);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Fill a "mem [narp] #imm16" instruction form.
|
||||
* \param out Instruction to fill in
|
||||
* \param id Instruction id to set
|
||||
* \param low Addressing byte (low 8 bits of the opcode word)
|
||||
* \param w2 Trailing word carrying the long immediate
|
||||
* \return Instruction length in bytes
|
||||
*
|
||||
* The next-ARP operand keeps its source order ahead of the immediate, so the
|
||||
* immediate lands at whichever slot the optional \p narp_op left free.
|
||||
*/
|
||||
static int mem_imm2_insn(C55Insn *out, ut16 id, ut8 low, ut16 w2) {
|
||||
out->id = id;
|
||||
mem_op(&out->ops[0], low);
|
||||
out->n_ops = 1;
|
||||
if (narp_op(&out->ops[1], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
imm_op(&out->ops[out->n_ops], w2, 16, false);
|
||||
out->n_ops++;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Fill a single-immediate-operand instruction form.
|
||||
* \param out Instruction to fill in
|
||||
* \param id Instruction id to set
|
||||
* \param v Immediate value, already masked to its encoded width
|
||||
* \param width Immediate width in bits
|
||||
* \return Instruction length in bytes
|
||||
*/
|
||||
static int imm_insn(C55Insn *out, ut16 id, ut64 v, ut8 width) {
|
||||
out->id = id;
|
||||
imm_op(&out->ops[0], v, width, false);
|
||||
out->n_ops = 1;
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// The BE group (0xBExx): ACCB ops, control bits, 2-word immediates.
|
||||
static int decode_be(C55Insn *out, ut16 op, ut16 w2) {
|
||||
ut8 sub = op & 0xff;
|
||||
switch (sub) {
|
||||
case 0x00: ID(C2X_INS_ABS); return 2;
|
||||
case 0x01: ID(C2X_INS_CMPL); return 2;
|
||||
case 0x02: ID(C2X_INS_NEG); return 2;
|
||||
case 0x03: ID(C2X_INS_PAC); return 2;
|
||||
case 0x04: ID(C2X_INS_APAC); return 2;
|
||||
case 0x05: ID(C2X_INS_SPAC); return 2;
|
||||
case 0x09: ID(C2X_INS_SFL); return 2;
|
||||
case 0x0a: ID(C2X_INS_SFR); return 2;
|
||||
case 0x0c: ID(C2X_INS_ROL); return 2;
|
||||
case 0x0d: ID(C2X_INS_ROR); return 2;
|
||||
case 0x10: ID(C5X_INS_ADDB); return 2;
|
||||
case 0x11: ID(C5X_INS_ADCB); return 2;
|
||||
case 0x12: ID(C5X_INS_ANDB); return 2;
|
||||
case 0x13: ID(C5X_INS_ORB); return 2;
|
||||
case 0x14: ID(C5X_INS_ROLB); return 2;
|
||||
case 0x15: ID(C5X_INS_RORB); return 2;
|
||||
case 0x16: ID(C5X_INS_SFLB); return 2;
|
||||
case 0x17: ID(C5X_INS_SFRB); return 2;
|
||||
case 0x18: ID(C5X_INS_SBB); return 2;
|
||||
case 0x19: ID(C5X_INS_SBBB); return 2;
|
||||
case 0x1a: ID(C5X_INS_XORB); return 2;
|
||||
case 0x1b: ID(C5X_INS_CRGT); return 2;
|
||||
case 0x1c: ID(C5X_INS_CRLT); return 2;
|
||||
case 0x1d: ID(C5X_INS_EXAR); return 2;
|
||||
case 0x1e: ID(C5X_INS_SACB); return 2;
|
||||
case 0x1f: ID(C5X_INS_LACB); return 2;
|
||||
case 0x20: ID(C2X_INS_BACC); return 2;
|
||||
case 0x21: ID(C5X_INS_BACCD); return 2;
|
||||
case 0x22: ID(C2X_INS_IDLE); return 2;
|
||||
case 0x23: ID(C5X_INS_IDLE2); return 2;
|
||||
case 0x30: ID(C2X_INS_CALA); return 2;
|
||||
case 0x32: ID(C2X_INS_POP); return 2;
|
||||
case 0x38: ID(C5X_INS_RETI); return 2;
|
||||
case 0x3a: ID(C5X_INS_RETE); return 2;
|
||||
case 0x3c: ID(C2X_INS_PUSH); return 2;
|
||||
case 0x3d: ID(C5X_INS_CALAD); return 2;
|
||||
case 0x51: ID(C2X_INS_TRAP); return 2;
|
||||
case 0x52: ID(C5X_INS_NMI); return 2;
|
||||
case 0x58: ID(C5X_INS_ZPR); return 2;
|
||||
case 0x59: ID(C5X_INS_ZAP); return 2;
|
||||
case 0x5a: ID(C5X_INS_SATH); return 2;
|
||||
case 0x5b: ID(C5X_INS_SATL); return 2;
|
||||
default: break;
|
||||
}
|
||||
if (sub >= 0x40 && sub <= 0x4f) {
|
||||
ID((sub & 1) ? C5X_INS_SETC : C5X_INS_CLRC);
|
||||
out->ops[0].kind = C55_OP_NONE;
|
||||
out->ops[0].raw = c5x_ctrl_bits[(sub >> 1) & 7];
|
||||
out->n_ops = 1;
|
||||
return 2;
|
||||
}
|
||||
if (sub >= 0x60 && sub <= 0x7f) {
|
||||
ID(C5X_INS_INTR);
|
||||
imm_op(&out->ops[0], op & 0x1f, 8, false);
|
||||
out->n_ops = 1;
|
||||
return 2;
|
||||
}
|
||||
switch (sub) {
|
||||
case 0x80:
|
||||
ID(C2X_INS_MPY);
|
||||
imm_op(&out->ops[0], w2, 16, true);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0x81:
|
||||
ID(C2X_INS_AND);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0x82:
|
||||
ID(C2X_INS_OR);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0x83:
|
||||
ID(C2X_INS_XOR);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0xc4:
|
||||
ID(C2X_INS_RPT);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0xc5:
|
||||
ID(C5X_INS_RPTZ);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
case 0xc6:
|
||||
ID(C5X_INS_RPTB);
|
||||
target_op(&out->ops[0], w2);
|
||||
out->n_ops = 1;
|
||||
return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The BF group (0xBFxx): LAR/SPM, CMPR, shifted long-immediate ALU, BSAR.
|
||||
static int decode_bf(C55Insn *out, ut16 op, ut16 w2) {
|
||||
ut8 sub = (op >> 4) & 0xf;
|
||||
ut8 shift = op & 0xf;
|
||||
if (sub == 0x0) {
|
||||
if (op & 0x8) {
|
||||
ID(C2X_INS_LAR);
|
||||
ar_op(&out->ops[0], op & 7);
|
||||
imm_op(&out->ops[1], w2, 16, false);
|
||||
out->n_ops = 2;
|
||||
return 4;
|
||||
}
|
||||
ID(C2X_INS_SPM);
|
||||
imm_op(&out->ops[0], op & 3, 8, false);
|
||||
out->n_ops = 1;
|
||||
return 2;
|
||||
}
|
||||
if (sub == 0x4) {
|
||||
ID(C2X_INS_CMPR);
|
||||
imm_op(&out->ops[0], op & 3, 8, false);
|
||||
out->n_ops = 1;
|
||||
return 2;
|
||||
}
|
||||
if (sub == 0xe) {
|
||||
ID(C5X_INS_BSAR);
|
||||
imm_op(&out->ops[0], shift + 1, 8, false);
|
||||
out->n_ops = 1;
|
||||
return 2;
|
||||
}
|
||||
if (c5x_long_imm_alu[sub]) {
|
||||
ID(c5x_long_imm_alu[sub]);
|
||||
imm_op(&out->ops[0], w2, 16, false);
|
||||
out->n_ops = 1;
|
||||
if (shift) {
|
||||
shift_op(&out->ops[1], shift);
|
||||
out->n_ops = 2;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode one C5x instruction into the shared C55 representation.
|
||||
* \param buf Instruction bytes, big-endian 16-bit words
|
||||
* \param len Number of readable bytes in \p buf
|
||||
* \param out Instruction to fill in
|
||||
* \return Instruction length in bytes (2 or 4), or 0 on an undefined opcode
|
||||
*
|
||||
* Two-word forms consume the following word as their trailing operand, so
|
||||
* \p len must cover both words for those to decode.
|
||||
*/
|
||||
RZ_IPI int c5x_decode(const ut8 *buf, int len, C55Insn *out) {
|
||||
if (!buf || !out || len < 2) {
|
||||
return 0;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->arch = C55_ARCH_C5X;
|
||||
ut16 op = ((ut16)buf[0] << 8) | buf[1];
|
||||
ut16 w2 = (len >= 4) ? (((ut16)buf[2] << 8) | buf[3]) : 0;
|
||||
ut8 base = (op >> 8) & 0xff;
|
||||
ut8 low = op & 0xff;
|
||||
ut8 sh4 = (op >> 8) & 0xf;
|
||||
ut8 sh3 = (op >> 8) & 7;
|
||||
ut8 ar = (op >> 8) & 7;
|
||||
|
||||
if (base <= 0x07) { // lar arN, mem
|
||||
ID(C2X_INS_LAR);
|
||||
ar_op(&out->ops[0], ar);
|
||||
mem_op(&out->ops[1], low);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
switch (base) {
|
||||
case 0x08: return mem_plain_insn(out, C5X_INS_LAMM, low);
|
||||
case 0x09: return mem_imm2_insn(out, C5X_INS_SMMR, low, w2);
|
||||
case 0x0a: return mem_plain_insn(out, C2X_INS_SUBC, low);
|
||||
case 0x0b: return mem_plain_insn(out, C2X_INS_RPT, low);
|
||||
case 0x0c: return mem_imm2_insn(out, C2X_INS_OUT, low, w2);
|
||||
case 0x0d: return mem_plain_insn(out, C2X_INS_LDP, low);
|
||||
case 0x0e:
|
||||
case 0x0f: {
|
||||
ID(C5X_INS_LST);
|
||||
imm_op(&out->ops[0], base & 1, 8, false);
|
||||
mem_op(&out->ops[1], low);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
if (base >= 0x10 && base <= 0x1f) {
|
||||
return mem_shift_insn(out, C5X_INS_LACC, low, sh4);
|
||||
}
|
||||
if (base >= 0x20 && base <= 0x2f) {
|
||||
return mem_shift_insn(out, C2X_INS_ADD, low, sh4);
|
||||
}
|
||||
if (base >= 0x30 && base <= 0x3f) {
|
||||
return mem_shift_insn(out, C2X_INS_SUB, low, sh4);
|
||||
}
|
||||
if (base >= 0x40 && base <= 0x4f) { // bit code, mem
|
||||
ID(C2X_INS_BIT);
|
||||
imm_op(&out->ops[0], sh4, 8, false);
|
||||
mem_op(&out->ops[1], low);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
switch (base) {
|
||||
case 0x50: return mem_plain_insn(out, C2X_INS_MPYA, low);
|
||||
case 0x51: return mem_plain_insn(out, C2X_INS_MPYS, low);
|
||||
case 0x52: return mem_plain_insn(out, C2X_INS_SQRA, low);
|
||||
case 0x53: return mem_plain_insn(out, C2X_INS_SQRS, low);
|
||||
case 0x54: return mem_plain_insn(out, C2X_INS_MPY, low);
|
||||
case 0x55: return mem_plain_insn(out, C2X_INS_MPYU, low);
|
||||
case 0x57: return mem_plain_insn(out, C5X_INS_BLDP, low);
|
||||
case 0x58: return mem_plain_insn(out, C5X_INS_XPL, low);
|
||||
case 0x59: return mem_plain_insn(out, C5X_INS_OPL, low);
|
||||
case 0x5a: return mem_plain_insn(out, C5X_INS_APL, low);
|
||||
case 0x5b: return mem_plain_insn(out, C5X_INS_CPL, low);
|
||||
case 0x5c: return mem_imm2_insn(out, C5X_INS_XPL, low, w2);
|
||||
case 0x5d: return mem_imm2_insn(out, C5X_INS_OPL, low, w2);
|
||||
case 0x5e: return mem_imm2_insn(out, C5X_INS_APL, low, w2);
|
||||
case 0x5f: return mem_imm2_insn(out, C5X_INS_CPL, low, w2);
|
||||
case 0x60: return mem_plain_insn(out, C2X_INS_ADDC, low);
|
||||
case 0x61: return mem_shift_insn(out, C2X_INS_ADD, low, 16);
|
||||
case 0x62: return mem_plain_insn(out, C2X_INS_ADDS, low);
|
||||
case 0x63: return mem_plain_insn(out, C2X_INS_ADDT, low);
|
||||
case 0x64: return mem_plain_insn(out, C2X_INS_SUBB, low);
|
||||
case 0x65: return mem_shift_insn(out, C2X_INS_SUB, low, 16);
|
||||
case 0x66: return mem_plain_insn(out, C2X_INS_SUBS, low);
|
||||
case 0x67: return mem_plain_insn(out, C2X_INS_SUBT, low);
|
||||
case 0x68: return mem_plain_insn(out, C2X_INS_ZALR, low);
|
||||
case 0x69: return mem_plain_insn(out, C5X_INS_LACL, low);
|
||||
case 0x6a: return mem_shift_insn(out, C5X_INS_LACC, low, 16);
|
||||
case 0x6b: return mem_plain_insn(out, C2X_INS_LACT, low);
|
||||
case 0x6c: return mem_plain_insn(out, C2X_INS_XOR, low);
|
||||
case 0x6d: return mem_plain_insn(out, C2X_INS_OR, low);
|
||||
case 0x6e: return mem_plain_insn(out, C2X_INS_AND, low);
|
||||
case 0x6f: return mem_plain_insn(out, C2X_INS_BITT, low);
|
||||
case 0x70: return mem_plain_insn(out, C2X_INS_LTA, low);
|
||||
case 0x71: return mem_plain_insn(out, C2X_INS_LTP, low);
|
||||
case 0x72: return mem_plain_insn(out, C2X_INS_LTD, low);
|
||||
case 0x73: return mem_plain_insn(out, C2X_INS_LT, low);
|
||||
case 0x74: return mem_plain_insn(out, C2X_INS_LTS, low);
|
||||
case 0x75: return mem_plain_insn(out, C2X_INS_LPH, low);
|
||||
case 0x76: return mem_plain_insn(out, C2X_INS_PSHD, low);
|
||||
case 0x77: return mem_plain_insn(out, C2X_INS_DMOV, low);
|
||||
default: break;
|
||||
}
|
||||
if (base == 0x78 || base == 0x7c) {
|
||||
return imm_insn(out, base == 0x78 ? C2X_INS_ADRK : C2X_INS_SBRK, low, 8);
|
||||
}
|
||||
{
|
||||
ut16 brid = 0;
|
||||
switch (base) {
|
||||
case 0x79: brid = C2X_INS_B; break;
|
||||
case 0x7a: brid = C2X_INS_CALL; break;
|
||||
case 0x7b: brid = C2X_INS_BANZ; break;
|
||||
case 0x7d: brid = C5X_INS_BD; break;
|
||||
case 0x7e: brid = C5X_INS_CALLD; break;
|
||||
case 0x7f: brid = C5X_INS_BANZD; break;
|
||||
default: break;
|
||||
}
|
||||
if (brid) {
|
||||
ID(brid);
|
||||
target_op(&out->ops[0], w2);
|
||||
mem_op(&out->ops[1], low | 0x80); // branch addressing is always indirect
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low | 0x80)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->has_branch = true;
|
||||
out->branch_target = w2;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
if (base >= 0x80 && base <= 0x87) { // sar arN, mem
|
||||
ID(C2X_INS_SAR);
|
||||
ar_op(&out->ops[0], ar);
|
||||
mem_op(&out->ops[1], low);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
switch (base) {
|
||||
case 0x88: return mem_plain_insn(out, C5X_INS_SAMM, low);
|
||||
case 0x89: return mem_imm2_insn(out, C5X_INS_LMMR, low, w2);
|
||||
case 0x8a: return mem_plain_insn(out, C2X_INS_POPD, low);
|
||||
case 0x8b:
|
||||
if (low == 0) {
|
||||
ID(C2X_INS_NOP);
|
||||
out->n_ops = 0;
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
return mem_plain_insn(out, C2X_INS_MAR, low);
|
||||
case 0x8c: return mem_plain_insn(out, C2X_INS_SPL, low);
|
||||
case 0x8d: return mem_plain_insn(out, C2X_INS_SPH, low);
|
||||
case 0x8e:
|
||||
case 0x8f: {
|
||||
ID(C5X_INS_SST);
|
||||
imm_op(&out->ops[0], base & 1, 8, false);
|
||||
mem_op(&out->ops[1], low);
|
||||
out->n_ops = 2;
|
||||
if (narp_op(&out->ops[2], low)) {
|
||||
out->n_ops++;
|
||||
}
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
if (base >= 0x90 && base <= 0x97) {
|
||||
return mem_shift_insn(out, C2X_INS_SACL, low, sh3);
|
||||
}
|
||||
if (base >= 0x98 && base <= 0x9f) {
|
||||
return mem_shift_insn(out, C2X_INS_SACH, low, sh3);
|
||||
}
|
||||
switch (base) {
|
||||
case 0xa0: return mem_imm2_insn(out, C2X_INS_NORM, low, w2); // norm mem, #w2 (the trailing word is a count/operand)
|
||||
case 0xa2: return mem_imm2_insn(out, C2X_INS_MAC, low, w2);
|
||||
// MACD is MAC's data-move twin and carries the same trailing pma word; the
|
||||
// BMAR-addressed single-word variants are MADS/MADD below.
|
||||
case 0xa3: return mem_imm2_insn(out, C2X_INS_MACD, low, w2);
|
||||
case 0xa4: return mem_plain_insn(out, C5X_INS_BLPD, low);
|
||||
case 0xa5: return mem_imm2_insn(out, C5X_INS_BLPD, low, w2);
|
||||
case 0xa6: return mem_plain_insn(out, C2X_INS_TBLR, low);
|
||||
case 0xa7: return mem_plain_insn(out, C2X_INS_TBLW, low);
|
||||
case 0xa8:
|
||||
case 0xa9: return mem_imm2_insn(out, C5X_INS_BLDD, low, w2);
|
||||
case 0xaa: return mem_plain_insn(out, C5X_INS_MADS, low);
|
||||
case 0xab: return mem_plain_insn(out, C5X_INS_MADD, low);
|
||||
case 0xac:
|
||||
case 0xad: return mem_plain_insn(out, C5X_INS_BLDD, low);
|
||||
case 0xae: return mem_imm2_insn(out, C5X_INS_SPLK, low, w2);
|
||||
case 0xaf: return mem_imm2_insn(out, C2X_INS_IN, low, w2);
|
||||
default: break;
|
||||
}
|
||||
if (base >= 0xb0 && base <= 0xb7) { // lar arN, #imm8 (C2x LARK; rendered "lar")
|
||||
ID(C2X_INS_LARK);
|
||||
ar_op(&out->ops[0], ar);
|
||||
imm_op(&out->ops[1], low, 8, false);
|
||||
out->n_ops = 2;
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
switch (base) {
|
||||
case 0xb8:
|
||||
return imm_insn(out, C2X_INS_ADDK, low, 8);
|
||||
case 0xb9:
|
||||
return imm_insn(out, C5X_INS_LACL, low, 8);
|
||||
case 0xba:
|
||||
return imm_insn(out, C2X_INS_SUBK, low, 8);
|
||||
case 0xbb:
|
||||
return imm_insn(out, C2X_INS_RPTK, low, 8);
|
||||
case 0xbc:
|
||||
case 0xbd:
|
||||
return imm_insn(out, C2X_INS_LDPK, op & 0x1ff, 16);
|
||||
case 0xbe: {
|
||||
int r = decode_be(out, op, w2);
|
||||
if (r) {
|
||||
out->size = (ut8)r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
case 0xbf: {
|
||||
int r = decode_bf(out, op, w2);
|
||||
if (r) {
|
||||
out->size = (ut8)r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
if (base >= 0xc0 && base <= 0xdf) {
|
||||
return 0; // undefined opcode range
|
||||
}
|
||||
// 0xE0-0xFF: conditional branch / call / return / execute group.
|
||||
if (base >= 0xe0 && base <= 0xe3) {
|
||||
ID(C5X_INS_BCND);
|
||||
target_op(&out->ops[0], w2);
|
||||
out->n_ops = 1;
|
||||
cond_ops(out, op);
|
||||
out->has_branch = true;
|
||||
out->branch_target = w2;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
if ((base >= 0xe4 && base <= 0xe7) || (base >= 0xf4 && base <= 0xf7)) {
|
||||
ID(C5X_INS_XC);
|
||||
imm_op(&out->ops[0], ((op >> 12) & 1) + 1, 8, false);
|
||||
out->n_ops = 1;
|
||||
cond_ops(out, op);
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
if (base >= 0xe8 && base <= 0xeb) {
|
||||
ID(C5X_INS_CC);
|
||||
target_op(&out->ops[0], w2);
|
||||
out->n_ops = 1;
|
||||
cond_ops(out, op);
|
||||
out->has_branch = true;
|
||||
out->branch_target = w2;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
if (base >= 0xec && base <= 0xef) {
|
||||
if (op == 0xef00) {
|
||||
ID(C2X_INS_RET);
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
ID(C5X_INS_RETC);
|
||||
cond_ops(out, op);
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
if (base >= 0xf0 && base <= 0xf3) {
|
||||
ID(C5X_INS_BCNDD);
|
||||
target_op(&out->ops[0], w2);
|
||||
out->n_ops = 1;
|
||||
cond_ops(out, op);
|
||||
out->has_branch = true;
|
||||
out->branch_target = w2;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
if (base >= 0xf8 && base <= 0xfb) {
|
||||
ID(C5X_INS_CCD);
|
||||
target_op(&out->ops[0], w2);
|
||||
out->n_ops = 1;
|
||||
cond_ops(out, op);
|
||||
out->has_branch = true;
|
||||
out->branch_target = w2;
|
||||
out->size = 4;
|
||||
return 4;
|
||||
}
|
||||
if (base >= 0xfc && base <= 0xff) {
|
||||
if (op == 0xff00) {
|
||||
ID(C5X_INS_RETD); // unconditional delayed return
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
ID(C5X_INS_RETCD);
|
||||
cond_ops(out, op);
|
||||
out->size = 2;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -322,6 +322,8 @@ arch_isa_sources = [
|
|||
'isa/tms320/c54x/c54x.c',
|
||||
'isa/tms320/c54x/c54x_il.c',
|
||||
'isa/tms320/c2x/c2x.c',
|
||||
'isa/tms320/c5x/c5x.c',
|
||||
'isa/tms320/c5x/c5x_decode.c',
|
||||
'isa/tms320/c55x_plus/c55plus_arch.c',
|
||||
'isa/tms320/c55x/c55x_analysis.c',
|
||||
'isa/tms320/c55x_plus/c55plus_analysis.c',
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <tms320/c55x_plus/c55plus_analysis.h>
|
||||
#include <tms320/c54x/c54x.h>
|
||||
#include <tms320/c2x/c2x.h>
|
||||
#include <tms320/c5x/c5x.h>
|
||||
#include <tms320/c64x/c64x.h>
|
||||
|
||||
typedef struct tms320_ctx_t {
|
||||
|
|
@ -28,6 +29,8 @@ int tms320_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const
|
|||
return tms320_c54x_op(analysis, op, addr, buf, len, mask);
|
||||
} else if (cpu && rz_str_casecmp(cpu, "c2x") == 0) {
|
||||
return tms320_c2x_op(analysis, op, addr, buf, len, mask);
|
||||
} else if (cpu && rz_str_casecmp(cpu, "c5x") == 0) {
|
||||
return tms320_c5x_op(analysis, op, addr, buf, len, mask);
|
||||
}
|
||||
return tms320_c55x_op_byte(analysis, op, addr, buf, len, mask);
|
||||
}
|
||||
|
|
@ -162,6 +165,72 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) {
|
|||
"gpr arb .16 44 0\n" // Auxiliary register pointer backup
|
||||
"gpr rptc .16 46 0\n"); // Repeat counter
|
||||
}
|
||||
if (cpu0 && rz_str_casecmp(cpu0, "c5x") == 0) {
|
||||
// TMS320C5x: the C2x register file plus the C5x additions — the 32-bit
|
||||
// accumulator buffer ACCB, the TREG1/TREG2 multiplier/shift registers,
|
||||
// the processor-mode status PMST, the index register INDX, the
|
||||
// auxiliary-compare register ARCR, the circular-buffer start/end
|
||||
// pointers CBSR1/CBER1/CBSR2/CBER2, the block-repeat registers
|
||||
// BRCR/PASR/PAER, the block-move address BMAR, the dynamic bit-mask
|
||||
// DBMR and the global-memory register GREG. The core names match the
|
||||
// C2x lifter's il_var bindings (the C5x lifter reuses it).
|
||||
return rz_str_dup(
|
||||
"=PC\tpc\n"
|
||||
"=SP\tsp\n"
|
||||
"=BP\tsp\n"
|
||||
"=A0\tar0\n"
|
||||
"=A1\tar1\n"
|
||||
"=A2\tar2\n"
|
||||
"=A3\tar3\n"
|
||||
"=R0\tacc\n"
|
||||
"ctr acc .32 0 0\n" // Accumulator
|
||||
"gpr accl .16 0 0\n" // Accumulator low word
|
||||
"gpr acch .16 2 0\n" // Accumulator high word
|
||||
"ctr accb .32 4 0\n" // Accumulator buffer
|
||||
"ctr t .16 8 0\n" // Temporary/multiplicand register (TREG0)
|
||||
"ctr treg1 .16 10 0\n" // TREG1 (dynamic shift count)
|
||||
"ctr treg2 .16 12 0\n" // TREG2 (dynamic bit position)
|
||||
"ctr p .32 14 0\n" // Product register
|
||||
"gpr pl .16 14 0\n" // Product low word
|
||||
"gpr ph .16 16 0\n" // Product high word
|
||||
"gpr ar0 .16 18 0\n" // Auxiliary register 0
|
||||
"gpr ar1 .16 20 0\n" // Auxiliary register 1
|
||||
"gpr ar2 .16 22 0\n" // Auxiliary register 2
|
||||
"gpr ar3 .16 24 0\n" // Auxiliary register 3
|
||||
"gpr ar4 .16 26 0\n" // Auxiliary register 4
|
||||
"gpr ar5 .16 28 0\n" // Auxiliary register 5
|
||||
"gpr ar6 .16 30 0\n" // Auxiliary register 6
|
||||
"gpr ar7 .16 32 0\n" // Auxiliary register 7
|
||||
"ctr arp .16 34 0\n" // Auxiliary register pointer
|
||||
"ctr dp .16 36 0\n" // Data page pointer
|
||||
"ctr st0 .16 38 0\n" // Status register 0
|
||||
"ctr st1 .16 40 0\n" // Status register 1
|
||||
"ctr pmst .16 42 0\n" // Processor mode status register
|
||||
"ctr indx .16 44 0\n" // Index register
|
||||
"ctr arcr .16 46 0\n" // Auxiliary register compare register
|
||||
"ctr cbsr1 .16 48 0\n" // Circular buffer 1 start address
|
||||
"ctr cber1 .16 50 0\n" // Circular buffer 1 end address
|
||||
"ctr cbsr2 .16 52 0\n" // Circular buffer 2 start address
|
||||
"ctr cber2 .16 54 0\n" // Circular buffer 2 end address
|
||||
"ctr brcr .16 56 0\n" // Block repeat counter register
|
||||
"ctr pasr .16 58 0\n" // Block repeat program address start
|
||||
"ctr paer .16 60 0\n" // Block repeat program address end
|
||||
"ctr bmar .16 62 0\n" // Block move address register
|
||||
"ctr dbmr .16 64 0\n" // Dynamic bit manipulation register
|
||||
"ctr greg .16 66 0\n" // Global memory allocation register
|
||||
"ctr sp .16 68 0\n" // Stack pointer (synthetic)
|
||||
"ctr pc .16 70 0\n" // Program counter
|
||||
// status/mode bits modelled individually for the shared C2x-core
|
||||
// lifter (they also live inside ST0/ST1 on silicon)
|
||||
"flg c .1 72.0 0\n" // Carry
|
||||
"flg ov .1 73.0 0\n" // Overflow (sticky until tested)
|
||||
"flg tc .1 74.0 0\n" // Test/control bit
|
||||
"gpr ovm .1 75.0 0\n" // Overflow saturation mode
|
||||
"gpr sxm .1 76.0 0\n" // Sign-extension mode
|
||||
"gpr pm .2 77.0 0\n" // Product shift mode
|
||||
"gpr arb .16 78 0\n" // Auxiliary register pointer backup
|
||||
"gpr rptc .16 80 0\n"); // Repeat counter
|
||||
}
|
||||
if (is_c5000(rz_analysis_get_cpu(a))) {
|
||||
p =
|
||||
"=PC pc\n"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <tms320/c55x/c55x_analysis.h>
|
||||
#include <tms320/c54x/c54x.h>
|
||||
#include <tms320/c2x/c2x.h>
|
||||
#include <tms320/c5x/c5x.h>
|
||||
#include <tms320/c64x/c64x.h>
|
||||
|
||||
typedef struct tms_cs_context_t {
|
||||
|
|
@ -31,13 +32,22 @@ static int tms320_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int l
|
|||
desc = &c54x_arch_desc;
|
||||
} else if (a->cpu && !rz_str_casecmp(a->cpu, "c2x")) {
|
||||
desc = &c2x_arch_desc;
|
||||
} else if (a->cpu && !rz_str_casecmp(a->cpu, "c5x")) {
|
||||
desc = &c5x_arch_desc;
|
||||
} else {
|
||||
rz_asm_op_set_asm(op, "unknown asm.cpu");
|
||||
return op->size = -1;
|
||||
}
|
||||
if (desc) {
|
||||
C55Insn insn;
|
||||
if (c55_decode(desc, buf, len, &insn)) {
|
||||
bool ok;
|
||||
if (desc == &c5x_arch_desc) {
|
||||
// The C5x has its own decode front-end (real C5x encoding).
|
||||
ok = c5x_decode(buf, len, &insn) > 0;
|
||||
} else {
|
||||
ok = c55_decode(desc, buf, len, &insn);
|
||||
}
|
||||
if (ok) {
|
||||
char *s = c55_format(desc, &insn);
|
||||
if (s) {
|
||||
rz_asm_op_set_asm(op, s);
|
||||
|
|
@ -80,6 +90,7 @@ static char **tms320_cpu_descriptions() {
|
|||
static char *cpu_desc[] = {
|
||||
"c54x", "Texas Instruments TMS320C54x DSP family",
|
||||
"c2x", "Texas Instruments TMS320C2x legacy fixed-point DSP family",
|
||||
"c5x", "Texas Instruments TMS320C5x fixed-point DSP family (C2x-compatible superset)",
|
||||
"c55x", "Texas Instruments TMS320C55x DSP family",
|
||||
"c55x+", "Texas Instruments TMS320C55x+ DSP family",
|
||||
"c64x", "Texas Instruments TMS320C64x DSP family",
|
||||
|
|
@ -91,8 +102,8 @@ static char **tms320_cpu_descriptions() {
|
|||
RzAsmPlugin rz_asm_plugin_tms320 = {
|
||||
.name = "tms320",
|
||||
.arch = "tms320",
|
||||
.cpus = "c54x,c55x,c55x+,c2x,c64x",
|
||||
.desc = "Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c2x,c64x) disassembler",
|
||||
.cpus = "c54x,c55x,c55x+,c2x,c5x,c64x",
|
||||
.desc = "Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c2x,c5x,c64x) disassembler",
|
||||
.license = "LGPL3",
|
||||
.bits = 16 | 32,
|
||||
.endian = RZ_SYS_ENDIAN_LITTLE | RZ_SYS_ENDIAN_BIG,
|
||||
|
|
|
|||
112
test/db/analysis/tms320.c5x_16
Normal file
112
test/db/analysis/tms320.c5x_16
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
NAME=c5x analysis: register profile (PC alias resolves)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c5x
|
||||
arp~^=PC
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
=PC pc
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c5x analysis: C5x-specific registers present (ACCB / PMST / circular buffer)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c5x
|
||||
arp~accb,pmst,cbsr1,bmar
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
ctr accb .32 4 0
|
||||
ctr pmst .16 42 0
|
||||
ctr cbsr1 .16 48 0
|
||||
ctr bmar .16 62 0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c5x analysis: opcode classification (shared C2x core)
|
||||
FILE=malloc://64
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c5x
|
||||
wx 8b00
|
||||
ao 1 @ 0~^type
|
||||
wx 79800040
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx ef00
|
||||
ao 1 @ 0~^type
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
type: nop
|
||||
type: jmp
|
||||
jump: 0x00000080
|
||||
type: ret
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c5x analysis: register argument detection (ar0/ar1 via calling convention)
|
||||
FILE=malloc://64
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e asm.bits=16
|
||||
e analysis.cpu=c5x
|
||||
wx 801081111010ef00
|
||||
af
|
||||
afc c5x
|
||||
afc
|
||||
afvl
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
c5x
|
||||
arg int16_t arg1 @ ar0
|
||||
arg int16_t arg2 @ ar1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c5x analysis: op fields filled (direct data pointer, access direction, register source)
|
||||
FILE=malloc://64
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e asm.bits=16
|
||||
e analysis.cpu=c5x
|
||||
wx 1010
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^ptr
|
||||
wx 9010
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^ptr
|
||||
wx 8010
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
direction: read
|
||||
ptr: 0x00000010
|
||||
type: store
|
||||
direction: write
|
||||
ptr: 0x00000010
|
||||
type: store
|
||||
reg: ar0
|
||||
direction: write
|
||||
EOF
|
||||
RUN
|
||||
# Same exerciser built for the C5x; "dbl" lands at word 0x4d.
|
||||
|
||||
NAME=c5x analysis: emulateme function recovery
|
||||
FILE=bins/tms320/c5x_legacy/emulateme.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c5x -e analysis.cpu=c5x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afl~[0,2]
|
||||
axt @ 0x9a
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x00000000 154
|
||||
0x0000009a 8
|
||||
fcn.00000000 0x7e [CALL] call 0x4d, *
|
||||
EOF
|
||||
RUN
|
||||
154
test/db/analysis/tms320.typezoo_16
Normal file
154
test/db/analysis/tms320.typezoo_16
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# Function recovery on typezoo.bin, compiled from c2x_legacy/src/typezoo.c: 18
|
||||
# functions with distinct signatures, plus the entry stub. Program addresses
|
||||
# count 16-bit words, so a function sits at twice its symbol value (square =
|
||||
# word 0x1a = byte 0x34).
|
||||
|
||||
NAME=c2x analysis: typezoo function boundaries
|
||||
FILE=bins/tms320/c2x_legacy/typezoo_c2x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c2x -e analysis.cpu=c2x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afl~?
|
||||
afl~[0,2]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
19
|
||||
0x00000000 42
|
||||
0x0000002a 6
|
||||
0x00000030 4
|
||||
0x00000034 24
|
||||
0x0000004c 22
|
||||
0x00000062 24
|
||||
0x0000007a 26
|
||||
0x00000094 44
|
||||
0x000000c0 24
|
||||
0x000000d8 4
|
||||
0x000000dc 24
|
||||
0x000000f4 60
|
||||
0x00000130 82
|
||||
0x00000182 64
|
||||
0x000001c2 60
|
||||
0x000001fe 28
|
||||
0x0000021a 26
|
||||
0x00000234 20
|
||||
0x00000248 256
|
||||
EOF
|
||||
RUN
|
||||
|
||||
# The sizes above tile the image with no gaps, matching the compiler layout.
|
||||
|
||||
NAME=c2x analysis: typezoo callers of square, add2 and sum_array
|
||||
FILE=bins/tms320/c2x_legacy/typezoo_c2x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c2x -e analysis.cpu=c2x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
axt @ 0x34~CALL
|
||||
axt @ 0x4c~CALL
|
||||
axt @ 0xf4~CALL
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
fcn.000001fe 0x206 [CALL] call 0x1a
|
||||
fcn.0000021a 0x21e [CALL] call 0x1a
|
||||
fcn.0000021a 0x228 [CALL] call 0x1a
|
||||
fcn.0000021a 0x22e [CALL] call 0x26
|
||||
fcn.00000234 0x242 [CALL] call 0x7a
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c2x analysis: typezoo main call graph
|
||||
FILE=bins/tms320/c2x_legacy/typezoo_c2x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c2x -e analysis.cpu=c2x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afi @ 0x248~call-refs
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
call-refs: 0x0000002a C 0x00000182 C 0x0000028c J 0x00000234 C 0x00000130 C 0x0000021a C 0x000001fe C 0x00000062 C 0x0000007a C 0x00000094 C 0x000000d8 C 0x000000c0 C 0x000001c2 C 0x000000d8 C 0x00000030 C
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c2x analysis: typezoo calling convention
|
||||
FILE=bins/tms320/c2x_legacy/typezoo_c2x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c2x -e analysis.cpu=c2x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afi @ 0x7a~call-convention
|
||||
afi @ 0x34~call-convention
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
call-convention: c2x
|
||||
call-convention: c2x
|
||||
EOF
|
||||
RUN
|
||||
|
||||
# The C5x build of the same source recovers the same layout on its own ISA.
|
||||
|
||||
NAME=c5x analysis: typezoo function boundaries
|
||||
FILE=bins/tms320/c5x_legacy/typezoo_c5x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c5x -e analysis.cpu=c5x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afl~?
|
||||
afl~[0,2]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
19
|
||||
0x00000000 42
|
||||
0x0000002a 6
|
||||
0x00000030 4
|
||||
0x00000034 24
|
||||
0x0000004c 22
|
||||
0x00000062 24
|
||||
0x0000007a 26
|
||||
0x00000094 44
|
||||
0x000000c0 22
|
||||
0x000000d6 4
|
||||
0x000000da 24
|
||||
0x000000f2 60
|
||||
0x0000012e 82
|
||||
0x00000180 64
|
||||
0x000001c0 60
|
||||
0x000001fc 28
|
||||
0x00000218 26
|
||||
0x00000232 20
|
||||
0x00000246 256
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c5x analysis: typezoo callers of square and add2
|
||||
FILE=bins/tms320/c5x_legacy/typezoo_c5x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c5x -e analysis.cpu=c5x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
axt @ 0x34~CALL
|
||||
axt @ 0x4c~CALL
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
fcn.000001fc 0x204 [CALL] call 0x1a, *
|
||||
fcn.00000218 0x21c [CALL] call 0x1a, *
|
||||
fcn.00000218 0x226 [CALL] call 0x1a, *
|
||||
fcn.00000218 0x22c [CALL] call 0x26, *
|
||||
EOF
|
||||
RUN
|
||||
|
||||
# cc-tms320-16.sdb is keyed by arch and bits, not by CPU, so both cores share
|
||||
# its default.cc; the c5x convention is still selectable.
|
||||
|
||||
NAME=c5x analysis: typezoo calling convention
|
||||
FILE=bins/tms320/c5x_legacy/typezoo_c5x.bin
|
||||
ARGS=-a tms320 -b 16 -e asm.cpu=c5x -e analysis.cpu=c5x
|
||||
CMDS=<<EOF
|
||||
aaa
|
||||
afcl
|
||||
afi @ 0x7a~call-convention
|
||||
afc c5x @ 0x96
|
||||
afi @ 0x7a~call-convention
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
c2x
|
||||
c5x
|
||||
reg
|
||||
call-convention: c2x
|
||||
call-convention: c2x
|
||||
EOF
|
||||
RUN
|
||||
126
test/db/asm/tms320_c5x_16
Normal file
126
test/db/asm/tms320_c5x_16
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# TMS320C5x disassembly + RzIL. The C5x is source-compatible with the C2x but
|
||||
# uses a different object encoding, decoded by the dedicated C5x front-end
|
||||
# (c5x_decode). Shared-semantics instructions carry the C2x ids and lift through
|
||||
# the C2x lifter; the C5x-only instructions (ACCB ops, parallel-logic, memory-
|
||||
# mapped register access, conditional execute/call/return, block moves, ...)
|
||||
# carry the C5X_INS_* ids. Vectors are real C5x opcodes (assembled by the C5x
|
||||
# tool-chain) covering direct/indirect/shift/next-ARP forms.
|
||||
d "lacc 0x9, #0x0" 1009
|
||||
d "lacc 0x9, #0x4" 1409
|
||||
d "lacc *+, #0x0" 10a0
|
||||
d "lacc *+, #0x0, ar2" 10aa
|
||||
d "lacc 0x9, #0x10" 6a09
|
||||
d "add 0x9, #0x0" 2009
|
||||
d "add 0x9, #0x4" 2409
|
||||
d "add *-, #0x0" 2090
|
||||
d "add 0x9, #0x10" 6109
|
||||
d "sub 0x9, #0x0" 3009
|
||||
d "sub *0+, #0x0, ar3" 30eb
|
||||
d "sacl 0x10, #0x0" 9010
|
||||
d "sacl *+, #0x1" 91a0
|
||||
d "sach 0x10, #0x1" 9910
|
||||
d "and 0x9" 6e09
|
||||
d "or 0x9" 6d09
|
||||
d "xor 0x9" 6c09
|
||||
d "lacl 0x9" 6909
|
||||
d "lacl #0x42" b942
|
||||
d "lar ar0, 0x9" 0009
|
||||
d "lar ar1, #0x10" b110
|
||||
d "sar ar0, 0x9" 8009
|
||||
d "lt 0x9" 7309
|
||||
d "lta 0x9" 7009
|
||||
d "ltp 0x9" 7109
|
||||
d "ltd 0x9" 7209
|
||||
d "mpy 0x9" 5409
|
||||
d "mpyu 0x9" 5509
|
||||
d "sqra 0x9" 5209
|
||||
d "pac" be03
|
||||
d "apac" be04
|
||||
d "spac" be05
|
||||
d "abs" be00
|
||||
d "neg" be02
|
||||
d "cmpl" be01
|
||||
d "sfl" be09
|
||||
d "sfr" be0a
|
||||
d "rol" be0c
|
||||
d "ror" be0d
|
||||
d "zalr 0x9" 6809
|
||||
d "lph 0x9" 7509
|
||||
d "spl 0x9" 8c09
|
||||
d "sph 0x9" 8d09
|
||||
d "add #0x12" b812
|
||||
d "sub #0x12" ba12
|
||||
d "rpt #0x7" bb07
|
||||
d "ldp #0x4" bc04
|
||||
d "adrk #0x8" 7808
|
||||
d "sbrk #0x8" 7c08
|
||||
d "spm #0x2" bf02
|
||||
d "lacb" be1f
|
||||
d "sacb" be1e
|
||||
d "exar" be1d
|
||||
d "addb" be10
|
||||
d "sbb" be18
|
||||
d "andb" be12
|
||||
d "orb" be13
|
||||
d "xorb" be1a
|
||||
d "crgt" be1b
|
||||
d "crlt" be1c
|
||||
d "zap" be59
|
||||
d "zpr" be58
|
||||
d "setc ovm" be43
|
||||
d "clrc sxm" be46
|
||||
d "setc carry" be4f
|
||||
d "clrc tc" be4a
|
||||
d "samm 0x10" 8810
|
||||
d "lamm 0x10" 0810
|
||||
d "bldd 0x9" ac09
|
||||
d "blpd 0x9" a409
|
||||
d "splk 0x10, #0x1234" ae101234
|
||||
d "in 0x9, #0x5" af090005
|
||||
d "out 0x9, #0x5" 0c090005
|
||||
d "mac 0x9, #0x1234" a2091234
|
||||
d "macd 0x9, #0x1234" a3091234
|
||||
d "ret" ef00
|
||||
d "b 0x40, *" 79800040
|
||||
d "call 0x50, *" 7a800050
|
||||
d "bcnd 0x40, geq" e38c0040
|
||||
d "cc 0x50, neq" eb080050
|
||||
d "retc geq" ef8c
|
||||
d "bd 0x40, *" 7d800040
|
||||
d "calld 0x50, *" 7e800050
|
||||
d "rptz #0x7" bec50007
|
||||
d "rptb 0x40" bec60040
|
||||
d "bsar #0x4" bfe3
|
||||
d "lst #0x0, 0x9" 0e09
|
||||
d "sst #0x1, 0x10" 8f10
|
||||
d "tblr 0x9" a609
|
||||
d "tblw 0x9" a709
|
||||
d "dmov 0x9" 7709
|
||||
d "subc 0x9" 0a09
|
||||
d "bldp 0x9" 5709
|
||||
d "mads 0x9" aa09
|
||||
d "madd 0x9" ab09
|
||||
d "apl 0x9" 5a09
|
||||
d "opl 0x9" 5909
|
||||
d "xpl 0x9" 5809
|
||||
d "cpl 0x9" 5b09
|
||||
d "addc 0x9" 6009
|
||||
d "subb 0x9" 6409
|
||||
d "adds 0x9" 6209
|
||||
d "subs 0x9" 6609
|
||||
d "addt 0x9" 6309
|
||||
d "subt 0x9" 6709
|
||||
d "lact 0x9" 6b09
|
||||
d "bacc" be20
|
||||
d "cala" be30
|
||||
d "push" be3c
|
||||
d "pop" be32
|
||||
d "pshd 0x9" 7609
|
||||
d "popd 0x9" 8a09
|
||||
d "bit #0x9, 0x0" 4900
|
||||
d "bitt 0x9" 6f09
|
||||
d "idle" be22
|
||||
d "idle2" be23
|
||||
d "trap" be51
|
||||
d "rete" be3a
|
||||
d "reti" be38
|
||||
|
|
@ -58,7 +58,7 @@ _dA__ 8 16 snes LGPL3 SuperNES CPU disassembler
|
|||
_dA_I 32 64 sparc BSD Sun SPARC Capstone-based disassembler
|
||||
_dA__ 16 spc700 LGPL3 Sony SPC700 (Nintendo SuperNES sound-chip) disassembler
|
||||
_dA__ 32 64 sysz BSD IBM SystemZ (S/390) Capstone-based disassembler
|
||||
_dA_I 16 32 tms320 LGPL3 Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c2x,c64x) disassembler
|
||||
_dA_I 16 32 tms320 LGPL3 Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c2x,c5x,c64x) disassembler
|
||||
_dA_I 32 tricore BSD Siemens TriCore Capstone-based disassembler (by billow)
|
||||
_dAeI 32 v810 LGPL3 NEC V810 disassembler (by pancake)
|
||||
_dAeI 32 v850 LGPL3 NEC/Renesas V850 disassembler
|
||||
|
|
@ -186,6 +186,7 @@ qpx PowerPC with Quad Processing eXtensions
|
|||
v9 SPARC V9: 64-bit RISC architecture specification
|
||||
c54x Texas Instruments TMS320C54x DSP family
|
||||
c2x Texas Instruments TMS320C2x legacy fixed-point DSP family
|
||||
c5x Texas Instruments TMS320C5x fixed-point DSP family (C2x-compatible superset)
|
||||
c55x Texas Instruments TMS320C55x DSP family
|
||||
c55x+ Texas Instruments TMS320C55x+ DSP family
|
||||
c64x Texas Instruments TMS320C64x DSP family
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -664,6 +664,7 @@ ARGS=-m tms320
|
|||
EXPECT=<<EOF
|
||||
c54x Texas Instruments TMS320C54x DSP family
|
||||
c2x Texas Instruments TMS320C2x legacy fixed-point DSP family
|
||||
c5x Texas Instruments TMS320C5x fixed-point DSP family (C2x-compatible superset)
|
||||
c55x Texas Instruments TMS320C55x DSP family
|
||||
c55x+ Texas Instruments TMS320C55x+ DSP family
|
||||
c64x Texas Instruments TMS320C64x DSP family
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue