diff --git a/librz/arch/isa/tms320/c55_ir.h b/librz/arch/isa/tms320/c55_ir.h index 2d7c05e16d1..1c7563be118 100644 --- a/librz/arch/isa/tms320/c55_ir.h +++ b/librz/arch/isa/tms320/c55_ir.h @@ -25,10 +25,8 @@ extern "C" { * descriptor but reuses the engine, IR and primitives. * * An arch may skip the decode engine and fill \ref C55Insn from its own - * front-end while still using the consumers and primitives -- this is how a - * future C64x RzIL lifter would attach to capstone's existing C64x disassembler - * (C64x has disasm but no IL yet), so the decode-table fields of \ref C55ArchDesc - * are optional. + * front-end while still using the consumers and primitives, so the + * decode-table fields of \ref C55ArchDesc are optional. */ /** Family member (so shared code can special-case where unavoidable). */ @@ -369,8 +367,8 @@ typedef struct { * The arch extension point. Adding C54x = providing one of these; C55x/C55x+ * each provide one but share id/register/mnemonic tables. The decode fields * (\ref table .. \ref insn_len) are optional: an arch may instead fill - * \ref C55Insn from its own front-end (e.g. capstone for a future C64x lifter) - * and still use the consumers and primitives. + * \ref C55Insn from its own front-end and still use the consumers and + * primitives. */ typedef struct c55_arch_desc_t { C55Arch arch; ///< family member diff --git a/librz/arch/isa/tms320/c64x/c64x.c b/librz/arch/isa/tms320/c64x/c64x.c deleted file mode 100644 index 2a38d2a8c49..00000000000 --- a/librz/arch/isa/tms320/c64x/c64x.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-FileCopyrightText: 2017 pancake -// SPDX-License-Identifier: LGPL-3.0-only - -#include "c64x.h" -#ifdef CAPSTONE_TMS320C64X_H - -#define INSOP(n) insn->detail->tms320c64x.operands[n] -#define INSCC insn->detail->tms320c64x.cc - -typedef struct { - cs_mode omode; - int obits; - csh handle; -} TMSContext; - -void *tms320_c64x_new() { - TMSContext *ctx = RZ_NEW0(TMSContext); - if (!ctx) { - return false; - } - ctx->omode = -1; - ctx->handle = 0; - return ctx; -} - -void tms320_c64x_free(void *p) { - if (!p) { - } - TMSContext *ctx = (TMSContext *)p; - if (ctx->handle) { - cs_close(&ctx->handle); - } - free(ctx); -} - -int tms320_c64x_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int len, void *c64x) { - TMSContext *ctx = (TMSContext *)c64x; - - cs_insn *insn; - int n = -1, ret = -1; - if (op) { - memset(op, 0, sizeof(RzAsmOp)); - op->size = 4; - } - if (ctx->omode != 0) { - cs_close(&ctx->handle); - ctx->omode = -1; - } - if (!ctx->handle) { - cs_mode mode = a->big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN; - ret = cs_open(CS_ARCH_TMS320C64X, mode, &ctx->handle); - if (ret) { - goto fin; - } - ctx->omode = 0; - cs_option(ctx->handle, CS_OPT_DETAIL, CS_OPT_OFF); - } - if (!op) { - return 0; - } - n = cs_disasm(ctx->handle, buf, len, a->pc, 1, &insn); - if (n < 1) { - rz_asm_op_set_asm(op, "invalid"); - op->size = 4; - ret = -1; - goto fin; - } else { - ret = 4; - } - if (insn->size < 1) { - goto fin; - } - op->size = insn->size; - rz_asm_op_setf_asm(op, "%s%s%s", insn->mnemonic, insn->op_str[0] ? " " : "", insn->op_str); - char *str = rz_asm_op_get_asm(op); - if (str) { - rz_str_replace_char(str, '%', 0); - rz_str_case(str, false); - } - cs_free(insn, n); -fin: - return ret; -} - -char *tms320_c64x_mnemonics(const RzAsm *a, int id, bool json, void *c64x) { - TMSContext *ctx = (TMSContext *)c64x; - a->cur->disassemble(a, NULL, NULL, -1); - if (id != -1) { - const char *vname = cs_insn_name(ctx->handle, id); - if (json) { - return vname ? rz_str_newf("[\"%s\"]\n", vname) : NULL; - } - return rz_str_dup(vname); - } - RzStrBuf *buf = rz_strbuf_new(""); - if (json) { - rz_strbuf_append(buf, "["); - } - for (int i = 1;; i++) { - const char *op = cs_insn_name(ctx->handle, i); - if (!op) { - break; - } - if (json) { - rz_strbuf_append(buf, "\""); - } - rz_strbuf_append(buf, op); - if (json) { - if (cs_insn_name(ctx->handle, i + 1)) { - rz_strbuf_append(buf, "\","); - } else { - rz_strbuf_append(buf, "\"]\n"); - } - } else { - rz_strbuf_append(buf, "\n"); - } - } - return rz_strbuf_drain(buf); -} - -static RzStructuredData *tms320_c64x_opex(csh handle, cs_insn *insn) { - if (!insn->detail) { - return NULL; - } - - RzStructuredData *root = rz_structured_data_new_map(); - if (!root) { - return NULL; - } - - RzStructuredData *opex = rz_structured_data_map_add_map(root, "opex"); - if (!opex) { - rz_structured_data_free(root); - return NULL; - } - - RzStructuredData *operands = rz_structured_data_map_add_array(opex, "operands"); - cs_tms320c64x *x = &insn->detail->tms320c64x; - for (st32 i = 0; i < x->op_count; i++) { - cs_tms320c64x_op *op = x->operands + i; - RzStructuredData *operand = rz_structured_data_array_add_map(operands); - switch (op->type) { - case TMS320C64X_OP_REG: - rz_structured_data_map_add_string(operand, "type", "reg"); - rz_structured_data_map_add_string(operand, "value", cs_reg_name(handle, op->reg)); - break; - case TMS320C64X_OP_IMM: - rz_structured_data_map_add_string(operand, "type", "imm"); - rz_structured_data_map_add_signed(operand, "value", op->imm); - break; - case TMS320C64X_OP_MEM: - rz_structured_data_map_add_string(operand, "type", "mem"); - if (op->mem.base != SPARC_REG_INVALID) { - rz_structured_data_map_add_string(operand, "base", cs_reg_name(handle, op->mem.base)); - } - rz_structured_data_map_add_signed(operand, "disp", (st64)op->mem.disp); - break; - default: - rz_structured_data_map_add_string(operand, "type", "invalid"); - break; - } - } - - return root; -} - -int tms320_c64x_op(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask, void *c64x) { - TMSContext *ctx = (TMSContext *)c64x; - cs_insn *insn; - int mode = 0, n, ret; - - if (mode != ctx->omode) { - cs_close(&ctx->handle); - ctx->handle = 0; - ctx->omode = mode; - } - if (ctx->handle == 0) { - mode = a->big_endian ? CS_MODE_BIG_ENDIAN : CS_MODE_LITTLE_ENDIAN; - ret = cs_open(CS_ARCH_TMS320C64X, mode, &ctx->handle); - if (ret != CS_ERR_OK) { - return -1; - } - cs_option(ctx->handle, CS_OPT_DETAIL, CS_OPT_ON); - } - // capstone-next - n = cs_disasm(ctx->handle, (const ut8 *)buf, len, addr, 1, &insn); - if (n < 1) { - op->type = RZ_ANALYSIS_OP_TYPE_ILL; - } else { - if (mask & RZ_ANALYSIS_OP_MASK_OPEX) { - op->opex = tms320_c64x_opex(ctx->handle, insn); - } - op->size = insn->size; - op->id = insn->id; - switch (insn->id) { - case TMS320C64X_INS_INVALID: - op->type = RZ_ANALYSIS_OP_TYPE_ILL; - break; - case TMS320C64X_INS_AND: - case TMS320C64X_INS_ANDN: - op->type = RZ_ANALYSIS_OP_TYPE_AND; - break; - case TMS320C64X_INS_NOT: - op->type = RZ_ANALYSIS_OP_TYPE_NOT; - break; - case TMS320C64X_INS_NEG: - op->type = RZ_ANALYSIS_OP_TYPE_NOT; - break; - case TMS320C64X_INS_SWAP2: - case TMS320C64X_INS_SWAP4: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - case TMS320C64X_INS_BNOP: - case TMS320C64X_INS_NOP: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - case TMS320C64X_INS_CMPEQ: - case TMS320C64X_INS_CMPEQ2: - case TMS320C64X_INS_CMPEQ4: - case TMS320C64X_INS_CMPGT: - case TMS320C64X_INS_CMPGT2: - case TMS320C64X_INS_CMPGTU4: - case TMS320C64X_INS_CMPLT: - case TMS320C64X_INS_CMPLTU: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - break; - case TMS320C64X_INS_B: - op->type = RZ_ANALYSIS_OP_TYPE_JMP; - // higher 32bits of the 64bit address is lost, lets clone - op->jump = INSOP(0).imm + (addr & 0xFFFFFFFF00000000); - break; - case TMS320C64X_INS_LDB: - case TMS320C64X_INS_LDBU: - case TMS320C64X_INS_LDDW: - case TMS320C64X_INS_LDH: - case TMS320C64X_INS_LDHU: - case TMS320C64X_INS_LDNDW: - case TMS320C64X_INS_LDNW: - case TMS320C64X_INS_LDW: - case TMS320C64X_INS_LMBD: - op->type = RZ_ANALYSIS_OP_TYPE_LOAD; - break; - case TMS320C64X_INS_STB: - case TMS320C64X_INS_STDW: - case TMS320C64X_INS_STH: - case TMS320C64X_INS_STNDW: - case TMS320C64X_INS_STNW: - case TMS320C64X_INS_STW: - op->type = RZ_ANALYSIS_OP_TYPE_STORE; - break; - case TMS320C64X_INS_OR: - op->type = RZ_ANALYSIS_OP_TYPE_OR; - break; - case TMS320C64X_INS_SSUB: - case TMS320C64X_INS_SUB: - case TMS320C64X_INS_SUB2: - case TMS320C64X_INS_SUB4: - case TMS320C64X_INS_SUBAB: - case TMS320C64X_INS_SUBABS4: - case TMS320C64X_INS_SUBAH: - case TMS320C64X_INS_SUBAW: - case TMS320C64X_INS_SUBC: - case TMS320C64X_INS_SUBU: - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - break; - case TMS320C64X_INS_ADD: - case TMS320C64X_INS_ADD2: - case TMS320C64X_INS_ADD4: - case TMS320C64X_INS_ADDAB: - case TMS320C64X_INS_ADDAD: - case TMS320C64X_INS_ADDAH: - case TMS320C64X_INS_ADDAW: - case TMS320C64X_INS_ADDK: - case TMS320C64X_INS_ADDKPC: - case TMS320C64X_INS_ADDU: - case TMS320C64X_INS_SADD: - case TMS320C64X_INS_SADD2: - case TMS320C64X_INS_SADDU4: - case TMS320C64X_INS_SADDUS2: - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - } - cs_free(insn, n); - } - return op->size; -} - -#endif /* CAPSTONE_TMS320C64X_H */ diff --git a/librz/arch/isa/tms320/c64x/c64x.h b/librz/arch/isa/tms320/c64x/c64x.h deleted file mode 100644 index d073c90bf55..00000000000 --- a/librz/arch/isa/tms320/c64x/c64x.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: 2024 deroad -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef TMS320_C64X_CAPSTONE_H -#define TMS320_C64X_CAPSTONE_H - -#include "asm_private.h" -#include "analysis_private.h" -#include - -#ifdef CAPSTONE_TMS320C64X_H - -void *tms320_c64x_new(); -void tms320_c64x_free(void *p); -int tms320_c64x_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int len, void *c64x); -char *tms320_c64x_mnemonics(const RzAsm *a, int id, bool json, void *c64x); -int tms320_c64x_op(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask, void *c64x); - -#else - -/** - * The supplied capstone (4 or lower) does not have - * commit ae6f2d141118680a04a949b31e9e82e7b70f10b8 - * https://github.com/capstone-engine/capstone/commit/ae6f2d141118680a04a949b31e9e82e7b70f10b8 - */ - -#warning Capstone does not support tms320c64x - -#define tms320_c64x_new() (NULL) -#define tms320_c64x_free(x) -#define tms320_c64x_disassemble(a, op, buf, len, c64x) (-1) -#define tms320_c64x_mnemonics(a, id, json, c64x) (NULL) -#define tms320_c64x_op(a, op, addr, buf, le, mask, c64x) (-1) - -#endif /* CAPSTONE_TMS320C64X_H */ - -#endif /* TMS320_C64X_CAPSTONE_H */ diff --git a/librz/arch/isa/tms320/c6x/README.md b/librz/arch/isa/tms320/c6x/README.md new file mode 100644 index 00000000000..6cba927d59d --- /dev/null +++ b/librz/arch/isa/tms320/c6x/README.md @@ -0,0 +1,136 @@ +# TMS320C6000 (c6x) native engine + +Native disassembler and analysis for the Texas Instruments TMS320C6000 +VLIW DSP line, shared across the `c62x`, `c64x`, `c67x`, `c674x`, and +`c66x` cpus of the `tms320` arch plugin. It replaces the incomplete +Capstone-based C64x path with one code base for the whole family, +mirroring how the `c55`/`c54x` engines are laid out. + +## Design + +The engine is split into a single pure decoder and a set of stateless +consumers, so new output backends (RzIL, pseudo) can be added without +touching instruction decoding: + +- `c6x_decode.c` -- turns a 32-bit word into a `C6xInsn` (unit, operands, + predicate, parallel bit, analysis op-type). No allocation, no Rizin + asm/analysis structures. +- `c6x_format.c` -- renders a `C6xInsn` to TI-style assembly text. +- `c6x_analysis.c` -- fills the control-flow fields of an `RzAnalysisOp` + from a `C6xInsn`. +- `c6x.h` -- the shared `C6xInsn`/operand types and the per-cpu + `C6xArchDesc` feature descriptors. + +Each cpu is a `C6xArchDesc` carrying the register-file width and the +`has_fp` / `has_simd` feature gates. A single opcode table drives every +variant; rows tagged floating-point or SIMD only decode when the +selected cpu provides them, so `mpysp` disassembles on `c67x`/`c674x` +but is `invalid` on `c62x`. + +### Instruction format + +Every C6000 instruction is a fixed 32-bit little-endian word (the plugin +also honours `cfg.bigendian` for big-endian images). The low bits carry +the framing common to all formats: + +- bit 0 `p` -- parallel bit. A run of instructions with `p = 1` + terminated by `p = 0` forms one execute packet; the members run in + parallel. Following TI/objdump, the `||` prefix marks every instruction + after the first in a packet -- i.e. `this` instruction renders `||` when + the **previous** word set its `p` bit. Decoding is stateless, so the + disassembler and analysis plugins carry a one-word look-back (previous + end address and its `p` bit) to reconstruct that; a fetch-packet header + is transparent to the chain so an execute packet may span it. +- bit 1 `s` -- destination side / unit side (0 = A, 1 = B). +- bits 31:29 `creg`, bit 28 `z` -- predication (SPRU733 Table 3-9). + `creg` names the guarding register (`B0..B2`, `A0..A2`); `z` selects + test-nonzero (`[reg]`) vs test-zero (`[!reg]`). + +The functional unit and operation are then recognised from each format's +fixed low-bit signature (`.L` `bits[4:2]==110`, `.S` 6-bit-op +`bits[5:2]==1000`, `.M` `bits[6:2]==00000`, `.D` arithmetic +`bits[6:2]==10000`, `.D` load/store `bits[3:2]==01`, `.D` long-immediate +load/store `bits[3:2]==11`, `.S` branch/ADDK/MVK, no-unit NOP), after +which the operand fields are sliced out and the op field matched in that +unit's table. + + +## Coverage + +CPUs: `c62x`, `c64x`, `c64x+` (`c64xp`), `c67x`, `c674x`, `c66x`. The +register-file view (16 or 32 registers per side) and the control-register +set follow the selected cpu, and floating-point / C64x+ / C66x rows are +gated so an opcode is `invalid` on a cpu that lacks it. + +Decoded (operands verified byte-exact against TI `dis6x` and hand-encoded +SPRU733/SPRUFE8 vectors), across the full instruction set: + +- the `.L`/`.S`/`.D`/`.M` fixed-point ALU, packed-halfword/byte add and + subtract, shifts, saturating add/sub, min/max, compares, `lmbd`, `norm`, + and `abs` -- in register, constant, cross-path, and 40-bit-long forms; +- the `.M` multiply family: the 16x16 `mpy*`/`smpy*` set, 32x32 + `mpyi`/`mpyid`/`mpy32(u/su/us)`, the C64x+ packed and interpolated + multiplies (`mpy2`/`smpy2`, `mpyhi`/`mpyli`, `mpyhir`/`mpylir`, the dot + products), and the Galois `gmpy4`; +- `.D` address arithmetic and the load/store set (`ldb`..`ldw`, `lddw`, + and the non-aligned/doubleword forms) in every addressing mode, + including the `*+B14/B15[ucst15]` long-immediate and B15 stack forms; +- control transfer: PC-relative `b`, `bnop`, `callp`, `addkpc`, + `bdec`/`bpos`, the register and interrupt-return branches, and the + `B .S2 B3` ABI return -- with resolved, packet-relative targets; +- `mvc` control-register moves (named from the `crlo`/`crhi` field), the + `ext`/`extu`/`set`/`clr` field ops, `mvk`/`mvkl`/`mvkh`/`mvd`, and the + `mv`/`neg`/`not`/`zero` assembler idioms on every unit; +- single- and double-precision float: arithmetic (`addsp`..`mpydp`), + conversions (`intsp`/`spint`/`sptrunc`/`intdp`/`spdp`/`dpint`/`dpsp`/`dptrunc`), + compares, `abssp`/`absdp`, `mpyspdp`/`mpysp2dp`, and the `rsqrdp` estimate; +- the C64x+/C66x packed-SIMD and complex/matrix set (packs, unpacks, + shuffles, `bitr`/`bitc4`/`deal`/`shfl`, the complex and quad multiplies), + the software-pipelined loop-buffer ops (`sploop*`/`spmask`/`idle`), and + the compact 16-bit fetch-packet header (`.fphead`) and every compact + slot form. + +Packet parallelism (`||`) is reconstructed from the `p` bit across the +stateless decode calls and matches TI/objdump address-by-address, +including across a fetch-packet header and in mixed compact/32-bit packets. + +### RzIL + +`c6x_il.c` lifts a decoded `C6xInsn` to RzIL, wired through `op->il_op` +with a per-cpu IL config (32-bit `pce1` PC, byte memory, the general +register file and the control registers `mvc` reaches). Every decoded +instruction that has data semantics is lifted: the integer ALU, shifts, +the full multiply set, packed-SIMD lanes, saturating and packed compares, +field ops, the loads/stores and doubleword pairs, and the control +transfers (predication wraps the effect in a branch on the guard +register). Floating point is lifted through the RzIL float ops -- +single/double arithmetic, conversions, the ordered and equality compares, +and the mixed-precision `mpyspdp`/`mpysp2dp`. The 40-bit long `abs`, `sat`, +`norm`, compares and shift use a shared sign-extending pair reader and a +64-bit `clz`. Lifts are validated by `rz-asm -I` on each `d` line in the +c6x asm test databases and by stepping the RzIL VM. + +Not lifted: `dint`/`rint` carry no data effect, and the hardware +reciprocal/reciprocal-root estimates (`rcpsp`/`rsqrsp`/`rcpdp`) and the +Galois `gmpy4`/`xormpy` reduction have no exact functional model. The five +branch delay slots are not modelled (as in Rizin's other delay-slot +lifters): the transfer is emitted directly, while `cont`/`parallel` and +`op->delay` carry what a delay-slot-aware pass would need. + +### Calling convention and mnemonics + +The C6000 EABI (SPRAB89) calling convention is registered for the C6000 +cpus -- arguments in `A4`/`B4`/`A6`/`B6`/`A8`/`B8`/`A10`/`B10`/`A12`/`B12` +and the return value in `A4` -- and made the analysis default whenever a +c6x cpu is selected (a C5000 cpu restores the `c55x` convention), so +`arcc` and the argument analysis resolve against the real register file. +`aoma` lists the instruction mnemonics the decoder knows and `aom` +converts between a mnemonic and its id. + +## References + +- TI **SPRU733** -- *TMS320C67x/C67x+ DSP CPU and Instruction Set* + (primary encoding reference used here). +- TI **SPRU732** (C64x), **SPRUFE8** (C674x unified), **SPRUGH7** (C66x), + **SPRU731** (C62x) -- per-family instruction sets. +- TI **SPRAB89** -- C6000 Embedded Application Binary Interface (ELF). diff --git a/librz/arch/isa/tms320/c6x/c6x.h b/librz/arch/isa/tms320/c6x/c6x.h new file mode 100644 index 00000000000..28a2e83c54b --- /dev/null +++ b/librz/arch/isa/tms320/c6x/c6x.h @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_TMS320_C6X_H +#define RZ_TMS320_C6X_H + +#include +#include // RzAnalysisOp, _RzAnalysisOpType + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * Shared decode/format core for the TMS320C6000 VLIW DSP family (C62x / C64x / + * C64x+ / C67x / C67x+ / C674x / C66x). + * + * Every C6000 generation shares one instruction container: a 32-bit opcode with + * a fixed low-bit frame (bit 0 = parallel, bit 1 = dst side, bits 31:28 = the + * creg/z predicate) and a functional-unit-selected body. The families differ + * only in *which* opcodes decode -- C67x adds the floating-point set on top of + * the C62x fixed-point base, C64x adds the SIMD/packed set, C66x adds more -- + * so a single decode engine driven by one instruction table plus a per-variant + * \ref C6xArchDesc feature gate covers them all, mirroring how the C55 engine + * serves C54x/C55x/C55x+. + * + * The decoder (c6x_decode()) runs once and fills a \ref C6xInsn; two pure + * consumers read it and never re-parse: c6x_format() (asm string) and + * c6x_fill_analysis() (RzAnalysisOp). RzIL uplifting is a planned third + * consumer and attaches the same way. + */ + +/** Family member; gates variant-specific opcodes and register width. */ +typedef enum { + C6X_GEN_C62X = 0, ///< TMS320C62x (fixed-point base, 16+16 registers) + C6X_GEN_C67X, ///< TMS320C67x/C67x+ (adds floating-point) + C6X_GEN_C64X, ///< TMS320C64x/C64x+ (adds SIMD, 32+32 registers) + C6X_GEN_C674X, ///< TMS320C674x (unified C64x+ and C67x+) + C6X_GEN_C66X, ///< TMS320C66x (adds 4x SIMD / complex arithmetic) +} C6xGen; + +/** Functional unit an instruction issues on. */ +typedef enum { + C6X_UNIT_NONE = 0, ///< no unit (NOP, IDLE, loop buffer, emulation) + C6X_UNIT_L, ///< .L1 / .L2 (arithmetic, logical, compares) + C6X_UNIT_S, ///< .S1 / .S2 (shifts, branches, moves, field ops) + C6X_UNIT_M, ///< .M1 / .M2 (multiplies) + C6X_UNIT_D, ///< .D1 / .D2 (loads, stores, address arithmetic) +} C6xUnit; + +/** Data-memory addressing modes for .D loads/stores (Table 3-11). */ +typedef enum { + C6X_AM_NONE = 0, + C6X_AM_NEG_CST, ///< *-R[ucst5] + C6X_AM_POS_CST, ///< *+R[ucst5] + C6X_AM_NEG_REG, ///< *-R[offsetR] + C6X_AM_POS_REG, ///< *+R[offsetR] + C6X_AM_PREDEC_CST, ///< *--R[ucst5] + C6X_AM_PREINC_CST, ///< *++R[ucst5] + C6X_AM_POSTDEC_CST, ///< *R--[ucst5] + C6X_AM_POSTINC_CST, ///< *R++[ucst5] + C6X_AM_PREDEC_REG, ///< *--R[offsetR] + C6X_AM_PREINC_REG, ///< *++R[offsetR] + C6X_AM_POSTDEC_REG, ///< *R--[offsetR] + C6X_AM_POSTINC_REG, ///< *R++[offsetR] + C6X_AM_BASE_LONG, ///< *+B14/B15[ucst15] long-immediate offset +} C6xAddrMode; + +/** Operand kind within a decoded instruction. */ +typedef enum { + C6X_OP_NONE = 0, + C6X_OP_REG, ///< general-purpose register (side + num) + C6X_OP_REGPAIR, ///< register pair reg+1:reg (odd:even), 64-bit + C6X_OP_REGQUAD, ///< register quad reg+3:reg+2:reg+1:reg (C66x 128-bit ops) + C6X_OP_IMM, ///< immediate constant (signed value in \ref C6xOperand::imm) + C6X_OP_CTRLREG, ///< control register named by \ref C6xOperand::name (MVC) + C6X_OP_MEM, ///< *baseR addressing (see \ref C6xAddrMode) + C6X_OP_PCREL, ///< PC-relative branch target (absolute address in imm) +} C6xOpKind; + +/** One decoded operand. */ +typedef struct { + C6xOpKind kind; + ut8 side; ///< register file: 0 = A, 1 = B + ut8 num; ///< register number (low member of a pair) + st64 imm; ///< immediate / displacement / absolute branch target + bool imm_dec; ///< render imm in decimal (bit counts: csta/cstb) + const char *name; ///< control-register name for C6X_OP_CTRLREG + // memory operands (kind == C6X_OP_MEM) + C6xAddrMode mode; ///< addressing mode + ut8 base; ///< base register number + ut8 base_side; ///< base register file (0 = A, 1 = B) + ut8 off_reg; ///< register offset number (register-offset modes) + ut32 off_cst; ///< constant offset (ucst5 / ucst15), unscaled + bool scaled; ///< offset is scaled by the access size (bracket syntax) +} C6xOperand; + +#define C6X_MAX_OPS 4 + +/** A fully decoded C6000 instruction. */ +typedef struct { + ut32 word; ///< raw 32-bit opcode (little-endian host value) + ut32 size; ///< instruction size in bytes (always 4) + const char *mnemonic; ///< base mnemonic ("add", "ldw", "mpysp", ...) + C6xUnit unit; ///< functional unit + ut8 unit_side; ///< 1 = .x1 unit (A datapath), 2 = .x2 unit (B datapath) + bool cross; ///< src2 uses the register-file cross path (x bit) + bool parallel; ///< p-bit: the *following* instruction runs in parallel + bool cont; ///< this instruction continues the previous execute packet (renders "||") + bool is_fp; ///< the operation is floating-point (selects the FPU family) + ut8 creg; ///< predicate register code (bits 31:29); 0 = unconditional + ut8 z; ///< predicate sense (bit 28): 0 = nonzero, 1 = zero + _RzAnalysisOpType op_type; ///< analysis classification + C6xOperand ops[C6X_MAX_OPS]; + ut8 nops; ///< number of valid operands + bool is_header; ///< compact fetch-packet header word (renders as .fphead) +} C6xInsn; + +/** Per-variant feature gate and register-set descriptor. */ +typedef struct { + C6xGen gen; ///< which family member + ut8 num_regs; ///< registers per side (16 for C62x/C67x, 32 otherwise) + bool has_fp; ///< floating-point set present (C67x/C674x/C66x) + bool has_simd; ///< SIMD/packed set present (C64x/C674x/C66x) +} C6xArchDesc; + +/** Ready-made descriptors, one per supported asm.cpu value. */ +extern const C6xArchDesc c6x_desc_c62x; +extern const C6xArchDesc c6x_desc_c64x; +extern const C6xArchDesc c6x_desc_c67x; +extern const C6xArchDesc c6x_desc_c674x; +extern const C6xArchDesc c6x_desc_c66x; + +/** Resolve an asm.cpu string to a descriptor, or NULL if not a C6000 cpu. */ +RZ_IPI const C6xArchDesc *c6x_desc_from_cpu(const char *cpu); + +/** + * Decode one 32-bit C6000 opcode. + * \param desc variant descriptor (feature gate) + * \param buf input bytes (>= 4) + * \param len available bytes + * \param big_endian byte order of \p buf + * \param insn output, filled on success + * \return true if a 4-byte opcode was consumed (even when unrecognised, in + * which case \ref C6xInsn::mnemonic is NULL) + */ +RZ_IPI bool c6x_decode(const C6xArchDesc *desc, const ut8 *buf, int len, ut64 pc, bool big_endian, RZ_OUT C6xInsn *insn); + +/** + * \brief Flag whether \p insn continues the previous execute packet. + * + * TI marks an instruction with "||" when it runs in parallel with the one + * before it, i.e. when the *previous* word set its parallel bit. Decoding is + * stateless, so \p prev_end (address just past the last instruction) and + * \p prev_par (its parallel bit) carry that one-word look-back across calls; + * both are updated for the next call. + */ +RZ_IPI void c6x_mark_parallel(RZ_INOUT C6xInsn *insn, ut64 addr, RZ_INOUT ut64 *prev_end, RZ_INOUT bool *prev_par); + +/** Render a decoded instruction to a newly allocated asm string. \p pc is the + * instruction address, used to resolve PC-relative branch targets. */ +RZ_IPI RZ_OWN char *c6x_format(const C6xArchDesc *desc, const C6xInsn *insn, ut64 pc); + +/** Fill an RzAnalysisOp from a decoded instruction (type, jump/fail, ...). */ +RZ_IPI void c6x_fill_analysis(const C6xArchDesc *desc, const C6xInsn *insn, ut64 addr, RZ_OUT RzAnalysisOp *op); + +/** RzIL configuration (register bindings, PC/memory widths) for the C6000 VM. */ +RZ_IPI RzAnalysisILConfig *tms320_c6x_il_config(RZ_NONNULL RzAnalysis *analysis); + +/** Lift a decoded instruction to RzIL, or NULL when the form is not yet lifted. + * \p pc is the instruction address (for PC-relative results). */ +RZ_IPI RZ_OWN RzILOpEffect *c6x_lift(const C6xInsn *insn, ut64 pc); + +/** Every mnemonic the decoder can print, sorted and de-duplicated, for the + * `rz-asm -e` listing. Elements are borrowed static strings; free the vector + * only. */ +RZ_IPI RZ_OWN RzPVector /**/ *c6x_mnemonics(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/librz/arch/isa/tms320/c6x/c6x_analysis.c b/librz/arch/isa/tms320/c6x/c6x_analysis.c new file mode 100644 index 00000000000..6b07a6e2d95 --- /dev/null +++ b/librz/arch/isa/tms320/c6x/c6x_analysis.c @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "c6x.h" + +// Pure consumer of a decoded C6xInsn: fills the RzAnalysisOp fields that come +// from the instruction alone (type, size and control-flow targets). A predicated +// instruction (creg != 0) is conditional, which turns a branch into a CJMP. + +RZ_IPI void c6x_fill_analysis(const C6xArchDesc *desc, const C6xInsn *insn, ut64 addr, RZ_OUT RzAnalysisOp *op) { + rz_return_if_fail(desc && insn && op); + op->size = insn->size; // 4 for a normal word, 2 for a compact instruction + op->type = insn->op_type; + op->family = insn->is_fp ? RZ_ANALYSIS_OP_FAMILY_FPU : RZ_ANALYSIS_OP_FAMILY_CPU; + // An instruction is predicated only when it names a predicate register + // (creg != 0). z is the test sense and is meaningful only alongside creg; + // creg == 0 with z == 1 is an opcode bit (CALLP, the C66x complex ops), + // not a predicate. + bool conditional = insn->creg != 0; + + // Expose a constant operand as the op value, and treat an ADD/SUB that + // writes the stack pointer (B15 in the C6000 EABI) as a frame adjustment. + for (ut8 i = 0; i < insn->nops; i++) { + if (insn->ops[i].kind == C6X_OP_IMM) { + op->val = insn->ops[i].imm; + break; + } + } + if ((insn->op_type == RZ_ANALYSIS_OP_TYPE_ADD || insn->op_type == RZ_ANALYSIS_OP_TYPE_SUB) && insn->nops >= 2) { + const C6xOperand *dst = &insn->ops[insn->nops - 1]; + bool sp_dst = dst->kind == C6X_OP_REG && dst->side == 1 && dst->num == 15; + bool sp_src = insn->nops == 2; // 2-op ADDK/SUBK operate on dst in place + st64 k = 0; + bool has_imm = false; + for (ut8 i = 0; i + 1 < insn->nops; i++) { + if (insn->ops[i].kind == C6X_OP_IMM) { + k = insn->ops[i].imm; + has_imm = true; + } else if (insn->ops[i].kind == C6X_OP_REG && insn->ops[i].side == 1 && insn->ops[i].num == 15) { + sp_src = true; + } + } + if (sp_dst && sp_src && has_imm) { + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = insn->op_type == RZ_ANALYSIS_OP_TYPE_SUB ? -k : k; + } + } + + switch (insn->op_type) { + case RZ_ANALYSIS_OP_TYPE_JMP: + // B displacement is relative to the fetch-packet base (PCE1), i.e. the + // address masked to the 32-byte packet, not to the branch instruction. + if (insn->nops > 0 && insn->ops[0].kind == C6X_OP_PCREL) { + op->jump = (addr & ~(ut64)0x1f) + insn->ops[0].imm; + } + if (conditional) { + op->type = RZ_ANALYSIS_OP_TYPE_CJMP; + op->fail = addr + insn->size; + } + // A branch has five delay slots; the transfer is not immediate, but the + // jump target is what matters for the CFG. + op->delay = 5; + break; + case RZ_ANALYSIS_OP_TYPE_RJMP: + // B to a register (e.g. B B3 return); target is data-dependent. B3 is the + // return-address register in the C6x ABI, so an unconditional B .S2 B3 is + // a function return. + if (insn->nops > 0 && insn->ops[0].kind == C6X_OP_REG && + insn->ops[0].side == 1 && insn->ops[0].num == 3) { + op->type = conditional ? RZ_ANALYSIS_OP_TYPE_CRET : RZ_ANALYSIS_OP_TYPE_RET; + } else if (conditional) { + op->type = RZ_ANALYSIS_OP_TYPE_RCJMP; + op->fail = addr + insn->size; + } + op->delay = 5; + break; + case RZ_ANALYSIS_OP_TYPE_CJMP: + // BDEC/BPOS resolve a PC-relative target (PCE1 + scst10*4) like B, but + // the branch is taken on a register test, so it is always two-way -- the + // fall-through is the next execute packet. + if (insn->nops > 0 && insn->ops[0].kind == C6X_OP_PCREL) { + op->jump = (addr & ~(ut64)0x1f) + insn->ops[0].imm; + op->fail = addr + insn->size; + } + op->delay = 5; + break; + default: + if (conditional && insn->op_type != RZ_ANALYSIS_OP_TYPE_NOP) { + op->cond = RZ_TYPE_COND_AL; // predicated, but not a branch + } + break; + } +} diff --git a/librz/arch/isa/tms320/c6x/c6x_decode.c b/librz/arch/isa/tms320/c6x/c6x_decode.c new file mode 100644 index 00000000000..4dd649f1258 --- /dev/null +++ b/librz/arch/isa/tms320/c6x/c6x_decode.c @@ -0,0 +1,1940 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "c6x.h" + +// The C6000 opcode is a fixed 32-bit word. Bit 0 is the parallel bit, bit 1 the +// destination side, bits 31:28 the creg/z predicate; the remaining bits select a +// functional unit and operation through the formats of SPRU733 Appendix C-G. +// Each format is recognised from its fixed low-bit signature, its operand fields +// are sliced out, and the op field is matched in that unit's table below. + +#define BITS(w, hi, lo) (((w) >> (lo)) & ((1u << ((hi) - (lo) + 1)) - 1)) +#define BIT(w, n) (((w) >> (n)) & 1u) + +// src1 (bits 17:13) is either a register or a short constant, fixed per opcode. +typedef enum { + SRC1_REG = 0, ///< src1 is a register + SRC1_SCST5, ///< src1 is a signed 5-bit constant + SRC1_UCST5, ///< src1 is an unsigned 5-bit constant + SRC1_NONE, ///< no src1 (unary) +} C6xSrc1Kind; + +// A row only decodes on a variant that has the required instruction set. +typedef enum { + FEAT_BASE = 0, ///< C62x base, present on every generation + FEAT_FP, ///< floating-point (C67x and up) + FEAT_SIMD, ///< SIMD/packed (C64x and up) + FEAT_CPLX, ///< complex/matrix multiply (C674x and C66x: has_fp && has_simd) + FEAT_C66X, ///< C66x-only additions (4-way dot products, double complex) +} C6xFeat; + +// Some floating-point ops mix single registers and 64-bit pairs in ways that +// `pair` alone cannot express, so a row may override the src2/dst shape. +typedef enum { + OSHAPE_UNIFORM = 0, ///< every register operand follows `pair` + OSHAPE_CMP_DP, ///< src1/src2 are pairs, dst is a single sint (DP compares) + OSHAPE_CMP_LONG, ///< src2 is a pair (40-bit long), dst and src1 are singles + OSHAPE_TO_DP, ///< src2 single widens into a pair dst; src1 (if any) single + OSHAPE_LONG_DST, ///< src1 single, src2 a pair, dst a pair (long ADD) +} C6xOShape; + +typedef struct { + ut16 op; ///< opfield value within the format + const char *mnem; ///< mnemonic + ut8 src1; ///< C6xSrc1Kind + ut8 feat; ///< C6xFeat gate + _RzAnalysisOpType type; ///< analysis classification + bool pair; ///< operands are 64-bit register pairs (double precision) + ut8 oshape; ///< C6xOShape override for mixed single/pair operands + bool src1_cross; ///< the cross-path (x) bit selects src1's side, not src2's + bool no_operand_swap; ///< keep the src1, src2 print order for shift-typed ops + ///< that are not reversed-syntax (SHLMB/SHRMB) +} C6xRow; + +// .L unit, 7-bit opfield at bits 11:5 (SPRU733 Table 3-12 and per-instruction +// opcodes). Fixed-point base plus the C67x floating-point set. +static const C6xRow c6x_l_rows[] = { + { 0x03, "add", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x23, "add", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD, false, OSHAPE_TO_DP }, + { 0x21, "add", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD, false, OSHAPE_LONG_DST, true }, + { 0x02, "add", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x20, "add", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x2b, "addu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x29, "addu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x07, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x17, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB, false, OSHAPE_UNIFORM, true }, + { 0x27, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB, false, OSHAPE_TO_DP }, + { 0x37, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB, false, OSHAPE_TO_DP, true }, + { 0x06, "sub", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x24, "sub", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x4b, "subc", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x2f, "subu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x0f, "ssub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x0e, "ssub", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x13, "sadd", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x12, "sadd", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x7f, "or", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x7e, "or", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x7b, "and", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x7a, "and", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x6f, "xor", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_XOR }, + { 0x6e, "xor", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_XOR }, + { 0x7c, "andn", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x53, "cmpeq", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x52, "cmpeq", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x51, "cmpeq", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x50, "cmpeq", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x47, "cmpgt", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x46, "cmpgt", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x45, "cmpgt", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x44, "cmpgt", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x4f, "cmpgtu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x4e, "cmpgtu", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x4d, "cmpgtu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x4c, "cmpgtu", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x57, "cmplt", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x56, "cmplt", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x55, "cmplt", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x54, "cmplt", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x5f, "cmpltu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x5e, "cmpltu", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x5d, "cmpltu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x5c, "cmpltu", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_CMP, false, OSHAPE_CMP_LONG }, + { 0x6b, "lmbd", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x6a, "lmbd", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x63, "norm", SRC1_NONE, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x60, "norm", SRC1_NONE, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_CMP_LONG }, + { 0x61, "shlmb", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_SHL, false, OSHAPE_UNIFORM, false, true }, + { 0x62, "shrmb", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_SHR, false, OSHAPE_UNIFORM, false, true }, + { 0x1a, "abs", SRC1_NONE, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ABS }, + { 0x38, "abs", SRC1_NONE, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ABS, true }, + { 0x40, "sat", SRC1_NONE, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_CMP_LONG }, + // C67x floating-point on .L. Single precision uses scalar registers; + // double precision uses 64-bit register pairs. + { 0x10, "addsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x11, "subsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x18, "adddp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD, true }, + { 0x19, "subdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, true }, + // src1-cross forms of the non-commutative FP subtracts (x selects src1). + { 0x15, "subsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, false, OSHAPE_UNIFORM, true }, + { 0x1d, "subdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, true, OSHAPE_UNIFORM, true }, + // int/float conversions and truncation (unary src2 -> dst). + { 0x4a, "intsp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x0a, "spint", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x0b, "sptrunc", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x39, "intdp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_TO_DP }, + // double-precision -> integer / single conversions (unary DP pair src2 -> + // single dst; op fields 0x08/0x09/0x01 per SPRUFE8, encoded but not + // disassembled by dis6x). CMP_LONG shapes the pair src2 and single dst. + { 0x08, "dpint", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_CMP_LONG }, + { 0x09, "dpsp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_CMP_LONG }, + { 0x01, "dptrunc", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_CMP_LONG }, + // C64x+ SIMD pack/interleave: pack halfwords or bytes from two source + // registers into one; single registers, src1/src2/dst in the usual order. + { 0x00, "pack2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x1e, "packh2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x1b, "packlh2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x1c, "packhl2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x68, "packl4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x69, "packh4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + // C64x+ packed add/sub (halfword pairs, byte quads) and lane min/max. The + // saturating ssub2 and the *4 byte forms are single registers throughout. + { 0x04, "sub2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x05, "add2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x41, "min2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x42, "max2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x43, "maxu4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x48, "minu4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x64, "ssub2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x65, "add4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x66, "sub4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_SUB }, + // C66x SIMD double add: scst5 src1 with register-pair src2 and dst. + { 0x22, "dadd", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD, true }, +}; + +// Floating-point add/sub also issue on the .S unit. They share the .L +// 1-or-2-source format (bits 4:2 == 110, 7-bit opfield) but their opfields +// (0x70..0x77) name the .S unit. SUBSP/SUBDP have a second, cross-path +// (xsint) opfield like their .L counterparts; the DP forms are register pairs. +static const C6xRow c6x_s_fp_rows[] = { + { 0x70, "addsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x71, "subsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x75, "subsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, false, OSHAPE_UNIFORM, true }, + { 0x72, "adddp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD, true }, + { 0x73, "subdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, true }, + { 0x77, "subdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_SUB, true, OSHAPE_UNIFORM, true }, +}; + +// .D-unit extended logic/arith (C64x+): bits 5:2 == 1100, bits 11:10 == 10, +// 4-bit opfield at bits 9:6. Operand layout matches the standard 3-op form +// (src1, src2, dst; the x bit crosses src2). AND/OR/XOR each have a register +// (uint) and a scst5 form; ADD carries a 5-bit signed constant. OR with a zero +// constant is the MV idiom, but the underlying opcode is what we render. +static const C6xRow c6x_d_ext_rows[] = { + { 0x2, "or", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x3, "or", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x6, "and", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x7, "and", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0xb, "add", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0xe, "xor", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_XOR }, + { 0xf, "xor", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_XOR }, +}; + +// .S unit, 6-bit opfield at bits 11:6, signature bits 5:2 == 1000. +static const C6xRow c6x_s_rows[] = { + { 0x07, "add", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x06, "add", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x17, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x16, "sub", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x01, "add2", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x11, "sub2", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + // Shifts (S3/S3i, SPRUFE8 SHL/SHR/SHRU/SSHL opcode maps). op bit 0 selects + // the register (1) vs ucst5 (0) src1 form. The slong (40-bit, register-pair) + // variants are omitted; they need src2/dst-only pairing that this shared + // path cannot yet express. + { 0x33, "shl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x32, "shl", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x31, "shl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL, true }, + { 0x30, "shl", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL, true }, + { 0x13, "shl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x12, "shl", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x37, "shr", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SAR }, + { 0x36, "shr", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SAR }, + { 0x27, "shru", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHR }, + { 0x26, "shru", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHR }, + { 0x23, "sshl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x22, "sshl", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SHL }, + { 0x1f, "and", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x1e, "and", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x0b, "xor", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_XOR }, + { 0x0a, "xor", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_XOR }, + { 0x1b, "or", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x1a, "or", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x36, "andn", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + // single-precision float compares: src1, src2, dst all single + { 0x38, "cmpeqsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x39, "cmpgtsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x3a, "cmpltsp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP }, + // double-precision float compares: src1/src2 pairs, dst single sint + { 0x28, "cmpeqdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP, true, OSHAPE_CMP_DP }, + { 0x29, "cmpgtdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP, true, OSHAPE_CMP_DP }, + { 0x2a, "cmpltdp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_CMP, true, OSHAPE_CMP_DP }, + // abs (unary, src2 -> dst): single for SP, pair for DP + { 0x3c, "abssp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ABS }, + { 0x2c, "absdp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ABS, true }, + // reciprocal square-root, DP (unary, pair) + { 0x2e, "rsqrdp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, true }, + // SP -> DP widening convert (unary, single src2 -> pair dst) + { 0x02, "spdp", SRC1_NONE, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MOV, false, OSHAPE_TO_DP }, + // C64x+ packed byte/halfword reshuffles and packed compares (normal .S + // format). The compares set one result bit per lane in the dst. + { 0x08, "packhl2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x10, "packlh2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MOV }, + { 0x14, "cmpgt2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x15, "cmpgtu4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x1c, "cmpeq4", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_CMP }, + { 0x1d, "cmpeq2", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_CMP }, +}; + +// .M unit, 5-bit opfield at bits 11:7, signature bits 6:2 == 00000. +static const C6xRow c6x_m_rows[] = { + { 0x19, "mpy", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1f, "mpyu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1d, "mpyus", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1b, "mpysu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1e, "mpysu", SRC1_SCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x01, "mpyh", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x07, "mpyhu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x05, "mpyhus", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x03, "mpyhsu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x11, "mpylh", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x17, "mpylhu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x15, "mpyluhs", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x13, "mpylshu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x09, "mpyhl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x0f, "mpyhlu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x0d, "mpyhuls", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x0b, "mpyhslu", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1a, "smpy", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x02, "smpyh", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x0a, "smpyhl", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x12, "smpylh", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x1c, "mpysp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x0e, "mpydp", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MUL, true }, + { 0x04, "mpyi", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MUL }, + // MPYID writes the full 64-bit product of the same 32x32 into a pair. + { 0x08, "mpyid", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_MUL, false, OSHAPE_TO_DP }, + // MPY32 (C64x+): 32x32 multiply. The op-0x10 slot keeps the low 32 bits in + // a single dst; op-0x14/0x16 write the full 64-bit product to a pair. + { 0x10, "mpy32", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0x14, "mpy32", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MUL, false, OSHAPE_TO_DP }, + { 0x16, "mpy32su", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_MUL, false, OSHAPE_TO_DP }, +}; + +// Extended .M (C64x+): bits 5:2 == 1100 with bits 11:10 in {0,1} select the .M +// unit, and the (bits 11:10, bits 9:6) pair names the operation. The wide +// multiplies write a register pair; mpyspdp additionally reads a pair src2. +typedef struct { + ut8 sub; ///< bits 11:10 + ut8 op; ///< bits 9:6 + const char *mnem; + ut8 feat; + bool dst_pair; ///< dst is a 64-bit register pair + bool src2_pair; ///< src2 is a 64-bit register pair + _RzAnalysisOpType type; + bool src1_cross; ///< the cross-path (x) bit selects src1's side (mpyspdp) + bool swap; ///< reversed syntax src2, src1, dst (sshvl/sshvr/rotl) +} C6xMExtRow; + +static const C6xMExtRow c6x_m_ext_rows[] = { + { 0, 0, "mpy2", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 1, "smpy2", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 2, "dotpsu4", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 4, "mpyu4", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 5, "mpysu4", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 6, "dotpu4", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 7, "dotpnrsu2", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 9, "dotpn2", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 12, "dotp2", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 13, "dotprsu2", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 0, 14, "mpylir", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 0, "mpyhir", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 1, "gmpy4", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 4, "mpyhi", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 5, "mpyli", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 6, "mpyspdp", FEAT_FP, true, true, RZ_ANALYSIS_OP_TYPE_MUL, true }, + { 1, 7, "mpysp2dp", FEAT_FP, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 8, "mpy32u", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 9, "mpy32us", FEAT_SIMD, true, false, RZ_ANALYSIS_OP_TYPE_MUL }, + { 1, 2, "avgu4", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_ADD }, + { 1, 3, "avg2", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_ADD }, + { 1, 10, "sshvr", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_SHR, false, true }, + { 1, 12, "sshvl", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_SHL, false, true }, + { 1, 13, "rotl", FEAT_SIMD, false, false, RZ_ANALYSIS_OP_TYPE_ROL }, +}; + +// The bits 11:10 == 0, bits 9:6 == 3 slot is a family of unary moves and +// bit/byte shuffles (src2 -> dst) selected by the src1 field. +typedef struct { + ut8 src1; + const char *mnem; +} C6xMUnaryRow; + +static const C6xMUnaryRow c6x_m_unary_rows[] = { + { 24, "xpnd4" }, { 25, "xpnd2" }, { 26, "mvd" }, { 28, "shfl" }, + { 29, "deal" }, { 30, "bitc4" }, { 31, "bitr" } +}; + +// C66x complex/matrix multiplies and 4-way dot products live in the extended +// .M space too, but are always unconditional and mark it with z = 1, creg = 0 +// -- that pattern tells them apart from the C64x+ op in the same slot. The +// complex multiplies and Galois ops reach C674x; the double/4H forms are C66x. +typedef struct { + ut8 sub; ///< bits 11:10 + ut8 op; ///< bits 9:6 + const char *mnem; + ut8 feat; + ut8 k1; ///< src1 operand kind (C6X_OP_REG / _REGPAIR / _REGQUAD) + ut8 k2; ///< src2 operand kind + ut8 k3; ///< dst operand kind +} C6xMCplxRow; + +static const C6xMCplxRow c6x_m_cplx_rows[] = { + { 0, 5, "dotpsu4h", FEAT_C66X, C6X_OP_REGPAIR, C6X_OP_REGPAIR, C6X_OP_REG }, + { 0, 6, "dotp4h", FEAT_C66X, C6X_OP_REGPAIR, C6X_OP_REGPAIR, C6X_OP_REG }, + { 0, 10, "cmpy", FEAT_CPLX, C6X_OP_REG, C6X_OP_REG, C6X_OP_REGPAIR }, + { 0, 11, "cmpyr", FEAT_CPLX, C6X_OP_REG, C6X_OP_REG, C6X_OP_REG }, + { 0, 12, "cmpyr1", FEAT_CPLX, C6X_OP_REG, C6X_OP_REG, C6X_OP_REG }, + { 0, 13, "dcmpyr1", FEAT_C66X, C6X_OP_REGPAIR, C6X_OP_REGPAIR, C6X_OP_REGPAIR }, + { 0, 14, "dccmpyr1", FEAT_C66X, C6X_OP_REGPAIR, C6X_OP_REGPAIR, C6X_OP_REGPAIR }, + { 1, 6, "ddotpl2", FEAT_CPLX, C6X_OP_REGPAIR, C6X_OP_REG, C6X_OP_REGPAIR }, + { 1, 7, "ddotph2", FEAT_CPLX, C6X_OP_REGPAIR, C6X_OP_REG, C6X_OP_REGPAIR }, + { 1, 10, "qsmpy32r1", FEAT_C66X, C6X_OP_REGQUAD, C6X_OP_REGQUAD, C6X_OP_REGQUAD }, + { 1, 11, "xormpy", FEAT_CPLX, C6X_OP_REG, C6X_OP_REG, C6X_OP_REG }, + { 1, 15, "gmpy", FEAT_CPLX, C6X_OP_REG, C6X_OP_REG, C6X_OP_REG } +}; + +// C66x quad/matrix multiplies sit in the normal .M format but, like the complex +// ops, mark themselves unconditional with z = 1, creg = 0. They take 128-bit +// register quads; cmatmpy reads its pair src1 and quad src2 from swapped fields. +typedef struct { + ut8 op; ///< opcode bits 11:7 + const char *mnem; + ut8 k1; ///< src1 operand kind (C6X_OP_REG / _REGPAIR / _REGQUAD) + ut8 k2; ///< src2 operand kind + ut8 k3; ///< dst operand kind + bool swap; ///< src1 is in the src2 field and vice versa +} C6xMWideRow; + +static const C6xMWideRow c6x_m_wide_rows[] = { + { 0x04, "cmatmpy", C6X_OP_REGPAIR, C6X_OP_REGQUAD, C6X_OP_REGQUAD, true }, + { 0x06, "cmatmpyr1", C6X_OP_REGPAIR, C6X_OP_REGQUAD, C6X_OP_REGPAIR, true }, + { 0x10, "qmpy32", C6X_OP_REGQUAD, C6X_OP_REGQUAD, C6X_OP_REGQUAD, false }, + { 0x1c, "dmpysp", C6X_OP_REGPAIR, C6X_OP_REGPAIR, C6X_OP_REGPAIR, false }, + { 0x1d, "qmpysp", C6X_OP_REGQUAD, C6X_OP_REGQUAD, C6X_OP_REGQUAD, false } +}; + +// Extended .S (C64x+): bits 5:2 == 1100, bits 11:10 == 11, op at bits 9:6. +// Packed shifts, saturating add/pack and andn; register src1, src2, dst. +typedef struct { + ut8 op; ///< bits 9:6 + const char *mnem; + _RzAnalysisOpType type; + bool swap; ///< render src2 (value) before src1 (count), as shifts do +} C6xSExtRow; + +static const C6xSExtRow c6x_s_ext_rows[] = { + { 0, "sadd2", RZ_ANALYSIS_OP_TYPE_ADD }, + { 1, "saddus2", RZ_ANALYSIS_OP_TYPE_ADD }, + { 3, "saddu4", RZ_ANALYSIS_OP_TYPE_ADD }, + { 2, "spack2", RZ_ANALYSIS_OP_TYPE_MOV }, + { 4, "spacku4", RZ_ANALYSIS_OP_TYPE_MOV }, + { 6, "andn", RZ_ANALYSIS_OP_TYPE_AND }, + { 7, "shr2", RZ_ANALYSIS_OP_TYPE_SHR, true }, + { 8, "shru2", RZ_ANALYSIS_OP_TYPE_SHR, true }, + { 9, "shlmb", RZ_ANALYSIS_OP_TYPE_SHL }, + { 10, "shrmb", RZ_ANALYSIS_OP_TYPE_SHR }, + { 11, "rpack2", RZ_ANALYSIS_OP_TYPE_MOV }, + { 15, "pack2", RZ_ANALYSIS_OP_TYPE_MOV } +}; + +// The .L opfield 0x1a (nominally ABS) is shared by unary byte/halfword +// reshuffles on C64x+, selected by the src1 field (src2 -> dst). +static const C6xMUnaryRow c6x_l_unary_rows[] = { + { 1, "swap4" }, { 2, "unpklu4" }, { 3, "unpkhu4" }, { 4, "abs2" } +}; + +// .D unit arithmetic and address ops, 6-bit opfield at bits 12:7, signature +// bits 6:2 == 10000 (SPRU733 Figure C-1 body). +static const C6xRow c6x_d_rows[] = { + { 0x10, "add", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x12, "add", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x11, "sub", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x13, "sub", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x30, "addab", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x32, "addab", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x34, "addah", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x36, "addah", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x38, "addaw", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x3a, "addaw", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x3c, "addad", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x3c, "addad", SRC1_REG, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x3d, "addad", SRC1_UCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x3d, "addad", SRC1_UCST5, FEAT_FP, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0x31, "subab", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x35, "subah", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x39, "subaw", SRC1_REG, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, + { 0x3b, "subaw", SRC1_UCST5, FEAT_BASE, RZ_ANALYSIS_OP_TYPE_SUB }, +}; + +// .D-unit extended logical/arith (C64x+): SPRUFE8 Dx2/Dx5 family, marked by +// bits 5:2 == 1100 with bits 11:10 == 10 selecting the .D unit. The op is a +// 4-bit field at bits 9:6; OR with a zero source constant becomes MV. +static const C6xRow c6x_dext_rows[] = { + { 0x0, "andn", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x2, "or", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x3, "or", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_OR }, + { 0x6, "and", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0x7, "and", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_AND }, + { 0xa, "add", SRC1_REG, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_ADD }, + { 0xf, "xor", SRC1_SCST5, FEAT_SIMD, RZ_ANALYSIS_OP_TYPE_XOR }, +}; + +// .D unit loads/stores, 3-bit opfield at bits 6:4, signature bits 3:2 == 01 +// (SPRU733 Tables 3-17/3-19). The op encodes access width and direction. +static const struct { + ut8 op; + const char *mnem; + bool store; +} c6x_mem_rows[] = { + { 0x0, "ldhu", false }, + { 0x1, "ldbu", false }, + { 0x2, "ldb", false }, + { 0x3, "stb", true }, + { 0x4, "ldh", false }, + { 0x5, "sth", true }, + { 0x6, "ldw", false }, + { 0x7, "stw", true }, +}; + +const C6xArchDesc c6x_desc_c62x = { C6X_GEN_C62X, 16, false, false }; +const C6xArchDesc c6x_desc_c67x = { C6X_GEN_C67X, 16, true, false }; +const C6xArchDesc c6x_desc_c64x = { C6X_GEN_C64X, 32, false, true }; +const C6xArchDesc c6x_desc_c674x = { C6X_GEN_C674X, 32, true, true }; +const C6xArchDesc c6x_desc_c66x = { C6X_GEN_C66X, 32, true, true }; + +RZ_IPI const C6xArchDesc *c6x_desc_from_cpu(const char *cpu) { + if (!cpu) { + return NULL; + } + if (!rz_str_casecmp(cpu, "c62x")) { + return &c6x_desc_c62x; + } + if (!rz_str_casecmp(cpu, "c67x")) { + return &c6x_desc_c67x; + } + if (!rz_str_casecmp(cpu, "c64x")) { + return &c6x_desc_c64x; + } + // C64x+ is a superset of C64x; the extra instructions this engine decodes + // are gated by the SIMD feature the C64x descriptor already carries. + if (!rz_str_casecmp(cpu, "c64x+") || !rz_str_casecmp(cpu, "c64xp") || + !rz_str_casecmp(cpu, "c64x_plus")) { + return &c6x_desc_c64x; + } + if (!rz_str_casecmp(cpu, "c674x")) { + return &c6x_desc_c674x; + } + if (!rz_str_casecmp(cpu, "c66x")) { + return &c6x_desc_c66x; + } + return NULL; +} + +RZ_IPI void c6x_mark_parallel(RZ_INOUT C6xInsn *insn, ut64 addr, RZ_INOUT ut64 *prev_end, RZ_INOUT bool *prev_par) { + if (insn->is_header) { + // a fetch-packet header occupies a word but is not part of any execute + // packet: keep it out of the "||" chain and pass the previous parallel + // state through unchanged so an execute packet can span the header + insn->cont = false; + *prev_end = addr + insn->size; + return; + } + // only a word landing exactly after the previous one continues its packet; + // a gap (random-access disassembly, region restart) starts fresh + insn->cont = addr == *prev_end && *prev_par; + *prev_end = addr + insn->size; + *prev_par = insn->parallel; +} + +static bool feat_ok(const C6xArchDesc *d, ut8 feat) { + switch (feat) { + case FEAT_FP: + return d->has_fp; + case FEAT_SIMD: + return d->has_simd; + case FEAT_CPLX: + return d->has_fp && d->has_simd; // C674x and C66x + case FEAT_C66X: + return d->gen == C6X_GEN_C66X; + default: + return true; + } +} + +static const C6xRow *row_find(const C6xRow *rows, size_t n, ut16 op, const C6xArchDesc *d) { + for (size_t i = 0; i < n; i++) { + if (rows[i].op == op && feat_ok(d, rows[i].feat)) { + return &rows[i]; + } + } + return NULL; +} + +static void op_reg(C6xOperand *o, ut8 side, ut8 num) { + o->kind = C6X_OP_REG; + o->side = side; + o->num = num; +} + +static void op_regpair(C6xOperand *o, ut8 side, ut8 num) { + o->kind = C6X_OP_REGPAIR; + o->side = side; + o->num = num & ~1u; +} + +static void op_regquad(C6xOperand *o, ut8 side, ut8 num) { + o->kind = C6X_OP_REGQUAD; + o->side = side; + o->num = num & ~3u; +} + +// Fill a register operand of a caller-chosen width (single / pair / quad). +static void op_by_kind(C6xOperand *o, ut8 kind, ut8 side, ut8 num) { + if (kind == C6X_OP_REGQUAD) { + op_regquad(o, side, num); + } else if (kind == C6X_OP_REGPAIR) { + op_regpair(o, side, num); + } else { + op_reg(o, side, num); + } +} + +static void op_imm(C6xOperand *o, st64 v) { + o->kind = C6X_OP_IMM; + o->imm = v; +} + +static void op_ctrlreg(C6xOperand *o, const char *name) { + o->kind = C6X_OP_CTRLREG; + o->name = name; +} + +// Control registers by crlo field (SPRUFE8 Table 3-27). crlo 2 and 29 name a +// different register per direction (read vs write), disambiguated by \p read. +static const char *ctrlreg_name(ut8 crlo, bool read) { + static const char *const by_crlo[32] = { + "amr", "csr", NULL, "icr", "ier", "istp", "irp", "nrp", + NULL, NULL, "tscl", "tsch", NULL, "ilc", "rilc", "rep", + "pce1", "dnum", "fadcr", "faucr", "fmcr", "ssr", "gplya", "gplyb", + "gfpgfr", "dier", "tsr", "itsr", "ntsr", NULL, NULL, "ierr" + }; + if (crlo == 2) { + return read ? "ifr" : "isr"; + } + if (crlo == 29) { + return read ? "efr" : "ecr"; + } + return by_crlo[crlo]; +} + +// Sign-extend the low \p bits of \p v. The cast goes through st32 so the sign +// bit propagates: without it the ut32 subtraction wraps and widens to a large +// positive st64 (which broke backward branches and negative constants). +static st64 sext(ut32 v, ut8 bits) { + ut32 m = 1u << (bits - 1); + return (st64)(st32)((v ^ m) - m); +} + +// Fill src1 (bits 17:13) as register or short constant per the row's kind. +static void fill_src1(C6xOperand *o, ut32 w, ut8 side, C6xSrc1Kind kind) { + ut8 f = BITS(w, 17, 13); + switch (kind) { + case SRC1_REG: + op_reg(o, side, f); + break; + case SRC1_SCST5: + op_imm(o, sext(f, 5)); + break; + case SRC1_UCST5: + op_imm(o, f); + break; + default: + o->kind = C6X_OP_NONE; + break; + } +} + +// .L/.S/.M 3-operand form: dst, src2 (cross-pathable), src1 (reg or const). +// Double-precision rows read and write 64-bit register pairs instead of scalars. +static void decode_3op(C6xInsn *insn, const C6xRow *row) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + ut8 x = BIT(w, 12); + insn->mnemonic = row->mnem; + insn->op_type = row->type; + insn->is_fp = row->feat == FEAT_FP; + insn->unit_side = s + 1; + insn->cross = x != 0; + // The cross-path (x) bit moves one source operand to the opposite register + // side. It normally selects src2; src1-cross variants (non-commutative ops + // such as SUBSP/SUBDP) apply it to src1 instead. + ut8 src1_side = (row->src1_cross && x) ? !s : s; + ut8 src2_side = (!row->src1_cross && x) ? !s : s; + ut8 nops = 0; + if (row->src1 != SRC1_NONE) { + if (row->pair && row->src1 == SRC1_REG) { + op_regpair(&insn->ops[nops++], src1_side, BITS(w, 17, 13)); + } else { + fill_src1(&insn->ops[nops++], w, src1_side, row->src1); + } + } + bool src2_pair = row->pair; + bool dst_pair = row->pair; + if (row->oshape == OSHAPE_CMP_DP) { + dst_pair = false; // DP compare result is a single sint + } else if (row->oshape == OSHAPE_TO_DP) { + src2_pair = false; // a single src2 widened into a pair dst + dst_pair = true; + } else if (row->oshape == OSHAPE_CMP_LONG) { + src2_pair = true; // a 40-bit long src2 compared against a single/const + dst_pair = false; + } else if (row->oshape == OSHAPE_LONG_DST) { + src2_pair = true; // long ADD: single src1 + long src2 -> long dst + dst_pair = true; + } + if (src2_pair) { + op_regpair(&insn->ops[nops++], src2_side, BITS(w, 22, 18)); // src2 + } else { + op_reg(&insn->ops[nops++], src2_side, BITS(w, 22, 18)); // src2 + } + // Shifts print value (src2) before count (src1); swap the two sources + // that were emitted in the fixed src1, src2 order above. + bool is_shift = row->type == RZ_ANALYSIS_OP_TYPE_SHL || + row->type == RZ_ANALYSIS_OP_TYPE_SHR || row->type == RZ_ANALYSIS_OP_TYPE_SAR; + if (is_shift && nops == 2 && !row->no_operand_swap) { + C6xOperand tmp = insn->ops[0]; + insn->ops[0] = insn->ops[1]; + insn->ops[1] = tmp; + } + if (dst_pair) { + op_regpair(&insn->ops[nops++], s, BITS(w, 27, 23)); // dst + } else { + op_reg(&insn->ops[nops++], s, BITS(w, 27, 23)); // dst + } + insn->nops = nops; + // Render the .S/.L assembler idioms the way the TI tools do so the output + // matches the reference disassembler: + // MV dst = 0 | src or 0 + src (OR/ADD against 0) + // NEG dst = 0 - src (SUB from 0) + // ZERO dst = src - src (SUB of a register from itself) + // SUB src2, k, dst for ADD -k, src2, dst (negative-constant ADD) + bool is_add = !strcmp(row->mnem, "add"); + bool is_sub = !strcmp(row->mnem, "sub"); + bool is_or = !strcmp(row->mnem, "or"); + bool is_xor = !strcmp(row->mnem, "xor"); + if (nops == 3 && insn->ops[0].kind == C6X_OP_IMM && insn->ops[0].imm == 0 && + (is_or || is_add || is_sub)) { + insn->mnemonic = is_sub ? "neg" : "mv"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->ops[0] = insn->ops[1]; + insn->ops[1] = insn->ops[2]; + insn->nops = 2; + } else if (nops == 3 && is_xor && insn->ops[0].kind == C6X_OP_IMM && + insn->ops[0].imm == -1) { + insn->mnemonic = "not"; // XOR -1, src, dst == NOT src, dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_NOT; + insn->ops[0] = insn->ops[1]; + insn->ops[1] = insn->ops[2]; + insn->nops = 2; + } else if (nops == 3 && is_add && insn->ops[0].kind == C6X_OP_IMM && + insn->ops[0].imm < 0) { + insn->mnemonic = "sub"; // ADD -k, src2, dst == SUB src2, k, dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_SUB; + st64 k = -insn->ops[0].imm; + insn->ops[0] = insn->ops[1]; + op_imm(&insn->ops[1], k); + } else if (nops == 3 && is_sub && insn->ops[0].kind == C6X_OP_REG && + insn->ops[1].kind == C6X_OP_REG && insn->ops[0].num == insn->ops[1].num && + insn->ops[0].side == insn->ops[1].side) { + insn->mnemonic = "zero"; // SUB src, src, dst == ZERO dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->ops[0] = insn->ops[2]; + insn->nops = 1; + } +} + +// .D arithmetic/address form, all on side s with no cross path. The .D +// encoding places the base/src2 register at bits 22:18 and src1 (register or +// short constant) at bits 17:13, and the assembler prints them in that order +// -- src2, src1, dst -- for both plain ADD/SUB and the ADDA*/SUBA* address +// ops, unlike the .L/.S forms that print src1 first. +static void decode_darith(C6xInsn *insn, const C6xRow *row) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + insn->mnemonic = row->mnem; + insn->op_type = row->type; + insn->unit_side = s + 1; + op_reg(&insn->ops[0], s, BITS(w, 22, 18)); // src2 (base) + fill_src1(&insn->ops[1], w, s, row->src1); // src1 (offset/operand) + op_reg(&insn->ops[2], s, BITS(w, 27, 23)); // dst + insn->nops = 3; + // The .D unit shares the same assembler idioms as .L/.S: MV/NEG (ADD/SUB + // against 0), NOT (XOR -1), ZERO (SUB of a register from itself), and the + // negative-constant SUB spelled as ADD. In the .D operand order src2 comes + // first, so the constant (when present) is src1 at ops[1]. + bool d_add = !strcmp(row->mnem, "add"); + bool d_sub = !strcmp(row->mnem, "sub"); + bool d_xor = !strcmp(row->mnem, "xor"); + if (insn->ops[1].kind == C6X_OP_IMM && insn->ops[1].imm == 0 && (d_add || d_sub)) { + insn->mnemonic = d_sub ? "neg" : "mv"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->ops[1] = insn->ops[2]; // dst follows src + insn->nops = 2; + } else if (insn->ops[1].kind == C6X_OP_IMM && insn->ops[1].imm == -1 && d_xor) { + insn->mnemonic = "not"; // XOR src, -1, dst == NOT src, dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_NOT; + insn->ops[1] = insn->ops[2]; + insn->nops = 2; + } else if (d_add && insn->ops[1].kind == C6X_OP_IMM && insn->ops[1].imm < 0) { + insn->mnemonic = "sub"; // ADD src2, -k, dst == SUB src2, k, dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_SUB; + op_imm(&insn->ops[1], -insn->ops[1].imm); + } else if (d_sub && insn->ops[0].kind == C6X_OP_REG && insn->ops[1].kind == C6X_OP_REG && + insn->ops[0].num == insn->ops[1].num && insn->ops[0].side == insn->ops[1].side) { + insn->mnemonic = "zero"; // SUB src, src, dst == ZERO dst + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->ops[0] = insn->ops[2]; + insn->nops = 1; + } +} + +// .D long-immediate load/store: *+B14/B15[ucst15] (SPRU733 LDW 15-bit offset). +// Base is B14 (y=0) or B15 (y=1); the 15-bit unsigned offset sits at bits 22:8. +static void decode_ldst_long(C6xInsn *insn, const char *mnem, bool store) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + ut8 y = BIT(w, 7); + C6xOperand mem = { 0 }; + C6xOperand reg = { 0 }; + insn->mnemonic = mnem; + insn->op_type = store ? RZ_ANALYSIS_OP_TYPE_STORE : RZ_ANALYSIS_OP_TYPE_LOAD; + insn->unit = C6X_UNIT_D; + insn->unit_side = 2; // B14/B15 are on the B side; .D2 only + mem.kind = C6X_OP_MEM; + mem.mode = C6X_AM_POS_CST; + mem.base = y ? 15 : 14; + mem.base_side = 1; + mem.off_cst = BITS(w, 22, 8); + mem.scaled = true; + op_reg(®, s, BITS(w, 27, 23)); + insn->ops[0] = store ? reg : mem; + insn->ops[1] = store ? mem : reg; + insn->nops = 2; +} + +// .D ADDAW with a 15-bit constant: B14/B15 + ucst15 -> dst. It reuses the STW +// 15-bit-offset slot (bits 6:2 == 0x1f, op 7); the address form is marked by +// the otherwise-reserved creg=0/z=1 pattern (bits 31:28 == 0001), so a real +// (predicated or plain) STW there stays a store. The offset is a word count. +static void decode_addaw_long(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + ut8 y = BIT(w, 7); + insn->mnemonic = "addaw"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_ADD; + insn->unit = C6X_UNIT_D; + insn->unit_side = s + 1; + op_reg(&insn->ops[0], 1, y ? 15 : 14); // base B14/B15 + op_imm(&insn->ops[1], BITS(w, 22, 8)); // ucst15 + insn->ops[1].imm_dec = true; + op_reg(&insn->ops[2], s, BITS(w, 27, 23)); // dst + insn->nops = 3; +} + +// .D load/store addressing mode (bits 12:9), Table 3-11. +static C6xAddrMode ldst_mode(ut8 field, bool *reg_offset) { + static const C6xAddrMode modes[16] = { + C6X_AM_NEG_CST, C6X_AM_POS_CST, C6X_AM_NEG_CST, C6X_AM_POS_CST, + C6X_AM_NEG_REG, C6X_AM_POS_REG, C6X_AM_NEG_REG, C6X_AM_POS_REG, + C6X_AM_PREDEC_CST, C6X_AM_PREINC_CST, C6X_AM_POSTDEC_CST, C6X_AM_POSTINC_CST, + C6X_AM_PREDEC_REG, C6X_AM_PREINC_REG, C6X_AM_POSTDEC_REG, C6X_AM_POSTINC_REG + }; + *reg_offset = (field & 0x4) != 0; + return modes[field & 0xf]; +} + +static void decode_ldst(C6xInsn *insn, const char *mnem, bool store, bool dword) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + ut8 y = BIT(w, 7); + bool reg_off = false; + C6xOperand mem = { 0 }; + C6xOperand reg = { 0 }; + insn->mnemonic = mnem; + insn->op_type = store ? RZ_ANALYSIS_OP_TYPE_STORE : RZ_ANALYSIS_OP_TYPE_LOAD; + insn->unit_side = y + 1; + mem.kind = C6X_OP_MEM; + mem.mode = ldst_mode(BITS(w, 12, 9), ®_off); + mem.base = BITS(w, 22, 18); + mem.base_side = y; + mem.scaled = true; // [] bracket scaling by access size + if (reg_off) { + mem.off_reg = BITS(w, 17, 13); + } else { + mem.off_cst = BITS(w, 17, 13); + } + if (dword) { + reg.kind = C6X_OP_REGPAIR; + reg.side = s; + reg.num = BITS(w, 27, 23) & ~1u; + } else { + op_reg(®, s, BITS(w, 27, 23)); + } + // TI syntax: loads read "*mem, dst"; stores read "src, *mem". + insn->ops[0] = store ? reg : mem; + insn->ops[1] = store ? mem : reg; + insn->nops = 2; +} + +// .S branch to a register: B/BNOP (.S2) src2 (SPRU733 "Branch Using a +// Register"). src2 holds the target; a return is BNOP .S2 B3, 5. The 3-bit +// field at 15:13 is the NOP count -- when non-zero the assembler spells it +// BNOP; the C64x+ compiler uses BNOP B3, 5 as the return idiom. +static void decode_branch_reg(C6xInsn *insn) { + ut32 w = insn->word; + ut8 x = BIT(w, 12); + ut8 n = BITS(w, 15, 13); + insn->mnemonic = n ? "bnop" : "b"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_RJMP; + insn->unit_side = 2; // .S2 only + insn->cross = x != 0; + // src2 is read from the B side, or the A side when the cross path is used + op_reg(&insn->ops[0], x ? 0 : 1, BITS(w, 22, 18)); + insn->nops = 1; + if (n) { + op_imm(&insn->ops[insn->nops++], n); + } +} + +// .S branch, 21-bit signed PC-relative displacement (scaled by 4), Figure F-5. +static void decode_branch(C6xInsn *insn) { + ut32 w = insn->word; + st64 disp = sext(BITS(w, 27, 7), 21) * 4; + insn->unit_side = BIT(w, 1) + 1; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = disp; // absolute target resolved by the analysis filler + insn->nops = 1; + // CALLP is encoded as an otherwise-meaningless unconditional branch with the + // z bit set; the return address is placed in A3 (.S1) or B3 (.S2). + if (insn->creg == 0 && insn->z) { + insn->mnemonic = "callp"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_CALL; + op_reg(&insn->ops[1], insn->unit_side - 1, 3); + insn->nops = 2; + return; + } + insn->mnemonic = "b"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_JMP; +} + +// .S BNOP with a displacement (SPRUFE8 Figure F-10): a 12-bit signed +// displacement plus a NOP count. Unlike the plain B (scaled by 4), this +// displacement is half-word-scaled (x2) so it can target compact instructions; +// it stays packet-relative, so the analysis filler resolves it against PCE1. +static void decode_branch_nop(C6xInsn *insn) { + ut32 w = insn->word; + ut8 n = BITS(w, 15, 13); + insn->mnemonic = "bnop"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_JMP; + insn->unit_side = BIT(w, 1) + 1; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = sext(BITS(w, 27, 16), 12) * 2; + op_imm(&insn->ops[1], n); + insn->nops = 2; +} +static void decode_mvk(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + ut8 h = BIT(w, 6); + insn->mnemonic = h ? "mvkh" : "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit_side = s + 1; + op_imm(&insn->ops[0], h ? (st64)BITS(w, 22, 7) : sext(BITS(w, 22, 7), 16)); + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); + insn->nops = 2; +} + +// .S ADDK, 16-bit signed constant, Figure (ADDK opcode row bits 6:2 == 10100). +static void decode_addk(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + insn->mnemonic = "addk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_ADD; + insn->unit_side = s + 1; + op_imm(&insn->ops[0], sext(BITS(w, 22, 7), 16)); + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); + insn->nops = 2; +} + +// .S2 ADDKPC (C64x+): forms a PC-relative address, dst = PCE1 + scst7 * 4, +// and skips a following count (bits 15:13) of parallel NOP cycles. The target +// is rendered like a branch so it reads as an absolute address. +static void decode_addkpc(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + insn->mnemonic = "addkpc"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_ADD; + insn->unit_side = s + 1; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = sext(BITS(w, 22, 16), 7) * 4; + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); // dst + op_imm(&insn->ops[2], BITS(w, 15, 13)); // parallel NOP count + insn->ops[2].imm_dec = true; + insn->nops = 3; +} + +// .S BDEC/BPOS (C64x+ loop control): branch to PCE1 + scst10 * 4 while the +// named register is non-negative; BDEC also predecrements it. Bit 12 selects +// the decrementing form (1) over the plain positive test (0). +static void decode_bdec(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + insn->mnemonic = BIT(w, 12) ? "bdec" : "bpos"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_CJMP; + insn->unit_side = s + 1; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = sext(BITS(w, 22, 13), 10) * 4; + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); // counter register + insn->nops = 2; +} + +// No-unit NOP (SPRU733 Appendix G): the word is all zero except the parallel bit +// and a 1..8 cycle count in bits 16:13 (count = field + 1). IDLE is a dedicated +// control opcode handled by the emulation format. +static bool decode_nounit(C6xInsn *insn) { + ut32 w = insn->word; + // only the count field (16:13) and the parallel bit (0) may be set + if ((w & ~((0xfu << 13) | 1u)) == 0) { + ut8 cnt = BITS(w, 16, 13) + 1; + // a 16-cycle delay is not a real NOP; that slot is the IDLE opcode + if (cnt == 16) { + insn->mnemonic = "idle"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NOP; + insn->unit = C6X_UNIT_NONE; + return true; + } + insn->mnemonic = "nop"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NOP; + insn->unit = C6X_UNIT_NONE; + if (cnt > 1) { + op_imm(&insn->ops[0], cnt); + insn->nops = 1; + } + return true; + } + // SPLOOP/SPLOOPD/SPMASK (C64x+ software-pipelined loop buffer): bit 17 marks + // this class, bits 17:13 pick the op, and SPLOOP(D) carry the loop iteration + // interval (ii = field + 1) in bits 27:23. + if (BIT(w, 17) && (w & ~((0x1fu << 23) | (0x1fu << 13) | 1u)) == 0) { + ut8 sel = BITS(w, 17, 13); + const char *mn = sel == 24 ? "spmask" : sel == 28 ? "sploop" + : sel == 29 ? "sploopd" + : NULL; + if (mn) { + insn->mnemonic = mn; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NULL; + insn->unit = C6X_UNIT_NONE; + if (sel != 24) { + op_imm(&insn->ops[0], BITS(w, 27, 23) + 1); + insn->nops = 1; + } + return true; + } + } + // DINT/RINT: no-unit interrupt-enable control (SPRUFE8 Figure H-1). The + // 0001 prefix at bits 31:28 is opcode, not a predicate, so it must not be + // read as creg/z. op (bits 16:13) selects the mnemonic; only the op field + // and the parallel bit may otherwise be set. + if ((w & ~((0xfu << 13) | 1u)) == 0x10000000) { + ut8 op = BITS(w, 16, 13); + const char *mn = op == 2 ? "dint" : op == 3 ? "rint" + : NULL; + if (mn) { + insn->mnemonic = mn; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NULL; + insn->unit = C6X_UNIT_NONE; + insn->creg = 0; + insn->z = 0; + return true; + } + } + return false; +} + +// MVC: move between a control register and a general register (.S2 only). +// bit 6 selects direction; the control register is named by its crlo field +// (crhi is 0 for register access, so it is not rendered). +static bool decode_mvc(C6xInsn *insn) { + ut32 w = insn->word; + ut8 s = BIT(w, 1); + bool read = BIT(w, 6); // 1: control -> register; 0: register -> control + ut8 crlo = read ? BITS(w, 22, 18) : BITS(w, 27, 23); + ut8 reg = read ? BITS(w, 27, 23) : BITS(w, 22, 18); + const char *cr = ctrlreg_name(crlo, read); + insn->mnemonic = "mvc"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_S; + insn->unit_side = s + 1; + if (read) { + op_ctrlreg(&insn->ops[0], cr); + op_reg(&insn->ops[1], s, reg); + } else { + op_reg(&insn->ops[0], s, reg); + op_ctrlreg(&insn->ops[1], cr); + } + insn->nops = 2; + return cr != NULL; +} + +// EXTU/EXT/SET/CLR, constant form (SPRUFE8 field-op format): csta and cstb +// give the bit-field bounds as 5-bit constants; the op is bits 7:6. +static bool decode_field(C6xInsn *insn) { + ut32 w = insn->word; + static const char *const names[4] = { "extu", "ext", "set", "clr" }; + static const _RzAnalysisOpType types[4] = { + RZ_ANALYSIS_OP_TYPE_SHR, RZ_ANALYSIS_OP_TYPE_SHR, + RZ_ANALYSIS_OP_TYPE_OR, RZ_ANALYSIS_OP_TYPE_AND + }; + ut8 op = BITS(w, 7, 6); + ut8 s = BIT(w, 1); + insn->mnemonic = names[op]; + insn->op_type = types[op]; + insn->unit = C6X_UNIT_S; + insn->unit_side = s + 1; + op_reg(&insn->ops[0], s, BITS(w, 22, 18)); // src2 + op_imm(&insn->ops[1], BITS(w, 17, 13)); // csta + op_imm(&insn->ops[2], BITS(w, 12, 8)); // cstb + insn->ops[1].imm_dec = true; + insn->ops[2].imm_dec = true; + op_reg(&insn->ops[3], s, BITS(w, 27, 23)); // dst + insn->nops = 4; + return true; +} + +// Decode a functional-unit instruction from its low-bit signature. +static bool decode_unit(const C6xArchDesc *d, C6xInsn *insn) { + ut32 w = insn->word; + // .D loads/stores: bits 3:2 == 01 + if (BITS(w, 3, 2) == 0x1) { + // Doubleword variants (register pair) are marked by bit 8; the op + // field then selects among them. Available on C64x+ (SIMD) and C67x + // (FP), not plain C62x. + if (BIT(w, 8)) { + const char *mn = NULL; + bool store = false, dword = true; + switch (BITS(w, 6, 4)) { + case 0x2: mn = "ldndw"; break; + case 0x3: + mn = "ldnw", dword = false; + break; // non-aligned word load (C64x+) + case 0x4: mn = "stdw", store = true; break; + case 0x5: + mn = "stnw", store = true, dword = false; + break; // non-aligned word store (C64x+) + case 0x6: mn = "lddw"; break; + case 0x7: mn = "stndw", store = true; break; + } + if (mn) { + decode_ldst(insn, mn, store, dword); + insn->unit = C6X_UNIT_D; + return feat_ok(d, FEAT_FP) || feat_ok(d, FEAT_SIMD); + } + } + ut8 op = BITS(w, 6, 4); + decode_ldst(insn, c6x_mem_rows[op].mnem, c6x_mem_rows[op].store, false); + insn->unit = C6X_UNIT_D; + return true; + } + // .D long-immediate loads/stores (*+B14/B15[ucst15]): bits 3:2 == 11 + if (BITS(w, 3, 2) == 0x3) { + ut8 op = BITS(w, 6, 4); + // ADDAW shares the op-7 (STW) slot; bits 31:28 == 0001 selects it. + if (op == 0x7 && BITS(w, 31, 28) == 0x1) { + decode_addaw_long(insn); + return true; + } + decode_ldst_long(insn, c6x_mem_rows[op].mnem, c6x_mem_rows[op].store); + return true; + } + // .L 1-or-2-source: bits 4:2 == 110 + if (BITS(w, 4, 2) == 0x6) { + // FP add/sub on .S issue in this same format; their opfields select + // the .S unit, so match them before the .L table. + const C6xRow *sf = row_find(c6x_s_fp_rows, RZ_ARRAY_SIZE(c6x_s_fp_rows), BITS(w, 11, 5), d); + if (sf) { + decode_3op(insn, sf); + insn->unit = C6X_UNIT_S; + return true; + } + // MVK.L (LSDx1 overlay, C64x+): the ABS opfield 0x1a is reused for + // a 5-bit signed constant move; ABS's unary form fixes src1 = 0, + // so a src1 field of 0x05 means MVK instead. + if (BITS(w, 11, 5) == 0x1a && BITS(w, 17, 13) == 0x05) { + ut8 s = BIT(w, 1); + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_L; + insn->unit_side = s + 1; + op_imm(&insn->ops[0], sext(BITS(w, 22, 18), 5)); + insn->ops[0].imm_dec = true; + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); + insn->nops = 2; + return true; + } + // The ABS opfield also carries the C64x+ unary byte reshuffles, chosen + // by src1; src1 == 0 is the ABS itself and falls through to the table. + if (feat_ok(d, FEAT_SIMD) && BITS(w, 11, 5) == 0x1a) { + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_l_unary_rows); i++) { + if (c6x_l_unary_rows[i].src1 != BITS(w, 17, 13)) { + continue; + } + ut8 s = BIT(w, 1); + ut8 x = BIT(w, 12); + insn->mnemonic = c6x_l_unary_rows[i].mnem; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_L; + insn->unit_side = s + 1; + insn->cross = x; + op_reg(&insn->ops[0], x ? !s : s, BITS(w, 22, 18)); // src2 + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); // dst + insn->nops = 2; + return true; + } + } + const C6xRow *r = row_find(c6x_l_rows, RZ_ARRAY_SIZE(c6x_l_rows), BITS(w, 11, 5), d); + if (r) { + decode_3op(insn, r); + insn->unit = C6X_UNIT_L; + return true; + } + return false; + } + // EXTU/EXT/SET/CLR, constant field form: bits 5:2 == 0010 + if (BITS(w, 5, 2) == 0x2) { + return decode_field(insn); + } + // .D-unit extended logic/arith (C64x+): bits 5:2 == 1100, bits 11:10 == 10. + if (BITS(w, 5, 2) == 0xc && BITS(w, 11, 10) == 0x2) { + const C6xRow *r = row_find(c6x_d_ext_rows, RZ_ARRAY_SIZE(c6x_d_ext_rows), BITS(w, 9, 6), d); + if (r) { + decode_3op(insn, r); + insn->unit = C6X_UNIT_D; + return true; + } + } + // Extended .M (C64x+): bits 5:2 == 1100 with bits 11:10 in {0,1}. The + // unary moves/shuffles share the bits 9:6 == 3 slot and are told apart by + // the src1 field; every other slot is a two-source (packed) multiply. + if (feat_ok(d, FEAT_SIMD) && BITS(w, 5, 2) == 0xc && BITS(w, 11, 10) <= 1) { + ut8 s = BIT(w, 1); + ut8 x = BIT(w, 12); + // C66x complex/matrix ops occupy this space with z = 1, creg = 0 (they + // are always unconditional); that signature reserves the slot from the + // C64x+ op below, which is either unconditional (z = 0) or predicated + // (creg != 0). + if (BITS(w, 31, 29) == 0 && BIT(w, 28) == 1) { + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_m_cplx_rows); i++) { + const C6xMCplxRow *r = &c6x_m_cplx_rows[i]; + if (r->sub != BITS(w, 11, 10) || r->op != BITS(w, 9, 6) || !feat_ok(d, r->feat)) { + continue; + } + insn->mnemonic = r->mnem; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MUL; + insn->unit = C6X_UNIT_M; + insn->unit_side = s + 1; + insn->cross = x; + op_by_kind(&insn->ops[0], r->k1, s, BITS(w, 17, 13)); // src1 + op_by_kind(&insn->ops[1], r->k2, x ? !s : s, BITS(w, 22, 18)); // src2 (cross) + op_by_kind(&insn->ops[2], r->k3, s, BITS(w, 27, 23)); // dst + insn->nops = 3; + return true; + } + return false; + } + if (BITS(w, 11, 10) == 0 && BITS(w, 9, 6) == 3) { + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_m_unary_rows); i++) { + if (c6x_m_unary_rows[i].src1 != BITS(w, 17, 13)) { + continue; + } + insn->mnemonic = c6x_m_unary_rows[i].mnem; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_M; + insn->unit_side = s + 1; + insn->cross = x; + op_reg(&insn->ops[0], x ? !s : s, BITS(w, 22, 18)); // src2 + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); // dst + insn->nops = 2; + return true; + } + return false; + } + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_m_ext_rows); i++) { + const C6xMExtRow *r = &c6x_m_ext_rows[i]; + if (r->sub != BITS(w, 11, 10) || r->op != BITS(w, 9, 6) || !feat_ok(d, r->feat)) { + continue; + } + insn->mnemonic = r->mnem; + insn->op_type = r->type; + insn->is_fp = r->feat == FEAT_FP; + insn->unit = C6X_UNIT_M; + insn->unit_side = s + 1; + insn->cross = x; + // the x bit normally crosses src2; mpyspdp crosses src1 (the SP + // value) instead, its DP-pair src2 staying on the unit side + ut8 src1_side = r->src1_cross && x ? !s : s; + ut8 src2_side = !r->src1_cross && x ? !s : s; + if (r->swap) { + // reversed syntax (src2, src1, dst): the shift-count and + // rotate ops print the value (src2) before the amount (src1) + op_reg(&insn->ops[0], src2_side, BITS(w, 22, 18)); // src2 + op_reg(&insn->ops[1], src1_side, BITS(w, 17, 13)); // src1 + } else { + op_reg(&insn->ops[0], src1_side, BITS(w, 17, 13)); // src1 + if (r->src2_pair) { + op_regpair(&insn->ops[1], src2_side, BITS(w, 22, 18)); + } else { + op_reg(&insn->ops[1], src2_side, BITS(w, 22, 18)); + } + } + if (r->dst_pair) { + op_regpair(&insn->ops[2], s, BITS(w, 27, 23)); + } else { + op_reg(&insn->ops[2], s, BITS(w, 27, 23)); + } + insn->nops = 3; + return true; + } + return false; + } + // Extended .S (C64x+): bits 5:2 == 1100, bits 11:10 == 11. Packed shifts, + // saturating add/pack and andn share this space, keyed by bits 9:6. + if (feat_ok(d, FEAT_SIMD) && BITS(w, 5, 2) == 0xc && BITS(w, 11, 10) == 0x3) { + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_s_ext_rows); i++) { + if (c6x_s_ext_rows[i].op != BITS(w, 9, 6)) { + continue; + } + ut8 s = BIT(w, 1); + ut8 x = BIT(w, 12); + ut8 src2_side = x ? !s : s; + insn->mnemonic = c6x_s_ext_rows[i].mnem; + insn->op_type = c6x_s_ext_rows[i].type; + insn->unit = C6X_UNIT_S; + insn->unit_side = s + 1; + insn->cross = x; + if (c6x_s_ext_rows[i].swap) { + // packed shifts print value (src2) then count (src1) + op_reg(&insn->ops[0], src2_side, BITS(w, 22, 18)); + op_reg(&insn->ops[1], s, BITS(w, 17, 13)); + } else { + op_reg(&insn->ops[0], s, BITS(w, 17, 13)); // src1 + op_reg(&insn->ops[1], src2_side, BITS(w, 22, 18)); // src2 + } + op_reg(&insn->ops[2], s, BITS(w, 27, 23)); // dst + insn->nops = 3; + return true; + } + return false; + } + // .D extended logical/arith (C64x+): bits 5:2 == 1100, bits 11:10 == 10 + if (BITS(w, 5, 2) == 0xc && BITS(w, 11, 10) == 0x2) { + const C6xRow *r = row_find(c6x_dext_rows, RZ_ARRAY_SIZE(c6x_dext_rows), BITS(w, 9, 6), d); + if (r) { + decode_3op(insn, r); + insn->unit = C6X_UNIT_D; + return true; + } + return false; + } + // remaining formats have bits 4:2 == 000, 100 or 010; refine by higher bits + ut8 sig = BITS(w, 6, 2); + if (sig == 0x00) { // .M unit + // C66x quad/matrix multiplies share this format but are unconditional + // with z = 1, creg = 0 -- that reserves the slot from the z = 0 scalar + // multiply at the same opcode. + if (BITS(w, 31, 29) == 0 && BIT(w, 28) == 1 && feat_ok(d, FEAT_C66X)) { + for (size_t i = 0; i < RZ_ARRAY_SIZE(c6x_m_wide_rows); i++) { + const C6xMWideRow *r = &c6x_m_wide_rows[i]; + if (r->op != BITS(w, 11, 7)) { + continue; + } + ut8 s = BIT(w, 1); + insn->mnemonic = r->mnem; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MUL; + insn->unit = C6X_UNIT_M; + insn->unit_side = s + 1; + // cmatmpy swaps the src1/src2 register fields + ut8 f1 = r->swap ? BITS(w, 22, 18) : BITS(w, 17, 13); + ut8 f2 = r->swap ? BITS(w, 17, 13) : BITS(w, 22, 18); + op_by_kind(&insn->ops[0], r->k1, s, f1); + op_by_kind(&insn->ops[1], r->k2, s, f2); + op_by_kind(&insn->ops[2], r->k3, s, BITS(w, 27, 23)); + insn->nops = 3; + return true; + } + return false; + } + const C6xRow *r = row_find(c6x_m_rows, RZ_ARRAY_SIZE(c6x_m_rows), BITS(w, 11, 7), d); + if (r) { + decode_3op(insn, r); + insn->unit = C6X_UNIT_M; + return true; + } + return false; + } + if (sig == 0x10) { // .D arithmetic/address + // MVK on the .D unit (C64x+) reuses the op-0 slot: a 5-bit signed + // constant move, dst on side s. cst5 sits at bits 17:13. + if (BITS(w, 12, 7) == 0x0) { + ut8 s = BIT(w, 1); + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_D; + insn->unit_side = s + 1; + op_imm(&insn->ops[0], sext(BITS(w, 17, 13), 5)); + insn->ops[0].imm_dec = true; + op_reg(&insn->ops[1], s, BITS(w, 27, 23)); + insn->nops = 2; + return true; + } + const C6xRow *r = row_find(c6x_d_rows, RZ_ARRAY_SIZE(c6x_d_rows), BITS(w, 12, 7), d); + if (r) { + decode_darith(insn, r); + insn->unit = C6X_UNIT_D; + return true; + } + return false; + } + if (BITS(w, 5, 2) == 0x8) { // .S 1-or-2-source (6-bit op) + ut16 sop = BITS(w, 11, 6); + if (sop == 0x0d) { // B src2: register branch (.S2 only) + decode_branch_reg(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (sop == 0x04) { // BNOP with a displacement (Figure F-10) + decode_branch_nop(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (sop == 0x0e || sop == 0x0f) { // MVC (control-register move) + return decode_mvc(insn); + } + if (sop == 0x05) { // ADDKPC (.S2, C64x+): PC-relative address form + decode_addkpc(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (sop == 0x00) { // BDEC/BPOS (C64x+): register-conditional loop branch + decode_bdec(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (sop == 0x03) { // B IRP/NRP (.S2): return from a (non)maskable interrupt + ut8 n = BITS(w, 15, 13); + insn->mnemonic = n ? "bnop" : "b"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_RJMP; + insn->unit_side = 2; + op_ctrlreg(&insn->ops[0], ctrlreg_name(BITS(w, 22, 18), true)); + insn->nops = 1; + if (n) { + op_imm(&insn->ops[insn->nops++], n); + } + insn->unit = C6X_UNIT_S; + return true; + } + const C6xRow *r = row_find(c6x_s_rows, RZ_ARRAY_SIZE(c6x_s_rows), sop, d); + if (r) { + decode_3op(insn, r); + insn->unit = C6X_UNIT_S; + return true; + } + return false; + } + if (sig == 0x04) { // .S branch immediate + decode_branch(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (sig == 0x14) { // .S ADDK + decode_addk(insn); + insn->unit = C6X_UNIT_S; + return true; + } + if (BITS(w, 5, 2) == 0xa) { // .S MVK/MVKH (bit6 = h) + decode_mvk(insn); + insn->unit = C6X_UNIT_S; + return true; + } + return false; +} + +// Map a 3-bit compact register field to a register operand. The header RS bit +// selects the low (A0-A7 / B0-B7) or high (A16-A23 / B16-B23) register set. +static void compact_reg(C6xOperand *o, ut8 field, ut8 side, bool rs) { + op_reg(o, side, (rs ? 16 : 0) + (field & 0x7)); +} + +// The .L L3i short constant (SPRUFE8 Figure D-5): sn:cst3 encode a 5-bit signed +// value where cst3 == 0 means +-8 rather than 0. +static st64 l3i_const(ut8 sn, ut8 cst3) { + ut8 raw = (sn << 3) | cst3; + return raw == 0 ? 8 : (raw < 8 ? (st64)raw : (st64)raw - 16); +} + +// Decode a 16-bit compact instruction (SPRUFE8 3.10, Appendices D and G). \p rs +// and \p sat come from the fetch-packet header expansion field. Only the common +// subset is handled (NOP; MV; predicated MVK; and the .L ALU/compare/MVK forms); +// unhandled encodings leave the mnemonic NULL but keep the 2-byte size so the +// packet stays aligned. bit 0 is the side selector (s); the parallel bit comes +// from the header p-bits and is set by the caller. +static void decode_compact(C6xInsn *insn, ut16 w, bool rs, bool sat, ut8 dsz, bool br) { + insn->size = 2; + ut8 s = w & 1; // 0 = A side (.x1), 1 = B side (.x2) + insn->unit_side = s + 1; + + // no-unit NOP: 13 fixed low bits, (count - 1) in bits 15:13. + if ((w & 0x1fff) == 0x0c6e) { + insn->mnemonic = "nop"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NOP; + insn->unit_side = 0; + op_imm(&insn->ops[0], ((w >> 13) & 0x7) + 1); + insn->nops = 1; + return; + } + + if ((w & 0x6) == 0x6) { + // Shared .L/.S/.D forms (Appendix G): bits 2:1 == 11, unit at bits 4:3. + ut8 unit_f = (w >> 3) & 0x3; + C6xUnit unit = unit_f == 0 ? C6X_UNIT_L : unit_f == 1 ? C6X_UNIT_S + : unit_f == 2 ? C6X_UNIT_D + : C6X_UNIT_NONE; + ut8 b5 = (w >> 5) & 1, b6 = (w >> 6) & 1; + ut8 b10 = (w >> 10) & 1, b11 = (w >> 11) & 1, b12 = (w >> 12) & 1; + ut8 x = b12; + if (unit == C6X_UNIT_D && b5 == 1 && b6 == 1 && b11 == 0) { + // Dpp (Figure C-21): B15 stack load/store, word or doubleword. + // The pointer is always B15; stores post-decrement and loads + // pre-increment by ucst2. src/dst is a full 4-bit register on the + // side named by t (bit 12); the RS header bit does not apply here. + bool dw = (w >> 15) & 1; + bool ld = (w >> 14) & 1; + ut8 t = (w >> 12) & 1; + ut8 rnum = (w >> 7) & 0xf; + insn->mnemonic = dw ? (ld ? "lddw" : "stdw") : (ld ? "ldw" : "stw"); + insn->op_type = ld ? RZ_ANALYSIS_OP_TYPE_LOAD : RZ_ANALYSIS_OP_TYPE_STORE; + insn->unit = C6X_UNIT_D; + C6xOperand mem = { 0 }; + mem.kind = C6X_OP_MEM; + mem.base = 15; + mem.base_side = 1; // B15 + mem.scaled = true; + mem.mode = ld ? C6X_AM_PREINC_CST : C6X_AM_POSTDEC_CST; + mem.off_cst = ((w >> 13) & 1) + 1; // ucst2 = ucst0 + 1 + C6xOperand reg = { 0 }; + if (dw) { + op_regpair(®, t, rnum); + } else { + op_reg(®, t, rnum); + } + insn->ops[0] = ld ? mem : reg; + insn->ops[1] = ld ? reg : mem; + insn->nops = 2; + return; + } + if (b5 == 0) { // G-1 / G-2: MV (mvto has bit6 = 0, mvfr has bit6 = 1) + insn->mnemonic = "mv"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = unit; + insn->cross = x; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, x ? !s : s, rs); // src2 + compact_reg(&insn->ops[1], (w >> 13) & 0x7, s, rs); // dst + insn->nops = 2; + return; + } + if (b6 == 1 && b12 == 0 && b11 == 1 && b10 == 0) { // G-3: predicated MVK ucst1 + ut8 cc = (w >> 14) & 0x3; + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = unit; + insn->creg = (cc & 2) ? 0x1 : 0x6; // 10x -> B0, 0x -> A0 + insn->z = cc & 1; // odd CC is the negated ([!A0]/[!B0]) sense + op_imm(&insn->ops[0], (w >> 13) & 0x1); // ucst1 + compact_reg(&insn->ops[1], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 2; + return; + } + if (unit == C6X_UNIT_L && b6 == 0 && b5 == 1 && b10 == 1) { // Lx5: MVK scst5 (.L) + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_L; + ut8 scst5 = (((w >> 11) & 0x3) << 3) | ((w >> 13) & 0x7); + op_imm(&insn->ops[0], sext(scst5, 5)); + compact_reg(&insn->ops[1], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 2; + return; + } + if (unit == C6X_UNIT_L && b6 == 0 && b5 == 1 && b10 == 0) { + // Lx3c/Lx1c: .L compare against a small constant. bit 12 == 0 is + // CMPEQ with a ucst3 (Lx3c); bit 12 == 1 selects CMPLT/CMPGT/ + // CMPLTU/CMPGTU with a ucst1 (Lx1c). dst is A0/A1 or B0/B1. + ut8 dstlo = (w >> 11) & 1; + insn->unit = C6X_UNIT_L; + if (b12 == 0) { + insn->mnemonic = "cmpeq"; + op_imm(&insn->ops[0], (w >> 13) & 0x7); // ucst3 + } else { + static const char *const mn[4] = { "cmplt", "cmpgt", "cmpltu", "cmpgtu" }; + insn->mnemonic = mn[(w >> 14) & 0x3]; + op_imm(&insn->ops[0], (w >> 13) & 0x1); // ucst1 + } + insn->op_type = RZ_ANALYSIS_OP_TYPE_CMP; + compact_reg(&insn->ops[1], (w >> 7) & 0x7, s, rs); // src2 + op_reg(&insn->ops[2], s, dstlo); // dst A0/A1 or B0/B1 + insn->nops = 3; + return; + } + if (b6 == 1 && b5 == 1 && b12 == 1 && b11 == 1 && b10 == 0) { + // G-4 LSDx1: op field selects MVK 0/1 or ADD/XOR src,1 (src == dst). + ut8 op = (w >> 13) & 0x7; + if (op < 2) { // MVK 0 or MVK 1 + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = unit; + op_imm(&insn->ops[0], op); + compact_reg(&insn->ops[1], (w >> 7) & 0x7, s, rs); + insn->nops = 2; + return; + } + if (op == 5 || op == 7) { // ADD src,1,dst or XOR src,1,dst + insn->mnemonic = op == 5 ? "add" : "xor"; + insn->op_type = op == 5 ? RZ_ANALYSIS_OP_TYPE_ADD : RZ_ANALYSIS_OP_TYPE_XOR; + insn->unit = unit; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src (== dst) + op_imm(&insn->ops[1], 1); + compact_reg(&insn->ops[2], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 3; + return; + } + if (unit == C6X_UNIT_S && op == 6) { + // Sx1: MVC src, ILC -- move a general register into the + // inner-loop-count control register (encoded with s = 1). + insn->mnemonic = "mvc"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_S; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src + op_ctrlreg(&insn->ops[1], "ilc"); + insn->nops = 2; + return; + } + return; // remaining op 2/3/4 per-unit Dx1/Lx1/Sx1 forms not decoded + } + if (unit == C6X_UNIT_S && b6 == 1 && b5 == 1 && b12 == 0 && b11 == 0) { + // Sx1b: BNOP src2, N3 -- a branch to a B-side register (B0-B15). + // This is the C64x+ compact form of the ABI return B .S2 B3; the + // encoding is always BNOP, even when the NOP count is zero. + ut8 n3 = (w >> 13) & 0x7; + insn->mnemonic = "bnop"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_RJMP; + insn->unit = C6X_UNIT_S; + op_reg(&insn->ops[0], 1, (w >> 7) & 0xf); // src2 on the B side + op_imm(&insn->ops[1], n3); + insn->nops = 2; + return; + } + return; // other shared forms not decoded yet + } + + if ((w & 0x6) == 0x0) { + // .L-only forms (Appendix D): bits 2:1 == 00, distinguished by bits 3,10. + ut8 b3 = (w >> 3) & 1, b10 = (w >> 10) & 1, x = (w >> 12) & 1; + insn->unit = C6X_UNIT_L; + insn->cross = x; + if (b3 == 0 && b10 == 0) { // L3: ADD/SUB (+ SADD/SSUB under header SAT) + ut8 op = (w >> 11) & 1; + insn->mnemonic = op ? (sat ? "ssub" : "sub") : (sat ? "sadd" : "add"); + insn->op_type = op ? RZ_ANALYSIS_OP_TYPE_SUB : RZ_ANALYSIS_OP_TYPE_ADD; + compact_reg(&insn->ops[0], (w >> 13) & 0x7, s, rs); // src1 + compact_reg(&insn->ops[1], (w >> 7) & 0x7, x ? !s : s, rs); // src2 + compact_reg(&insn->ops[2], (w >> 4) & 0x7, s, rs); // dst + insn->nops = 3; + return; + } + if (b3 == 0 && b10 == 1) { // L3i: ADD scst5, src2, dst + insn->mnemonic = "add"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_ADD; + op_imm(&insn->ops[0], l3i_const((w >> 11) & 1, (w >> 13) & 0x7)); + compact_reg(&insn->ops[1], (w >> 7) & 0x7, x ? !s : s, rs); // src2 + compact_reg(&insn->ops[2], (w >> 4) & 0x7, s, rs); // dst + insn->nops = 3; + return; + } + if (b3 == 1 && b10 == 1) { // L2c: AND/OR/XOR/CMPEQ/CMPLT/CMPGT(U) + static const char *ops[8] = { "and", "or", "xor", "cmpeq", + "cmplt", "cmpgt", "cmpltu", "cmpgtu" }; + static const _RzAnalysisOpType types[8] = { RZ_ANALYSIS_OP_TYPE_AND, + RZ_ANALYSIS_OP_TYPE_OR, RZ_ANALYSIS_OP_TYPE_XOR, + RZ_ANALYSIS_OP_TYPE_CMP, RZ_ANALYSIS_OP_TYPE_CMP, + RZ_ANALYSIS_OP_TYPE_CMP, RZ_ANALYSIS_OP_TYPE_CMP, + RZ_ANALYSIS_OP_TYPE_CMP }; + ut8 op = ((w >> 11) & 1) << 2 | ((w >> 5) & 0x3); // op2 : op1-0 + insn->mnemonic = ops[op]; + insn->op_type = types[op]; + compact_reg(&insn->ops[0], (w >> 13) & 0x7, s, rs); // src1 + compact_reg(&insn->ops[1], (w >> 7) & 0x7, x ? !s : s, rs); // src2 + op_reg(&insn->ops[2], s, (w >> 4) & 0x1); // dst: A0/A1 or B0/B1 + insn->nops = 3; + return; + } + return; // Ltbd and other .L forms not decoded yet + } + if ((w & 0x6) == 0x4) { + // .D-unit loads/stores (Appendix C): bits 2:1 == 10. Data size comes + // from the header DSZ combined with the opcode sz bit; the addressing + // form (offset const, offset reg, post-inc, pre-dec) from bits 11:10. + ut8 t = (w >> 12) & 1; // T datapath (data register) side + ut8 sz = (w >> 9) & 1; + bool ld = (w >> 3) & 1; + ut8 prim = (dsz >> 2) & 1, sec = dsz & 0x3; + const char *mn; + bool dwpair = false; + if (sz == 0 && prim == 1) { + // Doubleword-primary access (Doff4DW/DindDW/DincDW/DdecDW): the + // register is an even pair encoded at bits 6:5, and na (bit 4) + // selects aligned (DW) versus nonaligned (NDW). + ut8 na = (w >> 4) & 1; + mn = na ? (ld ? "ldndw" : "stndw") : (ld ? "lddw" : "stdw"); + dwpair = true; + } else if (sz == 0) { + mn = ld ? "ldw" : "stw"; // word-primary access + } else if (prim == 0) { // word-primary packet: secondary byte/half sizes + static const char *l[4] = { "ldbu", "ldb", "ldhu", "ldh" }; + static const char *st[4] = { "stb", "stb", "sth", "sth" }; + mn = ld ? l[sec] : st[sec]; + } else { // doubleword-primary packet: secondary word/byte/half sizes + static const char *l[4] = { "ldw", "ldb", "ldnw", "ldh" }; + static const char *st[4] = { "stw", "stb", "stnw", "sth" }; + mn = ld ? l[sec] : st[sec]; + } + insn->mnemonic = mn; + insn->op_type = ld ? RZ_ANALYSIS_OP_TYPE_LOAD : RZ_ANALYSIS_OP_TYPE_STORE; + insn->unit = C6X_UNIT_D; + C6xOperand mem = { 0 }; + mem.kind = C6X_OP_MEM; + mem.base = (rs ? 16 : 0) + 4 + ((w >> 7) & 0x3); // pointer is R4-R7 + mem.base_side = s; + mem.scaled = true; + ut8 b11 = (w >> 11) & 1, b10 = (w >> 10) & 1; + if (b10 == 0) { // Doff4: *+ptr[ucst4] + mem.mode = C6X_AM_POS_CST; + mem.off_cst = (b11 << 3) | ((w >> 13) & 0x7); + } else if (b11 == 0) { // Dind: *+ptr[src1] + mem.mode = C6X_AM_POS_REG; + mem.off_reg = (rs ? 16 : 0) + ((w >> 13) & 0x7); + } else if (((w >> 14) & 0x3) == 0) { // Dinc: *ptr++[ucst2] + mem.mode = C6X_AM_POSTINC_CST; + mem.off_cst = ((w >> 13) & 1) + 1; + } else { // Ddec: *--ptr[ucst2] + mem.mode = C6X_AM_PREDEC_CST; + mem.off_cst = ((w >> 13) & 1) + 1; + } + C6xOperand reg = { 0 }; + if (dwpair) { + // even register pair, address formed as op6:op5:0 + op_regpair(®, t, (rs ? 16 : 0) + (((w >> 5) & 0x3) << 1)); + } else { + op_reg(®, t, (rs ? 16 : 0) + ((w >> 4) & 0x7)); + } + insn->ops[0] = ld ? mem : reg; // loads: *mem, dst; stores: src, *mem + insn->ops[1] = ld ? reg : mem; + insn->nops = 2; + return; + } + if ((w & 0x6) == 0x2) { + // .S-unit compact formats (Appendix F): bits 2:1 == 01. Under a header + // BR bit the bit-3 forms are branches; otherwise they are ALU/shift ops + // (still to do). The MVK form (Smvk8) is BR-independent. + if (((w >> 4) & 1) && !((w >> 3) & 1)) { + // Smvk8 MVK: ucst8 is scattered across the word. + ut16 imm = ((w >> 13) & 0x7) | (((w >> 11) & 0x3) << 3) | + (((w >> 5) & 0x3) << 5) | (((w >> 10) & 0x1) << 7); + insn->mnemonic = "mvk"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_MOV; + insn->unit = C6X_UNIT_S; + op_imm(&insn->ops[0], imm); + compact_reg(&insn->ops[1], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 2; + return; + } + if (!((w >> 3) & 1) && !((w >> 4) & 1)) { + // .S ALU: shifts (Ssh5/S2sh, bit 10 == 1) and field operations + // (Sc5/S2ext, bit 10 == 0). The op at bits 6:5 selects the form; + // op == 3 escapes to the register-count (S2sh) or fixed-width + // (S2ext) variant, which carries its own op at bits 12:11. + insn->unit = C6X_UNIT_S; + ut8 b10 = (w >> 10) & 1; + ut8 op = (w >> 5) & 0x3; + ut8 ucst5 = (((w >> 11) & 0x3) << 3) | ((w >> 13) & 0x7); + if (b10 && op != 3) { // Ssh5: SHL/SHR/SHRU (SSHL when header SAT) + static const char *const mn[3] = { "shl", "shr", "shru" }; + insn->mnemonic = (op == 2 && sat) ? "sshl" : mn[op]; + insn->op_type = op ? RZ_ANALYSIS_OP_TYPE_SHR : RZ_ANALYSIS_OP_TYPE_SHL; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src2 (= dst) + op_imm(&insn->ops[1], ucst5); + compact_reg(&insn->ops[2], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 3; + return; + } + if (b10) { // S2sh: register-count shift, src1 (count) at bits 15:13 + static const char *const mn[4] = { "shl", "shr", "shru", "sshl" }; + ut8 op2 = (w >> 11) & 0x3; + insn->mnemonic = mn[op2]; + insn->op_type = op2 == 1 ? RZ_ANALYSIS_OP_TYPE_SHR : RZ_ANALYSIS_OP_TYPE_SHL; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src2 (= dst) + compact_reg(&insn->ops[1], (w >> 13) & 0x7, s, rs); // src1 (count) + compact_reg(&insn->ops[2], (w >> 7) & 0x7, s, rs); // dst + insn->nops = 3; + return; + } + if (op != 3) { // Sc5: EXTU/SET/CLR with a ucst5 field (dst is A0/B0) + static const char *const mn[3] = { "extu", "set", "clr" }; + static const _RzAnalysisOpType ty[3] = { RZ_ANALYSIS_OP_TYPE_SHR, + RZ_ANALYSIS_OP_TYPE_OR, RZ_ANALYSIS_OP_TYPE_AND }; + insn->mnemonic = mn[op]; + insn->op_type = ty[op]; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src2 + op_imm(&insn->ops[1], ucst5); + op_imm(&insn->ops[2], op == 0 ? 31 : ucst5); // EXTU pairs with 31 + op_reg(&insn->ops[3], s, 0); // dst A0/B0 + insn->nops = 4; + return; + } + // S2ext: EXT/EXTU with a fixed 16- or 24-bit field width. + static const char *const mn[4] = { "ext", "ext", "extu", "extu" }; + ut8 op2 = (w >> 11) & 0x3; + ut8 width = (op2 & 1) ? 24 : 16; + insn->mnemonic = mn[op2]; + insn->op_type = RZ_ANALYSIS_OP_TYPE_SHR; + compact_reg(&insn->ops[0], (w >> 7) & 0x7, s, rs); // src2 + op_imm(&insn->ops[1], width); + op_imm(&insn->ops[2], width); + compact_reg(&insn->ops[3], (w >> 13) & 0x7, s, rs); // dst + insn->nops = 4; + return; + } + if (br && ((w >> 3) & 1)) { + // Branch forms (Sbs7/Sbu8/Scs10 and their conditional variants). The + // displacement is packet-relative and scaled to the 16-bit slot, so + // the analysis filler resolves it against the fetch-packet base. + insn->unit = C6X_UNIT_S; + ut8 b4 = (w >> 4) & 1, b5 = (w >> 5) & 1; + if (b5 == 0 && b4 == 1) { // Scs10: CALLP scst10, 5 + insn->mnemonic = "callp"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_CALL; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = sext((w >> 6) & 0x3ff, 10) * 2; + insn->nops = 1; + return; + } + bool wide = ((w >> 14) & 0x3) == 0x3; // Sbu8/Sbu8c use an 8-bit ucst + st64 disp = wide ? (st64)(((w >> 6) & 0xff) * 2) // ucst8 (unsigned) + : sext((w >> 6) & 0x7f, 7) * 2; // scst7 (signed) + ut8 n3 = wide ? 5 : ((w >> 13) & 0x7); // NOP count (Sbu8 implies 5) + if (b5) { // conditional variants carry a predicate in s and bit 4 + insn->creg = (w & 1) ? 0x1 : 0x6; // s selects B0 / A0 + insn->z = b4; // bit 4 is the z (negate) sense + } + insn->mnemonic = "bnop"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_JMP; + insn->ops[0].kind = C6X_OP_PCREL; + insn->ops[0].imm = disp; + op_imm(&insn->ops[1], n3); + insn->nops = 2; + return; + } + return; + } +} + +RZ_IPI bool c6x_decode(const C6xArchDesc *desc, const ut8 *buf, int len, ut64 pc, bool big_endian, RZ_OUT C6xInsn *insn) { + rz_return_val_if_fail(desc && buf && insn, false); + if (len < 2) { + return false; + } + memset(insn, 0, sizeof(*insn)); + // C64x+ compact fetch packets (SPRUFE8 3.10): the eighth word of a 32-byte + // fetch packet is a header (top nibble 1110) whose layout field marks which + // of the other seven words hold two 16-bit compact instructions. Look for + // that header ahead of the current word; if this slot is compact, decode a + // 16-bit instruction and take the parallel bit from the header p-bit field. + // Only families with the compact set are probed, so C62x/C67x are untouched; + // when the header is not reachable (an isolated single-word decode) the + // 32-bit path runs and still recognises a header word on its own. + if (desc->has_simd) { + ut32 fp_off = (ut32)(pc & 0x1f); + int hdr_off = 0x1c - (int)fp_off; // header lives at packet offset 0x1c + if (fp_off < 0x1c && hdr_off + 4 <= len) { + ut32 hdr = big_endian ? rz_read_be32(buf + hdr_off) : rz_read_le32(buf + hdr_off); + ut8 slot = fp_off >> 2; + if ((hdr >> 28) == 0xe && ((hdr >> (21 + slot)) & 1)) { + ut16 w16 = big_endian ? rz_read_be16(buf) : rz_read_le16(buf); + ut8 half = (fp_off >> 1) & 1; + insn->word = w16; + insn->parallel = (hdr >> (slot * 2 + half)) & 1; + decode_compact(insn, w16, (hdr >> 19) & 1, (hdr >> 14) & 1, + (hdr >> 16) & 0x7, (hdr >> 15) & 1); + return true; + } + } + } + if (len < 4) { + return false; + } + insn->word = big_endian ? rz_read_be32(buf) : rz_read_le32(buf); + insn->size = 4; + ut32 w = insn->word; + // A C64x+ compact fetch packet replaces its eighth word with a header + // (SPRUFE8 3.10.2): top nibble 1110, i.e. creg=111/z=0, which is the + // reserved predicate no real instruction uses. The header repacks the + // other slots as 16-bit compact instructions; decoding those is future + // work, but the header itself is recognised so it is not mis-read as a + // 32-bit opcode and to flag the packet as compact. + if ((w >> 28) == 0xe) { + insn->is_header = true; + insn->mnemonic = ".fphead"; + insn->op_type = RZ_ANALYSIS_OP_TYPE_NULL; + return true; + } + insn->parallel = BIT(w, 0) != 0; + insn->creg = BITS(w, 31, 29); + insn->z = BIT(w, 28); + insn->op_type = RZ_ANALYSIS_OP_TYPE_ILL; + if (decode_nounit(insn)) { + return true; + } + if (!decode_unit(desc, insn)) { + insn->mnemonic = NULL; // unrecognised, still 4 bytes + insn->op_type = RZ_ANALYSIS_OP_TYPE_ILL; + } + return true; +} + +static int c6x_mnem_cmp(const void *a, const void *b, RZ_UNUSED void *user) { + return strcmp((const char *)a, (const char *)b); +} + +RZ_IPI RZ_OWN RzPVector /**/ *c6x_mnemonics(void) { + RzPVector *v = rz_pvector_new(NULL); + if (!v) { + return NULL; + } + // Each functional-unit table stores its printed mnemonic in a `mnem` + // field; gather them all, then add the few the decoder emits directly + // (loads, stores, moves, branches, the address-arithmetic forms and the + // nop/idle family). Sorting and dropping duplicates leaves `rz-asm -e` + // with one clean, stable entry per opcode. The strings are borrowed from + // static storage, so the pvector owns no elements. +#define C6X_MNEMS(tbl) \ + do { \ + for (size_t i = 0; i < RZ_ARRAY_SIZE(tbl); i++) { \ + if ((tbl)[i].mnem) { \ + rz_pvector_push(v, (void *)(tbl)[i].mnem); \ + } \ + } \ + } while (0) + C6X_MNEMS(c6x_l_rows); + C6X_MNEMS(c6x_s_fp_rows); + C6X_MNEMS(c6x_d_ext_rows); + C6X_MNEMS(c6x_s_rows); + C6X_MNEMS(c6x_m_rows); + C6X_MNEMS(c6x_m_ext_rows); + C6X_MNEMS(c6x_m_unary_rows); + C6X_MNEMS(c6x_m_cplx_rows); + C6X_MNEMS(c6x_m_wide_rows); + C6X_MNEMS(c6x_s_ext_rows); + C6X_MNEMS(c6x_l_unary_rows); + C6X_MNEMS(c6x_d_rows); + C6X_MNEMS(c6x_dext_rows); + C6X_MNEMS(c6x_mem_rows); +#undef C6X_MNEMS + static const char *synthesized[] = { + "add", "addaw", "addk", "addkpc", "b", "bdec", "bnop", "bpos", + "callp", "cmpeq", "idle", "lddw", "ldndw", "ldnw", "ldw", "mv", + "mvc", "mvk", "mvkh", "neg", "nop", "not", "sadd", "sshl", + "ssub", "stdw", "stndw", "stnw", "stw", "sub", "xor", "zero" + }; + for (size_t i = 0; i < RZ_ARRAY_SIZE(synthesized); i++) { + rz_pvector_push(v, (void *)synthesized[i]); + } + rz_pvector_sort(v, c6x_mnem_cmp, NULL); + for (size_t i = 1; i < rz_pvector_len(v);) { + if (!strcmp((const char *)rz_pvector_at(v, i - 1), (const char *)rz_pvector_at(v, i))) { + rz_pvector_remove_at(v, i); + } else { + i++; + } + } + return v; +} diff --git a/librz/arch/isa/tms320/c6x/c6x_format.c b/librz/arch/isa/tms320/c6x/c6x_format.c new file mode 100644 index 00000000000..330cca99d97 --- /dev/null +++ b/librz/arch/isa/tms320/c6x/c6x_format.c @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "c6x.h" + +// Pure consumer of a decoded C6xInsn: emits the TI-style assembly text. The +// decoder has already resolved every field, so this only concerns syntax. + +// Predicate register named by the creg field (SPRU733 Table 3-9). A0 (creg 110) +// is a C64x+ addition; naming it on older parts is harmless as the encoding is +// otherwise reserved there. +static const char *pred_reg(ut8 creg) { + static const char *const names[8] = { + NULL, "b0", "b1", "b2", "a1", "a2", "a0", NULL + }; + return names[creg & 7]; +} + +static void fmt_reg(RzStrBuf *sb, ut8 side, ut8 num) { + rz_strbuf_appendf(sb, "%c%u", side ? 'b' : 'a', num); +} + +// Register pair is written odd:even (high:low), e.g. a1:a0. +static void fmt_regpair(RzStrBuf *sb, ut8 side, ut8 num) { + char c = side ? 'b' : 'a'; + rz_strbuf_appendf(sb, "%c%u:%c%u", c, (num | 1u), c, (num & ~1u)); +} + +// Register quad is written high-to-low over four aligned registers, e.g. +// a7:a6:a5:a4 (base is a multiple of four). +static void fmt_regquad(RzStrBuf *sb, ut8 side, ut8 num) { + char c = side ? 'b' : 'a'; + num &= ~3u; + rz_strbuf_appendf(sb, "%c%u:%c%u:%c%u:%c%u", c, num + 3, c, num + 2, c, num + 1, c, num); +} + +// Memory operand: base register with the addressing-mode decoration and either a +// register or (scaled) constant offset (SPRU733 Table 3-11). +static void fmt_mem(RzStrBuf *sb, const C6xOperand *o) { + const char *pre = "", *post = "", *sign = "+"; + bool reg = false; + switch (o->mode) { + case C6X_AM_NEG_CST: sign = "-"; break; + case C6X_AM_POS_CST: sign = "+"; break; + case C6X_AM_NEG_REG: sign = "-", reg = true; break; + case C6X_AM_POS_REG: sign = "+", reg = true; break; + case C6X_AM_PREDEC_CST: pre = "--"; break; + case C6X_AM_PREINC_CST: pre = "++"; break; + case C6X_AM_POSTDEC_CST: post = "--"; break; + case C6X_AM_POSTINC_CST: post = "++"; break; + case C6X_AM_PREDEC_REG: pre = "--", reg = true; break; + case C6X_AM_PREINC_REG: pre = "++", reg = true; break; + case C6X_AM_POSTDEC_REG: post = "--", reg = true; break; + case C6X_AM_POSTINC_REG: post = "++", reg = true; break; + default: break; + } + // plain positive constant offset of zero collapses to *baseR + bool plain = (o->mode == C6X_AM_POS_CST && o->off_cst == 0); + rz_strbuf_append(sb, "*"); + if (pre[0]) { + rz_strbuf_append(sb, pre); + } else if (!post[0] && !plain) { + rz_strbuf_append(sb, sign); + } + fmt_reg(sb, o->base_side, o->base); + if (post[0]) { + rz_strbuf_append(sb, post); + } + if (plain) { + return; + } + rz_strbuf_append(sb, "["); + if (reg) { + fmt_reg(sb, o->base_side, o->off_reg); + } else { + rz_strbuf_appendf(sb, "0x%x", o->off_cst); + } + rz_strbuf_append(sb, "]"); +} + +static void fmt_operand(RzStrBuf *sb, const C6xInsn *insn, ut64 pc, const C6xOperand *o) { + switch (o->kind) { + case C6X_OP_REG: + fmt_reg(sb, o->side, o->num); + break; + case C6X_OP_REGPAIR: + fmt_regpair(sb, o->side, o->num); + break; + case C6X_OP_REGQUAD: + fmt_regquad(sb, o->side, o->num); + break; + case C6X_OP_IMM: + // NOP cycle counts, bit counts, and short signed constants read best + // in decimal; wider move/address immediates in hex. + if (o->imm_dec || o->imm < 0 || (insn->unit == C6X_UNIT_NONE)) { + rz_strbuf_appendf(sb, "%" PFMT64d, (st64)o->imm); + } else { + rz_strbuf_appendf(sb, "0x%" PFMT64x, (ut64)o->imm); + } + break; + case C6X_OP_CTRLREG: + rz_strbuf_append(sb, o->name ? o->name : "?"); + break; + case C6X_OP_MEM: + fmt_mem(sb, o); + break; + case C6X_OP_PCREL: + // Branch displacements are relative to the fetch-packet base (PCE1), + // i.e. the address masked to the 32-byte packet, not the instruction. + rz_strbuf_appendf(sb, "0x%" PFMT64x, (ut64)((pc & ~(ut64)0x1f) + o->imm)); + break; + default: + break; + } +} + +RZ_IPI RZ_OWN char *c6x_format(const C6xArchDesc *desc, const C6xInsn *insn, ut64 pc) { + rz_return_val_if_fail(desc && insn, NULL); + RzStrBuf *sb = rz_strbuf_new(""); + if (!sb) { + return NULL; + } + // Compact fetch-packet header (SPRUFE8 3.10.2): render the layout (which + // of the seven words hold two 16-bit compact instructions) and the + // expansion field (protection, register set, LD/ST data sizes, branch, + // saturation). None of the predicate/parallel framing applies here. + if (insn->is_header) { + ut32 w = insn->word; + // Table 3-15: primary/secondary LD/ST data size from DSZ bits 18:16. + static const char *sec_dsz[8] = { "bu", "b", "hu", "h", "w", "b", "nw", "h" }; + ut8 dsz = (w >> 16) & 0x7; + rz_strbuf_appendf(sb, ".fphead %s, %s, %s, %s, %s, %s, ", + (w >> 20) & 1 ? "p" : "n", // PROT: protected loads + (w >> 19) & 1 ? "h" : "l", // RS: high/low register set + dsz < 4 ? "w" : "dw", // primary data size + sec_dsz[dsz], // secondary data size + (w >> 15) & 1 ? "br" : "nobr", // BR: S-unit compact are branches + (w >> 14) & 1 ? "sat" : "nosat"); // SAT: compact saturate + for (int b = 27; b >= 21; b--) { // layout field L7..L1 + rz_strbuf_append(sb, (w >> b) & 1 ? "1" : "0"); + } + rz_strbuf_append(sb, "b"); + return rz_strbuf_drain(sb); + } + if (insn->cont) { + rz_strbuf_append(sb, "|| "); + } + const char *pr = pred_reg(insn->creg); + if (pr) { + rz_strbuf_appendf(sb, "[%s%s] ", insn->z ? "!" : "", pr); + } + if (!insn->mnemonic) { + rz_strbuf_append(sb, "invalid"); + return rz_strbuf_drain(sb); + } + rz_strbuf_append(sb, insn->mnemonic); + if (insn->unit != C6X_UNIT_NONE) { + char u = insn->unit == C6X_UNIT_L ? 'l' : insn->unit == C6X_UNIT_S ? 's' + : insn->unit == C6X_UNIT_M ? 'm' + : 'd'; + rz_strbuf_appendf(sb, " .%c%u%s", u, insn->unit_side, insn->cross ? "x" : ""); + } + for (ut8 i = 0; i < insn->nops; i++) { + rz_strbuf_append(sb, i == 0 ? " " : ","); + fmt_operand(sb, insn, pc, &insn->ops[i]); + } + return rz_strbuf_drain(sb); +} diff --git a/librz/arch/isa/tms320/c6x/c6x_il.c b/librz/arch/isa/tms320/c6x/c6x_il.c new file mode 100644 index 00000000000..14ab9242c40 --- /dev/null +++ b/librz/arch/isa/tms320/c6x/c6x_il.c @@ -0,0 +1,1763 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file c6x_il.c + * TMS320C6000 RzIL lifting. + * + * Lifts a decoded \ref C6xInsn (from c6x_decode.c) to RzIL: the register / IL-VM + * configuration and the per-instruction lifter wired into the analysis op via + * op->il_op. Kept separate from the disassembler so decode and IL are + * independent. The scalar move / ALU / shift / multiply / load / store core is + * lifted; forms not yet handled return NULL and simply leave op->il_op unset. + * + * Two architectural notes bound what is modelled here. C6000 is a VLIW: every + * instruction of an execute packet reads its sources at the packet start and + * writes at its end, so within a packet an instruction never sees another's + * result. The RzIL VM steps sequentially, so a "|| mv a,b || mv b,a" swap is + * not modelled exactly; ordinary (non-swapping) parallel code lifts correctly. + * Branches have five delay slots and are left unlifted for now, so IL covers + * straight-line data flow. + */ + +#include +#include "c6x.h" + +/* RzIL VM */ + +// IL-VM register bindings: the 32+32 general-purpose registers (a0-a31, +// b0-b31), pce1 (the program counter, named as the register profile does), then +// the control registers MVC reaches -- the set common to every C6000, followed +// by the C64x+ extension. A single flat table doubles as the name lookup for a +// general-register operand: side A occupies indices 0-31 and side B 32-63, so +// c6x_reg_name(side, num) indexes it directly and never reaches pce1 or the +// control registers past it. Names are borrowed by the IL ops (never copied), +// so the table must outlive them -- hence static const. +static const char *const c6x_il_regs[] = { + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", + "a16", "a17", "a18", "a19", "a20", "a21", "a22", "a23", + "a24", "a25", "a26", "a27", "a28", "a29", "a30", "a31", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", + "b8", "b9", "b10", "b11", "b12", "b13", "b14", "b15", + "b16", "b17", "b18", "b19", "b20", "b21", "b22", "b23", + "b24", "b25", "b26", "b27", "b28", "b29", "b30", "b31", + "pce1", + // control registers common to all C6000 variants + "amr", "csr", "gfpgfr", "icr", "ier", "ifr", "irp", "isr", "istp", "nrp", + // C64x+ control-register extension + "dier", "dnum", "ecr", "efr", "gplya", "gplyb", "ierr", "ilc", "itsr", + "ntsr", "rep", "rilc", "ssr", "tsch", "tscl", "tsr", + NULL +}; + +// C62x/C67x expose only the low 16 registers per side and lack the C64x+ +// control-register extension; their register profile omits a16-a31/b16-b31 and +// that extension, so bind only the matching subset -- the IL-VM setup fails if +// asked to bind a register the profile never declares. +static const char *const c6x_il_regs16[] = { + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", + "b8", "b9", "b10", "b11", "b12", "b13", "b14", "b15", + "pce1", + "amr", "csr", "gfpgfr", "icr", "ier", "ifr", "irp", "isr", "istp", "nrp", + NULL +}; + +RZ_IPI RzAnalysisILConfig *tms320_c6x_il_config(RZ_NONNULL RzAnalysis *analysis) { + rz_return_val_if_fail(analysis, NULL); + // 32-bit PC over a byte-addressed space, little-endian. + RzAnalysisILConfig *cfg = rz_analysis_il_config_new(32, false, 32); + if (!cfg) { + return NULL; + } + const C6xArchDesc *desc = c6x_desc_from_cpu(rz_analysis_get_cpu(analysis)); + cfg->reg_bindings = (const char **)((desc && desc->num_regs <= 16) ? c6x_il_regs16 : c6x_il_regs); + return cfg; +} + +#include + +// Global-variable name of register `num` on side `side` (0 = A, 1 = B). +static const char *c6x_reg_name(ut8 side, ut8 num) { + return c6x_il_regs[(side & 1) * 32 + (num & 31)]; +} + +// Predicate register named by the creg field (SPRU733 Table 3-9), or NULL when +// unconditional. Mirrors the disassembler's pred_reg(). +static const char *c6x_pred_name(ut8 creg) { + static const char *const names[8] = { + NULL, "b0", "b1", "b2", "a1", "a2", "a0", NULL + }; + return names[creg & 7]; +} + +// 32-bit value of a register or immediate source operand, or NULL for kinds +// this core does not model as a scalar (pairs, quads, control regs, memory). +static RzILOpPure *c6x_src(const C6xOperand *o) { + switch (o->kind) { + case C6X_OP_REG: + return VARG(c6x_reg_name(o->side, o->num)); + case C6X_OP_IMM: + return S32(o->imm); + default: + return NULL; + } +} + +// Write `v` to a register destination, or free it and fail for other kinds. +static RzILOpEffect *c6x_wr(const C6xOperand *o, RzILOpPure *v) { + if (o->kind == C6X_OP_REG) { + return SETG(c6x_reg_name(o->side, o->num), v); + } + rz_il_op_pure_free(v); + return NULL; +} + +// dst = src1 OP src2, the shared shape of the register/immediate ALU ops. `mk` +// builds the value from the two decoded sources. +static RzILOpEffect *c6x_alu3(const C6xInsn *insn, rz_il_pure_2args_op *mk) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], mk(s1, s2)); +} + +// dst = (src1 CMP src2) ? 1 : 0, the shared shape of the compare ops. +static RzILOpEffect *c6x_cmp(const C6xInsn *insn, rz_il_bool_2args_op *mk) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], ITE(mk(s1, s2), U32(1), U32(0))); +} + +// Low 16 bits of `v`, sign-extended back to 32 -- the operand form shared by +// the 16x16 multiplies and the *L half selectors. +static RzILOpPure *c6x_low16s(RzILOpPure *v) { + return SIGNED(32, UNSIGNED(16, v)); +} + +// High 16 bits of `v`, sign-extended to 32. +static RzILOpPure *c6x_high16s(RzILOpPure *v) { + return SIGNED(32, UNSIGNED(16, SHIFTR0(v, U32(16)))); +} + +// Clamp a 32-bit signed value to the signed n-bit range, returned as n bits -- +// the saturation shared by the saturating packed ops. The wide value is bound +// once so both bound checks and the pass-through see the same operand. +static RzILOpBitVector *c6x_sat_s(RzILOpPure *x, ut8 n) { + st64 hi = (1LL << (n - 1)) - 1; + st64 lo = -(1LL << (n - 1)); + return LET("_sat", x, + ITE(SGT(VARLP("_sat"), S32(hi)), SN(n, hi), + ITE(SLT(VARLP("_sat"), S32(lo)), SN(n, lo), + UNSIGNED(n, VARLP("_sat"))))); +} + +// Absolute value of one signed 16-bit lane, saturating 0x8000 to 0x7fff (it is +// its own negation). The lane is bound once so the sign test, the endpoint test +// and the negation all see it. +static RzILOpBitVector *c6x_abs16(RzILOpBitVector *lane) { + return LET("_h", lane, + ITE(SGE(VARLP("_h"), SN(16, 0)), VARLP("_h"), + ITE(EQ(VARLP("_h"), UN(16, 0x8000)), UN(16, 0x7fff), NEG(VARLP("_h"))))); +} + +// Packed add/sub (ADD2/SUB2 over two halfwords, ADD4/SUB4 over four bytes): +// `lanes` equal-width lanes computed independently, with no carry across a lane +// boundary -- RzIL ADD/SUB is modular at the operand width, so each lane wraps +// on its own. APPEND stacks the lanes back with the highest on top. +static RzILOpEffect *c6x_packed(const C6xInsn *insn, ut8 lanes, bool sub) { + ut8 width = 32 / lanes; + RzILOpBitVector *acc = NULL; + for (ut8 i = 0; i < lanes; i++) { + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + rz_il_op_pure_free(acc); + return NULL; + } + RzILOpBitVector *la = UNSIGNED(width, i ? SHIFTR0(a, U32(i * width)) : a); + RzILOpBitVector *lb = UNSIGNED(width, i ? SHIFTR0(b, U32(i * width)) : b); + RzILOpBitVector *lane = sub ? SUB(la, lb) : ADD(la, lb); + acc = acc ? APPEND(lane, acc) : lane; + } + return c6x_wr(&insn->ops[2], acc); +} + +// Packed signed saturating add/sub (SADD2/SSUB2): each 16-bit lane is summed in +// 32-bit signed and clamped back to the signed 16-bit range, so a lane that +// overflows sticks at the endpoint instead of wrapping. +static RzILOpEffect *c6x_sat_packed(const C6xInsn *insn, ut8 lanes, bool sub) { + ut8 width = 32 / lanes; + RzILOpBitVector *acc = NULL; + for (ut8 i = 0; i < lanes; i++) { + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + rz_il_op_pure_free(acc); + return NULL; + } + RzILOpBitVector *la = SIGNED(32, UNSIGNED(width, i ? SHIFTR0(a, U32(i * width)) : a)); + RzILOpBitVector *lb = SIGNED(32, UNSIGNED(width, i ? SHIFTR0(b, U32(i * width)) : b)); + RzILOpBitVector *lane = c6x_sat_s(sub ? SUB(la, lb) : ADD(la, lb), width); + acc = acc ? APPEND(lane, acc) : lane; + } + return c6x_wr(&insn->ops[2], acc); +} + +// Packed per-lane min/max (MIN2/MAX2 signed halfwords, MINU4/MAXU4 unsigned +// bytes). Each lane compares src1 against src2 (signed or unsigned) and keeps +// the smaller or larger; the compared value is duplicated so the winning lane +// is still available to write. +static RzILOpEffect *c6x_minmax(const C6xInsn *insn, ut8 lanes, bool sgned, bool is_min) { + ut8 width = 32 / lanes; + RzILOpBitVector *acc = NULL; + for (ut8 i = 0; i < lanes; i++) { + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + rz_il_op_pure_free(acc); + return NULL; + } + RzILOpBitVector *la = UNSIGNED(width, i ? SHIFTR0(a, U32(i * width)) : a); + RzILOpBitVector *lb = UNSIGNED(width, i ? SHIFTR0(b, U32(i * width)) : b); + RzILOpBool *le = sgned ? SLE(DUP(la), DUP(lb)) : ULE(DUP(la), DUP(lb)); + // min keeps src1 when src1 <= src2; max keeps src2 in that case + RzILOpBitVector *lane = is_min ? ITE(le, la, lb) : ITE(le, lb, la); + acc = acc ? APPEND(lane, acc) : lane; + } + return c6x_wr(&insn->ops[2], acc); +} + +// Four-way byte dot product: sum of the four byte-lane products into a 32-bit +// result. `s1_signed` sign-extends src1's bytes (DOTPSU4); src2's bytes are +// always unsigned, as is DOTPU4's src1. +static RzILOpEffect *c6x_dotp4(const C6xInsn *insn, bool s1_signed) { + RzILOpBitVector *acc = NULL; + for (ut8 i = 0; i < 4; i++) { + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + rz_il_op_pure_free(acc); + return NULL; + } + RzILOpBitVector *ba = UNSIGNED(8, i ? SHIFTR0(a, U32(i * 8)) : a); + RzILOpBitVector *bb = UNSIGNED(8, i ? SHIFTR0(b, U32(i * 8)) : b); + RzILOpBitVector *prod = MUL(s1_signed ? SIGNED(32, ba) : UNSIGNED(32, ba), UNSIGNED(32, bb)); + acc = acc ? ADD(acc, prod) : prod; + } + return c6x_wr(&insn->ops[2], acc); +} + +// Packed compare to a bit-per-lane mask (CMPEQ2/CMPGT2 halfwords, CMPEQ4/CMPGTU4 +// bytes): lane i sets bit i when it satisfies `mk`, and the mask is +// zero-extended to 32 bits. The comparison reads each lane at its own width, so +// SGT/UGT there give the signed or unsigned relation the mnemonic wants. +static RzILOpEffect *c6x_cmp_packed(const C6xInsn *insn, ut8 lanes, rz_il_bool_2args_op *mk) { + ut8 width = 32 / lanes; + RzILOpBitVector *mask = NULL; + for (ut8 i = 0; i < lanes; i++) { + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + rz_il_op_pure_free(mask); + return NULL; + } + RzILOpBitVector *la = UNSIGNED(width, i ? SHIFTR0(a, U32(i * width)) : a); + RzILOpBitVector *lb = UNSIGNED(width, i ? SHIFTR0(b, U32(i * width)) : b); + RzILOpBitVector *bit = ITE(mk(la, lb), UN(1, 1), UN(1, 0)); + mask = mask ? APPEND(bit, mask) : bit; + } + return c6x_wr(&insn->ops[2], UNSIGNED(32, mask)); +} + +// A binary single-precision float constructor (fadd/fsub/fmul), mirroring the +// pure/bool 2-arg op typedefs for the shared shape of the float arithmetic. +typedef RzILOpFloat *(c6x_fbin_op)(RzFloatRMode, RzILOpFloat *, RzILOpFloat *); + +// Single-precision float op: read src1/src2 as binary32, apply `mk` rounding to +// nearest-even (the C6000 reset default), and write the result's bits back. +static RzILOpEffect *c6x_fsp(const C6xInsn *insn, c6x_fbin_op *mk) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], F2BV(mk(RZ_FLOAT_RMODE_RNE, FLOATV32(s1), FLOATV32(s2)))); +} + +// Read a register pair as one 64-bit value: the even member is the low word and +// the odd member the high word, matching how LDDW loads a doubleword. +static RzILOpBitVector *c6x_pair64(const C6xOperand *o) { + if (o->kind != C6X_OP_REGPAIR) { + return NULL; + } + return APPEND(VARG(c6x_reg_name(o->side, o->num + 1)), VARG(c6x_reg_name(o->side, o->num))); +} + +// Write a 64-bit value into a register pair (low word -> even, high -> odd), +// binding it to a local so both halves read the same result. +static RzILOpEffect *c6x_wr_pair64(const C6xOperand *o, RzILOpBitVector *v) { + if (o->kind != C6X_OP_REGPAIR) { + rz_il_op_pure_free(v); + return NULL; + } + return SEQ3(SETL("_d", v), + SETG(c6x_reg_name(o->side, o->num), UNSIGNED(32, VARL("_d"))), + SETG(c6x_reg_name(o->side, o->num + 1), UNSIGNED(32, SHIFTR0(VARL("_d"), U32(32))))); +} + +// Read a 40-bit long from a register pair, sign-extended from bit 39 to 64 bits +// (the even register holds bits [31:0], the odd register bits [39:32]). +static RzILOpBitVector *c6x_long40(const C6xOperand *o) { + RzILOpBitVector *p = c6x_pair64(o); + if (!p) { + return NULL; + } + return SHIFTRA(SHIFTL0(p, U32(24)), U32(24)); +} + +// Double-precision float op over register pairs (binary64, round to nearest +// even). +static RzILOpEffect *c6x_fdp(const C6xInsn *insn, c6x_fbin_op *mk) { + RzILOpBitVector *a = c6x_pair64(&insn->ops[0]); + RzILOpBitVector *b = c6x_pair64(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + return NULL; + } + return c6x_wr_pair64(&insn->ops[2], F2BV(mk(RZ_FLOAT_RMODE_RNE, FLOATV64(a), FLOATV64(b)))); +} + +// The high or low halfword of a source, as a 16-bit value -- the halfword +// selector shared by the PACK2 family. +static RzILOpBitVector *c6x_half(const C6xOperand *o, bool high) { + RzILOpPure *s = c6x_src(o); + return s ? UNSIGNED(16, high ? SHIFTR0(s, U32(16)) : s) : NULL; +} + +// dst = (halfword of src1) : (halfword of src2); PACK2/PACKH2/PACKLH2/PACKHL2 +// differ only in which halfword each source contributes. +static RzILOpEffect *c6x_pack(const C6xInsn *insn, bool hi_from_high, bool lo_from_high) { + RzILOpBitVector *hi = c6x_half(&insn->ops[0], hi_from_high); + RzILOpBitVector *lo = c6x_half(&insn->ops[1], lo_from_high); + if (!hi || !lo) { + rz_il_op_pure_free(hi); + rz_il_op_pure_free(lo); + return NULL; + } + return c6x_wr(&insn->ops[2], APPEND(hi, lo)); +} + +// Byte `idx` of a source, zero-extended to 16 bits -- the lane selector shared +// by the UNPK*U4 unpacks. +static RzILOpBitVector *c6x_byte_zx16(const C6xOperand *o, ut8 idx) { + RzILOpPure *s = c6x_src(o); + return s ? UNSIGNED(16, UNSIGNED(8, idx ? SHIFTR0(s, U32(idx * 8)) : s)) : NULL; +} + +// dst = zero-extend two bytes of src to two halfwords. UNPKLU4 takes bytes 0,1; +// UNPKHU4 takes bytes 2,3. The lower byte lands in the lower halfword. +static RzILOpEffect *c6x_unpku4(const C6xInsn *insn, bool high) { + ut8 base = high ? 2 : 0; + RzILOpBitVector *hw0 = c6x_byte_zx16(&insn->ops[0], base); + RzILOpBitVector *hw1 = c6x_byte_zx16(&insn->ops[0], base + 1); + if (!hw0 || !hw1) { + rz_il_op_pure_free(hw0); + rz_il_op_pure_free(hw1); + return NULL; + } + return c6x_wr(&insn->ops[1], APPEND(hw1, hw0)); +} + +// Effective address of a memory operand. When the mode modifies the base +// register, *wb receives that update and *pre says whether it applies before +// the access (pre-increment) or after it (post-increment). `bytes` scales a +// register/constant offset written with the scaled (bracket) syntax. +static RzILOpPure *c6x_ea(const C6xOperand *m, ut8 bytes, RzILOpEffect **wb, bool *pre) { + *wb = NULL; + *pre = false; + const char *base = c6x_reg_name(m->base_side, m->base); + ut32 scale = m->scaled ? bytes : 1; + bool reg_off = false, dec = false, modify = false, ispre = false; + switch (m->mode) { + case C6X_AM_NEG_REG: + reg_off = true; + dec = true; + break; + case C6X_AM_POS_REG: reg_off = true; break; + case C6X_AM_NEG_CST: dec = true; break; + case C6X_AM_POS_CST: + case C6X_AM_BASE_LONG: break; + case C6X_AM_PREDEC_REG: + reg_off = true; + dec = true; + modify = true; + ispre = true; + break; + case C6X_AM_PREINC_REG: + reg_off = true; + modify = true; + ispre = true; + break; + case C6X_AM_POSTDEC_REG: + reg_off = true; + dec = true; + modify = true; + break; + case C6X_AM_POSTINC_REG: + reg_off = true; + modify = true; + break; + case C6X_AM_PREDEC_CST: + dec = true; + modify = true; + ispre = true; + break; + case C6X_AM_PREINC_CST: + modify = true; + ispre = true; + break; + case C6X_AM_POSTDEC_CST: + dec = true; + modify = true; + break; + case C6X_AM_POSTINC_CST: modify = true; break; + default: + return NULL; + } + RzILOpPure *off = reg_off + ? (scale > 1 ? MUL(VARG(c6x_reg_name(m->base_side, m->off_reg)), U32(scale)) : VARG(c6x_reg_name(m->base_side, m->off_reg))) + : U32(m->off_cst * scale); + if (modify) { + // pre/post modify: base <- base +/- off; EA reads the base, before the + // update for pre-modify and (unchanged) after it for post-modify. + *wb = SETG(base, dec ? SUB(VARG(base), off) : ADD(VARG(base), off)); + *pre = ispre; + return VARG(base); + } + return dec ? SUB(VARG(base), off) : ADD(VARG(base), off); +} + +// Order a memory access against its base write-back: pre-modify updates first, +// post-modify updates after, and a plain offset has no write-back. +static RzILOpEffect *c6x_mem_seq(RzILOpEffect *access, RzILOpEffect *wb, bool pre) { + if (!wb) { + return access; + } + return pre ? SEQ2(wb, access) : SEQ2(access, wb); +} + +// Load: dst = extend(mem[EA]). `sign` selects sign- vs zero-extension to 32. +static RzILOpEffect *c6x_load(const C6xInsn *insn, ut8 bytes, bool sign) { + if (insn->ops[1].kind != C6X_OP_REG || bytes > 4) { + return NULL; // single-register widths only; pairs use c6x_load_pair + } + RzILOpEffect *wb; + bool pre; + RzILOpPure *ea = c6x_ea(&insn->ops[0], bytes, &wb, &pre); + if (!ea) { + return NULL; + } + RzILOpPure *val = bytes == 4 ? LOADW(32, ea) : (sign ? SIGNED(32, LOADW(bytes * 8, ea)) : UNSIGNED(32, LOADW(bytes * 8, ea))); + RzILOpEffect *set = SETG(c6x_reg_name(insn->ops[1].side, insn->ops[1].num), val); + return c6x_mem_seq(set, wb, pre); +} + +// Store: mem[EA] = truncate(src). Pairs use c6x_store_pair. +static RzILOpEffect *c6x_store(const C6xInsn *insn, ut8 bytes) { + if (insn->ops[0].kind != C6X_OP_REG || bytes > 4) { + return NULL; + } + RzILOpEffect *wb; + bool pre; + RzILOpPure *ea = c6x_ea(&insn->ops[1], bytes, &wb, &pre); + if (!ea) { + return NULL; + } + RzILOpPure *src = VARG(c6x_reg_name(insn->ops[0].side, insn->ops[0].num)); + RzILOpEffect *st = bytes == 4 ? STOREW(ea, src) : STOREW(ea, UNSIGNED(bytes * 8, src)); + return c6x_mem_seq(st, wb, pre); +} + +// Doubleword load into a register pair: the even member gets mem[EA] and the +// odd member mem[EA+4] (little-endian keeps the low word at the lower address). +// The address is bound to a local so both accesses share one base computation. +// LDNDW behaves the same here; only its alignment requirement differs. +static RzILOpEffect *c6x_load_pair(const C6xInsn *insn) { + if (insn->ops[1].kind != C6X_OP_REGPAIR) { + return NULL; + } + RzILOpEffect *wb; + bool pre; + RzILOpPure *ea = c6x_ea(&insn->ops[0], 8, &wb, &pre); + if (!ea) { + return NULL; + } + const char *lo = c6x_reg_name(insn->ops[1].side, insn->ops[1].num); + const char *hi = c6x_reg_name(insn->ops[1].side, insn->ops[1].num + 1); + RzILOpEffect *body = SEQ3(SETL("ea", ea), + SETG(lo, LOADW(32, VARL("ea"))), + SETG(hi, LOADW(32, ADD(VARL("ea"), U32(4))))); + return c6x_mem_seq(body, wb, pre); +} + +// Doubleword store from a register pair: mem[EA] <- even member, mem[EA+4] <- +// odd member. STDW and the non-aligned STNDW share this data path. +static RzILOpEffect *c6x_store_pair(const C6xInsn *insn) { + if (insn->ops[0].kind != C6X_OP_REGPAIR) { + return NULL; + } + RzILOpEffect *wb; + bool pre; + RzILOpPure *ea = c6x_ea(&insn->ops[1], 8, &wb, &pre); + if (!ea) { + return NULL; + } + const char *lo = c6x_reg_name(insn->ops[0].side, insn->ops[0].num); + const char *hi = c6x_reg_name(insn->ops[0].side, insn->ops[0].num + 1); + RzILOpEffect *body = SEQ3(SETL("ea", ea), + STOREW(VARL("ea"), VARG(lo)), + STOREW(ADD(VARL("ea"), U32(4)), VARG(hi))); + return c6x_mem_seq(body, wb, pre); +} + +// One 16-bit half of `v` widened to 32 bits: the high or low halfword, sign- or +// zero-extended per the multiply variant. +static RzILOpPure *c6x_half16(RzILOpPure *v, bool high, bool sign) { + RzILOpBitVector *h = UNSIGNED(16, high ? SHIFTR0(v, U32(16)) : v); + return sign ? SIGNED(32, h) : UNSIGNED(32, h); +} + +// dst = half16(src1) x half16(src2), the shared shape of the 16x16 multiplies. +// Each half is widened to 32 bits by its own signedness; the true product fits +// in 32 bits, so the modular MUL yields the exact result for every sign mix. +static RzILOpEffect *c6x_mpy16(const C6xInsn *insn, bool s1_hi, bool s1_sgn, bool s2_hi, bool s2_sgn) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], MUL(c6x_half16(s1, s1_hi, s1_sgn), c6x_half16(s2, s2_hi, s2_sgn))); +} + +// dst = sat((signed half16(src1) x signed half16(src2)) << 1). The doubled +// product overflows 32-bit signed only for (-0x8000)^2, whose product is +// 0x40000000; that one case saturates to 0x7fffffff, every other doubles in place. +static RzILOpEffect *c6x_smpy(const C6xInsn *insn, bool s1_hi, bool s2_hi) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpBitVector *p = MUL(c6x_half16(s1, s1_hi, true), c6x_half16(s2, s2_hi, true)); + return c6x_wr(&insn->ops[2], + LET("_p", p, + ITE(EQ(VARLP("_p"), U32(0x40000000)), U32(0x7fffffff), SHIFTL0(VARLP("_p"), U32(1))))); +} + +// 32x32 multiply, signedness per source. A pair dst takes the full 64-bit +// product (each source widened to 64 by its signedness); a single dst keeps the +// low 32 bits, which are signedness-independent. +static RzILOpEffect *c6x_mpy32(const C6xInsn *insn, bool s1_sgn, bool s2_sgn) { + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + const C6xOperand *dst = &insn->ops[2]; + if (dst->kind == C6X_OP_REGPAIR) { + RzILOpBitVector *w1 = s1_sgn ? SIGNED(64, s1) : UNSIGNED(64, s1); + RzILOpBitVector *w2 = s2_sgn ? SIGNED(64, s2) : UNSIGNED(64, s2); + return c6x_wr_pair64(dst, MUL(w1, w2)); + } + return c6x_wr(dst, MUL(s1, s2)); +} + +// Clamp a 64-bit signed intermediate to the 32-bit signed range and return the +// 32-bit result -- the saturation shared by SADD/SSUB/SSHL, which compute the +// exact result in 64 bits first so the overflow the 32-bit op would hide is +// visible to the bound checks. +static RzILOpBitVector *c6x_sats32(RzILOpBitVector *x) { + return LET("_s", x, + ITE(SGT(VARLP("_s"), SN(64, 0x7fffffff)), U32(0x7fffffff), + ITE(SLT(VARLP("_s"), SN(64, -0x80000000LL)), U32(0x80000000), + UNSIGNED(32, VARLP("_s"))))); +} + +// Count leading zero bits of a 32-bit value (clz(0) == 32), by binary search: +// each stage tests whether the current high bits are all zero and, if so, shifts +// the value up and contributes that stage's weight. The value is threaded through +// LET bindings and the per-stage weights summed at the end. +static RzILOpPure *c6x_clz32(RzILOpPure *x) { +#define HI_ZERO(vn, sh) IS_ZERO(SHIFTR0(VARLP(vn), U32(sh))) + return LET("_z0", x, + LET("_z1", ITE(HI_ZERO("_z0", 16), SHIFTL0(VARLP("_z0"), U32(16)), VARLP("_z0")), + LET("_z2", ITE(HI_ZERO("_z1", 24), SHIFTL0(VARLP("_z1"), U32(8)), VARLP("_z1")), + LET("_z3", ITE(HI_ZERO("_z2", 28), SHIFTL0(VARLP("_z2"), U32(4)), VARLP("_z2")), + LET("_z4", ITE(HI_ZERO("_z3", 30), SHIFTL0(VARLP("_z3"), U32(2)), VARLP("_z3")), + LET("_z5", ITE(HI_ZERO("_z4", 31), SHIFTL0(VARLP("_z4"), U32(1)), VARLP("_z4")), + ADD(ADD(ADD(ADD(ADD(ITE(HI_ZERO("_z0", 16), U32(16), U32(0)), + ITE(HI_ZERO("_z1", 24), U32(8), U32(0))), + ITE(HI_ZERO("_z2", 28), U32(4), U32(0))), + ITE(HI_ZERO("_z3", 30), U32(2), U32(0))), + ITE(HI_ZERO("_z4", 31), U32(1), U32(0))), + ITE(HI_ZERO("_z5", 31), U32(1), U32(0))))))))); +#undef HI_ZERO +} + +// Count leading zero bits of a 64-bit value (clz(0) == 64); the 64-bit analogue +// of c6x_clz32, used for the 40-bit long NORM. +static RzILOpPure *c6x_clz64(RzILOpPure *x) { +#define HI_ZERO(vn, sh) IS_ZERO(SHIFTR0(VARLP(vn), U32(sh))) + return LET("_q0", x, + LET("_q1", ITE(HI_ZERO("_q0", 32), SHIFTL0(VARLP("_q0"), U32(32)), VARLP("_q0")), + LET("_q2", ITE(HI_ZERO("_q1", 48), SHIFTL0(VARLP("_q1"), U32(16)), VARLP("_q1")), + LET("_q3", ITE(HI_ZERO("_q2", 56), SHIFTL0(VARLP("_q2"), U32(8)), VARLP("_q2")), + LET("_q4", ITE(HI_ZERO("_q3", 60), SHIFTL0(VARLP("_q3"), U32(4)), VARLP("_q3")), + LET("_q5", ITE(HI_ZERO("_q4", 62), SHIFTL0(VARLP("_q4"), U32(2)), VARLP("_q4")), + LET("_q6", ITE(HI_ZERO("_q5", 63), SHIFTL0(VARLP("_q5"), U32(1)), VARLP("_q5")), + ADD(ADD(ADD(ADD(ADD(ADD(ITE(HI_ZERO("_q0", 32), U32(32), U32(0)), + ITE(HI_ZERO("_q1", 48), U32(16), U32(0))), + ITE(HI_ZERO("_q2", 56), U32(8), U32(0))), + ITE(HI_ZERO("_q3", 60), U32(4), U32(0))), + ITE(HI_ZERO("_q4", 62), U32(2), U32(0))), + ITE(HI_ZERO("_q5", 63), U32(1), U32(0))), + ITE(HI_ZERO("_q6", 63), U32(1), U32(0)))))))))); +#undef HI_ZERO +} + +// Reverse the 32 bits of a value (bit i becomes bit 31 - i), by the standard +// butterfly that swaps adjacent bits, then pairs, nibbles, bytes and halfwords. +static RzILOpPure *c6x_bitrev32(RzILOpPure *x) { +#define REVSTAGE(vn, s, m) LOGOR(SHIFTL0(LOGAND(VARLP(vn), U32(m)), U32(s)), LOGAND(SHIFTR0(VARLP(vn), U32(s)), U32(m))) + return LET("_r0", x, + LET("_r1", REVSTAGE("_r0", 1, 0x55555555), + LET("_r2", REVSTAGE("_r1", 2, 0x33333333), + LET("_r3", REVSTAGE("_r2", 4, 0x0f0f0f0f), + LET("_r4", REVSTAGE("_r3", 8, 0x00ff00ff), + REVSTAGE("_r4", 16, 0x0000ffff)))))); +#undef REVSTAGE +} + +// Per-byte population count (BITC4): each result byte holds the number of set +// bits in the matching src2 byte, via SWAR reduction stopped at the byte level. +static RzILOpPure *c6x_bitc4(RzILOpPure *x) { + return LET("_b0", x, + LET("_b1", SUB(VARLP("_b0"), LOGAND(SHIFTR0(VARLP("_b0"), U32(1)), U32(0x55555555))), + LET("_b2", ADD(LOGAND(VARLP("_b1"), U32(0x33333333)), LOGAND(SHIFTR0(VARLP("_b1"), U32(2)), U32(0x33333333))), + LOGAND(ADD(VARLP("_b2"), SHIFTR0(VARLP("_b2"), U32(4))), U32(0x0f0f0f0f))))); +} + +// Deinterleave (DEAL): the even bits of src2 are packed into the lower halfword +// of the result and the odd bits into the upper, each compacted by folding the +// kept bits down (the even chain works on src2, the odd chain on src2 >> 1). +static RzILOpPure *c6x_deal(RzILOpPure *x) { +#define CMPR(vn, sh, m) LOGAND(LOGOR(VARLP(vn), SHIFTR0(VARLP(vn), U32(sh))), U32(m)) + return LET("_dx", x, + LET("_e0", LOGAND(VARLP("_dx"), U32(0x55555555)), + LET("_e1", CMPR("_e0", 1, 0x33333333), + LET("_e2", CMPR("_e1", 2, 0x0f0f0f0f), + LET("_e3", CMPR("_e2", 4, 0x00ff00ff), + LET("_elo", CMPR("_e3", 8, 0x0000ffff), + LET("_o0", LOGAND(SHIFTR0(VARLP("_dx"), U32(1)), U32(0x55555555)), + LET("_o1", CMPR("_o0", 1, 0x33333333), + LET("_o2", CMPR("_o1", 2, 0x0f0f0f0f), + LET("_o3", CMPR("_o2", 4, 0x00ff00ff), + LET("_ohi", CMPR("_o3", 8, 0x0000ffff), + LOGOR(SHIFTL0(VARLP("_ohi"), U32(16)), VARLP("_elo"))))))))))))); +#undef CMPR +} + +// Interleave (SHFL): the lower halfword of src2 is spread into the even bit +// positions of the result and the upper halfword into the odd positions, each +// spread by folding the bits up (the high chain is then shifted left by one). +static RzILOpPure *c6x_shfl(RzILOpPure *x) { +#define SPRD(vn, sh, m) LOGAND(LOGOR(VARLP(vn), SHIFTL0(VARLP(vn), U32(sh))), U32(m)) + return LET("_sx", x, + LET("_l0", LOGAND(VARLP("_sx"), U32(0x0000ffff)), + LET("_l1", SPRD("_l0", 8, 0x00ff00ff), + LET("_l2", SPRD("_l1", 4, 0x0f0f0f0f), + LET("_l3", SPRD("_l2", 2, 0x33333333), + LET("_llo", SPRD("_l3", 1, 0x55555555), + LET("_h0", LOGAND(SHIFTR0(VARLP("_sx"), U32(16)), U32(0x0000ffff)), + LET("_h1", SPRD("_h0", 8, 0x00ff00ff), + LET("_h2", SPRD("_h1", 4, 0x0f0f0f0f), + LET("_h3", SPRD("_h2", 2, 0x33333333), + LET("_hhi", SPRD("_h3", 1, 0x55555555), + LOGOR(SHIFTL0(VARLP("_hhi"), U32(1)), VARLP("_llo"))))))))))))); +#undef SPRD +} + +// arithmetic (ADDA*/SUBA*). The addressing syntax lists the base (src2) first, +// so ops[0] is the base and ops[1] the index; scale is 0/1/2/3. +static RzILOpEffect *c6x_addr(const C6xInsn *insn, bool sub, ut8 scale) { + RzILOpPure *base = c6x_src(&insn->ops[0]); + RzILOpPure *off = c6x_src(&insn->ops[1]); + if (!base || !off) { + rz_il_op_pure_free(base); + rz_il_op_pure_free(off); + return NULL; + } + if (scale) { + off = SHIFTL0(off, U32(scale)); + } + return c6x_wr(&insn->ops[2], sub ? SUB(base, off) : ADD(base, off)); +} + +// Build the (unconditional) effect for one instruction, or NULL if the form is +// not lifted. Predication is applied by the caller. +static RzILOpEffect *c6x_lift_core(const C6xInsn *insn, ut64 pc) { + const char *m = insn->mnemonic; + if (!m) { + return NULL; + } + if (!strcmp(m, "nop") || !strcmp(m, "idle")) { + return NOP(); + } + if (!strcmp(m, "b") || !strcmp(m, "bnop") || !strcmp(m, "callp")) { + // Control transfer. The target is either a register or a PC-relative + // displacement off the fetch-packet-aligned address (PCE1). The five + // delay slots are not modelled -- the sequential VM takes the branch at + // once -- but the target is exact. Call forms (a link register in + // ops[1]) also record the return address, the word after the branch. + const C6xOperand *t = &insn->ops[0]; + RzILOpBitVector *tgt; + if (t->kind == C6X_OP_PCREL) { + tgt = U32((ut32)((pc & ~(ut64)0x1f) + t->imm)); + } else if (t->kind == C6X_OP_REG) { + tgt = VARG(c6x_reg_name(t->side, t->num)); + } else if (t->kind == C6X_OP_CTRLREG) { + tgt = VARG(t->name); // e.g. b irp / b nrp (return from interrupt) + } else { + return NULL; + } + if (insn->nops > 1 && insn->ops[1].kind == C6X_OP_REG) { + return SEQ2(SETG(c6x_reg_name(insn->ops[1].side, insn->ops[1].num), U32((ut32)(pc + insn->size))), JMP(tgt)); + } + return JMP(tgt); + } + if (!strcmp(m, "addkpc")) { + // dst = PCE1 (the fetch-packet-aligned address) + displacement; a pure + // address computation, no control transfer + return c6x_wr(&insn->ops[1], U32((ut32)((pc & ~(ut64)0x1f) + insn->ops[0].imm))); + } + if (!strcmp(m, "mv")) { + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], s) : NULL; + } + if (!strcmp(m, "mvd")) { + // the .M-unit move; its extra latency is timing, not data flow + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], s) : NULL; + } + if (!strcmp(m, "mvk")) { + // sign-extended 16-bit move (imm already extended by the decoder) + return c6x_wr(&insn->ops[1], S32(insn->ops[0].imm)); + } + if (!strcmp(m, "mvkh")) { + // replace the top 16 bits, keep the low 16 + const C6xOperand *d = &insn->ops[1]; + RzILOpPure *hi = U32(((ut64)(insn->ops[0].imm & 0xffff)) << 16); + RzILOpPure *lo = LOGAND(VARG(c6x_reg_name(d->side, d->num)), U32(0xffff)); + return c6x_wr(d, LOGOR(lo, hi)); + } + if (!strcmp(m, "mvc")) { + // move between a control register (named by the decoder) and a general + // register; a plain copy, so status-register side effects are not modelled + const C6xOperand *s = &insn->ops[0]; + const C6xOperand *d = &insn->ops[1]; + const char *sn = s->kind == C6X_OP_CTRLREG ? s->name : (s->kind == C6X_OP_REG ? c6x_reg_name(s->side, s->num) : NULL); + const char *dn = d->kind == C6X_OP_CTRLREG ? d->name : (d->kind == C6X_OP_REG ? c6x_reg_name(d->side, d->num) : NULL); + return sn && dn ? SETG(dn, VARG(sn)) : NULL; + } + if (!strcmp(m, "add") || !strcmp(m, "addu")) { + return c6x_alu3(insn, rz_il_op_new_add); + } + if (!strcmp(m, "sub") || !strcmp(m, "subu")) { + return c6x_alu3(insn, rz_il_op_new_sub); + } + if (!strcmp(m, "and")) { + return c6x_alu3(insn, rz_il_op_new_log_and); + } + if (!strcmp(m, "andn")) { + // dst = src2 & ~src1 (src1 is the complemented operand) + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], LOGAND(s2, LOGNOT(s1))); + } + if (!strcmp(m, "or")) { + return c6x_alu3(insn, rz_il_op_new_log_or); + } + if (!strcmp(m, "xor")) { + return c6x_alu3(insn, rz_il_op_new_log_xor); + } + if (!strcmp(m, "cmpeq")) { + return c6x_cmp(insn, rz_il_op_new_eq); + } + if (!strcmp(m, "cmpgt") || !strcmp(m, "cmpgtu")) { + bool uns = m[5] == 'u'; + if (insn->ops[1].kind == C6X_OP_REGPAIR) { + // long form: a 32-bit src1 compared against a 40-bit long src2 + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + if (!s1) { + return NULL; + } + RzILOpBool *cmp = uns + ? UGT(UNSIGNED(64, s1), LOGAND(c6x_pair64(&insn->ops[1]), SN(64, 0xffffffffffLL))) + : SGT(SIGNED(64, s1), c6x_long40(&insn->ops[1])); + return c6x_wr(&insn->ops[2], ITE(cmp, U32(1), U32(0))); + } + return c6x_cmp(insn, uns ? rz_il_op_new_ugt : rz_il_op_new_sgt); + } + if (!strcmp(m, "cmplt")) { + return c6x_cmp(insn, rz_il_op_new_slt); + } + if (!strcmp(m, "cmpltu")) { + return c6x_cmp(insn, rz_il_op_new_ult); + } + if (!strcmp(m, "cmpeq2")) { + return c6x_cmp_packed(insn, 2, rz_il_op_new_eq); + } + if (!strcmp(m, "cmpgt2")) { + return c6x_cmp_packed(insn, 2, rz_il_op_new_sgt); + } + if (!strcmp(m, "cmpeq4")) { + return c6x_cmp_packed(insn, 4, rz_il_op_new_eq); + } + if (!strcmp(m, "cmpgtu4")) { + return c6x_cmp_packed(insn, 4, rz_il_op_new_ugt); + } + if (!strcmp(m, "zero")) { + // clear a register or a register pair + const C6xOperand *d = &insn->ops[0]; + if (d->kind == C6X_OP_REG) { + return SETG(c6x_reg_name(d->side, d->num), U32(0)); + } + if (d->kind == C6X_OP_REGPAIR) { + return SEQ2(SETG(c6x_reg_name(d->side, d->num), U32(0)), + SETG(c6x_reg_name(d->side, d->num + 1), U32(0))); + } + return NULL; + } + if (!strcmp(m, "neg")) { + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], NEG(s)) : NULL; + } + if (!strcmp(m, "not")) { + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], LOGNOT(s)) : NULL; + } + if (!strcmp(m, "abs")) { + // dst = |src|, saturating INT_MIN (0x80000000) to INT_MAX (0x7fffffff). + // The 40-bit long form saturates -2^39 to 2^39-1 over a register pair. + if (insn->ops[0].kind == C6X_OP_REGPAIR) { + RzILOpBitVector *v = c6x_long40(&insn->ops[0]); + if (!v) { + return NULL; + } + return c6x_wr_pair64(&insn->ops[1], + LET("_a", v, + ITE(SGE(VARLP("_a"), SN(64, 0)), VARLP("_a"), + ITE(EQ(VARLP("_a"), SN(64, -0x8000000000LL)), SN(64, 0x7fffffffffLL), NEG(VARLP("_a")))))); + } + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], LET("_a", s, ITE(SGE(VARLP("_a"), S32(0)), VARLP("_a"), ITE(EQ(VARLP("_a"), U32(0x80000000)), U32(0x7fffffff), NEG(VARLP("_a")))))); + } + if (!strcmp(m, "sat")) { + // saturate a 40-bit long in a register pair to a signed 32-bit int + RzILOpBitVector *v = c6x_long40(&insn->ops[0]); + if (!v) { + return NULL; + } + return c6x_wr(&insn->ops[1], c6x_sats32(v)); + } + if (!strcmp(m, "abssp")) { + // single-precision float abs: clear the sign bit + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], LOGAND(s, U32(0x7fffffff))); + } + if (!strcmp(m, "absdp")) { + // double-precision float abs: clear the sign bit of the 64-bit pair + RzILOpBitVector *s = c6x_pair64(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr_pair64(&insn->ops[1], LOGAND(s, SN(64, 0x7fffffffffffffffLL))); + } + if (!strcmp(m, "abs2")) { + // per-halfword absolute value with 0x8000 -> 0x7fff saturation + RzILOpPure *slo = c6x_src(&insn->ops[0]); + RzILOpPure *shi = c6x_src(&insn->ops[0]); + if (!slo || !shi) { + rz_il_op_pure_free(slo); + rz_il_op_pure_free(shi); + return NULL; + } + RzILOpBitVector *lo = c6x_abs16(UNSIGNED(16, slo)); + RzILOpBitVector *hi = c6x_abs16(UNSIGNED(16, SHIFTR0(shi, U32(16)))); + return c6x_wr(&insn->ops[1], APPEND(hi, lo)); + } + if (!strcmp(m, "extu") || !strcmp(m, "ext")) { + // field extract: dst = (src << csta) >> cstb, right shift logical for + // extu and arithmetic (sign-extending) for ext + if (insn->ops[1].kind != C6X_OP_IMM || insn->ops[2].kind != C6X_OP_IMM) { + return NULL; + } + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + RzILOpPure *shl = SHIFTL0(s, U32((ut32)insn->ops[1].imm)); + RzILOpPure *v = m[3] == 'u' + ? SHIFTR0(shl, U32((ut32)insn->ops[2].imm)) + : SHIFTRA(shl, U32((ut32)insn->ops[2].imm)); + return c6x_wr(&insn->ops[3], v); + } + if (!strcmp(m, "set") || !strcmp(m, "clr")) { + // bit field set/clear: dst = src2 set/cleared over bits [csta, cstb]. + // Only the constant form is modeled; the register form takes the field + // bounds from src1 at run time. + if (insn->ops[1].kind != C6X_OP_IMM || insn->ops[2].kind != C6X_OP_IMM) { + return NULL; + } + ut32 csta = (ut32)insn->ops[1].imm & 0x1f; + ut32 cstb = (ut32)insn->ops[2].imm & 0x1f; + if (cstb < csta) { + return NULL; + } + ut32 width = cstb - csta + 1; + ut32 mask = width >= 32 ? 0xffffffff : (((ut32)1 << width) - 1) << csta; + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + RzILOpPure *v = m[0] == 's' ? LOGOR(s, U32(mask)) : LOGAND(s, U32(~mask)); + return c6x_wr(&insn->ops[3], v); + } + if (!strcmp(m, "lmbd")) { + // leftmost bit detection: src1[0] selects whether to find the leftmost 1 + // (count leading zeros of src2) or the leftmost 0 (count leading zeros of + // its complement); dst is that count. + RzILOpPure *src1 = c6x_src(&insn->ops[0]); + RzILOpPure *src2 = c6x_src(&insn->ops[1]); + if (!src1 || !src2) { + rz_il_op_pure_free(src1); + rz_il_op_pure_free(src2); + return NULL; + } + RzILOpPure *arg = LET("_lm", src2, + ITE(IS_ZERO(LOGAND(src1, U32(1))), LOGNOT(VARLP("_lm")), VARLP("_lm"))); + return c6x_wr(&insn->ops[2], c6x_clz32(arg)); + } + if (!strcmp(m, "norm")) { + // redundant sign bits: clz(src2 ^ (src2 >>a 31)) - 1. XOR by the sign turns + // the leading run of sign bits into leading zeros; subtract the sign bit. + // The 40-bit long form uses the sign-extended value and a 64-bit count. + if (insn->ops[0].kind == C6X_OP_REGPAIR) { + RzILOpBitVector *v = c6x_long40(&insn->ops[0]); + if (!v) { + return NULL; + } + RzILOpPure *arg = LET("_nl", v, LOGXOR(VARLP("_nl"), SHIFTRA(VARLP("_nl"), U32(63)))); + return c6x_wr(&insn->ops[1], SUB(c6x_clz64(arg), U32(25))); + } + RzILOpPure *src = c6x_src(&insn->ops[0]); + if (!src) { + return NULL; + } + RzILOpPure *arg = LET("_nm", src, LOGXOR(VARLP("_nm"), SHIFTRA(VARLP("_nm"), U32(31)))); + return c6x_wr(&insn->ops[1], SUB(c6x_clz32(arg), U32(1))); + } + if (!strcmp(m, "deal")) { + // dst = src2 deinterleaved: even bits to the low halfword, odd to the high + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], c6x_deal(s)); + } + if (!strcmp(m, "shfl")) { + // dst = src2 interleaved: low halfword to even bits, high to odd + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], c6x_shfl(s)); + } + if (!strcmp(m, "bitr")) { + // dst = src2 with its 32 bits reversed + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], c6x_bitrev32(s)); + } + if (!strcmp(m, "bitc4")) { + // dst = per-byte population count of src2 + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + return c6x_wr(&insn->ops[1], c6x_bitc4(s)); + } + if (!strcmp(m, "xpnd2")) { + // expand src2[1:0] into two halfword masks: bit 0 fills the lower + // halfword, bit 1 the upper, each 0x0000 or 0xffff + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + RzILOpPure *v = LET("_x", s, + LOGOR(SHIFTL0(MUL(LOGAND(SHIFTR0(VARLP("_x"), U32(1)), U32(1)), U32(0xffff)), U32(16)), + MUL(LOGAND(VARLP("_x"), U32(1)), U32(0xffff)))); + return c6x_wr(&insn->ops[1], v); + } + if (!strcmp(m, "xpnd4")) { + // expand src2[3:0] into four byte masks, each 0x00 or 0xff + RzILOpPure *s = c6x_src(&insn->ops[0]); + if (!s) { + return NULL; + } + RzILOpPure *v = LET("_x", + s, + LOGOR(LOGOR(SHIFTL0(MUL(LOGAND(SHIFTR0(VARLP("_x"), U32(3)), U32(1)), U32(0xff)), U32(24)), + SHIFTL0(MUL(LOGAND(SHIFTR0(VARLP("_x"), U32(2)), U32(1)), U32(0xff)), U32(16))), + LOGOR(SHIFTL0(MUL(LOGAND(SHIFTR0(VARLP("_x"), U32(1)), U32(1)), U32(0xff)), U32(8)), + MUL(LOGAND(VARLP("_x"), U32(1)), U32(0xff))))); + return c6x_wr(&insn->ops[1], v); + } + if (!strcmp(m, "subc")) { + // subtract-conditional divide step: d = src1 - src2; + // dst = d >= 0 ? (d << 1) | 1 : src1 << 1 + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpPure *v = LET("_s1", s1, + LET("_df", SUB(VARLP("_s1"), s2), + ITE(SGE(VARLP("_df"), S32(0)), + LOGOR(SHIFTL0(VARLP("_df"), U32(1)), U32(1)), + SHIFTL0(VARLP("_s1"), U32(1))))); + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "rpack2")) { + // shift src1 and src2 left by 1 with saturation, then pack their 16 MSBs + // into the upper and lower halfword of dst + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpPure *hi = SHIFTR0(c6x_sats32(SHIFTL0(SIGNED(64, s1), U32(1))), U32(16)); + RzILOpPure *lo = SHIFTR0(c6x_sats32(SHIFTL0(SIGNED(64, s2), U32(1))), U32(16)); + return c6x_wr(&insn->ops[2], LOGOR(SHIFTL0(LOGAND(hi, U32(0xffff)), U32(16)), LOGAND(lo, U32(0xffff)))); + } + if (!strcmp(m, "avg2")) { + // signed packed 16-bit rounding average: (a + b + 1) >>a 1 per halfword + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpPure *v = LET("_a", s1, + LET("_b", s2, + LOGOR(SHIFTL0(LOGAND(SHIFTRA(ADD(ADD(c6x_high16s(VARLP("_a")), c6x_high16s(VARLP("_b"))), U32(1)), U32(1)), U32(0xffff)), U32(16)), + LOGAND(SHIFTRA(ADD(ADD(c6x_low16s(VARLP("_a")), c6x_low16s(VARLP("_b"))), U32(1)), U32(1)), U32(0xffff))))); + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "avgu4")) { + // unsigned packed 8-bit rounding average: (a + b + 1) >> 1 per byte + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } +#define AVGU4_BYTE(sh) \ + SHIFTL0(LOGAND(SHIFTR0(ADD(ADD(LOGAND(SHIFTR0(VARLP("_a"), U32(sh)), U32(0xff)), LOGAND(SHIFTR0(VARLP("_b"), U32(sh)), U32(0xff))), U32(1)), U32(1)), U32(0xff)), U32(sh)) + RzILOpPure *v = LET("_a", s1, + LET("_b", s2, + LOGOR(LOGOR(AVGU4_BYTE(24), AVGU4_BYTE(16)), LOGOR(AVGU4_BYTE(8), AVGU4_BYTE(0))))); +#undef AVGU4_BYTE + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "saddu4")) { + // packed 8-bit unsigned saturating add: min(a + b, 255) per byte + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } +#define SADDU4_SUM(sh) ADD(LOGAND(SHIFTR0(VARLP("_a"), U32(sh)), U32(0xff)), LOGAND(SHIFTR0(VARLP("_b"), U32(sh)), U32(0xff))) +#define SADDU4_BYTE(sh) SHIFTL0(ITE(UGT(SADDU4_SUM(sh), U32(0xff)), U32(0xff), SADDU4_SUM(sh)), U32(sh)) + RzILOpPure *v = LET("_a", s1, + LET("_b", s2, + LOGOR(LOGOR(SADDU4_BYTE(24), SADDU4_BYTE(16)), LOGOR(SADDU4_BYTE(8), SADDU4_BYTE(0))))); +#undef SADDU4_BYTE +#undef SADDU4_SUM + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "saddus2")) { + // per halfword: clamp(u16(src1) + s16(src2), 0, 65535); src1 unsigned, + // src2 signed + RzILOpPure *a_lo = c6x_src(&insn->ops[0]); + RzILOpPure *b_lo = c6x_src(&insn->ops[1]); + RzILOpPure *a_hi = c6x_src(&insn->ops[0]); + RzILOpPure *b_hi = c6x_src(&insn->ops[1]); + if (!a_lo || !b_lo || !a_hi || !b_hi) { + rz_il_op_pure_free(a_lo); + rz_il_op_pure_free(b_lo); + rz_il_op_pure_free(a_hi); + rz_il_op_pure_free(b_hi); + return NULL; + } + RzILOpPure *lo = LET("_lo", ADD(LOGAND(a_lo, U32(0xffff)), c6x_low16s(b_lo)), + ITE(SLT(VARLP("_lo"), S32(0)), U32(0), ITE(SGT(VARLP("_lo"), S32(0xffff)), U32(0xffff), VARLP("_lo")))); + RzILOpPure *hi = LET("_hi", ADD(LOGAND(SHIFTR0(a_hi, U32(16)), U32(0xffff)), c6x_high16s(b_hi)), + ITE(SLT(VARLP("_hi"), S32(0)), U32(0), ITE(SGT(VARLP("_hi"), S32(0xffff)), U32(0xffff), VARLP("_hi")))); + return c6x_wr(&insn->ops[2], LOGOR(SHIFTL0(hi, U32(16)), lo)); + } + if (!strcmp(m, "sshvl") || !strcmp(m, "sshvr")) { + // variable shift of src2 by src1, clamped to [-31, 31]. SSHVL shifts left + // for a positive count (saturating) and arithmetic-right for a negative + // one; SSHVR is the mirror. src2 is ops[0], the count src1 is ops[1]. + bool left = m[4] == 'l'; + RzILOpPure *v = c6x_src(&insn->ops[0]); + RzILOpPure *s = c6x_src(&insn->ops[1]); + if (!v || !s) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(s); + return NULL; + } + // n >= 0 and n < 0 halves; the shift-left half saturates to 32 bits + RzILOpPure *pos = left + ? c6x_sats32(SHIFTL0(SIGNED(64, VARLP("_v")), VARLP("_n"))) + : SHIFTRA(VARLP("_v"), VARLP("_n")); + RzILOpPure *neg = left + ? SHIFTRA(VARLP("_v"), NEG(VARLP("_n"))) + : c6x_sats32(SHIFTL0(SIGNED(64, VARLP("_v")), NEG(VARLP("_n")))); + return c6x_wr(&insn->ops[2], + LET("_v", v, + LET("_sc", s, + LET("_n", ITE(SGT(VARLP("_sc"), S32(31)), S32(31), ITE(SLT(VARLP("_sc"), S32(-31)), S32(-31), VARLP("_sc"))), + ITE(SGE(VARLP("_n"), S32(0)), pos, neg))))); + } + if (!strcmp(m, "addk")) { + // dst += sign-extended 16-bit constant (dst is also src) + const C6xOperand *d = &insn->ops[1]; + return c6x_wr(d, ADD(VARG(c6x_reg_name(d->side, d->num)), S32(insn->ops[0].imm))); + } + if (!strcmp(m, "shl")) { + if (insn->ops[0].kind == C6X_OP_REGPAIR) { + // 40-bit long shifted left by src1, truncated back to 40 bits + RzILOpBitVector *v = c6x_long40(&insn->ops[0]); + RzILOpPure *n = c6x_src(&insn->ops[1]); + if (!v || !n) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(n); + return NULL; + } + return c6x_wr_pair64(&insn->ops[2], LOGAND(SHIFTL0(v, n), SN(64, 0xffffffffffLL))); + } + RzILOpPure *v = c6x_src(&insn->ops[0]); + RzILOpPure *n = c6x_src(&insn->ops[1]); + if (!v || !n) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(n); + return NULL; + } + return c6x_wr(&insn->ops[2], SHIFTL0(v, n)); + } + if (!strcmp(m, "shr") || !strcmp(m, "shru")) { + RzILOpPure *v = c6x_src(&insn->ops[0]); + RzILOpPure *n = c6x_src(&insn->ops[1]); + if (!v || !n) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(n); + return NULL; + } + // shr is arithmetic (sign-propagating); shru is logical + return c6x_wr(&insn->ops[2], m[3] == 'u' ? SHIFTR0(v, n) : SHIFTRA(v, n)); + } + // Byte/halfword/word/doubleword address arithmetic: the index is scaled by + // the access width before it is added to (ADDA*) or subtracted from (SUBA*) + // the base. + if (!strcmp(m, "addab")) { + return c6x_addr(insn, false, 0); + } + if (!strcmp(m, "addah")) { + return c6x_addr(insn, false, 1); + } + if (!strcmp(m, "addaw")) { + return c6x_addr(insn, false, 2); + } + if (!strcmp(m, "addad")) { + return c6x_addr(insn, false, 3); + } + if (!strcmp(m, "subab")) { + return c6x_addr(insn, true, 0); + } + if (!strcmp(m, "subah")) { + return c6x_addr(insn, true, 1); + } + if (!strcmp(m, "subaw")) { + return c6x_addr(insn, true, 2); + } + if (!strcmp(m, "sadd") || !strcmp(m, "ssub")) { + // signed add/subtract saturating to the 32-bit range (src1 - src2 for + // ssub, matching the SUB operand order) + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpBitVector *w1 = SIGNED(64, s1); + RzILOpBitVector *w2 = SIGNED(64, s2); + return c6x_wr(&insn->ops[2], c6x_sats32(m[1] == 's' ? SUB(w1, w2) : ADD(w1, w2))); + } + if (!strcmp(m, "sshl")) { + // dst = saturate32(src2 << (src1 & 0x1f)); the shift is done in 64 bits + // so an overflow past bit 31 saturates instead of being dropped + RzILOpPure *v = c6x_src(&insn->ops[0]); + RzILOpPure *n = c6x_src(&insn->ops[1]); + if (!v || !n) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(n); + return NULL; + } + return c6x_wr(&insn->ops[2], c6x_sats32(SHIFTL0(SIGNED(64, v), LOGAND(n, U32(0x1f))))); + } + if (!strcmp(m, "rotl")) { + // rotate src2 left by src1[4:0]: (v << n) | (v >>u (32 - n)); this .M-unit + // form decodes src1 (the count) into ops[0] and src2 (the value) into ops[1] + RzILOpPure *v = c6x_src(&insn->ops[1]); + RzILOpPure *cnt = c6x_src(&insn->ops[0]); + if (!v || !cnt) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(cnt); + return NULL; + } + return c6x_wr(&insn->ops[2], + LET("_rv", v, LET("_rn", LOGAND(cnt, U32(0x1f)), LOGOR(SHIFTL0(VARLP("_rv"), VARLP("_rn")), SHIFTR0(VARLP("_rv"), SUB(U32(32), VARLP("_rn"))))))); + } + if (!strcmp(m, "shlmb") || !strcmp(m, "shrmb")) { + // shift src2 by one byte and merge the adjacent byte of src1 into the + // vacated end: left brings src1's msb into the lsb, right the reverse + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpBitVector *v = m[2] == 'l' + ? LOGOR(SHIFTL0(s2, U32(8)), LOGAND(SHIFTR0(s1, U32(24)), U32(0xff))) + : LOGOR(SHIFTR0(s2, U32(8)), SHIFTL0(LOGAND(s1, U32(0xff)), U32(24))); + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "shr2") || !strcmp(m, "shru2")) { + // per-halfword shift right by src1[3:0], arithmetic for shr2 and logical + // (zero-extended) for shru2, lanes independent + RzILOpPure *v = c6x_src(&insn->ops[0]); + RzILOpPure *cnt = c6x_src(&insn->ops[1]); + if (!v || !cnt) { + rz_il_op_pure_free(v); + rz_il_op_pure_free(cnt); + return NULL; + } + bool ar = m[3] != 'u'; + return c6x_wr(&insn->ops[2], + LET("_sv", v, LET("_sn", UNSIGNED(16, LOGAND(cnt, U32(0xf))), APPEND(ar ? SHIFTRA(UNSIGNED(16, SHIFTR0(VARLP("_sv"), U32(16))), VARLP("_sn")) : SHIFTR0(UNSIGNED(16, SHIFTR0(VARLP("_sv"), U32(16))), VARLP("_sn")), ar ? SHIFTRA(UNSIGNED(16, VARLP("_sv")), VARLP("_sn")) : SHIFTR0(UNSIGNED(16, VARLP("_sv")), VARLP("_sn")))))); + } + if (!strcmp(m, "swap4")) { + // exchange the two bytes within each halfword + RzILOpPure *v = c6x_src(&insn->ops[0]); + if (!v) { + return NULL; + } + return c6x_wr(&insn->ops[1], + LET("_w4", v, + LOGOR(SHIFTL0(LOGAND(VARLP("_w4"), U32(0x00ff00ff)), U32(8)), + LOGAND(SHIFTR0(VARLP("_w4"), U32(8)), U32(0x00ff00ff))))); + } + if (!strcmp(m, "packl4") || !strcmp(m, "packh4")) { + // gather one byte from each of the four halfwords of src1:src2 -- the low + // byte of each for packl4, the high byte for packh4 -- src1 to the top + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + ut32 sh = m[4] == 'h' ? 8 : 0; // byte offset within each halfword + return c6x_wr(&insn->ops[2], + LET("_q1", s1, LET("_q2", s2, LOGOR(LOGOR(SHIFTL0(LOGAND(SHIFTR0(VARLP("_q1"), U32(16 + sh)), U32(0xff)), U32(24)), SHIFTL0(LOGAND(SHIFTR0(VARLP("_q1"), U32(sh)), U32(0xff)), U32(16))), LOGOR(SHIFTL0(LOGAND(SHIFTR0(VARLP("_q2"), U32(16 + sh)), U32(0xff)), U32(8)), LOGAND(SHIFTR0(VARLP("_q2"), U32(sh)), U32(0xff))))))); + } + // 16x16 multiplies, named by (src1 half, src1 sign)(src2 half, src2 sign): + // H/L pick the high/low halfword, and the S/U suffixes pick signed/unsigned. + if (!strcmp(m, "mpy")) { + return c6x_mpy16(insn, false, true, false, true); + } + if (!strcmp(m, "mpyh")) { + return c6x_mpy16(insn, true, true, true, true); + } + if (!strcmp(m, "mpyhl")) { + return c6x_mpy16(insn, true, true, false, true); + } + if (!strcmp(m, "mpylh")) { + return c6x_mpy16(insn, false, true, true, true); + } + if (!strcmp(m, "mpyu")) { + return c6x_mpy16(insn, false, false, false, false); + } + if (!strcmp(m, "mpyus")) { + return c6x_mpy16(insn, false, false, false, true); + } + if (!strcmp(m, "mpysu")) { + return c6x_mpy16(insn, false, true, false, false); + } + if (!strcmp(m, "mpyhu")) { + return c6x_mpy16(insn, true, false, true, false); + } + if (!strcmp(m, "mpyhus")) { + return c6x_mpy16(insn, true, false, true, true); + } + if (!strcmp(m, "mpyhsu")) { + return c6x_mpy16(insn, true, true, true, false); + } + if (!strcmp(m, "mpylhu")) { + return c6x_mpy16(insn, false, false, true, false); + } + if (!strcmp(m, "mpyhlu")) { + return c6x_mpy16(insn, true, false, false, false); + } + if (!strcmp(m, "mpyluhs")) { + return c6x_mpy16(insn, false, false, true, true); + } + if (!strcmp(m, "mpyhuls")) { + return c6x_mpy16(insn, true, false, false, true); + } + if (!strcmp(m, "mpylshu")) { + return c6x_mpy16(insn, false, true, true, false); + } + if (!strcmp(m, "mpyhslu")) { + return c6x_mpy16(insn, true, true, false, false); + } + // SMPY family: signed 16x16, doubled and saturated to 32-bit signed. + if (!strcmp(m, "smpy")) { + return c6x_smpy(insn, false, false); + } + if (!strcmp(m, "smpyh")) { + return c6x_smpy(insn, true, true); + } + if (!strcmp(m, "smpyhl")) { + return c6x_smpy(insn, true, false); + } + if (!strcmp(m, "smpylh")) { + return c6x_smpy(insn, false, true); + } + // 32x32 multiplies: MPYI/MPY32 keep the low 32 (single dst) or full 64 (pair + // dst); MPYID is the 64-bit signed form; MPY32U/SU/US set the source signs. + if (!strcmp(m, "mpyi") || !strcmp(m, "mpyid") || !strcmp(m, "mpy32")) { + return c6x_mpy32(insn, true, true); + } + if (!strcmp(m, "mpy32u")) { + return c6x_mpy32(insn, false, false); + } + if (!strcmp(m, "mpy32su")) { + return c6x_mpy32(insn, true, false); + } + if (!strcmp(m, "mpy32us")) { + return c6x_mpy32(insn, false, true); + } + if (!strcmp(m, "add2")) { + return c6x_packed(insn, 2, false); + } + if (!strcmp(m, "sub2")) { + return c6x_packed(insn, 2, true); + } + if (!strcmp(m, "add4")) { + return c6x_packed(insn, 4, false); + } + if (!strcmp(m, "sub4")) { + return c6x_packed(insn, 4, true); + } + if (!strcmp(m, "pack2")) { + return c6x_pack(insn, false, false); // low(src1):low(src2) + } + if (!strcmp(m, "packh2")) { + return c6x_pack(insn, true, true); // high(src1):high(src2) + } + if (!strcmp(m, "packlh2")) { + return c6x_pack(insn, false, true); // low(src1):high(src2) + } + if (!strcmp(m, "packhl2")) { + return c6x_pack(insn, true, false); // high(src1):low(src2) + } + if (!strcmp(m, "unpklu4")) { + return c6x_unpku4(insn, false); + } + if (!strcmp(m, "unpkhu4")) { + return c6x_unpku4(insn, true); + } + if (!strcmp(m, "sadd2")) { + return c6x_sat_packed(insn, 2, false); + } + if (!strcmp(m, "ssub2")) { + return c6x_sat_packed(insn, 2, true); + } + if (!strcmp(m, "spack2")) { + // saturate two 32-bit signed sources to signed 16 bits and pack + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + return c6x_wr(&insn->ops[2], APPEND(c6x_sat_s(s1, 16), c6x_sat_s(s2, 16))); + } + if (!strcmp(m, "mpy2") || !strcmp(m, "smpy2")) { + // two signed 16x16 products into a register pair: even reg = lsb*lsb, + // odd reg = msb*msb. SMPY2 additionally shifts each product left by 1 + // with saturation. + bool sat = m[0] == 's'; + RzILOpPure *a_lo = c6x_src(&insn->ops[0]); + RzILOpPure *b_lo = c6x_src(&insn->ops[1]); + RzILOpPure *a_hi = c6x_src(&insn->ops[0]); + RzILOpPure *b_hi = c6x_src(&insn->ops[1]); + if (!a_lo || !b_lo || !a_hi || !b_hi) { + rz_il_op_pure_free(a_lo); + rz_il_op_pure_free(b_lo); + rz_il_op_pure_free(a_hi); + rz_il_op_pure_free(b_hi); + return NULL; + } + RzILOpBitVector *lo = MUL(c6x_low16s(a_lo), c6x_low16s(b_lo)); + RzILOpBitVector *hi = MUL(c6x_high16s(a_hi), c6x_high16s(b_hi)); + if (sat) { + lo = c6x_sats32(SHIFTL0(SIGNED(64, lo), U32(1))); + hi = c6x_sats32(SHIFTL0(SIGNED(64, hi), U32(1))); + } + return c6x_wr_pair64(&insn->ops[2], APPEND(hi, lo)); + } + if (!strcmp(m, "mpyhi") || !strcmp(m, "mpyli")) { + // signed 16x32 multiply into a register pair (the 48-bit product, + // sign-extended): MPYHI uses src1's msb16, MPYLI its lsb16 + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpPure *h = m[3] == 'h' ? c6x_high16s(s1) : c6x_low16s(s1); + return c6x_wr_pair64(&insn->ops[2], MUL(SIGNED(64, h), SIGNED(64, s2))); + } + if (!strcmp(m, "mpyhir") || !strcmp(m, "mpylir")) { + // signed 16x32 multiply, rounded: (h16(src1) * src2 + 0x4000) >> 15 + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpPure *h = m[3] == 'h' ? c6x_high16s(s1) : c6x_low16s(s1); + RzILOpBitVector *prod = ADD(MUL(SIGNED(64, h), SIGNED(64, s2)), SN(64, 0x4000)); + return c6x_wr(&insn->ops[2], UNSIGNED(32, SHIFTRA(prod, U32(15)))); + } + if (!strcmp(m, "spacku4")) { + // saturate four signed 16-bit values to unsigned bytes and pack them: + // {satu8(src1_h), satu8(src1_l), satu8(src2_h), satu8(src2_l)} + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } +#define CLAMP8(vn) ITE(SLT(VARLP(vn), S32(0)), U32(0), ITE(SGT(VARLP(vn), S32(0xff)), U32(0xff), VARLP(vn))) + RzILOpPure *v = LET("_a", s1, + LET("_b", s2, + LET("_h3", c6x_high16s(VARLP("_a")), + LET("_h2", c6x_low16s(VARLP("_a")), + LET("_h1", c6x_high16s(VARLP("_b")), + LET("_h0", c6x_low16s(VARLP("_b")), + LOGOR(LOGOR(SHIFTL0(CLAMP8("_h3"), U32(24)), SHIFTL0(CLAMP8("_h2"), U32(16))), + LOGOR(SHIFTL0(CLAMP8("_h1"), U32(8)), CLAMP8("_h0"))))))))); +#undef CLAMP8 + return c6x_wr(&insn->ops[2], v); + } + if (!strcmp(m, "dotp2") || !strcmp(m, "dotpn2")) { + // DOTP2: lo*lo + hi*hi; DOTPN2: hi*hi - lo*lo (signed 16x16 products) + bool neg = m[4] == 'n'; + RzILOpPure *s1lo = c6x_src(&insn->ops[0]); + RzILOpPure *s2lo = c6x_src(&insn->ops[1]); + RzILOpPure *s1hi = c6x_src(&insn->ops[0]); + RzILOpPure *s2hi = c6x_src(&insn->ops[1]); + if (!s1lo || !s2lo || !s1hi || !s2hi) { + rz_il_op_pure_free(s1lo); + rz_il_op_pure_free(s2lo); + rz_il_op_pure_free(s1hi); + rz_il_op_pure_free(s2hi); + return NULL; + } + RzILOpBitVector *lo = MUL(c6x_low16s(s1lo), c6x_low16s(s2lo)); + RzILOpBitVector *hi = MUL(c6x_high16s(s1hi), c6x_high16s(s2hi)); + return c6x_wr(&insn->ops[2], neg ? SUB(hi, lo) : ADD(lo, hi)); + } + if (!strcmp(m, "dotpu4")) { + return c6x_dotp4(insn, false); // unsigned x unsigned bytes + } + if (!strcmp(m, "dotpsu4")) { + return c6x_dotp4(insn, true); // signed src1 x unsigned src2 bytes + } + if (!strcmp(m, "addsp")) { + return c6x_fsp(insn, rz_il_op_new_fadd); + } + if (!strcmp(m, "subsp")) { + return c6x_fsp(insn, rz_il_op_new_fsub); + } + if (!strcmp(m, "mpysp")) { + return c6x_fsp(insn, rz_il_op_new_fmul); + } + if (!strcmp(m, "intsp")) { + // signed 32-bit integer -> single-precision float + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], F2BV(SINT2F(RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE, s))) : NULL; + } + if (!strcmp(m, "spint")) { + // single-precision float -> signed 32-bit integer, round to nearest even + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], F2SINT(32, RZ_FLOAT_RMODE_RNE, FLOATV32(s))) : NULL; + } + if (!strcmp(m, "sptrunc")) { + // single-precision float -> signed 32-bit integer, truncating (round to zero) + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], F2SINT(32, RZ_FLOAT_RMODE_RTZ, FLOATV32(s))) : NULL; + } + if (!strcmp(m, "cmpeqsp")) { + // single-precision float equality -> 1/0 + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + return NULL; + } + return c6x_wr(&insn->ops[2], ITE(FEQ(FLOATV32(a), FLOATV32(b)), U32(1), U32(0))); + } + if (!strcmp(m, "cmpgtsp") || !strcmp(m, "cmpltsp")) { + // single-precision ordered compare -> 1/0. FORDER is an ordered less-than, + // so GT swaps the operands. + bool gt = m[3] == 'g'; + RzILOpPure *a = c6x_src(&insn->ops[0]); + RzILOpPure *b = c6x_src(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + return NULL; + } + RzILOpBool *cmp = gt ? FORDER(FLOATV32(b), FLOATV32(a)) : FORDER(FLOATV32(a), FLOATV32(b)); + return c6x_wr(&insn->ops[2], ITE(cmp, U32(1), U32(0))); + } + if (!strcmp(m, "cmpeqdp")) { + // double-precision float equality over register pairs -> 1/0 + RzILOpBitVector *a = c6x_pair64(&insn->ops[0]); + RzILOpBitVector *b = c6x_pair64(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + return NULL; + } + return c6x_wr(&insn->ops[2], ITE(FEQ(FLOATV64(a), FLOATV64(b)), U32(1), U32(0))); + } + if (!strcmp(m, "cmpgtdp") || !strcmp(m, "cmpltdp")) { + // double-precision ordered compare over register pairs -> 1/0 + bool gt = m[3] == 'g'; + RzILOpBitVector *a = c6x_pair64(&insn->ops[0]); + RzILOpBitVector *b = c6x_pair64(&insn->ops[1]); + if (!a || !b) { + rz_il_op_pure_free(a); + rz_il_op_pure_free(b); + return NULL; + } + RzILOpBool *cmp = gt ? FORDER(FLOATV64(b), FLOATV64(a)) : FORDER(FLOATV64(a), FLOATV64(b)); + return c6x_wr(&insn->ops[2], ITE(cmp, U32(1), U32(0))); + } + if (!strcmp(m, "mpysp2dp")) { + // two single-precision sources widened to double and multiplied into a pair + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpPure *s2 = c6x_src(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpFloat *a = FCONVERT(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE, FLOATV32(s1)); + RzILOpFloat *b = FCONVERT(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE, FLOATV32(s2)); + return c6x_wr_pair64(&insn->ops[2], F2BV(rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE, a, b))); + } + if (!strcmp(m, "mpyspdp")) { + // single-precision src1 widened to double, multiplied by double src2 + RzILOpPure *s1 = c6x_src(&insn->ops[0]); + RzILOpBitVector *s2 = c6x_pair64(&insn->ops[1]); + if (!s1 || !s2) { + rz_il_op_pure_free(s1); + rz_il_op_pure_free(s2); + return NULL; + } + RzILOpFloat *a = FCONVERT(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE, FLOATV32(s1)); + return c6x_wr_pair64(&insn->ops[2], F2BV(rz_il_op_new_fmul(RZ_FLOAT_RMODE_RNE, a, FLOATV64(s2)))); + } + if (!strcmp(m, "adddp")) { + return c6x_fdp(insn, rz_il_op_new_fadd); + } + if (!strcmp(m, "subdp")) { + return c6x_fdp(insn, rz_il_op_new_fsub); + } + if (!strcmp(m, "mpydp")) { + return c6x_fdp(insn, rz_il_op_new_fmul); + } + if (!strcmp(m, "intdp")) { + // signed 32-bit integer -> double-precision float (into a pair) + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr_pair64(&insn->ops[1], F2BV(SINT2F(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE, s))) : NULL; + } + if (!strcmp(m, "spdp")) { + // single-precision -> double-precision (into a pair) + RzILOpPure *s = c6x_src(&insn->ops[0]); + return s ? c6x_wr_pair64(&insn->ops[1], F2BV(FCONVERT(RZ_FLOAT_IEEE754_BIN_64, RZ_FLOAT_RMODE_RNE, FLOATV32(s)))) : NULL; + } + if (!strcmp(m, "dpint") || !strcmp(m, "dptrunc")) { + // double-precision float (pair src2) -> signed 32-bit integer. DPINT + // rounds to nearest even, DPTRUNC rounds toward zero. + RzILOpBitVector *s = c6x_pair64(&insn->ops[0]); + if (!s) { + return NULL; + } + RzFloatRMode rmode = m[2] == 'i' ? RZ_FLOAT_RMODE_RNE : RZ_FLOAT_RMODE_RTZ; + return c6x_wr(&insn->ops[1], F2SINT(32, rmode, FLOATV64(s))); + } + if (!strcmp(m, "dpsp")) { + // double-precision (pair src2) -> single-precision + RzILOpBitVector *s = c6x_pair64(&insn->ops[0]); + return s ? c6x_wr(&insn->ops[1], F2BV(FCONVERT(RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE, FLOATV64(s)))) : NULL; + } + if (!strcmp(m, "min2")) { + return c6x_minmax(insn, 2, true, true); + } + if (!strcmp(m, "max2")) { + return c6x_minmax(insn, 2, true, false); + } + if (!strcmp(m, "minu4")) { + return c6x_minmax(insn, 4, false, true); + } + if (!strcmp(m, "maxu4")) { + return c6x_minmax(insn, 4, false, false); + } + if (!strcmp(m, "ldw")) { + return c6x_load(insn, 4, false); + } + if (!strcmp(m, "ldb")) { + return c6x_load(insn, 1, true); + } + if (!strcmp(m, "ldbu")) { + return c6x_load(insn, 1, false); + } + if (!strcmp(m, "ldh")) { + return c6x_load(insn, 2, true); + } + if (!strcmp(m, "ldhu")) { + return c6x_load(insn, 2, false); + } + if (!strcmp(m, "stw")) { + return c6x_store(insn, 4); + } + if (!strcmp(m, "stb")) { + return c6x_store(insn, 1); + } + if (!strcmp(m, "sth")) { + return c6x_store(insn, 2); + } + if (!strcmp(m, "lddw") || !strcmp(m, "ldndw")) { + return c6x_load_pair(insn); + } + if (!strcmp(m, "stdw") || !strcmp(m, "stndw")) { + return c6x_store_pair(insn); + } + return NULL; +} + +RZ_IPI RzILOpEffect *c6x_lift(const C6xInsn *insn, ut64 pc) { + rz_return_val_if_fail(insn, NULL); + RzILOpEffect *eff = c6x_lift_core(insn, pc); + if (!eff) { + return NULL; + } + const char *pred = c6x_pred_name(insn->creg); + if (!pred) { + return eff; // unconditional + } + // predicated: run the effect only when the predicate register satisfies the + // z sense (z = 1 tests == 0, z = 0 tests != 0). + RzILOpBool *cond = insn->z ? IS_ZERO(VARG(pred)) : NON_ZERO(VARG(pred)); + return BRANCH(cond, eff, NOP()); +} + +#include diff --git a/librz/arch/meson.build b/librz/arch/meson.build index 7d86bee3b17..1e1ae9eb0bd 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -326,7 +326,10 @@ arch_isa_sources = [ 'isa/tms320/c55x_plus/c55plus_analysis.c', 'isa/tms320/c55x_plus/c55plus_il_config.c', 'isa/tms320/c55x_plus/ins.c', - 'isa/tms320/c64x/c64x.c', + 'isa/tms320/c6x/c6x_decode.c', + 'isa/tms320/c6x/c6x_format.c', + 'isa/tms320/c6x/c6x_analysis.c', + 'isa/tms320/c6x/c6x_il.c', 'isa/tms320/tms320c55x_insn.c', 'isa/v810/v810_disas.c', 'isa/v810/v810_il.c', diff --git a/librz/arch/p/analysis/analysis_tms320.c b/librz/arch/p/analysis/analysis_tms320.c index bce401a8126..fca6c783d15 100644 --- a/librz/arch/p/analysis/analysis_tms320.c +++ b/librz/arch/p/analysis/analysis_tms320.c @@ -9,20 +9,35 @@ #include #include #include -#include +#include typedef struct tms320_ctx_t { - void *c64x; + ut64 c6x_prev_end; ///< address just past the last c6x instruction analyzed + bool c6x_prev_par; ///< parallel bit of that instruction (for "||" continuation) } Tms320Context; int tms320_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) { Tms320Context *context = (Tms320Context *)analysis->plugin_data; const char *cpu = rz_analysis_get_cpu(analysis); + const C6xArchDesc *c6x = c6x_desc_from_cpu(cpu); + if (c6x) { + C6xInsn insn; + if (!c6x_decode(c6x, buf, len, addr, analysis->big_endian, &insn)) { + return -1; + } + c6x_fill_analysis(c6x, &insn, addr, op); + c6x_mark_parallel(&insn, addr, &context->c6x_prev_end, &context->c6x_prev_par); + if (mask & RZ_ANALYSIS_OP_MASK_IL) { + op->il_op = c6x_lift(&insn, addr); + } + if (mask & RZ_ANALYSIS_OP_MASK_DISASM) { + op->mnemonic = c6x_format(c6x, &insn, addr); + } + return op->size; + } if (cpu && rz_str_casecmp(cpu, "c55x+") == 0) { return tms320_c55x_plus_op(analysis, op, addr, buf, len, mask); - } else if (cpu && rz_str_casecmp(cpu, "c64x") == 0) { - return tms320_c64x_op(analysis, op, addr, buf, len, mask, context->c64x); } else if (cpu && rz_str_casecmp(cpu, "c54x") == 0) { return tms320_c54x_op(analysis, op, addr, buf, len, mask); } @@ -35,7 +50,6 @@ static bool tms320_analysis_init(void **user) { return false; } - context->c64x = tms320_c64x_new(); *user = context; return true; } @@ -44,7 +58,6 @@ static bool tms320_analysis_fini(void *user) { rz_return_val_if_fail(user, false); Tms320Context *context = (Tms320Context *)user; - tms320_c64x_free(context->c64x); free(context); return true; } @@ -59,6 +72,11 @@ static bool is_c5000(const char *cpu) { static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { const char *p; const char *cpu0 = rz_analysis_get_cpu(a); + // The tms320 arch shares one cc-tms320-32 sdb across its cpus, whose file + // default (c55x) is wrong for the C6000 family. Point the default at the + // C6000 EABI convention when a c6x cpu is selected; this callback runs on + // every cpu change, so switching back to a C5000 cpu restores c55x. + rz_analysis_set_cc_default(a, c6x_desc_from_cpu(cpu0) ? "c6x" : "c55x"); if (cpu0 && rz_str_casecmp(cpu0, "c54x") == 0) { // TMS320C54x: two 40-bit accumulators A/B (with the L/H 16-bit and G // 8-bit guard slices overlapping their parent), eight 16-bit auxiliary @@ -193,12 +211,22 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "ctr ac6 .40 674 0 # Accumulator 6\n" "ctr ac7 .40 679 0 # Accumulator 7\n"; } else { - p = + // C6000: C62x/C67x expose 16 general registers per side (A0-A15, + // B0-B15); C64x and later widen this to 32 and add the C64x+ control- + // register file. Emit the extended half only for the variants that own + // it, so `arp` on a C62x/C67x reads back the true 16-register machine. + bool wide = !(cpu0 && (rz_str_casecmp(cpu0, "c62x") == 0 || rz_str_casecmp(cpu0, "c67x") == 0)); + RzStrBuf sb; + rz_strbuf_init(&sb); + rz_strbuf_append(&sb, "=PC pce1\n" + "=SP b15\n" + "=BP a15\n" + "=SR csr\n" "=A0 a4\n" "=A1 b4\n" "=A2 a6\n" - "=A3 a6\n" + "=A3 b6\n" "=A4 a8\n" "=A5 b8\n" "=A6 a10\n" @@ -221,25 +249,27 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "gpr a12 .32 48 0\n" "gpr a13 .32 52 0\n" "gpr a14 .32 56 0\n" - "gpr a15 .32 60 0\n" -#ifdef CAPSTONE_TMS320C64X_H - "gpr a16 .32 64 0\n" - "gpr a17 .32 68 0\n" - "gpr a18 .32 72 0\n" - "gpr a19 .32 76 0\n" - "gpr a20 .32 80 0\n" - "gpr a21 .32 84 0\n" - "gpr a22 .32 88 0\n" - "gpr a23 .32 92 0\n" - "gpr a24 .32 96 0\n" - "gpr a25 .32 100 0\n" - "gpr a26 .32 104 0\n" - "gpr a27 .32 108 0\n" - "gpr a28 .32 112 0\n" - "gpr a29 .32 116 0\n" - "gpr a30 .32 120 0\n" - "gpr a31 .32 124 0\n" -#endif + "gpr a15 .32 60 0\n"); + if (wide) { + rz_strbuf_append(&sb, + "gpr a16 .32 64 0\n" + "gpr a17 .32 68 0\n" + "gpr a18 .32 72 0\n" + "gpr a19 .32 76 0\n" + "gpr a20 .32 80 0\n" + "gpr a21 .32 84 0\n" + "gpr a22 .32 88 0\n" + "gpr a23 .32 92 0\n" + "gpr a24 .32 96 0\n" + "gpr a25 .32 100 0\n" + "gpr a26 .32 104 0\n" + "gpr a27 .32 108 0\n" + "gpr a28 .32 112 0\n" + "gpr a29 .32 116 0\n" + "gpr a30 .32 120 0\n" + "gpr a31 .32 124 0\n"); + } + rz_strbuf_append(&sb, "gpr b0 .32 128 0\n" "gpr b1 .32 132 0\n" "gpr b2 .32 136 0\n" @@ -255,25 +285,27 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "gpr b12 .32 176 0\n" "gpr b13 .32 180 0\n" "gpr b14 .32 184 0\n" - "gpr b15 .32 188 0\n" -#ifdef CAPSTONE_TMS320C64X_H - "gpr b16 .32 192 0\n" - "gpr b17 .32 196 0\n" - "gpr b18 .32 200 0\n" - "gpr b19 .32 204 0\n" - "gpr b20 .32 208 0\n" - "gpr b21 .32 212 0\n" - "gpr b22 .32 216 0\n" - "gpr b23 .32 220 0\n" - "gpr b24 .32 224 0\n" - "gpr b25 .32 228 0\n" - "gpr b26 .32 232 0\n" - "gpr b27 .32 236 0\n" - "gpr b28 .32 240 0\n" - "gpr b29 .32 244 0\n" - "gpr b30 .32 248 0\n" - "gpr b31 .32 252 0\n" -#endif + "gpr b15 .32 188 0\n"); + if (wide) { + rz_strbuf_append(&sb, + "gpr b16 .32 192 0\n" + "gpr b17 .32 196 0\n" + "gpr b18 .32 200 0\n" + "gpr b19 .32 204 0\n" + "gpr b20 .32 208 0\n" + "gpr b21 .32 212 0\n" + "gpr b22 .32 216 0\n" + "gpr b23 .32 220 0\n" + "gpr b24 .32 224 0\n" + "gpr b25 .32 228 0\n" + "gpr b26 .32 232 0\n" + "gpr b27 .32 236 0\n" + "gpr b28 .32 240 0\n" + "gpr b29 .32 244 0\n" + "gpr b30 .32 248 0\n" + "gpr b31 .32 252 0\n"); + } + rz_strbuf_append(&sb, "ctr amr .32 256 0 # Addressing mode register\n" "ctr csr .32 260 0 # Control status register\n" "ctr gfpgfr .32 264 0 # Galois field multiply control register\n" @@ -284,26 +316,28 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "ctr isr .32 284 0 # Interrupt set register\n" "ctr istp .32 288 0 # Interrupt service table pointer register\n" "ctr nrp .32 292 0 # Nonmaskable interrupt return pointer register\n" - "ctr pce1 .32 296 0 # Program counter, E1 phase\n" -#ifdef CAPSTONE_TMS320C64X_H + "ctr pce1 .32 296 0 # Program counter, E1 phase\n"); + if (wide) { // Control Register File Extensions (C64x+ DSP) - "ctr dier .32 300 0 # (C64x+ only) Debug interrupt enable register\n" - "ctr dnum .32 304 0 # (C64x+ only) DSP core number register\n" - "ctr ecr .32 308 0 # (C64x+ only) Exception clear register\n" - "ctr efr .32 312 0 # (C64x+ only) Exception flag register\n" - "ctr gplya .32 316 0 # (C64x+ only) GMPY A-side polynomial register\n" - "ctr gplyb .32 320 0 # (C64x+ only) GMPY B-side polynomial register\n" - "ctr ierr .32 324 0 # (C64x+ only) Internal exception report register\n" - "ctr ilc .32 328 0 # (C64x+ only) Inner loop count register\n" - "ctr itsr .32 332 0 # (C64x+ only) Interrupt task state register\n" - "ctr ntsr .32 336 0 # (C64x+ only) NMI/Exception task state register\n" - "ctr rep .32 340 0 # (C64x+ only) Restricted entry point address register\n" - "ctr rilc .32 344 0 # (C64x+ only) Reload inner loop count register\n" - "ctr ssr .32 348 0 # (C64x+ only) Saturation status register\n" - "ctr tsch .32 352 0 # (C64x+ only) Time-stamp counter (high 32) register\n" - "ctr tscl .32 356 0 # (C64x+ only) Time-stamp counter (low 32) register\n" - "ctr tsr .32 360 0 # (C64x+ only) Task state register\n" -#endif + rz_strbuf_append(&sb, + "ctr dier .32 300 0 # (C64x+ only) Debug interrupt enable register\n" + "ctr dnum .32 304 0 # (C64x+ only) DSP core number register\n" + "ctr ecr .32 308 0 # (C64x+ only) Exception clear register\n" + "ctr efr .32 312 0 # (C64x+ only) Exception flag register\n" + "ctr gplya .32 316 0 # (C64x+ only) GMPY A-side polynomial register\n" + "ctr gplyb .32 320 0 # (C64x+ only) GMPY B-side polynomial register\n" + "ctr ierr .32 324 0 # (C64x+ only) Internal exception report register\n" + "ctr ilc .32 328 0 # (C64x+ only) Inner loop count register\n" + "ctr itsr .32 332 0 # (C64x+ only) Interrupt task state register\n" + "ctr ntsr .32 336 0 # (C64x+ only) NMI/Exception task state register\n" + "ctr rep .32 340 0 # (C64x+ only) Restricted entry point address register\n" + "ctr rilc .32 344 0 # (C64x+ only) Reload inner loop count register\n" + "ctr ssr .32 348 0 # (C64x+ only) Saturation status register\n" + "ctr tsch .32 352 0 # (C64x+ only) Time-stamp counter (high 32) register\n" + "ctr tscl .32 356 0 # (C64x+ only) Time-stamp counter (low 32) register\n" + "ctr tsr .32 360 0 # (C64x+ only) Task state register\n"); + } + rz_strbuf_append(&sb, "gpr a0:a1 .64 364 0\n" "gpr a2:a3 .64 368 0\n" "gpr a4:a5 .64 372 0\n" @@ -311,17 +345,19 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "gpr a8:a9 .64 380 0\n" "gpr a10:a11 .64 384 0\n" "gpr a12:a13 .64 388 0\n" - "gpr a14:a15 .64 392 0\n" -#ifdef CAPSTONE_TMS320C64X_H - "gpr a16:a17 .64 396 0\n" - "gpr a18:a19 .64 400 0\n" - "gpr a20:a21 .64 404 0\n" - "gpr a22:a23 .64 408 0\n" - "gpr a24:a25 .64 412 0\n" - "gpr a26:a27 .64 416 0\n" - "gpr a28:a29 .64 420 0\n" - "gpr a30:a31 .64 424 0\n" -#endif + "gpr a14:a15 .64 392 0\n"); + if (wide) { + rz_strbuf_append(&sb, + "gpr a16:a17 .64 396 0\n" + "gpr a18:a19 .64 400 0\n" + "gpr a20:a21 .64 404 0\n" + "gpr a22:a23 .64 408 0\n" + "gpr a24:a25 .64 412 0\n" + "gpr a26:a27 .64 416 0\n" + "gpr a28:a29 .64 420 0\n" + "gpr a30:a31 .64 424 0\n"); + } + rz_strbuf_append(&sb, "gpr b0:b1 .64 428 0\n" "gpr b2:b3 .64 432 0\n" "gpr b4:b5 .64 436 0\n" @@ -329,18 +365,19 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { "gpr b8:b9 .64 444 0\n" "gpr b10:b11 .64 448 0\n" "gpr b12:b13 .64 452 0\n" - "gpr b14:b15 .64 456 0\n" -#ifdef CAPSTONE_TMS320C64X_H - "gpr b16:b17 .64 460 0\n" - "gpr b18:b19 .64 464 0\n" - "gpr b20:b21 .64 468 0\n" - "gpr b22:b23 .64 472 0\n" - "gpr b24:b25 .64 476 0\n" - "gpr b26:b27 .64 480 0\n" - "gpr b28:b29 .64 484 0\n" - "gpr b30:b31 .64 488 0\n" -#endif - ; + "gpr b14:b15 .64 456 0\n"); + if (wide) { + rz_strbuf_append(&sb, + "gpr b16:b17 .64 460 0\n" + "gpr b18:b19 .64 464 0\n" + "gpr b20:b21 .64 468 0\n" + "gpr b22:b23 .64 472 0\n" + "gpr b24:b25 .64 476 0\n" + "gpr b26:b27 .64 480 0\n" + "gpr b28:b29 .64 484 0\n" + "gpr b30:b31 .64 488 0\n"); + } + return rz_strbuf_drain_nofree(&sb); } // C55x+ (Ryujin) has 32 accumulators and 16 (extended) auxiliary registers, @@ -430,8 +467,8 @@ static RzList /**/ *tms320_analysis_preludes(RzAnalysis *anal KW("\x0e\x00\x0e\x00\x0e\x00", 6, "\xff\x00\xff\x00\xff\x00", 6); /* two consecutive C55x+ pushes: 0e ?? 0e ?? */ KW("\x0e\x00\x0e\x00", 4, "\xff\x00\xff\x00", 4); - } else if (cpu && rz_str_casecmp(cpu, "c64x") == 0) { - /* C64x VLIW: no reliable fixed prologue; leave to call-graph. */ + } else if (c6x_desc_from_cpu(cpu)) { + /* C6000 VLIW: no reliable fixed prologue; leave to the call graph. */ } else { /* plain C55x: two consecutive single pushes (0x38 0x38) */ KW("\x38\x38", 2, "\xff\xff", 2); @@ -441,10 +478,13 @@ static RzList /**/ *tms320_analysis_preludes(RzAnalysis *anal } static RzAnalysisILConfig *tms320_il_config(RzAnalysis *analysis) { - // IL is provided for the C55x/C55x+ integer core and the C54x core; other - // CPUs (c64x, plain byte-mode) have no IL yet, so return NULL to leave the - // VM unconfigured. + // IL is provided for the C6000 (c6x) scalar core, the C55x/C55x+ integer + // core and the C54x core; other CPUs (plain byte-mode) have no IL yet, so + // return NULL to leave the VM unconfigured. const char *cpu = rz_analysis_get_cpu(analysis); + if (c6x_desc_from_cpu(cpu)) { + return tms320_c6x_il_config(analysis); + } if (cpu && (rz_str_casecmp(cpu, "c55x+") == 0 || rz_str_casecmp(cpu, "c55x") == 0)) { return tms320_c55x_plus_il_config(analysis); } diff --git a/librz/arch/p/asm/asm_tms320.c b/librz/arch/p/asm/asm_tms320.c index 165c68b82e9..1f803718fdb 100644 --- a/librz/arch/p/asm/asm_tms320.c +++ b/librz/arch/p/asm/asm_tms320.c @@ -8,16 +8,31 @@ #include #include #include -#include +#include typedef struct tms_cs_context_t { - void *c64x; + ut64 c6x_prev_end; ///< address just past the last c6x instruction disassembled + bool c6x_prev_par; ///< parallel bit of that instruction (for "||" continuation) } TmsContext; static int tms320_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) { TmsContext *ctx = (TmsContext *)a->plugin_data; - if (a->cpu && !rz_str_casecmp(a->cpu, "c64x")) { - return tms320_c64x_disassemble(a, op, buf, len, ctx->c64x); + // The native shared engine handles the whole TMS320C6000 VLIW family + // (c62x/c64x/c64x+/c67x/c674x/c66x). + const C6xArchDesc *c6x = c6x_desc_from_cpu(a->cpu); + if (c6x) { + C6xInsn insn; + if (c6x_decode(c6x, buf, len, a->pc, a->big_endian, &insn)) { + c6x_mark_parallel(&insn, a->pc, &ctx->c6x_prev_end, &ctx->c6x_prev_par); + char *s = c6x_format(c6x, &insn, a->pc); + if (s) { + rz_asm_op_set_asm(op, s); + free(s); + return op->size = insn.size; + } + } + rz_asm_op_set_asm(op, "invalid"); + return op->size = 4; } // C55x / C55x+ / C54x are decoded by the shared decode-IR engine; any other // cpu is unknown. @@ -52,7 +67,6 @@ static bool tms320_init(void **user) { if (!ctx) { return false; } - ctx->c64x = tms320_c64x_new(); *user = ctx; return true; } @@ -60,17 +74,48 @@ static bool tms320_init(void **user) { static bool tms320_fini(void *user) { rz_return_val_if_fail(user, false); TmsContext *ctx = (TmsContext *)user; - tms320_c64x_free(ctx->c64x); free(ctx); return true; } static char *tms320_mnemonics(const RzAsm *a, int id, bool json) { - TmsContext *ctx = (TmsContext *)a->plugin_data; - if (!a->cpu || rz_str_casecmp(a->cpu, "c64x")) { + // Only the native C6000 engine can enumerate its instruction set; the + // C5000/C54x decode-IR front-ends expose no such listing. + if (!c6x_desc_from_cpu(a->cpu)) { return NULL; } - return tms320_c64x_mnemonics(a, id, json, ctx->c64x); + RzPVector *mnems = c6x_mnemonics(); + if (!mnems) { + return NULL; + } + char *ret = NULL; + if (id >= 0) { + const char *m = id < (int)rz_pvector_len(mnems) ? rz_pvector_at(mnems, id) : NULL; + ret = m ? (json ? rz_str_newf("[\"%s\"]\n", m) : rz_str_dup(m)) : NULL; + } else { + RzStrBuf sb; + rz_strbuf_init(&sb); + if (json) { + rz_strbuf_append(&sb, "["); + } + void **it; + bool first = true; + rz_pvector_foreach (mnems, it) { + const char *m = *it; + if (json) { + rz_strbuf_appendf(&sb, "%s\"%s\"", first ? "" : ",", m); + } else { + rz_strbuf_appendf(&sb, "%s\n", m); + } + first = false; + } + if (json) { + rz_strbuf_append(&sb, "]\n"); + } + ret = rz_strbuf_drain_nofree(&sb); + } + rz_pvector_free(mnems); + return ret; } static char **tms320_cpu_descriptions() { @@ -78,7 +123,11 @@ static char **tms320_cpu_descriptions() { "c54x", "Texas Instruments TMS320C54x DSP family", "c55x", "Texas Instruments TMS320C55x DSP family", "c55x+", "Texas Instruments TMS320C55x+ DSP family", - "c64x", "Texas Instruments TMS320C64x DSP family", + "c62x", "Texas Instruments TMS320C62x fixed-point VLIW DSP family", + "c64x", "Texas Instruments TMS320C64x/C64x+ fixed-point VLIW DSP family", + "c67x", "Texas Instruments TMS320C67x/C67x+ floating-point VLIW DSP family", + "c674x", "Texas Instruments TMS320C674x unified fixed/floating-point DSP family", + "c66x", "Texas Instruments TMS320C66x VLIW DSP family", NULL }; return cpu_desc; @@ -87,8 +136,8 @@ static char **tms320_cpu_descriptions() { RzAsmPlugin rz_asm_plugin_tms320 = { .name = "tms320", .arch = "tms320", - .cpus = "c54x,c55x,c55x+,c64x", - .desc = "Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c64x) disassembler", + .cpus = "c54x,c55x,c55x+,c62x,c64x,c67x,c674x,c66x", + .desc = "Texas Instruments TMS320 DSP family (c54x,c55x,c55x+,c6x) disassembler", .license = "LGPL3", .bits = 32, .endian = RZ_SYS_ENDIAN_LITTLE | RZ_SYS_ENDIAN_BIG, diff --git a/librz/arch/types/cc-tms320-32.sdb.txt b/librz/arch/types/cc-tms320-32.sdb.txt index ffd6e0e24be..22fd43370e0 100644 --- a/librz/arch/types/cc-tms320-32.sdb.txt +++ b/librz/arch/types/cc-tms320-32.sdb.txt @@ -10,3 +10,18 @@ cc.c55x.arg5=t1 cc.c55x.argn=stack cc.c55x.maxargs=6 cc.c55x.ret=t0 + +c6x=cc +cc.c6x.arg0=a4 +cc.c6x.arg1=b4 +cc.c6x.arg2=a6 +cc.c6x.arg3=b6 +cc.c6x.arg4=a8 +cc.c6x.arg5=b8 +cc.c6x.arg6=a10 +cc.c6x.arg7=b10 +cc.c6x.arg8=a12 +cc.c6x.arg9=b12 +cc.c6x.argn=stack +cc.c6x.maxargs=10 +cc.c6x.ret=a4 diff --git a/librz/bin/format/coff/coff.h b/librz/bin/format/coff/coff.h index b8d29c64dcc..876921935f9 100644 --- a/librz/bin/format/coff/coff.h +++ b/librz/bin/format/coff/coff.h @@ -51,4 +51,7 @@ RZ_API RZ_BORROW RzBuffer *rz_coff_get_patched_buf(struct rz_bin_coff_obj *bin); #define RZ_COFF_RELOC_TARGET_SIZE 8 +/// on-disk size of a TI COFF2 relocation entry (r_vaddr, r_symndx, r_disp, r_type) +#define RZ_COFF_TI_RELOC_SIZE 12 + #endif /* COFF_H */ diff --git a/librz/bin/format/coff/coff_reloc.c b/librz/bin/format/coff/coff_reloc.c index 5a56e21949f..847219eba1d 100644 --- a/librz/bin/format/coff/coff_reloc.c +++ b/librz/bin/format/coff/coff_reloc.c @@ -144,10 +144,211 @@ static ut8 handle_arm64_relocs(struct rz_bin_coff_obj *bin, RzBinReloc *reloc, u return 0; } +// Store `value` into the field of the little-endian word at `paddr` that a TI +// C6000 field spec describes: bits [start, start + size) of a `container`-bit +// word, other bits preserved. The patched word is written to patch_buf; returns +// the byte count (container / 8), or 0 if the spec or read is invalid. +static size_t ti_c6000_store_field(struct rz_bin_coff_obj *bin, ut64 paddr, ut32 fldspec, ut64 value, ut8 *patch_buf) { + ut8 start = fldspec & 0xff; + ut8 size = (fldspec >> 8) & 0xff; + ut8 container = (fldspec >> 16) & 0xff; + if (!size || size > container || start + size > container) { + return 0; + } + ut64 mask = size >= 64 ? UT64_MAX : (((ut64)1 << size) - 1); + ut64 field = (value & mask) << start; + ut64 clear = mask << start; + switch (container) { + case 32: { + ut32 word; + if (!rz_buf_read_le32_at(bin->b, paddr, &word)) { + return 0; + } + word = (word & ~(ut32)clear) | (ut32)field; + rz_write_le32(patch_buf, word); + return 4; + } + case 16: { + ut16 hw; + if (!rz_buf_read_le16_at(bin->b, paddr, &hw)) { + return 0; + } + hw = (hw & ~(ut16)clear) | (ut16)field; + rz_write_le16(patch_buf, hw); + return 2; + } + case 8: { + ut8 b; + if (rz_buf_read_at(bin->b, paddr, &b, 1) != 1) { + return 0; + } + patch_buf[0] = (b & ~(ut8)clear) | (ut8)field; + return 1; + } + default: + return 0; + } +} + +// Evaluate the TI TMS320C6000 relocation expressions of every section. Each +// field is patched by a contiguous RPN sequence (operators in coff_specs.h) +// that ends in a store; the running stack is reset at each store so the next +// field starts clean. One RzBinReloc is emitted per store, carrying the field +// width and the symbol the expression referenced. +static void ti_c6000_relocs_foreach(struct rz_bin_coff_obj *bin, RelocsForeachCb cb, void *user) { + size_t i = 0; + CoffScnHdr *scn_hdr = NULL; + rz_vector_enumerate (bin->scn_hdrs, scn_hdr, i) { + if (!scn_hdr->s_nreloc || !bin->scn_va) { + continue; + } + if (scn_hdr->s_relptr > bin->size || + scn_hdr->s_relptr + (ut64)scn_hdr->s_nreloc * RZ_COFF_TI_RELOC_SIZE > bin->size) { + continue; + } + ut64 off = scn_hdr->s_relptr; + ut64 stack[16]; + int sp = 0; + RzBinSymbol *cur_sym = NULL; + for (ut32 j = 0; j < scn_hdr->s_nreloc; j++) { + ut32 r_vaddr, r_symndx; + ut16 r_disp, r_type; + if (!rz_buf_read_le32_offset(bin->b, &off, &r_vaddr) || + !rz_buf_read_le32_offset(bin->b, &off, &r_symndx) || + !rz_buf_read_le16_offset(bin->b, &off, &r_disp) || + !rz_buf_read_le16_offset(bin->b, &off, &r_type)) { + break; + } +#define C6000_BINOP(expr) \ + do { \ + if (sp >= 2) { \ + ut64 b = stack[--sp], a = stack[sp - 1]; \ + stack[sp - 1] = (expr); \ + } \ + } while (0) + switch (r_type) { + case COFF_REL_C6000_PUSH: { + RzBinSymbol *sym = ht_up_find(bin->sym_ht, (ut64)r_symndx, NULL); + if (sp < 16) { + stack[sp++] = sym ? sym->vaddr : 0; + } + if (sym) { + cur_sym = sym; + } + break; + } + case COFF_REL_C6000_PUSHUK: + if (sp < 16) { + stack[sp++] = r_symndx; + } + break; + case COFF_REL_C6000_PUSHSK: + if (sp < 16) { + stack[sp++] = (ut64)(st64)(st32)r_symndx; + } + break; + case COFF_REL_C6000_ADD: C6000_BINOP(a + b); break; + case COFF_REL_C6000_SUB: C6000_BINOP(a - b); break; + case COFF_REL_C6000_MPY: C6000_BINOP(a * b); break; + case COFF_REL_C6000_DIV: C6000_BINOP(b ? a / b : 0); break; + case COFF_REL_C6000_MOD: C6000_BINOP(b ? a % b : 0); break; + case COFF_REL_C6000_SR: C6000_BINOP(b < 64 ? a >> b : 0); break; + case COFF_REL_C6000_ASR: C6000_BINOP(b < 64 ? (ut64)((st64)a >> b) : 0); break; + case COFF_REL_C6000_SL: C6000_BINOP(b < 64 ? a << b : 0); break; + case COFF_REL_C6000_AND: C6000_BINOP(a & b); break; + case COFF_REL_C6000_OR: C6000_BINOP(a | b); break; + case COFF_REL_C6000_XOR: C6000_BINOP(a ^ b); break; + case COFF_REL_C6000_NEG: + if (sp >= 1) { + stack[sp - 1] = (ut64)(-(st64)stack[sp - 1]); + } + break; + case COFF_REL_C6000_NOTB: + if (sp >= 1) { + stack[sp - 1] = ~stack[sp - 1]; + } + break; + case COFF_REL_C6000_USTFLD: + case COFF_REL_C6000_SSTFLD: + case COFF_REL_C6000_XSTFLD: { + if (sp < 1) { + break; + } + ut64 value = stack[--sp]; + ut8 patch_buf[8]; + ut8 container = (r_symndx >> 16) & 0xff; + RzBinReloc reloc = { 0 }; + reloc.paddr = scn_hdr->s_scnptr + r_vaddr; + reloc.vaddr = bin->scn_va[i] + r_vaddr; + reloc.symbol = cur_sym; + reloc.target_vaddr = cur_sym ? cur_sym->vaddr : value; + reloc.type = container == 16 ? RZ_BIN_RELOC_16 : (container == 8 ? RZ_BIN_RELOC_8 : RZ_BIN_RELOC_32); + reloc.print_name = "R_C6000_STFLD"; + size_t plen = ti_c6000_store_field(bin, reloc.paddr, r_symndx, value, patch_buf); + cb(&reloc, plen ? patch_buf : NULL, plen, user); + sp = 0; + cur_sym = NULL; + break; + } + case COFF_REL_C6000_RELBYTE: + case COFF_REL_C6000_RELWORD: + case COFF_REL_C6000_RELLONG: { + // classic direct relocations: add the symbol value to the + // in-place field, no stack involved + RzBinSymbol *sym = ht_up_find(bin->sym_ht, (ut64)r_symndx, NULL); + sp = 0; + cur_sym = NULL; + if (!sym) { + break; + } + ut8 width = r_type == COFF_REL_C6000_RELLONG ? 32 : (r_type == COFF_REL_C6000_RELWORD ? 16 : 8); + ut8 patch_buf[8]; + RzBinReloc reloc = { 0 }; + reloc.paddr = scn_hdr->s_scnptr + r_vaddr; + reloc.vaddr = bin->scn_va[i] + r_vaddr; + reloc.symbol = sym; + reloc.additive = 1; + reloc.type = width == 32 ? RZ_BIN_RELOC_32 : (width == 16 ? RZ_BIN_RELOC_16 : RZ_BIN_RELOC_8); + reloc.print_name = r_type == COFF_REL_C6000_RELLONG ? "R_C6000_RELLONG" : (r_type == COFF_REL_C6000_RELWORD ? "R_C6000_RELWORD" : "R_C6000_RELBYTE"); + ut32 fldspec = ((ut32)width << 16) | ((ut32)width << 8); + ut64 addend = 0; + if (width == 32) { + ut32 v; + addend = rz_buf_read_le32_at(bin->b, reloc.paddr, &v) ? v : 0; + } else if (width == 16) { + ut16 v; + addend = rz_buf_read_le16_at(bin->b, reloc.paddr, &v) ? v : 0; + } else { + ut8 v; + addend = rz_buf_read_at(bin->b, reloc.paddr, &v, 1) == 1 ? v : 0; + } + reloc.addend = addend; + reloc.target_vaddr = sym->vaddr; + size_t plen = ti_c6000_store_field(bin, reloc.paddr, fldspec, sym->vaddr + addend, patch_buf); + cb(&reloc, plen ? patch_buf : NULL, plen, user); + break; + } + default: + // framing/attribute relocations (alignment, fetch-packet + // header, no-compact): no operand, and they terminate any + // field expression, so drop the stack + sp = 0; + cur_sym = NULL; + break; + } +#undef C6000_BINOP + } + } +} + static void relocs_foreach(struct rz_bin_coff_obj *bin, RelocsForeachCb cb, void *user) { if (!bin->scn_hdrs) { return; } + if (bin->target_id == COFF_FILE_TARGET_TI_TMS320C6000) { + ti_c6000_relocs_foreach(bin, cb, user); + return; + } size_t i = 0; CoffScnHdr *scn_hdr = NULL; diff --git a/librz/bin/format/coff/coff_specs.h b/librz/bin/format/coff/coff_specs.h index 672c7658e5b..3b94c62fc26 100644 --- a/librz/bin/format/coff/coff_specs.h +++ b/librz/bin/format/coff/coff_specs.h @@ -183,6 +183,39 @@ #define COFF_REL_ARM64_ADDR32NB 2 #define COFF_REL_ARM64_BRANCH26 3 +/* TI TMS320C6000 COFF relocations are RPN expressions evaluated by a small + * stack machine (TI "Common Object File Format Specification", SPRAAO8): the + * RE_PUSH* operators load operands, the arithmetic operators fold the two top + * stack entries, and the RE_*STFLD operators store the result into a field the + * reloc's symbol-index word describes as (container_bits << 16) | + * (field_bits << 8) | start_bit. Framing relocations (type < 0x4000, e.g. the + * no-compact marker) carry no operand and patch nothing. */ +#define COFF_REL_C6000_ADD 0x4000 /* pop b, a; push a + b */ +#define COFF_REL_C6000_SUB 0x4001 /* pop b, a; push a - b */ +#define COFF_REL_C6000_NEG 0x4002 /* pop a; push -a */ +#define COFF_REL_C6000_MPY 0x4003 /* pop b, a; push a * b */ +#define COFF_REL_C6000_DIV 0x4004 /* pop b, a; push a / b */ +#define COFF_REL_C6000_MOD 0x4005 /* pop b, a; push a % b */ +#define COFF_REL_C6000_SR 0x4006 /* pop b, a; push a >> b (unsigned) */ +#define COFF_REL_C6000_ASR 0x4007 /* pop b, a; push a >> b (arithmetic) */ +#define COFF_REL_C6000_SL 0x4008 /* pop b, a; push a << b */ +#define COFF_REL_C6000_AND 0x4009 /* pop b, a; push a & b */ +#define COFF_REL_C6000_OR 0x400a /* pop b, a; push a | b */ +#define COFF_REL_C6000_XOR 0x400b /* pop b, a; push a ^ b */ +#define COFF_REL_C6000_NOTB 0x400c /* pop a; push ~a */ +#define COFF_REL_C6000_USTFLD 0x400f /* pop v; store unsigned v into field */ +#define COFF_REL_C6000_SSTFLD 0x4010 /* pop v; store signed v into field */ +#define COFF_REL_C6000_PUSH 0x4011 /* push value of symbol symndx */ +#define COFF_REL_C6000_PUSHSK 0x4012 /* push signed constant symndx */ +#define COFF_REL_C6000_PUSHUK 0x4013 /* push unsigned constant symndx */ +#define COFF_REL_C6000_XSTFLD 0x4016 /* pop v; store v into a whole-word field */ + +/* Classic COFF direct data relocations reused by TI (not stack-machine ops): + * the referenced symbol value is added to the in-place field. */ +#define COFF_REL_C6000_RELBYTE 0x000f /* symbol value added to an 8-bit field */ +#define COFF_REL_C6000_RELWORD 0x0010 /* symbol value added to a 16-bit field */ +#define COFF_REL_C6000_RELLONG 0x0011 /* symbol value added to a 32-bit field */ + typedef struct coff_hdr { ut16 f_magic; /* Magic number */ ut16 f_nscns; /* Number of Sections */ diff --git a/librz/bin/format/elf/elf_info.c b/librz/bin/format/elf/elf_info.c index 9ece2c5abee..b08e90d86ac 100644 --- a/librz/bin/format/elf/elf_info.c +++ b/librz/bin/format/elf/elf_info.c @@ -361,6 +361,7 @@ static const struct arch_translation arch_translation_table[] = { { EM_VIDEOCORE3, "vc4" }, { EM_VIDEOCORE4, "vc4" }, { EM_MSP430, "msp430" }, + { EM_TI_C6000, "tms320" }, { EM_SH, "sh" }, { EM_V810, "v810" }, { EM_V800, "v850" }, @@ -926,6 +927,10 @@ static inline bool arch_is_parisc(ELFOBJ *bin) { return arch_is(bin, EM_PARISC); } +static inline bool arch_is_c6x(ELFOBJ *bin) { + return arch_is(bin, EM_TI_C6000); +} + static inline bool arch_is_riscv(ELFOBJ *bin) { return arch_is(bin, EM_RISCV); } @@ -1500,6 +1505,14 @@ static char *get_cpu_hppa(ELFOBJ *bin) { return strdup("Unknown HP PARISC ISA"); } +// EM_TI_C6000 objects (COFF ABI and the C6000 EABI, SPRAB89) do not record the +// exact C6x ISA variant in the ELF header, so default to the C674x superset (the +// unified C64x+/C67x+ ISA the EABI targets); a narrower variant can be forced +// with -c on the shared tms320 c6x decoder. +static char *get_cpu_c6x(ELFOBJ *bin) { + return rz_str_dup("c674x"); +} + static char *get_cpu_h8xx(ELFOBJ *bin) { if (bin->ehdr.e_machine == EM_H8_300H) { return rz_str_dup("h8300h"); @@ -2125,6 +2138,8 @@ RZ_OWN char *Elf_(rz_bin_elf_get_cpu)(RZ_NONNULL ELFOBJ *bin) { return bin->ehdr.e_machine == EM_SPARC ? strdup("v8") : strdup("v9"); } else if (arch_is_parisc(bin)) { return get_cpu_hppa(bin); + } else if (arch_is_c6x(bin)) { + return get_cpu_c6x(bin); } else if (arch_is_arm(bin)) { return get_cpu_arm(bin); } else if (arch_is_h8xx(bin)) { diff --git a/librz/bin/format/elf/elf_relocs_conversion.c b/librz/bin/format/elf/elf_relocs_conversion.c index 665e69a8f2a..3e158196e39 100644 --- a/librz/bin/format/elf/elf_relocs_conversion.c +++ b/librz/bin/format/elf/elf_relocs_conversion.c @@ -1531,6 +1531,58 @@ static RzBinReloc *reloc_convert_avr(ELFOBJ *bin, RzBinElfReloc *rel, ut64 GOT) } } +// TMS320C6000 EABI (see the "TMS320C6000 DSP Embedded Application Binary +// Interface" specification). Data relocations (ABS8/16/32) resolve to a flat +// symbol value; the many instruction-field relocations (MVKL/MVKH halves, +// static-base and PC-relative displacements) patch a bit field inside a 32-bit +// opcode, so they are exposed with their width but no directly resolvable +// value. Framing/attribute relocations (ALIGN/FPHEAD/NOCMP) patch nothing. +static RzBinReloc *reloc_convert_c6000(ELFOBJ *bin, RzBinElfReloc *rel, ut64 GOT) { + ut64 P = rel->vaddr; + + switch (rel->type) { + case R_C6000_NONE: + return reloc_convert_set(bin, rel, 0, "R_C6000_NONE"); + case R_C6000_ABS32: ADD(32, 0, "R_C6000_ABS32", RZ_RELOC_BASE_SYMBOL); + case R_C6000_ABS16: ADD(16, 0, "R_C6000_ABS16", RZ_RELOC_BASE_SYMBOL); + case R_C6000_ABS8: ADD(8, 0, "R_C6000_ABS8", RZ_RELOC_BASE_SYMBOL); + case R_C6000_PCR_S21: SET(32, "R_C6000_PCR_S21"); + case R_C6000_PCR_S12: SET(16, "R_C6000_PCR_S12"); + case R_C6000_PCR_S10: SET(16, "R_C6000_PCR_S10"); + case R_C6000_PCR_S7: SET(16, "R_C6000_PCR_S7"); + case R_C6000_PCR_H16: SET(16, "R_C6000_PCR_H16"); + case R_C6000_PCR_L16: SET(16, "R_C6000_PCR_L16"); + case R_C6000_ABS_S16: SET(16, "R_C6000_ABS_S16"); + case R_C6000_ABS_L16: SET(16, "R_C6000_ABS_L16"); + case R_C6000_ABS_H16: SET(16, "R_C6000_ABS_H16"); + case R_C6000_SBR_U15_B: SET(16, "R_C6000_SBR_U15_B"); + case R_C6000_SBR_U15_H: SET(16, "R_C6000_SBR_U15_H"); + case R_C6000_SBR_U15_W: SET(16, "R_C6000_SBR_U15_W"); + case R_C6000_SBR_S16: SET(16, "R_C6000_SBR_S16"); + case R_C6000_SBR_L16_B: SET(16, "R_C6000_SBR_L16_B"); + case R_C6000_SBR_L16_H: SET(16, "R_C6000_SBR_L16_H"); + case R_C6000_SBR_L16_W: SET(16, "R_C6000_SBR_L16_W"); + case R_C6000_SBR_H16_B: SET(16, "R_C6000_SBR_H16_B"); + case R_C6000_SBR_H16_H: SET(16, "R_C6000_SBR_H16_H"); + case R_C6000_SBR_H16_W: SET(16, "R_C6000_SBR_H16_W"); + case R_C6000_SBR_GOT_U15_W: SET(16, "R_C6000_SBR_GOT_U15_W"); + case R_C6000_SBR_GOT_L16_W: SET(16, "R_C6000_SBR_GOT_L16_W"); + case R_C6000_SBR_GOT_H16_W: SET(16, "R_C6000_SBR_GOT_H16_W"); + case R_C6000_DSBT_INDEX: SET(16, "R_C6000_DSBT_INDEX"); + case R_C6000_PREL31: ADD(32, -P, "R_C6000_PREL31", RZ_RELOC_BASE_SYMBOL); + case R_C6000_COPY: ADD(32, 0, "R_C6000_COPY", RZ_RELOC_BASE_UNKNOWN); + case R_C6000_JUMP_SLOT: SET(32, "R_C6000_JUMP_SLOT"); + case R_C6000_EHTYPE: SET(32, "R_C6000_EHTYPE"); + case R_C6000_ALIGN: + return reloc_convert_set(bin, rel, 0, "R_C6000_ALIGN"); + case R_C6000_FPHEAD: + return reloc_convert_set(bin, rel, 0, "R_C6000_FPHEAD"); + case R_C6000_NOCMP: + return reloc_convert_set(bin, rel, 0, "R_C6000_NOCMP"); + default: UNSUPP("C6000"); + } +} + #undef UNSUPP #undef UNHANDL #undef SET @@ -1683,7 +1735,7 @@ RZ_OWN RzBinReloc *Elf_(rz_bin_elf_convert_relocation)(RZ_NONNULL ELFOBJ *bin, R case EM_VIDEOCORE3: ARCH_MISSING("EM_VIDEOCORE3"); case EM_LATTICEMICO32: ARCH_MISSING("EM_LATTICEMICO32"); case EM_SE_C17: ARCH_MISSING("EM_SE_C17"); - case EM_TI_C6000: ARCH_MISSING("EM_TI_C6000"); + case EM_TI_C6000: return reloc_convert_c6000(bin, rel, GOT); case EM_TI_C2000: ARCH_MISSING("EM_TI_C2000"); case EM_TI_C5500: ARCH_MISSING("EM_TI_C5500"); case EM_TI_ARP32: ARCH_MISSING("EM_TI_ARP32"); diff --git a/librz/bin/format/elf/glibc_elf.h b/librz/bin/format/elf/glibc_elf.h index 1069975f8c6..fc1de261eef 100644 --- a/librz/bin/format/elf/glibc_elf.h +++ b/librz/bin/format/elf/glibc_elf.h @@ -4608,6 +4608,43 @@ ARM32 relocations: #define R_AVR_PORT5 35 #define R_AVR_32_PCREL 36 +/* TMS320C6000 (C6000 EABI) Relocations */ +#define R_C6000_NONE 0 +#define R_C6000_ABS32 1 +#define R_C6000_ABS16 2 +#define R_C6000_ABS8 3 +#define R_C6000_PCR_S21 4 /* 21-bit PC-relative branch displacement */ +#define R_C6000_PCR_S12 5 +#define R_C6000_PCR_S10 6 +#define R_C6000_PCR_S7 7 +#define R_C6000_ABS_S16 8 +#define R_C6000_ABS_L16 9 /* low 16 bits, MVKL */ +#define R_C6000_ABS_H16 10 /* high 16 bits, MVKH */ +#define R_C6000_SBR_U15_B 11 /* static-base-relative, unsigned, byte */ +#define R_C6000_SBR_U15_H 12 /* halfword */ +#define R_C6000_SBR_U15_W 13 /* word */ +#define R_C6000_SBR_S16 14 +#define R_C6000_SBR_L16_B 15 +#define R_C6000_SBR_L16_H 16 +#define R_C6000_SBR_L16_W 17 +#define R_C6000_SBR_H16_B 18 +#define R_C6000_SBR_H16_H 19 +#define R_C6000_SBR_H16_W 20 +#define R_C6000_SBR_GOT_U15_W 21 +#define R_C6000_SBR_GOT_L16_W 22 +#define R_C6000_SBR_GOT_H16_W 23 +#define R_C6000_DSBT_INDEX 24 +#define R_C6000_PREL31 25 +#define R_C6000_COPY 26 +#define R_C6000_JUMP_SLOT 27 +#define R_C6000_EHTYPE 28 +#define R_C6000_PCR_H16 29 +#define R_C6000_PCR_L16 30 +#define R_C6000_ALIGN 253 +#define R_C6000_FPHEAD 254 +#define R_C6000_NOCMP 255 + + /* Dynamic array tags */ #define DT_HEXAGON_SYMSZ 0x70000000 diff --git a/test/db/analysis/tms320.c64x_32 b/test/db/analysis/tms320.c64x_32 index 59d070b89f5..95592730055 100644 --- a/test/db/analysis/tms320.c64x_32 +++ b/test/db/analysis/tms320.c64x_32 @@ -1,214 +1,3 @@ -NAME=320c64 OPEX -FILE=malloc://512280 -CMDS=<> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (ite (== (var _p) (bv 32 0x40000000)) (bv 32 0x7fffffff) (<< (var _p) (bv 32 0x1) false)))) +# address arithmetic, saturating ALU and shift-with-saturation (RzIL) +d "addab .d1 a4,a5,a6" 40b81003 0x0 (set a6 (+ (var a4) (var a5))) +d "addah .d1 a4,a5,a6" 40ba1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x1) false))) +d "addaw .d1 a4,a5,a6" 40bc1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x2) false))) +d "subab .d1 a4,a5,a6" c0b81003 0x0 (set a6 (- (var a4) (var a5))) +d "subah .d1 a4,a5,a6" c0ba1003 0x0 (set a6 (- (var a4) (<< (var a5) (bv 32 0x1) false))) +d "subaw .d1 a4,a5,a6" c0bc1003 0x0 (set a6 (- (var a4) (<< (var a5) (bv 32 0x2) false))) +d "sadd .l1 a0,a1,a2" 78020401 0x0 (set a2 (let _s (+ (cast 64 (msb (var a0)) (var a0)) (cast 64 (msb (var a1)) (var a1))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "ssub .l1 a0,a1,a2" f8010401 0x0 (set a2 (let _s (- (cast 64 (msb (var a0)) (var a0)) (cast 64 (msb (var a1)) (var a1))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "sshl .s1 a0,a1,a2" e0280001 0x0 (set a2 (let _s (<< (cast 64 (msb (var a0)) (var a0)) (& (var a1) (bv 32 0x1f)) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +# bit-field set/clear and leftmost-bit/normalize (RzIL) +d "set .s1 a1,8,16,a2" 88100501 0x0 (set a2 (| (var a1) (bv 32 0x1ff00))) +d "clr .s1 a1,8,16,a2" c8100501 0x0 (set a2 (& (var a1) (bv 32 0xfffe00ff))) +d "lmbd .l1 a0,a1,a2" 780d0401 0x0 (set a2 (let _z0 (let _lm (var a1) (ite (is_zero (& (var a0) (bv 32 0x1))) (~ (var _lm)) (var _lm))) (let _z1 (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (<< (var _z0) (bv 32 0x10) false) (var _z0)) (let _z2 (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (<< (var _z1) (bv 32 0x8) false) (var _z1)) (let _z3 (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (<< (var _z2) (bv 32 0x4) false) (var _z2)) (let _z4 (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (<< (var _z3) (bv 32 0x2) false) (var _z3)) (let _z5 (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (<< (var _z4) (bv 32 0x1) false) (var _z4)) (+ (+ (+ (+ (+ (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (bv 32 0x10) (bv 32 0x0)) (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _z5) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0)))))))))) +d "norm .l1 a1,a2" 780c0401 0x0 (set a2 (- (let _z0 (let _nm (var a1) (^ (var _nm) (>> (var _nm) (bv 32 0x1f) (msb (var _nm))))) (let _z1 (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (<< (var _z0) (bv 32 0x10) false) (var _z0)) (let _z2 (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (<< (var _z1) (bv 32 0x8) false) (var _z1)) (let _z3 (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (<< (var _z2) (bv 32 0x4) false) (var _z2)) (let _z4 (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (<< (var _z3) (bv 32 0x2) false) (var _z3)) (let _z5 (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (<< (var _z4) (bv 32 0x1) false) (var _z4)) (+ (+ (+ (+ (+ (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (bv 32 0x10) (bv 32 0x0)) (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _z5) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))))))))) (bv 32 0x1))) +# subtract-conditional divide step (RzIL) +d "subc .l1 a0,a1,a2" 78090401 0x0 (set a2 (let _s1 (var a0) (let _df (- (var _s1) (var a1)) (ite (|| (! (sle (var _df) (bv 32 0x0))) (== (var _df) (bv 32 0x0))) (| (<< (var _df) (bv 32 0x1) false) (bv 32 0x1)) (<< (var _s1) (bv 32 0x1) false))))) +# NORM long (register-pair) form +d "norm .l1 a1:a0,a2" 180c0001 0x0 (set a2 (- (let _q0 (let _nl (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (^ (var _nl) (>> (var _nl) (bv 32 0x3f) (msb (var _nl))))) (let _q1 (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (<< (var _q0) (bv 32 0x20) false) (var _q0)) (let _q2 (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (<< (var _q1) (bv 32 0x10) false) (var _q1)) (let _q3 (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (<< (var _q2) (bv 32 0x8) false) (var _q2)) (let _q4 (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (<< (var _q3) (bv 32 0x4) false) (var _q3)) (let _q5 (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (<< (var _q4) (bv 32 0x2) false) (var _q4)) (let _q6 (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (<< (var _q5) (bv 32 0x1) false) (var _q5)) (+ (+ (+ (+ (+ (+ (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (bv 32 0x20) (bv 32 0x0)) (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (bv 32 0x10) (bv 32 0x0))) (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _q6) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0)))))))))) (bv 32 0x19))) +# 40-bit long ABS and SAT (decode) +d "abs .l1 a1:a0,a3:a2" 18070001 0x0 (seq (set _d (let _a (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (ite (|| (! (sle (var _a) (bv 64 0x0))) (== (var _a) (bv 64 0x0))) (var _a) (ite (== (var _a) (bv 64 0xffffff8000000000)) (bv 64 0x7fffffffff) (~- (var _a)))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "sat .l1 a1:a0,a2" 18080001 0x0 (set a2 (let _s (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) diff --git a/test/db/asm/tms320_c64x_32 b/test/db/asm/tms320_c64x_32 new file mode 100644 index 00000000000..6b34f461965 --- /dev/null +++ b/test/db/asm/tms320_c64x_32 @@ -0,0 +1,87 @@ +# TMS320C64x/C64x+ (fixed-point SIMD VLIW) native disassembler +# Instruction words are little-endian; addresses are the packet PCE1. +# Vectors are cross-checked against TI dis6x (c6000 v7.4.4). +# Doubleword loads/stores move register pairs (C64x+ / C67x, not C62x). +d "lddw .d2 *+b15[0x2],a13:a12" e4433c06 0x0 (seq (set ea (+ (var b15) (bv 32 0x10))) (set a12 (loadw 0 32 (var ea))) (set a13 (loadw 0 32 (+ (var ea) (bv 32 0x4))))) +d "stdw .d2 a11:a10,*+b15[0x1]" c5233c05 0x0 (seq (set ea (+ (var b15) (bv 32 0x8))) (storew 0 (var ea) (var a10)) (storew 0 (+ (var ea) (bv 32 0x4)) (var a11))) +d "ldndw .d2 *b4,b11:b10" a6039005 0x0 (seq (set ea (+ (var b4) (bv 32 0x0))) (set b10 (loadw 0 32 (var ea))) (set b11 (loadw 0 32 (+ (var ea) (bv 32 0x4))))) +# MVK.L: the ABS opfield reused for a signed 5-bit constant move (C64x+). +d "[!b0] mvk .l1 1,a0" 59a30430 0x0 (branch (is_zero (var b0)) (set a0 (bv 32 0x1)) nop) +d "[a1] mvk .l1 0,a0" 58a30080 0x0 (branch (! (is_zero (var a1))) (set a0 (bv 32 0x0)) nop) +# MV on .S/.D (OR-with-zero on those units, C64x+), collapsed to the mv alias. +d "mv .s1 a0,a1" a1068000 0x0 (set a1 (var a0)) +d "mv .d1 a0,a2" f1080001 0x0 (set a2 (var a0)) +# .D-unit extended logical/arith (C64x+): src2, src1, dst order. +d "and .d2 -8,b15,b15" f209bf07 0x0 (set b15 (& (bv 32 0xfffffff8) (var b15))) +d "or .d1x a3,b4,a4" b0781002 0x0 (set a4 (| (var a3) (var b4))) +d "andn .d2 b7,b5,b6" 33e81403 0x0 (set b6 (& (var b5) (~ (var b7)))) +d "xor .d2 0x1,b0,b13" f22b8006 0x0 (set b13 (^ (bv 32 0x1) (var b0))) +# .D address arithmetic: ADDAD scales src1 by 8; ADDAW by 4. src2, src1, dst. +d "addad .d2 b15,0x4,b4" c29e3c02 0x0 (set b4 (+ (var b15) (<< (bv 32 0x4) (bv 32 0x3) false))) +d "addaw .d1 a4,a3,a6" 417c1003 0x0 (set a6 (+ (var a4) (<< (var a3) (bv 32 0x2) false))) +# Plain .D add/sub also print src2, src1, dst with the short constant unsigned. +d "sub .d2 b15,0x10,b15" c209be07 0x0 (set b15 (- (var b15) (bv 32 0x10))) +# MVK on the .D unit (C64x+): 5-bit signed constant move (op-0 slot). +d "mvk .d2 0,b4" 43000002 0x0 (set b4 (bv 32 0x0)) +d "[b0] mvk .d1 0,a12" 40000026 0x0 (branch (! (is_zero (var b0))) (set a12 (bv 32 0x0)) nop) +# ANDN on .L (cross-path form). +d "andn .l1x a4,b4,a0" 989f1000 0x0 (set a0 (& (var b4) (~ (var a4)))) +# ADDKPC (.S2, C64x+): dst = PCE1 + scst7*4; target shown as an address. +d "addkpc .s2 0x54,b3,0" 62018501 0x4c (set b3 (bv 32 0x54)) +d "addkpc .s2 0x40,b3,2" 62418801 0x34 (set b3 (bv 32 0x40)) +# BDEC (.S, C64x+): register-conditional loop branch, target = PCE1 + scst10*4. +d "[a0] bdec .s1 0x18c,a0" 217000c0 0x19c +# MVD (.M, C64x+): 4-cycle register move. +d "mvd .m1 a0,a0" f0400300 0x0 (set a0 (var a0)) +# SUB on .L with the cross-path src1 (xsint form). +d "sub .l2x a4,b4,b4" fa921002 0x0 (set b4 (- (var a4) (var b4))) +# SUBAW on .D with a ucst5 offset. +d "[!b0] subaw .d2 b4,0x19,b4" c33d1332 0x0 (branch (is_zero (var b0)) (set b4 (- (var b4) (<< (bv 32 0x19) (bv 32 0x2) false))) nop) +# SHL on .S of a 40-bit long (register pairs); value prints before the count. +d "shl .s2 b9:b8,0x3,b7:b6" 226c2003 0x0 (seq (set _d (& (<< (>> (<< (append (var b9) (var b8)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var b9) (var b8)) (bv 32 0x18) false))) (bv 32 0x3) false) (bv 64 0xffffffffff))) (set b6 (cast 32 false (var _d))) (set b7 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +# Long (40-bit) compare on .L: src2 is a register pair, dst a single. +d "cmpgt .l1 0x0,a5:a4,a0" 98081000 0x0 (set a0 (ite (! (sle (cast 64 (msb (bv 32 0x0)) (bv 32 0x0)) (>> (<< (append (var a5) (var a4)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a5) (var a4)) (bv 32 0x18) false))))) (bv 32 0x1) (bv 32 0x0))) +# B IRP (.S2): return from a maskable interrupt (branch to the IRP register). +d "b .s2 irp" e2001800 0x0 (jmp (var irp)) +# ANDN on .S in the extended (bits[5:2]==1100) format. +d "andn .s1 a5,a4,a5" b0ad9002 0x0 (set a5 (& (var a4) (~ (var a5)))) +# MPYLI (.M, C64x+): 16x32 -> 64 multiply, writing a register pair. +d "mpyli .m2 b6,b5,b7:b6" 73c51403 0x0 (seq (set _d (* (cast 64 (msb (cast 32 (msb (cast 16 false (var b6))) (cast 16 false (var b6)))) (cast 32 (msb (cast 16 false (var b6))) (cast 16 false (var b6)))) (cast 64 (msb (var b5)) (var b5)))) (set b6 (cast 32 false (var _d))) (set b7 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpy32 .m1 a0,a1,a2" 00080401 0x0 (set a2 (* (var a0) (var a1))) +d "mpy32u .m1 a0,a1,a3:a2" 30060401 0x0 (seq (set _d (* (cast 64 false (var a0)) (cast 64 false (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +# C64x+ address/rotate/byte-merge/packed-shift/pack (RzIL) +d "addad .d1 a4,a5,a6" 40be1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x3) false))) +d "rotl .m1 a1,a0,a2" 70270001 0x0 (set a2 (let _rv (var a0) (let _rn (& (var a1) (bv 32 0x1f)) (| (<< (var _rv) (var _rn) false) (>> (var _rv) (- (bv 32 0x20) (var _rn)) false))))) +d "shlmb .s1 a0,a1,a2" 700e0401 0x0 (set a2 (| (<< (var a1) (bv 32 0x8) false) (& (>> (var a0) (bv 32 0x18) false) (bv 32 0xff)))) +d "shrmb .s1 a0,a1,a2" b00e0401 0x0 (set a2 (| (>> (var a1) (bv 32 0x8) false) (<< (& (var a0) (bv 32 0xff)) (bv 32 0x18) false))) +d "shr2 .s1 a0,a1,a2" f02d0001 0x0 (set a2 (let _sv (var a0) (let _sn (cast 16 false (& (var a1) (bv 32 0xf))) (append (>> (cast 16 false (>> (var _sv) (bv 32 0x10) false)) (var _sn) (msb (cast 16 false (>> (var _sv) (bv 32 0x10) false)))) (>> (cast 16 false (var _sv)) (var _sn) (msb (cast 16 false (var _sv)))))))) +d "shru2 .s1 a0,a1,a2" 302e0001 0x0 (set a2 (let _sv (var a0) (let _sn (cast 16 false (& (var a1) (bv 32 0xf))) (append (>> (cast 16 false (>> (var _sv) (bv 32 0x10) false)) (var _sn) false) (>> (cast 16 false (var _sv)) (var _sn) false))))) +d "swap4 .l1 a0,a2" 58230001 0x0 (set a2 (let _w4 (var a0) (| (<< (& (var _w4) (bv 32 0xff00ff)) (bv 32 0x8) false) (& (>> (var _w4) (bv 32 0x8) false) (bv 32 0xff00ff))))) +d "packl4 .l1 a0,a1,a2" 180d0401 0x0 (set a2 (let _q1 (var a0) (let _q2 (var a1) (| (| (<< (& (>> (var _q1) (bv 32 0x10) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (var _q1) (bv 32 0x0) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (var _q2) (bv 32 0x10) false) (bv 32 0xff)) (bv 32 0x8) false) (& (>> (var _q2) (bv 32 0x0) false) (bv 32 0xff))))))) +d "packh4 .l1 a0,a1,a2" 380d0401 0x0 (set a2 (let _q1 (var a0) (let _q2 (var a1) (| (| (<< (& (>> (var _q1) (bv 32 0x18) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (var _q1) (bv 32 0x8) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (var _q2) (bv 32 0x18) false) (bv 32 0xff)) (bv 32 0x8) false) (& (>> (var _q2) (bv 32 0x8) false) (bv 32 0xff))))))) +# C64x+ bit reverse/count, deal/shuffle and expand-to-mask (RzIL) +d "bitr .m1 a1,a2" f0e00701 0x0 (set a2 (let _r0 (var a1) (let _r1 (| (<< (& (var _r0) (bv 32 0x55555555)) (bv 32 0x1) false) (& (>> (var _r0) (bv 32 0x1) false) (bv 32 0x55555555))) (let _r2 (| (<< (& (var _r1) (bv 32 0x33333333)) (bv 32 0x2) false) (& (>> (var _r1) (bv 32 0x2) false) (bv 32 0x33333333))) (let _r3 (| (<< (& (var _r2) (bv 32 0xf0f0f0f)) (bv 32 0x4) false) (& (>> (var _r2) (bv 32 0x4) false) (bv 32 0xf0f0f0f))) (let _r4 (| (<< (& (var _r3) (bv 32 0xff00ff)) (bv 32 0x8) false) (& (>> (var _r3) (bv 32 0x8) false) (bv 32 0xff00ff))) (| (<< (& (var _r4) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (var _r4) (bv 32 0x10) false) (bv 32 0xffff))))))))) +d "bitc4 .m1 a1,a2" f0c00701 0x0 (set a2 (let _b0 (var a1) (let _b1 (- (var _b0) (& (>> (var _b0) (bv 32 0x1) false) (bv 32 0x55555555))) (let _b2 (+ (& (var _b1) (bv 32 0x33333333)) (& (>> (var _b1) (bv 32 0x2) false) (bv 32 0x33333333))) (& (+ (var _b2) (>> (var _b2) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)))))) +d "xpnd2 .m1 a1,a2" f0200701 0x0 (set a2 (let _x (var a1) (| (<< (* (& (>> (var _x) (bv 32 0x1) false) (bv 32 0x1)) (bv 32 0xffff)) (bv 32 0x10) false) (* (& (var _x) (bv 32 0x1)) (bv 32 0xffff))))) +d "xpnd4 .m1 a1,a2" f0000701 0x0 (set a2 (let _x (var a1) (| (| (<< (* (& (>> (var _x) (bv 32 0x3) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x18) false) (<< (* (& (>> (var _x) (bv 32 0x2) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (* (& (>> (var _x) (bv 32 0x1) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x8) false) (* (& (var _x) (bv 32 0x1)) (bv 32 0xff)))))) +d "deal .m1 a1,a2" f0a00701 0x0 (set a2 (let _dx (var a1) (let _e0 (& (var _dx) (bv 32 0x55555555)) (let _e1 (& (| (var _e0) (>> (var _e0) (bv 32 0x1) false)) (bv 32 0x33333333)) (let _e2 (& (| (var _e1) (>> (var _e1) (bv 32 0x2) false)) (bv 32 0xf0f0f0f)) (let _e3 (& (| (var _e2) (>> (var _e2) (bv 32 0x4) false)) (bv 32 0xff00ff)) (let _elo (& (| (var _e3) (>> (var _e3) (bv 32 0x8) false)) (bv 32 0xffff)) (let _o0 (& (>> (var _dx) (bv 32 0x1) false) (bv 32 0x55555555)) (let _o1 (& (| (var _o0) (>> (var _o0) (bv 32 0x1) false)) (bv 32 0x33333333)) (let _o2 (& (| (var _o1) (>> (var _o1) (bv 32 0x2) false)) (bv 32 0xf0f0f0f)) (let _o3 (& (| (var _o2) (>> (var _o2) (bv 32 0x4) false)) (bv 32 0xff00ff)) (let _ohi (& (| (var _o3) (>> (var _o3) (bv 32 0x8) false)) (bv 32 0xffff)) (| (<< (var _ohi) (bv 32 0x10) false) (var _elo)))))))))))))) +d "shfl .m1 a1,a2" f0800701 0x0 (set a2 (let _sx (var a1) (let _l0 (& (var _sx) (bv 32 0xffff)) (let _l1 (& (| (var _l0) (<< (var _l0) (bv 32 0x8) false)) (bv 32 0xff00ff)) (let _l2 (& (| (var _l1) (<< (var _l1) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)) (let _l3 (& (| (var _l2) (<< (var _l2) (bv 32 0x2) false)) (bv 32 0x33333333)) (let _llo (& (| (var _l3) (<< (var _l3) (bv 32 0x1) false)) (bv 32 0x55555555)) (let _h0 (& (>> (var _sx) (bv 32 0x10) false) (bv 32 0xffff)) (let _h1 (& (| (var _h0) (<< (var _h0) (bv 32 0x8) false)) (bv 32 0xff00ff)) (let _h2 (& (| (var _h1) (<< (var _h1) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)) (let _h3 (& (| (var _h2) (<< (var _h2) (bv 32 0x2) false)) (bv 32 0x33333333)) (let _hhi (& (| (var _h3) (<< (var _h3) (bv 32 0x1) false)) (bv 32 0x55555555)) (| (<< (var _hhi) (bv 32 0x1) false) (var _llo)))))))))))))) +# packed saturating add, reverse-pack, packed average, variable saturating shift +d "saddu4 .s1 a0,a1,a2" f00c0401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (| (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff)))) (bv 32 0x18) false) (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff)))) (bv 32 0x10) false)) (| (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff)))) (bv 32 0x8) false) (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff)))) (bv 32 0x0) false)))))) +d "rpack2 .s1 a0,a1,a2" f00e0411 0x0 (set a2 (| (<< (& (>> (let _s (<< (cast 64 (msb (var a0)) (var a0)) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (bv 32 0x10) false) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (let _s (<< (cast 64 (msb (var a1)) (var a1)) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (bv 32 0x10) false) (bv 32 0xffff)))) +d "avg2 .m1 a0,a1,a2" f0040401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (<< (& (>> (+ (+ (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false)))) (bv 32 0x1)) (bv 32 0x1) (msb (+ (+ (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false)))) (bv 32 0x1)))) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (+ (+ (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b)))) (bv 32 0x1)) (bv 32 0x1) (msb (+ (+ (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b)))) (bv 32 0x1)))) (bv 32 0xffff)))))) +d "avgu4 .m1 a0,a1,a2" b0040401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (| (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x8) false) (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x0) false)))))) +d "saddus2 .s1 a0,a1,a2" 700c0401 0x0 (set a2 (| (<< (let _hi (+ (& (>> (var a0) (bv 32 0x10) false) (bv 32 0xffff)) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (ite (&& (sle (var _hi) (bv 32 0x0)) (! (== (var _hi) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _hi) (bv 32 0xffff))) (bv 32 0xffff) (var _hi)))) (bv 32 0x10) false) (let _lo (+ (& (var a0) (bv 32 0xffff)) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1)))) (ite (&& (sle (var _lo) (bv 32 0x0)) (! (== (var _lo) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _lo) (bv 32 0xffff))) (bv 32 0xffff) (var _lo)))))) +d "sshvl .m1 a0,a1,a2" 30270001 0x0 (set a2 (let _v (var a0) (let _sc (var a1) (let _n (ite (! (sle (var _sc) (bv 32 0x1f))) (bv 32 0x1f) (ite (&& (sle (var _sc) (bv 32 0xffffffe1)) (! (== (var _sc) (bv 32 0xffffffe1)))) (bv 32 0xffffffe1) (var _sc))) (ite (|| (! (sle (var _n) (bv 32 0x0))) (== (var _n) (bv 32 0x0))) (let _s (<< (cast 64 (msb (var _v)) (var _v)) (var _n) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (>> (var _v) (~- (var _n)) (msb (var _v)))))))) +d "sshvr .m1 a0,a1,a2" b0260001 0x0 (set a2 (let _v (var a0) (let _sc (var a1) (let _n (ite (! (sle (var _sc) (bv 32 0x1f))) (bv 32 0x1f) (ite (&& (sle (var _sc) (bv 32 0xffffffe1)) (! (== (var _sc) (bv 32 0xffffffe1)))) (bv 32 0xffffffe1) (var _sc))) (ite (|| (! (sle (var _n) (bv 32 0x0))) (== (var _n) (bv 32 0x0))) (>> (var _v) (var _n) (msb (var _v))) (let _s (<< (cast 64 (msb (var _v)) (var _v)) (~- (var _n)) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))))))) +# .L-unit byte-merge shifts and C64x+ packed multiply/pack (decode + RzIL) +d "shlmb .l1 a0,a1,a2" 380c0401 0x0 (set a2 (| (<< (var a1) (bv 32 0x8) false) (& (>> (var a0) (bv 32 0x18) false) (bv 32 0xff)))) +d "shrmb .l1 a0,a1,a2" 580c0401 0x0 (set a2 (| (>> (var a1) (bv 32 0x8) false) (<< (& (var a0) (bv 32 0xff)) (bv 32 0x18) false))) +d "mpy2 .m1 a0,a1,a3:a2" 30000401 0x0 (seq (set _d (append (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1)))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "smpy2 .m1 a0,a1,a3:a2" 70000401 0x0 (seq (set _d (append (let _s (<< (cast 64 (msb (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))))) (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))))) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (let _s (<< (cast 64 (msb (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))))) (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))))) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyhi .m1 a0,a1,a3:a2" 30050401 0x0 (seq (set _d (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyli .m1 a0,a1,a3:a2" 70050401 0x0 (seq (set _d (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyhir .m1 a0,a1,a2" 30040401 0x0 (set a2 (cast 32 false (>> (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)) (bv 32 0xf) (msb (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)))))) +d "mpylir .m1 a0,a1,a2" b0030401 0x0 (set a2 (cast 32 false (>> (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)) (bv 32 0xf) (msb (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)))))) +d "spacku4 .s1 a0,a1,a2" 300d0401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (let _h3 (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (let _h2 (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (let _h1 (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false))) (let _h0 (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b))) (| (| (<< (ite (&& (sle (var _h3) (bv 32 0x0)) (! (== (var _h3) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h3) (bv 32 0xff))) (bv 32 0xff) (var _h3))) (bv 32 0x18) false) (<< (ite (&& (sle (var _h2) (bv 32 0x0)) (! (== (var _h2) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h2) (bv 32 0xff))) (bv 32 0xff) (var _h2))) (bv 32 0x10) false)) (| (<< (ite (&& (sle (var _h1) (bv 32 0x0)) (! (== (var _h1) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h1) (bv 32 0xff))) (bv 32 0xff) (var _h1))) (bv 32 0x8) false) (ite (&& (sle (var _h0) (bv 32 0x0)) (! (== (var _h0) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h0) (bv 32 0xff))) (bv 32 0xff) (var _h0)))))))))))) +d "norm .l1 a1:a0,a2" 180c0001 0x0 (set a2 (- (let _q0 (let _nl (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (^ (var _nl) (>> (var _nl) (bv 32 0x3f) (msb (var _nl))))) (let _q1 (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (<< (var _q0) (bv 32 0x20) false) (var _q0)) (let _q2 (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (<< (var _q1) (bv 32 0x10) false) (var _q1)) (let _q3 (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (<< (var _q2) (bv 32 0x8) false) (var _q2)) (let _q4 (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (<< (var _q3) (bv 32 0x4) false) (var _q3)) (let _q5 (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (<< (var _q4) (bv 32 0x2) false) (var _q4)) (let _q6 (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (<< (var _q5) (bv 32 0x1) false) (var _q5)) (+ (+ (+ (+ (+ (+ (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (bv 32 0x20) (bv 32 0x0)) (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (bv 32 0x10) (bv 32 0x0))) (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _q6) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0)))))))))) (bv 32 0x19))) diff --git a/test/db/asm/tms320_c674x_32 b/test/db/asm/tms320_c674x_32 new file mode 100644 index 00000000000..f0d5851cada --- /dev/null +++ b/test/db/asm/tms320_c674x_32 @@ -0,0 +1,209 @@ +# TMS320C674x (unified fixed + floating-point VLIW) native disassembler +# Instruction words are little-endian; addresses are the packet PCE1. +# Several vectors are cross-checked against TI dis6x (c6000 v7.4.4). +d "nop" 00000000 0x0 nop +d "nop 5" 00800000 0x0 nop +d "mvk .s1 0x0,a0" 28000000 0x0 (set a0 (bv 32 0x0)) +d "mvk .s2 0x0,b0" 2a000000 0x0 (set b0 (bv 32 0x0)) +d "mvkh .s1 0x0,a5" 68008002 0x0 (set a5 (| (& (var a5) (bv 32 0xffff)) (bv 32 0x0))) +d "add .l1 a0,a1,a2" 78000401 0x0 (set a2 (+ (var a0) (var a1))) +d "and .l1 0x8,a3,a0" 580f0d00 0x0 (set a0 (& (bv 32 0x8) (var a3))) +d "mpysp .m1 a0,a1,a2" 000e0401 0x0 (set a2 (fbits (*. rne (float 0 (var a0) ) (float 0 (var a1) )))) +d "adddp .l1 a1:a0,a3:a2,a5:a4" 18030802 0x0 (seq (set _d (fbits (+. rne (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpydp .m1 a1:a0,a3:a2,a5:a4" 00070802 0x0 (seq (set _d (fbits (*. rne (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "add2 .s1 a1,a2,a3" 60208801 0x0 (set a3 (append (+ (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false))) (+ (cast 16 false (var a1)) (cast 16 false (var a2))))) +d "sub2 .s2 b1,b2,b3" 62248801 0x0 (set b3 (append (- (cast 16 false (>> (var b1) (bv 32 0x10) false)) (cast 16 false (>> (var b2) (bv 32 0x10) false))) (- (cast 16 false (var b1)) (cast 16 false (var b2))))) +d "ldb .d1 *a4,a3" 24029001 0x0 (set a3 (cast 32 (msb (loadw 0 8 (+ (var a4) (bv 32 0x0)))) (loadw 0 8 (+ (var a4) (bv 32 0x0))))) +d "ldbu .d1 *+a3[0x1],a3" 14228c01 0x0 (set a3 (cast 32 false (loadw 0 8 (+ (var a3) (bv 32 0x1))))) +d "ldw .d2 *-b1[b5],b28" e6a8041e 0x0 (set b28 (loadw 0 32 (- (var b1) (* (var b5) (bv 32 0x4))))) +d "lddw .d1 *a4,a1:a0" 64031000 0x0 (seq (set ea (+ (var a4) (bv 32 0x0))) (set a0 (loadw 0 32 (var ea))) (set a1 (loadw 0 32 (+ (var ea) (bv 32 0x4))))) +d "[a0] stb .d2 b0,*b4++[0x1]" b63610c0 0x0 (branch (! (is_zero (var a0))) (seq (storew 0 (var b4) (cast 8 false (var b0))) (set b4 (+ (var b4) (bv 32 0x1)))) nop) +d "[a2] ldw .d2 *+b14[0x302c],b3" 6e2cb0a1 0x0 (branch (! (is_zero (var a2))) (set b3 (loadw 0 32 (+ (var b14) (bv 32 0xc0b0)))) nop) +d "[a0] mv .l2x a5,b5" db1f94c2 0x0 (branch (! (is_zero (var a0))) (set b5 (var a5)) nop) +d "[a0] mv .l1x b5,a3" d91f94c1 0x0 (branch (! (is_zero (var a0))) (set a3 (var b5)) nop) +d "[b1] neg .l1 a6,a6" d8001843 0x0 (branch (! (is_zero (var b1))) (set a6 (~- (var a6))) nop) +d "[a0] sub .l2 b4,0x1,b4" 5ae013c2 0x0 (branch (! (is_zero (var a0))) (set b4 (- (var b4) (bv 32 0x1))) nop) +d "bnop .s2 b3,0x4" 62838c00 0x0 (jmp (var b3)) +d "b .s1 0x20" 10040000 0x0 (jmp (bv 32 0x20)) +d "b .s2 b3" 62030c00 0x0 (jmp (var b3)) +d ".fphead n, l, w, bu, nobr, nosat, 0001000b" 000000e1 0x1c +d ".fphead n, l, w, b, nobr, nosat, 1111010b" 082841ef 0x5c +d "[b0] bnop .s1 0x30,0x4" 21810820 0x38 (branch (! (is_zero (var b0))) (jmp (bv 32 0x30)) nop) +d "dint" 00400010 0x0 +d "rint" 00600010 0x0 +d "shru .s1 a3,0x14,a6" a1890e03 0x0 (set a6 (>> (var a3) (bv 32 0x14) false)) +d "shr .s1 a6,0x2,a6" a04d1803 0x0 (set a6 (>> (var a6) (bv 32 0x2) (msb (var a6)))) +d "cmpgtsp .s1 a10,a3,a1" 614e8d00 0x0 (set a1 (ite (<. (float 0 (var a3) ) (float 0 (var a10) )) (bv 32 0x1) (bv 32 0x0))) +d "cmpltsp .s1 a4,a3,a1" a18e8c00 0x0 (set a1 (ite (<. (float 0 (var a4) ) (float 0 (var a3) )) (bv 32 0x1) (bv 32 0x0))) +d "cmpgtdp .s2 b5:b4,b7:b6,b1" 628a9800 0x0 (set b1 (ite (<. (float 1 (append (var b7) (var b6)) ) (float 1 (append (var b5) (var b4)) )) (bv 32 0x1) (bv 32 0x0))) +d "cmpltdp .s2 b11:b10,b5:b4,b0" a24a1100 0x0 (set b0 (ite (<. (float 1 (append (var b11) (var b10)) ) (float 1 (append (var b5) (var b4)) )) (bv 32 0x1) (bv 32 0x0))) +d "absdp .s2 b11:b10,b5:b4" 224b2d02 0x0 (seq (set _d (& (append (var b11) (var b10)) (bv 64 0x7fffffffffffffff))) (set b4 (cast 32 false (var _d))) (set b5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpydp .m1 a5:a4,a7:a6,a5:a4" 00871802 0x0 (seq (set _d (fbits (*. rne (float 1 (append (var a5) (var a4)) ) (float 1 (append (var a7) (var a6)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "intsp .l2 b4,b5" 5a099002 0x0 (set b5 (fbits (fcast_sfloat ieee754-bin32 rne (var b4)))) +d "mvc .s2 csr,b4" e2030402 0x0 (set b4 (var csr)) +d "mvc .s2 b6,csr" a2039800 0x0 (set csr (var b6)) +d "set .s2 b4,31,31,b4" 8aff1302 0x0 (set b4 (| (var b4) (bv 32 0x80000000))) +d "extu .s2 b4,1,21,b4" 0b351002 0x0 (set b4 (>> (<< (var b4) (bv 32 0x1) false) (bv 32 0x15) false)) +d "addaw .d2 b15,6,b4" fe060012 0x0 (set b4 (+ (var b15) (<< (bv 32 0x6) (bv 32 0x2) false))) +d "addsp .s2 b5,b7,b5" 1aae9c02 0x0 (set b5 (fbits (+. rne (float 0 (var b5) ) (float 0 (var b7) )))) +d "adddp .s1x a5:a4,b11:b10,a7:a6" 589e2803 0x0 (seq (set _d (fbits (+. rne (float 1 (append (var a5) (var a4)) ) (float 1 (append (var b11) (var b10)) )))) (set a6 (cast 32 false (var _d))) (set a7 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "xor .d2x b7,a4,b5" b3fb9002 0x0 (set b5 (^ (var b7) (var a4))) +d "add .d1x 0x8,b15,a4" f01a3d02 0x0 (set a4 (+ (bv 32 0x8) (var b15))) +d "mpy32u .m1 a8,a5,a7:a6" 30061503 0x0 (seq (set _d (* (cast 64 false (var a8)) (cast 64 false (var a5)))) (set a6 (cast 32 false (var _d))) (set a7 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyspdp .m1x b4,a11:a10,a7:a6" b0952803 0x0 (seq (set _d (fbits (*. rne (fconvert ieee754-bin64 rne (float 0 (var b4) )) (float 1 (append (var a11) (var a10)) )))) (set a6 (cast 32 false (var _d))) (set a7 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpysu .m1 0xc,a11,a3" 008fad01 0x0 (set a3 (* (cast 32 (msb (cast 16 false (bv 32 0xc))) (cast 16 false (bv 32 0xc))) (cast 32 false (cast 16 false (var a11))))) +# Assembler idioms rendered like the TI reference (dis6x): MV/NEG/NOT/ZERO and +# the negative-constant SUB, plus the 40-bit (slong) SUB/ADD pair destinations. +d "zero .l1 a3" f8008001 0x0 (set a3 (bv 32 0x0)) +d "zero .l1 a7:a6" f8040003 0x0 (seq (set a6 (bv 32 0x0)) (set a7 (bv 32 0x0))) +d "not .s1 a3,a3" a0e28f01 0x0 (set a3 (~ (var a3))) +d "sub .l1x b4,0x6,a1" 58509300 0x0 (set a1 (- (var b4) (bv 32 0x6))) +d "cmpgtu .l1 a5,a7:a6,a1" b8a99800 0x0 (set a1 (ite (! (ule (cast 64 false (var a5)) (& (append (var a7) (var a6)) (bv 64 0xffffffffff)))) (bv 32 0x1) (bv 32 0x0))) +# RzIL scalar-core coverage (bytes from TI asm6x): addk, the five compares, +# signed ext, idle, sign/zero-extended halfword loads, 16x16 multiplies, neg, +# register shl, and byte/half/word stores. Each line's IL is validated by rz-asm. +d "addk .s1 0x64,a0" 50320000 0x0 (set a0 (+ (var a0) (bv 32 0x64))) +d "cmpeq .l1 a1,a2,a3" 782a8801 0x0 (set a3 (ite (== (var a1) (var a2)) (bv 32 0x1) (bv 32 0x0))) +d "cmpgt .l1 a1,a2,a3" f8288801 0x0 (set a3 (ite (! (sle (var a1) (var a2))) (bv 32 0x1) (bv 32 0x0))) +d "cmpgtu .l1 a1,a2,a3" f8298801 0x0 (set a3 (ite (! (ule (var a1) (var a2))) (bv 32 0x1) (bv 32 0x0))) +d "cmplt .l1 a1,a2,a3" f82a8801 0x0 (set a3 (ite (&& (sle (var a1) (var a2)) (! (== (var a1) (var a2)))) (bv 32 0x1) (bv 32 0x0))) +d "cmpltu .l1 a1,a2,a3" f82b8801 0x0 (set a3 (ite (&& (ule (var a1) (var a2)) (! (== (var a1) (var a2)))) (bv 32 0x1) (bv 32 0x0))) +d "ext .s1 a1,8,24,a3" 48188501 0x0 (set a3 (>> (<< (var a1) (bv 32 0x8) false) (bv 32 0x18) (msb (<< (var a1) (bv 32 0x8) false)))) +d "idle" 00e00100 0x0 nop +d "ldh .d1 *a4,a3" 44029001 0x0 (set a3 (cast 32 (msb (loadw 0 16 (+ (var a4) (bv 32 0x0)))) (loadw 0 16 (+ (var a4) (bv 32 0x0))))) +d "ldhu .d1 *a4,a3" 04029001 0x0 (set a3 (cast 32 false (loadw 0 16 (+ (var a4) (bv 32 0x0))))) +d "mpy .m1 a1,a2,a3" 802c8801 0x0 (set a3 (* (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))) (cast 32 (msb (cast 16 false (var a2))) (cast 16 false (var a2))))) +d "mpyh .m1 a1,a2,a3" 80208801 0x0 (set a3 (* (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false))))) +d "mpyu .m1 a0,a1,a2" 800f0401 0x0 (set a2 (* (cast 32 false (cast 16 false (var a0))) (cast 32 false (cast 16 false (var a1))))) +d "mpysu .m1 a0,a1,a2" 800d0401 0x0 (set a2 (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 false (cast 16 false (var a1))))) +d "mpyhu .m1 a0,a1,a2" 80030401 0x0 (set a2 (* (cast 32 false (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 false (cast 16 false (>> (var a1) (bv 32 0x10) false))))) +d "mpyluhs .m1 a0,a1,a2" 800a0401 0x0 (set a2 (* (cast 32 false (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))))) +d "smpy .m1 a0,a1,a2" 000d0401 0x0 (set a2 (let _p (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1)))) (ite (== (var _p) (bv 32 0x40000000)) (bv 32 0x7fffffff) (<< (var _p) (bv 32 0x1) false)))) +d "smpylh .m1 a0,a1,a2" 00090401 0x0 (set a2 (let _p (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (ite (== (var _p) (bv 32 0x40000000)) (bv 32 0x7fffffff) (<< (var _p) (bv 32 0x1) false)))) +d "mpyi .m1 a0,a1,a2" 00020401 0x0 (set a2 (* (var a0) (var a1))) +d "mpyid .m1 a0,a1,a3:a2" 00040401 0x0 (seq (set _d (* (cast 64 (msb (var a0)) (var a0)) (cast 64 (msb (var a1)) (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "sptrunc .l1 a0,a2" 78010001 0x0 (set a2 (fcast_sint 32 rtz (float 0 (var a0) ))) +d "neg .l1 a1,a3" d8008401 0x0 (set a3 (~- (var a1))) +d "shl .s1 a1,0x2,a3" a04c8401 0x0 (set a3 (<< (var a1) (bv 32 0x2) false)) +d "stw .d1 a3,*a4" 74029001 0x0 (storew 0 (+ (var a4) (bv 32 0x0)) (var a3)) +d "stb .d1 a3,*a4" 34029001 0x0 (storew 0 (+ (var a4) (bv 32 0x0)) (cast 8 false (var a3))) +d "sth .d1 a3,*a4" 54029001 0x0 (storew 0 (+ (var a4) (bv 32 0x0)) (cast 16 false (var a3))) +# Non-aligned doubleword store (STNDW): same 64-bit data path as STDW. +d "stndw .d1 a3:a2,*a4" 74039001 0x0 (seq (set ea (+ (var a4) (bv 32 0x0))) (storew 0 (var ea) (var a2)) (storew 0 (+ (var ea) (bv 32 0x4)) (var a3))) +# MVC reads of further control registers: irp (common to all C6000) and ssr +# (C64x+ extension), confirming the control-register bindings resolve. +d "mvc .s2 irp,b4" e2031802 0x0 (set b4 (var irp)) +d "mvc .s2 ssr,b4" e2035402 0x0 (set b4 (var ssr)) +# Packed byte add/sub (ADD4/SUB4, C64x+): four independent 8-bit lanes. +d "add4 .l1 a1,a2,a3" b82c8801 0x0 (set a3 (append (+ (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (append (+ (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (append (+ (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (+ (cast 8 false (var a1)) (cast 8 false (var a2))))))) +d "sub4 .l1 a1,a2,a3" d82c8801 0x0 (set a3 (append (- (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (append (- (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (append (- (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (- (cast 8 false (var a1)) (cast 8 false (var a2))))))) +# Halfword packs (PACK2/PACKH2/PACKLH2/PACKHL2) and byte unpacks (UNPKLU4/UNPKHU4). +d "pack2 .l1 a1,a2,a3" 18208801 0x0 (set a3 (append (cast 16 false (var a1)) (cast 16 false (var a2)))) +d "packh2 .l1 a1,a2,a3" d8238801 0x0 (set a3 (append (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) +d "packlh2 .l1 a1,a2,a3" 78238801 0x0 (set a3 (append (cast 16 false (var a1)) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) +d "packhl2 .l1 a1,a2,a3" 98238801 0x0 (set a3 (append (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (var a2)))) +d "unpklu4 .l1 a2,a3" 58438801 0x0 (set a3 (append (cast 16 false (cast 8 false (>> (var a2) (bv 32 0x8) false))) (cast 16 false (cast 8 false (var a2))))) +d "unpkhu4 .l1 a2,a3" 58638801 0x0 (set a3 (append (cast 16 false (cast 8 false (>> (var a2) (bv 32 0x18) false))) (cast 16 false (cast 8 false (>> (var a2) (bv 32 0x10) false))))) +# Saturating packed ops (C64x+): signed halfword add/sub (SADD2/SSUB2) and the +# signed 32->16 pack (SPACK2), each lane clamped to the signed range. +d "sadd2 .s1 a1,a2,a3" 302c8801 0x0 (set a3 (append (let _sat (+ (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))) (let _sat (+ (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))) (cast 32 (msb (cast 16 false (var a2))) (cast 16 false (var a2)))) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))))) +d "ssub2 .l1 a1,a2,a3" 982c8801 0x0 (set a3 (append (let _sat (- (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))) (let _sat (- (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))) (cast 32 (msb (cast 16 false (var a2))) (cast 16 false (var a2)))) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))))) +d "spack2 .s1 a1,a2,a3" b02c8801 0x0 (set a3 (append (let _sat (var a1) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))) (let _sat (var a2) (ite (! (sle (var _sat) (bv 32 0x7fff))) (bv 16 0x7fff) (ite (&& (sle (var _sat) (bv 32 0xffff8000)) (! (== (var _sat) (bv 32 0xffff8000)))) (bv 16 0x8000) (cast 16 false (var _sat))))))) +# Dot products (DOTP2 = lo*lo+hi*hi, DOTPN2 = hi*hi-lo*lo) and packed min/max +# (MIN2/MAX2 signed halfwords, MINU4/MAXU4 unsigned bytes). +d "dotp2 .m1 a1,a2,a3" 30238801 0x0 (set a3 (+ (* (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))) (cast 32 (msb (cast 16 false (var a2))) (cast 16 false (var a2)))) (* (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false)))))) +d "dotpn2 .m1 a1,a2,a3" 70228801 0x0 (set a3 (- (* (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) (* (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))) (cast 32 (msb (cast 16 false (var a2))) (cast 16 false (var a2)))))) +d "min2 .l1 a1,a2,a3" 38288801 0x0 (set a3 (append (ite (sle (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false))) (ite (sle (cast 16 false (var a1)) (cast 16 false (var a2))) (cast 16 false (var a1)) (cast 16 false (var a2))))) +d "max2 .l1 a1,a2,a3" 58288801 0x0 (set a3 (append (ite (sle (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false))) (cast 16 false (>> (var a2) (bv 32 0x10) false)) (cast 16 false (>> (var a1) (bv 32 0x10) false))) (ite (sle (cast 16 false (var a1)) (cast 16 false (var a2))) (cast 16 false (var a2)) (cast 16 false (var a1))))) +d "minu4 .l1 a1,a2,a3" 18298801 0x0 (set a3 (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (ite (ule (cast 8 false (var a1)) (cast 8 false (var a2))) (cast 8 false (var a1)) (cast 8 false (var a2))))))) +d "maxu4 .l1 a1,a2,a3" 78288801 0x0 (set a3 (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (cast 8 false (>> (var a2) (bv 32 0x18) false)) (cast 8 false (>> (var a1) (bv 32 0x18) false))) (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (cast 8 false (>> (var a2) (bv 32 0x10) false)) (cast 8 false (>> (var a1) (bv 32 0x10) false))) (append (ite (ule (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (cast 8 false (>> (var a2) (bv 32 0x8) false)) (cast 8 false (>> (var a1) (bv 32 0x8) false))) (ite (ule (cast 8 false (var a1)) (cast 8 false (var a2))) (cast 8 false (var a2)) (cast 8 false (var a1))))))) +# Absolute value with saturation (ABS 32-bit, ABS2 per-halfword) and four-way +# byte dot products (DOTPU4 unsigned, DOTPSU4 signed src1 x unsigned src2). +d "abs .l1 a2,a3" 58038801 0x0 (set a3 (let _a (var a2) (ite (|| (! (sle (var _a) (bv 32 0x0))) (== (var _a) (bv 32 0x0))) (var _a) (ite (== (var _a) (bv 32 0x80000000)) (bv 32 0x7fffffff) (~- (var _a)))))) +d "abs2 .l1 a2,a3" 58838801 0x0 (set a3 (append (let _h (cast 16 false (>> (var a2) (bv 32 0x10) false)) (ite (|| (! (sle (var _h) (bv 16 0x0))) (== (var _h) (bv 16 0x0))) (var _h) (ite (== (var _h) (bv 16 0x8000)) (bv 16 0x7fff) (~- (var _h))))) (let _h (cast 16 false (var a2)) (ite (|| (! (sle (var _h) (bv 16 0x0))) (== (var _h) (bv 16 0x0))) (var _h) (ite (== (var _h) (bv 16 0x8000)) (bv 16 0x7fff) (~- (var _h))))))) +d "dotpu4 .m1 a1,a2,a3" b0218801 0x0 (set a3 (+ (+ (+ (* (cast 32 false (cast 8 false (var a1))) (cast 32 false (cast 8 false (var a2)))) (* (cast 32 false (cast 8 false (>> (var a1) (bv 32 0x8) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x8) false))))) (* (cast 32 false (cast 8 false (>> (var a1) (bv 32 0x10) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x10) false))))) (* (cast 32 false (cast 8 false (>> (var a1) (bv 32 0x18) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x18) false)))))) +d "dotpsu4 .m1 a1,a2,a3" b0208801 0x0 (set a3 (+ (+ (+ (* (cast 32 (msb (cast 8 false (var a1))) (cast 8 false (var a1))) (cast 32 false (cast 8 false (var a2)))) (* (cast 32 (msb (cast 8 false (>> (var a1) (bv 32 0x8) false))) (cast 8 false (>> (var a1) (bv 32 0x8) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x8) false))))) (* (cast 32 (msb (cast 8 false (>> (var a1) (bv 32 0x10) false))) (cast 8 false (>> (var a1) (bv 32 0x10) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x10) false))))) (* (cast 32 (msb (cast 8 false (>> (var a1) (bv 32 0x18) false))) (cast 8 false (>> (var a1) (bv 32 0x18) false))) (cast 32 false (cast 8 false (>> (var a2) (bv 32 0x18) false)))))) +# Packed compares to a bit-per-lane mask: CMPEQ2/CMPGT2 (signed halfwords), +# CMPEQ4/CMPGTU4 (bytes, unsigned for CMPGTU4); lane i sets bit i. +d "cmpeq2 .s1 a1,a2,a3" 60278801 0x0 (set a3 (cast 32 false (append (ite (== (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false))) (bv 1 0x1) (bv 1 0x0)) (ite (== (cast 16 false (var a1)) (cast 16 false (var a2))) (bv 1 0x1) (bv 1 0x0))))) +d "cmpgt2 .s1 a1,a2,a3" 20258801 0x0 (set a3 (cast 32 false (append (ite (! (sle (cast 16 false (>> (var a1) (bv 32 0x10) false)) (cast 16 false (>> (var a2) (bv 32 0x10) false)))) (bv 1 0x1) (bv 1 0x0)) (ite (! (sle (cast 16 false (var a1)) (cast 16 false (var a2)))) (bv 1 0x1) (bv 1 0x0))))) +d "cmpeq4 .s1 a1,a2,a3" 20278801 0x0 (set a3 (cast 32 false (append (ite (== (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false))) (bv 1 0x1) (bv 1 0x0)) (append (ite (== (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false))) (bv 1 0x1) (bv 1 0x0)) (append (ite (== (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false))) (bv 1 0x1) (bv 1 0x0)) (ite (== (cast 8 false (var a1)) (cast 8 false (var a2))) (bv 1 0x1) (bv 1 0x0))))))) +d "cmpgtu4 .s1 a1,a2,a3" 60258801 0x0 (set a3 (cast 32 false (append (ite (! (ule (cast 8 false (>> (var a1) (bv 32 0x18) false)) (cast 8 false (>> (var a2) (bv 32 0x18) false)))) (bv 1 0x1) (bv 1 0x0)) (append (ite (! (ule (cast 8 false (>> (var a1) (bv 32 0x10) false)) (cast 8 false (>> (var a2) (bv 32 0x10) false)))) (bv 1 0x1) (bv 1 0x0)) (append (ite (! (ule (cast 8 false (>> (var a1) (bv 32 0x8) false)) (cast 8 false (>> (var a2) (bv 32 0x8) false)))) (bv 1 0x1) (bv 1 0x0)) (ite (! (ule (cast 8 false (var a1)) (cast 8 false (var a2)))) (bv 1 0x1) (bv 1 0x0))))))) +# Single-precision float: ADDSP/SUBSP/MPYSP (IEEE-754 binary32, round to nearest +# even) and INTSP (signed int -> single). +d "addsp .l1 a1,a2,a3" 18228801 0x0 (set a3 (fbits (+. rne (float 0 (var a1) ) (float 0 (var a2) )))) +d "subsp .l1 a1,a2,a3" 38228801 0x0 (set a3 (fbits (-. rne (float 0 (var a1) ) (float 0 (var a2) )))) +d "mpysp .m1 a1,a2,a3" 002e8801 0x0 (set a3 (fbits (*. rne (float 0 (var a1) ) (float 0 (var a2) )))) +d "intsp .l1 a2,a3" 58098801 0x0 (set a3 (fbits (fcast_sfloat ieee754-bin32 rne (var a2)))) +# Double-precision float over register pairs (IEEE-754 binary64): ADDDP/SUBDP/ +# MPYDP, plus INTDP (int -> double) and SPDP (single -> double). +d "adddp .l1 a1:a0,a3:a2,a5:a4" 18030802 0x0 (seq (set _d (fbits (+. rne (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "subdp .l1 a1:a0,a3:a2,a5:a4" 38030802 0x0 (seq (set _d (fbits (-. rne (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpydp .m1 a1:a0,a3:a2,a5:a4" 00070802 0x0 (seq (set _d (fbits (*. rne (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "intdp .l1 a2,a5:a4" 38070802 0x0 (seq (set _d (fbits (fcast_sfloat ieee754-bin64 rne (var a2)))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "spdp .s1 a2,a5:a4" a0000802 0x0 (seq (set _d (fbits (fconvert ieee754-bin64 rne (float 0 (var a2) )))) (set a4 (cast 32 false (var _d))) (set a5 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "spint .l1 a2,a3" 58018801 0x0 (set a3 (fcast_sint 32 rne (float 0 (var a2) ))) +# address arithmetic, saturating ALU, rotate, byte-merge, packed-shift, pack (RzIL) +d "addab .d1 a4,a5,a6" 40b81003 0x0 (set a6 (+ (var a4) (var a5))) +d "addah .d1 a4,a5,a6" 40ba1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x1) false))) +d "addaw .d1 a4,a5,a6" 40bc1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x2) false))) +d "subab .d1 a4,a5,a6" c0b81003 0x0 (set a6 (- (var a4) (var a5))) +d "subah .d1 a4,a5,a6" c0ba1003 0x0 (set a6 (- (var a4) (<< (var a5) (bv 32 0x1) false))) +d "subaw .d1 a4,a5,a6" c0bc1003 0x0 (set a6 (- (var a4) (<< (var a5) (bv 32 0x2) false))) +d "sadd .l1 a0,a1,a2" 78020401 0x0 (set a2 (let _s (+ (cast 64 (msb (var a0)) (var a0)) (cast 64 (msb (var a1)) (var a1))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "ssub .l1 a0,a1,a2" f8010401 0x0 (set a2 (let _s (- (cast 64 (msb (var a0)) (var a0)) (cast 64 (msb (var a1)) (var a1))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "sshl .s1 a0,a1,a2" e0280001 0x0 (set a2 (let _s (<< (cast 64 (msb (var a0)) (var a0)) (& (var a1) (bv 32 0x1f)) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "addad .d1 a4,a5,a6" 40be1003 0x0 (set a6 (+ (var a4) (<< (var a5) (bv 32 0x3) false))) +d "rotl .m1 a1,a0,a2" 70270001 0x0 (set a2 (let _rv (var a0) (let _rn (& (var a1) (bv 32 0x1f)) (| (<< (var _rv) (var _rn) false) (>> (var _rv) (- (bv 32 0x20) (var _rn)) false))))) +d "shlmb .s1 a0,a1,a2" 700e0401 0x0 (set a2 (| (<< (var a1) (bv 32 0x8) false) (& (>> (var a0) (bv 32 0x18) false) (bv 32 0xff)))) +d "shrmb .s1 a0,a1,a2" b00e0401 0x0 (set a2 (| (>> (var a1) (bv 32 0x8) false) (<< (& (var a0) (bv 32 0xff)) (bv 32 0x18) false))) +d "shr2 .s1 a0,a1,a2" f02d0001 0x0 (set a2 (let _sv (var a0) (let _sn (cast 16 false (& (var a1) (bv 32 0xf))) (append (>> (cast 16 false (>> (var _sv) (bv 32 0x10) false)) (var _sn) (msb (cast 16 false (>> (var _sv) (bv 32 0x10) false)))) (>> (cast 16 false (var _sv)) (var _sn) (msb (cast 16 false (var _sv)))))))) +d "shru2 .s1 a0,a1,a2" 302e0001 0x0 (set a2 (let _sv (var a0) (let _sn (cast 16 false (& (var a1) (bv 32 0xf))) (append (>> (cast 16 false (>> (var _sv) (bv 32 0x10) false)) (var _sn) false) (>> (cast 16 false (var _sv)) (var _sn) false))))) +d "swap4 .l1 a0,a2" 58230001 0x0 (set a2 (let _w4 (var a0) (| (<< (& (var _w4) (bv 32 0xff00ff)) (bv 32 0x8) false) (& (>> (var _w4) (bv 32 0x8) false) (bv 32 0xff00ff))))) +d "packl4 .l1 a0,a1,a2" 180d0401 0x0 (set a2 (let _q1 (var a0) (let _q2 (var a1) (| (| (<< (& (>> (var _q1) (bv 32 0x10) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (var _q1) (bv 32 0x0) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (var _q2) (bv 32 0x10) false) (bv 32 0xff)) (bv 32 0x8) false) (& (>> (var _q2) (bv 32 0x0) false) (bv 32 0xff))))))) +d "packh4 .l1 a0,a1,a2" 380d0401 0x0 (set a2 (let _q1 (var a0) (let _q2 (var a1) (| (| (<< (& (>> (var _q1) (bv 32 0x18) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (var _q1) (bv 32 0x8) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (var _q2) (bv 32 0x18) false) (bv 32 0xff)) (bv 32 0x8) false) (& (>> (var _q2) (bv 32 0x8) false) (bv 32 0xff))))))) +# bit-field set/clear and leftmost-bit/normalize (RzIL) +d "set .s1 a1,8,16,a2" 88100501 0x0 (set a2 (| (var a1) (bv 32 0x1ff00))) +d "clr .s1 a1,8,16,a2" c8100501 0x0 (set a2 (& (var a1) (bv 32 0xfffe00ff))) +d "lmbd .l1 a0,a1,a2" 780d0401 0x0 (set a2 (let _z0 (let _lm (var a1) (ite (is_zero (& (var a0) (bv 32 0x1))) (~ (var _lm)) (var _lm))) (let _z1 (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (<< (var _z0) (bv 32 0x10) false) (var _z0)) (let _z2 (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (<< (var _z1) (bv 32 0x8) false) (var _z1)) (let _z3 (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (<< (var _z2) (bv 32 0x4) false) (var _z2)) (let _z4 (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (<< (var _z3) (bv 32 0x2) false) (var _z3)) (let _z5 (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (<< (var _z4) (bv 32 0x1) false) (var _z4)) (+ (+ (+ (+ (+ (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (bv 32 0x10) (bv 32 0x0)) (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _z5) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0)))))))))) +d "norm .l1 a1,a2" 780c0401 0x0 (set a2 (- (let _z0 (let _nm (var a1) (^ (var _nm) (>> (var _nm) (bv 32 0x1f) (msb (var _nm))))) (let _z1 (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (<< (var _z0) (bv 32 0x10) false) (var _z0)) (let _z2 (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (<< (var _z1) (bv 32 0x8) false) (var _z1)) (let _z3 (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (<< (var _z2) (bv 32 0x4) false) (var _z2)) (let _z4 (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (<< (var _z3) (bv 32 0x2) false) (var _z3)) (let _z5 (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (<< (var _z4) (bv 32 0x1) false) (var _z4)) (+ (+ (+ (+ (+ (ite (is_zero (>> (var _z0) (bv 32 0x10) false)) (bv 32 0x10) (bv 32 0x0)) (ite (is_zero (>> (var _z1) (bv 32 0x18) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _z2) (bv 32 0x1c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _z3) (bv 32 0x1e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _z4) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _z5) (bv 32 0x1f) false)) (bv 32 0x1) (bv 32 0x0))))))))) (bv 32 0x1))) +# C64x+ bit reverse/count, deal/shuffle and expand-to-mask (RzIL) +d "bitr .m1 a1,a2" f0e00701 0x0 (set a2 (let _r0 (var a1) (let _r1 (| (<< (& (var _r0) (bv 32 0x55555555)) (bv 32 0x1) false) (& (>> (var _r0) (bv 32 0x1) false) (bv 32 0x55555555))) (let _r2 (| (<< (& (var _r1) (bv 32 0x33333333)) (bv 32 0x2) false) (& (>> (var _r1) (bv 32 0x2) false) (bv 32 0x33333333))) (let _r3 (| (<< (& (var _r2) (bv 32 0xf0f0f0f)) (bv 32 0x4) false) (& (>> (var _r2) (bv 32 0x4) false) (bv 32 0xf0f0f0f))) (let _r4 (| (<< (& (var _r3) (bv 32 0xff00ff)) (bv 32 0x8) false) (& (>> (var _r3) (bv 32 0x8) false) (bv 32 0xff00ff))) (| (<< (& (var _r4) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (var _r4) (bv 32 0x10) false) (bv 32 0xffff))))))))) +d "bitc4 .m1 a1,a2" f0c00701 0x0 (set a2 (let _b0 (var a1) (let _b1 (- (var _b0) (& (>> (var _b0) (bv 32 0x1) false) (bv 32 0x55555555))) (let _b2 (+ (& (var _b1) (bv 32 0x33333333)) (& (>> (var _b1) (bv 32 0x2) false) (bv 32 0x33333333))) (& (+ (var _b2) (>> (var _b2) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)))))) +d "xpnd2 .m1 a1,a2" f0200701 0x0 (set a2 (let _x (var a1) (| (<< (* (& (>> (var _x) (bv 32 0x1) false) (bv 32 0x1)) (bv 32 0xffff)) (bv 32 0x10) false) (* (& (var _x) (bv 32 0x1)) (bv 32 0xffff))))) +d "xpnd4 .m1 a1,a2" f0000701 0x0 (set a2 (let _x (var a1) (| (| (<< (* (& (>> (var _x) (bv 32 0x3) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x18) false) (<< (* (& (>> (var _x) (bv 32 0x2) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (* (& (>> (var _x) (bv 32 0x1) false) (bv 32 0x1)) (bv 32 0xff)) (bv 32 0x8) false) (* (& (var _x) (bv 32 0x1)) (bv 32 0xff)))))) +d "deal .m1 a1,a2" f0a00701 0x0 (set a2 (let _dx (var a1) (let _e0 (& (var _dx) (bv 32 0x55555555)) (let _e1 (& (| (var _e0) (>> (var _e0) (bv 32 0x1) false)) (bv 32 0x33333333)) (let _e2 (& (| (var _e1) (>> (var _e1) (bv 32 0x2) false)) (bv 32 0xf0f0f0f)) (let _e3 (& (| (var _e2) (>> (var _e2) (bv 32 0x4) false)) (bv 32 0xff00ff)) (let _elo (& (| (var _e3) (>> (var _e3) (bv 32 0x8) false)) (bv 32 0xffff)) (let _o0 (& (>> (var _dx) (bv 32 0x1) false) (bv 32 0x55555555)) (let _o1 (& (| (var _o0) (>> (var _o0) (bv 32 0x1) false)) (bv 32 0x33333333)) (let _o2 (& (| (var _o1) (>> (var _o1) (bv 32 0x2) false)) (bv 32 0xf0f0f0f)) (let _o3 (& (| (var _o2) (>> (var _o2) (bv 32 0x4) false)) (bv 32 0xff00ff)) (let _ohi (& (| (var _o3) (>> (var _o3) (bv 32 0x8) false)) (bv 32 0xffff)) (| (<< (var _ohi) (bv 32 0x10) false) (var _elo)))))))))))))) +d "shfl .m1 a1,a2" f0800701 0x0 (set a2 (let _sx (var a1) (let _l0 (& (var _sx) (bv 32 0xffff)) (let _l1 (& (| (var _l0) (<< (var _l0) (bv 32 0x8) false)) (bv 32 0xff00ff)) (let _l2 (& (| (var _l1) (<< (var _l1) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)) (let _l3 (& (| (var _l2) (<< (var _l2) (bv 32 0x2) false)) (bv 32 0x33333333)) (let _llo (& (| (var _l3) (<< (var _l3) (bv 32 0x1) false)) (bv 32 0x55555555)) (let _h0 (& (>> (var _sx) (bv 32 0x10) false) (bv 32 0xffff)) (let _h1 (& (| (var _h0) (<< (var _h0) (bv 32 0x8) false)) (bv 32 0xff00ff)) (let _h2 (& (| (var _h1) (<< (var _h1) (bv 32 0x4) false)) (bv 32 0xf0f0f0f)) (let _h3 (& (| (var _h2) (<< (var _h2) (bv 32 0x2) false)) (bv 32 0x33333333)) (let _hhi (& (| (var _h3) (<< (var _h3) (bv 32 0x1) false)) (bv 32 0x55555555)) (| (<< (var _hhi) (bv 32 0x1) false) (var _llo)))))))))))))) +# subc, packed saturating add, reverse-pack, packed average, variable saturating shift +d "subc .l1 a0,a1,a2" 78090401 0x0 (set a2 (let _s1 (var a0) (let _df (- (var _s1) (var a1)) (ite (|| (! (sle (var _df) (bv 32 0x0))) (== (var _df) (bv 32 0x0))) (| (<< (var _df) (bv 32 0x1) false) (bv 32 0x1)) (<< (var _s1) (bv 32 0x1) false))))) +d "saddu4 .s1 a0,a1,a2" f00c0401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (| (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff)))) (bv 32 0x18) false) (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff)))) (bv 32 0x10) false)) (| (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff)))) (bv 32 0x8) false) (<< (ite (! (ule (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff))) (bv 32 0xff))) (bv 32 0xff) (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff)))) (bv 32 0x0) false)))))) +d "rpack2 .s1 a0,a1,a2" f00e0411 0x0 (set a2 (| (<< (& (>> (let _s (<< (cast 64 (msb (var a0)) (var a0)) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (bv 32 0x10) false) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (let _s (<< (cast 64 (msb (var a1)) (var a1)) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (bv 32 0x10) false) (bv 32 0xffff)))) +d "avg2 .m1 a0,a1,a2" f0040401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (<< (& (>> (+ (+ (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false)))) (bv 32 0x1)) (bv 32 0x1) (msb (+ (+ (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false)))) (bv 32 0x1)))) (bv 32 0xffff)) (bv 32 0x10) false) (& (>> (+ (+ (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b)))) (bv 32 0x1)) (bv 32 0x1) (msb (+ (+ (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b)))) (bv 32 0x1)))) (bv 32 0xffff)))))) +d "avgu4 .m1 a0,a1,a2" b0040401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (| (| (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x18) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x18) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x18) false) (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x10) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x10) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x10) false)) (| (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x8) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x8) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x8) false) (<< (& (>> (+ (+ (& (>> (var _a) (bv 32 0x0) false) (bv 32 0xff)) (& (>> (var _b) (bv 32 0x0) false) (bv 32 0xff))) (bv 32 0x1)) (bv 32 0x1) false) (bv 32 0xff)) (bv 32 0x0) false)))))) +d "saddus2 .s1 a0,a1,a2" 700c0401 0x0 (set a2 (| (<< (let _hi (+ (& (>> (var a0) (bv 32 0x10) false) (bv 32 0xffff)) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (ite (&& (sle (var _hi) (bv 32 0x0)) (! (== (var _hi) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _hi) (bv 32 0xffff))) (bv 32 0xffff) (var _hi)))) (bv 32 0x10) false) (let _lo (+ (& (var a0) (bv 32 0xffff)) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1)))) (ite (&& (sle (var _lo) (bv 32 0x0)) (! (== (var _lo) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _lo) (bv 32 0xffff))) (bv 32 0xffff) (var _lo)))))) +d "sshvl .m1 a0,a1,a2" 30270001 0x0 (set a2 (let _v (var a0) (let _sc (var a1) (let _n (ite (! (sle (var _sc) (bv 32 0x1f))) (bv 32 0x1f) (ite (&& (sle (var _sc) (bv 32 0xffffffe1)) (! (== (var _sc) (bv 32 0xffffffe1)))) (bv 32 0xffffffe1) (var _sc))) (ite (|| (! (sle (var _n) (bv 32 0x0))) (== (var _n) (bv 32 0x0))) (let _s (<< (cast 64 (msb (var _v)) (var _v)) (var _n) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (>> (var _v) (~- (var _n)) (msb (var _v)))))))) +d "sshvr .m1 a0,a1,a2" b0260001 0x0 (set a2 (let _v (var a0) (let _sc (var a1) (let _n (ite (! (sle (var _sc) (bv 32 0x1f))) (bv 32 0x1f) (ite (&& (sle (var _sc) (bv 32 0xffffffe1)) (! (== (var _sc) (bv 32 0xffffffe1)))) (bv 32 0xffffffe1) (var _sc))) (ite (|| (! (sle (var _n) (bv 32 0x0))) (== (var _n) (bv 32 0x0))) (>> (var _v) (var _n) (msb (var _v))) (let _s (<< (cast 64 (msb (var _v)) (var _v)) (~- (var _n)) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))))))) +# .L byte-merge shifts, NORM long, C64x+ packed multiply/pack (decode + RzIL) +d "shlmb .l1 a0,a1,a2" 380c0401 0x0 (set a2 (| (<< (var a1) (bv 32 0x8) false) (& (>> (var a0) (bv 32 0x18) false) (bv 32 0xff)))) +d "shrmb .l1 a0,a1,a2" 580c0401 0x0 (set a2 (| (>> (var a1) (bv 32 0x8) false) (<< (& (var a0) (bv 32 0xff)) (bv 32 0x18) false))) +d "mpy2 .m1 a0,a1,a3:a2" 30000401 0x0 (seq (set _d (append (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false)))) (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1)))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "smpy2 .m1 a0,a1,a3:a2" 70000401 0x0 (seq (set _d (append (let _s (<< (cast 64 (msb (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))))) (* (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 32 (msb (cast 16 false (>> (var a1) (bv 32 0x10) false))) (cast 16 false (>> (var a1) (bv 32 0x10) false))))) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))) (let _s (<< (cast 64 (msb (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))))) (* (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0))) (cast 32 (msb (cast 16 false (var a1))) (cast 16 false (var a1))))) (bv 32 0x1) false) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s))))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyhi .m1 a0,a1,a3:a2" 30050401 0x0 (seq (set _d (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyli .m1 a0,a1,a3:a2" 70050401 0x0 (seq (set _d (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1)))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "mpyhir .m1 a0,a1,a2" 30040401 0x0 (set a2 (cast 32 false (>> (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)) (bv 32 0xf) (msb (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 32 (msb (cast 16 false (>> (var a0) (bv 32 0x10) false))) (cast 16 false (>> (var a0) (bv 32 0x10) false)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)))))) +d "mpylir .m1 a0,a1,a2" b0030401 0x0 (set a2 (cast 32 false (>> (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)) (bv 32 0xf) (msb (+ (* (cast 64 (msb (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 32 (msb (cast 16 false (var a0))) (cast 16 false (var a0)))) (cast 64 (msb (var a1)) (var a1))) (bv 64 0x4000)))))) +d "spacku4 .s1 a0,a1,a2" 300d0401 0x0 (set a2 (let _a (var a0) (let _b (var a1) (let _h3 (cast 32 (msb (cast 16 false (>> (var _a) (bv 32 0x10) false))) (cast 16 false (>> (var _a) (bv 32 0x10) false))) (let _h2 (cast 32 (msb (cast 16 false (var _a))) (cast 16 false (var _a))) (let _h1 (cast 32 (msb (cast 16 false (>> (var _b) (bv 32 0x10) false))) (cast 16 false (>> (var _b) (bv 32 0x10) false))) (let _h0 (cast 32 (msb (cast 16 false (var _b))) (cast 16 false (var _b))) (| (| (<< (ite (&& (sle (var _h3) (bv 32 0x0)) (! (== (var _h3) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h3) (bv 32 0xff))) (bv 32 0xff) (var _h3))) (bv 32 0x18) false) (<< (ite (&& (sle (var _h2) (bv 32 0x0)) (! (== (var _h2) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h2) (bv 32 0xff))) (bv 32 0xff) (var _h2))) (bv 32 0x10) false)) (| (<< (ite (&& (sle (var _h1) (bv 32 0x0)) (! (== (var _h1) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h1) (bv 32 0xff))) (bv 32 0xff) (var _h1))) (bv 32 0x8) false) (ite (&& (sle (var _h0) (bv 32 0x0)) (! (== (var _h0) (bv 32 0x0)))) (bv 32 0x0) (ite (! (sle (var _h0) (bv 32 0xff))) (bv 32 0xff) (var _h0)))))))))))) +d "norm .l1 a1:a0,a2" 180c0001 0x0 (set a2 (- (let _q0 (let _nl (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (^ (var _nl) (>> (var _nl) (bv 32 0x3f) (msb (var _nl))))) (let _q1 (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (<< (var _q0) (bv 32 0x20) false) (var _q0)) (let _q2 (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (<< (var _q1) (bv 32 0x10) false) (var _q1)) (let _q3 (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (<< (var _q2) (bv 32 0x8) false) (var _q2)) (let _q4 (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (<< (var _q3) (bv 32 0x4) false) (var _q3)) (let _q5 (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (<< (var _q4) (bv 32 0x2) false) (var _q4)) (let _q6 (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (<< (var _q5) (bv 32 0x1) false) (var _q5)) (+ (+ (+ (+ (+ (+ (ite (is_zero (>> (var _q0) (bv 32 0x20) false)) (bv 32 0x20) (bv 32 0x0)) (ite (is_zero (>> (var _q1) (bv 32 0x30) false)) (bv 32 0x10) (bv 32 0x0))) (ite (is_zero (>> (var _q2) (bv 32 0x38) false)) (bv 32 0x8) (bv 32 0x0))) (ite (is_zero (>> (var _q3) (bv 32 0x3c) false)) (bv 32 0x4) (bv 32 0x0))) (ite (is_zero (>> (var _q4) (bv 32 0x3e) false)) (bv 32 0x2) (bv 32 0x0))) (ite (is_zero (>> (var _q5) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0))) (ite (is_zero (>> (var _q6) (bv 32 0x3f) false)) (bv 32 0x1) (bv 32 0x0)))))))))) (bv 32 0x19))) +# 40-bit long ABS/SAT decode, single/double float abs (RzIL) +d "abs .l1 a1:a0,a3:a2" 18070001 0x0 (seq (set _d (let _a (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (ite (|| (! (sle (var _a) (bv 64 0x0))) (== (var _a) (bv 64 0x0))) (var _a) (ite (== (var _a) (bv 64 0xffffff8000000000)) (bv 64 0x7fffffffff) (~- (var _a)))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +d "sat .l1 a1:a0,a2" 18080001 0x0 (set a2 (let _s (>> (<< (append (var a1) (var a0)) (bv 32 0x18) false) (bv 32 0x18) (msb (<< (append (var a1) (var a0)) (bv 32 0x18) false))) (ite (! (sle (var _s) (bv 64 0x7fffffff))) (bv 32 0x7fffffff) (ite (&& (sle (var _s) (bv 64 0xffffffff80000000)) (! (== (var _s) (bv 64 0xffffffff80000000)))) (bv 32 0x80000000) (cast 32 false (var _s)))))) +d "abssp .s1 a0,a2" 200f0001 0x0 (set a2 (& (var a0) (bv 32 0x7fffffff))) +d "absdp .s1 a1:a0,a3:a2" 200b0401 0x0 (seq (set _d (& (append (var a1) (var a0)) (bv 64 0x7fffffffffffffff))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +# single/double float equality and SPxSP->DP multiply (decode + RzIL) +d "cmpeqsp .s1 a0,a1,a2" 200e0401 0x0 (set a2 (ite (! (|| (|| (is_nan (float 0 (var a0) )) (is_nan (float 0 (var a1) ))) (|| (<. (float 0 (var a0) ) (float 0 (var a1) )) (<. (float 0 (var a1) ) (float 0 (var a0) ))))) (bv 32 0x1) (bv 32 0x0))) +d "cmpeqdp .s1 a1:a0,a3:a2,a4" 200a0802 0x0 (set a4 (ite (! (|| (|| (is_nan (float 1 (append (var a1) (var a0)) )) (is_nan (float 1 (append (var a3) (var a2)) ))) (|| (<. (float 1 (append (var a1) (var a0)) ) (float 1 (append (var a3) (var a2)) )) (<. (float 1 (append (var a3) (var a2)) ) (float 1 (append (var a1) (var a0)) ))))) (bv 32 0x1) (bv 32 0x0))) +d "mpysp2dp .m1 a0,a1,a3:a2" f0050401 0x0 (seq (set _d (fbits (*. rne (fconvert ieee754-bin64 rne (float 0 (var a0) )) (fconvert ieee754-bin64 rne (float 0 (var a1) ))))) (set a2 (cast 32 false (var _d))) (set a3 (cast 32 false (>> (var _d) (bv 32 0x20) false)))) +# double-precision -> int/single conversions (decode + RzIL; encoded per SPRUFE8, not disassembled by dis6x) +d "dpint .l1 a1:a0,a2" 18010401 0x0 (set a2 (fcast_sint 32 rne (float 1 (append (var a1) (var a0)) ))) +d "dpsp .l1 a1:a0,a2" 38010401 0x0 (set a2 (fbits (fconvert ieee754-bin32 rne (float 1 (append (var a1) (var a0)) )))) +d "dptrunc .l1 a1:a0,a2" 38000401 0x0 (set a2 (fcast_sint 32 rtz (float 1 (append (var a1) (var a0)) ))) diff --git a/test/db/formats/coff b/test/db/formats/coff index 564b9ef2674..1b16ab13a2f 100644 --- a/test/db/formats/coff +++ b/test/db/formats/coff @@ -277,3 +277,23 @@ bcc 0x2F, tc1 mov xar0, ac3 EOF RUN + +NAME=TMS320C6000 COFF relocation application +FILE=bins/tms320/coff2/c6x_reloc.ticoff2.obj +CMDS=<