diff --git a/librz/arch/isa/tms320/c55_ir.c b/librz/arch/isa/tms320/c55_ir.c new file mode 100644 index 0000000000..954a89ce9d --- /dev/null +++ b/librz/arch/isa/tms320/c55_ir.c @@ -0,0 +1,3316 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file + * Shared, arch-independent layer of the TMS320 decode IR (see c55_ir.h): + * - c55_decode(): the table-walking decode engine + * - c55_x_*(): the operand extractors the per-arch tables reference + * - c55_generic_ea() etc: RzIL primitives consumed by each arch's ::lift + * - c55_format / c55_fill_analysis / c55_lift: the three pure consumers + * + * Per-arch specifics (encoding tables, register set, mnemonic/op-type/lift + * tables, exotic addressing) live behind a \ref C55ArchDesc; this file only + * uses that interface, so it is identical for C55x, C55x+ and a future C54x. + */ + +#include +#include +#include +#include "c55_ir.h" + +#include + +// --------------------------------------------------------------------------- +// decode engine +// --------------------------------------------------------------------------- + +// Pack the first \p n bytes (<= 8) MSB-first into a word: byte 0 is most +// significant, so a field's bit index counts from the LSB of the instruction. +static ut64 c55_pack(const ut8 *buf, int n) { + ut64 v = 0; + for (int i = 0; i < n && i < 8; i++) { + v = (v << 8) | buf[i]; + } + return v; +} + +// Top-aligned first 4 bytes, the value matched against C55InsnDef::mask/match. +static ut32 c55_head(const ut8 *buf, int len) { + ut32 h = 0; + for (int i = 0; i < 4; i++) { + h = (h << 8) | (ut32)(i < len ? buf[i] : 0); + } + return h; +} + +static ut64 c55_field(ut64 bits, ut8 lo, ut8 width) { + if (width == 0 || width >= 64) { + return bits >> lo; + } + return (bits >> lo) & (((ut64)1 << width) - 1); +} + +bool c55_decode(const C55ArchDesc *a, const ut8 *buf, int len, C55Insn *out) { + if (!a || !buf || len <= 0 || !out) { + return false; + } + memset(out, 0, sizeof(*out)); + out->arch = a->arch; + if (!a->table) { + return false; // arch decodes via this engine's own front-end, not this engine + } + // Conditional-execution prefix (C55x+ "if(!TC1)/if(TC1) execute D_Unit", + // opcodes 0x2e / 0x2f): a one-byte prefix in front of a D-unit instruction. + // The TI disassembler renders the prefixed instruction identically to the + // bare one, so when the byte that follows the prefix decodes to a complete + // instruction through this engine, emit that instruction with its size grown + // by the prefix byte. (When the following byte does not decode here -- the + // standalone 0x2e/0x2f encodings such as mmap/lock/linr -- fall through to + // the normal table walk / legacy front-end.) + if (a->cond_exec_prefix && len >= 2 && (buf[0] == 0x2e || buf[0] == 0x2f)) { + C55Insn sub; + if (c55_decode(a, buf + 1, len - 1, &sub) && sub.size > 0 && 1 + sub.size <= len) { + *out = sub; + out->size = sub.size + 1; + out->cond_exec = true; + return true; + } + } + // Parallel-pair prefix (C55x+ user-defined parallelism, opcodes 0x30-0x3F): a + // one-byte prefix whose low nibble is the total byte length of the pair (values + // below 4 mean 0xF + nibble). The two independent sub-instructions follow, + // back-to-back, and the TI disassembler renders them joined by " || ". The + // sub-instructions are re-decoded on demand by the format / lift / analysis + // consumers from the raw bytes kept here, so no sub-instruction state has to be + // embedded in C55Insn. (When the two halves do not both decode through this + // engine, fall through so the legacy front-end can handle the pair.) + if (a->parallel_prefix && len >= 2 && (buf[0] & 0xf0) == 0x30) { + ut8 total = buf[0] & 0x0f; + if (total < 4) { + total += 0xf; + } + if (total <= len && total <= sizeof(out->par_bytes)) { + C55Insn s1, s2; + if (c55_decode(a, buf + 1, total - 1, &s1) && s1.size > 0 && 1 + s1.size < total && + c55_decode(a, buf + 1 + s1.size, total - 1 - s1.size, &s2) && s2.size > 0 && + 1 + s1.size + s2.size == total) { + out->parallel_pair = true; + out->size = total; + memcpy(out->par_bytes, buf, total); + out->par_off1 = 1; + out->par_off2 = (ut8)(1 + s1.size); + return true; + } + } + } + const ut32 head = c55_head(buf, len); + for (size_t i = 0; i < a->table_len; i++) { + const C55InsnDef *def = &a->table[i]; + if ((head & def->mask) != def->match) { + continue; + } + int ilen = def->len ? def->len : (a->insn_len ? a->insn_len(buf, len) : 0); + if (ilen <= 0 || ilen > len) { + continue; + } + out->id = def->id; + out->lop = def->lop; + out->square = def->square; + out->shift16 = def->shift16; + out->mac_mov = def->mac_mov; + out->mant_nexp = def->mant_nexp; + out->mac_store = def->mac_store; + out->diff_pair = def->diff_pair; + out->diff_form = def->diff_form; + out->uns_all = def->uns_all; + if (def->side_load) { + out->side_load = true; + } + out->both = def->both; + out->xcc_guard = def->xcc_guard; + out->quad = def->quad; + out->size = (ut8)ilen; + // Parallel-execution marker: in the parallel-capable opcode range bit 0 + // of the leading byte is the "||" flag rather than part of the opcode. + // A row opts into this by leaving that bit unconstrained in its mask + // (e.g. 0xfe000000), in which case the bit's value selects the parallel + // form; rows that pin the bit (0xff000000) treat it as opcode. + out->parallel = !def->no_parallel && (def->mask & 0x01000000) == 0 && (head & 0x01000000) != 0; + const ut64 bits = c55_pack(buf, ilen); + if (def->alt_bit && (c55_field(bits, (ut8)(def->alt_bit - 1), 1) != 0)) { + // a variant selector beyond the 4-byte match head (e.g. firssub vs + // firsadd): switch to the alternate id / lift op. + out->id = def->alt_id; + out->lop = def->alt_lop; + } + if (def->dual && a->fill_dual) { + // dual "::" MAC: a dedicated filler builds the canonical 6-slot + // operand layout and the sub-op metadata (the conditional amar1 + // destination assignment does not fit the generic ops[] loop). + return a->fill_dual(a, bits, def, out); + } + // Variant flags packed into def->mods: each 6-bit field holds a bit + // position (within the packed instruction word) plus one, 0 meaning the + // flag is absent. Bits 0-5 select the rounding (r) variant, bits 6-11 the + // memory-MAC side-load (T3=) flag, bits 12-17 the 40-bit (M40) variant, + // bits 18-23 the fractional (f) variant. Six-bit fields so positions in + // the wider (5/6-byte) packed words are representable. + ut8 round_bit = def->mods & 0x3f; + if (round_bit) { + out->round = (bits >> (round_bit - 1)) & 1; + } + ut8 side_bit = (def->mods >> 6) & 0x3f; + if (side_bit) { + out->side_load = (bits >> (side_bit - 1)) & 1; + } + ut8 m40_bit = (def->mods >> 12) & 0x3f; + if (m40_bit) { + out->m40 = (bits >> (m40_bit - 1)) & 1; + } + ut8 fract_bit = (def->mods >> 18) & 0x3f; + if (fract_bit) { + out->fract = (bits >> (fract_bit - 1)) & 1; + } + ut8 n = 0; + bool ok = true; + for (ut8 k = 0; k < C55_MAX_OPS; k++) { + const C55OpDesc *od = &def->ops[k]; + if (!od->fn) { + break; + } + od->fn(a, bits, od, &out->ops[n]); + if (out->ops[n].kind == C55_OP_INVALID) { + // extractor saw an encoding it cannot represent yet: + // abandon the structured decode so the caller falls + // back to the legacy front-end. + ok = false; + break; + } + if (out->ops[n].kind != C55_OP_NONE) { + n++; + } + } + if (!ok) { + return false; + } + // Instruction-extending memory operand (*arN(#K16) long const-index): + // the 16-bit constant is a 2-byte extension following the base + // instruction. It is read big-endian (matching c55_pack), stored as the + // signed displacement, the size grows by two, and the parallel flag is + // cleared (the extension occupies the encoding space the "||" bit uses). + for (ut8 j = 0; j < n; j++) { + C55AddrMode am = out->ops[j].amode; + if (am == C55_AM_CONST_IDX || am == C55_AM_CONST_IDX_PRE || am == C55_AM_ABS16) { + if (ilen + 2 > len) { + return false; + } + ut64 ext = c55_pack(buf + ilen, 2); + // abs16's k16 is an unsigned page offset; the const-index + // displacement is a signed offset added to ARn. + out->ops[j].disp = (am == C55_AM_ABS16) + ? (st32)(ut16)ext + : (st32)(st16)(ut16)ext; + out->size = (ut8)(ilen + 2); + out->parallel = false; + } else if (am == C55_AM_ABSOLUTE && out->ops[j].abs_addr == C55_ABS_EXT) { + // Absolute *(#addr): the 24-bit byte address is a 3-byte + // extension following the base instruction (used by the + // byte-access mov forms). The extractor marks the pending + // address with the C55_ABS_EXT sentinel. + if (ilen + 3 > len) { + return false; + } + out->ops[j].abs_addr = c55_pack(buf + ilen, 3) & 0xffffff; + out->size = (ut8)(ilen + 3); + out->parallel = false; + } + } + out->n_ops = n; + return true; + } + return false; +} + +// --------------------------------------------------------------------------- +// operand extractors (referenced from the per-arch tables; provisional bodies +// here, tightened against each arch's bit layout when its table is added) +// --------------------------------------------------------------------------- + +void c55_x_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = (C55RegClass)d->param; + out->reg.num = (ut8)c55_field(bits, d->lo, d->width); + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, out->reg.sub) : NULL; + out->width = ri ? ri->width : 16; +} + +void c55_x_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = c55_field(bits, d->lo, d->width); + out->width = d->width ? d->width : 16; + out->imm_signed = (d->param & 1) != 0; + out->reltarget = (d->param & 2) != 0; + out->addr = (d->param & 4) != 0; + out->abs_target = (d->param & 8) != 0; +} + +void c55_x_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_MEM; + out->reg.cls = C55_RC_XAR; + out->reg.num = (ut8)c55_field(bits, d->lo, d->width); + out->reg.sub = C55_SUB_NONE; + out->amode = C55_AM_INDIRECT; + out->access = 16; +} + +void c55_x_cond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_COND; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)c55_field(bits, d->lo, d->width); + out->reg.sub = C55_SUB_NONE; + out->relop = (C55Relop)(d->param % 6); +} + +// --------------------------------------------------------------------------- +// RzIL primitives +// --------------------------------------------------------------------------- + +// VARG of the whole register backing \p r (sub-field slicing is done by callers). +static RzILOpPure *c55_reg_var(const C55ArchDesc *a, C55Reg r) { + const C55RegInfo *ri = a->reg_info ? a->reg_info(r.cls, r.num, C55_SUB_NONE) : NULL; + return (ri && ri->il_var) ? VARG(ri->il_var) : NULL; +} + +RzILOpPure *c55_generic_ea(const C55ArchDesc *a, const C55Operand *m) { + if (!a || !m) { + return NULL; + } + const ut32 aw = 24; // C55x byte-address space + if (m->amode == C55_AM_ABSOLUTE) { + return UN(aw, m->abs_addr); + } + RzILOpPure *ea; + if (m->amode == C55_AM_ABS16) { + // abs16(#k16): the data address is DPH:k16 -- the page register supplies + // the high bits and the unsigned 16-bit constant the low bits. + ut64 k16 = (ut64)((ut32)m->disp & 0xffff); + ea = a->mem.page_reg + ? LOGOR(SHIFTL(IL_FALSE, UNSIGNED(aw, VARG(a->mem.page_reg)), UN(8, 16)), UN(aw, k16)) + : UN(aw, k16); + } else { + RzILOpPure *base = c55_reg_var(a, m->reg); + if (!base) { + return NULL; + } + ea = UNSIGNED(aw, base); + if (m->amode == C55_AM_INDEXED) { + ea = ADD(ea, SN(aw, m->disp)); + } else if (m->amode == C55_AM_CONST_IDX || m->amode == C55_AM_CONST_IDX_PRE) { + // *arN(#K16) / *+arN(#K16): ARn is the base, the signed 16-bit + // constant is the offset. For the plain form ARn is not modified; + // the pre-modify form additionally writes ARn += K16 via + // c55_post_effect(). + ea = ADD(ea, SN(aw, m->disp)); + } else if (m->amode == C55_AM_IDXREG) { + RzILOpPure *idx = c55_reg_var(a, m->index); + if (idx) { + ea = ADD(ea, SIGNED(aw, idx)); + } + } + } + // INDIRECT / POST* / PRE* / POSTADD / POSTSUB: EA is the (current) base; the + // modification is emitted separately by c55_post_effect(). + if (a->mem.addr_unit_log2) { + ea = MUL(ea, UN(aw, (ut64)1 << a->mem.addr_unit_log2)); + } + return ea; +} + +// EA for any memory operand: generic modes here, exotic modes via the arch hook. +static RzILOpPure *c55_ea(const C55ArchDesc *a, const C55Operand *m) { + if (m->amode >= C55_AM_INDIRECT && m->amode <= C55_AM_ABS16) { + return c55_generic_ea(a, m); + } + return a->ea ? a->ea(a, m) : NULL; +} + +RzILOpPure *c55_read(const C55ArchDesc *a, const C55Operand *op) { + if (!a || !op) { + return NULL; + } + if (op->kind == C55_OP_IMM) { + return UN(op->width ? op->width : 16, op->imm); + } + if (op->kind == C55_OP_REG) { + RzILOpPure *v = c55_reg_var(a, op->reg); + if (!v) { + return NULL; + } + switch (op->reg.sub) { + case C55_SUB_LO: return CAST(16, IL_FALSE, v); + case C55_SUB_HI: return CAST(16, IL_FALSE, SHIFTR(IL_FALSE, v, UN(8, 16))); + case C55_SUB_GUARD: return CAST(8, IL_FALSE, SHIFTR(IL_FALSE, v, UN(8, 32))); + default: return v; + } + } + if (op->kind == C55_OP_MEM) { + RzILOpPure *addr = c55_ea(a, op); + return addr ? LOADW(op->access ? op->access : 16, addr) : NULL; + } + return NULL; +} + +RzILOpEffect *c55_write(const C55ArchDesc *a, const C55Operand *dst, RzILOpPure *val) { + if (!a || !dst || !val) { + rz_il_op_pure_free(val); + return NULL; + } + if (dst->kind == C55_OP_REG) { + const C55RegInfo *ri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + rz_il_op_pure_free(val); + return NULL; + } + if (dst->reg.sub == C55_SUB_NONE) { + return SETG(ri->il_var, val); + } + RzILOpPure *wide = UNSIGNED(ri->width, val); // half read-modify-write + switch (dst->reg.sub) { + case C55_SUB_LO: + return SETG(ri->il_var, LOGOR(LOGAND(VARG(ri->il_var), UN(ri->width, 0xffffff0000ULL)), wide)); + case C55_SUB_HI: + return SETG(ri->il_var, LOGOR(LOGAND(VARG(ri->il_var), UN(ri->width, 0xff0000ffffULL)), SHIFTL(IL_FALSE, wide, UN(8, 16)))); + case C55_SUB_GUARD: + return SETG(ri->il_var, LOGOR(LOGAND(VARG(ri->il_var), UN(ri->width, 0x00ffffffffULL)), SHIFTL(IL_FALSE, LOGAND(wide, UN(ri->width, 0xff)), UN(8, 32)))); + default: + return SETG(ri->il_var, wide); + } + } + if (dst->kind == C55_OP_MEM) { + RzILOpPure *addr = c55_ea(a, dst); + if (!addr) { + rz_il_op_pure_free(val); + return NULL; + } + return STOREW(addr, val); + } + rz_il_op_pure_free(val); + return NULL; +} + +RzILOpEffect *c55_post_effect(const C55ArchDesc *a, const C55Operand *m) { + if (!a || !m) { + return NULL; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(m->reg.cls, m->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + const ut32 pw = a->mem.ptr_width ? a->mem.ptr_width : 23; + switch (m->amode) { + case C55_AM_POSTINC: return SETG(ri->il_var, ADD(VARG(ri->il_var), UN(pw, 1))); + case C55_AM_POSTDEC: return SETG(ri->il_var, SUB(VARG(ri->il_var), UN(pw, 1))); + case C55_AM_POSTADD: { + RzILOpPure *idx = c55_reg_var(a, m->index); + return idx ? SETG(ri->il_var, ADD(VARG(ri->il_var), SIGNED(pw, idx))) : NULL; + } + case C55_AM_POSTSUB: { + RzILOpPure *idx = c55_reg_var(a, m->index); + return idx ? SETG(ri->il_var, SUB(VARG(ri->il_var), SIGNED(pw, idx))) : NULL; + } + case C55_AM_CONST_IDX_PRE: + // *+arN(#K16): pre-modify writes the signed constant back into ARn. + return SETG(ri->il_var, ADD(VARG(ri->il_var), SN(pw, m->disp))); + default: return NULL; + } +} + +// --------------------------------------------------------------------------- +// consumers +// --------------------------------------------------------------------------- + +// Resolve a PC-relative branch/call target: next-instruction address plus the +// sign-extended offset carried by the address-style immediate operand (its +// width sets the sign bit). Shared by the analysis and lifter consumers. +static ut64 c55_branch_target(const C55Insn *insn, ut64 pc) { + for (ut8 i = 0; i < insn->n_ops; i++) { + const C55Operand *o = &insn->ops[i]; + if (o->kind == C55_OP_IMM && (o->addr || o->reltarget)) { + ut32 w = o->width ? o->width : 16; + ut64 v = o->imm; + if (o->abs_target) { + // Absolute target: the operand is the destination address + // itself (24-bit program space), not a pc-relative offset. + return v & 0xffffff; + } + st64 soff = o->reltarget_unsigned + ? (st64)v + : ((w < 64 && (v & ((ut64)1 << (w - 1)))) ? (st64)(v - ((ut64)1 << w)) : (st64)v); + return pc + insn->size + soff; + } + } + return pc + insn->size; +} + +/* The instruction id alone can be ambiguous for control transfers: 'b' and + * 'call' cover both a register-indirect form (target in a register) and a + * direct form (a pc-relative or absolute immediate target). Refine the + * op_type by operand kind so a direct target reports as JMP/CALL while a + * register operand keeps the register-indirect UJMP/UCALL. */ +static ut32 c55_effective_type(const C55ArchDesc *a, const C55Insn *insn) { + ut32 type = a->op_type ? a->op_type(insn->id) : RZ_ANALYSIS_OP_TYPE_NULL; + if (insn->n_ops >= 1 && insn->ops[0].kind == C55_OP_IMM) { + if (type == RZ_ANALYSIS_OP_TYPE_UJMP) { + type = RZ_ANALYSIS_OP_TYPE_JMP; + } else if (type == RZ_ANALYSIS_OP_TYPE_UCALL) { + type = RZ_ANALYSIS_OP_TYPE_CALL; + } + } else if (insn->n_ops >= 1 && insn->ops[0].kind == C55_OP_REG) { + // A register operand makes a branch / call register-indirect (b ACx / + // call ACx); refine the resolved direct type to its indirect form. + if (type == RZ_ANALYSIS_OP_TYPE_JMP) { + type = RZ_ANALYSIS_OP_TYPE_UJMP; + } else if (type == RZ_ANALYSIS_OP_TYPE_CALL) { + type = RZ_ANALYSIS_OP_TYPE_UCALL; + } + } + return type; +} + +// PSH / POP move one stack word for a 16-bit operand and two for a 32-bit +// (accumulator / dbl) operand. On C55x the magnitude follows the operand's +// width, so a 16-bit register push moves a single word; C55x+ stores a full +// 32-bit (two-word) slot for every register push. A memory operand moves one +// word, or two when doubled. A form with no register or memory operand (a +// dual-register push-pop) moves two words. +static st32 c55_stack_words(const C55ArchDesc *a, const C55Insn *insn) { + if (insn->both && insn->n_ops >= 2) { + // Dual-register psh/pop (0x38/0x3a): two gr4 registers are pushed/popped + // in one instruction. The legacy decoder records a fixed single-word SP + // delta here regardless of the operand widths, so reproduce that. (The + // pshboth/popboth pair form has a single operand and keeps the width- + // based count below.) + return 1; + } + for (ut8 i = 0; i < insn->n_ops; i++) { + if (insn->ops[i].kind == C55_OP_MEM) { + return insn->ops[i].dbl ? 2 : 1; + } + if (insn->ops[i].kind == C55_OP_REG) { + if (a->arch == C55_ARCH_C55X && !insn->ops[i].dbl && insn->ops[i].width <= 16) { + return 1; + } + return 2; + } + } + return 2; +} + +void c55_fill_analysis(const C55ArchDesc *a, const C55Insn *insn, RzAnalysisOp *op) { + if (!a || !insn || !op) { + return; + } + op->id = insn->id; + if (insn->parallel_pair) { + // "||" parallel pair: report the combined size and take the analysis op + // type from the first sub-instruction (the legacy decoder likewise types + // the pair by its leading instruction). + op->size = insn->size; + C55Insn s1; + if (c55_decode(a, insn->par_bytes + insn->par_off1, insn->size - insn->par_off1, &s1)) { + op->type = c55_effective_type(a, &s1); + } + return; + } + op->size = insn->size; + ut32 type = c55_effective_type(a, insn); + op->type = type; + switch (type) { + case RZ_ANALYSIS_OP_TYPE_CALL: + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_CJMP: + case RZ_ANALYSIS_OP_TYPE_JMP: { + op->jump = c55_branch_target(insn, op->addr); + op->direction = RZ_ANALYSIS_OP_DIR_EXEC; + if (type == RZ_ANALYSIS_OP_TYPE_JMP) { + op->eob = true; + } else { + op->fail = op->addr + insn->size; + } + if (type == RZ_ANALYSIS_OP_TYPE_CALL || type == RZ_ANALYSIS_OP_TYPE_CCALL) { + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = 2; + } + break; + } + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_UCALL: { + // Register-indirect branch / call: the target lives in a register, so + // there is no static jump address to record. Expose the indirect + // register and the fall-through address; an indirect call additionally + // adjusts the stack pointer by the return-address slot. + op->direction = RZ_ANALYSIS_OP_DIR_EXEC; + op->fail = op->addr + insn->size; + if (insn->n_ops >= 1 && insn->ops[0].kind == C55_OP_REG && a->reg_info) { + const C55RegInfo *ri = a->reg_info(insn->ops[0].reg.cls, insn->ops[0].reg.num, insn->ops[0].reg.sub); + if (ri) { + op->ireg = ri->name; + } + } + if (type == RZ_ANALYSIS_OP_TYPE_UCALL) { + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = 2; + } + break; + } + case RZ_ANALYSIS_OP_TYPE_SWI: + case RZ_ANALYSIS_OP_TYPE_TRAP: + // Software interrupt / trap to a vector number: expose the vector as + // the op value when the instruction carries one (reset, also a trap, + // has no operand and leaves the value unset). + if (insn->n_ops >= 1 && insn->ops[0].kind == C55_OP_IMM) { + op->val = insn->ops[0].imm; + } + break; + case RZ_ANALYSIS_OP_TYPE_RET: + op->eob = true; + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = -2; + // The C55x return leaves the destination-register field unset; the + // C55x+ convention marks the stack pointer as the affected register. + if (a->arch != C55_ARCH_C55X) { + op->reg = "sp"; + } + break; + case RZ_ANALYSIS_OP_TYPE_CRET: + // Conditional return: pops the return address like RET when taken, but + // keeps a fall-through edge instead of ending the block. + op->fail = op->addr + insn->size; + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = -2; + // As with RET, the C55x+ convention marks the stack pointer as the + // affected register. + if (a->arch != C55_ARCH_C55X) { + op->reg = "sp"; + } + break; + case RZ_ANALYSIS_OP_TYPE_PUSH: + case RZ_ANALYSIS_OP_TYPE_UPUSH: + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = c55_stack_words(a, insn); + op->reg = "sp"; + op->direction = RZ_ANALYSIS_OP_DIR_WRITE; + break; + case RZ_ANALYSIS_OP_TYPE_POP: + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = -c55_stack_words(a, insn); + op->reg = "sp"; + op->direction = RZ_ANALYSIS_OP_DIR_READ; + break; + case RZ_ANALYSIS_OP_TYPE_ADD: + case RZ_ANALYSIS_OP_TYPE_SUB: + // aadd #k8, sp (frame setup): the immediate prints unsigned but is a + // signed 8-bit constant, and the stack grows toward lower addresses, so + // the recorded SP delta is the negation of the (signed) amount added. + if (insn->n_ops == 2 && insn->ops[0].kind == C55_OP_IMM && + insn->ops[1].kind == C55_OP_REG && insn->ops[1].reg.cls == C55_RC_SP) { + st32 k = (st8)(ut8)insn->ops[0].imm; + op->val = k; + op->disp = k; + op->stackop = RZ_ANALYSIS_STACK_INC; + op->stackptr = (type == RZ_ANALYSIS_OP_TYPE_ADD) ? -k : k; + op->reg = "sp"; + } else if (insn->n_ops == 2 && insn->ops[0].kind == C55_OP_REG && + insn->ops[1].kind == C55_OP_REG && a->reg_info && + (insn->lop == C55_LOP_AREG_ADD || insn->lop == C55_LOP_AREG_SUB)) { + // Register-to-register address arithmetic (aadd / asub ACx, ARy): + // expose the destination as the affected register and the source as + // the index register. + const C55RegInfo *dri = a->reg_info(insn->ops[1].reg.cls, insn->ops[1].reg.num, C55_SUB_NONE); + const C55RegInfo *sri = a->reg_info(insn->ops[0].reg.cls, insn->ops[0].reg.num, C55_SUB_NONE); + if (dri) { + op->reg = dri->name; + } + if (sri) { + op->ireg = sri->name; + } + } + break; + case RZ_ANALYSIS_OP_TYPE_MOV: { + // Single data-memory load / store: expose the register operand, the + // addressing base register, the displacement, the access direction and + // the referenced-data size. A register-to-register or immediate move + // has no memory operand and falls through without setting these. + // A single memory operand with no register (delay Smem: a memory-to- + // memory word move) still records the access width and write direction. + if (insn->n_ops == 1 && insn->ops[0].kind == C55_OP_MEM) { + const C55Operand *mem = &insn->ops[0]; + op->refptr = (mem->access ? mem->access : 16) / 8; + op->ptrsize = op->refptr; + op->direction = RZ_ANALYSIS_OP_DIR_WRITE; + break; + } + if (insn->n_ops < 2 || !a->reg_info) { + break; + } + const C55Operand *o0 = &insn->ops[0]; + const C55Operand *o1 = &insn->ops[1]; + const C55Operand *mem = NULL; + const C55Operand *reg = NULL; + bool load = false; + if (o0->kind == C55_OP_MEM && o1->kind == C55_OP_REG) { + mem = o0; + reg = o1; + load = true; + } else if (o1->kind == C55_OP_MEM && o0->kind == C55_OP_REG) { + mem = o1; + reg = o0; + } else { + break; + } + const C55RegInfo *rri = a->reg_info(reg->reg.cls, reg->reg.num, C55_SUB_NONE); + if (rri) { + op->reg = rri->name; + } + const C55RegInfo *bri = a->reg_info(mem->reg.cls, mem->reg.num, C55_SUB_NONE); + if (bri) { + op->ireg = bri->name; + } + if (mem->amode == C55_AM_INDEXED) { + op->disp = mem->disp; + } + op->direction = load ? RZ_ANALYSIS_OP_DIR_READ : RZ_ANALYSIS_OP_DIR_WRITE; + if (load) { + op->refptr = (mem->access ? mem->access : 16) / 8; + } + break; + } + default: + break; + } +} + +// Build the IL predicate for a register-compare branch condition (reg +// imm|0). Status-flag and register-to-register compares return NULL so the +// per-arch lifter handles them. The compare constant takes the subject's width. +static RzILOpPure *c55_cmp_pred(const C55ArchDesc *a, const C55Operand *c, bool uns); // reg-reg compare predicate (defined below) +static RzILOpPure *c55_cond_pred(const C55ArchDesc *a, const C55Operand *c, bool uns) { + if (c->cond_is_flag) { + // The single-bit st0_55 status flags (tc1, tc2, carry and their + // negations) share the same bit layout on both C55x and C55x+, so the + // shared predicate covers both. The overflow flags and the boolean + // combinations are not modelled (NULL -> the per-arch lifter handles + // them); only the flag ids enumerated below are lifted here. + ut8 bit; + bool neg = false; + switch (c->cond_flag) { + case 4: bit = 13; break; // tc1 + case 5: bit = 12; break; // tc2 + case 6: bit = 11; break; // carry + case 20: + bit = 13; + neg = true; + break; // !tc1 + case 21: + bit = 12; + neg = true; + break; // !tc2 + case 22: + bit = 11; + neg = true; + break; // !carry + default: return NULL; + } + RzILOpBool *p = LSB(SHIFTR(IL_FALSE, VARG("st0_55"), UN(4, (ut64)bit))); + return neg ? INV(p) : p; + } + if (c->cmp_to_reg) { + // register-register compare-and-branch (bccu Ra Rb): reuse the + // shared cmp predicate builder (same relop/uns/sub-register handling as + // the cmp/cmpand/cmpor instructions). + return c55_cmp_pred(a, c, uns); + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(c->reg.cls, c->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + // An accumulator-half operand (ACx.h / ACx.l) compares the 16-bit + // sub-register, not the full accumulator: read it through c55_read (which + // applies the .h >> 16 / .l low-word extraction) and compare at 16 bits. + // Full registers (sub == NONE) keep the register width. This sub-register + // path is only reachable on C55x+, whose cond-imm extractor sets reg.sub; + // the C55x extractor never does, so C55x behaviour is unchanged. + bool half = c->reg.sub == C55_SUB_HI || c->reg.sub == C55_SUB_LO; + ut8 w = half ? 16 : ri->width; + ut64 k = c->imm; + C55Operand regop = { 0 }; + regop.kind = C55_OP_REG; + regop.reg = c->reg; +#define CONDV() (half ? c55_read(a, ®op) : VARG(ri->il_var)) + // 'bccu' compares unsigned: the ordered relops use ule rather than sle. + // Equality is sign-agnostic. C55x cond-imm branches are always signed + // (uns == false), so their comparisons are unchanged. +#define LEQ(x, y) (uns ? ULE((x), (y)) : SLE((x), (y))) + switch (c->relop) { + case C55_REL_EQ: return EQ(CONDV(), UN(w, k)); + case C55_REL_NE: return INV(EQ(CONDV(), UN(w, k))); + case C55_REL_LT: return AND(LEQ(CONDV(), UN(w, k)), INV(EQ(CONDV(), UN(w, k)))); + case C55_REL_LE: return LEQ(CONDV(), UN(w, k)); + case C55_REL_GT: return INV(LEQ(CONDV(), UN(w, k))); + case C55_REL_GE: return INV(AND(LEQ(CONDV(), UN(w, k)), INV(EQ(CONDV(), UN(w, k))))); + default: return NULL; + } +#undef LEQ +#undef CONDV +} + +// One operand of a register-register compare (cmp/cmpand/cmpor), promoted to +// the comparison width `tw` (the wider of the two operands): a narrower operand +// is sign- or zero-extended per the unsigned flag; an operand already `tw` bits +// wide is taken as-is. An accumulator-half operand (ACx.h / ACx.l, only seen on +// C55x+) is read as its 16-bit value through c55_read. +static RzILOpPure *c55_cmp_regval(const C55ArchDesc *a, const C55Reg *r, bool uns, ut8 tw) { + bool half = r->sub == C55_SUB_HI || r->sub == C55_SUB_LO; + ut8 rw; + RzILOpPure *v; + if (half) { + C55Operand regop = { 0 }; + regop.kind = C55_OP_REG; + regop.reg = *r; + v = c55_read(a, ®op); // 16-bit half (.l low word / .h bits 31..16) + rw = 16; + } else { + const C55RegInfo *ri = a->reg_info ? a->reg_info(r->cls, r->num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + v = VARG(ri->il_var); + rw = ri->width; + } + if (rw < tw) { + v = uns ? UNSIGNED(tw, v) : SIGNED(tw, v); + } + return v; +} + +// The width of one compare operand: a sub-register half is 16 bits, otherwise +// the register's own width. +static ut8 c55_cmp_opwidth(const C55ArchDesc *a, const C55Reg *r) { + if (r->sub == C55_SUB_HI || r->sub == C55_SUB_LO) { + return 16; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(r->cls, r->num, C55_SUB_NONE) : NULL; + return ri ? ri->width : 0; +} + +// The comparison width of a register-register compare: the wider of the two +// operand registers (T/AR-vs-T/AR or half-vs-half compare at 16 bits, anything +// against a full accumulator at 40). +static ut8 c55_cmp_width(const C55ArchDesc *a, const C55Operand *c) { + ut8 sw = c55_cmp_opwidth(a, &c->reg); + ut8 dw = c55_cmp_opwidth(a, &c->index); + return sw > dw ? sw : dw; +} + +// Build the boolean predicate (SRC DST) for a register-register compare. +// `c` is the COND operand: c->reg is SRC, c->index is DST. Each operand value is +// rebuilt for every use (RzIL pures are not shared). +static RzILOpPure *c55_cmp_pred(const C55ArchDesc *a, const C55Operand *c, bool uns) { + ut8 tw = c55_cmp_width(a, c); + switch (c->relop) { + case C55_REL_EQ: return EQ(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)); + case C55_REL_NE: return INV(EQ(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw))); + case C55_REL_LT: { + RzILOpPure *le = uns ? ULE(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)) + : SLE(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)); + return AND(le, INV(EQ(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)))); + } + case C55_REL_GE: { + RzILOpPure *le = uns ? ULE(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)) + : SLE(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw)); + return INV(AND(le, INV(EQ(c55_cmp_regval(a, &c->reg, uns, tw), c55_cmp_regval(a, &c->index, uns, tw))))); + } + default: return NULL; + } +} + +// Lift a C55x+ single-data-memory move for the register-indirect modes. The +// Smem base is an ARn for display, but the address pointer is the 23-bit XARn, +// so the base register is promoted before the address is formed. The address, +// memory access and post-modify are produced by the shared EA primitives. +// Pre-modify (*+ar / *-ar) addressing is left to the per-arch lifter for now, +// since the shared post-modify primitive does not model it. +static RzILOpEffect *c55_mem_move(const C55ArchDesc *a, const C55Operand *reg, const C55Operand *mem, bool load) { + if (mem->shamt) { + // A shifted memory access (mov Smem << #SHIFT, ACx) is not lifted yet; + // the legacy decoder leaves it without IL, so do the same here. + return NULL; + } + if (mem->sh_mem_reg_set || mem->mem_round) { + // A register-shifted (Smem << Tx) or rounded (rnd(...)) memory access is + // likewise left without IL, matching the legacy decoder. + return NULL; + } + if (mem->byte_sel == 1 || mem->byte_sel == 2) { + // high_byte()/low_byte() accesses are left without IL, matching the + // legacy decoder; only the plain byte() form (byte_sel 3) is lifted. + return NULL; + } + if (mem->byte_sel == 3) { + // Plain byte() access: an 8-bit load/store. On load the byte is + // sign-/zero-extended to a 16-bit word and then written through the + // destination (a half-register write merges it via read-modify-write); + // on store the source is truncated to its low 8 bits. The data address + // is computed exactly as for a word access (the byte-vs-word width only + // affects loadw/storew, not the effective address). + switch (mem->amode) { + case C55_AM_INDIRECT: + case C55_AM_POSTINC: + case C55_AM_POSTDEC: + case C55_AM_IDXREG: + case C55_AM_POSTADD: + case C55_AM_POSTSUB: + case C55_AM_INDEXED: + case C55_AM_ABSOLUTE: + break; + default: + return NULL; + } + C55Operand bm = *mem; + if (bm.reg.cls == C55_RC_AR) { + bm.reg.cls = C55_RC_XAR; + } + bm.access = 8; + if (load) { + const C55RegInfo *dri = a->reg_info ? a->reg_info(reg->reg.cls, reg->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var) { + return NULL; + } + RzILOpPure *v = c55_read(a, &bm); // loadw 0 8 + if (!v) { + return NULL; + } + if (reg->reg.sub == C55_SUB_NONE) { + // whole-register destination: extend the byte straight to the + // register width (ac -> 40, ar -> 16). + v = bm.uns ? UNSIGNED(dri->width, v) : SIGNED(dri->width, v); + } else { + // half-register destination: extend to a 16-bit word; c55_write + // then merges it into the accumulator via read-modify-write. + v = bm.uns ? UNSIGNED(16, v) : SIGNED(16, v); + } + RzILOpEffect *wr = c55_write(a, reg, v); + if (!wr) { + return NULL; + } + RzILOpEffect *post = c55_post_effect(a, &bm); + return post ? SEQ2(wr, post) : wr; + } + RzILOpPure *v = c55_read(a, reg); + if (!v) { + return NULL; + } + v = UNSIGNED(8, v); // store the low byte of the source + RzILOpEffect *wr = c55_write(a, &bm, v); + if (!wr) { + return NULL; + } + RzILOpEffect *post = c55_post_effect(a, &bm); + return post ? SEQ2(wr, post) : wr; + } + switch (mem->amode) { + case C55_AM_INDIRECT: + case C55_AM_POSTINC: + case C55_AM_POSTDEC: + case C55_AM_IDXREG: + case C55_AM_POSTADD: + case C55_AM_POSTSUB: + case C55_AM_INDEXED: + case C55_AM_ABSOLUTE: + break; + default: + return NULL; // pre-modify (PRE*) and exotic modes -> per-arch lifter + } + C55Operand m = *mem; + if (m.reg.cls == C55_RC_AR) { + m.reg.cls = C55_RC_XAR; // ARn is the low half of the XARn pointer + } + if (load) { + const C55RegInfo *dri = a->reg_info ? a->reg_info(reg->reg.cls, reg->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var) { + return NULL; + } + RzILOpPure *v = c55_read(a, &m); + if (!v) { + return NULL; + } + ut32 lw = m.access ? m.access : 16; + if (reg->reg.sub == C55_SUB_LO || reg->reg.sub == C55_SUB_HI) { + // Half-register destination (mov Smem, ACx.l / ACx.h): the loaded + // word is merged into the accumulator half by c55_write's + // read-modify-write; a wider memory access is first narrowed to the + // 16-bit half width. + if (lw > 16) { + v = UNSIGNED(16, v); + } + } else if (dri->width > lw) { + // mov Smem, ACx sign-extends; the uns() qualifier zero-extends. + v = m.uns ? UNSIGNED(dri->width, v) : SIGNED(dri->width, v); + } else if (dri->width < lw) { + // a narrow destination register (e.g. the 7-bit dph or 9-bit pdp + // targeted by the mov Smem, forms) truncates the + // loaded word down to its width. + v = UNSIGNED(dri->width, v); + } + RzILOpEffect *wr = c55_write(a, reg, v); + if (!wr) { + return NULL; + } + RzILOpEffect *post = c55_post_effect(a, &m); + return post ? SEQ2(wr, post) : wr; + } + RzILOpPure *v = c55_read(a, reg); // half / register source + if (!v) { + return NULL; + } + if (reg->reg.sub == C55_SUB_NONE) { + // A whole-register source wider than the memory access (e.g. a 40-bit + // accumulator stored to a 16-bit word) is truncated to the access + // width; a narrower source (e.g. a 23-bit XARn stored as a 32-bit + // double word) is zero-extended up to it; a half source is already the + // right width. + const C55RegInfo *sri = a->reg_info ? a->reg_info(reg->reg.cls, reg->reg.num, C55_SUB_NONE) : NULL; + ut32 sw = m.access ? m.access : 16; + if (sri && sri->width != sw) { + v = UNSIGNED(sw, v); + } + } + RzILOpEffect *wr = c55_write(a, &m, v); + if (!wr) { + return NULL; + } + RzILOpEffect *post = c55_post_effect(a, &m); + return post ? SEQ2(wr, post) : wr; +} + +// A multiplicand for the multiply family, sign-extended (or, under the uns +// qualifier, zero-extended) to the 40-bit product width: a 16-bit register +// (Tx) is taken directly, a 40-bit accumulator contributes its high word +// (ACx(32-16), sign-extended), and a memory operand is the loaded word. A memory +// operand is assumed +// already promoted to its address-pointer register class (ARn -> XARn) by the +// caller. Returns NULL for operand shapes that are not valid multiplicands. +static RzILOpPure *c55_mul_val(const C55ArchDesc *a, const C55Operand *op) { + if (op->kind == C55_OP_MEM) { + RzILOpPure *l = c55_read(a, op); + if (!l) { + return NULL; + } + return op->uns ? UNSIGNED(40, l) : SIGNED(40, l); + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(op->reg.cls, op->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + if (ri->width == 16) { + return op->uns ? UNSIGNED(40, VARG(ri->il_var)) : SIGNED(40, VARG(ri->il_var)); + } + if (ri->width == 40) { + // An accumulator feeds the multiplier as ACx(32-16) -- the high word, + // sign-extended -- not its low half. This holds for the register multiply + // (MPY/MAC ACx, Tx), the memory MAC (Smem * ACx) and the FIRS pair; see + // spru374g. Modeled as the sign-extended bits 31-16, matching c55_read's + // C55_SUB_HI reader. + return SIGNED(40, CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(ri->il_var), UN(8, 16)))); + } + return NULL; +} + +// Build the RzIL effect for one multiply / multiply-accumulate / multiply-subtract. +// `ops` points at this operation's operands: m1 first, the destination accumulator +// last; the three-operand memory MAC adds Cmem in the middle, the four-operand +// register form (mac{m} Smem, Tx, ACx, ACy) carries an explicit accumulator source +// in ops[2], and the three-operand square form keeps the accumulator source in +// ops[1]. `lop` selects plain multiply (NONE), accumulate (MAC) or subtract (MAS); +// `shift16` shifts the accumulator right by 16 before accumulating (the dual-MAC +// ">> #16" form); `square` multiplies the first multiplicand by itself; `side_load` +// also writes the loaded Smem word into T3. Memory post-modify side effects are +// applied after the write, in operand order. Returns NULL if the operands are not a +// shape this builder represents. +static RzILOpEffect *c55_mac_effect(const C55ArchDesc *a, const C55Operand *ops, ut8 n_ops, + C55LiftOp lop, bool round, bool shift16, bool square, bool side_load) { + if (n_ops < 2 || n_ops > 4) { + return NULL; + } + const C55Operand *m1 = &ops[0]; + const C55Operand *dst = &ops[n_ops - 1]; + const C55Operand *m2, *acc; + if (square) { + m2 = m1; + acc = (n_ops == 3) ? &ops[1] : dst; + } else { + m2 = (n_ops == 2) ? dst : &ops[1]; + acc = (n_ops == 4) ? &ops[2] : dst; + } + if (dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE) { + return NULL; + } + const C55Operand *mul[2] = { m1, m2 }; + for (int i = 0; i < 2; i++) { + const C55Operand *o = mul[i]; + bool reg_ok = o->kind == C55_OP_REG && o->reg.sub == C55_SUB_NONE && + !o->shamt && !o->sh_left && !o->sh_by_reg; + if (!reg_ok && o->kind != C55_OP_MEM) { + return NULL; + } + } + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var || dri->width != 40) { + return NULL; + } + const C55RegInfo *ari = a->reg_info ? a->reg_info(acc->reg.cls, acc->reg.num, C55_SUB_NONE) : NULL; + if (acc->kind != C55_OP_REG || acc->reg.sub != C55_SUB_NONE || !ari || !ari->il_var || ari->width != 40) { + return NULL; + } + C55Operand m1p = *m1, m2p = *m2; + if (m1p.kind == C55_OP_MEM && m1p.reg.cls == C55_RC_AR) { + m1p.reg.cls = C55_RC_XAR; + } + if (m2p.kind == C55_OP_MEM && m2p.reg.cls == C55_RC_AR) { + m2p.reg.cls = C55_RC_XAR; + } + RzILOpPure *v1 = c55_mul_val(a, &m1p); + RzILOpPure *v2 = c55_mul_val(a, &m2p); + if (!v1 || !v2) { + rz_il_op_pure_free(v1); + rz_il_op_pure_free(v2); + return NULL; + } + RzILOpPure *val = MUL(v1, v2); + if (lop == C55_LOP_MAC || lop == C55_LOP_MAS) { + // the ">> #16" form shifts the accumulator right by 16 (arithmetic, + // 40-bit) before the product is accumulated. + RzILOpPure *acc_term = shift16 + ? SHIFTR(MSB(VARG(ari->il_var)), VARG(ari->il_var), UN(6, 16)) + : VARG(ari->il_var); + val = (lop == C55_LOP_MAC) ? ADD(acc_term, val) : SUB(acc_term, val); + } + if (round) { + // the rounding variant rounds the result to the upper word -- + // (result + 0x8000) with the low 16 bits cleared. + val = LOGAND(ADD(val, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); + } + RzILOpEffect *eff = SETG(dri->il_var, val); + if (m1->kind == C55_OP_MEM) { + RzILOpEffect *post = c55_post_effect(a, &m1p); + if (post) { + eff = SEQ2(eff, post); + } + } + if (m2 != m1 && m2 != dst && m2->kind == C55_OP_MEM) { + RzILOpEffect *post = c55_post_effect(a, &m2p); + if (post) { + eff = SEQ2(eff, post); + } + } + if (side_load && m1->kind == C55_OP_MEM) { + // memory-MAC side-load ([T3 = ]Smem): the Smem word is also written + // into T3, sequenced before the multiply / accumulate effect. + const C55RegInfo *t3 = a->reg_info ? a->reg_info(C55_RC_T, 3, C55_SUB_NONE) : NULL; + RzILOpPure *ld = c55_read(a, &m1p); + if (t3 && t3->il_var && ld) { + eff = SEQ2(SETG(t3->il_var, ld), eff); + } else { + rz_il_op_pure_free(ld); + } + } + return eff; +} + +RzILOpEffect *c55_lift(const C55ArchDesc *a, const C55Insn *insn, ut64 pc) { + if (!a || !insn) { + return NULL; + } + if (insn->parallel_pair) { + // "||" parallel pair: lift the two sub-instructions and sequence them. The + // pair is treated as executing both halves; the architectural single-cycle + // parallelism is not otherwise modelled. If either half does not lift, the + // pair does not lift (matching the legacy lifter, which had no IL here). + C55Insn s1, s2; + if (!c55_decode(a, insn->par_bytes + insn->par_off1, insn->size - insn->par_off1, &s1) || + !c55_decode(a, insn->par_bytes + insn->par_off2, insn->size - insn->par_off2, &s2)) { + return NULL; + } + // xccpart guards the *other* sub-instruction: it executes only when the + // xccpart condition holds. (Plain xcc guards the following instruction, + // not the parallel one, so it sequences normally below.) + const C55Insn *guard = s1.xcc_guard ? &s1 : (s2.xcc_guard ? &s2 : NULL); + if (guard) { + const C55Insn *body = guard == &s1 ? &s2 : &s1; + RzILOpEffect *be = c55_lift(a, body, pc); + if (!be) { + return NULL; + } + const C55Operand *cond = NULL; + for (ut8 i = 0; i < guard->n_ops; i++) { + if (guard->ops[i].kind == C55_OP_COND) { + cond = &guard->ops[i]; + break; + } + } + RzILOpPure *pred = cond ? c55_cond_pred(a, cond, guard->uns_all) : NULL; + if (!pred) { + rz_il_op_effect_free(be); + return NULL; + } + return BRANCH(pred, be, NOP()); + } + RzILOpEffect *e1 = c55_lift(a, &s1, pc); + RzILOpEffect *e2 = c55_lift(a, &s2, pc); + if (!e1 || !e2) { + rz_il_op_effect_free(e1); + rz_il_op_effect_free(e2); + return NULL; + } + return SEQ2(e1, e2); + } + if (a->lift) { + // arch-specific override takes precedence when it produces something + RzILOpEffect *e = a->lift(insn, pc); + if (e) { + return e; + } + } + if (insn->dual) { + // dual "::" MAC: two parallel sub-MACs sharing the Cmem coefficient. + // sub1 = ops[0..2] (Xmem, Cmem, ACx), sub2 = ops[3..5] (Ymem, Cmem, ACy); + // an amar sub1 only post-modifies its Xmem pointer (no product). The shared + // Cmem post-modify is emitted by each sub-MAC, matching the hardware's + // twice-applied coefficient update. Dispatched here (not via the op type) + // since the amar forms classify as LEA, not MUL. + RzILOpEffect *e1, *e2; + if (insn->amar1) { + C55Operand xp = insn->ops[0]; + if (xp.kind == C55_OP_MEM && xp.reg.cls == C55_RC_AR) { + xp.reg.cls = C55_RC_XAR; + } + e1 = c55_post_effect(a, &xp); + if (!e1) { + e1 = NOP(); + } + } else { + e1 = c55_mac_effect(a, &insn->ops[0], 3, insn->lop, insn->round, insn->shift1, false, false); + } + e2 = c55_mac_effect(a, &insn->ops[3], 3, insn->lop2, insn->round, insn->shift2, false, false); + if (!e1 || !e2) { + if (e1) { + rz_il_op_effect_free(e1); + } + if (e2) { + rz_il_op_effect_free(e2); + } + return NULL; + } + return SEQ2(e1, e2); + } + if (insn->lop == C55_LOP_FIRSADD || insn->lop == C55_LOP_FIRSSUB) { + // FIRSADD / FIRSSUB Xmem, Ymem, Cmem, ACx, ACy: two parallel operations, + // ACy = ACy + (ACx(32-16) * Cmem) + // :: ACx = (Xmem << #16) +/- (Ymem << #16) + // The MAC reads the old ACx high word (bits 31-16, sign-extended) times the + // sign-extended Cmem; the ALU op forms the (anti)symmetric sum of the two + // sign-extended data words, each shifted left 16. ops: [0]=Xmem [1]=Ymem + // [2]=Cmem [3]=ACx [4]=ACy. Memory post-modify side effects follow. + if (insn->n_ops != 5) { + return NULL; + } + const C55Operand *acx = &insn->ops[3]; + const C55Operand *acy = &insn->ops[4]; + const C55RegInfo *xi = a->reg_info ? a->reg_info(acx->reg.cls, acx->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(acy->reg.cls, acy->reg.num, C55_SUB_NONE) : NULL; + if (acx->kind != C55_OP_REG || acy->kind != C55_OP_REG || + !xi || !xi->il_var || xi->width != 40 || !yi || !yi->il_var || yi->width != 40) { + return NULL; + } + C55Operand xp = insn->ops[0], yp = insn->ops[1], cp = insn->ops[2]; + if (xp.kind == C55_OP_MEM && xp.reg.cls == C55_RC_AR) { + xp.reg.cls = C55_RC_XAR; + } + if (yp.kind == C55_OP_MEM && yp.reg.cls == C55_RC_AR) { + yp.reg.cls = C55_RC_XAR; + } + // MAC: ACy += ACx(31-16, sign-extended) * Cmem(sign-extended) + C55Operand acx_hi = *acx; + acx_hi.reg.sub = C55_SUB_HI; + RzILOpPure *hi = c55_read(a, &acx_hi); // 16-bit high word + RzILOpPure *cm = c55_mul_val(a, &cp); // Cmem -> 40-bit (signed) + RzILOpPure *xs = c55_mul_val(a, &xp); // Xmem -> 40-bit (signed) + RzILOpPure *ys = c55_mul_val(a, &yp); // Ymem -> 40-bit (signed) + if (!hi || !cm || !xs || !ys) { + rz_il_op_pure_free(hi); + rz_il_op_pure_free(cm); + rz_il_op_pure_free(xs); + rz_il_op_pure_free(ys); + return NULL; + } + RzILOpEffect *e1 = SETG(yi->il_var, ADD(VARG(yi->il_var), MUL(SIGNED(40, hi), cm))); + RzILOpPure *sx = SHIFTL(IL_FALSE, xs, UN(8, 16)); + RzILOpPure *sy = SHIFTL(IL_FALSE, ys, UN(8, 16)); + RzILOpPure *sum = (insn->lop == C55_LOP_FIRSADD) ? ADD(sx, sy) : SUB(sx, sy); + RzILOpEffect *eff = SEQ2(e1, SETG(xi->il_var, sum)); + RzILOpEffect *px = c55_post_effect(a, &xp); + if (px) { + eff = SEQ2(eff, px); + } + RzILOpEffect *py = c55_post_effect(a, &yp); + if (py) { + eff = SEQ2(eff, py); + } + RzILOpEffect *pc = c55_post_effect(a, &cp); + if (pc) { + eff = SEQ2(eff, pc); + } + return eff; + } + if (insn->lop == C55_LOP_SQDST || insn->lop == C55_LOP_ABDST) { + // SQDST / ABDST Xmem, Ymem, ACx, ACy: two parallel operations, + // ACy = ACy + (ACx.h * ACx.h) (sqdst) or ACy + |ACx.h| (abdst) + // :: ACx = (Xmem << #16) - (Ymem << #16) + // The MAC squares (sqdst) or takes the absolute value (abdst) of the + // sign-extended ACx high word (bits 31-16); the ALU op forms the shifted + // difference of the two sign-extended data words. ops: [0]=Xmem [1]=Ymem + // [2]=ACx [3]=ACy. Memory post-modify side effects follow. + if (insn->n_ops != 4) { + return NULL; + } + const C55Operand *acx = &insn->ops[2]; + const C55Operand *acy = &insn->ops[3]; + const C55RegInfo *xi = a->reg_info ? a->reg_info(acx->reg.cls, acx->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(acy->reg.cls, acy->reg.num, C55_SUB_NONE) : NULL; + if (acx->kind != C55_OP_REG || acy->kind != C55_OP_REG || + !xi || !xi->il_var || xi->width != 40 || !yi || !yi->il_var || yi->width != 40) { + return NULL; + } + C55Operand xp = insn->ops[0], yp = insn->ops[1]; + if (xp.kind == C55_OP_MEM && xp.reg.cls == C55_RC_AR) { + xp.reg.cls = C55_RC_XAR; + } + if (yp.kind == C55_OP_MEM && yp.reg.cls == C55_RC_AR) { + yp.reg.cls = C55_RC_XAR; + } + C55Operand acx_hi = *acx; + acx_hi.reg.sub = C55_SUB_HI; + RzILOpPure *term; + if (insn->lop == C55_LOP_SQDST) { + // ACx.h * ACx.h, both sign-extended to 40 bits + term = MUL(SIGNED(40, c55_read(a, &acx_hi)), SIGNED(40, c55_read(a, &acx_hi))); + } else { + // |ACx.h| : (s <= 0) ? -s : s (with -0 == 0 the <=0 boundary is exact) + term = ITE(SLE(SIGNED(40, c55_read(a, &acx_hi)), UN(40, 0)), + SUB(UN(40, 0), SIGNED(40, c55_read(a, &acx_hi))), + SIGNED(40, c55_read(a, &acx_hi))); + } + RzILOpPure *xs = c55_mul_val(a, &xp); // Xmem -> 40-bit (signed) + RzILOpPure *ys = c55_mul_val(a, &yp); // Ymem -> 40-bit (signed) + if (!xs || !ys) { + rz_il_op_pure_free(xs); + rz_il_op_pure_free(ys); + rz_il_op_pure_free(term); + return NULL; + } + RzILOpEffect *e2 = SETG(xi->il_var, SUB(SHIFTL(IL_FALSE, xs, UN(8, 16)), SHIFTL(IL_FALSE, ys, UN(8, 16)))); + RzILOpEffect *eff = SEQ2(SETG(yi->il_var, ADD(VARG(yi->il_var), term)), e2); + RzILOpEffect *px = c55_post_effect(a, &xp); + if (px) { + eff = SEQ2(eff, px); + } + RzILOpEffect *py = c55_post_effect(a, &yp); + if (py) { + eff = SEQ2(eff, py); + } + return eff; + } + if (insn->lop == C55_LOP_LMS) { + // LMS Xmem, Ymem, ACx, ACy: two parallel operations, + // ACy = ACy + (Xmem * Ymem) + // :: ACx = round(ACx + (Xmem << #16)) + // The first is a MAC of the two sign-extended data words; the second adds + // Xmem (shifted left 16) to ACx and rounds to the upper word. Both read the + // old ACx, so it is snapshotted into a local first (the two destinations may + // alias). ops: [0]=Xmem [1]=Ymem [2]=ACx [3]=ACy; post-modifies follow. + if (insn->n_ops != 4) { + return NULL; + } + const C55Operand *acx = &insn->ops[2]; + const C55Operand *acy = &insn->ops[3]; + const C55RegInfo *xi = a->reg_info ? a->reg_info(acx->reg.cls, acx->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(acy->reg.cls, acy->reg.num, C55_SUB_NONE) : NULL; + if (acx->kind != C55_OP_REG || acy->kind != C55_OP_REG || + !xi || !xi->il_var || xi->width != 40 || !yi || !yi->il_var || yi->width != 40) { + return NULL; + } + C55Operand xp = insn->ops[0], yp = insn->ops[1]; + if (xp.kind == C55_OP_MEM && xp.reg.cls == C55_RC_AR) { + xp.reg.cls = C55_RC_XAR; + } + if (yp.kind == C55_OP_MEM && yp.reg.cls == C55_RC_AR) { + yp.reg.cls = C55_RC_XAR; + } + RzILOpPure *xm = c55_mul_val(a, &xp); // Xmem for the multiply + RzILOpPure *ym = c55_mul_val(a, &yp); // Ymem + RzILOpPure *xs = c55_mul_val(a, &xp); // Xmem for the shifted add + if (!xm || !ym || !xs) { + rz_il_op_pure_free(xm); + rz_il_op_pure_free(ym); + rz_il_op_pure_free(xs); + return NULL; + } + // ACx = round(old_ACx + (Xmem << #16)); round to the upper word. + RzILOpPure *sum = ADD(VARL("lms_acx"), SHIFTL(IL_FALSE, xs, UN(8, 16))); + sum = LOGAND(ADD(sum, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); + RzILOpEffect *eff = SEQ2(SETL("lms_acx", VARG(xi->il_var)), + SETG(yi->il_var, ADD(VARG(yi->il_var), MUL(xm, ym)))); + eff = SEQ2(eff, SETG(xi->il_var, sum)); + RzILOpEffect *px = c55_post_effect(a, &xp); + if (px) { + eff = SEQ2(eff, px); + } + RzILOpEffect *py = c55_post_effect(a, &yp); + if (py) { + eff = SEQ2(eff, py); + } + return eff; + } + if (insn->mac_mov) { + // MAC :: parallel load: sub1 = macm/masm Xmem, Tx, ACx (a Tx-coefficient + // memory MAC; side_load -> T3=Xmem); sub2 = mov Ymem << #16, ACy. The mov does + // not read ACy, so no aliasing snapshot is needed (when ACx==ACy the load + // simply wins). ops: [0]=Xmem [1]=Tx [2]=ACx [3]=Ymem [4]=ACy. + if (insn->n_ops != 5) { + return NULL; + } + const C55Operand *acy = &insn->ops[4]; + const C55RegInfo *yi = a->reg_info ? a->reg_info(acy->reg.cls, acy->reg.num, C55_SUB_NONE) : NULL; + if (acy->kind != C55_OP_REG || !yi || !yi->il_var || yi->width != 40) { + return NULL; + } + C55Operand yp = insn->ops[3]; + if (yp.kind == C55_OP_MEM && yp.reg.cls == C55_RC_AR) { + yp.reg.cls = C55_RC_XAR; + } + RzILOpPure *yv = c55_mul_val(a, &yp); // Ymem -> 40-bit (signed) + if (!yv) { + return NULL; + } + RzILOpEffect *sub1 = c55_mac_effect(a, &insn->ops[0], 3, insn->lop, insn->round, false, false, insn->side_load); + if (!sub1) { + rz_il_op_pure_free(yv); + return NULL; + } + RzILOpEffect *eff = SEQ2(sub1, SETG(yi->il_var, SHIFTL(IL_FALSE, yv, UN(8, 16)))); + RzILOpEffect *py = c55_post_effect(a, &yp); + if (py) { + eff = SEQ2(eff, py); + } + return eff; + } + // Register or immediate shift (sftl / sfts ACx, Tx|#SHIFTW [, ACy]): a + // three-operand form the two-operand lop branch below cannot express. A + // negative count shifts right by its magnitude, a positive count shifts + // left; sftl fills logically (zero), sfts fills the right shift with the + // sign bit. ACy collapses onto ACx when they are equal. + if (insn->lop == C55_LOP_SFTL || insn->lop == C55_LOP_SFTS) { + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *acx = &insn->ops[0]; + const C55Operand *cnt = &insn->ops[1]; + const C55Operand *acy = &insn->ops[insn->n_ops >= 3 ? 2 : 0]; + if (acx->kind != C55_OP_REG || acy->kind != C55_OP_REG) { + return NULL; + } + // A half-register source or destination (the 0xa7 .h/.l shift forms) + // operates on a 16-bit slice with a merge-back; the legacy leaves those + // unlifted, so do the same rather than shifting the full register. + if (acx->reg.sub != C55_SUB_NONE || acy->reg.sub != C55_SUB_NONE) { + return NULL; + } + const C55RegInfo *xi = a->reg_info ? a->reg_info(acx->reg.cls, acx->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(acy->reg.cls, acy->reg.num, C55_SUB_NONE) : NULL; + if (!xi || !yi || !xi->il_var || !yi->il_var) { + return NULL; + } + bool arith = insn->lop == C55_LOP_SFTS; + if (cnt->kind == C55_OP_REG) { + // count in a T register: the direction depends on its sign at runtime. + const C55RegInfo *ti = a->reg_info ? a->reg_info(cnt->reg.cls, cnt->reg.num, C55_SUB_NONE) : NULL; + if (!ti || !ti->il_var) { + return NULL; + } + RzILOpPure *fill = arith ? MSB(VARG(xi->il_var)) : IL_FALSE; + return SETG(yi->il_var, + ITE(AND(SLE(VARG(ti->il_var), UN(ti->width, 0)), INV(EQ(VARG(ti->il_var), UN(ti->width, 0)))), + SHIFTR(fill, VARG(xi->il_var), SUB(UN(ti->width, 0), VARG(ti->il_var))), + SHIFTL(IL_FALSE, VARG(xi->il_var), VARG(ti->il_var)))); + } + if (cnt->kind == C55_OP_IMM) { + // #SHIFTW is a decode-time signed count: sign-extend the field, then + // emit a fixed left or right shift of that magnitude. A zero count is + // a plain move (no shift node), matching the legacy lifter. + ut32 w = cnt->width ? cnt->width : 6; + st64 s = (st64)(cnt->imm << (64 - w)) >> (64 - w); + if (s == 0) { + return SETG(yi->il_var, VARG(xi->il_var)); + } + if (s > 0) { + return SETG(yi->il_var, SHIFTL(IL_FALSE, VARG(xi->il_var), UN(w, (ut64)s))); + } + RzILOpPure *fill = arith ? MSB(VARG(xi->il_var)) : IL_FALSE; + return SETG(yi->il_var, SHIFTR(fill, VARG(xi->il_var), UN(w, (ut64)(-s)))); + } + return NULL; + } + // Immediate-ALU with immediate shift: ACy = ACx (#k16 << #sh) for the + // opcode-0x7a (fixed sh=16) and opcode-0x70..0x74 (variable sh) add/sub/and/ + // or/xor forms. Distinguished from the register-shift forms below by a + // leading immediate operand (ops = [#k16, ACx, ACy]). The shift count is the + // operand's shamt. + // Immediate shifted-load: dst = sign-extend(#k16) << #sh for the opcode-0xc2 + // "mov #k16 << #sh, ACx" form (ops = [#k16, ACx]). The shift count is the + // immediate operand's shamt. + if (insn->lop == C55_LOP_MOVSHL && insn->n_ops >= 2 && insn->ops[0].kind == C55_OP_IMM) { + const C55Operand *imm = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + if (dst->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *yi = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!yi || !yi->il_var) { + return NULL; + } + ut64 k16sign = imm->imm & 0xffff; + if (k16sign & 0x8000) { + k16sign |= 0xffffff0000ULL; + } + // The shift amount is an assemble-time constant, so the legacy decoder + // folds "#k16 << #sh" to a single constant; reproduce that (the result + // is masked to the destination width). + ut64 folded = (k16sign << (imm->shamt & 0x3f)); + if (yi->width < 64) { + folded &= ((ut64)1 << yi->width) - 1; + } + return SETG(yi->il_var, UN(yi->width, folded)); + } + if ((insn->lop == C55_LOP_ANDSHL || insn->lop == C55_LOP_ORSHL || insn->lop == C55_LOP_XORSHL || + insn->lop == C55_LOP_ADDSHL || insn->lop == C55_LOP_SUBSHL) && + insn->n_ops >= 3 && insn->ops[0].kind == C55_OP_IMM) { + const C55Operand *imm = &insn->ops[0]; + const C55Operand *src = &insn->ops[1]; + const C55Operand *dst = &insn->ops[2]; + if (src->kind != C55_OP_REG || dst->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *xi = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!xi || !yi || !xi->il_var || !yi->il_var) { + return NULL; + } + // add/sub treat #k16 as a signed constant (sign-extended to the + // accumulator width); the bitwise and/or/xor forms treat it as a raw + // bit pattern (zero-extended). Both then shift up 16 bits. + ut64 k16sign = imm->imm & 0xffff; + if ((insn->lop == C55_LOP_ADDSHL || insn->lop == C55_LOP_SUBSHL) && (k16sign & 0x8000)) { + k16sign |= 0xffffff0000ULL; + } + RzILOpPure *shifted = SHIFTL(IL_FALSE, UN(yi->width, k16sign), UN(8, (ut64)(ut8)imm->shamt)); + RzILOpPure *res; + switch (insn->lop) { + case C55_LOP_ADDSHL: res = ADD(VARG(xi->il_var), shifted); break; + case C55_LOP_SUBSHL: res = SUB(VARG(xi->il_var), shifted); break; + case C55_LOP_ANDSHL: res = LOGAND(VARG(xi->il_var), shifted); break; + case C55_LOP_ORSHL: res = LOGOR(VARG(xi->il_var), shifted); break; + default: res = LOGXOR(VARG(xi->il_var), shifted); break; + } + return SETG(yi->il_var, res); + } + // Immediate ALU without shift: dst = ACx zero-extend(#k16) for the + // 0x7b-0x7f forms (ops = [#k16, gr4-src, gr4-dst]). The legacy lifts only + // the accumulator-source encodings -- a 16-bit T / AR source is left + // unlifted -- which is reproduced here; the 40-bit result is truncated to a + // 16-bit destination. + if (insn->lop == C55_LOP_ADDK || insn->lop == C55_LOP_SUBK || + insn->lop == C55_LOP_ANDK || insn->lop == C55_LOP_ORK || insn->lop == C55_LOP_XORK) { + if (insn->n_ops < 3 || insn->ops[0].kind != C55_OP_IMM) { + return NULL; + } + const C55Operand *imm = &insn->ops[0]; + const C55Operand *src = &insn->ops[1]; + const C55Operand *dst = &insn->ops[2]; + if (src->kind != C55_OP_REG || dst->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *xi = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!xi || !yi || !xi->il_var || !yi->il_var) { + return NULL; + } + bool bitwise = insn->lop == C55_LOP_ANDK || insn->lop == C55_LOP_ORK || insn->lop == C55_LOP_XORK; + // Accumulator-half operands (only the 0xc4/0xc5 gr1 forms carry these): + // the operation runs at the destination width, reading the source through + // c55_read (a half yields its 16-bit value) and writing back through + // c55_write (a half does the read-modify-write merge). The legacy lifts + // only the bitwise ops here, and only when the source is at least as wide + // as the destination (a narrower source -> destination promotion is left + // unlifted); add/sub with a half operand are not lifted. + bool src_half = src->reg.sub == C55_SUB_HI || src->reg.sub == C55_SUB_LO; + bool dst_half = dst->reg.sub == C55_SUB_HI || dst->reg.sub == C55_SUB_LO; + if (src_half || dst_half) { + if (!bitwise) { + return NULL; // add/sub on a half: legacy leaves it unlifted + } + ut8 dw = dst_half ? 16 : yi->width; + ut8 sw = src_half ? 16 : xi->width; + if (sw < dw) { + return NULL; // narrower source than destination: unlifted + } + RzILOpPure *sv = c55_read(a, src); // 16-bit for a half, else full + if (sw > dw) { + sv = CAST(dw, IL_FALSE, sv); // truncate a full source to the half width + } + RzILOpPure *iv = UN(dw, (ut64)(imm->imm & 0xffff)); + RzILOpPure *r = insn->lop == C55_LOP_ANDK ? LOGAND(sv, iv) + : insn->lop == C55_LOP_ORK ? LOGOR(sv, iv) + : LOGXOR(sv, iv); + return c55_write(a, dst, r); + } + RzILOpPure *res; + if (bitwise && yi->width < xi->width) { + // and / or / xor into a 16-bit (T / AR) destination: the legacy + // truncates the accumulator first and operates at the destination + // width with a same-width immediate (no outer cast). + RzILOpPure *sv = CAST(yi->width, IL_FALSE, VARG(xi->il_var)); + RzILOpPure *iv = UN(yi->width, (ut64)(imm->imm & 0xffff)); + res = insn->lop == C55_LOP_ANDK ? LOGAND(sv, iv) : insn->lop == C55_LOP_ORK ? LOGOR(sv, iv) : LOGXOR(sv, iv); + } else { + // add / sub operate at the 40-bit accumulator width. The elided + // 2-operand form (destination == source) zero-extends #k16; the + // 3-operand form sign-extends it. The bitwise ops always zero-extend + // and truncate to a 16-bit destination above; here they zero-extend. + ut64 immv = (ut64)(imm->imm & 0xffff); + bool same = src->reg.cls == dst->reg.cls && src->reg.num == dst->reg.num; + if ((insn->lop == C55_LOP_ADDK || insn->lop == C55_LOP_SUBK) && !same && (immv & 0x8000)) { + immv |= 0xffffff0000ULL; + } + RzILOpPure *iv = UN(xi->width, immv); + switch (insn->lop) { + case C55_LOP_ADDK: res = ADD(VARG(xi->il_var), iv); break; + case C55_LOP_SUBK: res = SUB(VARG(xi->il_var), iv); break; + case C55_LOP_ANDK: res = LOGAND(VARG(xi->il_var), iv); break; + case C55_LOP_ORK: res = LOGOR(VARG(xi->il_var), iv); break; + default: res = LOGXOR(VARG(xi->il_var), iv); break; + } + if (yi->width < xi->width) { + res = CAST(yi->width, IL_FALSE, res); + } + } + return SETG(yi->il_var, res); + } + // Shift-and-combine: dst = dst (src << #SHIFTW) for the 0x10 and / or / + // xor / add / sub register-ALU forms. The legacy renders and lifts the shift + // as an unsigned 8-bit left shift of the raw SHIFTW field (it does not treat + // the field as signed here); this preserves that behaviour byte-for-byte. The + // and / or / xor forms collapse the destination when it equals the source and + // the legacy leaves the collapsed encodings unlifted, so those return null and + // fall through to the (also null) legacy IL. + if (insn->lop == C55_LOP_ANDSHL || insn->lop == C55_LOP_ORSHL || insn->lop == C55_LOP_XORSHL || + insn->lop == C55_LOP_ADDSHL || insn->lop == C55_LOP_SUBSHL) { + if (insn->n_ops < 2) { + return NULL; + } + bool bitwise = insn->lop == C55_LOP_ANDSHL || insn->lop == C55_LOP_ORSHL || insn->lop == C55_LOP_XORSHL; + if (bitwise && insn->n_ops < 3) { + return NULL; // collapsed and/or/xor: left to the legacy (unlifted) + } + const C55Operand *src = &insn->ops[0]; + const C55Operand *cnt = &insn->ops[1]; + const C55Operand *dst = &insn->ops[insn->n_ops >= 3 ? 2 : 0]; + if (src->kind != C55_OP_REG || dst->kind != C55_OP_REG || + (cnt->kind != C55_OP_IMM && cnt->kind != C55_OP_REG)) { + return NULL; + } + // A half-register source or destination (the 0xa7 .h/.l shift-ALU forms) + // works on a 16-bit slice with a merge-back; the legacy leaves those + // unlifted, so do likewise rather than operating on the full register. + if (src->reg.sub != C55_SUB_NONE || dst->reg.sub != C55_SUB_NONE || + (cnt->kind == C55_OP_REG && cnt->reg.sub != C55_SUB_NONE)) { + return NULL; + } + const C55RegInfo *xi = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *yi = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!xi || !yi || !xi->il_var || !yi->il_var) { + return NULL; + } + RzILOpPure *shifted; + if (cnt->kind == C55_OP_IMM) { + // "src << #SHIFTW": the legacy treats the field as an unsigned 8-bit + // left-shift amount (0x10 and/or/xor/add/sub register-ALU forms). + shifted = SHIFTL(IL_FALSE, VARG(xi->il_var), UN(8, (ut64)(cnt->imm & 0xff))); + } else { + // "src << Tx": the shift count is a (16-bit) T register (opcode 0x5a + // add/sub ACx << Tx, ACy). + const C55RegInfo *ti = a->reg_info ? a->reg_info(cnt->reg.cls, cnt->reg.num, C55_SUB_NONE) : NULL; + if (!ti || !ti->il_var) { + return NULL; + } + shifted = SHIFTL(IL_FALSE, VARG(xi->il_var), VARG(ti->il_var)); + } + RzILOpPure *res = NULL; + switch (insn->lop) { + case C55_LOP_ANDSHL: res = LOGAND(VARG(yi->il_var), shifted); break; + case C55_LOP_ORSHL: res = LOGOR(VARG(yi->il_var), shifted); break; + case C55_LOP_XORSHL: res = LOGXOR(VARG(yi->il_var), shifted); break; + case C55_LOP_ADDSHL: res = ADD(VARG(yi->il_var), shifted); break; + case C55_LOP_SUBSHL: res = SUB(VARG(yi->il_var), shifted); break; + default: rz_il_op_pure_free(shifted); return NULL; + } + return SETG(yi->il_var, res); + } + // bclr / bset st0_, st0_55: clear or set a named bit in st0_55. + if (insn->lop == C55_LOP_STBITCLR || insn->lop == C55_LOP_STBITSET) { + // ops[0] is the bit-name register (its index encodes the bit position, + // 192 + bit), ops[1] is st0_55. Only the semantic bits (9..15: acov, + // carry, tc) are lifted; the data-page bits (0..8) produce no IL, as in + // the legacy lifter. The legacy form is an ite over a constant predicate: + // st0_55 = ite(set?, st0_55 | (1<n_ops < 2 || insn->ops[0].kind != C55_OP_REG || + insn->ops[1].kind != C55_OP_REG) { + return NULL; + } + if (insn->ops[0].reg.cls != C55_RC_SPECIAL || insn->ops[0].reg.num < 192 || + insn->ops[0].reg.num > 207) { + return NULL; + } + unsigned bit = (unsigned)(insn->ops[0].reg.num - 192); + if (bit < 9) { + return NULL; // data-page bits: no IL, matching the legacy lifter + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(insn->ops[1].reg.cls, insn->ops[1].reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + ut64 one = 1ULL << bit; + ut64 widthmask = (1ULL << ri->width) - 1; + RzILOpEffect *r = SETG(ri->il_var, + ITE(insn->lop == C55_LOP_STBITSET ? IL_TRUE : IL_FALSE, + LOGOR(VARG(ri->il_var), UN(ri->width, one & widthmask)), + LOGAND(VARG(ri->il_var), UN(ri->width, (~one) & widthmask)))); + return r; + } + // Status-register bit clear / set: STx = STx & ~(1 << #k4) (bclr) or + // STx = STx | (1 << #k4) (bset), opcode 0x46. ops[0] is the #k4 bit index, + // ops[1] the status register. + if (insn->lop == C55_LOP_BITCLR || insn->lop == C55_LOP_BITSET) { + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *bit = &insn->ops[0]; + const C55Operand *reg = &insn->ops[1]; + if (bit->kind != C55_OP_IMM || reg->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(reg->reg.cls, reg->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + unsigned b = (unsigned)(bit->imm & 0xf); + ut64 one = 1ULL << b; + ut64 widthmask = (ri->width >= 64) ? ~0ULL : ((1ULL << ri->width) - 1); + RzILOpPure *res = (insn->lop == C55_LOP_BITCLR) + ? LOGAND(VARG(ri->il_var), UN(ri->width, (~one) & widthmask)) + : LOGOR(VARG(ri->il_var), UN(ri->width, one & widthmask)); + return SETG(ri->il_var, res); + } + // amov #k16, dst: load a zero-extended 16-bit constant (or address) into the + // destination register (opcode 0x77). ops[0] is the constant, ops[1] the dst. + if (insn->lop == C55_LOP_AMOV) { + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *imm = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + if (imm->kind != C55_OP_IMM || dst->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + // amov #k, reg: load the unsigned constant into the pointer register. + // When the constant does not fit the register width (a 24-bit field into + // a 23-bit XARn), the legacy decoder leaves it unlifted; preserve that + // correct-or-NULL behaviour rather than silently truncating an address. + if (ri->width < 64 && ((ut64)imm->imm >> ri->width)) { + return NULL; + } + return SETG(ri->il_var, UN(ri->width, (ut64)imm->imm)); + } + // Instructions with no modelled data effect (repeat / loop control such as + // rptb, rptcc) lift to a nop, matching the legacy lifter. + if (insn->lop == C55_LOP_NOP) { + return NOP(); + } + // Decode-and-analyse-only instructions (the exponent / bit-count style DSP + // primitives) carry no modelled data effect: no IL. + if (insn->lop == C55_LOP_OPAQUE) { + return NULL; + } + // amov / asub ACx, ACy register forms (opcode 0x14): ACy = ACx (amov) or + // ACy = ACy - ACx (asub), with the source converted to the destination width + // (zero-extended when narrower, truncated when wider; no cast when equal). + // ops[0] is the source register, ops[1] the destination. + if (insn->lop == C55_LOP_AREG_MOV || insn->lop == C55_LOP_AREG_SUB || insn->lop == C55_LOP_AREG_ADD || + insn->lop == C55_LOP_AREG_AND || insn->lop == C55_LOP_AREG_OR || insn->lop == C55_LOP_AREG_XOR) { + if (insn->n_ops < 2 || !a->reg_info) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + if (dst->kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *di = a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE); + if (!di || !di->il_var) { + return NULL; + } + RzILOpPure *sv; + if (src->kind == C55_OP_IMM) { + // a{add,sub} #k, reg: add/subtract an unsigned constant to/from the + // pointer. A constant that does not fit the register width is left + // unlifted, matching the legacy decoder and the amov treatment. + if (di->width < 64 && ((ut64)src->imm >> di->width)) { + return NULL; + } + sv = UN(di->width, (ut64)src->imm); + } else if (src->kind == C55_OP_REG) { + const C55RegInfo *si = a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE); + if (!si || !si->il_var) { + return NULL; + } + sv = VARG(si->il_var); + if (si->width != di->width) { + sv = UNSIGNED(di->width, sv); + } + } else { + return NULL; + } + if (insn->lop == C55_LOP_AREG_MOV) { + return SETG(di->il_var, sv); + } + if (insn->lop == C55_LOP_AREG_ADD) { + return SETG(di->il_var, ADD(VARG(di->il_var), sv)); + } + if (insn->lop == C55_LOP_AREG_AND) { + return SETG(di->il_var, LOGAND(VARG(di->il_var), sv)); + } + if (insn->lop == C55_LOP_AREG_OR) { + return SETG(di->il_var, LOGOR(VARG(di->il_var), sv)); + } + if (insn->lop == C55_LOP_AREG_XOR) { + return SETG(di->il_var, LOGXOR(VARG(di->il_var), sv)); + } + return SETG(di->il_var, SUB(VARG(di->il_var), sv)); + } + // cmp / cmpand / cmpor SRC DST, [TCx,] TCz (opcode 0x12): compare two + // registers and write the TCz status bit. cmpand/cmpor first AND/OR the + // comparison with the input TCx bit. ops[0] is the compare COND; for cmp + // ops[1] is TCz, for cmpand/cmpor ops[1] is TCx and ops[2] is TCz. The 'u' + // (unsigned) variants compare unsigned and zero-extend the 16-bit operands. + if (insn->lop == C55_LOP_CMP || insn->lop == C55_LOP_CMPAND || insn->lop == C55_LOP_CMPOR) { + bool andor = insn->lop != C55_LOP_CMP; + if (insn->n_ops < (andor ? 3 : 2) || !a->reg_info) { + return NULL; + } + const C55Operand *cond = &insn->ops[0]; + const C55Operand *tcx = andor ? &insn->ops[1] : NULL; + const C55Operand *tcz = andor ? &insn->ops[2] : &insn->ops[1]; + if (cond->kind != C55_OP_COND || !cond->cmp_to_reg || + tcz->kind != C55_OP_COND || !tcz->cond_is_flag) { + return NULL; + } + const C55RegInfo *si = a->reg_info(cond->reg.cls, cond->reg.num, C55_SUB_NONE); + const C55RegInfo *di = a->reg_info(cond->index.cls, cond->index.num, C55_SUB_NONE); + if (!si || !si->il_var || !di || !di->il_var) { + return NULL; + } + RzILOpPure *pred = c55_cmp_pred(a, cond, insn->uns_all); + if (!pred) { + return NULL; + } + if (andor) { + if (tcx->kind != C55_OP_COND || !tcx->cond_is_flag) { + rz_il_op_pure_free(pred); + return NULL; + } + // cond_flag 4/20 -> tc1 (bit 0x2000), 5/21 -> tc2 (bit 0x1000); ids + // >= 20 are the negated !tcN inputs, which add an extra INV. + ut64 xbit = (tcx->cond_flag & 1) ? 0x1000 : 0x2000; + RzILOpPure *tcin = INV(IS_ZERO(LOGAND(VARG("st0_55"), UN(16, xbit)))); + if (tcx->cond_flag >= 20) { + tcin = INV(tcin); + } + pred = (insn->lop == C55_LOP_CMPAND) ? AND(pred, tcin) : OR(pred, tcin); + } + ut64 zbit = (tcz->cond_flag & 1) ? 0x1000 : 0x2000; + return SETG("st0_55", ITE(pred, + LOGOR(VARG("st0_55"), UN(16, zbit)), + LOGAND(VARG("st0_55"), UN(16, (~zbit) & 0xffff)))); + } + // rol / ror BitIn, ACx, BitOut, ACy: rotate ACx left/right by one through a + // status bit. The bit shifted in comes from st0_55 (carry = bit 11, tc2 = + // bit 12), zero-extended to the 40-bit accumulator; the bit shifted out + // (msb for rol, lsb for ror) is written to the rotate-out status bit. The + // legacy models this only for accumulator src+dst, so other classes fall + // through to a null lift. + if (insn->lop == C55_LOP_ROL || insn->lop == C55_LOP_ROR) { + if (insn->n_ops < 4 || !a->reg_info) { + return NULL; + } + const C55Operand *bin = &insn->ops[0]; + const C55Operand *src = &insn->ops[1]; + const C55Operand *bout = &insn->ops[2]; + const C55Operand *dst = &insn->ops[3]; + if (src->reg.cls != C55_RC_AC || dst->reg.cls != C55_RC_AC) { + return NULL; + } + const C55RegInfo *si = a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE); + const C55RegInfo *di = a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE); + if (!si || !si->il_var || !di || !di->il_var) { + return NULL; + } + const ut32 in_pos = (bin->cond_flag == 6) ? 11 : 12; // carry / tc2 + const ut32 out_pos = (bout->cond_flag == 6) ? 11 : 12; + const ut64 out_mask = 1ULL << out_pos; + // the rotate-in bit, isolated from st0_55 and widened to the accumulator + RzILOpPure *inbit = UNSIGNED(40, LOGAND(SHIFTR(IL_FALSE, VARG("st0_55"), UN(8, in_pos)), UN(16, 1))); + RzILOpPure *rotated; + RzILOpBool *outbit; + if (insn->lop == C55_LOP_ROL) { + rotated = LOGOR(SHIFTL(IL_FALSE, VARG(si->il_var), UN(6, 1)), inbit); + outbit = MSB(VARG(si->il_var)); + } else { + rotated = LOGOR(SHIFTR(IL_FALSE, VARG(si->il_var), UN(6, 1)), + SHIFTL(IL_FALSE, inbit, UN(6, 0x27))); + outbit = LSB(VARG(si->il_var)); + } + return SEQ2(SETG(di->il_var, rotated), + SETG("st0_55", ITE(outbit, + LOGOR(VARG("st0_55"), UN(16, out_mask)), + LOGAND(VARG("st0_55"), UN(16, (~out_mask) & 0xffff))))); + } + // mpyk / mpykr #k, ACx[, ACy]: ACy = #k * ACx, the signed constant times the + // low 16 bits of ACx, sign-extended to the 40-bit accumulator. The rounding + // (mpykr) variant adds 0x8000 and clears the low word. + if (insn->lop == C55_LOP_MPYK) { + if (insn->n_ops < 3 || !a->reg_info) { + return NULL; + } + const C55Operand *imm = &insn->ops[0]; + const C55Operand *src = &insn->ops[1]; + const C55Operand *dst = &insn->ops[2]; + if (src->reg.cls != C55_RC_AC || dst->reg.cls != C55_RC_AC) { + return NULL; + } + if (src->reg.sub == C55_SUB_HI || src->reg.sub == C55_SUB_LO) { + // A half-register (ACx.h / ACx.l) multiplicand is left unlifted, as + // the legacy lifter does -- only the whole-accumulator (low 16 bits) + // source form lifts. + return NULL; + } + const C55RegInfo *si = a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE); + const C55RegInfo *di = a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE); + if (!si || !si->il_var || !di || !di->il_var) { + return NULL; + } + // the constant is a 16-bit signed value (the 0x1e short form supplies + // only the low 8 bits, with the high byte zero), sign-extended to the + // 40-bit accumulator. + ut64 k = imm->imm & 0xffff; + if (k & 0x8000) { + k |= 0xffffff0000ULL; + } + RzILOpPure *val = MUL(UN(40, k), SIGNED(40, UNSIGNED(16, VARG(si->il_var)))); + if (insn->round) { + val = LOGAND(ADD(val, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); + } + return SETG(di->il_var, val); + } + // mack / mackr Tx, #k, ACx[, ACy]: ACy = ACx + #k * Tx, the signed constant + // times the (sign-extended) Tx coefficient, accumulated into ACx. The + // rounding (mackr) variant adds 0x8000 and clears the low word. + if (insn->lop == C55_LOP_MACK) { + if (insn->n_ops < 4 || !a->reg_info) { + return NULL; + } + const C55Operand *tx = &insn->ops[0]; + const C55Operand *imm = &insn->ops[1]; + const C55Operand *src = &insn->ops[2]; + const C55Operand *dst = &insn->ops[3]; + if (tx->reg.cls != C55_RC_T || src->reg.cls != C55_RC_AC || dst->reg.cls != C55_RC_AC) { + return NULL; + } + const C55RegInfo *ti = a->reg_info(tx->reg.cls, tx->reg.num, C55_SUB_NONE); + const C55RegInfo *si = a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE); + const C55RegInfo *di = a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE); + if (!ti || !ti->il_var || !si || !si->il_var || !di || !di->il_var) { + return NULL; + } + ut64 k = imm->imm & 0xffff; + if (k & 0x8000) { + k |= 0xffffff0000ULL; + } + RzILOpPure *val = ADD(VARG(si->il_var), MUL(SIGNED(40, VARG(ti->il_var)), UN(40, k))); + if (insn->round) { + val = LOGAND(ADD(val, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); + } + return SETG(di->il_var, val); + } + // Memory bitwise: ACy = ACx sx(Smem) for the 0xd9 / 0xda / 0xdb "and / + // or / xor Smem, [src,] dst" forms. The 16-bit memory operand is sign-extended + // to the 40-bit accumulator width and combined with the source accumulator. + // The legacy lifts these only when both source and destination are + // accumulators (it leaves T/AR operands unlifted), so the other register + // classes return null and match the legacy by falling through. + if (insn->lop == C55_LOP_ANDMEM || insn->lop == C55_LOP_ORMEM || insn->lop == C55_LOP_XORMEM) { + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *smem = &insn->ops[0]; + const C55Operand *dst = &insn->ops[insn->n_ops - 1]; + const C55Operand *src = insn->n_ops >= 3 ? &insn->ops[1] : dst; + if (smem->kind != C55_OP_MEM || + src->kind != C55_OP_REG || src->reg.sub != C55_SUB_NONE || + dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE) { + return NULL; + } + const C55RegInfo *si = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *di = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!si || !di || !si->il_var || !di->il_var || si->width != di->width) { + return NULL; + } + // The Smem base is an ARn for display but the address pointer is the + // 23-bit XARn, so promote it before forming the effective address. + C55Operand mp = *smem; + if (mp.reg.cls == C55_RC_AR) { + mp.reg.cls = C55_RC_XAR; + } + RzILOpPure *rd = c55_read(a, &mp); + if (!rd) { + return NULL; + } + RzILOpPure *opnd; + if (si->width >= 40) { + // accumulator destination: sign-extend the 16-bit memory word to the + // 40-bit accumulator width before the bitwise op. + RzILOpPure *rd2 = c55_read(a, &mp); + if (!rd2) { + rz_il_op_pure_free(rd); + return NULL; + } + opnd = CAST(40, MSB(rd), rd2); + } else { + // 16-bit AR / T destination: operate directly on the 16-bit word. + opnd = rd; + } + RzILOpPure *res = NULL; + switch (insn->lop) { + case C55_LOP_ANDMEM: res = LOGAND(VARG(si->il_var), opnd); break; + case C55_LOP_ORMEM: res = LOGOR(VARG(si->il_var), opnd); break; + case C55_LOP_XORMEM: res = LOGXOR(VARG(si->il_var), opnd); break; + default: rz_il_op_pure_free(opnd); return NULL; + } + RzILOpEffect *eff = SETG(di->il_var, res); + RzILOpEffect *post = c55_post_effect(a, &mp); + return post ? SEQ2(eff, post) : eff; + } + // Dual-memory move (mov [dbl(]Xmem[)], [dbl(]Ymem[)]): store the word read + // from Xmem (the source) into Ymem (the destination); dbl() operands move a + // 32-bit long word. The pointer bases display as ARn but address through the + // 23-bit XARn, so promote before forming the effective address. Each + // operand's post-modify is sequenced after the move, Xmem then Ymem. + if (insn->lop == C55_LOP_MOVMEM) { + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; // Xmem + const C55Operand *dst = &insn->ops[1]; // Ymem + if (src->kind != C55_OP_MEM || dst->kind != C55_OP_MEM) { + return NULL; + } + C55Operand sp = *src; + if (sp.reg.cls == C55_RC_AR) { + sp.reg.cls = C55_RC_XAR; + } + C55Operand dp = *dst; + if (dp.reg.cls == C55_RC_AR) { + dp.reg.cls = C55_RC_XAR; + } + RzILOpPure *ld = c55_read(a, &sp); + if (!ld) { + return NULL; + } + RzILOpEffect *eff = c55_write(a, &dp, ld); + if (!eff) { + return NULL; + } + RzILOpEffect *px = c55_post_effect(a, &sp); + if (px) { + eff = SEQ2(eff, px); + } + RzILOpEffect *py = c55_post_effect(a, &dp); + if (py) { + eff = SEQ2(eff, py); + } + return eff; + } + // Dual-memory add / subtract into an accumulator (add / sub Xmem, Ymem, + // ACx): ACx = sx(Xmem) +/- sx(Ymem), each Smem read as a signed 16-bit word + // widened to the 40-bit accumulator. Post-modify of each pointer follows. + if (insn->lop == C55_LOP_DUALADD || insn->lop == C55_LOP_DUALSUB) { + if (insn->n_ops < 3) { + return NULL; + } + const C55Operand *xm = &insn->ops[0]; + const C55Operand *ym = &insn->ops[1]; + const C55Operand *dst = &insn->ops[2]; + if (xm->kind != C55_OP_MEM || ym->kind != C55_OP_MEM || + dst->kind != C55_OP_REG || dst->reg.cls != C55_RC_AC || dst->reg.sub != C55_SUB_NONE) { + return NULL; + } + const C55RegInfo *di = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!di || !di->il_var) { + return NULL; + } + C55Operand xp = *xm; + if (xp.reg.cls == C55_RC_AR) { + xp.reg.cls = C55_RC_XAR; + } + C55Operand yp = *ym; + if (yp.reg.cls == C55_RC_AR) { + yp.reg.cls = C55_RC_XAR; + } + RzILOpPure *xa = c55_read(a, &xp), *xb = c55_read(a, &xp); + RzILOpPure *ya = c55_read(a, &yp), *yb = c55_read(a, &yp); + if (!xa || !xb || !ya || !yb) { + rz_il_op_pure_free(xa); + rz_il_op_pure_free(xb); + rz_il_op_pure_free(ya); + rz_il_op_pure_free(yb); + return NULL; + } + RzILOpPure *sx = CAST(40, MSB(xa), xb); + RzILOpPure *sy = CAST(40, MSB(ya), yb); + RzILOpPure *res = (insn->lop == C55_LOP_DUALADD) ? ADD(sx, sy) : SUB(sx, sy); + RzILOpEffect *eff = SETG(di->il_var, res); + RzILOpEffect *px = c55_post_effect(a, &xp); + if (px) { + eff = SEQ2(eff, px); + } + RzILOpEffect *py = c55_post_effect(a, &yp); + if (py) { + eff = SEQ2(eff, py); + } + return eff; + } + // (neg / max / min map to SUB / CMP / CMP). All are equal-width register + // forms; mixed-width encodings (which the legacy decoder either truncates + // or leaves unlifted) and the equal-register short forms are left to the + // per-arch lifter. The multiply-accumulate ops (MAC / MAS) are an exception: + // they share the MUL op-type lifting (with the product accumulated into the + // destination), so they fall through to the switch below. + if (insn->lop != C55_LOP_NONE && insn->lop != C55_LOP_MAC && insn->lop != C55_LOP_MAS) { + if (insn->n_ops < 1) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; + // The 0x54 register ALU forms (round / sat / addv ACx, ACy) drop the ACx + // source when it equals the destination ACy, leaving a single operand + // that is both source and destination. + const C55Operand *dst = &insn->ops[insn->n_ops >= 2 ? 1 : 0]; + // neg ACx.h/.l, ACy.h/.l: a half-register negate. Read the 16-bit + // source half, negate it, and merge-write it into the destination half + // (preserving the rest of the accumulator), via the half-aware + // read/write helpers. The full-register path below requires SUB_NONE. + if (insn->lop == C55_LOP_NEG && + src->kind == C55_OP_REG && (src->reg.sub == C55_SUB_HI || src->reg.sub == C55_SUB_LO) && + dst->kind == C55_OP_REG && (dst->reg.sub == C55_SUB_HI || dst->reg.sub == C55_SUB_LO)) { + RzILOpPure *sv = c55_read(a, src); + if (!sv) { + return NULL; + } + return c55_write(a, dst, SUB(UN(16, 0), sv)); + } + if (src->kind != C55_OP_REG || src->reg.sub != C55_SUB_NONE || + dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE || + src->shamt || src->sh_left || src->sh_by_reg) { + return NULL; + } + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *sri = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var || !sri || !sri->il_var || dri->width != sri->width) { + return NULL; + } + switch (insn->lop) { + case C55_LOP_NEG: // dst = -src + return SETG(dri->il_var, SUB(UN(dri->width, 0), VARG(sri->il_var))); + case C55_LOP_MAX: // dst = (src > dst) ? src : dst + return SETG(dri->il_var, + ITE(INV(SLE(VARG(sri->il_var), VARG(dri->il_var))), + VARG(sri->il_var), VARG(dri->il_var))); + case C55_LOP_MIN: // dst = (src < dst) ? src : dst + return SETG(dri->il_var, + ITE(AND(SLE(VARG(sri->il_var), VARG(dri->il_var)), + INV(EQ(VARG(sri->il_var), VARG(dri->il_var)))), + VARG(sri->il_var), VARG(dri->il_var))); + case C55_LOP_ABS: // dst = (src < 0) ? -src : src + return SETG(dri->il_var, + ITE(AND(SLE(VARG(sri->il_var), UN(dri->width, 0)), + INV(EQ(VARG(sri->il_var), UN(dri->width, 0)))), + SUB(UN(dri->width, 0), VARG(sri->il_var)), + VARG(sri->il_var))); + case C55_LOP_ROUND: // dst = round(src) -- (src + 0x8000) with the low word cleared + return SETG(dri->il_var, + LOGAND(ADD(VARG(sri->il_var), UN(dri->width, 0x8000)), + UN(dri->width, 0xffffff0000ULL))); + case C55_LOP_SAT: + // dst = saturate(src) to the 32-bit signed range: above 00 7FFF FFFFh + // clamps to that maximum, below FF 8000 0000h clamps to that minimum, + // otherwise the value passes through. The satr rounding refinement is + // not modeled (the legacy left this form unlifted). + return SETG(dri->il_var, + ITE(INV(SLE(VARG(sri->il_var), UN(dri->width, 0x7fffffffULL))), + UN(dri->width, 0x7fffffffULL), + ITE(AND(SLE(VARG(sri->il_var), UN(dri->width, 0xff80000000ULL)), + INV(EQ(VARG(sri->il_var), UN(dri->width, 0xff80000000ULL)))), + UN(dri->width, 0xff80000000ULL), + VARG(sri->il_var)))); + case C55_LOP_ADDV: { + // dst = dst + |src(32-16)| -- the source high word's absolute value is + // accumulated into the destination low part (addrv's rounding is not + // modeled; the legacy left this form unlifted). + RzILOpPure *hi_sign = SIGNED(dri->width, CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(sri->il_var), UN(8, 16)))); + RzILOpPure *hi_neg = SIGNED(dri->width, CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(sri->il_var), UN(8, 16)))); + RzILOpPure *hi_pos = SIGNED(dri->width, CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(sri->il_var), UN(8, 16)))); + RzILOpPure *absv = ITE(MSB(hi_sign), SUB(UN(dri->width, 0), hi_neg), hi_pos); + return SETG(dri->il_var, ADD(VARG(dri->il_var), absv)); + } + default: + return NULL; + } + } + // Generic op_type-driven lifts for the shapes that are identical across the + // C55 family. Anything not handled here returns NULL so the caller can fall + // back to the legacy per-arch lifter while the migration is in progress. + ut32 type = c55_effective_type(a, insn); + switch (type) { + case RZ_ANALYSIS_OP_TYPE_NOP: + return NOP(); + case RZ_ANALYSIS_OP_TYPE_REP: + // rpt only arms the repeat counter; it has no per-instruction data + // effect of its own, so it lifts to a nop (matching the legacy lifter). + return NOP(); + case RZ_ANALYSIS_OP_TYPE_LEA: { + // amar (modify auxiliary register(s)): no memory access, only the + // addressing modes' post-modify side effects (e.g. *arN+ increments + // xarN), sequenced in operand order. The triple-operand form + // (amar Xmem, Ymem, Cmem) modifies up to three pointers; a non-modifying + // mode (plain indirect, indexed, *arN(tM), ...) contributes no effect, so + // an all-non-modifying instruction lifts to a nop, matching the legacy. + // + // The C55x+ "amar Smem, xar" form instead loads the effective (word) + // address into an extended AR register: xar = base + offset, with no + // byte-address scaling and no memory access. It is distinguished by a + // single memory operand followed by a register destination. + if (insn->n_ops == 2 && insn->ops[0].kind == C55_OP_MEM && + insn->ops[1].kind == C55_OP_REG) { + const C55Operand *mem = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var || dst->reg.sub != C55_SUB_NONE) { + return NULL; + } + // Only the base+constant forms are lifted (SP/AR relative); the + // legacy decoder leaves the DP-direct and register-modify forms + // without IL, so decline those here too. + RzILOpPure *base; + if (mem->amode == C55_AM_INDEXED || mem->amode == C55_AM_CONST_IDX) { + C55Reg br = mem->reg; + if (br.cls == C55_RC_AR) { + br.cls = C55_RC_XAR; // ARn is the low half of XARn + } + base = c55_reg_var(a, br); + if (!base) { + return NULL; + } + RzILOpPure *ea = ADD(UNSIGNED(24, base), SN(24, mem->disp)); + return SETG(dri->il_var, CAST(dri->width, IL_FALSE, ea)); + } + return NULL; + } + RzILOpEffect *acc = NULL; + for (ut8 i = 0; i < insn->n_ops; i++) { + if (insn->ops[i].kind != C55_OP_MEM) { + continue; + } + C55Operand m = insn->ops[i]; + if (m.amode == C55_AM_DIRECT) { + // the @#k data-page direct form has no pointer side effect and + // the legacy lifter emits no IL for it (not even a nop); match + // that rather than claiming an empty effect. + rz_il_op_effect_free(acc); + return NULL; + } + if (m.amode == C55_AM_BITREV || m.amode == C55_AM_BITREV_SUB) { + // the reverse-carry post-modify is a real pointer update but is + // not modeled here; emit no IL (matching the legacy lifter) + // rather than a misleading nop that would claim no effect. + rz_il_op_effect_free(acc); + return NULL; + } + if (m.reg.cls == C55_RC_AR) { + m.reg.cls = C55_RC_XAR; // ARn is the low half of XARn + } + RzILOpEffect *e = c55_post_effect(a, &m); + if (!e) { + continue; + } + acc = acc ? SEQ2(acc, e) : e; + } + return acc ? acc : NOP(); + } + case RZ_ANALYSIS_OP_TYPE_MOV: { + // reg-to-reg move (sign-extended to dst width), mov #imm16, reg + // (immediate zero-extended to dst width), or a single-data-memory + // load / store / copy. Shifted / sub-field register forms are left to + // the per-arch lifter. + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + // Smem load / copy (memory -> register) and store (register -> memory). + if (src->kind == C55_OP_MEM && dst->kind == C55_OP_REG) { + return c55_mem_move(a, dst, src, true); + } + if (dst->kind == C55_OP_MEM && src->kind == C55_OP_REG) { + return c55_mem_move(a, src, dst, false); + } + // mov #imm, Smem (immediate -> memory store, e.g. the byte() form): the + // immediate is materialised as a 16-bit word and truncated to the access + // width, then stored at the (post-modify-free) effective address. Only + // the addressing modes the shared mover supports are lifted. + if (dst->kind == C55_OP_MEM && src->kind == C55_OP_IMM && + !dst->shamt && !dst->sh_mem_reg_set && !dst->mem_round && dst->byte_sel != 1 && dst->byte_sel != 2) { + switch (dst->amode) { + case C55_AM_INDIRECT: + case C55_AM_POSTINC: + case C55_AM_POSTDEC: + case C55_AM_IDXREG: + case C55_AM_POSTADD: + case C55_AM_POSTSUB: + case C55_AM_INDEXED: + case C55_AM_ABSOLUTE: + case C55_AM_CONST_IDX: + case C55_AM_CONST_IDX_PRE: + break; + default: + return NULL; + } + C55Operand m = *dst; + if (m.reg.cls == C55_RC_AR) { + m.reg.cls = C55_RC_XAR; + } + RzILOpPure *addr = c55_ea(a, &m); + if (!addr) { + return NULL; + } + ut32 aw = m.access ? m.access : 16; + RzILOpPure *v = UN(16, src->imm & 0xffff); + if (aw != 16) { + v = UNSIGNED(aw, v); + } + RzILOpEffect *wr = STOREW(addr, v); + RzILOpEffect *post = c55_post_effect(a, &m); + return post ? SEQ2(wr, post) : wr; + } + // mov gr4, hi(ACx): write the source's low 16 bits into the accumulator + // high word (bits 31-16), preserving the rest. The legacy lifter shifts + // by a 6-bit count, reproduced here for byte-exactness. + if (dst->kind == C55_OP_REG && dst->reg.sub == C55_SUB_HI && + src->kind == C55_OP_REG && src->reg.sub == C55_SUB_NONE && + !src->shamt && !src->sh_left && !src->sh_by_reg) { + const C55RegInfo *hdri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!hdri || !hdri->il_var) { + return NULL; + } + RzILOpPure *sv = c55_read(a, src); + if (!sv) { + return NULL; + } + const C55RegInfo *ssri = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + if (ssri && ssri->width > 16) { + sv = CAST(16, IL_FALSE, sv); + } + RzILOpPure *wide = UNSIGNED(hdri->width, sv); + return SETG(hdri->il_var, + LOGOR(LOGAND(VARG(hdri->il_var), UN(hdri->width, 0xff0000ffffULL)), + SHIFTL(IL_FALSE, wide, UN(6, 16)))); + } + // mov #imm, ACx.h / ACx.l: write the immediate into the accumulator + // high or low word, preserving the rest (the 0x7b #k4 and 0xac #k16 + // register-short / immediate moves into a sub-register). The legacy + // lifter shifts the high word by a 6-bit count and zero-extends the + // immediate to 16 bits; reproduced here for byte-exactness. + if (src->kind == C55_OP_IMM && !src->addr && !src->neg_imm && + !src->sh_left && !src->shamt && dst->kind == C55_OP_REG && + (dst->reg.sub == C55_SUB_HI || dst->reg.sub == C55_SUB_LO)) { + const C55RegInfo *sdri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!sdri || !sdri->il_var) { + return NULL; + } + RzILOpPure *wide = UNSIGNED(sdri->width, UN(16, src->imm & 0xffff)); + if (dst->reg.sub == C55_SUB_HI) { + return SETG(sdri->il_var, + LOGOR(LOGAND(VARG(sdri->il_var), UN(sdri->width, 0xff0000ffffULL)), + SHIFTL(IL_FALSE, wide, UN(6, 16)))); + } + return SETG(sdri->il_var, + LOGOR(LOGAND(VARG(sdri->il_var), UN(sdri->width, 0xffffff0000ULL)), wide)); + } + if (dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE) { + return NULL; + } + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var) { + return NULL; + } + if (src->kind == C55_OP_IMM && !src->addr) { + if (src->sh_left && src->shamt_hex) { + // mov #k16 << #sh (opcode 0x75): the legacy leaves this form + // unlifted, so decline and fall through to the (null) legacy IL. + return NULL; + } + if (src->neg_imm) { + // mov -#k, dst (opcode 0x3e): unlifted by the legacy decoder. + return NULL; + } + ut64 v = src->imm; + if (src->sh_left && src->shamt) { + // A left-shifted immediate (mov #k16 << #16): the field is a + // signed constant, sign-extended before the shift; the UN below + // truncates back to the destination width. + if (src->width > 0 && src->width < 64 && (v & (1ULL << (src->width - 1)))) { + v |= ~((1ULL << src->width) - 1); + } + v <<= src->shamt; + } + return SETG(dri->il_var, UN(dri->width, v)); + } + if (src->kind == C55_OP_REG && src->reg.sub == C55_SUB_NONE && + !src->shamt && !src->sh_left && !src->sh_by_reg) { + const C55RegInfo *sri = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + if (!sri || !sri->il_var) { + return NULL; + } + RzILOpPure *v = VARG(sri->il_var); + if (dri->width > sri->width) { + // Widening register move. Address/pointer registers (AR, XAR) + // hold unsigned values and are zero-extended; data registers + // (AC, T) are sign-extended. + if (src->reg.cls == C55_RC_AR || src->reg.cls == C55_RC_XAR) { + v = UNSIGNED(dri->width, v); + } else { + v = SIGNED(dri->width, v); + } + } else if (dri->width < sri->width) { + // narrowing register move (e.g. mov ACx, sp): keep the low bits. + v = UNSIGNED(dri->width, v); + } + return SETG(dri->il_var, v); + } + if (src->kind == C55_OP_REG && (src->reg.sub == C55_SUB_HI || src->reg.sub == C55_SUB_LO) && + !src->shamt && !src->sh_left && !src->sh_by_reg) { + // mov hi(ACx), dst / mov ACx.l, dst: the 16-bit high or low word, + // sign-extended to the destination width (c55_read selects the half). + RzILOpPure *v = c55_read(a, src); + if (!v) { + return NULL; + } + if (dri->width > 16) { + v = SIGNED(dri->width, v); + } else if (dri->width < 16) { + rz_il_op_pure_free(v); + return NULL; + } + return SETG(dri->il_var, v); + } + return NULL; + } + case RZ_ANALYSIS_OP_TYPE_XCHG: { + // swap rX, rY: exchange via the XOR-swap idiom (X^=Y; Y^=X; X^=Y). + // swapp exchanges two consecutive pairs, swap4 four (X..X+3 <-> Y..Y+3). + if (insn->n_ops < 2 || !a->reg_info) { + return NULL; + } + const C55Operand *x = &insn->ops[0]; + const C55Operand *y = &insn->ops[1]; + if (x->kind != C55_OP_REG || y->kind != C55_OP_REG) { + return NULL; + } + int npairs = insn->quad ? 4 : (insn->both ? 2 : 1); + const C55RegInfo *xi[4], *yi[4]; + for (int p = 0; p < npairs; p++) { + xi[p] = a->reg_info(x->reg.cls, (ut8)(x->reg.num + p), C55_SUB_NONE); + yi[p] = a->reg_info(y->reg.cls, (ut8)(y->reg.num + p), C55_SUB_NONE); + if (!xi[p] || !xi[p]->il_var || !yi[p] || !yi[p]->il_var) { + return NULL; + } + } + RzILOpEffect *eff[12]; + for (int p = 0; p < npairs; p++) { + eff[p * 3 + 0] = SETG(xi[p]->il_var, LOGXOR(VARG(xi[p]->il_var), VARG(yi[p]->il_var))); + eff[p * 3 + 1] = SETG(yi[p]->il_var, LOGXOR(VARG(yi[p]->il_var), VARG(xi[p]->il_var))); + eff[p * 3 + 2] = SETG(xi[p]->il_var, LOGXOR(VARG(xi[p]->il_var), VARG(yi[p]->il_var))); + } + if (npairs == 1) { + return SEQ3(eff[0], eff[1], eff[2]); + } + if (npairs == 2) { + return SEQ6(eff[0], eff[1], eff[2], eff[3], eff[4], eff[5]); + } + return SEQN(12, eff[0], eff[1], eff[2], eff[3], eff[4], eff[5], + eff[6], eff[7], eff[8], eff[9], eff[10], eff[11]); + } + case RZ_ANALYSIS_OP_TYPE_ADD: + case RZ_ANALYSIS_OP_TYPE_SUB: { + // dst = src. A full-register source is sign-extended to the dst + // width (add/sub ACx, ACy or add/sub Tx, ACy); a plain (unshifted, + // unsigned) immediate source is zero-extended to the dst width + // (add/sub k4, dst). Sub-field (.h/.l), shifted, and signed immediate + // sources are left to the per-arch lifter. + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + if (src->kind == C55_OP_IMM && !src->imm_signed && !src->addr && + !src->shamt && !src->sh_left && !src->sh_by_reg && + dst->kind == C55_OP_REG && dst->reg.sub == C55_SUB_NONE) { + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var) { + return NULL; + } + RzILOpPure *res = (type == RZ_ANALYSIS_OP_TYPE_ADD) + ? ADD(VARG(dri->il_var), UN(dri->width, src->imm)) + : SUB(VARG(dri->il_var), UN(dri->width, src->imm)); + return SETG(dri->il_var, res); + } + if (dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE || + src->kind != C55_OP_REG || src->reg.sub != C55_SUB_NONE || + src->shamt || src->sh_left || src->sh_by_reg) { + return NULL; + } + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *sri = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var || !sri || !sri->il_var || dri->width < sri->width) { + return NULL; + } + RzILOpPure *s = VARG(sri->il_var); + if (dri->width > sri->width) { + s = SIGNED(dri->width, s); + } + RzILOpPure *res = (type == RZ_ANALYSIS_OP_TYPE_ADD) + ? ADD(VARG(dri->il_var), s) + : SUB(VARG(dri->il_var), s); + return SETG(dri->il_var, res); + } + case RZ_ANALYSIS_OP_TYPE_AND: + case RZ_ANALYSIS_OP_TYPE_OR: + case RZ_ANALYSIS_OP_TYPE_XOR: + case RZ_ANALYSIS_OP_TYPE_NOT: { + // Bitwise accumulator ops on equal-width full registers: and/or/xor are + // dst = src, not is dst = ~src. Narrow (sub-width) sources and + // sub-field (.h/.l) operands use different bit semantics (operating on a + // 16-bit slice and merging) and are left to the per-arch lifter. + if (insn->n_ops < 2) { + return NULL; + } + const C55Operand *src = &insn->ops[0]; + const C55Operand *dst = &insn->ops[1]; + // and/or #k16, Smem: read-modify-write the data-memory word in place + // (Smem op= imm). Only the addressing modes the shared effective-address + // primitive supports are lifted; the byte-select and shifted forms fall + // back. NOT has a single source and is excluded. + if (type != RZ_ANALYSIS_OP_TYPE_NOT && dst->kind == C55_OP_MEM && src->kind == C55_OP_IMM && + !dst->shamt && !dst->sh_mem_reg_set && !dst->mem_round && dst->byte_sel != 1 && dst->byte_sel != 2) { + switch (dst->amode) { + case C55_AM_INDIRECT: + case C55_AM_POSTINC: + case C55_AM_POSTDEC: + case C55_AM_IDXREG: + case C55_AM_POSTADD: + case C55_AM_POSTSUB: + case C55_AM_INDEXED: + case C55_AM_ABSOLUTE: + case C55_AM_CONST_IDX: + case C55_AM_CONST_IDX_PRE: + break; + default: + return NULL; + } + C55Operand m = *dst; + if (m.reg.cls == C55_RC_AR) { + m.reg.cls = C55_RC_XAR; + } + RzILOpPure *la = c55_ea(a, &m); + RzILOpPure *sa = c55_ea(a, &m); + if (!la || !sa) { + rz_il_op_pure_free(la); + rz_il_op_pure_free(sa); + return NULL; + } + ut32 aw = m.access ? m.access : 16; + RzILOpPure *k = UN(16, src->imm & 0xffff); + if (aw != 16) { + k = UNSIGNED(aw, k); + } + RzILOpPure *res = type == RZ_ANALYSIS_OP_TYPE_AND ? LOGAND(LOADW(aw, la), k) : LOGOR(LOADW(aw, la), k); + RzILOpEffect *wr = STOREW(sa, res); + RzILOpEffect *post = c55_post_effect(a, &m); + return post ? SEQ2(wr, post) : wr; + } + // C55x+ 16-bit slice bitwise ops (the 0x75 and/or/xor src, dst forms): + // when an operand is a sub-register half, or the two registers differ in + // width (e.g. xor Tx, ACy / xor ACx.l, Ty), the operation runs on 16-bit + // low-word slices and the 16-bit result is merged back -- accumulator + // destinations preserve bits 39-16, 16-bit destinations are written + // whole. NOT is excluded (single source, handled below). + if (type != RZ_ANALYSIS_OP_TYPE_NOT && src->kind == C55_OP_REG && dst->kind == C55_OP_REG && + !src->shamt && !src->sh_left && !src->sh_by_reg && a->reg_info) { + const C55RegInfo *sr = a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE); + const C55RegInfo *dr = a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE); + bool src_half = src->reg.sub == C55_SUB_HI || src->reg.sub == C55_SUB_LO; + bool dst_half = dst->reg.sub == C55_SUB_HI || dst->reg.sub == C55_SUB_LO; + if (sr && dr && sr->il_var && dr->il_var && + (src_half || dst_half || sr->width != dr->width)) { + RzILOpPure *sv = src_half ? c55_read(a, src) + : (sr->width > 16 ? CAST(16, IL_FALSE, VARG(sr->il_var)) : VARG(sr->il_var)); + RzILOpPure *dv = dst_half ? c55_read(a, dst) + : (dr->width > 16 ? CAST(16, IL_FALSE, VARG(dr->il_var)) : VARG(dr->il_var)); + if (!sv || !dv) { + rz_il_op_pure_free(sv); + rz_il_op_pure_free(dv); + return NULL; + } + RzILOpPure *res = type == RZ_ANALYSIS_OP_TYPE_AND ? LOGAND(dv, sv) + : type == RZ_ANALYSIS_OP_TYPE_OR ? LOGOR(dv, sv) + : LOGXOR(dv, sv); + if (dr->width > 16) { + if (dst->reg.sub == C55_SUB_HI) { + // high-word destination (bits 31-16): preserve bits 39-32 + // and 15-0, and shift the 16-bit result up by 16. + ut64 keep = (dr->width >= 64 ? ~0ULL : ((1ULL << dr->width) - 1)) & ~((ut64)0xffff << 16); + return SETG(dr->il_var, LOGOR(LOGAND(VARG(dr->il_var), UN(dr->width, keep)), + SHIFTL(IL_FALSE, UNSIGNED(dr->width, res), UN(6, 16)))); + } + // low-word destination (bits 15-0): preserve bits 39-16. + ut64 keep = (dr->width >= 64 ? ~0ULL : ((1ULL << dr->width) - 1)) & ~(ut64)0xffff; + return SETG(dr->il_var, LOGOR(LOGAND(VARG(dr->il_var), UN(dr->width, keep)), UNSIGNED(dr->width, res))); + } + return SETG(dr->il_var, res); + } + } + if (dst->kind != C55_OP_REG || dst->reg.sub != C55_SUB_NONE || + src->kind != C55_OP_REG || src->reg.sub != C55_SUB_NONE || + src->shamt || src->sh_left || src->sh_by_reg) { + return NULL; + } + const C55RegInfo *dri = a->reg_info ? a->reg_info(dst->reg.cls, dst->reg.num, C55_SUB_NONE) : NULL; + const C55RegInfo *sri = a->reg_info ? a->reg_info(src->reg.cls, src->reg.num, C55_SUB_NONE) : NULL; + if (!dri || !dri->il_var || !sri || !sri->il_var || dri->width != sri->width) { + return NULL; + } + if (type == RZ_ANALYSIS_OP_TYPE_NOT) { + return SETG(dri->il_var, LOGNOT(VARG(sri->il_var))); + } + RzILOpPure *res = + type == RZ_ANALYSIS_OP_TYPE_AND ? LOGAND(VARG(dri->il_var), VARG(sri->il_var)) : + type == RZ_ANALYSIS_OP_TYPE_OR ? LOGOR(VARG(dri->il_var), VARG(sri->il_var)) : + LOGXOR(VARG(dri->il_var), VARG(sri->il_var)); + return SETG(dri->il_var, res); + } + case RZ_ANALYSIS_OP_TYPE_PUSH: + case RZ_ANALYSIS_OP_TYPE_POP: { + // Single-word stack op: SP is a 16-bit word pointer, the stack lives in + // data memory at byte address SP<<1. Push pre-decrements then stores; + // pop loads then post-increments. Accumulator / dbl(ACx) (32-bit, two + // words) move two words. The register-pair forms (pop/psh rX, rY) move + // each register in turn, in operand order, with its own SP adjustment. + if (insn->n_ops < 1) { + return NULL; + } + if (insn->both && insn->n_ops < 2) { + // popboth / pshboth move a register *pair* in one op via a single + // operand; the single-register lift below would be wrong, so leave + // them unlifted (matching the legacy decoder). The two-operand + // "psh/pop rX, rY" forms (also flagged .both) fall through to the + // per-register loop below, which models them correctly. + return NULL; + } + // Build the per-register effect sequence; for the two-register pair + // forms this is the two single-register sequences concatenated. + RzILOpEffect *acc = NULL; + for (ut8 i = 0; i < insn->n_ops && i < 2; i++) { + const C55Operand *o = &insn->ops[i]; + C55Reg reg; + if (o->kind == C55_OP_REG && o->reg.sub == C55_SUB_NONE) { + reg = o->reg; + } else if (o->kind == C55_OP_MEM && o->amode == C55_AM_MMR) { + // pop / psh mmap(@reg): the memory-mapped register moves through + // the stack exactly as a plain register of its own width. + reg = o->reg; + } else { + return NULL; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(reg.cls, reg.num, C55_SUB_NONE) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + RzILOpEffect *step = NULL; + if (ri->width == 40) { + // Accumulator stack op: 32 bits (two words) move through the + // stack. psh stores the low 32 bits; pop reloads them while + // preserving the guard byte. SP advances by two words. + if (type == RZ_ANALYSIS_OP_TYPE_PUSH) { + step = SEQ2( + SETG("sp", SUB(VARG("sp"), UN(16, 2))), + STOREW(MUL(UNSIGNED(24, VARG("sp")), UN(24, 2)), UNSIGNED(32, VARG(ri->il_var)))); + } else { + step = SEQ2( + SETG(ri->il_var, LOGOR(LOGAND(VARG(ri->il_var), UN(40, 0xff00000000ULL)), + UNSIGNED(40, LOADW(32, MUL(UNSIGNED(24, VARG("sp")), UN(24, 2)))))), + SETG("sp", ADD(VARG("sp"), UN(16, 2)))); + } + } else { + if (o->dbl || ri->width != 16) { + return NULL; + } + if (type == RZ_ANALYSIS_OP_TYPE_PUSH) { + step = SEQ2( + SETG("sp", SUB(VARG("sp"), UN(16, 1))), + STOREW(MUL(UNSIGNED(24, VARG("sp")), UN(24, 2)), VARG(ri->il_var))); + } else { + step = SEQ2( + SETG(ri->il_var, LOADW(16, MUL(UNSIGNED(24, VARG("sp")), UN(24, 2)))), + SETG("sp", ADD(VARG("sp"), UN(16, 1)))); + } + } + acc = acc ? SEQ2(acc, step) : step; + } + return acc; + } + case RZ_ANALYSIS_OP_TYPE_JMP: + case RZ_ANALYSIS_OP_TYPE_CALL: + // A direct branch / call lifts as an unconditional transfer to the + // resolved (pc-relative or absolute) target. + return JMP(UN(24, c55_branch_target(insn, pc) & 0xffffff)); + case RZ_ANALYSIS_OP_TYPE_UJMP: + case RZ_ANALYSIS_OP_TYPE_UCALL: { + // Register-indirect branch / call: jump to the (24-bit) address held in + // the operand register. The call's return-address push is modelled in + // the analysis stack metadata rather than the IL, matching the legacy + // lifter. + if (insn->n_ops < 1 || insn->ops[0].kind != C55_OP_REG) { + return NULL; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(insn->ops[0].reg.cls, insn->ops[0].reg.num, insn->ops[0].reg.sub) : NULL; + if (!ri || !ri->il_var) { + return NULL; + } + return JMP(UNSIGNED(24, VARG(ri->il_var))); + } + case RZ_ANALYSIS_OP_TYPE_CCALL: + case RZ_ANALYSIS_OP_TYPE_CJMP: { + // bcc: take the branch to the resolved target when the predicate holds, + // else fall through. Flag-based predicates fall back to the per-arch lifter. + const C55Operand *cond = NULL; + for (ut8 i = 0; i < insn->n_ops; i++) { + if (insn->ops[i].kind == C55_OP_COND) { + cond = &insn->ops[i]; + break; + } + } + RzILOpPure *pred = cond ? c55_cond_pred(a, cond, insn->uns_all) : NULL; + if (!pred) { + return NULL; + } + return BRANCH(pred, JMP(UN(24, c55_branch_target(insn, pc) & 0xffffff)), NOP()); + } + case RZ_ANALYSIS_OP_TYPE_MUL: { + // Multiply, optionally accumulating. c55_mac_effect handles the register / + // memory single forms (mpy, mpym, mac{m}, mas{m}, sqrm / sqam / sqsm); the + // dual "::" forms are dispatched on insn->dual before this switch. + // The 0x54 register ALU forms (mpy / sqr / sqa / sqs ACx, ACy) drop the + // ACx multiplicand when it equals the destination ACy, leaving a single + // operand; the operation then squares / multiplies that accumulator by + // itself, so feed it to both multiplier inputs. + if (insn->n_ops >= 1 && insn->ops[0].kind == C55_OP_IMM) { + // The multiply-by-constant forms (mpyk / mack with a leading #k) are + // not lifted: c55_mac_effect models the multiplicand as a register or + // memory operand, and the dedicated MPYK lifter does not yet cover the + // C55x+ sub-register (ACx.h / ACx.l) source selection. Leave them + // unlifted (as the legacy lifter does) rather than emit wrong IL. + return NULL; + } + if (insn->n_ops == 1) { + C55Operand self[2] = { insn->ops[0], insn->ops[0] }; + return c55_mac_effect(a, self, 2, insn->lop, insn->round, insn->shift16, insn->square, insn->side_load); + } + return c55_mac_effect(a, insn->ops, insn->n_ops, insn->lop, insn->round, insn->shift16, insn->square, insn->side_load); + } + default: + return NULL; + } +} + +static void c55_fmt_reg(RzStrBuf *sb, const C55ArchDesc *a, const C55Reg *r) { + const C55RegInfo *ri = a->reg_info ? a->reg_info(r->cls, r->num, r->sub) : NULL; + const char *name = (ri && ri->name) ? ri->name : ""; + if (a->arch == C55_ARCH_C55X && (r->sub == C55_SUB_HI || r->sub == C55_SUB_LO)) { + // C55x renders the accumulator halves as hi(acN) / lo(acN); the C55x+ + // algebraic syntax uses the .h / .l suffix handled below. + rz_strbuf_appendf(sb, "%s(%s)", r->sub == C55_SUB_HI ? "hi" : "lo", name); + return; + } + rz_strbuf_append(sb, name); + switch (r->sub) { + case C55_SUB_LO: rz_strbuf_append(sb, ".l"); break; + case C55_SUB_HI: rz_strbuf_append(sb, ".h"); break; + case C55_SUB_GUARD: rz_strbuf_append(sb, ".g"); break; + default: break; + } +} + +static void c55_fmt_mem(RzStrBuf *sb, const C55ArchDesc *a, const C55Operand *m) { + if (m->uns) { + rz_strbuf_append(sb, "uns("); + } + if (m->byte_sel == 1) { + rz_strbuf_append(sb, "high_byte("); + } else if (m->byte_sel == 2) { + rz_strbuf_append(sb, "low_byte("); + } else if (m->byte_sel == 3) { + rz_strbuf_append(sb, "byte("); + } + if (m->amode == C55_AM_ABSOLUTE) { + if (a->arch == C55_ARCH_C55X) { + rz_strbuf_appendf(sb, "*(0x%06" PFMT64X ")", m->abs_addr); + } else { + rz_strbuf_appendf(sb, "*(#0x%" PFMT64x ")", m->abs_addr); + } + } else if (m->amode == C55_AM_MMR) { + // memory-mapped register access: mmap(@). + rz_strbuf_append(sb, "mmap(@"); + c55_fmt_reg(sb, a, &m->reg); + rz_strbuf_append(sb, ")"); + } else if (m->amode == C55_AM_DIRECT) { + // DP/SP-relative direct: @#k (the data-page direct address). + rz_strbuf_appendf(sb, "@#0x%x", (unsigned)((ut32)m->disp & 0x7f)); + } else if (m->amode == C55_AM_ABS16) { + rz_strbuf_appendf(sb, "abs16(0x%x)", (unsigned)((ut32)m->disp & 0xffff)); + } else if (m->amode == C55_AM_INDEXED && m->reg.cls == C55_RC_SP) { + // SP-relative direct: the C54x/C55x syntax is *sp(#Nh) (uppercase hex with + // an 'h' suffix); the C55x+ algebraic syntax is *sp(#0xN). + if (a->arch == C55_ARCH_C55X) { + rz_strbuf_appendf(sb, "*sp(#%Xh)", (unsigned)(m->disp & 0xffff)); + } else { + rz_strbuf_appendf(sb, "*sp(#0x%x)", (unsigned)(m->disp & 0xffff)); + } + } else { + rz_strbuf_append(sb, "*"); + if (m->amode == C55_AM_PREINC || m->amode == C55_AM_CONST_IDX_PRE) { + rz_strbuf_append(sb, "+"); + } else if (m->amode == C55_AM_PREDEC) { + rz_strbuf_append(sb, "-"); + } + if (m->amode == C55_AM_POSTADD || m->amode == C55_AM_POSTSUB || + m->amode == C55_AM_BITREV || m->amode == C55_AM_BITREV_SUB) { + rz_strbuf_append(sb, "("); + } + c55_fmt_reg(sb, a, &m->reg); + switch (m->amode) { + case C55_AM_POSTINC: rz_strbuf_append(sb, "+"); break; + case C55_AM_POSTDEC: rz_strbuf_append(sb, "-"); break; + case C55_AM_INDEXED: rz_strbuf_appendf(sb, "(short(#0x%x))", (unsigned)(m->disp & 0xffff)); break; + case C55_AM_CONST_IDX: + case C55_AM_CONST_IDX_PRE: + // The C54x/C55x syntax renders the long const-index in hex as + // (0xN); the C55x+ algebraic syntax renders it as (#decimal). + if (a->arch == C55_ARCH_C55X) { + rz_strbuf_appendf(sb, "(0x%x)", (unsigned)(m->disp & 0xffff)); + } else { + rz_strbuf_appendf(sb, "(#%u)", (unsigned)(m->disp & 0xffff)); + } + break; + case C55_AM_IDXREG: + rz_strbuf_append(sb, "("); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, ")"); + break; + case C55_AM_IDXSCALE: + // *arN(tM<<#1): the index register scaled left by one. + rz_strbuf_append(sb, "("); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, "<<#1)"); + break; + case C55_AM_XAR15: + // *arN(xar15): coefficient addressing through XAR15. + rz_strbuf_append(sb, "(xar15)"); + break; + case C55_AM_POSTADD: + // the CDP coefficient form *(cdp+t0) has no spaces around the + // operator, unlike the spaced C55x AR post-indexed form. + rz_strbuf_append(sb, m->reg.cls == C55_RC_CDP ? "+" : (a->arch == C55_ARCH_C55X ? " + " : "+")); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, ")"); + break; + case C55_AM_POSTSUB: + rz_strbuf_append(sb, m->reg.cls == C55_RC_CDP ? "-" : (a->arch == C55_ARCH_C55X ? " - " : "-")); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, ")"); + break; + case C55_AM_BITREV: + // reverse-carry post-increment by t0: the index prints with a "b" + // (bit-reverse) suffix -> *(arN + t0b). + rz_strbuf_append(sb, a->arch == C55_ARCH_C55X ? " + " : "+"); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, "b)"); + break; + case C55_AM_BITREV_SUB: + rz_strbuf_append(sb, a->arch == C55_ARCH_C55X ? " - " : "-"); + c55_fmt_reg(sb, a, &m->index); + rz_strbuf_append(sb, "b)"); + break; + default: break; + } + } + if (m->byte_sel) { + rz_strbuf_append(sb, ")"); + } + if (m->uns) { + rz_strbuf_append(sb, ")"); + } +} + +// Render one sub-MAC of a dual "::" instruction: "[r][40] Smem, Cmem, ACx [>> #16]" +// (c55_fmt_mem emits each operand's own uns() wrapper), or "amar Smem" for an amar sub1. +static void c55_fmt_dual_sub(RzStrBuf *sb, const C55ArchDesc *a, const C55Operand *mem, + const C55Operand *cmem, const C55Operand *dst, C55LiftOp lop, bool amar, + bool round, bool m40, bool shift16) { + if (amar) { + rz_strbuf_append(sb, "amar "); + c55_fmt_mem(sb, a, mem); + return; + } + rz_strbuf_append(sb, (lop == C55_LOP_MAC) ? "mac" : (lop == C55_LOP_MAS) ? "mas" : "mpy"); + if (round) { + rz_strbuf_append(sb, "r"); + } + if (m40) { + rz_strbuf_append(sb, "40"); + } + rz_strbuf_append(sb, " "); + c55_fmt_mem(sb, a, mem); + rz_strbuf_append(sb, ", "); + c55_fmt_mem(sb, a, cmem); + rz_strbuf_append(sb, ", "); + c55_fmt_reg(sb, a, &dst->reg); + if (shift16) { + rz_strbuf_append(sb, " >> #16"); + } +} + +char *c55_format(const C55ArchDesc *a, const C55Insn *insn) { + if (!a || !insn) { + return NULL; + } + if (insn->parallel_pair) { + // "||" parallel pair: re-decode and format the two sub-instructions, then + // join them with " || " (the order reversed for the legacy hash 0xF0/0xF1 + // forms). + C55Insn s1, s2; + if (!c55_decode(a, insn->par_bytes + insn->par_off1, insn->size - insn->par_off1, &s1) || + !c55_decode(a, insn->par_bytes + insn->par_off2, insn->size - insn->par_off2, &s2)) { + return NULL; + } + char *a1 = c55_format(a, &s1); + char *a2 = c55_format(a, &s2); + if (!a1 || !a2) { + free(a1); + free(a2); + return NULL; + } + char *res = insn->par_swap ? rz_str_newf("%s || %s", a2, a1) : rz_str_newf("%s || %s", a1, a2); + free(a1); + free(a2); + return res; + } + const char *mn = a->mnemonic ? a->mnemonic(insn->id) : NULL; + RzStrBuf sb; + rz_strbuf_init(&sb); + if (insn->dual) { + // dual "::" MAC: render the two sub-MACs joined by " :: ". Sub1 is the + // ops[0..2] (Xmem, Cmem, ACx) triple (or an amar over ops[0]); sub2 is + // the ops[3..5] (Ymem, Cmem, ACy) triple. + c55_fmt_dual_sub(&sb, a, &insn->ops[0], &insn->ops[1], &insn->ops[2], + insn->lop, insn->amar1, insn->round, insn->m40, insn->shift1); + rz_strbuf_append(&sb, " :: "); + c55_fmt_dual_sub(&sb, a, &insn->ops[3], &insn->ops[4], &insn->ops[5], + insn->lop2, false, insn->round, insn->m40, insn->shift2); + return rz_strbuf_drain_nofree(&sb); + } + if (insn->diff_form) { + // " ACc, ACd, ACa, ACb, [pair(]trn[)]": ops [0]=ACc [1]=ACd [2]=ACa + // [3]=ACb [4]=trn. The max/min-diff (non-d) variants wrap trn in pair(). + const char *mn = a->mnemonic ? a->mnemonic(insn->id) : NULL; + rz_strbuf_append(&sb, mn ? mn : "invalid"); + rz_strbuf_append(&sb, " "); + for (int i = 0; i < 4; i++) { + c55_fmt_reg(&sb, a, &insn->ops[i].reg); + rz_strbuf_append(&sb, ", "); + } + if (insn->diff_pair) { + rz_strbuf_append(&sb, "pair("); + } + c55_fmt_reg(&sb, a, &insn->ops[4].reg); + if (insn->diff_pair) { + rz_strbuf_append(&sb, ")"); + } + return rz_strbuf_drain_nofree(&sb); + } + if (insn->mant_nexp) { + // "mant ACa, ACb :: nexp ACa, ACc": ops [0]=ACa (shared) [1]=ACb [2]=ACc. + rz_strbuf_append(&sb, "mant "); + c55_fmt_reg(&sb, a, &insn->ops[0].reg); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[1].reg); + rz_strbuf_append(&sb, " :: nexp "); + c55_fmt_reg(&sb, a, &insn->ops[0].reg); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[2].reg); + return rz_strbuf_drain_nofree(&sb); + } + if (insn->mac_store) { + // C55x 0x87 parallel dual-MAC with a parallel hi-word store. Three + // shapes, distinguished by operand count / kinds: + // mpym/macm/masm[r] [t3=]Xmem, Tx, ACy :: mov hi(ACx << t2), Ymem + // ops [0]=Xmem [1]=Tx [2]=ACy [3]=Ymem [4]=ACx + // add/sub Xmem << #16, ACx, ACy :: mov hi(ACy << t2), Ymem + // ops [0]=Xmem [1]=ACx [2]=ACy [3]=Ymem + // mov Xmem << #16, ACy :: mov hi(ACx << t2), Ymem + // ops [0]=Xmem [1]=ACy [2]=Ymem [3]=ACx + const char *mn = a->mnemonic ? a->mnemonic(insn->id) : NULL; + rz_strbuf_append(&sb, mn ? mn : "invalid"); + if (insn->round) { + rz_strbuf_append(&sb, "r"); + } + rz_strbuf_append(&sb, " "); + if (insn->n_ops >= 5) { + if (insn->side_load) { + rz_strbuf_append(&sb, "t3="); + } + c55_fmt_mem(&sb, a, &insn->ops[0]); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[1].reg); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[2].reg); + rz_strbuf_append(&sb, " :: mov hi("); + c55_fmt_reg(&sb, a, &insn->ops[4].reg); + rz_strbuf_append(&sb, " << t2), "); + c55_fmt_mem(&sb, a, &insn->ops[3]); + } else if (insn->n_ops == 4 && insn->ops[2].kind == C55_OP_REG) { + c55_fmt_mem(&sb, a, &insn->ops[0]); + rz_strbuf_append(&sb, " << #16, "); + c55_fmt_reg(&sb, a, &insn->ops[1].reg); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[2].reg); + rz_strbuf_append(&sb, " :: mov hi("); + c55_fmt_reg(&sb, a, &insn->ops[2].reg); + rz_strbuf_append(&sb, " << t2), "); + c55_fmt_mem(&sb, a, &insn->ops[3]); + } else { + c55_fmt_mem(&sb, a, &insn->ops[0]); + rz_strbuf_append(&sb, " << #16, "); + c55_fmt_reg(&sb, a, &insn->ops[1].reg); + rz_strbuf_append(&sb, " :: mov hi("); + c55_fmt_reg(&sb, a, &insn->ops[3].reg); + rz_strbuf_append(&sb, " << t2), "); + c55_fmt_mem(&sb, a, &insn->ops[2]); + } + return rz_strbuf_drain_nofree(&sb); + } + if (insn->mac_mov) { + // MAC :: parallel load: "macm/masm[r] [t3=]Xmem, Tx, ACx :: mov Ymem << #16, ACy". + // ops: [0]=Xmem [1]=Tx [2]=ACx [3]=Ymem [4]=ACy. + const char *mn = a->mnemonic ? a->mnemonic(insn->id) : NULL; + rz_strbuf_append(&sb, mn ? mn : "invalid"); + if (insn->round) { + rz_strbuf_append(&sb, "r"); + } + rz_strbuf_append(&sb, " "); + if (insn->side_load) { + rz_strbuf_append(&sb, "t3="); + } + c55_fmt_mem(&sb, a, &insn->ops[0]); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[1].reg); + rz_strbuf_append(&sb, ", "); + c55_fmt_reg(&sb, a, &insn->ops[2].reg); + rz_strbuf_append(&sb, " :: mov "); + c55_fmt_mem(&sb, a, &insn->ops[3]); + rz_strbuf_append(&sb, " << #16, "); + c55_fmt_reg(&sb, a, &insn->ops[4].reg); + return rz_strbuf_drain_nofree(&sb); + } + if (insn->parallel) { + rz_strbuf_append(&sb, "|| "); + } + rz_strbuf_append(&sb, mn ? mn : "invalid"); + if (insn->fract) { + // the fractional-mode variant appends 'f' before any 'r' (mpyk -> mpykf, + // mpykf -> mpykfr) + rz_strbuf_append(&sb, "f"); + } + if (insn->round) { + // the rounding variant appends 'r' to the mnemonic (mpy -> mpyr, ...) + rz_strbuf_append(&sb, "r"); + } + if (insn->m40) { + // the 40-bit (M40) variant appends '40', after any 'r' (mpyr -> mpyr40) + rz_strbuf_append(&sb, "40"); + } + if (insn->uns_all) { + // whole-operation unsigned appends 'u' (mpym -> mpymu); the operands carry + // uns for the lift but are rendered without per-operand uns() wrappers. + rz_strbuf_append(&sb, "u"); + } + for (ut8 i = 0; i < insn->n_ops; i++) { + const C55Operand *op = &insn->ops[i]; + if (op->elide_if_eq_prev && i > 0) { + // the optional ACy defaults to ACx: omit it (and its separator) + // when it names the same register as the preceding operand. + const C55Operand *prev = &insn->ops[i - 1]; + if (op->kind == C55_OP_REG && prev->kind == C55_OP_REG && + op->reg.cls == prev->reg.cls && op->reg.num == prev->reg.num && + op->reg.sub == prev->reg.sub) { + continue; + } + } + rz_strbuf_append(&sb, i == 0 ? " " : (op->shl_join ? " << " : (op->qual_join ? " || " : ", "))); + if (i == 0 && insn->side_load) { + // memory-MAC side-load: the first (Smem) operand is also written + // into T3, rendered as "t3=" (C55x) / "t3 = " (C55x+) before it. + rz_strbuf_append(&sb, a->arch == C55_ARCH_C55X ? "t3=" : "t3 = "); + } + if (op->raw) { + // a verbatim operand string (e.g. the un-decoded "Baddr" of btstp). + rz_strbuf_append(&sb, op->raw); + continue; + } + switch (op->kind) { + case C55_OP_REG: { + int nclose = 0; + if (op->wrap_uns) { + rz_strbuf_append(&sb, "uns("); + nclose++; + } + if (op->wrap_round) { + rz_strbuf_append(&sb, "rnd("); + nclose++; + } + if (op->wrap_half == 1) { + rz_strbuf_append(&sb, "hi("); + nclose++; + } else if (op->wrap_half == 2) { + rz_strbuf_append(&sb, "lo("); + nclose++; + } + if (op->dbl) { + rz_strbuf_append(&sb, "dbl("); + } + c55_fmt_reg(&sb, a, &op->reg); + if (op->dbl) { + rz_strbuf_append(&sb, ")"); + } + if (op->sh_by_reg) { + rz_strbuf_append(&sb, " << "); + c55_fmt_reg(&sb, a, &op->index); + } else if (op->sh_left && op->shamt) { + rz_strbuf_appendf(&sb, " << #%d", op->shamt); + } + while (nclose-- > 0) { + rz_strbuf_append(&sb, ")"); + } + break; + } + case C55_OP_IMM: { + // The C55x (non-plus) disassembler prints immediates as uppercase + // hex without the '#' prefix that the C55x+ / C54x syntax uses. + bool c55x = (a->arch == C55_ARCH_C55X); + const char *ip = c55x ? "" : "#"; + if (op->is_bit) { + // A bit number (btst/bclr/bset/... @#k): the '@#' prefix. + rz_strbuf_appendf(&sb, "@#0x%" PFMT64x, op->imm); + } else if (op->neg_imm) { + // A negated-magnitude immediate (mov -#k, dst): the minus sign + // precedes the prefix (C55x+ "-#0x1", C55x "-0x1"), always printed + // even for -0. + ut64 mag = (ut64)(-(st64)op->imm); + if (c55x) { + rz_strbuf_appendf(&sb, "-0x%" PFMT64X, mag); + } else { + rz_strbuf_appendf(&sb, "-#0x%" PFMT64x, mag); + } + } else if (op->hash_dec) { + // A fixed literal shift count rendered as a signed decimal + // with the '#' prefix (the sftl dst, #1 / #-1 forms). + rz_strbuf_appendf(&sb, "#%" PFMT64d, (st64)op->imm); + } else if (op->addr) { + rz_strbuf_appendf(&sb, c55x ? "%s0x%06" PFMT64X : "%s0x%06" PFMT64x, ip, op->imm); + } else if (op->imm_signed && (st64)op->imm < 0) { + rz_strbuf_appendf(&sb, c55x ? "%s-0x%" PFMT64X : "%s-0x%" PFMT64x, ip, (ut64)(-(st64)op->imm)); + } else if (c55x) { + // C55x immediates are printed zero-padded to their field + // width (one hex digit per 4 bits): a k4 stays a single + // digit, a k8 prints as two, etc. + int digits = (op->width + 3) / 4; + rz_strbuf_appendf(&sb, "%s0x%0*" PFMT64X, ip, digits < 1 ? 1 : digits, op->imm); + } else { + rz_strbuf_appendf(&sb, "%s0x%" PFMT64x, ip, op->imm); + } + if (op->sh_left && op->shamt_hex) { + // A variable shift count rendered as hex; always emitted, even + // when zero. C55x prints it without a '#' and in uppercase (the + // opcode-0x70..0x75 forms); C55x+ uses the "#0x%x" algebraic + // form (the opcode-0xc2 forms). + if (a->arch == C55_ARCH_C55X) { + rz_strbuf_appendf(&sb, " << 0x%X", (unsigned)(ut8)op->shamt); + } else { + rz_strbuf_appendf(&sb, " << #0x%x", (unsigned)(ut8)op->shamt); + } + } else if (op->sh_left && op->shamt) { + // A fixed shift rendered as a decimal "#N" (the opcode-0x7a / + // 0xc0 "#k16 << #16" immediate-ALU forms). + rz_strbuf_appendf(&sb, " << #%d", op->shamt); + } + break; + } + case C55_OP_MEM: + if (op->mem_round) { + // rounding store/load: the whole memory operand (including + // any "<< Tx" shift) is wrapped in rnd(...). + rz_strbuf_append(&sb, "rnd("); + } + if (op->dbl) { + rz_strbuf_append(&sb, "dbl("); + } + if (op->dual_wrap) { + rz_strbuf_append(&sb, "dual("); + } + if (insn->uns_all && op->uns) { + // the 'u' mnemonic suffix covers the unsignedness; render the + // memory operand itself without its own uns() wrapper. + C55Operand bare = *op; + bare.uns = false; + c55_fmt_mem(&sb, a, &bare); + } else { + c55_fmt_mem(&sb, a, op); + } + if (op->dbl) { + rz_strbuf_append(&sb, ")"); + } + if (op->dual_wrap) { + rz_strbuf_append(&sb, ")"); + } + if (op->sh_left && op->shamt_hex) { + // A fixed shift rendered as "#0x%x", always emitted even when + // zero (the opcode-0xb7 shifted-load forms). + rz_strbuf_appendf(&sb, " << #0x%x", (unsigned)(ut8)op->shamt); + } else if (op->sh_left && op->shamt) { + rz_strbuf_appendf(&sb, " << #%d", op->shamt); + } + if (op->sh_mem_reg_set) { + rz_strbuf_append(&sb, " << "); + c55_fmt_reg(&sb, a, &op->sh_mem_reg); + } + if (op->mem_round) { + rz_strbuf_append(&sb, ")"); + } + break; + case C55_OP_COND: { + static const char *const rel[] = { "==", "!=", "<", "<=", ">", ">=" }; + // status-bit flag conditions (C55x condition field, ids 0..31) + static const char *const cond_flags[32] = { + "overflow(ac0)", "overflow(ac1)", "overflow(ac2)", "overflow(ac3)", + "tc1", "tc2", "carry", "overflow(govf)", + "tc1 & tc2", "tc1 & !tc2", "!tc1 & tc2", "!tc1 & !tc2", + "word_mode", "byte_mode", NULL, NULL, + "!overflow(ac0)", "!overflow(ac1)", "!overflow(ac2)", "!overflow(ac3)", + "!tc1", "!tc2", "!carry", "!overflow(govf)", + "tc1 | tc2", "tc1 | !tc2", "!tc1 | tc2", "!tc1 | !tc2", + "tc1 ^ tc2", "tc1 ^ !tc2", "!tc1 ^ tc2", "!tc1 ^ !tc2" + }; + if (op->cond_is_flag) { + const char *f = (op->cond_flag < 32) ? cond_flags[op->cond_flag] : NULL; + rz_strbuf_append(&sb, f ? f : ""); + break; + } + if (op->cmp_mem) { + c55_fmt_mem(&sb, a, op); + } else { + c55_fmt_reg(&sb, a, &op->reg); + } + rz_strbuf_appendf(&sb, " %s ", rel[op->relop % 6]); + if (op->cmp_to_reg) { + c55_fmt_reg(&sb, a, &op->index); + } else if (op->cmp_imm) { + if (a->arch == C55_ARCH_C55X) { + int digits = (op->width + 3) / 4; + rz_strbuf_appendf(&sb, "0x%0*" PFMT64X, digits < 1 ? 1 : digits, op->imm); + } else { + rz_strbuf_appendf(&sb, "#0x%" PFMT64x, op->imm); + } + } else { + // C55x renders the zero comparison as a bare 0; C55x+ keeps the + // '#' immediate prefix. + rz_strbuf_append(&sb, a->arch == C55_ARCH_C55X ? "0" : "#0"); + } + break; + } + default: break; + } + if (i == 2 && insn->shift16) { + // macm ... ACx >> #16[, ACy]: the accumulator term (always the + // third operand -- the source AC, or the destination in the + // two-operand form) is rendered with a ">> #16" suffix. + rz_strbuf_append(&sb, " >> #16"); + } + } + return rz_strbuf_drain_nofree(&sb); +} + +#include diff --git a/librz/arch/isa/tms320/c55_ir.h b/librz/arch/isa/tms320/c55_ir.h new file mode 100644 index 0000000000..f84925230c --- /dev/null +++ b/librz/arch/isa/tms320/c55_ir.h @@ -0,0 +1,416 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_TMS320_C55_IR_H +#define RZ_TMS320_C55_IR_H + +#include +#include // RzAnalysisOp, RzAnalysisOpType, RzILOpEffect, RzILOpPure + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * Shared decode IR for the TMS320 fixed-point DSP family (C54x / C55x / C55x+). + * + * Decoders run once and produce a \ref C55Insn; three pure consumers read it and + * never re-parse text: c55_format() (asm string), c55_fill_analysis() + * (RzAnalysisOp) and c55_lift() (RzIL). Everything generic (IR, decode engine, + * operand extractors, consumers, RzIL primitives) is shared; everything + * arch-specific (encoding, register set, mnemonic/op-type/lift tables, exotic + * addressing) sits behind one \ref C55ArchDesc. C55x and C55x+ differ only in + * encoding and share id/register/mnemonic tables; C54x supplies its own + * 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. + */ + +/** Family member (so shared code can special-case where unavoidable). */ +typedef enum { + C55_ARCH_C54X = 0, ///< TMS320C54x + C55_ARCH_C55X, ///< TMS320C55x + C55_ARCH_C55XPLUS, ///< TMS320C55x+ +} C55Arch; + +/** Register class; family-wide superset, each arch uses only its subset. */ +typedef enum { + C55_RC_NONE = 0, ///< no register + C55_RC_AC, ///< accumulators: C54x A/B (0..1), C55x AC0..31 (40-bit) + C55_RC_AR, ///< 16-bit auxiliary regs ARn (C54x AR0..7; C55x low 16 of XARn) + C55_RC_XAR, ///< 23-bit extended aux regs XARn (C55x/C55x+) + C55_RC_T, ///< temp regs: C54x T (0); C55x T0..3 + C55_RC_TRN, ///< transition register(s) (Viterbi) + C55_RC_CDP, ///< coefficient data pointer (CDP / XCDP) + C55_RC_SP, ///< stack pointer + C55_RC_DP, ///< data-page pointer (C54x DP; C55x DPH:DP) + C55_RC_BK, ///< circular-buffer size reg(s) (BK; BK03/BK47/BKC) + C55_RC_ARP, ///< aux-reg pointer (C54x indirect-mode selector) + C55_RC_ST, ///< status regs (C54x ST0/ST1/PMST; C55x ST0_55..ST3_55) + C55_RC_SPECIAL, ///< anything else addressed by name (PC, RPTC, BRC, ...) + C55_RC_TC, ///< test-control status bits TC1/TC2 (the btst destination) +} C55RegClass; + +/** Accumulator sub-field selector. */ +typedef enum { + C55_SUB_NONE = 0, ///< whole register + C55_SUB_LO, ///< .l, bits 15:0 + C55_SUB_HI, ///< .h, bits 31:16 + C55_SUB_GUARD, ///< .g, bits 39:32 (accumulator guard) +} C55SubReg; + +/** Register reference, resolved to name/IL-var/width by C55ArchDesc::reg_info(). */ +typedef struct { + C55RegClass cls; ///< register class + ut8 num; ///< index within the class (0 for singletons) + C55SubReg sub; ///< accumulator sub-field selector +} C55Reg; + +/** Per-arch register descriptor returned by C55ArchDesc::reg_info(). */ +typedef struct { + const char *name; ///< display name for the formatter ("ac0", "a", "xar15") + const char *il_var; ///< RzIL global-variable name for the lifter (often == name) + ut8 width; ///< IL bit-width (AR/T 16, XAR 23, AC 40, status 16, ...) +} C55RegInfo; + +/** + * Data-memory addressing mode (family-wide superset). + * + * EA-boundary contract: the GENERIC modes (above the divider) have an effective + * address of base +/- {const | index | scaled index} in linear space and are + * computed by c55_generic_ea() from \ref C55MemModel alone, with no arch state. + * The ARCH-SPECIFIC modes need paging / BK / ARP / reverse-carry state and are + * delegated to C55ArchDesc::ea(). Keeping this divider fixed is what lets a later + * C54x (DP-direct / MMR / circular) attach without touching shipped C55 code. + */ +typedef enum { + C55_AM_NONE = 0, ///< not a memory operand + // generic: c55_generic_ea() + C55_AM_INDIRECT, ///< *arN + C55_AM_POSTINC, ///< *arN+ + C55_AM_POSTDEC, ///< *arN- + C55_AM_PREINC, ///< *+arN + C55_AM_PREDEC, ///< *-arN + C55_AM_INDEXED, ///< *arN(short(#K)) / *sp(#K) + C55_AM_IDXREG, ///< *arN(tM) + C55_AM_POSTADD, ///< *(arN+tM) + C55_AM_POSTSUB, ///< *(arN-tM) + C55_AM_IDXSCALE, ///< *arN(tM<<#1) + C55_AM_ABSOLUTE, ///< *(#k) absolute address + C55_AM_CONST_IDX, ///< *arN(#K16) long const-index (2-byte extension, ARn unmodified) + C55_AM_CONST_IDX_PRE, ///< *+arN(#K16) long const-index with pre-modify (2-byte extension, ARn += K16) + C55_AM_ABS16, ///< abs16(#k16) data-page absolute: DPH:k16 (2-byte extension, k16 unsigned) + // arch-specific: C55ArchDesc::ea() + C55_AM_BITREV, ///< *(arN+t0b) reverse-carry (C54x *arN+0B) + C55_AM_XAR15, ///< *arN(xar15) + C55_AM_DIRECT, ///< C54x DP/SP-relative direct (@addr / addr) + C55_AM_MMR, ///< C54x memory-mapped register + C55_AM_CIRCULAR, ///< explicit circular via BK/ARP + C55_AM_BITREV_SUB, ///< *(arN-t0b) reverse-carry (the decrement bit-reverse form) +} C55AddrMode; + +/** Parameters c55_generic_ea() needs; set per session, not baked in. */ +typedef struct { + ut8 addr_unit_log2; ///< 0 = byte-addressed; 1 = word-pointer (EA = reg<<1, C55x) + ut8 ptr_width; ///< pointer-register width in bits (C54x AR 16, C55x XAR 23) + bool big_endian; ///< image endianness + const char *page_reg; ///< il_var of the data-page register for abs16 (DPH); NULL = page 0 +} C55MemModel; + +/** Operand kind; selects which of the fields below are meaningful. */ +typedef enum { + C55_OP_NONE = 0, ///< unused slot + C55_OP_REG, ///< register (optional sub-field, optional shift wrapper) + C55_OP_IMM, ///< immediate (width + signedness, optional shift wrapper) + C55_OP_MEM, ///< data-memory operand (base + amode + index + disp + access) + C55_OP_COND, ///< condition: reg 0|imm|reg (bcc/xcc/cmp/btst) + C55_OP_INVALID, ///< operand the decoder cannot represent yet; forces legacy fallback +} C55OpKind; + +/** Relational operator for \ref C55_OP_COND. */ +typedef enum { + C55_REL_EQ, ///< == + C55_REL_NE, ///< != + C55_REL_LT, ///< < + C55_REL_LE, ///< <= + C55_REL_GT, ///< > + C55_REL_GE, ///< >= +} C55Relop; + +/** A decoded operand. */ +typedef struct { + C55OpKind kind; ///< discriminates which fields below apply + const char *raw; ///< if set, the operand renders as this verbatim string (the un-decoded "Baddr" bit-address placeholder of btstp); other fields ignored + + C55Reg reg; ///< REG / MEM base / COND subject register + ut64 imm; ///< IMM value or COND compare-constant + bool imm_signed; ///< IMM / disp is signed + ut8 width; ///< IL width of the REG / IMM + + C55AddrMode amode; ///< MEM: addressing mode + C55Reg index; ///< MEM: index register (Tm) for indexed / post-modify modes + st32 disp; ///< MEM: signed displacement (INDEXED / DIRECT) + ut64 abs_addr; ///< MEM: address for ABSOLUTE / MMR + ut8 access; ///< MEM: access width in bits (8/16/32 = byte/word/dbl) + bool uns; ///< MEM: uns() wrapper -> zero-extend on load + ut8 byte_sel; ///< MEM: 0 none, 1 high_byte(), 2 low_byte(), 3 byte() wrapper + + int8_t shamt; ///< REG/IMM: shift amount (when not \ref sh_by_reg) + bool sh_left; ///< REG/IMM: shift left (else right) + bool sh_by_reg; ///< REG/IMM: shift amount is \ref index register, not \ref shamt + bool shamt_hex; ///< IMM: render the shift count as "<< 0x%X" (always, even 0) rather than "<< #%d" + bool neg_imm; ///< IMM: a negated-magnitude immediate, always rendered "-0x%X" (even -0); see opcode 0x3e + bool shl_join; ///< IMM: render joined to the previous operand by " << " (the "ACx << #SHIFTW" syntax) rather than ", " + bool qual_join; ///< render joined to the previous operand by " || " (the b/call ACx "|| local()/|| far()" parallel qualifiers) rather than ", " + C55Reg sh_mem_reg; ///< MEM: a register shift count Tx rendered as " << Tx" after the operand (the "Smem << Tx" forms), distinct from \ref index + bool sh_mem_reg_set; ///< MEM: \ref sh_mem_reg is present + bool mem_round; ///< MEM: render the whole memory operand wrapped in rnd(...) (the "mov rnd(Smem << Tx), ACx" rounding forms) + ut8 wrap_half; ///< REG: 1 -> hi(...), 2 -> lo(...) wrapper around the (shifted) accumulator (the 0xb4 "mov hi(ACx << Tx), Smem" store forms) + bool wrap_round; ///< REG: render the (shifted) accumulator wrapped in rnd(...) + bool wrap_uns; ///< REG: render the (shifted) accumulator wrapped in uns(...) + bool dbl; ///< REG: rendered dbl(...) (full accumulator / pointer push-pop) + bool dual_wrap; ///< MEM: render the memory operand wrapped in dual(...) (the subadd dual-access form) + bool addr; ///< IMM: rendered as a 24-bit address (#0x%06x), e.g. branch/call fields + bool is_bit; ///< IMM: a bit number rendered with the '@#' prefix (the btst/bclr/bset/bnot/btstp bit operand) + bool hash_dec; ///< IMM: rendered as a signed decimal with a '#' prefix (#1 / #-1), e.g. the sftl fixed-shift literal + bool reltarget; ///< IMM: a pc-relative branch offset used for target computation but rendered as a plain immediate (not a 24-bit address) + bool reltarget_unsigned; ///< IMM: a \ref reltarget whose offset is non-negative, so it is not sign-extended before being added to pc + bool abs_target; ///< IMM: an absolute branch target (the operand value is the target, not a pc-relative displacement) + + C55Relop relop; ///< COND: relational operator + bool cmp_to_reg; ///< COND: compare to \ref index register rather than \ref imm + bool cond_is_flag; ///< COND: a status-bit flag expression rather than reg 0 + ut8 cond_flag; ///< COND: status-flag id (when \ref cond_is_flag) + bool cmp_imm; ///< COND: compare to an explicit immediate (#0x..) vs literal #0 + bool cmp_mem; ///< COND: the left-hand side is a memory operand (cmp Smem #k) + bool elide_if_eq_prev; ///< REG: omit this operand (and its separator) when it equals the immediately preceding operand (mpyk/mack ACy defaulting to ACx) +} C55Operand; + +/// Max operands: amar carries 3 memory operands (+dst); dmaxdiff/maxdiff reach 5. +#define C55_MAX_OPS 6 + +/// Sentinel abs_addr value meaning "the absolute address is a trailing extension +/// to be filled by the decoder" (out of the 24-bit program-address range). +#define C55_ABS_EXT 0xffffffffffffffffULL + +/** + * Lift operation for instructions whose RzAnalysis op type does not determine + * their RzIL semantics. For example neg maps to RZ_ANALYSIS_OP_TYPE_SUB (there + * is no dedicated NEG op type), so the lifter cannot tell it apart from a real + * sub by the op type alone; the decode table sets this field to select the + * lift explicitly. C55_LOP_NONE (the default) means the lifter dispatches on + * the op type as usual. + */ +typedef enum { + C55_LOP_NONE = 0, + C55_LOP_NEG, ///< dst = -src + C55_LOP_MAX, ///< dst = (src > dst) ? src : dst + C55_LOP_MIN, ///< dst = (src < dst) ? src : dst + C55_LOP_ABS, ///< dst = (src < 0) ? -src : src + C55_LOP_MAC, ///< dst += product (multiply-accumulate, on the MUL path) + C55_LOP_MAS, ///< dst -= product (multiply-subtract, on the MUL path) + C55_LOP_FIRSADD, ///< FIR symmetric: ACy += ACx.h*Cmem :: ACx = (Xmem<<16)+(Ymem<<16) + C55_LOP_FIRSSUB, ///< FIR antisymmetric: ACy += ACx.h*Cmem :: ACx = (Xmem<<16)-(Ymem<<16) + C55_LOP_SQDST, ///< square distance: ACy += ACx.h*ACx.h :: ACx = (Xmem<<16)-(Ymem<<16) + C55_LOP_ABDST, ///< absolute distance: ACy += |ACx.h| :: ACx = (Xmem<<16)-(Ymem<<16) + C55_LOP_LMS, ///< least mean square: ACy += Xmem*Ymem :: ACx = round(ACx + (Xmem<<16)) + C55_LOP_ROUND, ///< dst = round(src) -- (src + 0x8000) with the low word cleared + C55_LOP_SAT, ///< dst = saturate(src) to the 32-bit signed range + C55_LOP_ADDV, ///< dst = dst + |src(32-16)| (addition with absolute value) + C55_LOP_SFTL, ///< dst = sftl(src, Tx): logical shift, right by -Tx when Tx<0 else left by Tx + C55_LOP_SFTS, ///< dst = sfts(src, Tx): arithmetic right shift (sign-fill) / logical left + C55_LOP_ANDSHL, ///< dst = dst & (src << #SHIFTW) + C55_LOP_ORSHL, ///< dst = dst | (src << #SHIFTW) + C55_LOP_XORSHL, ///< dst = dst ^ (src << #SHIFTW) + C55_LOP_ADDSHL, ///< dst = dst + (src << #SHIFTW) + C55_LOP_SUBSHL, ///< dst = dst - (src << #SHIFTW) + C55_LOP_MOVSHL, ///< dst = (src << #SHIFTW): load a shifted immediate (mov #k16 << #sh) + C55_LOP_ANDMEM, ///< ACy = ACx & sx(Smem) + C55_LOP_ORMEM, ///< ACy = ACx | sx(Smem) + C55_LOP_XORMEM, ///< ACy = ACx ^ sx(Smem) + C55_LOP_BITCLR, ///< STx = STx & ~(1 << #k4): clear a status-register bit (bclr) + C55_LOP_BITSET, ///< STx = STx | (1 << #k4): set a status-register bit (bset) + C55_LOP_STBITCLR, ///< st0_55 = ite(0, st0_55 | (1< DST): register compare writing a status TC bit + C55_LOP_CMPAND, ///< TCz = (SRC DST) && TCx + C55_LOP_CMPOR, ///< TCz = (SRC DST) || TCx + C55_LOP_AREG_MOV, ///< dst = cast(dst, src): register move with width conversion (amov ACx,ACy) + C55_LOP_AREG_SUB, ///< dst = dst - cast(dst, src): register address subtract (asub ACx,ACy) + C55_LOP_AREG_ADD, ///< dst = dst + cast(dst, src): register/immediate address add (aadd #k,reg) + C55_LOP_AREG_AND, ///< dst = dst & cast(dst, src): A-unit register and (and src, dst) + C55_LOP_AREG_OR, ///< dst = dst | cast(dst, src): A-unit register or (or src, dst) + C55_LOP_AREG_XOR, ///< dst = dst ^ cast(dst, src): A-unit register xor (xor src, dst) + C55_LOP_NOP, ///< no data effect: lift to nop (repeat/loop control etc.) + C55_LOP_MOVMEM, ///< storew(Ymem, loadw(Xmem)): dual-memory move (Xmem=src, Ymem=dst); dbl operands move a 32-bit word + C55_LOP_DUALADD, ///< ACx = sx(Xmem) + sx(Ymem): dual-memory add into an accumulator + C55_LOP_DUALSUB, ///< ACx = sx(Xmem) - sx(Ymem): dual-memory subtract into an accumulator + C55_LOP_OPAQUE, ///< decode + analyse only; data effect not modelled (no IL) + C55_LOP_ROL, ///< ACy = rotate-left(ACx) by one through a status bit (carry/tc2) + C55_LOP_ROR, ///< ACy = rotate-right(ACx) by one through a status bit (carry/tc2) + C55_LOP_MPYK, ///< ACy = #k * ACx: multiply accumulator low word by a signed constant (mpyk/mpykr) + C55_LOP_MACK, ///< ACy = ACx + #k * Tx: multiply-accumulate a signed constant by a Tx coefficient (mack/mackr) + C55_LOP_ADDK, ///< dst = ACx + zero-extend(#k16): immediate add (opcode 0x7b) + C55_LOP_SUBK, ///< dst = ACx - zero-extend(#k16): immediate subtract (opcode 0x7c) + C55_LOP_ANDK, ///< dst = ACx & zero-extend(#k16): immediate and (opcode 0x7d) + C55_LOP_ORK, ///< dst = ACx | zero-extend(#k16): immediate or (opcode 0x7e) + C55_LOP_XORK, ///< dst = ACx ^ zero-extend(#k16): immediate xor (opcode 0x7f) +} C55LiftOp; + +/** A fully decoded instruction: the single hand-off from decode to the consumers. */ +typedef struct { + C55Arch arch; ///< producing family member + ut16 id; ///< arch-local instruction id (TMS320C5{4,5}_INS_*); opaque to shared code + C55LiftOp lop; ///< explicit lift op (C55_LOP_NONE => dispatch on the op type) + ut8 size; ///< instruction length in bytes + + C55Operand ops[C55_MAX_OPS]; ///< operands, in assembly order + ut8 n_ops; ///< number of valid entries in \ref ops + + bool round; ///< rounding variant (r) + bool fract; ///< fractional-mode variant (f), rendered before the 'r' suffix (mpyk -> mpykf -> mpykfr) + bool m40; ///< M40 / 40-bit mode (40) + bool saturate; ///< saturating variant ((saturate / sat) + bool side_load; ///< memory-MAC side-load: also load Smem into T3 ([T3 = ]Smem) + bool square; ///< squaring multiply: the (memory) multiplicand is multiplied by itself + bool parallel; ///< has a "||" parallel companion slot + bool dual; ///< "::" paired form (dual MAC: two parallel sub-ops sharing Cmem) + C55LiftOp lop2; ///< dual: lift op of the second sub-MAC (sub1 uses \ref lop) + bool amar1; ///< dual: sub1 is an "amar" address-modify (no product) + bool shift1; ///< dual: sub1 accumulates with the accumulator shifted right 16 (">> #16") + bool shift2; ///< dual: sub2 accumulates with the accumulator shifted right 16 (">> #16") + bool shift16; ///< single MAC: the accumulator term is shifted right 16 before the product (macm ... ACx >> #16) + bool mac_mov; ///< MAC :: parallel load: a Tx-coefficient MAC (ops[0..2]) with a parallel "mov Ymem << #16, ACy" (ops[3..4]) + bool mant_nexp; ///< "mant ACa, ACb :: nexp ACa, ACc": ops [0]=ACa [1]=ACb [2]=ACc, ACa shared by both halves + bool mac_store; ///< C55x 0x87 parallel dual-MAC store: "MN Xmem, Tx, ACy :: mov hi(ACx << t2), Ymem" and the add/sub/mov << #16 variants + bool diff_pair; ///< max/min-diff form: ops [0..3]=ACc,ACd,ACa,ACb [4]=trn; the trn is wrapped in pair() (the non-d variants) + bool diff_form; ///< max/min/dmax/dmin-diff "ACc, ACd, ACa, ACb, [pair(]trn[)]" rendering + bool uns_all; ///< whole-operation unsigned (mnemonic 'u' suffix, e.g. mpymu): operands multiplied unsigned, no per-operand uns() wrapper rendered + bool both; ///< register-pair stack op (popboth / pshboth): pushed/popped as a pair, left unlifted by the shared push/pop lifter + bool xcc_guard; ///< xccpart conditional-execute guard: in a "||" parallel pair the *other* sub-instruction executes only when this one's condition holds + bool quad; ///< swap4: exchange four consecutive register pairs (the XCHG lifter emits four XOR-swaps) + + bool has_branch; ///< \ref branch_target is valid + ut64 branch_target; ///< absolute target for B/BC/CALL/RPTB/... (precomputed) + st32 stack_delta; ///< net SP change for psh/pop/aadd sp / frame ops + bool cond_exec; ///< carried a conditional-execution prefix (C55x+ 0x2e/0x2f if(!TC1)/if(TC1)); rendered/lifted transparently, as the legacy decoder does + bool parallel_pair; ///< a "||" parallel pair of two independent sub-instructions (C55x+ 0x3N prefix): the two sub-instructions are re-decoded on demand from \ref par_bytes + ut8 par_bytes[16]; ///< raw instruction bytes (parallel pair only), so the two sub-instructions can be re-decoded by the format / lift / analysis consumers + ut8 par_off1; ///< byte offset of the first sub-instruction within \ref par_bytes + ut8 par_off2; ///< byte offset of the second sub-instruction within \ref par_bytes + bool par_swap; ///< render / sequence the second sub-instruction before the first (the legacy hash 0xF0/0xF1 ordering) +} C55Insn; + +struct c55_arch_desc_t; +struct c55_op_desc_t; + +/** + * Shared operand extractor: fills one \ref C55Operand from the instruction bits. + * \param a arch descriptor (for reg_info / memory model) + * \param bits whole instruction, packed MSB-first into a word (up to 8 bytes) + * \param d the operand slot being decoded (its \ref C55OpDesc.lo / .width / .param) + * \param out operand to fill + */ +typedef void (*C55Extract)(const struct c55_arch_desc_t *a, ut64 bits, const struct c55_op_desc_t *d, C55Operand *out); + +/** One operand slot in a \ref C55InsnDef: which bits feed which extractor. */ +typedef struct c55_op_desc_t { + ut8 lo; ///< bitfield start (LSB index within the packed instruction word) + ut8 width; ///< bitfield width + C55Extract fn; ///< shared extractor to run + ut8 param; ///< class selector / scale / width hint passed to \ref fn +} C55OpDesc; + +/** One row of a per-arch instruction table (data only; the engine is shared). */ +typedef struct { + ut32 mask; ///< opcode discriminator mask over the leading byte(s) + ut32 match; ///< required value under \ref mask + ut16 id; ///< arch-local instruction id + C55LiftOp lop; ///< explicit lift op copied to \ref C55Insn.lop (0 => dispatch on op type) + ut8 len; ///< fixed length in bytes, or 0 => C55ArchDesc::insn_len() + C55OpDesc ops[C55_MAX_OPS]; ///< operand slots + ut32 mods; ///< variant-flag bit positions, packed as 5-bit (position+1, 0=absent) fields: rounding in bits 0-4, side-load (T3=) in 5-9, M40 in 10-14, fractional (f) in 15-19 + ut8 alt_bit; ///< sub-discriminator bit position (in the full packed bits, LSB-counted) + 1, or 0 if absent: when that bit is set the decode uses \ref alt_id / \ref alt_lop instead of \ref id / \ref lop (for variants whose selector lies beyond the 4-byte match head) + ut16 alt_id; ///< instruction id when \ref alt_bit fires + C55LiftOp alt_lop; ///< lift op when \ref alt_bit fires + bool no_parallel; ///< suppress the parallel-bit derivation (bit 0 of the leading byte is an operand/opcode field here, not the parallel marker) + bool square; ///< squaring multiply: the (memory) multiplicand is multiplied by itself (sqrm / sqam / sqsm) + bool dual; ///< dual "::" MAC: operands filled by C55ArchDesc::fill_dual, not the ops[] extractors + C55LiftOp lop2; ///< dual: lift op of the second sub-MAC (sub1 uses \ref lop) + bool amar1; ///< dual: sub1 is an "amar" address-modify (no product, no Cmem/dst) + bool shift1; ///< dual: sub1 accumulates with the accumulator shifted right 16 (">> #16") + bool shift2; ///< dual: sub2 accumulates with the accumulator shifted right 16 (">> #16") + bool shift16; ///< single MAC: the accumulator term is shifted right 16 before the product (macm ... ACx >> #16) + bool mac_mov; ///< MAC :: parallel load form (macm/masm Xmem, Tx, ACx :: mov Ymem << #16, ACy) + bool mant_nexp; ///< "mant ACa, ACb :: nexp ACa, ACc" dual form (copied to \ref C55Insn.mant_nexp) + bool mac_store; ///< C55x 0x87 parallel dual-MAC store form (copied to \ref C55Insn.mac_store) + bool diff_pair; ///< max/min-diff: the trn operand is wrapped in pair() (copied to \ref C55Insn.diff_pair) + bool diff_form; ///< max/min/dmax/dmin-diff rendering (copied to \ref C55Insn.diff_form) + bool uns_all; ///< whole-operation unsigned (mpymu): 'u' mnemonic suffix; multiplicand operands carry uns + bool side_load; ///< memory-MAC side-load: also load Smem into T3 ([T3 = ]Smem), copied to \ref C55Insn.side_load + bool both; ///< register-pair stack op (popboth / pshboth): copied to \ref C55Insn.both; the shared push/pop lifter leaves it unlifted + bool xcc_guard; ///< xccpart parallel-slot guard form (copied to \ref C55Insn.xcc_guard) + bool quad; ///< swap4: copied to \ref C55Insn.quad; the XCHG lifter exchanges four consecutive register pairs +} C55InsnDef; + +/** + * 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. + */ +typedef struct c55_arch_desc_t { + C55Arch arch; ///< family member + const char *cpu_name; ///< "c54x" / "c55x" / "c55x+" + + const C55InsnDef *table; ///< instruction table (NULL if decoded externally) + size_t table_len; ///< number of rows in \ref table + ut8 (*insn_len)(const ut8 *buf, int len); ///< variable-length decode helper + + const C55RegInfo *(*reg_info)(C55RegClass cls, ut8 num, C55SubReg sub); ///< register resolver + + const char *(*mnemonic)(ut16 id); ///< id -> mnemonic; NULL if disasm is external + ut32 (*op_type)(ut16 id); ///< id -> RZ_ANALYSIS_OP_TYPE_* (the type of RzAnalysisOp::type) + RzILOpEffect *(*lift)(const C55Insn *insn, ut64 pc); ///< id -> RzIL (per-arch dispatch) + + C55MemModel mem; ///< memory model for c55_generic_ea() + RzILOpPure *(*ea)(const struct c55_arch_desc_t *a, const C55Operand *m); ///< arch-specific EA; NULL => generic only + /// Dual "::" MAC operand filler: for a row with C55InsnDef::dual set, fills the + /// canonical 6-slot layout (Xmem, Cmem-x, ACx, Ymem, Cmem-y, ACy) plus the dual + /// sub-op metadata on \ref C55Insn, returning false (-> legacy) on an + /// unrepresentable mode. NULL => the arch has no dual forms. + bool (*fill_dual)(const struct c55_arch_desc_t *a, ut64 bits, const C55InsnDef *def, C55Insn *out); + bool cond_exec_prefix; ///< the arch has the 0x2e/0x2f conditional-execution prefix (C55x+); a leading 0x2e/0x2f is consumed and the following instruction decoded transparently + bool parallel_prefix; ///< the arch has the 0x3N parallel-pair prefix (C55x+): a leading byte in 0x30-0x3F introduces two independent sub-instructions joined by " || ", the low nibble giving the total byte length +} C55ArchDesc; + +// shared API: one decode, three pure consumers +bool c55_decode(const C55ArchDesc *a, const ut8 *buf, int len, C55Insn *out); +char *c55_format(const C55ArchDesc *a, const C55Insn *insn); // caller frees +void c55_fill_analysis(const C55ArchDesc *a, const C55Insn *insn, RzAnalysisOp *op); +RzILOpEffect *c55_lift(const C55ArchDesc *a, const C55Insn *insn, ut64 pc); + +// shared operand extractors referenced by the per-arch tables +void c55_x_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out); +void c55_x_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out); +void c55_x_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out); +void c55_x_cond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out); + +// shared RzIL primitives used by each arch's ::lift; c55_generic_ea() serves only +// the generic addressing modes (the EA-boundary contract on C55AddrMode). +RzILOpPure *c55_generic_ea(const C55ArchDesc *a, const C55Operand *m); +RzILOpPure *c55_read(const C55ArchDesc *a, const C55Operand *op); +RzILOpEffect *c55_write(const C55ArchDesc *a, const C55Operand *dst, RzILOpPure *val); +RzILOpEffect *c55_post_effect(const C55ArchDesc *a, const C55Operand *m); + +#ifdef __cplusplus +} +#endif +#endif /* RZ_TMS320_C55_IR_H */ diff --git a/librz/arch/isa/tms320/c55x/c55x_analysis.c b/librz/arch/isa/tms320/c55x/c55x_analysis.c index 8a9f11a16a..0248078676 100644 --- a/librz/arch/isa/tms320/c55x/c55x_analysis.c +++ b/librz/arch/isa/tms320/c55x/c55x_analysis.c @@ -10,7 +10,7 @@ #include "c55x_analysis.h" #include "../tms320c55x_insn.h" -#include "../tms320_dasm.h" +#include "../c55_ir.h" #include "../c55x_plus/c55plus_analysis.h" /** @@ -54,159 +54,6 @@ * any unaligned-int dereference. */ -/* Sign-extend an n-bit value to st32. */ -static inline st32 sign_extend(ut32 v, ut32 bits) { - const ut32 mask = (1u << bits) - 1; - v &= mask; - if (v & (1u << (bits - 1))) { - return (st32)(v | ~mask); - } - return (st32)v; -} - -/* Relative branch target from the decoder's displacement operand. The C55x - * disassembler renders the signed displacement correctly for every BCC/B form - * (the raw bytes place it differently per encoding, and the short 0x60-0x67 - * forms fold it into the opcode), so parse the first hex operand and - * sign-extend it: 16-bit for the long 0x6f form, 8-bit otherwise. Returns - * false when there is no hex displacement (register / absolute branch), which - * the dedicated absolute paths handle instead. */ -static bool c55x_rel_target_from_syntax(const char *syntax, ut64 addr, int sz, ut8 op_byte, ut64 *out) { - if (!syntax) { - return false; - } - const char *h = strstr(syntax, "0x"); - if (!h) { - return false; - } - ut64 v = strtoull(h, NULL, 16); - st32 d = sign_extend((ut32)v, (op_byte == 0x6f) ? 16 : 8); - *out = addr + (ut64)sz + (st64)d; - return true; -} - -/* Set conditional-jump fields: type=cjmp, jump=target, fail=fallthrough. */ -static inline void set_cjmp(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CJMP; - op->jump = target; - op->fail = op->addr + op->size; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set conditional-call fields: type=ccall, jump=target, fail=fallthrough. */ -static inline void set_ccall(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CCALL; - op->jump = target; - op->fail = op->addr + op->size; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set unconditional-call fields: type=call, jump=target. */ -static inline void set_call(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CALL; - op->jump = target; - op->fail = op->addr + op->size; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set unconditional-jump fields. */ -static inline void set_jmp(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_JMP; - op->jump = target; - op->eob = true; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Mark an instruction as a return, with stack accounting. */ -static inline void set_ret(RzAnalysisOp *op) { - op->type = RZ_ANALYSIS_OP_TYPE_RET; - op->eob = true; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -2; -} - -/* Record a memory access width (in bytes: 1, 2, or 4) for loads and - * stores whose effective address is computed at runtime (e.g. via - * an address register or SP+disp). */ -static inline void set_mem_width(RzAnalysisOp *op, int width) { - op->refptr = width; - op->ptrsize = width; -} - -/* Record an immediate value (for "mov #k, dst" / "add #k, dst" etc.). */ -static inline void set_imm(RzAnalysisOp *op, st64 val) { - op->val = (ut64)val; -} - -/* Stack push: write+decrement. Track the byte delta. */ -static inline void set_push(RzAnalysisOp *op, int delta) { - op->type = RZ_ANALYSIS_OP_TYPE_PUSH; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = delta; -} - -/* Stack pop: read+increment. Track the byte delta. */ -static inline void set_pop(RzAnalysisOp *op, int delta) { - op->type = RZ_ANALYSIS_OP_TYPE_POP; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = delta; -} - -/* Conditional return -- like RET but doesn't end the basic block - * (fallthrough is possible if the condition is false). */ -static inline void set_cret(RzAnalysisOp *op) { - op->type = RZ_ANALYSIS_OP_TYPE_CRET; - op->fail = op->addr + op->size; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -2; -} - -/* Set op->reg (destination register name) for instructions whose - * destination register is encoded statically in the leading byte(s). - * The string is borrowed and must point to static storage. */ -static inline void set_dst_reg(RzAnalysisOp *op, const char *name) { - op->reg = name; -} - -/* Set op->ireg (register used for indirect memory computation) for - * register-indirect loads, stores, branches and calls (e.g. B ACx, - * CALL ACx). The string is borrowed and must point to static storage. */ -static inline void set_ireg(RzAnalysisOp *op, const char *name) { - op->ireg = name; -} - -/* Set op->direction so higher-level analysis knows whether the op - * reads from memory (LOAD-style), writes to memory (STORE-style), - * jumps (EXEC), or just references an address (REF). */ -static inline void set_dir(RzAnalysisOp *op, RzAnalysisOpDirection dir) { - op->direction = dir; -} - -/* Set op->disp (displacement) for memory references that compute - * their effective address as `base_register + disp`. */ -static inline void set_disp(RzAnalysisOp *op, st64 disp) { - op->disp = (ut64)disp; -} - -/* ACx selector tables -- index 0..3 corresponds to AC0..AC3. */ -static const char *const c55x_acc_names[4] = { "ac0", "ac1", "ac2", "ac3" }; - -/* General-purpose register table indexed by the 4-bit field used in - * 0x14 (AADD register form) and a number of other instructions: - * 0..3 -> AC0..AC3 (accumulators) - * 4..7 -> T0..T3 (temporary registers) - * 8..f -> AR0..AR7 (auxiliary / address registers) - * Confirmed against rz-asm output on the c55x decoder. */ -static const char *const c55x_gpr_names[16] = { - "ac0", "ac1", "ac2", "ac3", - "t0", "t1", "t2", "t3", - "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7" -}; - /* Per-leading-byte instruction size, extracted from the c55x * decoder's opcode table (librz/arch/isa/tms320/c55x/table.h -- * originally by th0rpe 2013, sourced from TI SPRU374). Bytes not @@ -251,411 +98,2804 @@ static int c55x_op_size(const ut8 *buf, int len) { return ((int)sz <= len) ? (int)sz : 0; } +/* ---- shared decode-IR descriptor (incremental C55x cutover) ----------- + * The shared c55_ir engine decodes a C55Insn once; the disassembler, + * analyzer and RzIL lifter then consume it. This is the first member of the + * TMS320C55x family wired onto the same engine as C55x+, validating that the + * IR/consumers are arch-agnostic. Only opcodes present in c55x_table take + * this path; everything else falls through to the legacy byte-driven code, + * so the migration stays behaviour-preserving at every step. + * + * The instruction ids and 23-bit data-pointer model are common to the family, + * so id->mnemonic and id->type are resolved exactly as for C55x+. The + * descriptor carries the operand-free nop and the register-to-register mov + * family; reg_info resolves the C55x register file (AC0-3 / T0-3 / AR0-7), + * a subset of the C55x+ file with identical il_vars and widths. */ +// C55x register file (a subset of C55x+; identical il_vars/widths): the +// register-to-register forms reach AC0-3, T0-3 and AR0-7. +static const C55RegInfo c55x_ac_ri[4] = { + { "ac0", "ac0", 40 }, { "ac1", "ac1", 40 }, { "ac2", "ac2", 40 }, { "ac3", "ac3", 40 } +}; +static const C55RegInfo c55x_t_ri[4] = { + { "t0", "t0", 16 }, { "t1", "t1", 16 }, { "t2", "t2", 16 }, { "t3", "t3", 16 } +}; +static const C55RegInfo c55x_ar_ri[8] = { + { "ar0", "ar0", 16 }, { "ar1", "ar1", 16 }, { "ar2", "ar2", 16 }, { "ar3", "ar3", 16 }, + { "ar4", "ar4", 16 }, { "ar5", "ar5", 16 }, { "ar6", "ar6", 16 }, { "ar7", "ar7", 16 } +}; +// The ARn registers are 16-bit for data, but the address pointer they form is +// the 23-bit XARn (used to compute effective addresses and apply post-modify). +static const C55RegInfo c55x_xar_ri[8] = { + { "xar0", "xar0", 23 }, { "xar1", "xar1", 23 }, { "xar2", "xar2", 23 }, { "xar3", "xar3", 23 }, + { "xar4", "xar4", 23 }, { "xar5", "xar5", 23 }, { "xar6", "xar6", 23 }, { "xar7", "xar7", 23 } +}; +static const C55RegInfo c55x_sp_ri = { "sp", "sp", 16 }; +// The coefficient data pointer: rendered "cdp", but the address pointer it +// forms (used for effective-address and post-modify of the Cmem coefficient +// operand) is the 23-bit XCDP, so its il_var is xcdp. +static const C55RegInfo c55x_cdp_ri = { "cdp", "xcdp", 23 }; +// The four C55x status registers ST0_55..ST3_55, written by the bclr / bset +// bit-clear / bit-set forms (opcode 0x46). They are 16-bit control registers +// with matching lifter variables. +static const C55RegInfo c55x_st_ri[4] = { + { "st0_55", "st0_55", 16 }, { "st1_55", "st1_55", 16 }, + { "st2_55", "st2_55", 16 }, { "st3_55", "st3_55", 16 } +}; +// The test-control status bits, written by the btst bit-test forms. Rendered +// TC1 / TC2 (uppercase); they carry no lifter variable here because the +// bit-test instruction that names them is not yet lifted. +static const C55RegInfo c55x_tc_ri[2] = { + { "TC1", NULL, 1 }, + { "TC2", NULL, 1 } +}; +// Named status flags and special registers used as explicit operands. CARRY / +// BORROW are the 0xdf carry/borrow flags; the rest are the destinations of the +// 0xdc mov Smem, forms. These are the 16-bit views the legacy +// lifter writes (e.g. dp / cdp, not the 23-bit xcdp), so they carry their own +// il_var here; dph is 7-bit and pdp is 9-bit, the rest 16-bit. +enum { + C55X_SPR_CARRY = 0, C55X_SPR_BORROW, + C55X_SPR_DP, C55X_SPR_CDP, C55X_SPR_BSA01, C55X_SPR_BSA23, C55X_SPR_BSA45, + C55X_SPR_BSA67, C55X_SPR_BSAC, C55X_SPR_SP, C55X_SPR_SSP, C55X_SPR_BK03, + C55X_SPR_BK47, C55X_SPR_BKC, C55X_SPR_DPH, C55X_SPR_PDP, C55X_SPR_CSR, + C55X_SPR_BRC0, C55X_SPR_BRC1, C55X_SPR_TRN0, C55X_SPR_TRN1, C55X_SPR_RPTC, + // 23-bit extended pointers named by the popboth / pshboth register-pair + // stack ops (the "xdst"/"xsrc" field). They carry no lifter variable: the + // pair push-pop is left unlifted. + C55X_SPR_XSP, C55X_SPR_XSSP, C55X_SPR_XDP, C55X_SPR_XCDP, C55X_SPR_COUNT +}; +static const C55RegInfo c55x_special_ri[C55X_SPR_COUNT] = { + { "CARRY", NULL, 1 }, { "BORROW", NULL, 1 }, + { "dp", "dp", 16 }, { "cdp", "cdp", 16 }, { "bsa01", "bsa01", 16 }, + { "bsa23", "bsa23", 16 }, { "bsa45", "bsa45", 16 }, { "bsa67", "bsa67", 16 }, + { "bsac", "bsac", 16 }, { "sp", "sp", 16 }, { "ssp", "ssp", 16 }, + { "bk03", "bk03", 16 }, { "bk47", "bk47", 16 }, { "bkc", "bkc", 16 }, + { "dph", "dph", 7 }, { "pdp", "pdp", 9 }, { "csr", "csr", 16 }, + { "brc0", "brc0", 16 }, { "brc1", "brc1", 16 }, { "trn0", "trn0", 16 }, + { "trn1", "trn1", 16 }, { "rptc", "rptc", 16 }, + { "xsp", NULL, 23 }, { "xssp", NULL, 23 }, { "xdp", NULL, 23 }, { "xcdp", NULL, 23 } +}; + +static const C55RegInfo *c55x_reg_info(C55RegClass cls, ut8 num, C55SubReg sub) { + (void)sub; + switch (cls) { + case C55_RC_AC: return num < 4 ? &c55x_ac_ri[num] : NULL; + case C55_RC_T: return num < 4 ? &c55x_t_ri[num] : NULL; + case C55_RC_AR: return num < 8 ? &c55x_ar_ri[num] : NULL; + case C55_RC_XAR: return num < 8 ? &c55x_xar_ri[num] : NULL; + case C55_RC_SP: return num == 0 ? &c55x_sp_ri : NULL; + case C55_RC_CDP: return num == 0 ? &c55x_cdp_ri : NULL; + case C55_RC_ST: return num < 4 ? &c55x_st_ri[num] : NULL; + case C55_RC_TC: return num < 2 ? &c55x_tc_ri[num] : NULL; + case C55_RC_SPECIAL: return num < C55X_SPR_COUNT ? &c55x_special_ri[num] : NULL; + default: return NULL; + } +} + +// C55x 4-bit register selector (TI "register field"): 0-3 -> AC0-3, +// 4-7 -> T0-3, 8-15 -> AR0-7. Shared by the register-to-register forms. +static void c55x_gr4(ut8 nib, C55Reg *r) { + r->sub = C55_SUB_NONE; + if (nib < 4) { + r->cls = C55_RC_AC; + r->num = nib; + } else if (nib < 8) { + r->cls = C55_RC_T; + r->num = (ut8)(nib - 4); + } else { + r->cls = C55_RC_AR; + r->num = (ut8)(nib - 8); + } +} + +// gr4 for the address-arithmetic forms (amov / asub ACx, ACy): the high range +// names the 23-bit XARn pointer rather than the 16-bit ARn. +static void c55x_gr4a(ut8 nib, C55Reg *r) { + r->sub = C55_SUB_NONE; + if (nib < 4) { + r->cls = C55_RC_AC; + r->num = nib; + } else if (nib < 8) { + r->cls = C55_RC_T; + r->num = (ut8)(nib - 4); + } else { + r->cls = C55_RC_XAR; + r->num = (ut8)(nib - 8); + } +} + +static void c55x_x_gr4a(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + c55x_gr4a((ut8)((bits >> d->lo) & 0x0f), &out->reg); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// The fixed CSR (computed single-repeat) register operand of the rpt / rptadd / +// rptsub forms. +static void c55x_x_csr(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = C55X_SPR_CSR; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// The transition register (trn0/trn1) operand of the dmaxdiff / dmindiff forms; +// the selecting bit is at d->lo. +static void c55x_x_trn(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = (ut8)(C55X_SPR_TRN0 + ((bits >> d->lo) & 0x1)); + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +static void c55x_x_gr4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + c55x_gr4((ut8)((bits >> d->lo) & 0x0f), &out->reg); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// As c55x_x_gr4, but the operand is omitted when it names the same register as +// the immediately-preceding operand (the unary "not/neg/abs ACx" forms collapse +// the destination against the source: "not ac0" rather than "not ac0, ac0"). +static void c55x_x_gr4_elide(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_gr4(a, bits, d, out); + out->elide_if_eq_prev = true; +} + +// Accumulator rendered with the dbl(...) wrapper -- the "dbl(ACx)" operand of +// the pop / psh dbl(ACx) stack forms (opcode 0x50, sub-opcodes 3/7). The dbl +// form only ever names an accumulator: the field's low two bits pick AC0-AC3 +// (the legacy decoder ignores the upper bits). The dbl marker is a disassembly +// decoration; the lifted op is the plain accumulator pop / psh. +static void c55x_x_ac_dbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + out->reg.sub = C55_SUB_NONE; + out->dbl = true; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// hi(ACx) accumulator high-word source of the "mov hi(ACx), dst" forms (opcode +// 0x44, high nibble 0-3): the accumulator index is the field's low two bits; +// the operand is the 16-bit high word (rendered hi(acN)). The shared MOV lifter +// sign-extends it to the destination width. +static void c55x_x_hi_ac(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + out->reg.sub = C55_SUB_HI; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Special-register source of the "mov , dst" forms (opcode 0x44, high +// nibble 8-15): 8 -> SP, 9 -> SSP, 10 -> CDP, 12 -> BRC0, 13 -> BRC1, +// 14 -> RPTC (the 16-bit views). 11 and 15 are unassigned -> left to the legacy +// decoder. The shared MOV lifter sign-extends the source to the dst width. +static void c55x_x_44src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 v = (ut8)((bits >> d->lo) & 0x0f); + ut8 spr; + switch (v) { + case 8: spr = C55X_SPR_SP; break; + case 9: spr = C55X_SPR_SSP; break; + case 10: spr = C55X_SPR_CDP; break; + case 12: spr = C55X_SPR_BRC0; break; + case 13: spr = C55X_SPR_BRC1; break; + case 14: spr = C55X_SPR_RPTC; break; + default: + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = spr; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} +// Special-register destination of "mov #k16, " (opcode 0x78): byte3 bit 0 +// is don't-care; byte3 bits 1-4 select the register -- 0 dp, 1 ssp, 2 cdp, +// 3 bsa01, 4 bsa23, 5 bsa45, 6 bsa67, 7 bsac, 8 sp. Selectors 9-15 are +// unassigned and left to the legacy decoder. +static void c55x_x_78dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 spr; + switch ((ut8)((bits >> d->lo) & 0x0f)) { + case 0: spr = C55X_SPR_DP; break; + case 1: spr = C55X_SPR_SSP; break; + case 2: spr = C55X_SPR_CDP; break; + case 3: spr = C55X_SPR_BSA01; break; + case 4: spr = C55X_SPR_BSA23; break; + case 5: spr = C55X_SPR_BSA45; break; + case 6: spr = C55X_SPR_BSA67; break; + case 7: spr = C55X_SPR_BSAC; break; + case 8: spr = C55X_SPR_SP; break; + default: + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = spr; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 16; +} + +// Destination register selector for mov #k12, (opcode 0x16): byte2's low +// nibble picks a control register; the immediate field sits in the 12 bits +// above it. Only eight encodings are defined (the rest are invalid). +static int c55x_16dst_spr(ut8 sel) { + switch (sel) { + case 0x0: return C55X_SPR_DPH; + case 0x3: return C55X_SPR_PDP; + case 0x4: return C55X_SPR_BK03; + case 0x5: return C55X_SPR_BK47; + case 0x6: return C55X_SPR_BKC; + case 0x8: return C55X_SPR_CSR; + case 0x9: return C55X_SPR_BRC0; + case 0xa: return C55X_SPR_BRC1; + default: return -1; + } +} +static void c55x_x_16dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + int spr = c55x_16dst_spr((ut8)((bits >> d->lo) & 0x0f)); + if (spr < 0) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = (ut8)spr; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 16; +} + +// Immediate for mov #k12, (opcode 0x16): a 12-bit field (d->lo .. d->lo+11) +// masked and displayed at the destination register's width (capped at 12). The +// destination selector is the nibble just below the field (byte2 low nibble). +static void c55x_x_16imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + int spr = c55x_16dst_spr((ut8)((bits >> (d->lo - 4)) & 0x0f)); + if (spr < 0) { + out->kind = C55_OP_INVALID; + return; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(C55_RC_SPECIAL, (ut8)spr, C55_SUB_NONE) : NULL; + int rw = ri ? ri->width : 16; + int iw = rw < 12 ? rw : 12; + ut64 k = (bits >> d->lo) & 0xfff; + out->kind = C55_OP_IMM; + out->imm = k & (((ut64)1 << iw) - 1); + out->width = iw; +} + +// The implicit stack-pointer destination of "aadd #k8, sp" (opcode 0x4e): no +// bits select it, so the extractor always yields sp. +static void c55x_x_sp(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SP; + out->reg.num = 0; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(C55_RC_SP, 0, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 16; +} + +// Special-register destination of the "mov gr4, " forms (opcode 0x52, low +// nibble 8-15): 8 -> sp, 9 -> ssp, 10 -> cdp, 12 -> csr, 13 -> brc1, 14 -> brc0. +// Low nibbles 11 and 15 are unassigned and left to the legacy decoder. (The map +// differs from the 0x44 source map: e.g. 14 is brc0 here, rptc there.) +static void c55x_x_52dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 spr; + switch ((ut8)((bits >> d->lo) & 0x0f)) { + case 8: spr = C55X_SPR_SP; break; + case 9: spr = C55X_SPR_SSP; break; + case 10: spr = C55X_SPR_CDP; break; + case 12: spr = C55X_SPR_CSR; break; + case 13: spr = C55X_SPR_BRC1; break; + case 14: spr = C55X_SPR_BRC0; break; + default: + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = spr; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *sri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = sri ? sri->width : 0; +} + +// swap / swapp register pair (opcode 0x5e). The byte-1 low nibble selects the +// pair; d->param 0 yields the first register, 1 the second. Byte-1 bit 4 marks +// swapp (the dual exchange), for which only the even-base pairs are valid; the +// lifter swaps the following pair too (signalled by the row's `.both`). +static void c55x_x_swap(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + static const struct { ut8 c0, n0, c1, n1; bool ok; } pairs[16] = { + [0] = { C55_RC_AC, 0, C55_RC_AC, 2, true }, + [1] = { C55_RC_AC, 1, C55_RC_AC, 3, true }, + [4] = { C55_RC_T, 0, C55_RC_T, 2, true }, + [5] = { C55_RC_T, 1, C55_RC_T, 3, true }, + [8] = { C55_RC_AR, 0, C55_RC_AR, 2, true }, + [9] = { C55_RC_AR, 1, C55_RC_AR, 3, true }, + [12] = { C55_RC_AR, 4, C55_RC_T, 0, true }, + [13] = { C55_RC_AR, 5, C55_RC_T, 1, true }, + [14] = { C55_RC_AR, 6, C55_RC_T, 2, true }, + [15] = { C55_RC_AR, 7, C55_RC_T, 3, true }, + }; + ut8 sel = (ut8)((bits >> d->lo) & 0x0f); + bool swapp = ((bits >> 4) & 1) != 0; + bool swap4 = ((bits >> 5) & 1) != 0; + // key 56 (both the swapp and swap4 marker bits set, sel == 8): the + // standalone "swap AR0, AR1" -- the single arbitrary AR pair, distinct + // from the sel-8 pair(AR0, AR2). + if (swapp && swap4 && sel == 8) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AR; + out->reg.num = d->param == 0 ? 0 : 1; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; + return; + } + if (!pairs[sel].ok) { + out->kind = C55_OP_INVALID; + return; + } + if (swap4) { + // swap4 only exchanges the ar4/ar5/ar6/ar7 <-> t0/t1/t2/t3 quad. + if (sel != 12) { + out->kind = C55_OP_INVALID; + return; + } + } else if (swapp && (sel & 1)) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + if (d->param == 0) { + out->reg.cls = pairs[sel].c0; + out->reg.num = pairs[sel].n0; + } else { + out->reg.cls = pairs[sel].c1; + out->reg.num = pairs[sel].n1; + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} +// 0x50, sub-opcodes 0/1). Sub-opcode bit 0 selects the direction: 0 -> +1, +// 1 -> -1. Rendered as a signed decimal with a '#' prefix; lifted by the shared +// SFTL shift handler (a positive count shifts left, a negative one right). +static void c55x_x_sftl_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = (bits & 1) ? (ut64)(-1) : 1; + out->width = 6; + out->imm_signed = true; + out->hash_dec = true; +} + +// The fixed shift count of the "sfts dst, #1" / "sfts dst, #-1" forms (opcode +// 0x44, high nibble 4-7). The high-nibble low bit (byte1 bit 4) selects the +// direction, with the opposite polarity to sftl: 1 -> +1, 0 -> -1. Rendered as +// a signed decimal with '#'; lifted (arithmetically) by the shared SFTS handler. +static void c55x_x_sfts_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = ((bits >> 4) & 1) ? 1 : (ut64)(-1); + out->width = 6; + out->imm_signed = true; + out->hash_dec = true; +} + +// Extended-register ("xdst" / "xsrc") field of the popboth / pshboth pair stack +// ops (opcode 0x50, sub-opcodes 4/5): 0-3 -> AC0-AC3, 4 -> XSP, 5 -> XSSP, +// 6 -> XDP, 7 -> XCDP, 8-15 -> XAR0-XAR7. These move a register pair; the +// shared push/pop lifter leaves them unlifted (see the .both flag). +static void c55x_x_xgr4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 v = (ut8)((bits >> d->lo) & 0x0f); + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + if (v < 4) { + out->reg.cls = C55_RC_AC; + out->reg.num = v; + } else if (v < 8) { + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = (ut8)(C55X_SPR_XSP + (v - 4)); // XSP, XSSP, XDP, XCDP + } else { + out->reg.cls = C55_RC_XAR; + out->reg.num = (ut8)(v - 8); + } + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Source gr4 register that collapses against the destination gr4 register when +// the two are equal -- the optional "src" of the "add/sub Smem, [src,] dst" +// forms (opcodes 0xd6/0xd7), where src is the last byte's bits 0-3 and dst its +// bits 4-7. When src == dst only the destination is printed (dst += Smem). +static void c55x_x_gr4_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 src = (ut8)((bits >> d->lo) & 0x0f); + ut8 dst = (ut8)((bits >> (d->lo + 4)) & 0x0f); + if (src == dst) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + c55x_gr4(src, &out->reg); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// The optional "ACx" source of the dbl add/sub forms (opcode 0xed): SS is the +// last byte's bits 7-6 (read at d->lo) and DD the destination at bits 5-4 +// (d->lo - 2). Both name an accumulator ac0-3; when SS == DD only the +// destination prints (ACy += dbl(Lmem)). +static void c55x_x_ac_src2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 src = (ut8)((bits >> d->lo) & 0x3); + ut8 dst = (ut8)((bits >> (d->lo - 2)) & 0x3); + if (src == dst) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_AC; + out->reg.num = src; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Condition field (xcc / xccpart, and the conditional transfers): the low seven +// bits encode either a register-versus-zero comparison or a status-flag +// expression. The subject register is reg-field-4 in bits 0-3 and the +// comparison is bits 4-6 (0..5 -> == != < <= > >=, matching C55Relop). The flag +// expressions (bits 4-6 of 6 or 7) are not represented here yet, so the decode +// is abandoned for them and the legacy front-end renders those conditions. +static void c55x_x_cond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 field = (ut8)((bits >> d->lo) & 0x7f); + ut8 cmp = (ut8)((field >> 4) & 0x07); + if (cmp <= C55_REL_GE) { + // 0-5: a register compared against zero. + out->kind = C55_OP_COND; + c55x_gr4((ut8)(field & 0x0f), &out->reg); + out->relop = (C55Relop)cmp; + out->imm = 0; + return; + } + // 6-7: a status-flag condition. The flag id is (cmp-6)*16 + the low nibble, + // which indexes the shared cond_flags table. C55x leaves a few of those + // slots undefined - overflow(govf) (7), word/byte mode (12/13), two reserved + // (14/15) and !overflow(govf) (23) - so those fall back to the legacy + // decoder rather than decoding as a flag. + ut8 flag = (ut8)(((cmp - 6) << 4) | (field & 0x0f)); + if (flag == 7 || (flag >= 12 && flag <= 15) || flag == 23) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = flag; +} + +// bcc short form (opcodes 0x60-0x67): the destination is a forward offset in a +// four-bit field whose high three bits are byte0 bits 0-2 and whose low bit is +// byte1 bit 7 (so the field occupies packed bits 7-10). The branch goes to +// pc + size + offset; the offset is small and always non-negative, so it is +// rendered as a one-digit immediate and flagged unsigned (no sign-extension). +static void c55x_x_bcc_short_target(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = (ut64)((bits >> d->lo) & 0x0f); + out->width = 4; + out->reltarget = true; + out->reltarget_unsigned = true; +} + +// Compare-and-branch condition (bcc 0x6f): the source register is gr4 in byte1 +// bits 4-7 and the comparison is byte1 bits 2-3 (0 ==, 1 <, 2 >=, 3 !=), with +// bit 0 selecting the unsigned form (bccu). byte2 is the 8-bit compare constant +// K8. The 16-bit field passed in is byte1:byte2. Only the signed comparisons +// are represented here; the unsigned form is left to the legacy decoder. +static void c55x_x_cond_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + static const C55Relop relmap[4] = { C55_REL_EQ, C55_REL_LT, C55_REL_GE, C55_REL_NE }; + ut16 field = (ut16)((bits >> d->lo) & 0xffff); + ut8 b1 = (ut8)(field >> 8); + if (b1 & 1) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_COND; + c55x_gr4((ut8)(b1 >> 4), &out->reg); + out->relop = relmap[(b1 >> 2) & 3]; + out->imm = (ut64)(field & 0xff); + out->width = 8; + out->cmp_imm = true; +} + +// 2-bit accumulator selector for the register-indirect control-transfer forms +// (b acx / call acx): only the four accumulators are addressable as a branch or +// call target, encoded in the low two bits of the operand byte (the remaining +// bits are don't-cares -- 0x9104, 0x9108, ... all decode to "b ac0"). +static void c55x_x_ac2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Destination accumulator ACy for mpyk/mpykr/mack/mackr: identical to +// c55x_x_ac2 but flagged so the formatter omits it when it equals the source +// ACx (the TI "mpyk #k, ACx" short form where ACy defaults to ACx). +static void c55x_x_ac2_elide(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_ac2(a, bits, d, out); + out->elide_if_eq_prev = true; +} + +// 2-bit Tx selector (T0-T3) for the mack/mackr coefficient register. +static void c55x_x_t2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_T; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// A left-shifted 16-bit immediate: the "#k16 << #16" operand of the opcode-0x7a +// immediate-ALU forms. The shift is a fixed 16 and is rendered/lifted as such. +static void c55x_x_imm_sh16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55_x_imm(a, bits, d, out); + out->sh_left = true; + out->shamt = 16; +} + +// A left-shifted 16-bit immediate with a variable shift count: the +// "#k16 << #sh" operand of the opcode-0x70..0x74 immediate-ALU forms. The shift +// is byte3 bits 0-3 (0-15) and is rendered as hex. +static void c55x_x_imm_varsh(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55_x_imm(a, bits, d, out); + out->sh_left = true; + out->shamt = (int8_t)(bits & 0xf); + out->shamt_hex = true; +} + +// A negated 4-bit magnitude immediate: the "-#k" operand of the opcode-0x3e +// short move (byte1 high nibble is the magnitude k, 0-15; the value is -k). +static void c55x_x_negk4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 k = (ut8)((bits >> d->lo) & 0xf); + out->kind = C55_OP_IMM; + out->imm = (ut64)(-(st64)k); + out->imm_signed = true; + out->neg_imm = true; + out->width = 4; +} + +// Fixed TC1 / TC2 literal operands (the addsub2cc form always tests both test- +// control flags, rendered as the constant "TC1, TC2" pair). +static void c55x_x_tc1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_TC; + out->reg.num = 0; + out->reg.sub = C55_SUB_NONE; + out->width = 1; +} +static void c55x_x_tc2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_TC; + out->reg.num = 1; + out->reg.sub = C55_SUB_NONE; + out->width = 1; +} + +// The "Baddr" bit-address operand of btstp: the legacy disassembler does not +// decode the bit address and renders the literal placeholder "Baddr". The +// operand carries a verbatim render string so the output matches byte-for-byte. +static void c55x_x_baddr(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_IMM; + out->raw = "Baddr"; +} + +// High accumulator-half source ACx.h for mov hi(ACx), Smem (opcodes 0xbc-0xbf): +// the accumulator number is the opcode's low two bits. +static void c55x_x_achi(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + out->reg.sub = C55_SUB_HI; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Single data-memory (Smem) operand for the C55x load / store group. The +// operand byte selects the addressing mode: an even byte is an SP-relative +// direct access (*sp(#k), k = byte>>1) and an odd byte selects a register mode +// whose base ARn is byte[7:5] and whose sub-mode is byte[4:1]. The SP-relative +// direct forms and the 2-byte register-modify matrix the shared +// effective-address / memory primitives understand are decoded here; the +// const-indexed and absolute forms (which extend the instruction with a 16-bit +// field) and the pre-modify and bit-reverse forms fall back to the legacy +// decoder. +static void c55x_x_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 b = (ut8)((bits >> d->lo) & 0xff); + if (!(b & 1)) { + // SP-relative direct: *sp(#k), the unsigned offset k = byte>>1 (0-127). + out->kind = C55_OP_MEM; + out->access = 16; + out->amode = C55_AM_INDEXED; + out->reg.cls = C55_RC_SP; + out->reg.num = 0; + out->reg.sub = C55_SUB_NONE; + out->disp = (st32)(b >> 1); + return; + } + ut8 mode = (ut8)((b >> 1) & 0x0f); + out->kind = C55_OP_MEM; + out->access = 16; + c55x_gr4((ut8)(8 + ((b >> 5) & 7)), &out->reg); // base ARn + switch (mode) { + case 0: out->amode = C55_AM_INDIRECT; return; + case 1: out->amode = C55_AM_POSTINC; return; + case 2: out->amode = C55_AM_POSTDEC; return; + case 3: + out->amode = C55_AM_POSTADD; + c55x_gr4(4, &out->index); // t0 + return; + case 4: + out->amode = C55_AM_POSTSUB; + c55x_gr4(4, &out->index); // t0 + return; + case 5: + out->amode = C55_AM_IDXREG; + c55x_gr4(4, &out->index); // t0 + return; + case 9: + out->amode = C55_AM_POSTADD; + c55x_gr4(5, &out->index); // t1 + return; + case 10: + out->amode = C55_AM_POSTSUB; + c55x_gr4(5, &out->index); // t1 + return; + case 11: + out->amode = C55_AM_IDXREG; + c55x_gr4(5, &out->index); // t1 + return; + case 12: out->amode = C55_AM_PREINC; return; // *+arN + case 13: out->amode = C55_AM_PREDEC; return; // *-arN + case 14: + out->amode = C55_AM_BITREV; // *(arN + t0b) reverse-carry + c55x_gr4(4, &out->index); // t0 (rendered t0b) + return; + case 15: + out->amode = C55_AM_BITREV_SUB; // *(arN - t0b) reverse-carry + c55x_gr4(4, &out->index); // t0 (rendered t0b) + return; + case 6: + // *arN(#K16) long const-index: ARn is the base pointer (unmodified); the + // signed 16-bit constant lives in a 2-byte extension that c55_decode + // appends and writes into ->disp. + out->amode = C55_AM_CONST_IDX; + return; + case 7: + // *+arN(#K16) long const-index with pre-modify: like mode 6 but ARn is + // updated (ARn += K16); the 2-byte extension is read by c55_decode. + out->amode = C55_AM_CONST_IDX_PRE; + return; + case 8: { + // Mode 8 dispatches on the base ARn field: 0 -> abs16(#k16), 3 -> *cdp, + // 4 -> *cdp+, 5 -> *cdp-, 6 -> *cdp(K16), 7 -> *+cdp(K16). For abs16 the + // address is DPH:k16 so the base is irrelevant; c55_decode reads the + // unsigned 2-byte k16. Base 1 (*(k23)) and base 2 (port(k16)) are not + // modelled here and fall through to the legacy decoder. + ut8 base = (b >> 5) & 7; + if (base >= 3) { + out->reg.cls = C55_RC_CDP; + out->reg.num = 0; + out->reg.sub = C55_SUB_NONE; + switch (base) { + case 4: out->amode = C55_AM_POSTINC; return; // *cdp+ + case 5: out->amode = C55_AM_POSTDEC; return; // *cdp- + case 6: out->amode = C55_AM_CONST_IDX; return; // *cdp(K16) + case 7: out->amode = C55_AM_CONST_IDX_PRE; return; // *+cdp(K16) + default: out->amode = C55_AM_INDIRECT; return; // 3 -> *cdp + } + } + if (base == 1) { + // *(k23): the 23-bit (k24-encoded) absolute byte address lives in a + // 3-byte extension that c55_decode appends and writes into abs_addr. + // (The legacy decoder renders this address incorrectly -- it leaks + // format-string bytes -- so the shared path supersedes it here.) + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = C55_ABS_EXT; + return; + } + out->amode = C55_AM_ABS16; + return; + } + default: + // all Smem modes (0-15) are handled above; this is defensive only. + out->kind = C55_OP_INVALID; + return; + } +} + +// Smem with a register shift count: the "Smem << Tx" forms (opcode 0xdd). The +// Smem byte is decoded as usual; the shift register Tx is the last byte's bits +// 2-3 (T0-T3), recorded in the dedicated shift-register field so it does not +// collide with any addressing index the Smem mode itself uses. +static void c55x_x_smem_shtx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind != C55_OP_MEM) { + return; // invalid / instruction-extending Smem mode -> leave for the legacy + } + out->sh_mem_reg.cls = C55_RC_T; + out->sh_mem_reg.num = (ut8)((bits >> 2) & 0x3); + out->sh_mem_reg.sub = C55_SUB_NONE; + out->sh_mem_reg_set = true; +} + +// As c55x_x_smem_shtx, but also reads the rounding bit (byte2 bit 6) so the +// formatter wraps the operand in rnd(...) (the "mov rnd(Smem << Tx), ACx" form). +static void c55x_x_smem_shtx_rnd(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem_shtx(a, bits, d, out); + if (out->kind != C55_OP_MEM) { + return; + } + out->mem_round = (bits >> 6) & 1; +} + +// Smem source shifted left by 16 for the mov Smem << #16, ACx load group +// (opcodes 0xb0-0xb3): reuses the Smem decode and tags the operand with the +// fixed << 16 shift. The shift is rendered but, like the legacy decoder, the +// load is not yet lifted (the shared lifter skips a shifted memory access). +static void c55x_x_smem_sh16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->sh_left = true; + out->shamt = 16; + } +} + +// Smem source carrying the "unsigned" qualifier from byte2 bit 0, rendered +// uns(...) by the memory formatter. Used by the 0xdf add/sub Smem forms, where +// bit 0 selects between a signed and an unsigned memory operand. +static void c55x_x_smem_uns(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->uns = (bits & 1) != 0; + } +} + +// Smem source with a byte-access wrapper (d->param: 1 high_byte, 2 low_byte) +// plus the unsigned qualifier from byte2 bit 0. Used by the 0xdf mov +// high_byte/low_byte forms. The byte-access load is not lifted (the memory +// mover declines a byte_sel operand), matching the legacy decoder. +static void c55x_x_smem_byte(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->byte_sel = (ut8)d->param; + out->uns = (bits & 1) != 0; + } +} + +// Destination special register of the 0xdc mov Smem, forms, +// selected by byte2 bits 4-7. d->param picks the sub-group: 2 is the +// dp/cdp/bsa*/sp/ssp/bk*/dph/pdp set (4-bit selector), 3 is the +// csr/brc*/trn* set (3-bit selector). Undefined selectors become INVALID so +// the instruction falls through to the legacy decoder. +static void c55x_x_dc_movdst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + static const int8_t g2[16] = { + C55X_SPR_DP, C55X_SPR_CDP, C55X_SPR_BSA01, C55X_SPR_BSA23, + C55X_SPR_BSA45, C55X_SPR_BSA67, C55X_SPR_BSAC, C55X_SPR_SP, + C55X_SPR_SSP, C55X_SPR_BK03, C55X_SPR_BK47, C55X_SPR_BKC, + C55X_SPR_DPH, -1, -1, C55X_SPR_PDP + }; + static const int8_t g3[8] = { + C55X_SPR_CSR, C55X_SPR_BRC0, C55X_SPR_BRC1, C55X_SPR_TRN0, + C55X_SPR_TRN1, -1, -1, -1 + }; + int8_t spr = (d->param == 2) ? g2[(bits >> 4) & 0xf] : g3[(bits >> 4) & 0x7]; + if (spr < 0) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = (ut8)spr; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 16; +} + +// A double-word (32-bit) single-data-memory operand: decoded like an Smem but +// rendered dbl(...) and treated as a two-word access (e.g. psh / pop dbl). +static void c55x_x_smem_dbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->dbl = true; + out->access = 32; + } +} + +// Long (dual) data-memory operand printed as "dual(Smem)": the dual-operand +// add/sub forms (addsub / subadd Tx, dual(Lmem), ACy) access a 32-bit long +// word and wrap the Smem in dual(...) rather than dbl(...). +static void c55x_x_smem_dual(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->dual_wrap = true; + out->access = 32; + } +} + +// MPY register form (opcode 0x58): the operand fields live in the low byte -- +// Tx (bits 2-3), the source accumulator ACy (bits 4-5) and the destination +// accumulator ACx (bits 6-7). The disassembly is "mpy Tx, [ACy,] ACx": the +// middle source accumulator is printed only when it differs from the +// destination, so the source extractor yields C55_OP_NONE (which the decode +// loop skips) when the two coincide, giving the 2-operand form "mpy Tx, ACx". +static void c55x_x_mpy_t(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_T; + out->reg.num = (ut8)((bits >> 2) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +static void c55x_x_mpy_acsrc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 src = (ut8)((bits >> 4) & 0x3); + ut8 dst = (ut8)((bits >> 6) & 0x3); + if (src == dst) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = src; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +static void c55x_x_mpy_acdst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> 6) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Trailing destination ACy (bits 6-7) of the register MAC (opcode 0x56, mac +// form): the "mac[r] ACx, Tx, ACy[, ACy]" syntax prints the explicit second ACy +// only when it differs from the ACx multiplicand (bits 4-5). When they coincide +// the operand collapses (C55_OP_NONE) and the three-operand form is rendered; +// either way the lifter folds the accumulate into the single destination ACy. +static void c55x_x_macreg_acy_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 acx = (ut8)((bits >> 4) & 0x3); + ut8 acy = (ut8)((bits >> 6) & 0x3); + if (acx == acy) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = acy; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// SHIFTW: the 6-bit signed shift count immediate of the 0x10 register-ALU group +// (the last byte's bits 0-5). It is stored raw (unsigned) so the disassembler +// prints the field value as the legacy does; the lifter sign-extends it to +// choose the shift direction and magnitude. +static void c55x_x_shiftw(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0x3f; + out->width = 6; + out->imm_signed = false; +} + +// As c55x_x_shiftw, but the field is the right-hand side of the "ACx << #SHIFTW" +// syntax used by the 0x10 and/or/xor/add/sub shift-and-combine forms, so it is +// rendered joined to the previous operand by " << " instead of a comma. +static void c55x_x_shiftw_shl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_shiftw(a, bits, d, out); + out->shl_join = true; +} + +// Tx shift-count operand of "add/sub ACx << Tx, ACy" (opcode 0x5a). The 2-bit +// field at d->lo selects t0..t3; it is rendered joined to the preceding ACx by +// " << " and read by the (register-count) ADDSHL/SUBSHL lifter. +static void c55x_x_tx_shl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_T; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + out->reg.sub = C55_SUB_NONE; + out->shl_join = true; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Status register STn_55 operand of bclr / bset (opcode 0x46): the 3-bit field +// at d->lo selects st0_55..st3_55; selectors 4-7 are unassigned -> INVALID. +static void c55x_x_st(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 n = (ut8)((bits >> d->lo) & 0x7); + if (n > 3) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_ST; + out->reg.num = n; + out->reg.sub = C55_SUB_NONE; + const C55RegInfo *sri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = sri ? sri->width : 0; +} + +// Register-register compare condition of cmp/cmpand/cmpor (opcode 0x12), +// rendered "SRC DST". SRC is the gr4 at byte-1 bits 4-7, DST the gr4 at +// byte-2 bits 4-7, the relop is byte-1 bits 2-3 (0 ==, 1 <, 2 >=, 3 !=). +static void c55x_x_cmpcond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + static const C55Relop rel_map[4] = { C55_REL_EQ, C55_REL_LT, C55_REL_GE, C55_REL_NE }; + out->kind = C55_OP_COND; + out->cmp_to_reg = true; + out->relop = rel_map[(bits >> 10) & 0x3]; + c55x_gr4((ut8)((bits >> 12) & 0xf), &out->reg); + c55x_gr4((ut8)((bits >> 4) & 0xf), &out->index); +} + +// A TC status-flag operand (tc1/tc2, optionally negated as !tc1/!tc2) of the +// compare forms. The selecting bit is at d->lo (byte-2 bit 0 for the TCz +// output, bit 1 for the cmpand/cmpor TCx input); 0 -> tc1, 1 -> tc2. When +// d->param is non-zero it is the bit position of the negation flag (byte-2 +// bit 3 for the TCx input); a set negation bit yields the !tcN condition id. +static void c55x_x_tcflag(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 tcb = (ut8)((bits >> d->lo) & 0x1); + bool neg = d->param && ((bits >> d->param) & 0x1); + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)((neg ? 20 : 4) + tcb); +} + +// rol/ror rotate-in / rotate-out bit selector: a single bit picking CARRY (0) +// or TC2 (1). Rendered via the shared cond-flag table (carry -> id 6, tc2 -> id +// 5); the lifter maps these ids to status-register bits 11 (carry) and 12 (tc2). +static void c55x_x_rolflag(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 b = (ut8)((bits >> d->lo) & 0x1); + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = b ? 5 : 6; // 0 -> carry, 1 -> tc2 +} + +// Trailing destination ACy (bits 14-15) of the three-byte 0x10 register-ALU +// group, collapsing against the source ACx (bits 12-13) when the two are equal +// (mirrors c55x_x_macreg_acy_dst but for the wider three-byte encoding). +static void c55x_x_shiftk_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 acx = (ut8)((bits >> 12) & 0x3); + ut8 acy = (ut8)((bits >> 14) & 0x3); + if (acx == acy) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = acy; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} +// (uns(Cmem) -> zero-extended). +static void c55x_x_cmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_MEM; + out->reg.cls = C55_RC_CDP; + out->reg.num = 0; + out->reg.sub = C55_SUB_NONE; + out->access = 16; + out->uns = d->param != 0; + switch ((ut8)(bits & 0x3)) { + case 0: out->amode = C55_AM_INDIRECT; break; + case 1: out->amode = C55_AM_POSTINC; break; + case 2: out->amode = C55_AM_POSTDEC; break; + default: // 3: *(cdp+t0) + out->amode = C55_AM_POSTADD; + out->index.cls = C55_RC_T; + out->index.num = 0; + out->index.sub = C55_SUB_NONE; + break; + } +} + +// Destination accumulator ACx of a memory multiply / MAC (last-byte bits 4-5). +static void c55x_x_mac_acdst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)((bits >> 4) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Coefficient accumulator ACx (its high word ACx(32-16) is the multiplicand) of the +// accumulator-coefficient memory MACs: last-byte bits 0-1 select the source +// ACx, bits 4-5 the destination ACy. When the two coincide the legacy decoder +// renders the two-operand form but leaves it unlifted, so that case is reported +// as INVALID (the structured decode is abandoned to the legacy path) and only +// the distinct-register three-operand form is lifted here. +static void c55x_x_mac_accoef(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 ss = (ut8)(bits & 0x3); + ut8 dd = (ut8)((bits >> 4) & 0x3); + if (ss == dd) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = ss; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Coefficient T register Tx of the Tx-coefficient memory MACs (macm / masm / +// mpym Smem, Tx, ...); the two-bit Tx selector is at the op-slot's `lo`. +static void c55x_x_mac_tcoef(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_T; + out->reg.num = (ut8)((bits >> d->lo) & 0x3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// mpymu Smem, Tx, ACx: the unsigned multiply (last byte's op bits = 11). The whole +// operation is unsigned, so both multiplicands carry uns; the 'u' mnemonic suffix +// (uns_all) renders them without per-operand uns() wrappers. +static void c55x_x_smem_u(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_smem(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->uns = true; + } +} +static void c55x_x_tcoef_u(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_mac_tcoef(a, bits, d, out); + out->uns = true; +} + +// Accumulator operand ACx (last-byte bits 0-1) that collapses against the +// destination ACy (bits 4-5): used both as the explicit accumulator source of +// the Tx-coefficient MACs and as the high-word ACx(32-16) coefficient of the +// accumulator-coefficient multiplies. When the two registers differ the +// four/three-operand form is rendered with ACx present; when they coincide the +// operand collapses (NONE) to the shorter form whose role defaults to ACy. +static void c55x_x_mac_accsrc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 ss = (ut8)(bits & 0x3); + ut8 dd = (ut8)((bits >> 4) & 0x3); + if (ss == dd) { + out->kind = C55_OP_NONE; + return; + } + out->kind = C55_OP_REG; + out->reg.sub = C55_SUB_NONE; + out->reg.cls = C55_RC_AC; + out->reg.num = ss; + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +static const char *c55x_mnemonic(ut16 id) { + return tms320c55x_insn_name((TMS320C55InsID)id); +} + +static ut32 c55x_op_type(ut16 id) { + switch ((TMS320C55InsID)id) { + case TMS320C55_INS_NOP: + case TMS320C55_INS_IDLE: return RZ_ANALYSIS_OP_TYPE_NOP; + case TMS320C55_INS_MOV: return RZ_ANALYSIS_OP_TYPE_MOV; + // delay Smem: a memory-delay that copies the addressed word to the next + // higher address (TI SWPU104 6.7.1); the legacy analysis models it as a + // data MOVE, so the shared path mirrors that op type. + case TMS320C55_INS_DELAY: return RZ_ANALYSIS_OP_TYPE_MOV; + // amar computes an effective address (and applies the addressing mode's + // post-modify side effect) without accessing memory: an address load. + case TMS320C55_INS_AMAR: return RZ_ANALYSIS_OP_TYPE_LEA; + // psh / pop a single-data-memory operand onto / off the stack. + case TMS320C55_INS_PSH: return RZ_ANALYSIS_OP_TYPE_PUSH; + case TMS320C55_INS_POP: return RZ_ANALYSIS_OP_TYPE_POP; + case TMS320C55_INS_PSHBOTH: return RZ_ANALYSIS_OP_TYPE_PUSH; + case TMS320C55_INS_POPBOTH: return RZ_ANALYSIS_OP_TYPE_POP; + case TMS320C55_INS_ADD: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_SUB: return RZ_ANALYSIS_OP_TYPE_SUB; + // mpy Tx, [ACy,] ACx: a register multiply (ACx = Tx * ACy(32-16)). + case TMS320C55_INS_MPY: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MPYK: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MACK: return RZ_ANALYSIS_OP_TYPE_MUL; + // mpym Smem, Cmem, ACx: a memory multiply (ACx = Smem * Cmem). + case TMS320C55_INS_MPYM: return RZ_ANALYSIS_OP_TYPE_MUL; + // macm / masm Smem, Cmem, ACx: memory multiply-accumulate / -subtract. + case TMS320C55_INS_MACM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MASM: return RZ_ANALYSIS_OP_TYPE_MUL; + // mac / mas Smem, uns(Cmem), ACx: the unsigned-coefficient memory MACs. + case TMS320C55_INS_MAC: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MAS: return RZ_ANALYSIS_OP_TYPE_MUL; + // sqrm / sqam / sqsm Smem, [ACx,] ACy: the squaring multiplies (ACy = + // [ACx +/-] Smem * Smem). The legacy decoder leaves these untyped and + // unlifted; the shared path classifies them as multiplies and lifts them. + case TMS320C55_INS_SQRM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQAM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQSM: return RZ_ANALYSIS_OP_TYPE_MUL; + // The 0x54 register square / square-accumulate forms are genuine multiplies + // (ACy = ACx*ACx [+/- ACy]); the legacy left them untyped (null). + case TMS320C55_INS_SQR: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQA: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQS: return RZ_ANALYSIS_OP_TYPE_MUL; + // addv / addrv (addition with absolute value) -- typed as an addition. + case TMS320C55_INS_ADDV: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_ADDRV: return RZ_ANALYSIS_OP_TYPE_ADD; + // aadd #k8, sp (address-arithmetic add, frame setup) -- an addition. + case TMS320C55_INS_AADD: return RZ_ANALYSIS_OP_TYPE_ADD; + // amov #k16, dst: legacy types the constant/address load as LEA; the actual + // dst = zero-extend(#k16) semantics come from the C55_LOP_AMOV lifter. + case TMS320C55_INS_AMOV: return RZ_ANALYSIS_OP_TYPE_LEA; + // rptb / rptcc: block / conditional repeat loop control. + case TMS320C55_INS_RPTB: + case TMS320C55_INS_RPTCC: return RZ_ANALYSIS_OP_TYPE_REP; + // rptadd / rptsub: adjust the single-repeat counter and repeat. + case TMS320C55_INS_RPTADD: + case TMS320C55_INS_RPTSUB: return RZ_ANALYSIS_OP_TYPE_REP; + // cmp / cmpand / cmpor SRC DST, TCz: register compare writing a TC bit. + case TMS320C55_INS_CMP: + case TMS320C55_INS_CMPAND: + case TMS320C55_INS_CMPOR: return RZ_ANALYSIS_OP_TYPE_CMP; + // swap / swapp / swap4: register (pair) exchange. + case TMS320C55_INS_SWAP: return RZ_ANALYSIS_OP_TYPE_XCHG; + case TMS320C55_INS_SWAPP: return RZ_ANALYSIS_OP_TYPE_XCHG; + case TMS320C55_INS_SWAP4: return RZ_ANALYSIS_OP_TYPE_XCHG; + // sftl / sfts / sftsc ACx, Tx[, ACy]: a register shift by a T-register count + // (the sign of Tx selects the direction). Typed as a shift like the legacy. + case TMS320C55_INS_SFTL: return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_SFTS: return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_SFTSC: return RZ_ANALYSIS_OP_TYPE_SHL; + // firsadd / firssub Xmem, Ymem, Cmem, ACx, ACy: a FIR-filter step combining a + // multiply-accumulate (ACy += ACx.h * Cmem) with a shifted (anti)symmetric sum + // (ACx = (Xmem<<16) +/- (Ymem<<16)). The legacy left these unlifted with an + // inconsistent type (firsadd -> lea, firssub -> null); the shared path lifts + // them and classifies both as the multiply that dominates. + case TMS320C55_INS_FIRSADD: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_FIRSSUB: return RZ_ANALYSIS_OP_TYPE_MUL; + // sqdst squares the ACx high word into ACy (a multiply); abdst only adds an + // absolute value, so it keeps the legacy's null type (no precise rizin type). + case TMS320C55_INS_SQDST: return RZ_ANALYSIS_OP_TYPE_MUL; + // lms multiply-accumulates Xmem*Ymem into ACy (with a parallel shifted add into + // ACx); the legacy left it unlifted and null-typed -- classify as the multiply. + case TMS320C55_INS_LMS: return RZ_ANALYSIS_OP_TYPE_MUL; + // neg has no dedicated RzAnalysis op type; the legacy analysis reports it + // as a subtraction, and the lifter distinguishes it via C55_LOP_NEG. + case TMS320C55_INS_NEG: return RZ_ANALYSIS_OP_TYPE_SUB; + // max / min likewise have no dedicated op type; the legacy analysis reports + // them as compares, and the lifter selects them via C55_LOP_MAX / _MIN. + case TMS320C55_INS_MAX: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_MIN: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_AND: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTST: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTSET: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BAND: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTCLR: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTNOT: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BSET: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BCLR: return RZ_ANALYSIS_OP_TYPE_MOV; + // bfxtr K16, ACx, ACy: extract the bits of ACx selected by the K16 mask and + // right-pack them into ACy -- a register field move (the companion bfxpa + // stays untyped, as the legacy decoder left it). + case TMS320C55_INS_BFXTR: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BNOT: return RZ_ANALYSIS_OP_TYPE_XOR; + case TMS320C55_INS_ADDSUBCC: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_SUBC: return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_ADDSUB: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_SUBADD: return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_OR: return RZ_ANALYSIS_OP_TYPE_OR; + case TMS320C55_INS_XOR: return RZ_ANALYSIS_OP_TYPE_XOR; + case TMS320C55_INS_NOT: return RZ_ANALYSIS_OP_TYPE_NOT; + // Register-indirect branch / call (b acx / call acx) transfer control to an + // address held in an accumulator, so they are unconditional indirect forms. + case TMS320C55_INS_B: return RZ_ANALYSIS_OP_TYPE_UJMP; + case TMS320C55_INS_CALL: return RZ_ANALYSIS_OP_TYPE_UCALL; + // reset triggers a non-maskable software reset; model it as a trap. + case TMS320C55_INS_RESET: return RZ_ANALYSIS_OP_TYPE_TRAP; + // intr / trap raise a software interrupt / trap to a vector number. + case TMS320C55_INS_INTR: return RZ_ANALYSIS_OP_TYPE_SWI; + case TMS320C55_INS_TRAP: return RZ_ANALYSIS_OP_TYPE_TRAP; + // rpt sets up a single-instruction hardware repeat (a loop construct). + case TMS320C55_INS_RPT: return RZ_ANALYSIS_OP_TYPE_REP; + // rptblocal arms a local block-repeat; likewise a loop construct. + case TMS320C55_INS_RPTBLOCAL: return RZ_ANALYSIS_OP_TYPE_REP; + // ret / reti pop the return (or interrupt-return) address; both are returns. + case TMS320C55_INS_RET: return RZ_ANALYSIS_OP_TYPE_RET; + case TMS320C55_INS_RETI: return RZ_ANALYSIS_OP_TYPE_RET; + // xcc / xccpart predicate the following instruction(s) on a condition; model + // them as a compare (matching the legacy type). + case TMS320C55_INS_XCC: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_XCCPART: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_BCC: return RZ_ANALYSIS_OP_TYPE_CJMP; + case TMS320C55_INS_CALLCC: return RZ_ANALYSIS_OP_TYPE_CCALL; + case TMS320C55_INS_RETCC: return RZ_ANALYSIS_OP_TYPE_CRET; + default: return RZ_ANALYSIS_OP_TYPE_NULL; + } +} + +static ut8 c55x_insn_len(const ut8 *buf, int len) { + const int sz = c55x_op_size(buf, len); + return (sz > 0) ? (ut8)sz : 0; +} + +// --- dual "::" MAC operand filling --------------------------------------- +// One Xmem / Ymem operand of a dual MAC: a 3-bit ARn selector and a 3-bit +// addressing mode. Unlike the single-data Smem 4-bit mode, the dual modes are +// 0:*ARn 1:*ARn+ 2:*ARn- 3:*(ARn+T0) 4:*(ARn+T1) 5:*(ARn-T0) 6:*(ARn-T1) +// 7:*ARn(T0); the uns() wrapper (zero-extend on load) is shared with this +// sub-MAC's Cmem coefficient. +static void c55x_dual_mem(C55Operand *out, ut8 ar, ut8 mode, bool uns) { + memset(out, 0, sizeof(*out)); + out->kind = C55_OP_MEM; + out->access = 16; + out->uns = uns; + c55x_gr4((ut8)(8 + (ar & 7)), &out->reg); // base ARn + switch (mode & 7) { + case 0: out->amode = C55_AM_INDIRECT; break; + case 1: out->amode = C55_AM_POSTINC; break; + case 2: out->amode = C55_AM_POSTDEC; break; + case 3: out->amode = C55_AM_POSTADD; c55x_gr4(4, &out->index); break; // *(ARn+T0) + case 4: out->amode = C55_AM_POSTADD; c55x_gr4(5, &out->index); break; // *(ARn+T1) + case 5: out->amode = C55_AM_POSTSUB; c55x_gr4(4, &out->index); break; // *(ARn-T0) + case 6: out->amode = C55_AM_POSTSUB; c55x_gr4(5, &out->index); break; // *(ARn-T1) + default: out->amode = C55_AM_IDXREG; c55x_gr4(4, &out->index); break; // *ARn(T0) + } +} + +// The shared Cmem coefficient (*CDP with the same post-modify mm as the single +// MACs); each sub-MAC reads it with its own uns() signedness. +static void c55x_dual_cmem(C55Operand *out, ut8 cmode, bool uns) { + memset(out, 0, sizeof(*out)); + out->kind = C55_OP_MEM; + out->reg.cls = C55_RC_CDP; + out->access = 16; + out->uns = uns; + switch (cmode & 3) { + case 0: out->amode = C55_AM_INDIRECT; break; + case 1: out->amode = C55_AM_POSTINC; break; + case 2: out->amode = C55_AM_POSTDEC; break; + default: // *(CDP+T0) + out->amode = C55_AM_POSTADD; + out->index.cls = C55_RC_T; + out->index.num = 0; + break; + } +} + +static void c55x_dual_ac(const C55ArchDesc *a, C55Operand *out, ut8 num) { + memset(out, 0, sizeof(*out)); + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_AC; + out->reg.num = (ut8)(num & 3); + const C55RegInfo *ri = a->reg_info ? a->reg_info(out->reg.cls, out->reg.num, C55_SUB_NONE) : NULL; + out->width = ri ? ri->width : 0; +} + +// Fill the canonical dual-MAC operand layout from the 4-byte word. Byte1 carries +// the Xmem ARn (bits 5-7) and mode (bits 2-4) and the Ymem ARn low bits (0-1); +// byte2 the Cmem mode (bits 0-1), the op selector (bits 2-3, already matched), +// the Ymem mode (bits 4-6) and the Ymem ARn high bit (bit 7); byte3 the round R +// (bit 0), the 40-bit flag (bit 1), ACx (bits 2-3), ACy (bits 4-5) and the per- +// sub uns flags (bit 6 = sub2/Ymem, bit 7 = sub1/Xmem). An amar sub1 has no Cmem +// or destination, and the single accumulator field then names sub2's destination. +static bool c55x_fill_dual(const C55ArchDesc *a, ut64 bits, const C55InsnDef *def, C55Insn *out) { + const ut8 b1 = (ut8)((bits >> 16) & 0xff); + const ut8 b2 = (ut8)((bits >> 8) & 0xff); + const ut8 b3 = (ut8)(bits & 0xff); + out->dual = true; + out->lop2 = def->lop2; + out->amar1 = def->amar1; + out->shift1 = def->shift1; + out->shift2 = def->shift2; + out->round = b3 & 1; + out->m40 = (b3 >> 1) & 1; + const bool uns_y = (b3 >> 6) & 1; + const bool uns_x = (b3 >> 7) & 1; + const ut8 acx = (ut8)((b3 >> 2) & 3); + const ut8 acy = (ut8)((b3 >> 4) & 3); + c55x_dual_mem(&out->ops[0], (ut8)((b1 >> 5) & 7), (ut8)((b1 >> 2) & 7), uns_x); // Xmem + c55x_dual_cmem(&out->ops[1], (ut8)(b2 & 3), uns_x); // Cmem (sub1) + c55x_dual_ac(a, &out->ops[2], acx); // ACx + c55x_dual_mem(&out->ops[3], (ut8)(((b1 & 3) << 1) | ((b2 >> 7) & 1)), (ut8)((b2 >> 4) & 7), uns_y); // Ymem + c55x_dual_cmem(&out->ops[4], (ut8)(b2 & 3), uns_y); // Cmem (sub2) + c55x_dual_ac(a, &out->ops[5], acy); // ACy + if (def->amar1) { + // amar sub1 has no Cmem / destination and renders no uns() wrapper; the + // single accumulator field (ACx slot) is sub2's destination, and sub2's + // uns comes from the high uu bit (uns_x); the low bit is unused here. + out->ops[0].uns = false; + out->ops[1].kind = C55_OP_NONE; + out->ops[2].kind = C55_OP_NONE; + out->ops[3].uns = uns_x; + out->ops[4].uns = uns_x; + c55x_dual_ac(a, &out->ops[5], acx); // single dst -> sub2 + } + out->n_ops = 6; + return true; +} + +// Operand extractors for the triple-register amar (amar Xmem, Ymem, Cmem): +// the same Xmem / Ymem / Cmem fields as the dual MACs, but as three plain +// address-modify operands (no uns, no destination). +static void c55x_x_dual_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 16) & 0xff); + c55x_dual_mem(out, (ut8)((b1 >> 5) & 7), (ut8)((b1 >> 2) & 7), false); +} +static void c55x_x_dual_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 16) & 0xff); + const ut8 b2 = (ut8)((bits >> 8) & 0xff); + c55x_dual_mem(out, (ut8)(((b1 & 3) << 1) | ((b2 >> 7) & 1)), (ut8)((b2 >> 4) & 7), false); +} +static void c55x_x_dual_cmem3(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + c55x_dual_cmem(out, (ut8)((bits >> 8) & 3), false); +} + +// Dual-memory Xmem/Ymem fields for the 3-byte mov / add / sub Xmem,Ymem forms +// (opcodes 0x80 / 0x81). Unlike the 4-byte 0x82-0x86 MAC family (where the +// fields sit in byte1/byte2), here byte1 = XXXMMMYY and byte2 = YMMM00xx, one +// byte earlier in the packed word -- so read byte1 = bits[15:8], byte2 = +// bits[7:0]. Xmem: ARn = byte1[7:5], mode = byte1[4:2]. Ymem: ARn = +// (byte1[1:0]<<1)|byte2[7], mode = byte2[6:4]. +static void c55x_x_dual_xmem3(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 8) & 0xff); + c55x_dual_mem(out, (ut8)((b1 >> 5) & 7), (ut8)((b1 >> 2) & 7), false); +} +static void c55x_x_dual_ymem3(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 8) & 0xff); + const ut8 b2 = (ut8)(bits & 0xff); + c55x_dual_mem(out, (ut8)(((b1 & 3) << 1) | ((b2 >> 7) & 1)), (ut8)((b2 >> 4) & 7), false); +} +// dbl() variants: the "mov dbl(Xmem), dbl(Ymem)" form moves a 32-bit long word. +static void c55x_x_dual_xmem3_dbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_dual_xmem3(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->dbl = true; + out->access = 32; + } +} +static void c55x_x_dual_ymem3_dbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55x_x_dual_ymem3(a, bits, d, out); + if (out->kind == C55_OP_MEM) { + out->dbl = true; + out->access = 32; + } +} + +// 0x86 dual-multiply (mpym / macm / masm Xmem, Ymem, ...): Xmem and Ymem share +// the 0x82-0x85 byte1/byte2 addressing-field layout, but the per-operand uns() +// qualifiers live in the last byte (bit 3 = Xmem, bit 2 = Ymem) and the source / +// destination accumulators in byte 2 (DD = bits 0-1, SS = bits 2-3). +static void c55x_x_xymac_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 16) & 0xff); + c55x_dual_mem(out, (ut8)((b1 >> 5) & 7), (ut8)((b1 >> 2) & 7), (bool)((bits >> 3) & 1)); +} +static void c55x_x_xymac_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + const ut8 b1 = (ut8)((bits >> 16) & 0xff); + const ut8 b2 = (ut8)((bits >> 8) & 0xff); + c55x_dual_mem(out, (ut8)(((b1 & 3) << 1) | ((b2 >> 7) & 1)), (ut8)((b2 >> 4) & 7), (bool)((bits >> 2) & 1)); +} +// Source accumulator of the dual-multiply MACs (macm / masm Xmem, Ymem, ACx, ACy): +// SS is byte2 bits 2-3, DD byte2 bits 0-1. When the two coincide the instruction +// is the two-operand form (ACy += Xmem*Ymem) and the explicit source slot is +// dropped (the generic ops[] loop compacts the NONE slot away). +static void c55x_x_xymac_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 dd = (ut8)((bits >> 8) & 3); + ut8 ss = (ut8)((bits >> 10) & 3); + if (ss == dd) { + out->kind = C55_OP_NONE; + return; + } + c55x_dual_ac(a, out, ss); +} + +static const C55InsnDef c55x_table[] = { + // nop: single-byte 0x20 (shared encoding with C55x+). + { .mask = 0xfe000000, .match = 0x20000000, .id = TMS320C55_INS_NOP }, + // mov src, dst (opcode 0x22): the operand byte's high nibble selects the + // source and the low nibble the destination, each a 4-bit register field + // (AC0-3 / T0-3 / AR0-7). ops[0]=src, ops[1]=dst per the shared lifter. + { .mask = 0xfe000000, .match = 0x22000000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + // add / sub src, dst (opcodes 0x24 / 0x26): same operand byte as mov (high + // nibble source, low nibble destination). dst = dst src; the + // destination is elided when it equals the source (the legacy single-operand + // "add ac0" form). + { .mask = 0xfe000000, .match = 0x24000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_AREG_ADD, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + { .mask = 0xfe000000, .match = 0x26000000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_AREG_SUB, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + // and / or / xor src, dst (opcodes 0x28 / 0x2a / 0x2c): same operand byte + // as mov (high nibble source, low nibble destination). dst = dst src + // via the shared lifter (equal-width forms; mixed-width forms fall back). + { .mask = 0xfe000000, .match = 0x28000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_AREG_AND, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x2a000000, .id = TMS320C55_INS_OR, .lop = C55_LOP_AREG_OR, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x2c000000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_AREG_XOR, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + // not src[, dst] (opcode 0x36): dst = ~src. The source is optional in the + // short form; the destination collapses against it (elided when equal). + { .mask = 0xfe000000, .match = 0x36000000, .id = TMS320C55_INS_NOT, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + // neg src[, dst] (opcode 0x34): dst = -src. Optional-source short form; the + // destination collapses against the source (elided when equal). The lifter + // uses C55_LOP_NEG since neg shares the SUB op type. + { .mask = 0xfe000000, .match = 0x34000000, .id = TMS320C55_INS_NEG, .lop = C55_LOP_NEG, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + // max / min src[, dst] (opcodes 0x2e / 0x30): dst = max/min(src, dst). + // Optional-source short form (destination elided when equal to the source); + // the lifter emits the ite via C55_LOP_MAX / _MIN since both share CMP. + { .mask = 0xfe000000, .match = 0x2e000000, .id = TMS320C55_INS_MAX, .lop = C55_LOP_MAX, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + { .mask = 0xfe000000, .match = 0x30000000, .id = TMS320C55_INS_MIN, .lop = C55_LOP_MIN, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + // abs src[, dst] (opcode 0x32): dst = |src|. Optional-source short form (the + // destination is elided when equal to the source). abs has no RzAnalysis op + // type at all (the legacy analysis reports type null), so it reaches the + // shared path via its lift-op rather than its op type; the lifter emits the + // ite through C55_LOP_ABS. + { .mask = 0xfe000000, .match = 0x32000000, .id = TMS320C55_INS_ABS, .lop = C55_LOP_ABS, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4_elide } } }, + // mov k4, dst (opcode 0x3c): 4-bit unsigned immediate (operand byte's high + // nibble) into the register selected by the low nibble. dst = k4, the + // immediate zero-extended to the destination width by the shared lifter. + { .mask = 0xfe000000, .match = 0x3c000000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + // mov -#k, dst (opcode 0x3e): like mov k4 but the high nibble is a negated + // magnitude (-0 .. -15). The legacy leaves it unlifted, so the shared MOV + // lifter declines it (see c55x_x_negk4 / the neg_imm guard). + { .mask = 0xfe000000, .match = 0x3e000000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55x_x_negk4 }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + // mov #k12, (opcode 0x16): a 12-bit immediate (bytes 1-2, bits 12-23) + // into a control register selected by byte2's low nibble (dph/pdp/bk03/bk47/ + // bkc/csr/brc0/brc1). The immediate is masked to the register width; the + // shared MOV lifter emits "reg = k". + { .mask = 0xfe000000, .match = 0x16000000, .id = TMS320C55_INS_MOV, .len = 3, + .ops = { { .lo = 4, .width = 12, .fn = c55x_x_16imm }, { .lo = 0, .width = 4, .fn = c55x_x_16dst } } }, + // mov #k16, (opcode 0x78): a 16-bit immediate (instruction bytes 1-2) + // into a control register selected by byte3 (see c55x_x_78dst). dst = k16, + // the immediate zero-extended to the dst width by the shared MOV lifter. + { .mask = 0xff000000, .match = 0x78000000, .id = TMS320C55_INS_MOV, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 1, .width = 4, .fn = c55x_x_78dst } } }, + // ACy = ACx (#k16 << #sh) (opcodes 0x70 add, 0x71 sub, 0x72 and, + // 0x73 or, 0x74 xor): a 16-bit immediate (bytes 1-2) shifted up by byte3 + // bits 0-3 (0-15), combined with ACx into ACy. byte3 bits 6-7 select ACx, + // bits 4-5 ACy (elided when equal to ACx). The 0x75 mov form is left to the + // legacy decoder (it carries no IL there). + { .mask = 0xff000000, .match = 0x70000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000000, .match = 0x71000000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000000, .match = 0x72000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000000, .match = 0x73000000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000000, .match = 0x74000000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + // mov #k16 << #sh, ACy (opcode 0x75): the immediate shifted into ACy; no + // source accumulator and (matching the legacy) no IL -- the MOV lifter + // declines the variable-shift form, leaving it unlifted. + { .mask = 0xff000000, .match = 0x75000000, .id = TMS320C55_INS_MOV, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_varsh }, { .lo = 4, .fn = c55x_x_ac2 } } }, + // ACy = ACx (#k16 << #16) (opcode 0x7a): a 16-bit immediate (bytes 1-2) + // shifted up 16 bits, combined with ACx into ACy. byte3 bits 1-3 select the + // operation (0 add, 1 sub, 2 and, 3 or, 4 xor; 5 mov / 6 idle / 7 invalid + // are left to the legacy decoder), bits 6-7 ACx, bits 4-5 ACy (elided when + // equal to ACx); byte3 bit 0 is don't-care. + { .mask = 0xff00000e, .match = 0x7a000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff00000e, .match = 0x7a000002, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff00000e, .match = 0x7a000004, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff00000e, .match = 0x7a000006, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff00000e, .match = 0x7a000008, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORSHL, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + // mov #k16 << #16, ACy (opcode 0x7a, byte3 selector 5): the immediate (sign- + // extended, shifted up 16) loaded into ACy; there is no source accumulator. + { .mask = 0xff00000e, .match = 0x7a00000a, .id = TMS320C55_INS_MOV, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55x_x_imm_sh16 }, { .lo = 4, .fn = c55x_x_ac2 } } }, + // dst = ACx zero-extend(#k16) (opcodes 0x7b add, 0x7c sub, 0x7d and, + // 0x7e or, 0x7f xor): a 16-bit immediate (bytes 1-2), the source accumulator + // in byte3 bits 0-3 (gr4) and the destination in bits 4-7 (gr4). add / sub + // collapse the destination when it equals the source ("add #k, ACx"); the + // bitwise forms always render all three operands. The lifter handles an + // accumulator source and (matching the legacy) leaves a T / AR source + // unlifted; a 16-bit destination truncates the 40-bit result. + { .mask = 0xff000000, .match = 0x7b000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDK, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4_elide } } }, + { .mask = 0xff000000, .match = 0x7c000000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBK, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4_elide } } }, + { .mask = 0xff000000, .match = 0x7d000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDK, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000000, .match = 0x7e000000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORK, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000000, .match = 0x7f000000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORK, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + // dst = ACx zero-extend(#k8) (opcodes 0x18 and, 0x1a or, 0x1c xor; the + // parallel "|| " encodings 0x19 / 0x1b / 0x1d differ only in byte0 bit 0, so + // the mask leaves that bit to the parallel-bit derivation): an 8-bit + // immediate (byte1), the gr4 source in byte2 bits 0-3 and the gr4 destination + // in bits 4-7. The same immediate-ALU lifter handles an accumulator source + // and (matching the legacy) leaves a T / AR source unlifted; a 16-bit T / AR + // destination truncates the result. + { .mask = 0xfe000000, .match = 0x18000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDK, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x1a000000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORK, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x1c000000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORK, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + // add / sub k4, dst (opcodes 0x40 / 0x42): same immediate layout as mov k4; + // dst = dst +/- k4 with the immediate zero-extended to the dst width. + { .mask = 0xfe000000, .match = 0x40000000, .id = TMS320C55_INS_ADD, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x42000000, .id = TMS320C55_INS_SUB, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 0, .width = 4, .fn = c55x_x_gr4 } } }, + // mov Smem, dst load (opcodes 0xa0-0xaf): the destination register is gr4 + // of the opcode's low nibble (AC0-3 / T0-3 / AR0-7) and the source is a + // single data-memory operand. Only the register-modify addressing modes the + // shared effective-address / memory primitives handle are decoded; the rest + // fall back to the legacy decoder via c55x_x_smem. The low nibble is the + // destination register, not the parallel marker, so the row opts out of the + // parallel-bit derivation (no_parallel). + { .mask = 0xf0000000, .match = 0xa0000000, .id = TMS320C55_INS_MOV, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem }, { .lo = 8, .width = 4, .fn = c55x_x_gr4 } } }, + // mov reg, Smem store (opcodes 0xc0-0xcf): the direction mirror of the load. + // The source register is gr4 of the opcode's low nibble and the destination + // is a single data-memory operand; a wider accumulator source is truncated + // to the 16-bit access by the shared memory primitives. Same addressing-mode + // coverage and no_parallel rationale as the load row. + { .mask = 0xf0000000, .match = 0xc0000000, .id = TMS320C55_INS_MOV, .len = 2, .no_parallel = true, + .ops = { { .lo = 8, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + // mov hi(ACx), Smem store (opcodes 0xbc-0xbf): stores the high half of the + // accumulator (ACx.h, accumulator number in the low two bits) to a single + // data-memory operand. The shared half-source read produces the 16-bit high + // half, stored through the same primitives. Same Smem coverage as the load + // and store rows; the low two bits are the accumulator field, not the + // parallel marker (no_parallel). + { .mask = 0xfc000000, .match = 0xbc000000, .id = TMS320C55_INS_MOV, .len = 2, .no_parallel = true, + .ops = { { .lo = 8, .width = 2, .fn = c55x_x_achi }, { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + // mov Smem << #16, ACx (opcodes 0xb0-0xb3): loads a single data-memory + // operand shifted left by 16 into an accumulator (number in the low two + // bits). The shift is rendered but the load is not yet lifted (matching the + // legacy decoder's empty IL); the low two bits are the accumulator field, + // not the parallel marker (no_parallel). + { .mask = 0xfc000000, .match = 0xb0000000, .id = TMS320C55_INS_MOV, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem_sh16 }, { .lo = 8, .width = 2, .fn = c55x_x_ac2 } } }, + // amar Smem (opcode 0xb4): modify auxiliary register -- computes the + // effective address and applies the addressing mode's post-modify side + // effect without accessing memory. Single Smem operand (the low byte is the + // addressing field, not the parallel marker -> no_parallel). It lifts to + // that side effect, or a nop for non-modifying modes, and is typed LEA. + { .mask = 0xff000000, .match = 0xb4000000, .id = TMS320C55_INS_AMAR, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + // amar Smem, xar (opcode 0xec): load the effective (word) address of the + // single-data-memory operand into an extended pointer register, with no + // memory access. byte1 is the Smem field; byte2's high nibble selects the + // XARn destination (c55x_x_gr4a maps 8..15 -> xar0..7) and its low nibble + // 0xe is the form marker (mask requires it, distinguishing this from the + // other 0xec sub-opcodes). The shared amar lifter loads base+offset into + // the destination for the SP/AR-relative modes; the *(k23) absolute sub- + // form (Smem mode-8 base-1) is left invalid above and falls to the legacy + // decoder, which sizes it correctly. + { .mask = 0xff000f00, .match = 0xec000e00, .id = TMS320C55_INS_AMAR, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 4, .width = 4, .fn = c55x_x_gr4a } } }, + // dbl(Lmem) 32-bit load / store / accumulate (opcodes 0xed load, 0xeb store). + // These are 3-byte forms whose last byte (the packed value's low byte) + // selects the sub-form: bits 3-1 == 100 is "mov dbl(Lmem), ACx" (DD = ACx in + // bits 5-4); bits 3-1 == 000/001 are "add/sub dbl(Lmem), [ACx,] ACy" (DD = dst + // ACy in bits 5-4, SS = optional src ACx in bits 7-6, suppressed when equal); + // and bits 3-0 == 1111 is "mov dbl(Lmem), XAdst" (the extended pointer in + // bits 7-4). The store opcode's "mov ACx, dbl(Lmem)" has bits 3,2,0 == 1,0,0 + // with SS = ACx in bits 5-4. The Lmem field is byte1; its *(k23) absolute + // sub-mode supplies the true k24 address (the legacy decoder rendered it + // wrongly). The dbl moves lift through the shared 32-bit memory mover; add/sub + // fall back to the legacy lifter. + { .mask = 0xff000e00, .match = 0xed000800, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_dbl }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000f00, .match = 0xed000f00, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_dbl }, { .lo = 4, .width = 4, .fn = c55x_x_gr4a } } }, + { .mask = 0xff000e00, .match = 0xed000000, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_dbl }, { .lo = 6, .width = 2, .fn = c55x_x_ac_src2 }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000e00, .match = 0xed000200, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_dbl }, { .lo = 6, .width = 2, .fn = c55x_x_ac_src2 }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000d00, .match = 0xeb000800, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 8, .width = 8, .fn = c55x_x_smem_dbl } } }, + // delay Smem (opcode 0xb6): memory delay -- copies the addressed word to + // the next higher data address (TI SWPU104 6.7.1). Single Smem operand (the + // low byte is the addressing field, so no_parallel; note 0xb7 is a distinct + // opcode, psh dbl). Typed MOVE via c55x_op_type; the side effect is not + // modelled (OPAQUE -> empty IL, matching the legacy lifter). + { .mask = 0xff000000, .match = 0xb6000000, .id = TMS320C55_INS_DELAY, .lop = C55_LOP_OPAQUE, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + // psh Smem (opcode 0xb5) / pop Smem (opcode 0xbb) push / pop a single + // data-memory word; psh dbl(Smem) (0xb7) / pop dbl(Smem) (0xb8) push / pop a + // double word. Single Smem operand (the low byte is the addressing field, + // not the parallel marker -> no_parallel); the analysis stack effect (one + // word for the single forms, two for the dbl forms) comes from the PUSH / + // POP op-type, and the IL is left to the legacy lifter (empty). Only the + // duplicate 0xb9 pop dbl now stays on the legacy decoder. + { .mask = 0xff000000, .match = 0xb5000000, .id = TMS320C55_INS_PSH, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000000, .match = 0xbb000000, .id = TMS320C55_INS_POP, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000000, .match = 0xb7000000, .id = TMS320C55_INS_PSH, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem_dbl } } }, + { .mask = 0xff000000, .match = 0xb8000000, .id = TMS320C55_INS_POP, .len = 2, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55x_x_smem_dbl } } }, + // psh / pop src1, src2 (opcode 0x38 psh, 0x3a pop; 0x39 / 0x3b parallel): push + // or pop two independent gr4 registers in one instruction (byte1 bits 4-7 name + // the first register, bits 0-3 the second). The two-register sequence is not + // modelled by the shared push/pop lifter -- the .both flag leaves it to the + // legacy lifter -- and the analysis records a fixed one-word SP delta (the + // dual-form branch in c55_stack_words). + { .mask = 0xfe000000, .match = 0x38000000, .id = TMS320C55_INS_PSH, .len = 2, .both = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 0, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe000000, .match = 0x3a000000, .id = TMS320C55_INS_POP, .len = 2, .both = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 0, .fn = c55x_x_gr4 } } }, + // 0x54 register D-unit ALU family (two bytes): "[r] [ACx,] ACy", a 3-bit + // sub-opcode in the low byte's bits 1-3 selects the operation, bit 0 is the + // rounding variant, ACx is bits 4-5 and the destination ACy is bits 6-7; the + // leading byte's bit 0 is the parallel marker (left free). ACx collapses + // against ACy when they are equal (the multiply/square then operates on ACy + // alone -- handled in the MUL lift). The multiply / square members are lifted + // here; addv (0), round (5) and sat (6) stay on the legacy decoder. The + // accumulator multiplicand is the high word ACx(32-16) (see c55_mul_val). + // mov hi(ACx), dst (opcode 0x44, high nibble 0-3): move an accumulator's + // 16-bit high word, sign-extended, into the gr4 destination (low nibble). + // 0x45 is the parallel form. The source ACx is byte1 bits 4-5. + { .mask = 0xfec00000, .match = 0x44000000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_hi_ac }, { .lo = 0, .fn = c55x_x_gr4 } } }, + // sfts dst, #1 / #-1 (opcode 0x44, high nibble 4-7): a fixed +-1 arithmetic + // shift of the gr4 register (low nibble). byte1 bit 4 selects the sign; the + // shared SFTS handler lifts the accumulator forms (left for +1, arithmetic + // right for -1). + { .mask = 0xfec00000, .match = 0x44400000, .id = TMS320C55_INS_SFTS, .lop = C55_LOP_SFTS, .len = 2, + .ops = { { .lo = 0, .fn = c55x_x_gr4 }, { .fn = c55x_x_sfts_imm } } }, + // aadd #k8, sp (opcode 0x4e, 0x4f parallel): add a signed 8-bit constant to + // the stack pointer (frame setup). k8 prints unsigned but is signed; the + // shared ADD lifter adds it zero-extended (matching the legacy IL) and the + // analysis records the signed SP delta. + { .mask = 0xfe000000, .match = 0x4e000000, .id = TMS320C55_INS_AADD, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm }, { .fn = c55x_x_sp } } }, + // mov gr4, hi(ACx) (opcode 0x52, 0x53 parallel): write a general register's + // low 16 bits into an accumulator's high word. Byte-1 high nibble selects + // the source (gr4: AC/T/AR), low nibble 0-3 selects the destination hi(AC0-3). + // The special-register destinations (low nibble >= 8) are left to the legacy. + { .mask = 0xfe0c0000, .match = 0x52000000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 0, .fn = c55x_x_hi_ac } } }, + // mov gr4, (opcode 0x52, low nibble 8-15): write a general + // register into sp/ssp/cdp/csr/brc1/brc0. An accumulator source is narrowed + // to the 16-bit special register by the shared MOV lifter. + { .mask = 0xfe080000, .match = 0x52080000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 0, .fn = c55x_x_52dst } } }, + // swap rX, rY (opcode 0x5e, 0x5f parallel): exchange a register pair via the + // XOR-swap idiom. Byte-1 bit 4 = 0 single, = 1 swapp (also swaps the next + // pair); bit 5 must be 0; bits 6-7 are ignored. + { .mask = 0xfe300000, .match = 0x5e000000, .id = TMS320C55_INS_SWAP, .len = 2, + .ops = { { .lo = 0, .param = 0, .fn = c55x_x_swap }, { .lo = 0, .param = 1, .fn = c55x_x_swap } } }, + { .mask = 0xfe300000, .match = 0x5e100000, .id = TMS320C55_INS_SWAPP, .both = true, .len = 2, + .ops = { { .lo = 0, .param = 0, .fn = c55x_x_swap }, { .lo = 0, .param = 1, .fn = c55x_x_swap } } }, + { .mask = 0xfe300000, .match = 0x5e200000, .id = TMS320C55_INS_SWAP4, .quad = true, .len = 2, + .ops = { { .lo = 0, .param = 0, .fn = c55x_x_swap }, { .lo = 0, .param = 1, .fn = c55x_x_swap } } }, + // rpt / rptadd / rptsub (opcode 0x48, 0x49 parallel): single-instruction + // repeat control over the CSR counter. byte1 low nibble selects the form + // (0 rpt, 1 rptadd CSR/TAx, 2 rptadd CSR/k4, 3 rptsub CSR/k4); ret/reti at + // nibble 4/5 are decoded elsewhere. The operand register/immediate is byte1 + // bits 4-7. + { .mask = 0xfe0f0000, .match = 0x48000000, .id = TMS320C55_INS_RPT, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55x_x_csr } } }, + { .mask = 0xfe0f0000, .match = 0x48010000, .id = TMS320C55_INS_RPTADD, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55x_x_csr }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe0f0000, .match = 0x48020000, .id = TMS320C55_INS_RPTADD, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55x_x_csr }, { .lo = 4, .width = 4, .fn = c55_x_imm } } }, + { .mask = 0xfe0f0000, .match = 0x48030000, .id = TMS320C55_INS_RPTSUB, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55x_x_csr }, { .lo = 4, .width = 4, .fn = c55_x_imm } } }, + // rpt #k16 (opcode 0x0c, 0x0d parallel): repeat the next instruction by a + // 16-bit immediate count (byte1 the high byte, byte2 the low byte). It has no + // data effect of its own, so it lifts to nop like the other repeat forms. + { .mask = 0xfe000000, .match = 0x0c000000, .id = TMS320C55_INS_RPT, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm } } }, + // rptcc K8, cond (opcode 0x00, 0x01 parallel): conditionally repeat the next + // instruction K8 times. byte2 is the count, byte1 the 7-bit condition. + { .mask = 0xfe000000, .match = 0x00000000, .id = TMS320C55_INS_RPTCC, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm }, { .lo = 8, .width = 7, .fn = c55x_x_cond } } }, + // rptb L16 (opcode 0x0e, 0x0f parallel): block repeat to the 16-bit end + // address held in byte1:byte2. + { .mask = 0xfe000000, .match = 0x0e000000, .id = TMS320C55_INS_RPTB, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm } } }, + // amov / asub ACx, ACy register forms (opcode 0x14, 0x15 parallel): byte-2 + // bits 0-1 select the operation (00 aadd, 01 amov, 10 asub) and bit 2 picks + // the register (0) vs immediate (1) source. Only the register amov/asub are + // taken here (aadd's legacy LEA type would change under the shared AADD=ADD, + // and the immediate forms stay on legacy). src is byte1 bits 4-7, dst byte2 + // bits 4-7. asub keeps the legacy null type; its IL comes from C55_LOP_AREG. + { .mask = 0xfe000700, .match = 0x14000100, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AREG_MOV, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_gr4a }, { .lo = 4, .fn = c55x_x_gr4a } } }, + { .mask = 0xfe000700, .match = 0x14000200, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_gr4a }, { .lo = 4, .fn = c55x_x_gr4a } } }, + // cmp / cmpand / cmpor SRC DST, [TCx,] TCz (opcode 0x12, 0x13 + // parallel): compare two gr4 registers and write a status TC bit. Byte-1 + // bits 0-1 select the variant (00 cmp, 01 cmpand, 10 cmpor; 11 is rol, left + // to legacy); bits 2-3 the relop; bits 4-7 SRC. Byte-2 bit 0 = TCz, bit 1 = + // TCx, bit 2 = unsigned (the 'u' variants), bits 4-7 = DST. + { .mask = 0xfe030400, .match = 0x12000000, .id = TMS320C55_INS_CMP, .lop = C55_LOP_CMP, .len = 3, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 0, .fn = c55x_x_tcflag } } }, + { .mask = 0xfe030400, .match = 0x12000400, .id = TMS320C55_INS_CMP, .lop = C55_LOP_CMP, .len = 3, .uns_all = true, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 0, .fn = c55x_x_tcflag } } }, + { .mask = 0xfe030400, .match = 0x12010000, .id = TMS320C55_INS_CMPAND, .lop = C55_LOP_CMPAND, .len = 3, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 1, .param = 3, .fn = c55x_x_tcflag }, { .lo = 0, .fn = c55x_x_tcflag } } }, + { .mask = 0xfe030400, .match = 0x12010400, .id = TMS320C55_INS_CMPAND, .lop = C55_LOP_CMPAND, .len = 3, .uns_all = true, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 1, .param = 3, .fn = c55x_x_tcflag }, { .lo = 0, .fn = c55x_x_tcflag } } }, + { .mask = 0xfe030400, .match = 0x12020000, .id = TMS320C55_INS_CMPOR, .lop = C55_LOP_CMPOR, .len = 3, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 1, .param = 3, .fn = c55x_x_tcflag }, { .lo = 0, .fn = c55x_x_tcflag } } }, + { .mask = 0xfe030400, .match = 0x12020400, .id = TMS320C55_INS_CMPOR, .lop = C55_LOP_CMPOR, .len = 3, .uns_all = true, + .ops = { { .fn = c55x_x_cmpcond }, { .lo = 1, .param = 3, .fn = c55x_x_tcflag }, { .lo = 0, .fn = c55x_x_tcflag } } }, + // rol / ror BitIn, ACx, BitOut, ACy (opcode 0x12, byte1 bits 0-1 = 11): + // rotate the accumulator left/right by one through a status bit. byte2 bit 3 + // selects ror (1) over rol (0); SRC is byte1 bits 4-7, DST byte2 bits 4-7. + // For rol, byte2 bit 0 is the rotate-in bit and bit 1 the rotate-out bit; + // the legacy swaps these for ror (bit 1 = rotate-in, bit 0 = rotate-out). + // Each bit picks carry (0) or tc2 (1). The legacy models the data effect only + // for accumulator src+dst, so T/AR forms fall through to a null lift. + { .mask = 0xfe030800, .match = 0x12030000, .id = TMS320C55_INS_ROL, .lop = C55_LOP_ROL, .len = 3, + .ops = { { .lo = 0, .fn = c55x_x_rolflag }, { .lo = 12, .width = 4, .fn = c55x_x_gr4 }, { .lo = 1, .fn = c55x_x_rolflag }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe030800, .match = 0x12030800, .id = TMS320C55_INS_ROR, .lop = C55_LOP_ROR, .len = 3, + .ops = { { .lo = 1, .fn = c55x_x_rolflag }, { .lo = 12, .width = 4, .fn = c55x_x_gr4 }, { .lo = 0, .fn = c55x_x_rolflag }, { .lo = 4, .width = 4, .fn = c55x_x_gr4 } } }, + // bfxtr / bfxpa K16, ACx, ACy (opcode 0x76): bit-field extract / expand. + // bfxtr extracts the bits of ACx selected by the K16 mask and right-packs + // them into ACy; bfxpa is the inverse (expand-and-pack). 0x76 is multi-form + // -- byte3 bits 2-3 select the operation (0 bfxtr, 1 bfxpa; 2 is a mov form + // and 3 is invalid, both left to the legacy decoder). K16 is bytes 1-2, the + // AC source is byte3 bits 0-1, and the gr4 destination is byte3 bits 4-7. + // bfxtr is typed MOV (a field move); bfxpa is untyped, as the legacy did. + // Neither is lifted (OPAQUE). + { .mask = 0xff00000c, .match = 0x76000000, .id = TMS320C55_INS_BFXTR, .lop = C55_LOP_OPAQUE, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff00000c, .match = 0x76000004, .id = TMS320C55_INS_BFXPA, .lop = C55_LOP_OPAQUE, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 0, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // amov #k16, dst (opcode 0x77, no parallel): load a zero-extended 16-bit + // constant/address. k16 is bits 8-23 (byte1:byte2); the gr4 destination + // selector is byte3 bits 4-7 (0-3 AC, 4-7 T, 8-15 AR). + { .mask = 0xff000000, .match = 0x77000000, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AMOV, .len = 4, .no_parallel = true, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // mpyk / mpykr #k, ACx[, ACy] (opcodes 0x1e 3-byte K8, 0x79 4-byte K16; + // 0x1e has a parallel companion 0x1f rendered "|| mpyk"): ACy = #k * ACx, + // the signed constant multiplying the low 16 bits of ACx. The variant byte + // (byte2 for 0x1e, byte3 for 0x79) holds ACx in bits 6-7, ACy in bits 4-5, + // bit 1 = 0 selecting the mpyk family (1 = mack), and bit 0 = the round (r) + // suffix (read via mods). ACy defaults to ACx (elided when equal). The + // 4-byte 0x79 has no parallel form (0x78 is a different opcode), so it pins + // byte0 fully. + { .mask = 0xfe000200, .match = 0x1e000000, .id = TMS320C55_INS_MPYK, .lop = C55_LOP_MPYK, .len = 3, .mods = 1, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000002, .match = 0x79000000, .id = TMS320C55_INS_MPYK, .lop = C55_LOP_MPYK, .len = 4, .no_parallel = true, .mods = 1, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + // mack / mackr Tx, #k, ACx[, ACy] (variant byte bit 1 = 1): ACy = ACx + #k * + // Tx. Same encoding as mpyk but the Tx coefficient (variant-byte bits 2-3) + // replaces the accumulator low word as the multiplicand; bit 0 = the round + // (r) suffix. ACy defaults to ACx (elided when equal). + { .mask = 0xfe000200, .match = 0x1e000200, .id = TMS320C55_INS_MACK, .lop = C55_LOP_MACK, .len = 3, .mods = 1, + .ops = { { .lo = 2, .fn = c55x_x_t2 }, { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + { .mask = 0xff000002, .match = 0x79000002, .id = TMS320C55_INS_MACK, .lop = C55_LOP_MACK, .len = 4, .no_parallel = true, .mods = 1, + .ops = { { .lo = 2, .fn = c55x_x_t2 }, { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 6, .fn = c55x_x_ac2 }, { .lo = 4, .fn = c55x_x_ac2_elide } } }, + // bclr / bset #k4, STx (opcode 0x46, 0x47 parallel): clear or set bit #k4 of + // a status register. Byte-1 bit 0 = 0 bclr, = 1 bset; bits 1-3 = STn_55; + // bits 4-7 = the 4-bit bit index #k4. + { .mask = 0xfe010000, .match = 0x46000000, .id = TMS320C55_INS_BCLR, .lop = C55_LOP_BITCLR, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 1, .fn = c55x_x_st } } }, + { .mask = 0xfe010000, .match = 0x46010000, .id = TMS320C55_INS_BSET, .lop = C55_LOP_BITSET, .len = 2, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 1, .fn = c55x_x_st } } }, + // add/sub ACx << Tx, ACy (opcode 0x5a, 0x5b parallel): ACy +/- (ACx shifted + // left by the Tx register). Byte-1 bit 0 selects add (0) vs sub (1); bit 1 + // must be 0 (bit 1 = 1 selects the sftcc forms, left to the legacy decoder). + // bits 2-3 = Tx, 4-5 = source ACx, 6-7 = destination ACy. + { .mask = 0xfe030000, .match = 0x5a000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 2, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .fn = c55x_x_tx_shl }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xfe030000, .match = 0x5a010000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 2, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .fn = c55x_x_tx_shl }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // sftcc ACx, TCx (opcode 0x5a, byte1 bit1 = 1): test the sign of an + // accumulator and copy it into a test-control bit (TI shift-conditional + // support op). ACx is byte1 bits 6-7; TCx is byte1 bit 0 (0 -> tc1, 1 -> + // tc2), rendered lowercase via the condition-flag path. The add / sub-shift + // forms occupy byte1 bit1 = 0, so this match is disjoint from them. Null + // type and IL (OPAQUE), matching the legacy decoder. + { .mask = 0xfe020000, .match = 0x5a020000, .id = TMS320C55_INS_SFTCC, .lop = C55_LOP_OPAQUE, .len = 2, + .ops = { { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 0, .fn = c55x_x_tcflag } } }, + // mov , dst (opcode 0x44, high nibble 8-15): move a 16-bit special + // register (sp / ssp / cdp / brc0 / brc1 / rptc), sign-extended, into the + // gr4 destination. The source extractor leaves the unassigned high nibbles + // (11, 15) to the legacy decoder. + { .mask = 0xfe800000, .match = 0x44800000, .id = TMS320C55_INS_MOV, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_44src }, { .lo = 0, .fn = c55x_x_gr4 } } }, + // sftl dst, #1 / #-1 (opcode 0x50, sub-opcodes 0/1): a fixed +-1 logical + // shift of the gr4 register. Sub-opcode bit 0 (matched loosely below) + // selects the count's sign; the shared SFTL handler lifts the accumulator + // forms (a positive count shifts left, a negative one right). + { .mask = 0xfe060000, .match = 0x50000000, .id = TMS320C55_INS_SFTL, .lop = C55_LOP_SFTL, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .fn = c55x_x_sftl_imm } } }, + // pop / psh ACx (opcode 0x50, sub-opcodes 2/3/6/7): single-accumulator + // stack ops. The plain forms (2/6) take the gr4 register field (byte1 + // bits 4-7); the dbl(ACx) forms (3/7) are accumulator-only. The operand + // moves as 32 bits / two stack words for an accumulator and one word for a + // 16-bit register through the shared push/pop lifter. 0x51 is the parallel + // form, so the opcode byte is matched modulo its low (parallel) bit. + { .mask = 0xfe070000, .match = 0x50020000, .id = TMS320C55_INS_POP, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe070000, .match = 0x50030000, .id = TMS320C55_INS_POP, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_ac_dbl } } }, + { .mask = 0xfe070000, .match = 0x50060000, .id = TMS320C55_INS_PSH, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xfe070000, .match = 0x50070000, .id = TMS320C55_INS_PSH, .len = 2, + .ops = { { .lo = 4, .fn = c55x_x_ac_dbl } } }, + // popboth / pshboth xdst (opcode 0x50, sub-opcodes 4/5): pop / push a + // register *pair* named by the extended-register field (byte1 bits 4-7). + // The pair semantics are not lifted (the .both flag), but the stack effect + // (two words) and pop/push type are modelled. + { .mask = 0xfe0f0000, .match = 0x50040000, .id = TMS320C55_INS_POPBOTH, .len = 2, .both = true, + .ops = { { .lo = 4, .fn = c55x_x_xgr4 } } }, + { .mask = 0xfe0f0000, .match = 0x50050000, .id = TMS320C55_INS_PSHBOTH, .len = 2, .both = true, + .ops = { { .lo = 4, .fn = c55x_x_xgr4 } } }, + // sub-opcode 3: mpy[r] -- ACy = ACx * ACy. + { .mask = 0xfe0e0000, .match = 0x54060000, .id = TMS320C55_INS_MPY, .len = 2, .mods = 1, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 4: sqr[r] -- ACy = ACx * ACx. + { .mask = 0xfe0e0000, .match = 0x54080000, .id = TMS320C55_INS_SQR, .len = 2, .mods = 1, .square = true, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 1: sqa[r] -- ACy = ACy + ACx * ACx. + { .mask = 0xfe0e0000, .match = 0x54020000, .id = TMS320C55_INS_SQA, .lop = C55_LOP_MAC, .len = 2, .mods = 1, .square = true, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 2: sqs[r] -- ACy = ACy - ACx * ACx. + { .mask = 0xfe0e0000, .match = 0x54040000, .id = TMS320C55_INS_SQS, .lop = C55_LOP_MAS, .len = 2, .mods = 1, .square = true, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 0: addv / addrv ACx, ACy -- ACy = ACy + |ACx(32-16)| (addition + // with absolute value). The rounded variant is spelled "addrv" (the r is + // infixed, not a trailing suffix), so it is a distinct id selected by bit 0 + // rather than the generic rounding-suffix mods; bit 0 is therefore pinned. + { .mask = 0xfe0f0000, .match = 0x54000000, .id = TMS320C55_INS_ADDV, .lop = C55_LOP_ADDV, .len = 2, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + { .mask = 0xfe0f0000, .match = 0x54010000, .id = TMS320C55_INS_ADDRV, .lop = C55_LOP_ADDV, .len = 2, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 5: round ACx, ACy -- ACy = round(ACx). The low byte's bit 0 is + // don't-care here (the operation always rounds, so both encodings print + // "round"); no rounding-suffix mods, leaving the type untyped as the legacy. + { .mask = 0xfe0e0000, .match = 0x540a0000, .id = TMS320C55_INS_ROUND, .lop = C55_LOP_ROUND, .len = 2, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // sub-opcode 6: sat[r] ACx, ACy -- ACy = saturate(ACx). bit 0 selects the + // rounding variant (satr) via mods. + { .mask = 0xfe0e0000, .match = 0x540c0000, .id = TMS320C55_INS_SAT, .lop = C55_LOP_SAT, .len = 2, .mods = 1, + .ops = { { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // mac[r] ACx, Tx, ACy[, ACy] and mas[r] Tx, [ACx,] ACy (opcode 0x56, two + // bytes): the register MAC / MAS that take a T-register coefficient, ACy = + // ACy [+/-] sx17(ACx(32-16)) * sx40(Tx). The low byte holds round (bit 0), + // the mac/mas selector (bit 1: 0 mac, 1 mas), Tx (bits 2-3), the ACx + // multiplicand (bits 4-5) and the destination ACy (bits 6-7); the leading + // byte's bit 0 is the parallel marker (left free). mac prints the trailing + // ACy only when ACx differs from it; mas drops the ACx when it equals ACy. + { .mask = 0xfe020000, .match = 0x56000000, .id = TMS320C55_INS_MAC, .lop = C55_LOP_MAC, .len = 2, .mods = 1, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_macreg_acy_dst } } }, + { .mask = 0xfe020000, .match = 0x56020000, .id = TMS320C55_INS_MAS, .lop = C55_LOP_MAS, .len = 2, .mods = 1, + .ops = { { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .fn = c55x_x_mpy_acsrc }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // mpy[r] Tx, [ACy,] ACx (opcode 0x58, two bytes): a register multiply, + // ACx = sx40(Tx) * sx17(ACy(32-16)) -- the accumulator multiplicand is its + // high word, not its low half (see c55_mul_val). An optional rounding variant. + // The leading byte's bit 0 is the parallel marker (so 0x58 / 0x59 both match, + // with that bit left free); the low byte's bit 0 is the round flag (folded in + // via mods so 0x58.. and its mpyr sibling share this row) and bit 1 selects the + // accumulate form mac[r] (the next row). + { .mask = 0xfe020000, .match = 0x58000000, .id = TMS320C55_INS_MPY, .len = 2, .mods = 1, + .ops = { { .fn = c55x_x_mpy_t }, { .fn = c55x_x_mpy_acsrc }, { .fn = c55x_x_mpy_acdst } } }, + // mac[r] ACy, Tx, ACx, ACy (opcode 0x58, low byte's bit 1 = 1): the register + // MAC, ACy = ACx + sx17(ACy(32-16)) * sx40(Tx), rounded for macr. ACy (bits 6-7) + // is both the multiplicand high word and the destination; ACx (bits 4-5) is the + // accumulator addend; Tx is bits 2-3. Unlike the multiply this form always + // prints all four operands. The legacy lifted the multiplicand as ACy.l; this + // matches the multiply in using the high word. + { .mask = 0xfe020000, .match = 0x58020000, .id = TMS320C55_INS_MAC, .lop = C55_LOP_MAC, .len = 2, .mods = 1, + .ops = { { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // sftl / sfts / sftsc ACx, Tx[, ACy] (opcode 0x5c, two bytes): a register + // shift of ACx by the signed count in Tx, written to ACy. The low byte holds + // the sub-opcode (bits 0-1: 0 sftl, 1 sfts, 2 sftsc; 3 is invalid), Tx (bits + // 2-3), the source ACx (bits 4-5) and the destination ACy (bits 6-7); the + // leading byte's bit 0 is the parallel marker (left free). ACy collapses + // against ACx when they are equal. sftl shifts logically and sfts shifts + // arithmetically (lifted here); sftsc additionally affects the carry and is + // left unlifted, matching the legacy. + { .mask = 0xfe030000, .match = 0x5c000000, .id = TMS320C55_INS_SFTL, .lop = C55_LOP_SFTL, .len = 2, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .fn = c55x_x_macreg_acy_dst } } }, + { .mask = 0xfe030000, .match = 0x5c010000, .id = TMS320C55_INS_SFTS, .lop = C55_LOP_SFTS, .len = 2, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .fn = c55x_x_macreg_acy_dst } } }, + { .mask = 0xfe030000, .match = 0x5c020000, .id = TMS320C55_INS_SFTSC, .len = 2, + .ops = { { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 2, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, + { .fn = c55x_x_macreg_acy_dst } } }, + // sfts / sftsc / sftl ACx, #SHIFTW[, ACy] (opcode 0x10, three bytes): the + // immediate-count companions of the 0x5c register shifts. The leading byte + // is the opcode (bit 0 the parallel marker); the middle byte holds the + // sub-opcode (bits 0-3: 5 sfts, 6 sftsc, 7 sftl -- the lower sub-opcodes are + // the and/or/xor/add/sub shift-and-combine forms, still on the legacy + // decoder), the source ACx (bits 4-5) and the destination ACy (bits 6-7); + // the last byte's bits 0-5 are the signed shift count SHIFTW. ACy collapses + // against ACx when equal. sftl/sfts are lifted (sign-extending SHIFTW to pick + // the direction); sftsc is left unlifted like the legacy. + // and / or / xor / add / sub ACx << #SHIFTW[, ACy] (opcode 0x10, sub-opcodes + // 0-4): shift-and-combine forms sharing the 0x10 encoding with the shifts + // below. ACx is byte1 bits 4-5, the destination ACy bits 6-7, SHIFTW the last + // byte's bits 0-5. The bitwise forms (and/or/xor) print the trailing ACy only + // when it differs from ACx and collapse otherwise; add/sub always print it. + // exp ACx, Tx (opcode 0x10, byte1 nibble 0x8): compute the exponent (the + // leading-sign-bit count) of ACx into Tx. ACx is byte1 bits 4-5, Tx byte2 + // bits 4-5. The shift computation is not modelled, so it decodes and analyses + // on the shared path (REP-free, default null type) but carries no IL. + { .mask = 0xfe0f0000, .match = 0x10080000, .id = TMS320C55_INS_EXP, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_T, .fn = c55_x_reg } } }, + // bcnt ACx, ACy, TCx, Tx (opcode 0x10, byte1 nibble 0xa): count bits; null + // analysis type and no modelled effect, so OPAQUE. ACx byte1 bits4-5, ACy + // byte2 bits6-7, TCx byte2 bit0, Tx byte2 bits4-5. + { .mask = 0xfe0f0000, .match = 0x100a0000, .id = TMS320C55_INS_BCNT, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 0, .fn = c55x_x_tcflag }, + { .lo = 4, .width = 2, .param = C55_RC_T, .fn = c55_x_reg } } }, + // maxdiff / mindiff ACx, ACy, ACz, ACw (opcode 0x10, nibbles 0xc / 0xe): the + // four accumulators are interleaved across byte1/byte2 (ACx byte1 bits4-5, + // ACy byte2 bits6-7, ACz byte1 bits6-7, ACw byte2 bits4-5). OPAQUE. + { .mask = 0xfe0f0000, .match = 0x100c0000, .id = TMS320C55_INS_MAXDIFF, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xfe0f0000, .match = 0x100e0000, .id = TMS320C55_INS_MINDIFF, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // dmaxdiff / dmindiff ACx, ACy, ACz, ACw, TRNx (opcode 0x10, nibbles 0xd / + // 0xf): as max/mindiff plus the trn0/trn1 transition register at byte2 bit0. + { .mask = 0xfe0f0000, .match = 0x100d0000, .id = TMS320C55_INS_DMAXDIFF, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 0, .fn = c55x_x_trn } } }, + { .mask = 0xfe0f0000, .match = 0x100f0000, .id = TMS320C55_INS_DMINDIFF, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .lo = 0, .fn = c55x_x_trn } } }, + { .mask = 0xfe0f0000, .match = 0x10000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDSHL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw_shl }, + { .fn = c55x_x_shiftk_acy } } }, + { .mask = 0xfe0f0000, .match = 0x10010000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORSHL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw_shl }, + { .fn = c55x_x_shiftk_acy } } }, + { .mask = 0xfe0f0000, .match = 0x10020000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORSHL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw_shl }, + { .fn = c55x_x_shiftk_acy } } }, + { .mask = 0xfe0f0000, .match = 0x10030000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw_shl }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xfe0f0000, .match = 0x10040000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw_shl }, + { .lo = 14, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xfe0f0000, .match = 0x10050000, .id = TMS320C55_INS_SFTS, .lop = C55_LOP_SFTS, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw }, + { .fn = c55x_x_shiftk_acy } } }, + { .mask = 0xfe0f0000, .match = 0x10060000, .id = TMS320C55_INS_SFTSC, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw }, + { .fn = c55x_x_shiftk_acy } } }, + { .mask = 0xfe0f0000, .match = 0x10070000, .id = TMS320C55_INS_SFTL, .lop = C55_LOP_SFTL, .len = 3, + .ops = { { .lo = 12, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, + { .fn = c55x_x_shiftw }, + { .fn = c55x_x_shiftk_acy } } }, + // mpy[r] / mac[r] / mas[r] Smem, uns(Cmem), ACx (opcode 0xd0, three bytes): + // the unsigned-coefficient memory multiplies, ACx [+/-]= sx40(Smem) * + // zx40(Cmem). The last byte's bits 2-3 select the operation (01 mpy, 10 mac, + // 11 mas); the coefficient is the (zero-extended) Cmem (bits 0-1 the CDP + // post-modify), the destination ACx is bits 4-5, round is bit 6. The signed + // macmz form (operation 00) has no dedicated id and stays on the legacy + // decoder, as do the *(cdp+t0) coefficient mode and the side-load (bit 15) + // forms. + { .mask = 0xff008c00, .match = 0xd0000400, .id = TMS320C55_INS_MPY, .len = 3, .no_parallel = true, .mods = 7, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem, .param = 1 }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff008c00, .match = 0xd0000800, .id = TMS320C55_INS_MAC, .lop = C55_LOP_MAC, .len = 3, .no_parallel = true, .mods = 7, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem, .param = 1 }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff008c00, .match = 0xd0000c00, .id = TMS320C55_INS_MAS, .lop = C55_LOP_MAS, .len = 3, .no_parallel = true, .mods = 7, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem, .param = 1 }, { .fn = c55x_x_mac_acdst } } }, + // mpym[r] Smem, Cmem, ACx (opcode 0xd1, three bytes): a memory multiply, + // ACx = sx40(Smem) * sx40(Cmem). Byte1 is the Smem field; the last byte + // holds the Cmem coefficient mode (bits 0-1), the operation (bits 2-3: + // 00 mpym), the destination ACx (bits 4-5), round (bit 6, folded in via + // mods) and uns (bit 7). In the top-aligned match word the last byte sits + // at bits 8-15, so op is pinned at bits 10-11 and uns at bit 15: this row + // matches op = 00 (mpym, no accumulate) with uns = 0, leaving macm / masm + // (op 01 / 10), the uns forms and the *(cdp+t0) coefficient mode (abandoned + // by the Cmem extractor) to the legacy decoder. + { .mask = 0xff000c00, .match = 0xd1000000, .id = TMS320C55_INS_MPYM, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem }, { .fn = c55x_x_mac_acdst } } }, + // macm[r] Smem, Cmem, ACx (op = 01, match bits 10-11 = 01): ACx += Smem*Cmem, + // and masm[r] (op = 10): ACx -= Smem*Cmem. Same operands and uns / coefficient + // constraints as mpym; the accumulate is carried via the MAC / MAS lift ops. + { .mask = 0xff000c00, .match = 0xd1000400, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000c00, .match = 0xd1000800, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_cmem }, { .fn = c55x_x_mac_acdst } } }, + // macm[r] / masm[r] Smem, [ACx,] ACy (opcode 0xd2, three bytes): a memory + // MAC whose coefficient is the source accumulator high word, + // ACy += sx40(Smem) * sx17(ACx(32-16)) (op = 00) or ACy -= ... (op = 01); the + // squaring sqam[r] / sqsm[r] forms (op = 10 / 11, below) instead multiply + // Smem by itself, ACy = ACx +/- sx40(Smem) * sx40(Smem). The last byte holds + // the source ACx (bits 0-1), the operation (bits 2-3), the destination ACy + // (bits 4-5), round (bit 6) and the side-load flag (bit 7). For the MACs only + // the distinct-register three-operand form is lifted: the two-operand + // (ACx == ACy) form has no legacy IL and stays on the legacy decoder. + { .mask = 0xff000c00, .match = 0xd2000000, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_accoef }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000c00, .match = 0xd2000400, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_accoef }, { .fn = c55x_x_mac_acdst } } }, + // the squares: the coefficient extractor collapses to NONE so the + // two-operand (ACx == ACy) form is lifted as well -- the legacy decoder has + // no IL for either, so the structured square is a strict improvement. + { .mask = 0xff000c00, .match = 0xd2000800, .id = TMS320C55_INS_SQAM, .lop = C55_LOP_MAC, .len = 3, .no_parallel = true, .mods = 0x207, .square = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_accsrc }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000c00, .match = 0xd2000c00, .id = TMS320C55_INS_SQSM, .lop = C55_LOP_MAS, .len = 3, .no_parallel = true, .mods = 0x207, .square = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_accsrc }, { .fn = c55x_x_mac_acdst } } }, + // mpym[r] Smem, [ACx,] ACy and mpym[r] Smem, Tx, ACx (opcode 0xd3, three + // bytes): the no-accumulate counterparts of the d2 / d4 MACs. The last + // byte's bits 2-3 select the form: 00 multiplies by the source accumulator's + // high word (ACx(32-16), the ACx operand collapsing against ACy as for the d2 + // MACs but with the two-operand form lifted here), 01 multiplies by a T + // register (bits 0-1) and 10 is the squaring sqrm[r] (ACx = Smem * Smem, + // below). mpymu (bits 2-3 = 11) is the unsigned multiply: both operands carry + // uns and the mnemonic gains the 'u' suffix. The legacy mis-lifted it as a + // signed multiply; the shared path multiplies unsigned (the disassembly is + // unchanged, so only the IL -- now correct -- differs). + { .mask = 0xff000c00, .match = 0xd3000000, .id = TMS320C55_INS_MPYM, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_accsrc }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000c00, .match = 0xd3000400, .id = TMS320C55_INS_MPYM, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_mac_tcoef }, { .fn = c55x_x_mac_acdst } } }, + // sqrm[r] Smem, ACx (op = 10): squaring multiply with no accumulation, the + // single accumulator (bits 4-5) being the destination; the source-AC bits + // are unused. No legacy IL, so the structured lift is a strict improvement. + { .mask = 0xff000c00, .match = 0xd3000800, .id = TMS320C55_INS_SQRM, .len = 3, .no_parallel = true, .mods = 0x207, .square = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000c00, .match = 0xd3000c00, .id = TMS320C55_INS_MPYM, .len = 3, .no_parallel = true, .mods = 0x207, .uns_all = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_u }, { .lo = 0, .fn = c55x_x_tcoef_u }, { .fn = c55x_x_mac_acdst } } }, + // macm[r] / masm[r] Smem, Tx, [ACx,] ACy (opcodes 0xd4 / 0xd5, three bytes): + // a memory MAC whose coefficient is a T register, ACy = acc +/- sx40(Smem) * + // sx40(Tx). The last byte holds the accumulator source ACx (bits 0-1), the + // coefficient Tx (bits 2-3), the destination ACy (bits 4-5), round (bit 6) + // and the side-load flag (bit 7). There is no operation field -- the opcode + // selects add vs subtract -- so the rows pin only the opcode and uns = 0 + // (bit 15). The four-operand form (ACx != ACy) accumulates into ACx; the + // three-operand form accumulates into ACy. The uns / side-load forms remain + // on the legacy decoder. + { .mask = 0xff000000, .match = 0xd4000000, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 2, .fn = c55x_x_mac_tcoef }, { .fn = c55x_x_mac_accsrc }, { .fn = c55x_x_mac_acdst } } }, + { .mask = 0xff000000, .match = 0xd5000000, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 3, .no_parallel = true, .mods = 0x207, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 2, .fn = c55x_x_mac_tcoef }, { .fn = c55x_x_mac_accsrc }, { .fn = c55x_x_mac_acdst } } }, + // add / sub Smem, [src,] dst (opcodes 0xd6/0xd7, three bytes): a memory + // operand added to (or subtracted from) a register, dst = src +/- Smem. The + // Smem byte is the middle byte; the last byte holds the destination gr4 + // register (bits 4-7) and the source gr4 register (bits 0-3), the latter + // printed only when it differs from the destination. The opcode's low bit + // selects add vs sub, so the leading byte is matched whole (no parallel + // form). Like the legacy these are typed but left unlifted (the generic + // add/sub lifter declines the memory source), and const-indexed / absolute + // Smem modes fall through to the legacy decoder via c55x_x_smem. + { .mask = 0xff000000, .match = 0xd6000000, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_gr4_src }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000000, .match = 0xd7000000, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_gr4_src }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // sub src, Smem, dst (opcode 0xd8, three bytes): the reverse-subtract form, + // dst = src - Smem, with the source register printed first. Same FDDD/FSSS + // last byte and Smem middle byte as 0xd6/0xd7, but both registers are always + // shown (no collapse). The legacy types it as a subtraction and leaves it + // unlifted (the generic subtract lifter declines the memory minuend). + { .mask = 0xff000000, .match = 0xd8000000, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 0, .fn = c55x_x_gr4 }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // and / or / xor Smem, [src,] dst (opcodes 0xd9/0xda/0xdb, three bytes): the + // bitwise counterparts of 0xd6/0xd7 with the identical operand layout. The + // 16-bit memory operand is sign-extended to the accumulator width; the legacy + // lifts these when both source and destination are accumulators. + { .mask = 0xff000000, .match = 0xd9000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDMEM, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000000, .match = 0xda000000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORMEM, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000000, .match = 0xdb000000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORMEM, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // add / sub Smem << Tx, [ACx,] ACy (opcode 0xdd, three bytes): a memory + // operand shifted left by the count in a T register before being added to + // (or subtracted from) an accumulator. The Smem byte is the middle byte; the + // last byte holds ACx (bits 6-7), ACy (bits 4-5), the shift register Tx (bits + // 2-3), and the add/sub selector (bits 0-1: 0 add, 1 sub). ACx collapses + // against ACy. The legacy types these and leaves them unlifted (the shifted + // memory source is declined by the generic add/sub lifter); const-indexed + // Smem modes fall through to the legacy decoder. + { .mask = 0xff000300, .match = 0xdd000000, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_shtx }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000300, .match = 0xdd000100, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_shtx }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // addsub2cc Smem, ACx, Tx, TC1, TC2, ACy (opcode 0xdd, variant byte2 bits + // 0-1 = 10): conditionally add or subtract the memory word to ACx based on + // TC1/TC2, writing ACy. The legacy types this null and leaves it unlifted; + // OPAQUE keeps the shared disasm while declining the IL. byte2 bits 6-7 = + // ACx, bits 4-5 = ACy, bits 2-3 = Tx; TC1/TC2 are fixed literals. + { .mask = 0xff000300, .match = 0xdd000200, .id = TMS320C55_INS_ADDSUB2CC, .lop = C55_LOP_OPAQUE, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 2, .fn = c55x_x_t2 }, { .fn = c55x_x_tc1 }, { .fn = c55x_x_tc2 }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // mov [rnd(]Smem << Tx[)], ACy (opcode 0xdd, variant byte2 bits 0-1 = 11): + // load the (Tx-shifted, optionally rounded) memory word into ACy. byte2 + // bits 4-5 = ACy, bits 2-3 = Tx, bit 6 = the rnd() wrapper. The legacy + // leaves this unlifted (the shifted memory source is declined by the mov + // lifter), so the IL is null. + { .mask = 0xff000300, .match = 0xdd000300, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_shtx_rnd }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // btst K4, Smem, TCx (opcode 0xdc, the bit-test sub-form): test bit K4 of + // Smem and record it in the test-control flag TC1 or TC2. 0xdc is a large + // multi-form opcode -- byte2 bit 1 selects btst (0) versus the family of + // mov-Smem-to-special-register forms (1), and byte2 bit 0 chooses TC1 (0) + // or TC2 (1); the bit number K4 is byte2 bits 4-7. Typed AND like the other + // bit operations, with no IL. The mov-to-special-register variants of 0xdc + // remain on the legacy decoder. + { .mask = 0xff000200, .match = 0xdc000000, .id = TMS320C55_INS_BTST, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg } } }, + // mov Smem, (opcode 0xdc, the non-btst sub-forms): load a + // data-memory word into a special register. byte2 bits 0-1 select the group + // (2: dp/cdp/bsa*/sp/ssp/bk*/dph/pdp; 3: csr/brc*/trn*) and bits 4-7 (4-bit + // in group 2, 3-bit in group 3) select the register. The shared mov lifter + // loads via c55_mem_move into the register's 16-bit view (dph/pdp truncate), + // matching the legacy IL. Undefined register selectors fall through to the + // legacy decoder. + { .mask = 0xff000300, .match = 0xdc000200, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .param = 2, .fn = c55x_x_dc_movdst } } }, + { .mask = 0xff000300, .match = 0xdc000300, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .param = 3, .fn = c55x_x_dc_movdst } } }, + // btstset / btstclr / btstnot K4, Smem, TCx (opcode 0xe3): test bit K4 of + // Smem into TC1/TC2 and then set / clear / toggle that bit in memory. 0xe3 + // is multi-form -- byte2 bits 2-3 select the operation (0 set, 1 clr, 2 not; + // 3 is the bset/bclr/bnot register-source family that stays on the legacy + // decoder), byte2 bit 1 selects TC1 (0) or TC2 (1), byte2 bit 0 is a + // don't-care, and the bit number K4 is byte2 bits 4-7. All three are typed + // AND like btst (the legacy decoder typed btstset that way but left btstclr + // and btstnot untyped); none is lifted. + { .mask = 0xff000c00, .match = 0xe3000000, .id = TMS320C55_INS_BTSTSET, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 1, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg } } }, + { .mask = 0xff000c00, .match = 0xe3000400, .id = TMS320C55_INS_BTSTCLR, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 1, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg } } }, + { .mask = 0xff000c00, .match = 0xe3000800, .id = TMS320C55_INS_BTSTNOT, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .width = 4, .fn = c55_x_imm }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 1, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg } } }, + // bset / bclr / bnot src, Smem (opcode 0xe3, the register-source forms that + // complete the family above): set / clear / toggle the bit of Smem selected + // by the register src. byte2 low nibble 0xc is bset, 0xd is bclr, 0xe/0xf is + // bnot (its bit 0 is a don't-care); src is the gr4 register in byte2 bits + // 4-7. The legacy decoder typed bset/bclr as MOV and left bnot untyped; bnot + // is a memory bit-toggle, so it is typed XOR here. None is lifted. + { .mask = 0xff000f00, .match = 0xe3000c00, .id = TMS320C55_INS_BSET, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 8, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000f00, .match = 0xe3000d00, .id = TMS320C55_INS_BCLR, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 8, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000e00, .match = 0xe3000e00, .id = TMS320C55_INS_BNOT, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 8, .width = 8, .fn = c55x_x_smem } } }, + // btst src, Smem, TCx (opcode 0xe0): test the bit of Smem selected by the + // register src (the bit *number* lives in the register, not an immediate as + // in the 0xdc K4 form) and copy it into TC1/TC2. src is the gr4 register in + // byte2 bits 4-7, the Smem field is byte1, and byte2 bit 0 chooses TC1 (0) + // or TC2 (1) -- rendered lowercase via the condition-flag path, matching + // this form's legacy rendering (distinct from the uppercase 0xdc/0xe3 + // forms). byte2 bits 1-3 are don't-cares. Typed AND like the other bit + // tests; not lifted. + { .mask = 0xff000000, .match = 0xe0000000, .id = TMS320C55_INS_BTST, .len = 3, .no_parallel = true, + .ops = { { .lo = 4, .fn = c55x_x_gr4 }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .fn = c55x_x_tcflag } } }, + // band Smem, K16, TCx (opcodes 0xf2 / 0xf3): bitwise-AND the 16-bit mask + // K16 with Smem and copy the zero result into a test-control bit. The K16 + // is always present (bytes 2-3, a 4-byte instruction), the Smem field is + // byte1, and the opcode LSB selects the destination flag -- 0xf2 -> TC1, + // 0xf3 -> TC2 (so byte0 bit0 is the flag selector, not the parallel marker: + // no_parallel). The flag prints uppercase (TC1/TC2) via the register path, + // matching this form's legacy rendering. Typed AND; not lifted. + { .mask = 0xfe000000, .match = 0xf2000000, .id = TMS320C55_INS_BAND, .len = 4, .no_parallel = true, + .ops = { { .lo = 16, .width = 8, .fn = c55x_x_smem }, { .lo = 0, .width = 16, .fn = c55_x_imm }, { .lo = 24, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg } } }, + // mov #k, Smem store (opcodes 0xe6 #k8 / 0xfb #k16): write an immediate + // constant into the single-data-memory operand. The immediate is the source + // (printed first) -- byte2 for 0xe6, bytes 2-3 for 0xfb -- and the Smem field + // is byte1. byte0 LSB is part of the opcode here, not the parallel marker + // (no_parallel). The shared MOV lifter declines an immediate-to-memory store + // (it only lifts reg/imm-to-reg and reg<->memory), so the IL falls back to the + // legacy lifter (the immediate store). + { .mask = 0xff000000, .match = 0xe6000000, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm }, { .lo = 8, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000000, .match = 0xfb000000, .id = TMS320C55_INS_MOV, .len = 4, .no_parallel = true, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm }, { .lo = 16, .width = 8, .fn = c55x_x_smem } } }, + // and/or #k16, Smem (opcodes 0xf4 / 0xf5): bitwise-combine an unsigned + // 16-bit constant into the single-data-memory operand in place. The + // immediate is the source (printed first, bytes 2-3), the Smem field is + // byte1; byte0 LSB is opcode, not the parallel marker (no_parallel). Lifted + // as a memory read-modify-write where the Smem mode has a shared effective + // address (see the AND/OR op-type lifter); other modes fall back. + { .mask = 0xff000000, .match = 0xf4000000, .id = TMS320C55_INS_AND, .len = 4, .no_parallel = true, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm }, { .lo = 16, .width = 8, .fn = c55x_x_smem } } }, + { .mask = 0xff000000, .match = 0xf5000000, .id = TMS320C55_INS_OR, .len = 4, .no_parallel = true, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm }, { .lo = 16, .width = 8, .fn = c55x_x_smem } } }, + // addsubcc Smem, ACx, TCx, ACy (opcode 0xde, selectors 0 and 1): a + // conditional add/subtract -- ACy = ACx +/- Smem depending on the test- + // control flag. 0xde is multi-form; byte2 bits 1-3 == 0 selects this form, + // with byte2 bit 0 choosing TC1 (0) or TC2 (1), ACx in byte2 bits 6-7 and + // ACy in bits 4-5. Typed ADD (as the legacy decoder did); not lifted. The + // remaining 0xde forms (the two-flag addsubcc, subc, the Smem<<#16 add/sub + // forms, and addsub/subadd) stay on the legacy decoder. + { .mask = 0xff000e00, .match = 0xde000000, .id = TMS320C55_INS_ADDSUBCC, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 0, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // addsubcc Smem, ACx, TC1, TC2, ACy (opcode 0xde, selector 2): the two-flag + // conditional add/subtract. The selector value 2 (byte2 bits 0-3 == 0b0010) + // conveniently has bit 0 == 0 and bit 1 == 1, so the same TC register + // extractor reads TC1 from bit 0 and TC2 from bit 1. + { .mask = 0xff000f00, .match = 0xde000200, .id = TMS320C55_INS_ADDSUBCC, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 0, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg }, { .lo = 1, .width = 1, .param = C55_RC_TC, .fn = c55_x_reg }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // subc Smem, [ACx,] ACy (opcode 0xde, selector 3): the conditional-subtract + // (subtract-with-borrow) used in division. ACx (byte2 bits 6-7) collapses + // against ACy (bits 4-5) when equal, leaving just the destination. The + // legacy decoder left it untyped; it is typed SUB here. The Smem source is + // declined by the generic sub lifter, so there is no IL (as before). + { .mask = 0xff000f00, .match = 0xde000300, .id = TMS320C55_INS_SUBC, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // add / sub Smem << #16, [ACx,] ACy (opcode 0xde, selectors 4 and 5): the + // memory operand is shifted left by a fixed 16 (rendered "<< #16" by the + // memory formatter via c55x_x_smem_sh16). ACx (byte2 bits 6-7) collapses + // against ACy (bits 4-5). Typed ADD / SUB as the legacy decoder did; the + // shifted Smem source is declined by the generic add/sub lifter, so no IL. + { .mask = 0xff000f00, .match = 0xde000400, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_sh16 }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000f00, .match = 0xde000500, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_sh16 }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // sub ACx, Smem << #16, ACy (opcode 0xde, selector 6): the reversed-operand + // subtract (ACx minus the shifted memory). ACx (byte2 bits 6-7) is always + // shown here. Typed SUB; no IL for the same reason. + { .mask = 0xff000f00, .match = 0xde000600, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 6, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg }, { .lo = 8, .width = 8, .fn = c55x_x_smem_sh16 }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // addsub / subadd Tx, Smem, ACx (opcode 0xde, selectors 8 and 9): the dual + // add-and-subtract (a butterfly forming ACx +/- and -/+ Tx around Smem). + // Tx is byte2 bits 6-7, ACx is bits 4-5. The legacy decoder left these + // untyped; they are categorised by their leading operation (addsub -> ADD, + // subadd -> SUB). The dual semantics are not modelled, and the generic + // add/sub lifter declines the memory destination, so there is no IL. + { .mask = 0xff000f00, .match = 0xde000800, .id = TMS320C55_INS_ADDSUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 6, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000f00, .match = 0xde000900, .id = TMS320C55_INS_SUBADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 6, .width = 2, .param = C55_RC_T, .fn = c55_x_reg }, { .lo = 8, .width = 8, .fn = c55x_x_smem }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // add / sub [uns(]Smem[)], [ACx,] ACy (opcode 0xdf, selectors 6 and 7): a + // plain add/subtract of a data-memory word into an accumulator. byte2 bits + // 1-3 pick add (6) or sub (7), byte2 bit 0 is the unsigned-memory qualifier + // (rendered uns(...)), ACx (bits 6-7) collapses against ACy (bits 4-5). + // Typed ADD/SUB as the legacy decoder did; the Smem source is declined by + // the generic add/sub lifter, so there is no IL. The 0xdf high_byte / + // low_byte / mov / carry / borrow forms remain on the legacy decoder. + { .mask = 0xff000e00, .match = 0xdf000c00, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_uns }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000e00, .match = 0xdf000e00, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_uns }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // mov [uns(]Smem[)], ACx (opcode 0xdf, selector 2): load a data-memory word + // into an accumulator, sign-extended to 40 bits (zero-extended when the + // uns() qualifier from byte2 bit 0 is present). ACx is byte2 bits 4-5. The + // shared mov lifter handles the load via c55_mem_move, matching the legacy + // sign/zero-extend IL exactly. + { .mask = 0xff000e00, .match = 0xdf000400, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_uns }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // mov [uns(]high_byte/low_byte(Smem)[)], ACx (opcode 0xdf, selectors 0 and + // 1): load the high or low byte of a data-memory word into an accumulator. + // byte2 bits 1-3 pick high_byte (0) or low_byte (1); byte2 bit 0 is the + // unsigned qualifier and ACx is byte2 bits 4-5. Typed MOV; the byte-access + // load is not lifted (the memory mover declines a byte_sel operand), as in + // the legacy decoder. + { .mask = 0xff000e00, .match = 0xdf000000, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .param = 1, .fn = c55x_x_smem_byte }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000e00, .match = 0xdf000200, .id = TMS320C55_INS_MOV, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .param = 2, .fn = c55x_x_smem_byte }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // add [uns(]Smem[)], CARRY, [ACx,] ACy / sub [uns(]Smem[)], BORROW, [ACx,] + // ACy (opcode 0xdf, selectors 4 and 5): add-with-carry / subtract-with- + // borrow of a memory word into an accumulator. byte2 bits 1-3 select add + // CARRY (4) or sub BORROW (5); the flag operand is read from byte2 bit 1 + // (pinned by the row mask: 0 -> CARRY, 1 -> BORROW). byte2 bit 0 is the + // unsigned qualifier and ACx (bits 6-7) collapses against ACy (bits 4-5). + // Typed ADD/SUB as the legacy decoder did; the Smem source is declined by + // the generic add/sub lifter, so there is no IL. + { .mask = 0xff000e00, .match = 0xdf000800, .id = TMS320C55_INS_ADD, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_uns }, { .lo = 1, .width = 1, .param = C55_RC_SPECIAL, .fn = c55_x_reg }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + { .mask = 0xff000e00, .match = 0xdf000a00, .id = TMS320C55_INS_SUB, .len = 3, .no_parallel = true, + .ops = { { .lo = 8, .width = 8, .fn = c55x_x_smem_uns }, { .lo = 1, .width = 1, .param = C55_RC_SPECIAL, .fn = c55_x_reg }, { .fn = c55x_x_macreg_acy_dst }, { .lo = 4, .width = 2, .param = C55_RC_AC, .fn = c55_x_reg } } }, + // mov src, dst (opcode 0x90): register-to-register move over the extended + // "xsrc"/"xdst" register set (0-3 AC, 4-7 XSP/XSSP/XDP/XCDP, 8-15 XAR). byte1 + // bits 4-7 = src, bits 0-3 = dst; the shared MOV lifter sign/zero-extends to + // the destination width (and declines when an operand has no lifter var). + { .mask = 0xff000000, .match = 0x90000000, .id = TMS320C55_INS_MOV, .len = 2, .no_parallel = true, + .ops = { { .lo = 4, .fn = c55x_x_xgr4 }, { .lo = 0, .fn = c55x_x_xgr4 } } }, + // b acx (opcode 0x91) / call acx (opcode 0x92): register-indirect branch and + // call to the address in an accumulator (AC0-3 in the low two bits). + { .mask = 0xff000000, .match = 0x91000000, .id = TMS320C55_INS_B, .len = 2, + .ops = { { .lo = 0, .width = 2, .fn = c55x_x_ac2 } } }, + { .mask = 0xff000000, .match = 0x92000000, .id = TMS320C55_INS_CALL, .len = 2, + .ops = { { .lo = 0, .width = 2, .fn = c55x_x_ac2 } } }, + // b L16 (opcode 0x06) / call L16 (opcode 0x08): 16-bit pc-relative branch and + // call. Three bytes, the signed displacement in bytes 1-2; target is + // pc + size + sign-extended(disp16). Same machinery as the 0x4a short branch + // (a reltarget immediate plus c55_effective_type refining 'b'/'call' to a + // direct JMP/CALL), just a wider field, and both are parallel-capable. + { .mask = 0xfe000000, .match = 0x06000000, .id = TMS320C55_INS_B, .len = 3, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm, .param = 2 } } }, + { .mask = 0xfe000000, .match = 0x08000000, .id = TMS320C55_INS_CALL, .len = 3, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm, .param = 2 } } }, + // b P24 (opcode 0x6a and 0x6b) / call P24 (opcode 0x6c): 24-bit absolute + // branch and call. Four bytes, the destination address in bytes 1-3; the + // target is that address directly (abs_target), not a pc-relative offset. + // These are not parallel-capable: 0x6b is a second plain 'b' encoding (not + // '|| b'), and 0x6d is a different instruction (bcc), so each pins its byte. + // The operand is an addr (rendered as a 24-bit address) carrying abs_target. + { .mask = 0xff000000, .match = 0x6a000000, .id = TMS320C55_INS_B, .len = 4, + .ops = { { .lo = 0, .width = 24, .fn = c55_x_imm, .param = 12 } } }, + { .mask = 0xff000000, .match = 0x6b000000, .id = TMS320C55_INS_B, .len = 4, + .ops = { { .lo = 0, .width = 24, .fn = c55_x_imm, .param = 12 } } }, + { .mask = 0xff000000, .match = 0x6c000000, .id = TMS320C55_INS_CALL, .len = 4, + .ops = { { .lo = 0, .width = 24, .fn = c55_x_imm, .param = 12 } } }, + // reset (opcode 0x94): software reset, no operands. + { .mask = 0xff000000, .match = 0x94000000, .id = TMS320C55_INS_RESET, .len = 2 }, + // ret / reti (opcode 0x48): within this group the low three bits of the + // operand byte select the operation (0b100 = ret, 0b101 = reti; bits 3-7 are + // don't-cares for these two), so the rows match byte0 plus those three bits + // via a 0xff070000 mask. Both pop a return address and are typed as returns; + // the remaining 0x48 sub-ops (rpt/rptadd/rptsub csr) stay on the legacy path. + { .mask = 0xff070000, .match = 0x48040000, .id = TMS320C55_INS_RET, .len = 2 }, + { .mask = 0xff070000, .match = 0x48050000, .id = TMS320C55_INS_RETI, .len = 2 }, + // xcc / xccpart (opcode 0x96): predicated execution. Bit 7 of the operand + // byte selects xccpart (set) from xcc (clear), and the low seven bits are the + // condition field. Only register-versus-zero comparisons are decoded here; + // status-flag conditions fall back to the legacy decoder via c55x_x_cond. + { .mask = 0xff800000, .match = 0x96000000, .id = TMS320C55_INS_XCC, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + { .mask = 0xff800000, .match = 0x96800000, .id = TMS320C55_INS_XCCPART, .lop = C55_LOP_NOP, .xcc_guard = true, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + // bcc short form (opcodes 0x60-0x67): the destination offset is a four-bit + // field with the high three bits in byte0 (bits 0-2) and the low bit in byte1 + // (bit 7), at packed bits 7-10; target = pc + size + offset. Bit 0 of the + // leading byte is part of that offset, not the parallel marker (no_parallel). + // The remaining low seven bits of byte1 carry the same register-versus-zero + // condition field as xcc; status-flag conditions fall back to the legacy + // decoder via c55x_x_cond. + { .mask = 0xf8000000, .match = 0x60000000, .id = TMS320C55_INS_BCC, .len = 2, .no_parallel = true, + .ops = { { .lo = 7, .width = 4, .fn = c55x_x_bcc_short_target }, + { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + // bcc 0x04 form: a 3-byte conditional branch. byte1 carries the register- + // versus-zero condition field (bit 7 unused) and byte2 is a signed 8-bit + // pc-relative offset (target = pc + size + offset). Parallel-capable + // (0x05 = || bcc); status-flag conditions fall back to the legacy decoder. + { .mask = 0xfe000000, .match = 0x04000000, .id = TMS320C55_INS_BCC, .len = 3, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm, .param = 2 }, + { .lo = 8, .width = 7, .fn = c55x_x_cond } } }, + // bcc L16 form (opcode 0x6d): byte1 is the register-versus-zero condition, + // byte2-3 a signed 16-bit pc-relative offset (target = pc + size + offset). + // The legacy front-end recovered the displacement by re-parsing the rendered + // text and sign-extending to 8 bits, truncating the L16 offset; decoding it + // from the instruction bits computes the correct target. + { .mask = 0xff000000, .match = 0x6d000000, .id = TMS320C55_INS_BCC, .len = 4, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm, .param = 2 }, + { .lo = 16, .width = 7, .fn = c55x_x_cond } } }, + // bcc P24 form (opcode 0x68): byte1 is the register-versus-zero condition, + // byte2-4 a 24-bit absolute program address (same as the unconditional P24 + // branch). The legacy 8-bit text-parsing path mis-computed this too; the + // shared decoder takes the absolute target directly from the bits. + { .mask = 0xff000000, .match = 0x68000000, .id = TMS320C55_INS_BCC, .len = 5, + .ops = { { .lo = 0, .width = 24, .fn = c55_x_imm, .param = 12 }, + { .lo = 24, .width = 7, .fn = c55x_x_cond } } }, + // bcc compare-and-branch (opcode 0x6f): byte1 selects the source register + // (gr4) and signed comparison, byte2 is the 8-bit compare constant K8, and + // byte3 a signed 8-bit pc-relative offset. The unsigned form (bccu) is left + // to the legacy decoder via c55x_x_cond_imm. The legacy text-parsing path + // sign-extended this displacement to 16 bits; decoding the L8 offset from the + // instruction bits yields the correct (possibly negative) targets. + { .mask = 0xff000000, .match = 0x6f000000, .id = TMS320C55_INS_BCC, .len = 4, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm, .param = 2 }, + { .lo = 8, .width = 16, .fn = c55x_x_cond_imm } } }, + // callcc (conditional call): byte1 is the register-versus-zero condition. + // 0x6e is the L16 form (byte2-3 a signed 16-bit pc-relative offset); 0x69 is + // the P24 form (byte2-4 a 24-bit absolute target). These reuse the same + // target machinery as the conditional branches with a CCALL type and the + // call stack effect. The legacy analysis read the displacement from the wrong + // bytes (a 16-bit value starting at the condition byte), so its targets were + // wrong for both forms; decoding from the bits computes them correctly. + { .mask = 0xff000000, .match = 0x6e000000, .id = TMS320C55_INS_CALLCC, .len = 4, + .ops = { { .lo = 0, .width = 16, .fn = c55_x_imm, .param = 2 }, + { .lo = 16, .width = 7, .fn = c55x_x_cond } } }, + { .mask = 0xff000000, .match = 0x69000000, .id = TMS320C55_INS_CALLCC, .len = 5, + .ops = { { .lo = 0, .width = 24, .fn = c55_x_imm, .param = 12 }, + { .lo = 24, .width = 7, .fn = c55x_x_cond } } }, + // retcc (conditional return, opcode 0x02; 0x03 = || retcc): byte1 carries the + // register-versus-zero condition and byte2 is unused. Typed CRET (a pop with + // a fall-through edge); no IL. Status-flag conditions fall back to the legacy + // decoder via c55x_x_cond. + { .mask = 0xfe000000, .match = 0x02000000, .id = TMS320C55_INS_RETCC, .len = 3, + .ops = { { .lo = 8, .width = 7, .fn = c55x_x_cond } } }, + // rpt k8 (opcode 0x4c): repeat the next instruction (k8+1) times; the count + // is the full operand byte. Parallel-capable, so the row matches 0x4c/0x4d. + { .mask = 0xfe000000, .match = 0x4c000000, .id = TMS320C55_INS_RPT, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm } } }, + // intr k5 / trap k5 (opcode 0x95): software interrupt vs trap to a 5-bit + // vector. The two share the leading byte and are told apart by bit 7 of the + // operand byte, so the rows reach into byte1 with a 0xff800000 mask; the + // vector is the low five bits (bits 5-6 are don't-cares). + { .mask = 0xff800000, .match = 0x95000000, .id = TMS320C55_INS_INTR, .len = 2, + .ops = { { .lo = 0, .width = 5, .fn = c55_x_imm } } }, + { .mask = 0xff800000, .match = 0x95800000, .id = TMS320C55_INS_TRAP, .len = 2, + .ops = { { .lo = 0, .width = 5, .fn = c55_x_imm } } }, + // b offset (opcode 0x4a with bit 7 of the operand byte clear; bit 7 set is + // rptblocal below). A short pc-relative branch: the displacement is the + // operand byte sign-extended to 8 bits (bit 7 being clear here, it is always + // a forward 0..127). The id 'b' is shared with the register-indirect form + // (0x91); c55_effective_type refines this immediate-operand form to a direct + // JMP. The offset is a reltarget so it drives target computation yet renders + // as a plain immediate rather than a 24-bit address. + { .mask = 0xfe800000, .match = 0x4a000000, .id = TMS320C55_INS_B, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55_x_imm, .param = 2 } } }, + // rptblocal k7 (opcode 0x4a with bit 7 of the operand byte set; bit 7 clear + // is the offset branch b, left to the legacy decoder). Block-local repeat + // with a 7-bit count. Both parallel-capable and byte1-discriminated, so the + // mask composes the two: 0xfe leaves the parallel bit free, 0x..800000 pins + // the discriminator (matching 0x4a/0x4b with operand bit 7 set). + { .mask = 0xfe800000, .match = 0x4a800000, .id = TMS320C55_INS_RPTBLOCAL, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55_x_imm } } }, + + // --- dual "::" MACs (opcodes 0x82-0x85) ----------------------------- + // Two parallel sub-MACs sharing a Cmem coefficient. The leading byte picks + // the family and byte2 bits 2-3 (packed bits 10-11) the sub-op pair; the + // operands and sub-op metadata are produced by c55x_fill_dual. The id only + // drives the op type (all -> MUL); the disasm uses the dual formatter. + // 0x85 op=10 (triple amar) / op=11 (firsadd/firssub) and all of 0x86 stay + // on the legacy front-end for now. + // 0x82: :: mpy + { .mask = 0xff000c00, .match = 0x82000000, .id = TMS320C55_INS_MPY, .len = 4, .dual = true, .lop = C55_LOP_NONE, .lop2 = C55_LOP_NONE }, + { .mask = 0xff000c00, .match = 0x82000400, .id = TMS320C55_INS_MPY, .len = 4, .dual = true, .lop = C55_LOP_MAC, .lop2 = C55_LOP_NONE }, + { .mask = 0xff000c00, .match = 0x82000800, .id = TMS320C55_INS_MPY, .len = 4, .dual = true, .lop = C55_LOP_MAS, .lop2 = C55_LOP_NONE }, + { .mask = 0xff000c00, .match = 0x82000c00, .id = TMS320C55_INS_AMAR, .len = 4, .dual = true, .amar1 = true, .lop2 = C55_LOP_NONE }, + // 0x83: :: mac + { .mask = 0xff000c00, .match = 0x83000000, .id = TMS320C55_INS_MAC, .len = 4, .dual = true, .lop = C55_LOP_MAC, .lop2 = C55_LOP_MAC }, + { .mask = 0xff000c00, .match = 0x83000400, .id = TMS320C55_INS_MAC, .len = 4, .dual = true, .lop = C55_LOP_MAS, .lop2 = C55_LOP_MAC }, + { .mask = 0xff000c00, .match = 0x83000800, .id = TMS320C55_INS_MAC, .len = 4, .dual = true, .lop = C55_LOP_MAC, .shift1 = true, .lop2 = C55_LOP_MAC }, + { .mask = 0xff000c00, .match = 0x83000c00, .id = TMS320C55_INS_AMAR, .len = 4, .dual = true, .amar1 = true, .lop2 = C55_LOP_MAC }, + // 0x84: :: mac >> #16 + { .mask = 0xff000c00, .match = 0x84000000, .id = TMS320C55_INS_MAS, .len = 4, .dual = true, .lop = C55_LOP_MAS, .lop2 = C55_LOP_MAC, .shift2 = true }, + { .mask = 0xff000c00, .match = 0x84000400, .id = TMS320C55_INS_AMAR, .len = 4, .dual = true, .amar1 = true, .lop2 = C55_LOP_MAC, .shift2 = true }, + { .mask = 0xff000c00, .match = 0x84000800, .id = TMS320C55_INS_MAS, .len = 4, .dual = true, .lop = C55_LOP_NONE, .lop2 = C55_LOP_MAC, .shift2 = true }, + { .mask = 0xff000c00, .match = 0x84000c00, .id = TMS320C55_INS_MAS, .len = 4, .dual = true, .lop = C55_LOP_MAC, .shift1 = true, .lop2 = C55_LOP_MAC, .shift2 = true }, + // 0x85: :: mas (op=00 amar::mas, op=01 mas::mas) + { .mask = 0xff000c00, .match = 0x85000000, .id = TMS320C55_INS_AMAR, .len = 4, .dual = true, .amar1 = true, .lop2 = C55_LOP_MAS }, + { .mask = 0xff000c00, .match = 0x85000400, .id = TMS320C55_INS_MAS, .len = 4, .dual = true, .lop = C55_LOP_MAS, .lop2 = C55_LOP_MAS }, + // 0x85 op=10: triple-register amar (amar Xmem, Ymem, Cmem) - three address + // modifies, no product; classified LEA and lifted as the sequence of the + // operands' post-modify side effects. (op=11 firsadd/firssub stays legacy.) + { .mask = 0xff000c00, .match = 0x85000800, .id = TMS320C55_INS_AMAR, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .fn = c55x_x_dual_cmem3 } } }, + // 0x85 op=11: FIR symmetric/antisymmetric filter step. byte3 bit4 selects + // firsadd (0) vs firssub (1); ACx is byte3 bits 2-3, ACy byte3 bits 6-7. + { .mask = 0xff000c10, .match = 0x85000c00, .id = TMS320C55_INS_FIRSADD, .lop = C55_LOP_FIRSADD, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .fn = c55x_x_dual_cmem3 }, + { .lo = 2, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 6, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff000c10, .match = 0x85000c10, .id = TMS320C55_INS_FIRSSUB, .lop = C55_LOP_FIRSSUB, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .fn = c55x_x_dual_cmem3 }, + { .lo = 2, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 6, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // 0x86 dual-multiply family selected by byte3 bits 5-7: mpym (0, ACx=Xmem*Ymem), + // macm (1, MAC), masm (3, MAS). uns()/T3=/r/40 qualifiers and the source/dest + // accumulators are decoded by the xymac extractors and .mods (round=bit0, + // side-load=bit1, M40=bit4). The macm/masm two-operand form (SS==DD) drops the + // explicit source slot. (macm>>16 (2), the ::mov pairs (4,5), lms (6) and + // sqdst/abdst (14,15) stay on the legacy path for now.) + { .mask = 0xff0000e0, .match = 0x86000000, .id = TMS320C55_INS_MPYM, .len = 4, .mods = 0x5081, + .ops = { { .fn = c55x_x_xymac_xmem }, { .fn = c55x_x_xymac_ymem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x86000020, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 4, .mods = 0x5081, + .ops = { { .fn = c55x_x_xymac_xmem }, { .fn = c55x_x_xymac_ymem }, { .fn = c55x_x_xymac_src }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x86000060, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 4, .mods = 0x5081, + .ops = { { .fn = c55x_x_xymac_xmem }, { .fn = c55x_x_xymac_ymem }, { .fn = c55x_x_xymac_src }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // 0x86 macm>>16 (byte3 bits 5-7 = 2): like macm but the accumulator is shifted + // right 16 before the product (ACy = (ACx >> #16) + Xmem*Ymem). + { .mask = 0xff0000e0, .match = 0x86000040, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 4, .mods = 0x5081, .shift16 = true, + .ops = { { .fn = c55x_x_xymac_xmem }, { .fn = c55x_x_xymac_ymem }, { .fn = c55x_x_xymac_src }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // 0x86 lms (byte3 bits 5-7 = 6): ACy += Xmem*Ymem :: ACx = round(ACx + Xmem<<16). + // ACx is byte2 bits 0-1, ACy bits 2-3; the round/reserved last-byte low bits do + // not affect the disassembly. (The ::mov pairs at 4,5 stay on the legacy path.) + { .mask = 0xff0000e0, .match = 0x860000c0, .id = TMS320C55_INS_LMS, .lop = C55_LOP_LMS, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // 0x86 distance forms (byte3 bits 4-7: sqdst=14, abdst=15): ACy += ACx.h^2 (or + // |ACx.h|) :: ACx = (Xmem<<16) - (Ymem<<16). ACx is byte2 bits 0-1, ACy bits 2-3. + { .mask = 0xff0000f0, .match = 0x860000e0, .id = TMS320C55_INS_SQDST, .lop = C55_LOP_SQDST, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000f0, .match = 0x860000f0, .id = TMS320C55_INS_ABDST, .lop = C55_LOP_ABDST, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // 0x86 MAC :: parallel load (byte3 bits 5-7: masm::mov=4, macm::mov=5): + // ACx = ACx -/+ (Xmem * Tx) [, T3=Xmem] :: ACy = Ymem << #16. + // ops: Xmem, Tx (byte3 bits 2-3), ACx (byte2 bits 0-1), Ymem, ACy (byte2 bits 2-3); + // .mods packs round (bit0) and the T3= side-load (bit1). + { .mask = 0xff0000e0, .match = 0x86000080, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 4, .mods = 0x81, .mac_mov = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 2, .fn = c55x_x_mac_tcoef }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x860000a0, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 4, .mods = 0x81, .mac_mov = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 2, .fn = c55x_x_mac_tcoef }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // --- A-unit ALU aadd / amov / asub (opcode 0x14) ---------------------- + // 3 bytes. The operation and operand form are selected by the low nibble + // of byte2: bit 2 chooses register (0) vs P8-immediate (1) source, and + // bits[1:0] select aadd (00) / amov (01) / asub (10); bit 3 is a redundant + // sub-bank that does not change the operation. The XAC (extended pointer) + // register form is marked by a 1 in byte1's low nibble and matched first. + // XACS = byte1[7:4], XACD = byte2[7:4]; FSSS/FDDD are the same fields read + // as the AC/T/AR set; P8 = byte1. dst = dst src via the shared AREG + // lift (aadd/amov/asub all carry IL on C55x). + { .mask = 0xff0f0700, .match = 0x14010000, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_xgr4 }, { .lo = 4, .fn = c55x_x_xgr4 } } }, + { .mask = 0xff0f0700, .match = 0x14010100, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AREG_MOV, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_xgr4 }, { .lo = 4, .fn = c55x_x_xgr4 } } }, + { .mask = 0xff0f0700, .match = 0x14010200, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_xgr4 }, { .lo = 4, .fn = c55x_x_xgr4 } } }, + { .mask = 0xff000700, .match = 0x14000000, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000700, .match = 0x14000100, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AREG_MOV, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000700, .match = 0x14000200, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 3, + .ops = { { .lo = 12, .fn = c55x_x_gr4 }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000700, .match = 0x14000400, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000700, .match = 0x14000500, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AREG_MOV, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 4, .fn = c55x_x_gr4 } } }, + { .mask = 0xff000700, .match = 0x14000600, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 3, + .ops = { { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // --- mov K16, dst (opcode 0x76, byte3[3:2] == 10) --------------------- + // 4 bytes; K16 = byte1:byte2 (MSB-first), dst = byte3[7:4] (AC/T/AR). The + // shared AREG move lifts dst = K16. + { .mask = 0xff00000c, .match = 0x76000008, .id = TMS320C55_INS_MOV, .lop = C55_LOP_AREG_MOV, .len = 4, + .ops = { { .lo = 8, .width = 16, .fn = c55_x_imm }, { .lo = 4, .fn = c55x_x_gr4 } } }, + // --- xcc / xccpart (opcodes 0x9e / 0x9f) ------------------------------ + // As the 0x96 forms: byte1[7] selects xccpart (1) over xcc (0); the 7-bit + // condition is byte1[6:0]. Standalone, the qualifier gates the following + // instruction, which per-instruction lifting expresses as nop. + { .mask = 0xff800000, .match = 0x9e000000, .id = TMS320C55_INS_XCC, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + { .mask = 0xff800000, .match = 0x9e800000, .id = TMS320C55_INS_XCCPART, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + { .mask = 0xff800000, .match = 0x9f000000, .id = TMS320C55_INS_XCC, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + { .mask = 0xff800000, .match = 0x9f800000, .id = TMS320C55_INS_XCCPART, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 7, .fn = c55x_x_cond } } }, + // --- dual-memory move (opcode 0x80) ----------------------------------- + // 3 bytes; byte1 = XXXMMMYY, byte2 = YMMM00xx with byte2[3:2] selecting the + // form: 00 = mov dbl(Xmem),dbl(Ymem); 01 = mov Xmem,Ymem; 10 = mov ACx, + // Xmem,Ymem (ACx = byte2[1:0]). Xmem is the source, Ymem the destination. + { .mask = 0xff000c00, .match = 0x80000000, .id = TMS320C55_INS_MOV, .lop = C55_LOP_MOVMEM, .len = 3, + .ops = { { .fn = c55x_x_dual_xmem3_dbl }, { .fn = c55x_x_dual_ymem3_dbl } } }, + { .mask = 0xff000c00, .match = 0x80000400, .id = TMS320C55_INS_MOV, .lop = C55_LOP_MOVMEM, .len = 3, + .ops = { { .fn = c55x_x_dual_xmem3 }, { .fn = c55x_x_dual_ymem3 } } }, + { .mask = 0xff000c00, .match = 0x80000800, .id = TMS320C55_INS_MOV, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 0, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_xmem3 }, { .fn = c55x_x_dual_ymem3 } } }, + // --- dual-memory add / sub into ACx (opcode 0x81) --------------------- + // Same dual-memory layout as 0x80; byte2[3:2] selects 00 = add Xmem,Ymem, + // ACx; 01 = sub; 10 = mov Xmem,Ymem,ACx. ACx = byte2[1:0]. ACx = sx(Xmem) + // +/- sx(Ymem). + { .mask = 0xff000c00, .match = 0x81000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_DUALADD, .len = 3, + .ops = { { .fn = c55x_x_dual_xmem3 }, { .fn = c55x_x_dual_ymem3 }, { .lo = 0, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff000c00, .match = 0x81000400, .id = TMS320C55_INS_SUB, .lop = C55_LOP_DUALSUB, .len = 3, + .ops = { { .fn = c55x_x_dual_xmem3 }, { .fn = c55x_x_dual_ymem3 }, { .lo = 0, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff000c00, .match = 0x81000800, .id = TMS320C55_INS_MOV, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .fn = c55x_x_dual_xmem3 }, { .fn = c55x_x_dual_ymem3 }, { .lo = 0, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // --- swap ar0, ar1 (opcode 0x5e, key 56) ------------------------------ + // The single arbitrary-AR swap pair; byte1 == 0x38 distinguishes it from + // the 4-bit sel pairs. Lifts as the same XOR exchange as the other swaps. + { .mask = 0xfeff0000, .match = 0x5e380000, .id = TMS320C55_INS_SWAP, .len = 2, + .ops = { { .lo = 0, .param = 0, .fn = c55x_x_swap }, { .lo = 0, .param = 1, .fn = c55x_x_swap } } }, + // --- idle (opcode 0x7a, byte3[3:1] == 6) ------------------------------ + // Low-power idle; no data effect. + { .mask = 0xff00000e, .match = 0x7a00000c, .id = TMS320C55_INS_IDLE, .lop = C55_LOP_OPAQUE, .len = 4 }, + // --- 0x87 parallel dual-MAC with hi-word store ------------------------ + // 4 bytes; the dual-memory Xmem/Ymem and SS/DD accumulator fields live in + // byte1/byte2 (the 0x82-0x86 layout). byte3 selects the form: bits[7:5] = + // 000/001/010 -> mpym/macm/masm "[t3=]Xmem, Tx, ACy :: mov hi(ACx << t2), + // Ymem" (Tx = byte3[3:2], round = byte3[0]); = 100/101/110 -> add/sub/mov + // "Xmem << #16, ... :: mov hi(ACz << t2), Ymem"; byte3 == 0x61 -> lmsf + // Xmem, Ymem, ACx, ACy. All decode-only (no modelled IL). + { .mask = 0xff0000ff, .match = 0x87000061, .id = TMS320C55_INS_LMSF, .lop = C55_LOP_OPAQUE, .len = 4, + .ops = { { .fn = c55x_x_dual_xmem }, { .fn = c55x_x_dual_ymem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x87000000, .id = TMS320C55_INS_MPYM, .lop = C55_LOP_OPAQUE, .len = 4, .mods = 0x01, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 2, .width = 2, .fn = c55x_x_t2 }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x87000020, .id = TMS320C55_INS_MACM, .lop = C55_LOP_OPAQUE, .len = 4, .mods = 0x01, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 2, .width = 2, .fn = c55x_x_t2 }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x87000040, .id = TMS320C55_INS_MASM, .lop = C55_LOP_OPAQUE, .len = 4, .mods = 0x01, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 2, .width = 2, .fn = c55x_x_t2 }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff0000e0, .match = 0x87000080, .id = TMS320C55_INS_ADD, .lop = C55_LOP_OPAQUE, .len = 4, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem } } }, + { .mask = 0xff0000e0, .match = 0x870000a0, .id = TMS320C55_INS_SUB, .lop = C55_LOP_OPAQUE, .len = 4, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem } } }, + { .mask = 0xff0000e0, .match = 0x870000c0, .id = TMS320C55_INS_MOV, .lop = C55_LOP_OPAQUE, .len = 4, .mac_store = true, + .ops = { { .fn = c55x_x_dual_xmem }, { .lo = 8, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .fn = c55x_x_dual_ymem }, { .lo = 10, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // --- mant ACx, ACy :: nexp ACx, Tx (opcode 0x10, selector 9) ---------- + // 3 bytes; ACx = byte1[5:4] (SS), ACy = byte1[7:6] (DD), Tx = byte2[5:4] + // (dd). ACx is shared by both halves. Decode-only. + { .mask = 0xff0f0000, .match = 0x10090000, .id = TMS320C55_INS_MANT, .lop = C55_LOP_OPAQUE, .len = 3, .mant_nexp = true, + .ops = { { .lo = 12, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 14, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 4, .width = 2, .fn = c55x_x_t2 } } }, + // --- addsub / subadd Tx, dual(Lmem), ACy (opcode 0xee, sel 6/7) ------- + // 3 bytes; same Lmem (AAAAAAAI) layout as the 0xee add/sub forms. The + // SS field is the Tx operand and DD the ACy. byte2[3:1] selects the form. + { .mask = 0xff000e00, .match = 0xee000c00, .id = TMS320C55_INS_ADDSUB, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 6, .width = 2, .fn = c55x_x_t2 }, { .lo = 8, .fn = c55x_x_smem_dual }, { .lo = 4, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff000e00, .match = 0xee000e00, .id = TMS320C55_INS_SUBADD, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .lo = 6, .width = 2, .fn = c55x_x_t2 }, { .lo = 8, .fn = c55x_x_smem_dual }, { .lo = 4, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // --- mpymk / macmk [t3=]Smem, K8, [ACx,] ACy (opcode 0xf8) ------------ + // 4 bytes; Smem = byte1, K8 = byte2. byte3[2] selects macmk (1) over mpymk + // (0); byte3[0] is the rounding (r) bit and byte3[1] the T3= side-load (the + // legacy "U" field). mpymk has only ACx = byte3[5:4]; macmk adds ACx = + // byte3[7:6] (SS) with ACy = byte3[5:4] (DD). Decode-only. + { .mask = 0xff000004, .match = 0xf8000000, .id = TMS320C55_INS_MPYMK, .lop = C55_LOP_OPAQUE, .len = 4, .mods = 0x81, + .ops = { { .lo = 16, .fn = c55x_x_smem }, { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 4, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + { .mask = 0xff000004, .match = 0xf8000004, .id = TMS320C55_INS_MACMK, .lop = C55_LOP_OPAQUE, .len = 4, .mods = 0x81, + .ops = { { .lo = 16, .fn = c55x_x_smem }, { .lo = 8, .width = 8, .fn = c55_x_imm }, { .lo = 6, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC }, { .lo = 4, .width = 2, .fn = c55_x_reg, .param = C55_RC_AC } } }, + // --- ret (opcode 0x48, selector 4) ----------------------------------- + // 2 bytes; selector = byte1[2:0] (4 = ret). byte0 bit0 is the parallel (E) + // flag, so masking it off (0xfe) lets the shared engine raise the "|| " + // prefix when set (the "|| ret" form 0x49 0x04). + { .mask = 0xfe0f0000, .match = 0x48040000, .id = TMS320C55_INS_RET, .lop = C55_LOP_NOP, .len = 2 }, + // --- band Smem, k16, TC1 (opcode 0xf2) ------------------------------- + // 4 bytes; Smem = byte1 (CDP modes 0x71/0x91/... now decode to *cdp...), + // k16 = bytes 2-3 (the low 16 bits of the packed word). TC1 is a fixed + // literal operand. Decode-only. + { .mask = 0xff000000, .match = 0xf2000000, .id = TMS320C55_INS_BAND, .lop = C55_LOP_OPAQUE, .len = 4, + .ops = { { .lo = 16, .fn = c55x_x_smem }, { .lo = 0, .width = 16, .fn = c55_x_imm }, { .fn = c55x_x_tc1 } } }, + // --- btstp Baddr, src (opcode 0xec, selector 2) ---------------------- + // 3 bytes; selector = byte2[3:1] (2 = btstp). src = byte2[7:4] (FSSS). The + // Baddr bit-address is not decoded by the legacy and renders as the literal + // "Baddr". Decode-only. + { .mask = 0xff000e00, .match = 0xec000400, .id = TMS320C55_INS_BTSTP, .lop = C55_LOP_OPAQUE, .len = 3, + .ops = { { .fn = c55x_x_baddr }, { .lo = 4, .fn = c55x_x_gr4 } } }, +}; + +const C55ArchDesc c55x_arch_desc = { + .arch = C55_ARCH_C55X, + .cpu_name = "c55x", + .table = c55x_table, + .table_len = sizeof(c55x_table) / sizeof(c55x_table[0]), + .insn_len = c55x_insn_len, + .reg_info = c55x_reg_info, + .mnemonic = c55x_mnemonic, + .op_type = c55x_op_type, + .lift = NULL, + .mem = { .addr_unit_log2 = 1, .ptr_width = 23, .big_endian = false, .page_reg = "dph" }, + .ea = NULL, + .fill_dual = c55x_fill_dual, +}; + int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) { if (!op || !buf || len < 1) { return 0; } - const int sz = c55x_op_size(buf, len); - if (sz == 0) { - return 0; - } - op->addr = addr; - op->size = sz; op->type = RZ_ANALYSIS_OP_TYPE_NULL; - /* C55x parallel-instruction marker: bit 0 of the leading - * opcode byte, when the byte is in the parallel-capable range - * 0x01..0x5F, is the 'execute in parallel with the previous - * instruction' flag -- not a separate prefix byte. So 0x03 is - * RETCC-with-parallel-bit-set (same encoding/operands as 0x02 - * but executed in parallel), 0x05 is BCC-with-parallel, 0x07 - * is B-with-parallel, etc. Per SPRU374 sec.5 ('Parallel Execution - * of Instructions') and cross-checked against the disassembler: - * - * $ rz-asm -a tms320 -c c55x -d 020405 -> retcc t0 == 0 - * $ rz-asm -a tms320 -c c55x -d 030405 -> || retcc t0 == 0 - * - * Above the 0x60 boundary, odd-byte opcodes are unrelated - * (0x69 CALLCC, 0x6B B abs, etc.); we only mask the bit in the - * parallel-capable range so we don't merge unrelated opcodes. - * - * The instruction size from the table is the same for the even - * and odd siblings (since they encode the same instruction). */ - ut8 op_byte = buf[0]; - if ((op_byte & 0x01) && op_byte < 0x60) { - op_byte &= ~0x01; - } - - /* Resolve the named instruction ID from the disassembler (single - * source of truth) and dispatch on it, rather than on the raw opcode - * byte. The disassembler runs the operand mask lists, so multi-form - * leading bytes resolve to the exact mnemonic decoded. Second-byte - * refinements and register-field extraction still read buf[]/op_byte - * directly where the mnemonic alone is insufficient (e.g. picking the - * displacement width for the several B / CALL encodings). */ - const ut16 id = tms320c55x_insn_id_decode(buf, len); - op->id = id; - - // Decode the mnemonic once. Branch-target resolution below reads the - // displacement the decoder rendered -- correct across every BCC/B form, - // whose raw displacement bytes differ by encoding (and the short 0x60-0x67 - // branches carry the displacement in the opcode itself) -- and the IL lift - // at the end reuses the same string. - const char *syntax = tms320c55x_insn_syntax_decode(buf, len); - - switch (id) { - /* ---- RPTCC k8, cond (3-byte conditional repeat) -------------- */ - case TMS320C55_INS_RPTCC: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - op->fail = addr + sz; - break; - - /* ---- RETCC cond (conditional return) ------------------------- */ - case TMS320C55_INS_RETCC: - set_cret(op); - break; - - /* ---- BCC: signed relative conditional branch. The decoder renders the - * displacement correctly for every form (0x04, 0x60-0x67 short, and - * the 16-bit 0x6f), so resolve the target from it. ------------- */ - case TMS320C55_INS_BCC: { - ut64 tgt; - if (c55x_rel_target_from_syntax(syntax, addr, sz, op_byte, &tgt)) { - set_cjmp(op, tgt); - } - break; - } - - /* ---- B: unconditional branch. Encodings: - * 0x06 16-bit relative, 0x4A 8-bit relative, - * 0x6A 24-bit absolute, 0x91 indirect via ACx. ------------- */ - case TMS320C55_INS_B: - if (op_byte == 0x91) { - op->type = RZ_ANALYSIS_OP_TYPE_UJMP; - op->eob = true; - op->fail = addr + sz; - if (sz >= 2) { - set_ireg(op, c55x_acc_names[buf[1] & 0x03]); - } - } else if (op_byte == 0x6a || op_byte == 0x6b) { - if (sz >= 4) { - set_jmp(op, rz_read_at_be24(buf, 1)); - } - } else if (op_byte == 0x4a) { - if (sz >= 2) { - set_jmp(op, addr + sz + sign_extend(buf[1], 8)); - } - } else { /* 0x06 */ - if (sz >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_jmp(op, addr + sz + sign_extend(disp, 16)); - } - } - break; - - /* ---- CALL: 0x08 16-bit relative, 0x6C 24-bit absolute, - * 0x92 indirect via ACx. ----------------------------------- */ - case TMS320C55_INS_CALL: - if (op_byte == 0x92 || op_byte == 0x93) { - op->type = RZ_ANALYSIS_OP_TYPE_UCALL; - op->fail = addr + sz; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - if (sz >= 2) { - set_ireg(op, c55x_acc_names[buf[1] & 0x03]); - } - } else if (op_byte == 0x6c) { - if (sz >= 4) { - set_call(op, rz_read_at_be24(buf, 1)); - } - } else { /* 0x08 */ - if (sz >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_call(op, addr + sz + sign_extend(disp, 16)); - } - } - break; - - /* ---- CALLCC P24, cond (conditional 24-bit call) -------------- */ - case TMS320C55_INS_CALLCC: - if (sz >= 4) { - /* Format: 6E hh ll cond -- disp is BE16 at offset 1. */ - const ut32 disp = rz_read_at_be16(buf, 1); - set_ccall(op, addr + sz + sign_extend(disp, 16)); - } - break; - - /* ---- RPT / RPTB / RPTADD / RPTSUB (block/single repeat) ------ */ - case TMS320C55_INS_RPT: - case TMS320C55_INS_RPTB: - case TMS320C55_INS_RPTADD: - case TMS320C55_INS_RPTSUB: - case TMS320C55_INS_RPTBLOCAL: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - - /* ---- RET / RETI: both are returns with -2 stack effect. The - * disassembler resolves the 0x48 family (and any other return - * encodings) to the exact mnemonic, so no second-byte logic is - * needed here. ------------------------------------------------ */ - case TMS320C55_INS_RET: - case TMS320C55_INS_RETI: - set_ret(op); - break; - - /* ---- NOP ----------------------------------------------------- */ - case TMS320C55_INS_NOP: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - - /* ---- PSH variants: 0x38 single push; 0xB5 PSH Smem, - * 0xB7 PSH dbl(Smem), 0xE4 PSH dbl(Lmem). ------------------ */ - case TMS320C55_INS_PSH: - if (op_byte == 0x38) { - set_push(op, 1); - } else { - set_push(op, (op_byte == 0xb7 || op_byte == 0xe4) ? 2 : 1); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - } - break; - - /* ---- POP variants: 0x3A single pop; 0xB8/0xB9 POP dbl(Smem), - * 0xBB POP Smem. ------------------------------------------- */ - case TMS320C55_INS_POP: - if (op_byte == 0x3a) { - set_pop(op, -1); - } else { - set_pop(op, (op_byte == 0xbb) ? -1 : -2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - } - break; - - /* ---- PSHBOTH / POPBOTH (dual register stack ops) ------------- */ - case TMS320C55_INS_PSHBOTH: - set_push(op, 2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - case TMS320C55_INS_POPBOTH: - set_pop(op, -2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - break; - - /* ---- AADD: address-arithmetic add. 0x4E is `AADD K8, SP` - * (frame setup); 0x14 is `AADD k16, AC/AR`. --------------- */ - case TMS320C55_INS_AADD: - if (op_byte == 0x4e) { - /* SP = SP + K8 (signed). rizin's convention: op->stackptr is - * the amount SP *decreases* (frame growth). On C55x the stack - * grows down, so stackptr = -K8 regardless of sign. See - * rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - if (sz >= 2) { - const st8 k8 = (st8)buf[1]; - set_imm(op, k8); - set_dst_reg(op, "sp"); - set_disp(op, k8); - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -k8; - } - } else { /* 0x14: AADD k16, AC/AR */ - op->type = RZ_ANALYSIS_OP_TYPE_LEA; - if (sz >= 3) { - const ut8 src_idx = (buf[1] >> 4) & 0x0f; - const ut8 dst_idx = (buf[2] >> 4) & 0x0f; - set_dst_reg(op, c55x_gpr_names[dst_idx]); - set_ireg(op, c55x_gpr_names[src_idx]); - } - } - break; - - /* ---- RESET --------------------------------------------------- */ - case TMS320C55_INS_RESET: - op->type = RZ_ANALYSIS_OP_TYPE_TRAP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - - /* ---- INTR #k5 / TRAP #k5: the disassembler distinguishes the two - * (bit 7 of buf[1]); use the resolved ID for the type and read - * the 5-bit immediate from buf[1]. ------------------------- */ - case TMS320C55_INS_INTR: - case TMS320C55_INS_TRAP: - op->type = (id == TMS320C55_INS_TRAP) ? RZ_ANALYSIS_OP_TYPE_TRAP : RZ_ANALYSIS_OP_TYPE_SWI; - if (sz >= 2) { - set_imm(op, buf[1] & 0x1f); - } - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - - /* ---- XCC / XCCPART (predicated execute) ---------------------- */ - case TMS320C55_INS_XCC: - case TMS320C55_INS_XCCPART: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - - /* ---- Logical AND family -------------------------------------- */ - case TMS320C55_INS_AND: - case TMS320C55_INS_BAND: - case TMS320C55_INS_BTST: - case TMS320C55_INS_BTSTSET: - op->type = RZ_ANALYSIS_OP_TYPE_AND; - break; - - /* ---- Logical OR ---------------------------------------------- */ - case TMS320C55_INS_OR: - op->type = RZ_ANALYSIS_OP_TYPE_OR; - break; - - /* ---- Logical XOR --------------------------------------------- */ - case TMS320C55_INS_XOR: - op->type = RZ_ANALYSIS_OP_TYPE_XOR; - break; - - /* ---- Compare family ------------------------------------------ */ - case TMS320C55_INS_CMP: - case TMS320C55_INS_CMPAND: - case TMS320C55_INS_CMPOR: - case TMS320C55_INS_MAX: - case TMS320C55_INS_MIN: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - break; - - /* ---- MOV family ---------------------------------------------- */ - case TMS320C55_INS_MOV: - /* 0xA0-0xAF: MOV Smem, REG (LOAD); 0xC0-0xCF: MOV REG, Smem - * (STORE). Both extract the register from the low nibble and, - * for the SP-relative form (bit 0 of buf[1] clear), set a - * *sp(#disp) operand. Other MOV-encoding bytes set only type. */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - if (op_byte >= 0xa0 && op_byte <= 0xaf) { - set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - set_mem_width(op, 2); - if (sz >= 2 && !(buf[1] & 0x01)) { - set_ireg(op, "sp"); - set_disp(op, buf[1] >> 1); - } - } else if (op_byte >= 0xc0 && op_byte <= 0xcf) { - set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - set_mem_width(op, 2); - if (sz >= 2 && !(buf[1] & 0x01)) { - set_ireg(op, "sp"); - set_disp(op, buf[1] >> 1); - } - } - break; - - /* ---- Bit set/clear (treated as MOV-like field writes) -------- */ - case TMS320C55_INS_BCLR: - case TMS320C55_INS_BSET: - case TMS320C55_INS_BFXTR: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- DELAY: memory-delay move. Per TI SWPU104 sec.6.7.1 (Memory - * Delay, grouped under "Move Operations"), delay(Smem) copies the - * content of Smem to the next-higher address Smem+1 -- a one-word - * memory-to-memory data shift used to implement delay lines in - * filters. It is a data MOV (one data read + one data write), not - * a CPU/system-control instruction, so it gets the default - * family rather than FAMILY_CPU. ----------------------------- */ - case TMS320C55_INS_DELAY: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_mem_width(op, 2); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - - /* ---- Add family ---------------------------------------------- */ - case TMS320C55_INS_ADD: - case TMS320C55_INS_ADDSUBCC: - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - - /* ---- Subtract / negate --------------------------------------- */ - case TMS320C55_INS_SUB: - case TMS320C55_INS_NEG: - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - break; - - /* ---- Bitwise NOT --------------------------------------------- */ - case TMS320C55_INS_NOT: - op->type = RZ_ANALYSIS_OP_TYPE_NOT; - break; - - /* ---- Shifts -------------------------------------------------- */ - case TMS320C55_INS_SFTL: - op->type = RZ_ANALYSIS_OP_TYPE_SHL; - break; - - /* ---- Multiply / MAC family ----------------------------------- */ - case TMS320C55_INS_MAC: - case TMS320C55_INS_MACM: - case TMS320C55_INS_MASM: - case TMS320C55_INS_MAS: - case TMS320C55_INS_MPY: - case TMS320C55_INS_MPYK: - case TMS320C55_INS_MPYM: - case TMS320C55_INS_MPYMK: - case TMS320C55_INS_SQDST: - case TMS320C55_INS_LMSF: - op->type = RZ_ANALYSIS_OP_TYPE_MUL; - break; - - /* ---- SWAP (register exchange) -------------------------------- */ - case TMS320C55_INS_SWAP: - op->type = RZ_ANALYSIS_OP_TYPE_XCHG; - break; - - /* ---- Address-register moves (LEA-like) ----------------------- */ - case TMS320C55_INS_AMOV: - case TMS320C55_INS_AMAR: - case TMS320C55_INS_FIRSADD: - op->type = RZ_ANALYSIS_OP_TYPE_LEA; - break; - - /* ---- ABS: leave NULL (no precise rizin type; see C55x+ note) - */ - case TMS320C55_INS_ABS: - op->type = RZ_ANALYSIS_OP_TYPE_NULL; - break; - - default: - /* Unknown / untyped instruction. Mark as NULL so the analyzer - * keeps walking but won't merge it into a basic block. The - * size=1 default in c55x_op_size() means we advance one byte - * and re-sync -- useful for parallel-prefixed instructions - * whose leading 0x01/0x03/0x05/... bytes are the parallel - * marker. */ - break; - } - - /* Short conditional branches 0x60-0x67 (and their parallel siblings) fold - * the displacement into the opcode low bits and do not all resolve to the - * BCC instruction id, so the switch above can leave them unclassified. - * Classify any still-unclassified op the decoder calls a conditional - * branch, taking the target from the rendered displacement. */ - if (op->type == RZ_ANALYSIS_OP_TYPE_NULL && syntax && - (!strncmp(syntax, "bcc ", 4) || !strncmp(syntax, "bccu ", 5) || - !strncmp(syntax, "|| bcc ", 7) || !strncmp(syntax, "|| bccu ", 8))) { - ut64 tgt; - if (c55x_rel_target_from_syntax(syntax, addr, sz, op_byte, &tgt)) { - set_cjmp(op, tgt); - } - } - - if (mask & RZ_ANALYSIS_OP_MASK_IL) { - // Reuse the C55x+ semantic lifter via syntax normalization. The - // analysis-resolved op->type/jump/fail already drive the control-flow - // lifts; the disassembled syntax drives the data-path lifts. - op->il_op = tms320_c55x_il_lift(op, syntax); + /* Decode-once analysis: the C55x instruction is decoded a single time by + * the shared decode-IR engine, and the analysis op (type, branch targets, + * basic-block fall-through, src/dst/val, stack effects, instruction id) and + * the RzIL lift are both derived from that one decoded C55Insn. Anything the + * engine does not decode is reported as an illegal instruction. */ + C55Insn ci; + if (c55_decode(&c55x_arch_desc, buf, len, &ci)) { + c55_fill_analysis(&c55x_arch_desc, &ci, op); + if (mask & RZ_ANALYSIS_OP_MASK_IL) { + op->il_op = c55_lift(&c55x_arch_desc, &ci, op->addr); + } + return op->size; } + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + op->size = 1; return op->size; } diff --git a/librz/arch/isa/tms320/c55x/c55x_analysis.h b/librz/arch/isa/tms320/c55x/c55x_analysis.h index d6f4010bba..935a681b9b 100644 --- a/librz/arch/isa/tms320/c55x/c55x_analysis.h +++ b/librz/arch/isa/tms320/c55x/c55x_analysis.h @@ -7,6 +7,10 @@ #define ANALYSIS_C55X_H #include +#include + +/// The TMS320C55x arch descriptor: drives the shared c55_ir engine/consumers. +extern const C55ArchDesc c55x_arch_desc; int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask); diff --git a/librz/arch/isa/tms320/c55x/table.h b/librz/arch/isa/tms320/c55x/table.h deleted file mode 100644 index acb97bf877..0000000000 --- a/librz/arch/isa/tms320/c55x/table.h +++ /dev/null @@ -1,4120 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov -// SPDX-License-Identifier: LGPL-3.0-only - -static insn_head_t c55x_list[] = { -{ - .byte = 0x00, - .size = 0x03, - .id = TMS320C55_INS_RPTCC, - .insn = { - // kkkkkkkkxCCCCCCC0000000E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,k8), LIST_END }, - .syntax = INSN_SYNTAX(rptcc k8, cond), - }, -}, -{ - .byte = 0x02, - .size = 0x03, - .id = TMS320C55_INS_RETCC, - .insn = { - // xxxxxxxxxCCCCCCC0000001E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(retcc cond), - }, -}, -{ - .byte = 0x04, - .size = 0x03, - .id = TMS320C55_INS_BCC, - .insn = { - // LLLLLLLLxCCCCCCC0000010E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,L8), LIST_END }, - .syntax = INSN_SYNTAX(bcc L8, cond), - }, -}, -{ - .byte = 0x06, - .size = 0x03, - .id = TMS320C55_INS_B, - .insn = { - // LLLLLLLLLLLLLLLL0000011E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,L16), LIST_END }, - .syntax = INSN_SYNTAX(b L16), - }, -}, -{ - .byte = 0x08, - .size = 0x03, - .id = TMS320C55_INS_CALL, - .insn = { - // LLLLLLLLLLLLLLLL0000100E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,L16), LIST_END }, - .syntax = INSN_SYNTAX(call L16), - }, -}, -{ - .byte = 0x0c, - .size = 0x03, - .id = TMS320C55_INS_RPT, - .insn = { - // kkkkkkkkkkkkkkkk0000110E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(rpt k16), - }, -}, -{ - .byte = 0x0e, - .size = 0x03, - .id = TMS320C55_INS_RPTB, - .insn = { - // llllllllllllllll0000111E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,l16), LIST_END }, - .syntax = INSN_SYNTAX(rptb pmad), - }, -}, -{ - .byte = 0x10, - .size = 0x03, - .id = TMS320C55_INS_AND, - .insn = { - .i_list = (insn_item_t []) { - { - // xxSHIFTWDDSS00000001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(and ACx << #SHIFTW[, ACy]), - }, - { - // xxSHIFTWDDSS00010001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(or ACx << #SHIFTW[, ACy]), - }, - { - // xxSHIFTWDDSS00100001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(xor ACx << #SHIFTW[, ACy]), - }, - { - // xxSHIFTWDDSS00110001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(add ACx << #SHIFTW, ACy), - }, - { - // xxSHIFTWDDSS01000001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(sub ACx << #SHIFTW, ACy), - }, - { - // xxSHIFTWDDSS01010001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(sfts ACx, #SHIFTW[, ACy]), - }, - { - // xxSHIFTWDDSS01100001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(sftsc ACx, #SHIFTW[, ACy]), - }, - { - // xxSHIFTWDDSS01110001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,SHIFTW), LIST_END }, - .syntax = INSN_SYNTAX(sftl ACx, #SHIFTW[, ACy]), - }, - { - // xxddxxxxxxSS10000001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(20,dd), LIST_END }, - .syntax = INSN_SYNTAX(exp ACx, Tx), - }, - { - // xxddxxxxDDSS10010001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(20,dd), LIST_END }, - .syntax = INSN_SYNTAX(mant ACx, ACy :: nexp ACx, Tx), - }, - { - // SSddxxxtxxSS10100001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(16,t), INSN_FLAG(20,dd), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(bcnt ACx, ACy, TCx, Tx), - }, - { - // SSDDnnnnDDSS11000001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(maxdiff ACx, ACy, ACz, ACw), - }, - { - // SSDDxxxrDDSS11010001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,r), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(dmaxdiff ACx, ACy, ACz, ACw, TRNx), - }, - { - // SSDDxxxxDDSS11100001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mindiff ACx, ACy, ACz, ACw), - }, - { - // SSDDxxxrDDSS11110001000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,15), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), INSN_FLAG(14,DD), INSN_FLAG(16,r), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(dmindiff ACx, ACy, ACz, ACw, TRNx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x12, - .size = 0x03, - .id = TMS320C55_INS_CMPAND, - .insn = { - .i_list = (insn_item_t []) { - { - // FDDD0uttFSSScc010001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,1), INSN_MASK(19,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,tt), INSN_FLAG(18,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(cmpand[u] src RELOP dst, TCy, TCx), - }, - { - // FDDD1uttFSSScc010001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,1), INSN_MASK(19,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,tt), INSN_FLAG(18,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(cmpand[u] src RELOP dst, !TCy, TCx), - }, - { - // FDDD0uttFSSScc100001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,2), INSN_MASK(19,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,tt), INSN_FLAG(18,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(cmpor[u] src RELOP dst, TCy, TCx), - }, - { - // FDDD1uttFSSScc100001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,2), INSN_MASK(19,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,tt), INSN_FLAG(18,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(cmpor[u] src RELOP dst, !TCy, TCx), - }, - { - // FDDD0xvvFSSSxx110001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,3), INSN_MASK(19,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(16,vv), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(rol BitOut, src, BitIn, dst), - }, - { - // FDDD1xvvFSSSxx110001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,3), INSN_MASK(19,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(16,vv), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(ror BitIn, src, BitOut, dst), - }, - { - // FDDDxuxtFSSScc000001001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,t), INSN_FLAG(18,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(cmp[u] src RELOP dst, TCx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x14, - .size = 0x03, - .id = TMS320C55_INS_AADD, - .insn = { - .i_list = (insn_item_t []) { - { - // XACD0000XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(aadd XACsrc, XACdst), - }, - { - // XACD0001XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(amov XACsrc, XACdst), - }, - { - // XACD0010XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(asub XACsrc, XACdst), - }, - { - // XACD1000XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(aadd XACsrc, XACdst), - }, - { - // XACD1001XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(amov XACsrc, XACdst), - }, - { - // XACD1010XACS00010001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), INSN_MASK(16,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XACS), INSN_FLAG(20,XACD), LIST_END }, - .syntax = INSN_SYNTAX(asub XACsrc, XACdst), - }, - { - // FDDD0000FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(aadd TAx, TAy), - }, - { - // FDDD0001FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(amov TAx, TAy), - }, - { - // FDDD0010FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(asub TAx, TAy), - }, - { - // FDDD0100PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(aadd P8, TAx), - }, - { - // FDDD0101PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(amov P8, TAx), - }, - { - // FDDD0110PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(asub P8, TAx), - }, - { - // FDDD1000FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(aadd TAx, TAy), - }, - { - // FDDD1001FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(amov TAx, TAy), - }, - { - // FDDD1010FSSSxxxx0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(asub TAx, TAy), - }, - { - // FDDD1100PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(aadd P8, TAx), - }, - { - // FDDD1101PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(amov P8, TAx), - }, - { - // FDDD1110PPPPPPPP0001010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,P8), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(asub P8, TAx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x16, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // kkkk0000xxxxxkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k3), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k7, dph), - }, - { - // kkkk0011xxxkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k5), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k9, pdp), - }, - { - // kkkk0100kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, bk03), - }, - { - // kkkk0101kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, bk47), - }, - { - // kkkk0110kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, bkc), - }, - { - // kkkk1000kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, csr), - }, - { - // kkkk1001kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, brc0), - }, - { - // kkkk1010kkkkkkkk0001011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov k12, brc1), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x18, - .size = 0x03, - .id = TMS320C55_INS_AND, - .insn = { - // FDDDFSSSkkkkkkkk0001100E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(and k8, src, dst), - }, -}, -{ - .byte = 0x1a, - .size = 0x03, - .id = TMS320C55_INS_OR, - .insn = { - // FDDDFSSSkkkkkkkk0001101E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(or k8, src, dst), - }, -}, -{ - .byte = 0x1c, - .size = 0x03, - .id = TMS320C55_INS_XOR, - .insn = { - // FDDDFSSSkkkkkkkk0001110E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(xor k8, src, dst), - }, -}, -{ - .byte = 0x1e, - .size = 0x03, - .id = TMS320C55_INS_MPYK, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDDxx0%KKKKKKKK0001111E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,K8), INSN_FLAG(16,R), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mpyk[r] K8, [ACx,] ACy), - }, - { - // SSDDss1%KKKKKKKK0001111E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,K8), INSN_FLAG(16,R), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mack[r] Tx, K8, [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x20, - .size = 0x01, - .id = TMS320C55_INS_NOP, - .insn = { - // 0010000E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), LIST_END }, - .syntax = INSN_SYNTAX(nop), - }, -}, -{ - .byte = 0x22, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // FSSSFDDD0010001E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov src, dst), - }, -}, -{ - .byte = 0x24, - .size = 0x02, - .id = TMS320C55_INS_ADD, - .insn = { - // FSSSFDDD0010010E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(add [src,] dst), - }, -}, -{ - .byte = 0x26, - .size = 0x02, - .id = TMS320C55_INS_SUB, - .insn = { - // FSSSFDDD0010011E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(sub [src,] dst), - }, -}, -{ - .byte = 0x28, - .size = 0x02, - .id = TMS320C55_INS_AND, - .insn = { - // FSSSFDDD0010100E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(and src, dst), - }, -}, -{ - .byte = 0x2a, - .size = 0x02, - .id = TMS320C55_INS_OR, - .insn = { - // FSSSFDDD0010101E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(or src, dst), - }, -}, -{ - .byte = 0x2c, - .size = 0x02, - .id = TMS320C55_INS_XOR, - .insn = { - // FSSSFDDD0010110E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(xor src, dst), - }, -}, -{ - .byte = 0x2e, - .size = 0x02, - .id = TMS320C55_INS_MAX, - .insn = { - // FSSSFDDD0010111E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(max [src,] dst), - }, -}, -{ - .byte = 0x30, - .size = 0x02, - .id = TMS320C55_INS_MIN, - .insn = { - // FSSSFDDD0011000E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(min [src,] dst), - }, -}, -{ - .byte = 0x32, - .size = 0x02, - .id = TMS320C55_INS_ABS, - .insn = { - // FSSSFDDD0011001E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(abs [src,] dst), - }, -}, -{ - .byte = 0x34, - .size = 0x02, - .id = TMS320C55_INS_NEG, - .insn = { - // FSSSFDDD0011010E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(neg [src,] dst), - }, -}, -{ - .byte = 0x36, - .size = 0x02, - .id = TMS320C55_INS_NOT, - .insn = { - // FSSSFDDD0011011E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(not [src,] dst), - }, -}, -{ - .byte = 0x38, - .size = 0x02, - .id = TMS320C55_INS_PSH, - .insn = { - // FSSSFDDD0011100E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(psh src1, src2), - }, -}, -{ - .byte = 0x3a, - .size = 0x02, - .id = TMS320C55_INS_POP, - .insn = { - // FSSSFDDD0011101E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(pop dst1, dst2), - }, -}, -{ - .byte = 0x3c, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // kkkkFDDD0011110E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov K4, dst), - }, -}, -{ - .byte = 0x3e, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // kkkkFDDD0011111E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(mov -K4, dst), - }, -}, -{ - .byte = 0x40, - .size = 0x02, - .id = TMS320C55_INS_ADD, - .insn = { - // kkkkFDDD0100000E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(add K4, dst), - }, -}, -{ - .byte = 0x42, - .size = 0x02, - .id = TMS320C55_INS_SUB, - .insn = { - // kkkkFDDD0100001E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(sub K4, dst), - }, -}, -{ - .byte = 0x44, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // 1000FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov sp, TAx), - }, - { - // 1001FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov ssp, TAx), - }, - { - // 1010FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov cdp, TAx), - }, - { - // 1100FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov brc0, TAx), - }, - { - // 1101FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov brc1, TAx), - }, - { - // 1110FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov rptc, TAx), - }, - { - // 01x0FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,1,0), INSN_MASK(14,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sfts dst, #-1), - }, - { - // 01x1FDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(12,1,1), INSN_MASK(14,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sfts dst, #1), - }, - { - // 00SSFDDD0100010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(14,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,FDDD), INSN_FLAG(12,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov hi(ACx), TAx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x46, - .size = 0x02, - .id = TMS320C55_INS_BCLR, - .insn = { - .i_list = (insn_item_t []) { - { - // kkkk00000100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bclr K4, st0_55), - }, - { - // kkkk00010100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bset K4, st0_55), - }, - { - // kkkk00100100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bclr K4, st1_55), - }, - { - // kkkk00110100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bset K4, st1_55), - }, - { - // kkkk01000100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bclr K4, st2_55), - }, - { - // kkkk01010100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bset K4, st2_55), - }, - { - // kkkk01100100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bclr K4, st3_55), - }, - { - // kkkk01110100011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(bset K4, st3_55), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x48, - .size = 0x02, - .id = TMS320C55_INS_RETI, - .insn = { - .i_list = (insn_item_t []) { - { - // xxxxx10101001000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,5), LIST_END }, - .f_list = NULL, - .syntax = INSN_SYNTAX(reti), - }, - { - // xxxxx0000100100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), LIST_END }, - .syntax = INSN_SYNTAX(rpt csr), - }, - { - // FSSSx0010100100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(rptadd csr, TAx), - }, - { - // kkkkx0100100100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(rptadd csr, K4), - }, - { - // kkkkx0110100100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,k4), LIST_END }, - .syntax = INSN_SYNTAX(rptsub csr, K4), - }, - { - // xxxxx1000100100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), LIST_END }, - .syntax = INSN_SYNTAX(ret), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x4a, - .size = 0x02, - .id = TMS320C55_INS_B, - .insn = { - .i_list = (insn_item_t []) { - { - // 0LLLLLLL0100101E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,L7), LIST_END }, - .syntax = INSN_SYNTAX(b L7), - }, - { - // 1lllllll0100101E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,l7), LIST_END }, - .syntax = INSN_SYNTAX(rptblocal pmad), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x4c, - .size = 0x02, - .id = TMS320C55_INS_RPT, - .insn = { - // kkkkkkkk0100110E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k8), LIST_END }, - .syntax = INSN_SYNTAX(rpt k8), - }, -}, -{ - .byte = 0x4e, - .size = 0x02, - .id = TMS320C55_INS_AADD, - .insn = { - // KKKKKKKK0100111E - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,K8), LIST_END }, - .syntax = INSN_SYNTAX(aadd K8, sp), - }, -}, -{ - .byte = 0x50, - .size = 0x02, - .id = TMS320C55_INS_POPBOTH, - .insn = { - .i_list = (insn_item_t []) { - { - // XDDD01000101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XDDD), LIST_END }, - .syntax = INSN_SYNTAX(popboth xdst), - }, - { - // XSSS01010101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,XSSS), LIST_END }, - .syntax = INSN_SYNTAX(pshboth xsrc), - }, - { - // FDDDx0000101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sftl dst, #1), - }, - { - // FDDDx0010101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sftl dst, #-1), - }, - { - // FDDDx0100101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(pop dst), - }, - { - // xxDDx0110101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,DD), LIST_END }, - .syntax = INSN_SYNTAX(pop dbl(ACx)), - }, - { - // FSSSx1100101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(psh src), - }, - { - // xxSSx1110101000E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,3,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,SS), LIST_END }, - .syntax = INSN_SYNTAX(psh dbl(ACx)), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x52, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // FSSS10000101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, sp), - }, - { - // FSSS10010101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, ssp), - }, - { - // FSSS10100101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, cdp), - }, - { - // FSSS11000101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, csr), - }, - { - // FSSS11010101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, brc1), - }, - { - // FSSS11100101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, brc0), - }, - { - // FSSS00DD0101001E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(10,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,DD), INSN_FLAG(12,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov TAx, hi(ACx)), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x54, - .size = 0x02, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // DDSS000%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(add[r]v [ACx,] ACy), - }, - { - // DDSS001%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sqa[r] [ACx,] ACy), - }, - { - // DDSS010%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sqs[r] [ACx,] ACy), - }, - { - // DDSS011%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r] [ACx,] ACy), - }, - { - // DDSS100%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sqr[r] [ACx,] ACy), - }, - { - // DDSS101%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(round [ACx,] ACy), - }, - { - // DDSS110%0101010E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sat[r] [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x56, - .size = 0x02, - .id = TMS320C55_INS_MAC, - .insn = { - .i_list = (insn_item_t []) { - { - // DDSSss0%0101011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r] ACx, Tx, ACy[, ACy]), - }, - { - // DDSSss1%0101011E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r] Tx, [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x58, - .size = 0x02, - .id = TMS320C55_INS_MPY, - .insn = { - .i_list = (insn_item_t []) { - { - // DDSSss0%0101100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r] Tx, [ACx,] ACy), - }, - { - // DDSSss1%0101100E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,R), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r] ACy, Tx, ACx, ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x5a, - .size = 0x02, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // DDSSss000101101E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(add ACx << Tx, ACy), - }, - { - // DDSSss010101101E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sub ACx << Tx, ACy), - }, - { - // DDxxxx1t0101101E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(9,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,t), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sftcc ACx, TCx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x5c, - .size = 0x02, - .id = TMS320C55_INS_SFTL, - .insn = { - .i_list = (insn_item_t []) { - { - // DDSSss000101110E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sftl ACx, Tx[, ACy]), - }, - { - // DDSSss010101110E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sfts ACx, Tx[, ACy]), - }, - { - // DDSSss100101110E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(8,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(10,ss), INSN_FLAG(12,SS), INSN_FLAG(14,DD), LIST_END }, - .syntax = INSN_SYNTAX(sftsc ACx, Tx[, ACy]), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x5e, - .size = 0x02, - .id = TMS320C55_INS_SWAP, - .insn = { - // 00kkkkkk0101111E - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(14,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(0,E), INSN_FLAG(8,k6), LIST_END }, - .syntax = INSN_SYNTAX(SWAP ( )), - }, -}, -{ - .byte = 0x60, - .size = 0x02, - .id = TMS320C55_INS_BCC, - .insn = { - // lCCCCCCC01100lll - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,l3), INSN_FLAG(8,CCCCCCC), INSN_FLAG(15,l1), LIST_END }, - .syntax = INSN_SYNTAX(bcc l4, cond), - }, -}, -{ - .byte = 0x68, - .size = 0x05, - .id = TMS320C55_INS_BCC, - .insn = { - // PPPPPPPPPPPPPPPPPPPPPPPPxCCCCCCC01101000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,P24), LIST_END }, - .syntax = INSN_SYNTAX(bcc P24, cond), - }, -}, -{ - .byte = 0x69, - .size = 0x05, - .id = TMS320C55_INS_CALLCC, - .insn = { - // PPPPPPPPPPPPPPPPPPPPPPPPxCCCCCCC01101001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,P24), LIST_END }, - .syntax = INSN_SYNTAX(callcc P24, cond), - }, -}, -{ - .byte = 0x6a, - .size = 0x04, - .id = TMS320C55_INS_B, - .insn = { - // PPPPPPPPPPPPPPPPPPPPPPPP01101010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,P24), LIST_END }, - .syntax = INSN_SYNTAX(b P24), - }, -}, -{ - .byte = 0x6c, - .size = 0x04, - .id = TMS320C55_INS_CALL, - .insn = { - // PPPPPPPPPPPPPPPPPPPPPPPP01101100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,P24), LIST_END }, - .syntax = INSN_SYNTAX(call P24), - }, -}, -{ - .byte = 0x6d, - .size = 0x04, - .id = TMS320C55_INS_BCC, - .insn = { - // LLLLLLLLLLLLLLLLxCCCCCCC01101101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,L16), LIST_END }, - .syntax = INSN_SYNTAX(bcc L16, cond), - }, -}, -{ - .byte = 0x6e, - .size = 0x04, - .id = TMS320C55_INS_CALLCC, - .insn = { - // LLLLLLLLLLLLLLLLxCCCCCCC01101110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), INSN_FLAG(16,L16), LIST_END }, - .syntax = INSN_SYNTAX(callcc L16, cond), - }, -}, -{ - .byte = 0x6f, - .size = 0x04, - .id = TMS320C55_INS_BCC, - .insn = { - // LLLLLLLLKKKKKKKKFSSSccxu01101111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,u), INSN_FLAG(10,cc), INSN_FLAG(12,FSSS), INSN_FLAG(16,K8), INSN_FLAG(24,L8), LIST_END }, - .syntax = INSN_SYNTAX(bcc[u] L8, src RELOP K8), - }, -}, -{ - .byte = 0x70, - .size = 0x04, - .id = TMS320C55_INS_ADD, - .insn = { - // SSDDSHFTKKKKKKKKKKKKKKKK01110000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(add K16 << #SHFT, [ACx,] ACy), - }, -}, -{ - .byte = 0x71, - .size = 0x04, - .id = TMS320C55_INS_SUB, - .insn = { - // SSDDSHFTKKKKKKKKKKKKKKKK01110001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub K16 << #SHFT, [ACx,] ACy), - }, -}, -{ - .byte = 0x72, - .size = 0x04, - .id = TMS320C55_INS_AND, - .insn = { - // SSDDSHFTkkkkkkkkkkkkkkkk01110010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(and k16 << #SHFT, [ACx,] ACy), - }, -}, -{ - .byte = 0x73, - .size = 0x04, - .id = TMS320C55_INS_OR, - .insn = { - // SSDDSHFTkkkkkkkkkkkkkkkk01110011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(or k16 << #SHFT, [ACx,] ACy), - }, -}, -{ - .byte = 0x74, - .size = 0x04, - .id = TMS320C55_INS_XOR, - .insn = { - // SSDDSHFTkkkkkkkkkkkkkkkk01110100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(xor k16 << #SHFT, [ACx,] ACy), - }, -}, -{ - .byte = 0x75, - .size = 0x04, - .id = TMS320C55_INS_MOV, - .insn = { - // xxDDSHFTKKKKKKKKKKKKKKKK01110101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,SHFT), INSN_FLAG(28,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov K16 << #SHFT, ACx), - }, -}, -{ - .byte = 0x76, - .size = 0x04, - .id = TMS320C55_INS_BFXTR, - .insn = { - .i_list = (insn_item_t []) { - { - // FDDD00SSkkkkkkkkkkkkkkkk01110110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,SS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(bfxtr k16, ACx, dst), - }, - { - // FDDD01SSkkkkkkkkkkkkkkkk01110110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,SS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(bfxpa k16, ACx, dst), - }, - { - // FDDD10xxKKKKKKKKKKKKKKKK01110110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov K16, dst), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x77, - .size = 0x04, - .id = TMS320C55_INS_AMOV, - .insn = { - // FDDDxxxxDDDDDDDDDDDDDDDD01110111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,D16), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(amov D16, TAx), - }, -}, -{ - .byte = 0x78, - .size = 0x04, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // xxx0000xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, dp), - }, - { - // xxx0001xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, ssp), - }, - { - // xxx0010xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, cdp), - }, - { - // xxx0011xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, bsa01), - }, - { - // xxx0100xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, bsa23), - }, - { - // xxx0101xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, bsa45), - }, - { - // xxx0110xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, bsa67), - }, - { - // xxx0111xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, bsac), - }, - { - // xxx1000xkkkkkkkkkkkkkkkk01111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), LIST_END }, - .syntax = INSN_SYNTAX(mov k16, sp), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x79, - .size = 0x04, - .id = TMS320C55_INS_MPYK, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDDxx0%KKKKKKKKKKKKKKKK01111001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,R), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(mpyk[r] K16, [ACx,] ACy), - }, - { - // SSDDss1%KKKKKKKKKKKKKKKK01111001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,R), INSN_FLAG(26,ss), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(mack[r] Tx, K16, [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x7a, - .size = 0x04, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDD000xKKKKKKKKKKKKKKKK01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(add K16 << #16, [ACx,] ACy), - }, - { - // SSDD001xKKKKKKKKKKKKKKKK01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub K16 << #16, [ACx,] ACy), - }, - { - // SSDD010xkkkkkkkkkkkkkkkk01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(and k16 << #16, [ACx,] ACy), - }, - { - // SSDD011xkkkkkkkkkkkkkkkk01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(or k16 << #16, [ACx,] ACy), - }, - { - // SSDD100xkkkkkkkkkkkkkkkk01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(xor k16 << #16, [ACx,] ACy), - }, - { - // xxDD101xKKKKKKKKKKKKKKKK01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(28,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov K16 << #16, ACx), - }, - { - // xxxx110xxxxxxxxxxxxxxxxx01111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(25,3,6), LIST_END }, - .f_list = NULL, - .syntax = INSN_SYNTAX(idle), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x7b, - .size = 0x04, - .id = TMS320C55_INS_ADD, - .insn = { - // FDDDFSSSKKKKKKKKKKKKKKKK01111011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,FSSS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(add K16, [src,] dst), - }, -}, -{ - .byte = 0x7c, - .size = 0x04, - .id = TMS320C55_INS_SUB, - .insn = { - // FDDDFSSSKKKKKKKKKKKKKKKK01111100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,K16), INSN_FLAG(24,FSSS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sub K16, [src,] dst), - }, -}, -{ - .byte = 0x7d, - .size = 0x04, - .id = TMS320C55_INS_AND, - .insn = { - // FDDDFSSSkkkkkkkkkkkkkkkk01111101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,FSSS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(and k16, src, dst), - }, -}, -{ - .byte = 0x7e, - .size = 0x04, - .id = TMS320C55_INS_OR, - .insn = { - // FDDDFSSSkkkkkkkkkkkkkkkk01111110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,FSSS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(or k16, src, dst), - }, -}, -{ - .byte = 0x7f, - .size = 0x04, - .id = TMS320C55_INS_XOR, - .insn = { - // FDDDFSSSkkkkkkkkkkkkkkkk01111111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k16), INSN_FLAG(24,FSSS), INSN_FLAG(28,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(xor k16, src, dst), - }, -}, -{ - .byte = 0x80, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // YMMM00xxXXXMMMYY10000000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Xmem), dbl(Ymem)), - }, - { - // YMMM01xxXXXMMMYY10000000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(mov Xmem, Ymem), - }, - { - // YMMM10SSXXXMMMYY10000000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(mov ACx, Xmem, Ymem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x81, - .size = 0x03, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // YMMM00DDXXXMMMYY10000001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(add Xmem, Ymem, ACx), - }, - { - // YMMM01DDXXXMMMYY10000001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(sub Xmem, Ymem, ACx), - }, - { - // YMMM10DDXXXMMMYY10000001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(mov Xmem, Ymem, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x82, - .size = 0x04, - .id = TMS320C55_INS_MPY, - .insn = { - .i_list = (insn_item_t []) { - { - // uuDDDDg%YMMM00mmXXXMMMYY10000010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mpy[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuDDDDg%YMMM01mmXXXMMMYY10000010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mpy[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuDDDDg%YMMM10mmXXXMMMYY10000010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mpy[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuxxDDg%YMMM11mmXXXMMMYY10000010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(amar Xmem :: mpy[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x83, - .size = 0x04, - .id = TMS320C55_INS_MAC, - .insn = { - .i_list = (insn_item_t []) { - { - // uuDDDDg%YMMM00mmXXXMMMYY10000011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuDDDDg%YMMM01mmXXXMMMYY10000011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuDDDDg%YMMM10mmXXXMMMYY10000011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx >> #16 :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // uuxxDDg%YMMM11mmXXXMMMYY10000011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(amar Xmem :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x84, - .size = 0x04, - .id = TMS320C55_INS_MAS, - .insn = { - .i_list = (insn_item_t []) { - { - // uuDDDDg%YMMM00mmXXXMMMYY10000100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy >> #16), - }, - { - // uuxxDDg%YMMM01mmXXXMMMYY10000100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(amar Xmem :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACx >> #16), - }, - { - // uuDDDDg%YMMM10mmXXXMMMYY10000100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy >> #16), - }, - { - // uuDDDDg%YMMM11mmXXXMMMYY10000100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx >> #16 :: mac[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy >> #16), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x85, - .size = 0x04, - .id = TMS320C55_INS_FIRSADD, - .insn = { - .i_list = (insn_item_t []) { - { - // DDx0DDU%YMMM11mmXXXMMMYY10000101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), INSN_MASK(28,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(firsadd Xmem, Ymem, Cmem, ACx, ACy), - }, - { - // DDx1DDU%YMMM11mmXXXMMMYY10000101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), INSN_MASK(28,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(firssub Xmem, Ymem, Cmem, ACx, ACy), - }, - { - // uuxxDDg%YMMM00mmXXXMMMYY10000101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(amar Xmem :: mas[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACx), - }, - { - // uuDDDDg%YMMM01mmXXXMMMYY10000101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,DD), INSN_FLAG(28,DD), INSN_FLAG(30,uu), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Xmem[)], [uns(]Cmem[)], ACx :: mas[r][40] [uns(]Ymem[)], [uns(]Cmem[)], ACy), - }, - { - // xxxxxxxxYMMM10mmXXXMMMYY10000101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,mm), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(amar Xmem, Ymem, Cmem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x86, - .size = 0x04, - .id = TMS320C55_INS_SQDST, - .insn = { - .i_list = (insn_item_t []) { - { - // 1110xxn%YMMMDDDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(28,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), LIST_END }, - .syntax = INSN_SYNTAX(sqdst Xmem, Ymem, ACx, ACy), - }, - { - // 1111xxn%YMMMDDDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(28,4,15), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), LIST_END }, - .syntax = INSN_SYNTAX(abdst Xmem, Ymem, ACx, ACy), - }, - { - // 000guuU%YMMMxxDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,uu), INSN_FLAG(28,g), LIST_END }, - .syntax = INSN_SYNTAX(mpym[r][40] [T3 = ][uns(]Xmem[)], [uns(]Ymem[)], ACx), - }, - { - // 001guuU%YMMMSSDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,uu), INSN_FLAG(28,g), LIST_END }, - .syntax = INSN_SYNTAX(macm[r][40] [T3 = ][uns(]Xmem[)], [uns(]Ymem[)], [ACx,] ACy), - }, - { - // 010guuU%YMMMSSDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,uu), INSN_FLAG(28,g), LIST_END }, - .syntax = INSN_SYNTAX(macm[r][40] [T3 = ][uns(]Xmem[)], [uns(]Ymem[)], ACx >> #16[, ACy]), - }, - { - // 011guuU%YMMMSSDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,uu), INSN_FLAG(28,g), LIST_END }, - .syntax = INSN_SYNTAX(masm[r][40] [T3 = ][uns(]Xmem[)], [uns(]Ymem[)], [ACx,] ACy), - }, - { - // 100xssU%YMMMDDDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,ss), LIST_END }, - .syntax = INSN_SYNTAX(masm[r] [T3 = ]Xmem, Tx, ACx :: mov Ymem << #16, ACy), - }, - { - // 101xssU%YMMMDDDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,ss), LIST_END }, - .syntax = INSN_SYNTAX(macm[r] [T3 = ]Xmem, Tx, ACx :: mov Ymem << #16, ACy), - }, - { - // 110xxxx%YMMMDDDDXXXMMMYY10000110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,DD), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), LIST_END }, - .syntax = INSN_SYNTAX(lms Xmem, Ymem, ACx, ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x87, - .size = 0x04, - .id = TMS320C55_INS_LMSF, - .insn = { - .i_list = (insn_item_t []) { - { - // 01100001YMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(24,8,97), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(lmsf Xmem, Ymem, ACx, ACy), - }, - { - // 000xssU%YMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,ss), LIST_END }, - .syntax = INSN_SYNTAX(mpym[r] [T3 = ]Xmem, Tx, ACy :: mov hi(ACx << t2), Ymem), - }, - { - // 001xssU%YMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,ss), LIST_END }, - .syntax = INSN_SYNTAX(macm[r] [T3 = ]Xmem, Tx, ACy :: mov hi(ACx << t2), Ymem), - }, - { - // 010xssU%YMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(26,ss), LIST_END }, - .syntax = INSN_SYNTAX(masm[r] [T3 = ]Xmem, Tx, ACy :: mov hi(ACx << t2), Ymem), - }, - { - // 100xxxxxYMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(add Xmem << #16, ACx, ACy :: mov hi(ACy << t2), Ymem), - }, - { - // 101xxxxxYMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(sub Xmem << #16, ACx, ACy :: mov hi(ACy << t2), Ymem), - }, - { - // 110xxxxxYMMMSSDDXXXMMMYY10000111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(29,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,YY), INSN_FLAG(10,MMM), INSN_FLAG(13,XXX), INSN_FLAG(16,DD), INSN_FLAG(18,SS), INSN_FLAG(20,MMM), INSN_FLAG(23,Y), LIST_END }, - .syntax = INSN_SYNTAX(mov Xmem << #16, ACy :: mov hi(ACx << t2), Ymem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x90, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // XSSSXDDD10010000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,XDDD), INSN_FLAG(12,XSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov xsrc, xdst), - }, -}, -{ - .byte = 0x91, - .size = 0x02, - .id = TMS320C55_INS_B, - .insn = { - // xxxxxxSS10010001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,SS), LIST_END }, - .syntax = INSN_SYNTAX(b ACx), - }, -}, -{ - .byte = 0x92, - .size = 0x02, - .id = TMS320C55_INS_CALL, - .insn = { - // xxxxxxSS10010010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,SS), LIST_END }, - .syntax = INSN_SYNTAX(call ACx), - }, -}, -{ - .byte = 0x94, - .size = 0x02, - .id = TMS320C55_INS_RESET, - .insn = { - // xxxxxxxx10010100 - .i_list = NULL, - .m_list = NULL, - .f_list = NULL, - .syntax = INSN_SYNTAX(reset), - }, -}, -{ - .byte = 0x95, - .size = 0x02, - .id = TMS320C55_INS_INTR, - .insn = { - .i_list = (insn_item_t []) { - { - // 0xxkkkkk10010101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k5), LIST_END }, - .syntax = INSN_SYNTAX(intr k5), - }, - { - // 1xxkkkkk10010101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,k5), LIST_END }, - .syntax = INSN_SYNTAX(trap k5), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x96, - .size = 0x02, - .id = TMS320C55_INS_XCC, - .insn = { - .i_list = (insn_item_t []) { - { - // 0CCCCCCC10010110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xcc [label, ]cond), - }, - { - // 1CCCCCCC10010110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xccpart [label, ]cond), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x9e, - .size = 0x02, - .id = TMS320C55_INS_XCC, - .insn = { - .i_list = (insn_item_t []) { - { - // 0CCCCCCC10011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xcc [label, ]cond), - }, - { - // 1CCCCCCC10011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xccpart [label, ]cond), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0x9f, - .size = 0x02, - .id = TMS320C55_INS_XCC, - .insn = { - .i_list = (insn_item_t []) { - { - // 0CCCCCCC10011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xcc [label, ]cond), - }, - { - // 1CCCCCCC10011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(15,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,CCCCCCC), LIST_END }, - .syntax = INSN_SYNTAX(xccpart [label, ]cond), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xa0, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // AAAAAAAI1010FDDD - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,FDDD), INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, dst), - }, -}, -{ - .byte = 0xb0, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // AAAAAAAI101100DD - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,DD), INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem << #16, ACx), - }, -}, -{ - .byte = 0xb4, - .size = 0x02, - .id = TMS320C55_INS_AMAR, - .insn = { - // AAAAAAAI10110100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(amar Smem), - }, -}, -{ - .byte = 0xb5, - .size = 0x02, - .id = TMS320C55_INS_PSH, - .insn = { - // AAAAAAAI10110101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(psh Smem), - }, -}, -{ - .byte = 0xb6, - .size = 0x02, - .id = TMS320C55_INS_DELAY, - .insn = { - // AAAAAAAI10110110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(delay Smem), - }, -}, -{ - .byte = 0xb7, - .size = 0x02, - .id = TMS320C55_INS_PSH, - .insn = { - // AAAAAAAI10110111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(psh dbl(Lmem)), - }, -}, -{ - .byte = 0xb8, - .size = 0x02, - .id = TMS320C55_INS_POP, - .insn = { - // AAAAAAAI10111000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(pop dbl(Lmem)), - }, -}, -{ - .byte = 0xbb, - .size = 0x02, - .id = TMS320C55_INS_POP, - .insn = { - // AAAAAAAI10111011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(pop Smem), - }, -}, -{ - .byte = 0xbc, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // AAAAAAAI101111SS - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,SS), INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov hi(ACx), Smem), - }, -}, -{ - .byte = 0xc0, - .size = 0x02, - .id = TMS320C55_INS_MOV, - .insn = { - // AAAAAAAI1100FSSS - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(0,FSSS), INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov src, Smem), - }, -}, -{ - .byte = 0xd0, - .size = 0x03, - .id = TMS320C55_INS_MPY, - .insn = { - .i_list = (insn_item_t []) { - { - // 0%DD01mmAAAAAAAI11010000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), INSN_MASK(23,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r] Smem, uns(Cmem), ACx), - }, - { - // 0%DD10mmAAAAAAAI11010000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), INSN_MASK(23,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), LIST_END }, - .syntax = INSN_SYNTAX(mac[r] Smem, uns(Cmem), ACx), - }, - { - // 0%DD11mmAAAAAAAI11010000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), INSN_MASK(23,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), LIST_END }, - .syntax = INSN_SYNTAX(mas[r] Smem, uns(Cmem), ACx), - }, - { - // U%DDxxmmAAAAAAAI11010000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(macm[r]z [T3 = ]Smem, Cmem, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xd1, - .size = 0x03, - .id = TMS320C55_INS_MPYM, - .insn = { - .i_list = (insn_item_t []) { - { - // U%DD00mmAAAAAAAI11010001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(mpym[r] [T3 = ]Smem, Cmem, ACx), - }, - { - // U%DD01mmAAAAAAAI11010001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(macm[r] [T3 = ]Smem, Cmem, ACx), - }, - { - // U%DD10mmAAAAAAAI11010001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(masm[r] [T3 = ]Smem, Cmem, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xd2, - .size = 0x03, - .id = TMS320C55_INS_MACM, - .insn = { - .i_list = (insn_item_t []) { - { - // U%DD00SSAAAAAAAI11010010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(macm[r] [T3 = ]Smem, [ACx,] ACy), - }, - { - // U%DD01SSAAAAAAAI11010010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(masm[r] [T3 = ]Smem, [ACx,] ACy), - }, - { - // U%DD10SSAAAAAAAI11010010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(sqam[r] [T3 = ]Smem, [ACx,] ACy), - }, - { - // U%DD11SSAAAAAAAI11010010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(sqsm[r] [T3 = ]Smem, [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xd3, - .size = 0x03, - .id = TMS320C55_INS_MPYM, - .insn = { - .i_list = (insn_item_t []) { - { - // U%DD00SSAAAAAAAI11010011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(mpym[r] [T3 = ]Smem, [ACx,] ACy), - }, - { - // U%DD10xxAAAAAAAI11010011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(sqrm[r] [T3 = ]Smem, ACx), - }, - { - // U%DDu1ssAAAAAAAI11010011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,ss), INSN_FLAG(19,u), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(mpym[r][u] [T3 = ]Smem, Tx, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xd4, - .size = 0x03, - .id = TMS320C55_INS_MACM, - .insn = { - // U%DDssSSAAAAAAAI11010100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(macm[r] [T3 = ]Smem, Tx, [ACx,] ACy), - }, -}, -{ - .byte = 0xd5, - .size = 0x03, - .id = TMS320C55_INS_MASM, - .insn = { - // U%DDssSSAAAAAAAI11010101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SS), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,R), INSN_FLAG(23,U), LIST_END }, - .syntax = INSN_SYNTAX(masm[r] [T3 = ]Smem, Tx, [ACx,] ACy), - }, -}, -{ - .byte = 0xd6, - .size = 0x03, - .id = TMS320C55_INS_ADD, - .insn = { - // FDDDFSSSAAAAAAAI11010110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(add Smem, [src,] dst), - }, -}, -{ - .byte = 0xd7, - .size = 0x03, - .id = TMS320C55_INS_SUB, - .insn = { - // FDDDFSSSAAAAAAAI11010111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sub Smem, [src,] dst), - }, -}, -{ - .byte = 0xd8, - .size = 0x03, - .id = TMS320C55_INS_SUB, - .insn = { - // FDDDFSSSAAAAAAAI11011000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(sub src, Smem, dst), - }, -}, -{ - .byte = 0xd9, - .size = 0x03, - .id = TMS320C55_INS_AND, - .insn = { - // FDDDFSSSAAAAAAAI11011001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(and Smem, src, dst), - }, -}, -{ - .byte = 0xda, - .size = 0x03, - .id = TMS320C55_INS_OR, - .insn = { - // FDDDFSSSAAAAAAAI11011010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(or Smem, src, dst), - }, -}, -{ - .byte = 0xdb, - .size = 0x03, - .id = TMS320C55_INS_XOR, - .insn = { - // FDDDFSSSAAAAAAAI11011011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,FSSS), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(xor Smem, src, dst), - }, -}, -{ - .byte = 0xdc, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // 0000xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, dp), - }, - { - // 0001xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, cdp), - }, - { - // 0010xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bsa01), - }, - { - // 0011xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bsa23), - }, - { - // 0100xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bsa45), - }, - { - // 0101xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bsa67), - }, - { - // 0110xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bsac), - }, - { - // 0111xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, sp), - }, - { - // 1000xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, ssp), - }, - { - // 1001xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bk03), - }, - { - // 1010xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bk47), - }, - { - // 1011xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,11), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, bkc), - }, - { - // 1100xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, dph), - }, - { - // 1111xx10AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), INSN_MASK(20,4,15), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, pdp), - }, - { - // x000xx11AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), INSN_MASK(20,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, csr), - }, - { - // x001xx11AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), INSN_MASK(20,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, brc0), - }, - { - // x010xx11AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), INSN_MASK(20,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, brc1), - }, - { - // x011xx11AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), INSN_MASK(20,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, trn0), - }, - { - // x100xx11AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), INSN_MASK(20,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, trn1), - }, - { - // kkkkxx00AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btst K4, Smem, TC1), - }, - { - // kkkkxx01AAAAAAAI11011100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btst K4, Smem, TC2), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xdd, - .size = 0x03, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDDss00AAAAAAAI11011101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add Smem << Tx, [ACx,] ACy), - }, - { - // SSDDss01AAAAAAAI11011101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub Smem << Tx, [ACx,] ACy), - }, - { - // SSDDss10AAAAAAAI11011101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(addsub2cc Smem, ACx, Tx, TC1, TC2, ACy), - }, - { - // x%DDss11AAAAAAAI11011101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(18,ss), INSN_FLAG(20,DD), INSN_FLAG(22,R), LIST_END }, - .syntax = INSN_SYNTAX(mov [rnd(]Smem << Tx[)], ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xde, - .size = 0x03, - .id = TMS320C55_INS_ADDSUBCC, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDD0000AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(addsubcc Smem, ACx, TC1, ACy), - }, - { - // SSDD0001AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(addsubcc Smem, ACx, TC2, ACy), - }, - { - // SSDD0010AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(addsubcc Smem, ACx, TC1, TC2, ACy), - }, - { - // SSDD0011AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(subc Smem, [ACx,] ACy), - }, - { - // SSDD0100AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add Smem << #16, [ACx,] ACy), - }, - { - // SSDD0101AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub Smem << #16, [ACx,] ACy), - }, - { - // SSDD0110AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub ACx, Smem << #16, ACy), - }, - { - // ssDD1000AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(addsub Tx, Smem, ACx), - }, - { - // ssDD1001AAAAAAAI11011110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(subadd Tx, Smem, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xdf, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // FDDD000uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(]high_byte(Smem)[)], dst), - }, - { - // FDDD001uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(]low_byte(Smem)[)], dst), - }, - { - // xxDD010uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(]Smem[)], ACx), - }, - { - // SSDD100uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add [uns(]Smem[)], CARRY, [ACx,] ACy), - }, - { - // SSDD101uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub [uns(]Smem[)], BORROW, [ACx,] ACy), - }, - { - // SSDD110uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add [uns(]Smem[)], [ACx,] ACy), - }, - { - // SSDD111uAAAAAAAI11011111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,u), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub [uns(]Smem[)], [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe0, - .size = 0x03, - .id = TMS320C55_INS_BTST, - .insn = { - // FSSSxxxtAAAAAAAI11100000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,t), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(btst src, Smem, TCx), - }, -}, -{ - .byte = 0xe1, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - // DDSHIFTWAAAAAAAI11100001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(22,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov low_byte(Smem) << #SHIFTW, ACx), - }, -}, -{ - .byte = 0xe2, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - // DDSHIFTWAAAAAAAI11100010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(22,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov high_byte(Smem) << #SHIFTW, ACx), - }, -}, -{ - .byte = 0xe3, - .size = 0x03, - .id = TMS320C55_INS_BSET, - .insn = { - .i_list = (insn_item_t []) { - { - // FSSS1100AAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bset src, Smem), - }, - { - // FSSS1101AAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bclr src, Smem), - }, - { - // kkkk000xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstset K4, Smem, TC1), - }, - { - // kkkk001xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstset K4, Smem, TC2), - }, - { - // kkkk010xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstclr K4, Smem, TC1), - }, - { - // kkkk011xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstclr K4, Smem, TC2), - }, - { - // kkkk100xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstnot K4, Smem, TC1), - }, - { - // kkkk101xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,k4), LIST_END }, - .syntax = INSN_SYNTAX(btstnot K4, Smem, TC2), - }, - { - // FSSS111xAAAAAAAI11100011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bnot src, Smem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe4, - .size = 0x03, - .id = TMS320C55_INS_PSH, - .insn = { - .i_list = (insn_item_t []) { - { - // FSSSx0xxAAAAAAAI11100100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(psh src,Smem), - }, - { - // FDDDx1xxAAAAAAAI11100100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(pop dst, Smem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe5, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // 000010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov dp, Smem), - }, - { - // 000110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov cdp, Smem), - }, - { - // 001010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bsa01, Smem), - }, - { - // 001110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bsa23, Smem), - }, - { - // 010010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,18), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bsa45, Smem), - }, - { - // 010110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,22), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bsa67, Smem), - }, - { - // 011010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,26), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bsac, Smem), - }, - { - // 011110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,30), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov sp, Smem), - }, - { - // 100010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,34), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov ssp, Smem), - }, - { - // 100110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,38), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bk03, Smem), - }, - { - // 101010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,42), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bk47, Smem), - }, - { - // 101110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,46), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov bkc, Smem), - }, - { - // 110010xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,50), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov dph, Smem), - }, - { - // 111110xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,62), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov pdp, Smem), - }, - { - // x00011xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,5,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov csr, Smem), - }, - { - // x00111xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,5,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov brc0, Smem), - }, - { - // x01011xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,5,11), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov brc1, Smem), - }, - { - // x01111xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,5,15), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov trn0, Smem), - }, - { - // x10011xxAAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,5,19), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov trn1, Smem), - }, - { - // FSSS01x0AAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,1,0), INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov src, high_byte(Smem)), - }, - { - // FSSS01x1AAAAAAAI11100101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,1,1), INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov src, low_byte(Smem)), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe6, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - // KKKKKKKKAAAAAAAI11100110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K8), LIST_END }, - .syntax = INSN_SYNTAX(mov K8, Smem), - }, -}, -{ - .byte = 0xe7, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // SSss00xxAAAAAAAI11100111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,ss), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov ACx << Tx, Smem), - }, - { - // SSss10x%AAAAAAAI11100111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,R), INSN_FLAG(20,ss), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [rnd(]hi(ACx << Tx)[)], Smem), - }, - { - // SSss11u%AAAAAAAI11100111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,R), INSN_FLAG(17,u), INSN_FLAG(20,ss), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(] [rnd(]hi[(saturate](ACx << Tx)[)))], Smem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe8, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // SSxxx0x%AAAAAAAI11101000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,R), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [rnd(]hi(ACx)[)], Smem), - }, - { - // SSxxx1u%AAAAAAAI11101000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,R), INSN_FLAG(17,u), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(] [rnd(]hi[(saturate](ACx)[)))], Smem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xe9, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - // SSSHIFTWAAAAAAAI11101001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov ACx << #SHIFTW, Smem), - }, -}, -{ - .byte = 0xea, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - // SSSHIFTWAAAAAAAI11101010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov hi(ACx << #SHIFTW), Smem), - }, -}, -{ - .byte = 0xeb, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // FSSS1100AAAAAAAI11101011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov pair(TAx), dbl(Lmem)), - }, - { - // xxSS1101AAAAAAAI11101011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,13), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov ACx >> #1, dual(Lmem)), - }, - { - // xxSS10x0AAAAAAAI11101011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,1,0), INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov ACx, dbl(Lmem)), - }, - { - // xxSS10u1AAAAAAAI11101011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,1,1), INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(17,u), INSN_FLAG(20,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(]saturate(ACx)[)], dbl(Lmem)), - }, - { - // xxxx01xxAAAAAAAI11101011 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov reta, dbl(Lmem)), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xec, - .size = 0x03, - .id = TMS320C55_INS_AMAR, - .insn = { - .i_list = (insn_item_t []) { - { - // XDDD1110AAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,14), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,XDDD), LIST_END }, - .syntax = INSN_SYNTAX(amar Smem, XAdst), - }, - { - // FSSS000xAAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bset Baddr, src), - }, - { - // FSSS001xAAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bclr Baddr, src), - }, - { - // FSSS010xAAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(btstp Baddr, src), - }, - { - // FSSS011xAAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(bnot Baddr, src), - }, - { - // FSSS100tAAAAAAAI11101100 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,t), INSN_FLAG(20,FSSS), LIST_END }, - .syntax = INSN_SYNTAX(btst Baddr, src, TCx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xed, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // 00DD1010AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,10), INSN_MASK(22,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), pair(hi(ACx))), - }, - { - // 00DD1100AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,12), INSN_MASK(22,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), pair(LO(ACx))), - }, - { - // 00SS1110AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,14), INSN_MASK(22,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov pair(hi(ACx)), dbl(Lmem)), - }, - { - // 00SS1111AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,15), INSN_MASK(22,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov pair(lo(ACx)), dbl(Lmem)), - }, - { - // XDDD1111AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,15), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,XDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), XAdst), - }, - { - // XSSS0101AAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(16,4,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,XSSS), LIST_END }, - .syntax = INSN_SYNTAX(mov XAsrc, dbl(Lmem)), - }, - { - // SSDD000nAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add dbl(Lmem), [ACx,] ACy), - }, - { - // SSDD001nAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub dbl(Lmem), [ACx,] ACy), - }, - { - // SSDD010xAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub ACx, dbl(Lmem), ACy), - }, - { - // xxxx011xAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), reta), - }, - { - // xxDD100gAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,g), INSN_FLAG(20,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov[40] dbl(Lmem), ACx), - }, - { - // FDDD111xAAAAAAAI11101101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,FDDD), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), pair(TAx)), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xee, - .size = 0x03, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDD000xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(add dual(Lmem), [ACx,] ACy), - }, - { - // SSDD001xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub dual(Lmem), [ACx,] ACy), - }, - { - // SSDD010xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub ACx, dual(Lmem), ACy), - }, - { - // ssDD011xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(sub dual(Lmem), Tx, ACx), - }, - { - // ssDD100xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(add dual(Lmem), Tx, ACx), - }, - { - // ssDD101xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(sub Tx, dual(Lmem), ACx), - }, - { - // ssDD110xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(addsub Tx, dual(Lmem), ACx), - }, - { - // ssDD111xAAAAAAAI11101110 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(17,3,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(20,DD), INSN_FLAG(22,ss), LIST_END }, - .syntax = INSN_SYNTAX(subadd Tx, dual(Lmem), ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xef, - .size = 0x03, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // xxxx00mmAAAAAAAI11101111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), LIST_END }, - .syntax = INSN_SYNTAX(mov Cmem, Smem), - }, - { - // xxxx01mmAAAAAAAI11101111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), LIST_END }, - .syntax = INSN_SYNTAX(mov Smem, Cmem), - }, - { - // xxxx10mmAAAAAAAI11101111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), LIST_END }, - .syntax = INSN_SYNTAX(mov Cmem,dbl(Lmem)), - }, - { - // xxxx11mmAAAAAAAI11101111 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,2,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), LIST_END }, - .syntax = INSN_SYNTAX(mov dbl(Lmem), Cmem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xf0, - .size = 0x04, - .id = TMS320C55_INS_CMP, - .insn = { - // KKKKKKKKKKKKKKKKAAAAAAAI11110000 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K16), LIST_END }, - .syntax = INSN_SYNTAX(cmp Smem == K16, TC1), - }, -}, -{ - .byte = 0xf1, - .size = 0x04, - .id = TMS320C55_INS_CMP, - .insn = { - // KKKKKKKKKKKKKKKKAAAAAAAI11110001 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K16), LIST_END }, - .syntax = INSN_SYNTAX(cmp Smem == K16, TC2), - }, -}, -{ - .byte = 0xf2, - .size = 0x04, - .id = TMS320C55_INS_BAND, - .insn = { - // kkkkkkkkkkkkkkkkAAAAAAAI11110010 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,k16), LIST_END }, - .syntax = INSN_SYNTAX(band Smem, k16, TC1), - }, -}, -{ - .byte = 0xf3, - .size = 0x04, - .id = TMS320C55_INS_BAND, - .insn = { - // kkkkkkkkkkkkkkkkAAAAAAAI11110011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,k16), LIST_END }, - .syntax = INSN_SYNTAX(band Smem, k16, TC2), - }, -}, -{ - .byte = 0xf4, - .size = 0x04, - .id = TMS320C55_INS_AND, - .insn = { - // kkkkkkkkkkkkkkkkAAAAAAAI11110100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,k16), LIST_END }, - .syntax = INSN_SYNTAX(and k16, Smem), - }, -}, -{ - .byte = 0xf5, - .size = 0x04, - .id = TMS320C55_INS_OR, - .insn = { - // kkkkkkkkkkkkkkkkAAAAAAAI11110101 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,k16), LIST_END }, - .syntax = INSN_SYNTAX(or k16, Smem), - }, -}, -{ - .byte = 0xf6, - .size = 0x04, - .id = TMS320C55_INS_XOR, - .insn = { - // kkkkkkkkkkkkkkkkAAAAAAAI11110110 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,k16), LIST_END }, - .syntax = INSN_SYNTAX(xor k16, Smem), - }, -}, -{ - .byte = 0xf7, - .size = 0x04, - .id = TMS320C55_INS_ADD, - .insn = { - // KKKKKKKKKKKKKKKKAAAAAAAI11110111 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K16), LIST_END }, - .syntax = INSN_SYNTAX(add K16, Smem), - }, -}, -{ - .byte = 0xf8, - .size = 0x04, - .id = TMS320C55_INS_MPYMK, - .insn = { - .i_list = (insn_item_t []) { - { - // xxDDx0U%KKKKKKKKAAAAAAAI11111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K8), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(28,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpymk[r] [T3 = ]Smem, K8, ACx), - }, - { - // SSDDx1U%KKKKKKKKAAAAAAAI11111000 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K8), INSN_FLAG(24,R), INSN_FLAG(25,U), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(macmk[r] [T3 = ]Smem, K8, [ACx,] ACy), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xf9, - .size = 0x04, - .id = TMS320C55_INS_ADD, - .insn = { - .i_list = (insn_item_t []) { - { - // SSDD00xxuxSHIFTWAAAAAAAI11111001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(23,u), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(add [uns(]Smem[)] << #SHIFTW, [ACx,] ACy), - }, - { - // SSDD01xxuxSHIFTWAAAAAAAI11111001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(23,u), INSN_FLAG(28,DD), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(sub [uns(]Smem[)] << #SHIFTW, [ACx,] ACy), - }, - { - // xxDD10xxuxSHIFTWAAAAAAAI11111001 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,2,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(23,u), INSN_FLAG(28,DD), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(]Smem[)] << #SHIFTW, ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xfa, - .size = 0x04, - .id = TMS320C55_INS_MOV, - .insn = { - .i_list = (insn_item_t []) { - { - // SSxxx0x%xxSHIFTWAAAAAAAI11111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,1,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(24,R), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [rnd(]hi(ACx << #SHIFTW)[)], Smem), - }, - { - // SSxxx1x%uxSHIFTWAAAAAAAI11111010 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(26,1,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,SHIFTW), INSN_FLAG(23,u), INSN_FLAG(24,R), INSN_FLAG(30,SS), LIST_END }, - .syntax = INSN_SYNTAX(mov [uns(] [rnd(]hi[(saturate](ACx << #SHIFTW)[)))], Smem), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -{ - .byte = 0xfb, - .size = 0x04, - .id = TMS320C55_INS_MOV, - .insn = { - // KKKKKKKKKKKKKKKKAAAAAAAI11111011 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,K16), LIST_END }, - .syntax = INSN_SYNTAX(mov K16, Smem), - }, -}, -{ - .byte = 0xfc, - .size = 0x04, - .id = TMS320C55_INS_BCC, - .insn = { - // LLLLLLLLLLLLLLLLAAAAAAAI11111100 - .i_list = NULL, - .m_list = NULL, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,L16), LIST_END }, - .syntax = INSN_SYNTAX(bcc L16, ARn_mod ! = #0), - }, -}, -{ - .byte = 0xfd, - .size = 0x04, - .id = TMS320C55_INS_MPY, - .insn = { - .i_list = (insn_item_t []) { - { - // DDDDuug%000000mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,0), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000001mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,1), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000010mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,2), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000011mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,3), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000100mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,4), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000101mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,5), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000110mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,6), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%000111mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,7), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%001000mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,8), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx>>#16), - }, - { - // DDDDuug%001001mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,9), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mas[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%001010mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,10), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mpy[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%001011mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,11), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mac[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx>>#16), - }, - { - // DDDDuug%001100mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,12), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]Smem[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]Smem[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010000mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,16), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010001mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,17), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010010mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,18), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010011mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,19), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mpy[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010100mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,20), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mpy[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010101mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,21), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010110mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,22), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%010111mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,23), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%011000mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,24), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mac[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx>>#16), - }, - { - // DDDDuug%011001mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,25), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mas[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%011010mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,26), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mpy[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - { - // DDDDuug%011011mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,27), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mac[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy>>#16 :: mac[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx>>#16), - }, - { - // DDDDuug%011100mmAAAAAAAI11111101 - .i_list = NULL, - .m_list = (insn_mask_t []) { INSN_MASK(18,6,28), LIST_END }, - .f_list = (insn_flag_t []) { INSN_FLAG(8,AAAAAAAI), INSN_FLAG(16,mm), INSN_FLAG(24,R), INSN_FLAG(25,g), INSN_FLAG(26,uu), INSN_FLAG(28,DD), INSN_FLAG(30,DD), LIST_END }, - .syntax = INSN_SYNTAX(mas[r][40] [uns(]hi(Lmem)[)], [uns(]hi(Cmem)[)], ACy :: mas[r][40] [uns(]lo(Lmem)[)], [uns(]lo(Cmem)[)], ACx), - }, - LIST_END, - }, - .m_list = NULL, - .f_list = NULL, - .syntax = NULL, - }, -}, -}; diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus.c b/librz/arch/isa/tms320/c55x_plus/c55plus.c deleted file mode 100644 index 1738958da1..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/c55plus.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2013 th0rpe -// SPDX-FileCopyrightText: 2026 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -/** - * \file c55plus.c - * - * TMS320C55x+ disassembler glue layer. - * - * This is the entry point called from tms320_dasm() when asm.cpu is - * "c55x+". It hands the input bytes to the th0rpe c55plus_decode() - * walker, lower-cases the resulting mnemonic to match rizin's house - * style, and writes it into the caller's tms320_dasm_t. - * - * The decode walker is a global-state machine using extern ins_buff / - * ins_buff_len pointers -- keep this glue thin so the global setup - * stays in one place. - */ - -#include -#include -#include -#include - -#define USE_DECODE -#include "decode.h" - -#include "../tms320_dasm.h" - -extern ut8 *ins_buff; -extern ut32 ins_buff_len; -extern char *c55plus_decode(ut32 ins_pos, ut32 *next_ins_pos); - -int c55x_plus_disassemble(tms320_dasm_t *dasm, const ut8 *buf, int len) { - ut32 next_ins_pos = 0; - - ins_buff = (ut8 *)buf; - ins_buff_len = (ut32)len; - - char *ins_decoded = c55plus_decode(0, &next_ins_pos); - dasm->length = next_ins_pos; - if (!ins_decoded) { - return 0; - } - - /* Walker emits mixed-case mnemonics (e.g. "MOV", "AC0"); rizin - * convention is all-lowercase. */ - rz_str_case(ins_decoded, false); - snprintf(dasm->syntax, sizeof(dasm->syntax), "%s", ins_decoded); - free(ins_decoded); - - return next_ins_pos; -} diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus.h b/librz/arch/isa/tms320/c55x_plus/c55plus.h deleted file mode 100644 index 15769fdc61..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/c55plus.h +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef C55PLUS_H -#define C55PLUS_H - -#include -#include -#include -#include -#include - -#include "../tms320_dasm.h" - -extern int c55x_plus_disassemble(tms320_dasm_t *dasm, const ut8 *buf, int len); - -#endif diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c index eb622de97b..7211860d2a 100644 --- a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c @@ -3,879 +3,41 @@ // SPDX-FileCopyrightText: 2026 RizinOrg // SPDX-License-Identifier: LGPL-3.0-only -#include #include -#include #include #include "c55plus_analysis.h" -#include "c55plus.h" -#include "ins.h" -#include "../tms320c55x_insn.h" -#include "../tms320_dasm.h" +#include "c55plus_arch.h" +#include "../c55_ir.h" /** * \file c55plus_analysis.c * - * TMS320C55x+ analysis: classify opcodes, resolve branch targets, set - * basic-block fallthrough, fill in src/dst/val and stack effects. - * - * Pure byte-level dispatch -- no mnemonic-string matching. Each - * recognised opcode is dispatched on its leading byte (or leading byte - * + a small subset of the second byte where the prefix family is - * shared by multiple instructions). - * - * The encoding map below was extracted from SWPU104 chapter 6 (Dec - * 2006 'Algebraic Instruction Set' reference) and SWPU086 chapter 4 - * (May 2005 'CPU Reference Guide', Preliminary), then cross-validated - * against TI dis55.exe v4.3.6 (CCSv5 c55x_plus SDK, Feb 2010) on the - * testbins#289 c55xp corpus. - * - * Branch and control-flow encodings: - * - * 0x00 .. NOP / IDLE / RETI / to_word sec.6.5.11, sec.6.5.20, sec.6.5.16 - * 0x02 b1 B/CALL ACx (indirect register) sec.6.5.2, sec.6.5.6 - * 0x03 b1 INTR #k4 / TRAP #k4 sec.6.5.13, sec.6.5.19 - * 0x04 / 0x06 XCC predicated execute sec.6.5.9 - * 0x05 / 0x07 XCCPART sec.6.5.9 - * 0x08 RETCC sec.6.5.17 - * 0x20 NOP (1 byte) sec.6.5.11 - * 0x21 RET (1 byte) sec.6.5.16 - * 0x68 hh ll B short-relative sec.6.5.2 - * 0x69 hh ll CALL short-relative sec.6.5.6 - * 0x6A ss dst BCC short-relative (8-bit) sec.6.5.1 - * 0x6C / 0x6D RPT / RPTCC sec.6.5.14 - * 0x6E / 0x6F RPTBLOCAL / RPTB sec.6.5.14 - * 0x9A hh ll d BCC long-relative (16-bit) sec.6.5.1 - * 0x9B hh ll d CALLCC long-relative sec.6.5.5 - * 0x9C hh ll d B long-absolute (24-bit) sec.6.5.2 - * 0x9D hh ll d CALL long-absolute sec.6.5.6 - * 0x9E / 0x9F B / CALL with far() prefix sec.6.5.2, sec.6.5.6 - * 0xD8 ... BCC far-absolute (5-byte) sec.6.5.1 - * 0xD9 ... CALLCC far-absolute sec.6.5.5 - * 0xDA / 0xDB BCC / BCCU short-form sec.6.5.1 - * 0xDC / 0xDD BCC / BCCU sec.6.5.1 - * 0xDE / 0xDF BCC / BCCU sec.6.5.1 - * - * Stack-affecting: - * - * 0x0D PSHBOTH ACx (2 words) sec.6.7.6 - * 0x0E PSH dbl(ACx) (2 words) sec.6.7 - * 0x0F POP dbl(ACx) (2 words) - * 0x61 PSH dbl(mem) (2 words) - * 0x70 PSH dual-register (2 words) - * 0x71 POP dual-register - * 0x94 PSH ACx, mem (2 words) - * - * Byte-order note: C55x+ branch displacements and absolute targets are - * stored MSB-first within the instruction stream even though the - * surrounding processor is little-endian -- see SWPU104 sec.3.5. We use - * rz_read_at_be16() / rz_read_at_be24() from rz_endian.h to extract - * them unambiguously without unaligned-int dereference. + * TMS320C55x+ analysis. The instruction is decoded once by the shared + * decode-IR engine (c55_ir), and the analysis op -- type, branch targets, + * basic-block fall-through, src/dst/val, stack effects and instruction id -- + * together with the RzIL lift are both derived from that single decoded + * C55Insn. Anything the engine does not decode is reported as illegal. */ - -/* Helper: sign-extend an n-bit value (for short conditional branch - * relative displacements, which are 8-bit signed in some encodings - * and 16-bit signed in others). */ -static inline st32 sign_extend(ut32 v, ut32 bits) { - const ut32 mask = (1u << bits) - 1; - v &= mask; - if (v & (1u << (bits - 1))) { - return (st32)(v | ~mask); - } - return (st32)v; -} - -/* Set conditional-jump fields: type=cjmp, jump=target, fail=fallthrough. */ -static inline void set_cjmp(RzAnalysisOp *op, ut64 addr, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CJMP; - op->jump = target; - op->fail = addr + op->size; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set conditional-call fields: type=ccall, jump=target, fail=fallthrough. */ -static inline void set_ccall(RzAnalysisOp *op, ut64 addr, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CCALL; - op->jump = target; - op->fail = addr + op->size; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set unconditional-call fields: type=call, jump=target. */ -static inline void set_call(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_CALL; - op->jump = target; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Set unconditional-jump fields. */ -static inline void set_jmp(RzAnalysisOp *op, ut64 target) { - op->type = RZ_ANALYSIS_OP_TYPE_JMP; - op->jump = target; - op->direction = RZ_ANALYSIS_OP_DIR_EXEC; -} - -/* Mark an instruction as a return, with stack accounting. */ -static inline void set_ret(RzAnalysisOp *op) { - op->type = RZ_ANALYSIS_OP_TYPE_RET; - op->eob = true; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -2; -} - -/* Conditional return -- like RET but doesn't end the basic block - * (fallthrough is possible if the condition is false). */ -static inline void set_cret(RzAnalysisOp *op) { - op->type = RZ_ANALYSIS_OP_TYPE_CRET; - op->fail = op->addr + op->size; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -2; -} - -/* Stack push: write+decrement. Track the byte delta. */ -static inline void set_push(RzAnalysisOp *op, int delta) { - op->type = RZ_ANALYSIS_OP_TYPE_PUSH; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = delta; -} - -/* Stack pop: read+increment. Track the byte delta. */ -static inline void set_pop(RzAnalysisOp *op, int delta) { - op->type = RZ_ANALYSIS_OP_TYPE_POP; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = delta; -} - -/* Record an immediate value (for "mov #k, dst" / "add #k, dst" etc.). */ -static inline void set_imm(RzAnalysisOp *op, st64 val) { - op->val = (ut64)val; -} - -/* Record a memory access width (in bytes: 1, 2, or 4) for loads and - * stores whose effective address is computed at runtime. */ -static inline void set_mem_width(RzAnalysisOp *op, int width) { - op->refptr = width; - op->ptrsize = width; -} - -/* Set op->reg (destination register name) for instructions whose - * destination register is encoded statically in the leading byte(s). - * The string is borrowed and must point to static storage. */ -static inline void set_dst_reg(RzAnalysisOp *op, const char *name) { - op->reg = name; -} - -/* Set op->ireg (register used for indirect memory computation) for - * register-indirect loads, stores, branches and calls (e.g. B ACx, - * CALL ACx). The string is borrowed and must point to static storage. */ -static inline void set_ireg(RzAnalysisOp *op, const char *name) { - op->ireg = name; -} - -/* Set op->direction so higher-level analysis knows whether the op - * reads from memory (LOAD-style), writes to memory (STORE-style), - * jumps (EXEC), or just references an address (REF). */ -static inline void set_dir(RzAnalysisOp *op, RzAnalysisOpDirection dir) { - op->direction = dir; -} - -/* Set op->disp (displacement) for memory references that compute - * their effective address as `base_register + disp`. */ -static inline void set_disp(RzAnalysisOp *op, st64 disp) { - op->disp = (ut64)disp; -} - -/* ACx selector tables -- index 0..3 corresponds to AC0..AC3. */ -static const char *const c55xp_acc_names[4] = { "ac0", "ac1", "ac2", "ac3" }; - int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) { if (!op || !buf || len < 1) { return 0; } - const ut32 base_len = get_ins_len(buf[0]); - if (base_len == 0 || (int)base_len > len) { - return 0; - } - - /* base_len is derived from the leading byte alone and so under-counts - * the instructions that carry a variable k16/k24 offset operand in a - * Smem field (e.g. "MOV *ARn(#K16), ACx"). Ask the disassembler for the - * true length, which folds in those extra operand bytes; fall back to - * the leading-byte length if the decode fails. */ - ut32 ins_len = base_len; - tms320_dasm_t dasm = { 0 }; - int dlen = c55x_plus_disassemble(&dasm, buf, len); - if (dlen > 0 && dlen <= len && (ut32)dlen >= base_len) { - ins_len = (ut32)dlen; - } - op->addr = addr; - op->size = ins_len; op->type = RZ_ANALYSIS_OP_TYPE_NULL; - /* Surface the named instruction ID (Capstone-style op->id), resolved - * from the C55x+ token decoder's decoded mnemonic. The type-level - * dispatch below stays byte-driven because C55x+ packs many distinct - * instructions behind each leading byte (disambiguated by operand - * bits the analyzer already inspects); the ID gives consumers the - * exact mnemonic without re-deriving it. */ - op->id = tms320c55x_plus_insn_id_decode(buf, len); - - switch (buf[0]) { - /* ---- 0x00 family: NOP_16 / IDLE / RETI / to_word --------------- */ - case 0x00: - if (ins_len < 2) { - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; + C55Insn ci; + if (c55_decode(&c55plus_arch_desc, buf, len, &ci)) { + c55_fill_analysis(&c55plus_arch_desc, &ci, op); + if (mask & RZ_ANALYSIS_OP_MASK_IL) { + op->il_op = c55_lift(&c55plus_arch_desc, &ci, op->addr); } - switch (buf[1]) { - case 0x20: /* IDLE */ - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - case 0xc0: /* RETI */ - set_ret(op); - set_dst_reg(op, "sp"); - break; - default: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - } - break; - - /* ---- 0x01 family: rptsub --------------------------------------- */ - case 0x01: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - - /* ---- 0x02 family: B/CALL ACx indirect; both eob -----------------*/ - case 0x02: { - if (ins_len < 2) { - break; - } - /* Bit 7 of the second byte selects CALL (=1) vs B (=0). - * The low 2 bits of buf[1] select ACx (0..3). */ - const bool is_call = (buf[1] & 0x80) != 0; - const ut8 ac_idx = buf[1] & 0x03; - if (is_call) { - op->type = RZ_ANALYSIS_OP_TYPE_UCALL; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - set_dir(op, RZ_ANALYSIS_OP_DIR_EXEC); - } else { - op->type = RZ_ANALYSIS_OP_TYPE_UJMP; - op->eob = true; - set_dir(op, RZ_ANALYSIS_OP_DIR_EXEC); - } - set_ireg(op, c55xp_acc_names[ac_idx]); - op->fail = addr + ins_len; - break; - } - - /* ---- 0x03 family: INTR / TRAP / SWAP / SIM_TRIG --------------- - * Differentiated by buf[1] high nibble: - * 0x0?,0x1?,0x2?,0x3? -> intr #k5 (SWPU104 6.5.13) - * 0x4?,0x5? -> trap #k5 (SWPU104 6.5.19) - * 0x8?,0x9?,0xa?,0xb? -> swap regs (SWPU104 6.7.x) - * 0xc?..0xf? -> sim_trig (C55x+ silicon variant) - */ - case 0x03: - if (ins_len < 2) { - break; - } - switch (buf[1] & 0xc0) { - case 0x00: /* intr #k5 */ - op->type = RZ_ANALYSIS_OP_TYPE_SWI; - set_imm(op, buf[1] & 0x1f); - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - case 0x40: /* trap #k5 */ - op->type = RZ_ANALYSIS_OP_TYPE_TRAP; - set_imm(op, buf[1] & 0x1f); - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - case 0x80: /* swap */ - op->type = RZ_ANALYSIS_OP_TYPE_XCHG; - break; - case 0xc0: /* sim_trig - simulator trigger (C55x+ variant) */ - op->type = RZ_ANALYSIS_OP_TYPE_TRAP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - } - break; - - /* ---- 0x04 / 0x06: XCC (predicated execute) --------------------- */ - case 0x04: - case 0x06: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - - /* ---- 0x05 / 0x07: XCCPART -------------------------------------- */ - case 0x05: - case 0x07: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - - /* ---- 0x08: RETCC (conditional ret) ----------------------------- */ - case 0x08: - set_cret(op); - set_dst_reg(op, "sp"); - break; - - /* ---- 0x0A: BCLR/BSET status-register-bit ---------------------- */ - case 0x0a: - /* Modifies a status-reg bit -- surface as MOV (the closest fit - * in the rizin optype set; the immediate-side bit pattern is - * not extracted here). */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- 0x0C: AADD addr-add ------------------------------------- */ - case 0x0c: - /* AADD K8, SP -- prologue/epilogue frame adjustment. - * - * Semantic: SP = SP + K8 (signed). rizin's convention is - * that op->stackptr is the amount by which SP *decreases*, - * so for AADD that is -K8. See c55x_analysis.c 0x4e and - * rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - if (ins_len >= 2) { - const st8 k8 = (st8)buf[1]; - set_imm(op, k8); - set_dst_reg(op, "sp"); - set_disp(op, k8); - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = -k8; - } - break; - - /* ---- 0x0D: PSHBOTH ------------------------------------------- */ - case 0x0d: - op->type = RZ_ANALYSIS_OP_TYPE_UPUSH; - op->stackop = RZ_ANALYSIS_STACK_INC; - op->stackptr = 2; - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - - /* ---- 0x0E / 0x0F: PSH/POP dbl -------------------------------- */ - case 0x0e: - set_push(op, 2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - case 0x0f: - set_pop(op, -2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - break; - - /* ---- 0x20: NOP ----------------------------------------------- */ - case 0x20: - op->type = RZ_ANALYSIS_OP_TYPE_NOP; - break; - - /* ---- 0x21: RET ----------------------------------------------- */ - case 0x21: - set_ret(op); - set_dst_reg(op, "sp"); - break; - - /* ---- 0x24-0x26: PSH variants --------------------------------- */ - case 0x24: - case 0x25: - case 0x26: - set_push(op, 1); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - /* ---- 0x27: CIRC -- circular addressing helper ---------------- */ - case 0x27: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- 0x60: DELAY -- memory-delay move (TI SWPU104 sec.6.7.1, - * "Memory Delay", grouped under Move Operations). delay(Smem) - * copies the word at Smem to the next-higher address Smem+1 (a - * one-word memory-to-memory data shift used to build delay lines - * in filters): one data read, one data write. It is a data MOV, - * not a CPU/system-control op, so no FAMILY_CPU. -------------- */ - case 0x60: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_mem_width(op, 2); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - - /* ---- 0x61: PSH dbl(mem) -------------------------------------- */ - case 0x61: - set_push(op, 2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - - /* ---- 0x68: B short-relative (16-bit) ------------------------- */ - case 0x68: - if (ins_len >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_jmp(op, addr + 3 + sign_extend(disp, 16)); - op->eob = true; - } - break; - - /* ---- 0x69: CALL short-relative ------------------------------- */ - case 0x69: - if (ins_len >= 3) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_call(op, addr + 3 + sign_extend(disp, 16)); - op->fail = addr + ins_len; - } - break; - - /* ---- 0x6A: BCC short-relative -------------------------------- */ - case 0x6a: - if (ins_len >= 3) { - /* 6A ss dst -- ss is 8-bit signed displacement. */ - set_cjmp(op, addr, addr + 3 + sign_extend(buf[1], 8)); - } - break; - - /* ---- 0x6C: RPT ----------------------------------------------- */ - case 0x6c: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - /* ---- 0x6D: RPTCC --------------------------------------------- */ - case 0x6d: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - op->fail = addr + ins_len; - break; - /* ---- 0x6E/0x6F: RPTBLOCAL / RPTB ----------------------------- */ - case 0x6e: - case 0x6f: - op->type = RZ_ANALYSIS_OP_TYPE_REP; - break; - - /* ---- 0x70/0x71: PSH/POP dual-register ------------------------ */ - case 0x70: - set_push(op, 2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - case 0x71: - set_pop(op, -2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_READ); - break; - - /* ---- 0x72: ASUB addr-sub ------------------------------------- */ - case 0x72: - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - break; - - /* ---- 0x74-0x76: ADD/AND/{ABS/NEG/MAX/MIN} -------------------- */ - case 0x74: - /* ADD smem,ACx -- but bit 7 of buf[2] flips ADD <-> SUB - * for one of the addressing-mode subforms. */ - if (ins_len >= 3 && (buf[2] & 0x80)) { - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - } else { - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - } - break; - case 0x75: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; - case 0x76: - /* 0x76 family -- unary arithmetic on accumulators. The two - * MSB-of-byte bits select the subfamily: - * - * buf[1] & 0x80 == 0: ABS (buf[2]&0x80==0) or NEG (==1) - * buf[1] & 0x80 == 1: MAX (buf[2]&0x80==0) or MIN (==1) - * - * ABS has no clean rizin optype (the RZ_ANALYSIS_OP_TYPE_ABS - * code 44 is missing from the optypes table -- renders as - * 'undefined') so we leave it at NULL. */ - if (ins_len >= 3) { - const ut8 b1_top = buf[1] & 0x80; - const ut8 b2_top = buf[2] & 0x80; - if (b1_top) { - op->type = RZ_ANALYSIS_OP_TYPE_CMP; /* MAX/MIN */ - (void)b2_top; - } else if (b2_top) { - op->type = RZ_ANALYSIS_OP_TYPE_SUB; /* NEG */ - } else { - /* ABS -- see comment above */ - op->type = RZ_ANALYSIS_OP_TYPE_NULL; - } - } - break; - - /* ---- 0x77: MOV ----------------------------------------------- */ - case 0x77: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- 0x79: ROUND --------------------------------------------- */ - case 0x79: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- 0x7B: Ra = Ra +/- k4 | Ra <> #1 | Ra = k4 ------------- - * Per TI SWPU104 Table 7-2 (opcode 01111011): - * byte1 bit7 (0x80) set -> LD (Ra = k4) -> MOV - * byte1 bit7 (0x80) clear -> arithmetic/shift on Ra, where - * byte2 bit4 (0x10) set -> shift by 1 (SFTA) -> SHL - * byte2 bit4 (0x10) clr -> add/sub k4, with - * byte2 bit7 (0x80) set -> SUB else ADD - * The k4 immediate is the low nibble of byte2. */ - case 0x7b: - if (ins_len >= 3) { - if (buf[1] & 0x80) { - /* LD Ra, #k4 -- load immediate */ - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_imm(op, buf[2] & 0x0f); - } else if (buf[2] & 0x10) { - /* SFTA Ra, #1 -- arithmetic shift by one */ - op->type = (buf[2] & 0x80) ? RZ_ANALYSIS_OP_TYPE_SHR : RZ_ANALYSIS_OP_TYPE_SHL; - } else if (buf[2] & 0x80) { - /* SUB Ra, #k4 */ - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - set_imm(op, buf[2] & 0x0f); - } else { - /* ADD Ra, #k4 */ - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - set_imm(op, buf[2] & 0x0f); - } - } else { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - } - break; - - /* ---- 0x80/0x81/0x82: ADD/SUB long-imm ------------------------ */ - case 0x80: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break; - case 0x81: - case 0x82: op->type = RZ_ANALYSIS_OP_TYPE_SUB; break; - - /* ---- 0x84/0x85/0x86: AND/OR/XOR ------------------------------ */ - case 0x84: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; - case 0x85: op->type = RZ_ANALYSIS_OP_TYPE_OR; break; - case 0x86: op->type = RZ_ANALYSIS_OP_TYPE_XOR; break; - - /* ---- 0x89: BCLR --------------------------------------------- */ - case 0x89: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - - /* ---- 0x8D / 0x8E: ADD smem,ACx ------------------------------ */ - case 0x8d: - case 0x8e: - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - - /* ---- 0x90/0x91/0x92/0x93/0x94: various ----------------------- */ - case 0x90: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break; - case 0x91: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; /* btstclr */ - case 0x92: - case 0x93: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* sqrm */ - case 0x94: - set_push(op, 2); - set_dst_reg(op, "sp"); - set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE); - break; - - /* ---- 0x9A: BCC long-relative (16-bit) ------------------------ */ - case 0x9a: - if (ins_len >= 4) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16)); - } - break; - - /* ---- 0x9B: CALLCC long-relative ------------------------------ */ - case 0x9b: - if (ins_len >= 4) { - const ut32 disp = rz_read_at_be16(buf, 1); - set_ccall(op, addr, addr + ins_len + sign_extend(disp, 16)); - } - break; - - /* ---- 0x9C: B long-absolute (24-bit) -------------------------- */ - case 0x9c: - if (ins_len >= 4) { - set_jmp(op, rz_read_at_be24(buf, 1)); - op->eob = true; - } - break; - - /* ---- 0x9D: CALL long-absolute -------------------------------- */ - case 0x9d: - if (ins_len >= 4) { - set_call(op, rz_read_at_be24(buf, 1)); - op->fail = addr + ins_len; - } - break; - - /* ---- 0x9E / 0x9F: B / CALL with far() prefix ----------------- */ - case 0x9e: - if (ins_len >= 4) { - set_jmp(op, rz_read_at_be24(buf, 1)); - op->eob = true; - } - break; - case 0x9f: - if (ins_len >= 4) { - set_call(op, rz_read_at_be24(buf, 1)); - op->fail = addr + ins_len; - } - break; - - /* ---- 0xA1/0xA4/0xA6/0xA7/0xA8: arith & compare --------------- */ - case 0xa1: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break; - case 0xa4: op->type = RZ_ANALYSIS_OP_TYPE_CMP; break; - case 0xa6: - case 0xa7: op->type = RZ_ANALYSIS_OP_TYPE_SHL; break; /* SFTS/SFTL */ - case 0xa8: op->type = RZ_ANALYSIS_OP_TYPE_ROL; break; - case 0xa9: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* EXP */ - case 0xaa: - case 0xab: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; - case 0xae: - op->type = RZ_ANALYSIS_OP_TYPE_SUB; - break; - - /* ---- 0xB0: BCC short-form (3-byte conditional jump) ---------- */ - case 0xb0: - if (ins_len >= 4) { - const ut32 disp = rz_read_at_be16(buf, 2); - set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16)); - } - break; - case 0xb1: - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - case 0xb2: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - break; - case 0xb3: - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - - /* ---- 0xB8/0xB9/0xBA/0xBB: MAC/MPY families ------------------- */ - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; - case 0xbc: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; /* BFXTR */ - - /* ---- 0xC1/0xC3/0xC5: AND/OR/XOR long-imm --------------------- - * For 0xC5 specifically, bit 7 of buf[1] selects XOR (=1); - * if not XOR, bit 7 of buf[2] picks OR (=1) over AND (=0). The - * 0xC1 and 0xC3 slots are AND / OR variants for different - * addressing forms (mem vs ACx) -- they have only one operation. */ - case 0xc1: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; - case 0xc3: op->type = RZ_ANALYSIS_OP_TYPE_OR; break; - case 0xc5: - if (ins_len >= 3 && (buf[1] & 0x80)) { - op->type = RZ_ANALYSIS_OP_TYPE_XOR; - } else if (ins_len >= 3 && (buf[2] & 0x80)) { - op->type = RZ_ANALYSIS_OP_TYPE_OR; - } else { - op->type = RZ_ANALYSIS_OP_TYPE_AND; - } - break; - case 0xc6: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* BFXTR / BFXPA - bit-field extract / pack */ - case 0xc7: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* MPYK */ - case 0xc8: - case 0xc9: - case 0xca: - case 0xcb: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; - case 0xce: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* SQDST */ - - case 0xd1: - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; /* COPY */ - /* ---- 0xD2: mar(XDAa op k24) -- modify extended address register. - * Per TI SWPU104 Table 7-2 (opcode 11010010), byte1 bits 6-5 select - * 00 -> mar(XDAa - k24) (ASUB) - * 01 -> mar(XDAa + k24) (AMOV/AADD with +) - * 10 -> mar(XDAa = k24) (AMOV load) - * All are address-register arithmetic/loads, classified LEA (the - * same family as 0x14 AADD and 0x77 AMOV). The earlier code typed - * this SUB, which mis-classified the load and add forms. */ - case 0xd2: op->type = RZ_ANALYSIS_OP_TYPE_LEA; break; - case 0xd4: - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - break; /* MAXDIFF */ - - /* ---- 0xD8: BCC far-absolute (5-byte) ------------------------- */ - case 0xd8: - if (ins_len >= 5) { - set_cjmp(op, addr, rz_read_at_be24(buf, 1)); - } - break; - - /* ---- 0xD9: CALLCC far-absolute (5-byte) ---------------------- */ - case 0xd9: - if (ins_len >= 5) { - set_ccall(op, addr, rz_read_at_be24(buf, 1)); - } - break; - - /* ---- 0xDA-0xDF: BCC/BCCU register-compare conditional ------- */ - case 0xda: - case 0xdb: - case 0xdc: - case 0xdd: - case 0xde: - case 0xdf: - /* 5-byte form: DA/DB ARx cmp RRx ll hh dd (16-bit signed disp). - * The target is encoded as a relative 16-bit displacement - * at bytes 3..4 in BE byte order. */ - if (ins_len >= 5) { - const ut32 disp = rz_read_at_be16(buf, 3); - set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16)); - } - break; - - /* ---- 0xE0/0xE1/0xE2/0xE3/0xE8/0xE9/0xEC/0xED: MAC/MPY parallel */ - case 0xe0: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe8: - case 0xe9: - case 0xec: - case 0xed: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; - case 0xea: - case 0xeb: op->type = RZ_ANALYSIS_OP_TYPE_LEA; break; /* AMAR parallel */ - case 0xee: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* MPYK */ - - default: - /* MOV-family bytes 0x48-0x4F (mov #imm, mem) - common in - * real firmware, classified as MOV here. */ - if (buf[0] >= 0x48 && buf[0] <= 0x4f) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - } - /* Memory<->register MOV cluster, byte 0x50-0x5F. Per the - * SWPU104 encoding map: 0x50 (mem <- ARx high), 0x51-0x53 - * (mem <- ACx parts), 0x54-0x57 (COPY), 0x58 (mem -> ACx), - * 0x59 (mem<<16 -> ACx), 0x5A/0x5B (mem -> ACx halves), - * 0x5C (40-bit dbl mov), 0x5D (ACx >> 1 -> dbl mem). All - * MOV-family for analysis purposes; the 0x5C / 0x5D dbl - * forms access 4 bytes, the rest 2. */ - if (buf[0] >= 0x50 && buf[0] <= 0x5f) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_mem_width(op, (buf[0] == 0x5c || buf[0] == 0x5d) ? 4 : 2); - break; - } - /* AMAR family - address-modifying instructions (LEA-like). */ - if (buf[0] == 0x62 || buf[0] == 0x63) { - op->type = RZ_ANALYSIS_OP_TYPE_LEA; - break; - } - /* MOV-family bytes 0x88, 0x8A: ACx <-> mem variants. */ - if (buf[0] == 0x88 || buf[0] == 0x8a) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - } - /* 0x8C: ADD with carry, mem -> ACx. */ - if (buf[0] == 0x8c) { - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - } - /* 0x97: dual-mem MOV. */ - if (buf[0] == 0x97) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - } - /* 0xA0, 0xAC, 0xAD: MOV with parallel dual addressing or - * MOV #imm,ACx (long form). */ - if (buf[0] == 0xa0 || buf[0] == 0xac || buf[0] == 0xad) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - } - /* 0xB4, 0xB5: MOV with rounding / shift. */ - if (buf[0] == 0xb4 || buf[0] == 0xb5) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - break; - } - /* 0xB6, 0xB7: ADD with shift (T-register or immediate). */ - if (buf[0] == 0xb6 || buf[0] == 0xb7) { - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - } - /* 0xC0, 0xC2, 0xC4: ADD #k16 with optional shift. */ - if (buf[0] == 0xc0 || buf[0] == 0xc2 || buf[0] == 0xc4) { - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - } - /* 0xCC: dual-instruction packed encoding (ADD :: MOV). The - * primary operation that affects control flow / data flow - * is the ADD, so classify as ADD. */ - if (buf[0] == 0xcc) { - op->type = RZ_ANALYSIS_OP_TYPE_ADD; - break; - } - /* 0xD0: MOV ACx, dbl(*(#abs24)) -- 4-byte (dbl) memory move. */ - if (buf[0] == 0xd0) { - op->type = RZ_ANALYSIS_OP_TYPE_MOV; - set_mem_width(op, 4); - break; - } - /* 0x2E, 0x2F: XCCPART predicated execute. */ - if (buf[0] == 0x2e || buf[0] == 0x2f) { - op->type = RZ_ANALYSIS_OP_TYPE_CMP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - } - /* 0x0B (ecopr__), 0x23 (estop_byte): pseudo opcodes - * specific to this C55x+ silicon variant. Used as emulation / - * coprocessor traps; classify as TRAP. */ - if (buf[0] == 0x0b || buf[0] == 0x23) { - op->type = RZ_ANALYSIS_OP_TYPE_TRAP; - op->family = RZ_ANALYSIS_OP_FAMILY_CPU; - break; - } - /* Anything we have not catalogued: leave op->type at its - * default (NULL). We intentionally do NOT mark unknown - * leading bytes as ILL: many bytes in the 0x10..0x1f and - * 0x30..0x3f ranges are valid parallel-instruction prefixes - * (0x39 = MACK, etc.) that decode to multi-instruction - * forms only when paired with the right following bytes. - * Flagging them as ILL would mislead the basic-block - * walker and the colorizer, which treats ILL as - * "definitely-invalid" and renders the opcode in bold - * red. Leaving as NULL lets the disassembler's own - * "invalid" rendering speak for itself per-instruction. */ - break; - } - - /* The byte-level switch above handles control flow (targets, fail - * paths), stack deltas and operand fields, but it cannot always tell - * apart instructions that share a leading byte but differ by operand - * bits (e.g. ADD vs SUB, AND vs OR vs XOR, AMOV vs ASUB, PSH vs POP). - * The decoder already resolved the exact mnemonic, so for the - * arithmetic / logical / move / multiply / stack families take the - * authoritative type from the instruction id. Control flow, repeats, - * XCC and the like map to NULL here and keep the type the byte switch - * assigned. Stack-effect bookkeeping (stackop/stackptr) set above is - * preserved. */ - const int id_type = tms320c55x_insn_optype((TMS320C55InsID)op->id); - if (id_type != RZ_ANALYSIS_OP_TYPE_NULL) { - op->type = id_type; - } - - if (mask & RZ_ANALYSIS_OP_MASK_IL) { - op->il_op = tms320_c55x_plus_il_lift(op, dasm.syntax); + return op->size; } + op->type = RZ_ANALYSIS_OP_TYPE_ILL; + op->size = 1; return op->size; } diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.h b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.h index 26cff28db8..f73c188946 100644 --- a/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.h +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_analysis.h @@ -11,7 +11,5 @@ int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask); RZ_IPI RzAnalysisILConfig *tms320_c55x_plus_il_config(RZ_NONNULL RzAnalysis *analysis); -RZ_IPI RzAnalysisLiftedILOp tms320_c55x_plus_il_lift(RZ_NONNULL RzAnalysisOp *op, const char *syntax); -RZ_IPI RzAnalysisLiftedILOp tms320_c55x_il_lift(RZ_NONNULL RzAnalysisOp *op, const char *syntax); #endif /* ANALYSIS_C55_PLUS_H */ diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_arch.c b/librz/arch/isa/tms320/c55x_plus/c55plus_arch.c new file mode 100644 index 0000000000..a913d57554 --- /dev/null +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_arch.c @@ -0,0 +1,3778 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +/** + * \file + * TMS320C55x+ arch descriptor for the shared c55_ir decode engine. + * + * Table-driven replacement, built incrementally, for the th0rpe string decoder. + * Rows not yet present fall back to the legacy decoder in the plugin, so the + * cutover stays byte-exact at every step. The operand decode was reconstructed + * and verified against TI's dis55 disassembler as ground truth. + */ + +#include +#include "c55plus_arch.h" +#include "ins.h" // get_ins_len +#include "../tms320c55x_insn.h" // tms320c55x_insn_name, TMS320C55_INS_* + +// Register tables (persistent, so reg_info hands out stable name/il_var ptrs). +static const C55RegInfo ac_ri[32] = { + { "ac0", "ac0", 40 }, { "ac1", "ac1", 40 }, { "ac2", "ac2", 40 }, { "ac3", "ac3", 40 }, + { "ac4", "ac4", 40 }, { "ac5", "ac5", 40 }, { "ac6", "ac6", 40 }, { "ac7", "ac7", 40 }, + { "ac8", "ac8", 40 }, { "ac9", "ac9", 40 }, { "ac10", "ac10", 40 }, { "ac11", "ac11", 40 }, + { "ac12", "ac12", 40 }, { "ac13", "ac13", 40 }, { "ac14", "ac14", 40 }, { "ac15", "ac15", 40 }, + { "ac16", "ac16", 40 }, { "ac17", "ac17", 40 }, { "ac18", "ac18", 40 }, { "ac19", "ac19", 40 }, + { "ac20", "ac20", 40 }, { "ac21", "ac21", 40 }, { "ac22", "ac22", 40 }, { "ac23", "ac23", 40 }, + { "ac24", "ac24", 40 }, { "ac25", "ac25", 40 }, { "ac26", "ac26", 40 }, { "ac27", "ac27", 40 }, + { "ac28", "ac28", 40 }, { "ac29", "ac29", 40 }, { "ac30", "ac30", 40 }, { "ac31", "ac31", 40 }, +}; +static const C55RegInfo ar_ri[16] = { + { "ar0", "ar0", 16 }, { "ar1", "ar1", 16 }, { "ar2", "ar2", 16 }, { "ar3", "ar3", 16 }, + { "ar4", "ar4", 16 }, { "ar5", "ar5", 16 }, { "ar6", "ar6", 16 }, { "ar7", "ar7", 16 }, + { "ar8", "ar8", 16 }, { "ar9", "ar9", 16 }, { "ar10", "ar10", 16 }, { "ar11", "ar11", 16 }, + { "ar12", "ar12", 16 }, { "ar13", "ar13", 16 }, { "ar14", "ar14", 16 }, { "ar15", "ar15", 16 }, +}; +static const C55RegInfo xar_ri[16] = { + { "xar0", "xar0", 23 }, { "xar1", "xar1", 23 }, { "xar2", "xar2", 23 }, { "xar3", "xar3", 23 }, + { "xar4", "xar4", 23 }, { "xar5", "xar5", 23 }, { "xar6", "xar6", 23 }, { "xar7", "xar7", 23 }, + { "xar8", "xar8", 23 }, { "xar9", "xar9", 23 }, { "xar10", "xar10", 23 }, { "xar11", "xar11", 23 }, + { "xar12", "xar12", 23 }, { "xar13", "xar13", 23 }, { "xar14", "xar14", 23 }, { "xar15", "xar15", 23 }, +}; +static const C55RegInfo t_ri[4] = { + { "t0", "t0", 16 }, { "t1", "t1", 16 }, { "t2", "t2", 16 }, { "t3", "t3", 16 }, +}; +static const C55RegInfo sp_ri[2] = { { "sp", "sp", 16 }, { "ssp", "ssp", 16 } }; +static const C55RegInfo trn_ri[8] = { + { "trn0", "trn0", 16 }, { "trn1", "trn1", 16 }, { "trn2", "trn2", 16 }, { "trn3", "trn3", 16 }, + { "trn4", "trn4", 16 }, { "trn5", "trn5", 16 }, { "trn6", "trn6", 16 }, { "trn7", "trn7", 16 } +}; +static const C55RegInfo dp_ri[2] = { { "dp", "dp", 16 }, { "dph", "dph", 16 } }; + +// Special / status / loop / extended-pointer registers, indexed by their flat +// register-byte value (gr1 index). Gaps are NULL. Transcribed from TI dis55. +static const C55RegInfo special_ri[256] = { + [56] = { "csr", "csr", 16 }, + [57] = { "rptc", "rptc", 16 }, + [58] = { "brc0", "brc0", 16 }, + [59] = { "brc1", "brc1", 16 }, + [62] = { "config", NULL, 16 }, + [63] = { "cpurev", NULL, 16 }, + [148] = { "xssp", NULL, 23 }, + [149] = { "xsp", NULL, 23 }, + [150] = { "xdp", NULL, 23 }, + [152] = { "rsa0", NULL, 16 }, + [153] = { "rsa1", NULL, 16 }, + [154] = { "rea0", NULL, 16 }, + [155] = { "rea1", NULL, 16 }, + [156] = { "dbgpaddr", NULL, 16 }, + [157] = { "dbgpdata", NULL, 16 }, + [159] = { "reta", NULL, 16 }, + [180] = { "xssp.h", NULL, 23 }, + [181] = { "xsp.h", NULL, 23 }, + [182] = { "xdp.h", NULL, 23 }, + [183] = { "pdp", "pdp", 16 }, + [184] = { "bsa01", "bsa01", 16 }, + [185] = { "bsa23", "bsa23", 16 }, + [186] = { "bsa45", "bsa45", 16 }, + [187] = { "bsa67", "bsa67", 16 }, + [188] = { "bsac", "bsac", 16 }, + [189] = { "bkc", "bkc", 16 }, + [190] = { "bk03", "bk03", 16 }, + [191] = { "bk47", "bk47", 16 }, + // st0_55 status-bit names, indexed [192 + bit position], for the bclr/bset + // st0_ forms (opcode 0x0a). The lift recovers the bit position from the + // register index. + [192] = { "st0_dp07", NULL, 16 }, [193] = { "st0_dp08", NULL, 16 }, + [194] = { "st0_dp09", NULL, 16 }, [195] = { "st0_dp10", NULL, 16 }, + [196] = { "st0_dp11", NULL, 16 }, [197] = { "st0_dp12", NULL, 16 }, + [198] = { "st0_dp13", NULL, 16 }, [199] = { "st0_dp14", NULL, 16 }, + [200] = { "st0_dp15", NULL, 16 }, [201] = { "st0_acov1", NULL, 16 }, + [202] = { "st0_acov0", NULL, 16 }, [203] = { "st0_carry", NULL, 16 }, + [204] = { "st0_tc2", NULL, 16 }, [205] = { "st0_tc1", NULL, 16 }, + [206] = { "st0_acov3", NULL, 16 }, [207] = { "st0_acov2", NULL, 16 }, + [224] = { "st0", NULL, 16 }, + [225] = { "st1", NULL, 16 }, + [226] = { "st2", NULL, 16 }, + [227] = { "st3", NULL, 16 }, + [228] = { "st0_55", "st0_55", 16 }, + [229] = { "st1_55", "st1_55", 16 }, + [231] = { "st3_55", "st3_55", 16 }, + [232] = { "ier0", NULL, 16 }, + [233] = { "ier1", NULL, 16 }, + [234] = { "ifr0", NULL, 16 }, + [235] = { "ifr1", NULL, 16 }, + [236] = { "dbier0", NULL, 16 }, + [237] = { "dbier1", NULL, 16 }, + [238] = { "ivpd", NULL, 16 }, + [239] = { "ivph", NULL, 16 }, + [240] = { "rsa0.h", NULL, 16 }, + [241] = { "rsa1.h", NULL, 16 }, + [242] = { "rea0.h", NULL, 16 }, + [243] = { "rea1.h", NULL, 16 }, + [244] = { "bios", NULL, 16 }, + [245] = { "brs1", "brs1", 16 }, + [246] = { "iir", NULL, 16 }, + [247] = { "ber", NULL, 16 }, + [248] = { "rsa0.l", NULL, 16 }, + [249] = { "rsa1.l", NULL, 16 }, + [250] = { "rea0.l", NULL, 16 }, + [251] = { "rea1.l", NULL, 16 }, + [252] = { "tsdr", NULL, 16 }, +}; + +static const C55RegInfo *c55plus_reg_info(C55RegClass cls, ut8 num, C55SubReg sub) { + (void)sub; // sub-field (.l/.h/.g) shows in the name via the formatter + switch (cls) { + case C55_RC_AC: return num < 32 ? &ac_ri[num] : NULL; + case C55_RC_AR: return num < 16 ? &ar_ri[num] : NULL; + case C55_RC_XAR: return num < 16 ? &xar_ri[num] : NULL; + case C55_RC_T: return num < 4 ? &t_ri[num] : NULL; + case C55_RC_SP: return num < 2 ? &sp_ri[num] : NULL; + case C55_RC_DP: return num < 2 ? &dp_ri[num] : NULL; + case C55_RC_SPECIAL: return special_ri[num].name ? &special_ri[num] : NULL; + case C55_RC_TRN: return num < 8 ? &trn_ri[num] : NULL; + default: return NULL; + } +} + +// The flat register byte (TI "register field 1"), full 0-255 decode transcribed +// from dis55. Structured classes (ac/ar/xar/t/sp/dp, with .h/.l/.g sub-fields) +// are returned as such; the status/loop/extended-pointer registers are returned +// as C55_RC_SPECIAL carrying the raw index. Unused indices yield C55_RC_NONE. +// NB: like dis55/th0rpe, ac16.g-ac31.g (208-223) are intentionally not decoded. +static void c55plus_gr1(ut8 idx, C55Reg *r) { + r->cls = C55_RC_NONE; + r->num = 0; + r->sub = C55_SUB_NONE; + if (idx < 32) { + r->cls = C55_RC_AC; r->num = idx; + } else if (idx < 48) { + r->cls = C55_RC_AR; r->num = (ut8)(idx - 32); + } else if (idx < 52) { + r->cls = C55_RC_T; r->num = (ut8)(idx - 48); + } else if (idx == 52) { + r->cls = C55_RC_SP; r->num = 1; + } else if (idx == 53) { + r->cls = C55_RC_SP; r->num = 0; + } else if (idx == 54) { + r->cls = C55_RC_DP; r->num = 0; + } else if (idx < 64) { + r->cls = C55_RC_SPECIAL; r->num = idx; // 56-59,62,63 (55/60/61 -> NULL) + } else if (idx < 96) { + r->cls = C55_RC_AC; r->num = (ut8)(idx - 64); r->sub = C55_SUB_HI; + } else if (idx < 128) { + r->cls = C55_RC_AC; r->num = (ut8)(idx - 96); r->sub = C55_SUB_LO; + } else if (idx < 144) { + r->cls = C55_RC_XAR; r->num = (ut8)(idx - 128); + } else if (idx < 160) { + r->cls = C55_RC_SPECIAL; r->num = idx; // 148-150 xssp/xsp/xdp, 152-159 rsa/rea/... + } else if (idx < 176) { + r->cls = C55_RC_XAR; r->num = (ut8)(idx - 160); r->sub = C55_SUB_HI; + } else if (idx < 192) { + r->cls = C55_RC_SPECIAL; r->num = idx; // 180-191 xssp.h/pdp/bsa/bk... + } else if (idx < 208) { + r->cls = C55_RC_AC; r->num = (ut8)(idx - 192); r->sub = C55_SUB_GUARD; // ac0.g-ac15.g + } else if (idx < 224) { + ; // 208-223 unused (ac16.g-ac31.g not decoded, matching dis55/th0rpe) + } else { + r->cls = C55_RC_SPECIAL; r->num = idx; // 224-252 st/ier/ifr/rsa.h/... + } +} + +static ut8 c55plus_reg_width(const C55ArchDesc *a, const C55Reg *r) { + const C55RegInfo *ri = a->reg_info ? a->reg_info(r->cls, r->num, r->sub) : NULL; + return ri ? ri->width : 16; +} + +// mov source byte: low 7 bits select the register (gr1); bit 7 set => "<< #16". +// Extended (pointer-capable) register decode, shared by the mov destination +// (leading byte >= 0x80) and the mov source (when the destination selects the +// extended form). \p h is the operand byte masked to 7 bits. Leaves cls NONE +// for the encodings TI rejects (xar16..xar19 / xar23..xar31). +static void c55plus_xptr(ut8 h, C55Reg *out) { + out->sub = C55_SUB_NONE; + if (h & 0x20) { + ut8 n = (ut8)(h & 0x1f); + if (n < 16) { + out->cls = C55_RC_XAR; + out->num = n; + } else if (n == 20) { + out->cls = C55_RC_SPECIAL; + out->num = 148; // xssp + } else if (n == 21) { + out->cls = C55_RC_SPECIAL; + out->num = 149; // xsp + } else if (n == 22) { + out->cls = C55_RC_SPECIAL; + out->num = 150; // xdp + } else { + out->cls = C55_RC_NONE; + out->num = 0; + } + } else { + out->cls = C55_RC_AC; + out->num = (ut8)(h & 0x1f); + } +} + +static void c55plus_x_reg_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 b = (ut8)((bits >> d->lo) & 0xff); + ut8 dst = (ut8)((bits >> 8) & 0xff); // byte1 selects regular vs extended form + C55Reg r = { C55_RC_NONE, 0, C55_SUB_NONE }; + out->kind = C55_OP_REG; + if (dst >= 0x80) { + // extended (xar-destination) form: the source is pointer-capable too + // and the high bit is part of the register select, not a << #16 flag. + c55plus_xptr((ut8)(b & 0x7f), &r); + } else { + c55plus_gr1((ut8)(b & 0x7f), &r); + if (b & 0x80) { + out->sh_left = true; + out->shamt = 16; + } + } + out->reg = r; + out->width = c55plus_reg_width(a, &out->reg); +} + +// mov destination byte: < 0x80 uses gr1; >= 0x80 selects the pointer-capable set +// (bit 5 of the low 7 picks XAR / extended pointer over a plain accumulator). +static void c55plus_x_reg_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 b = (ut8)((bits >> d->lo) & 0xff); + C55Reg r = { C55_RC_NONE, 0, C55_SUB_NONE }; + if (b < 0x80) { + c55plus_gr1(b, &r); + } else { + c55plus_xptr((ut8)(b & 0x7f), &r); + } + out->kind = C55_OP_REG; + out->reg = r; + out->width = c55plus_reg_width(a, &r); +} + +// A-unit register-register operand (opcode 0x72, "mar(WDAa op WDAb)"): a 6-bit +// WDA register field (bits[5:0] of the byte at \ref C55OpDesc.lo), decoded by +// c55plus_xptr (bit5 picks xar/xssp/xsp/xdp over an accumulator). The top bit +// of each operand byte selects the operation and is handled by the table match, +// not here. Shared by amov (MX), aadd (AX) and asub (SX). +static void c55plus_x_wda(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 b = (ut8)((bits >> d->lo) & 0x3f); + out->kind = C55_OP_REG; + c55plus_xptr(b, &out->reg); + if (out->reg.cls == C55_RC_NONE) { + out->kind = C55_OP_INVALID; + return; + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// psh/pop register byte: gr1 decode, with full accumulators and full pointer +// registers (xar) rendered as dbl(...) (a 40-/32-bit double push/pop). +static void c55plus_x_reg_pshpop(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + ut8 b = (ut8)((bits >> d->lo) & 0xff); + c55plus_gr1(b, &out->reg); + out->kind = C55_OP_REG; + // Double (40-/32-bit) push/pop is designated by the operand byte range, not the + // resulting register class: 0x00-0x1f (accumulators) and 0x80-0x9f (full pointer + // and extended-double registers) render as dbl(...). + if (b < 0x20 || (b >= 0x80 && b < 0xa0)) { + out->dbl = true; + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// call/branch target field: the raw offset, displayed as a 24-bit address +// (#0x00xxxx). The PC-relative resolution into an xref is done by the analysis. +static void c55plus_x_addr(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut64 mask = (d->width >= 32) ? 0xffffffffULL : (((ut64)1 << d->width) - 1); + out->kind = C55_OP_IMM; + out->imm = (bits >> d->lo) & mask; + out->width = d->width; + out->addr = true; +} + +// Absolute 24-bit call/branch target (opcodes 0x9d call, ... ): the operand +// value is the destination address itself (24-bit program space), rendered as +// #0xNNNNNN, not a pc-relative displacement. +static void c55plus_x_addr24(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = (bits >> d->lo) & 0xffffff; + out->width = 24; + out->addr = true; + out->abs_target = true; +} + +// Condition-field register set (TI "register field 4"): 0-7 acN, 8-11 tN, +// 16-23 arN, 24-31 acN.l; 12-15 unused. +static void c55plus_cond_reg(ut8 idx5, C55Reg *r) { + r->cls = C55_RC_NONE; + r->num = 0; + r->sub = C55_SUB_NONE; + if (idx5 < 8) { + r->cls = C55_RC_AC; r->num = idx5; + } else if (idx5 < 12) { + r->cls = C55_RC_T; r->num = (ut8)(idx5 - 8); + } else if (idx5 >= 16 && idx5 < 24) { + r->cls = C55_RC_AR; r->num = (ut8)(idx5 - 16); + } else if (idx5 >= 24 && idx5 < 32) { + r->cls = C55_RC_AC; r->num = (ut8)(idx5 - 24); r->sub = C55_SUB_LO; + } +} + +// Condition byte (bcc/xcc/...): either "reg #0" (register field 4 with a +// relop in the top bits, or an xar at 0xc0-0xdf) or a status-bit flag expression +// (0xe0-0xff). Reconstructed from th0rpe get_opers; verified against dis55. +static void c55plus_x_cond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + static const C55Relop relmap[6] = { + C55_REL_EQ, C55_REL_NE, C55_REL_LT, C55_REL_GE, C55_REL_GT, C55_REL_LE + }; + ut8 b = (ut8)((bits >> d->lo) & 0xff); + out->kind = C55_OP_COND; + out->imm = 0; + if (b >= 0xe0) { + out->cond_is_flag = true; + out->cond_flag = (ut8)(b - 0xe0); // 0xee/0xef map to empty flag slots + return; + } + ut8 oper_type = (ut8)(b >> 5); + if (oper_type == 6) { // 0xc0-0xdf: xar comparison + out->reg.cls = C55_RC_XAR; + out->reg.num = (ut8)(b & 0x0f); + out->relop = (((b >> 4) - 12) == 0) ? C55_REL_EQ : C55_REL_NE; + } else { // 0x00-0xbf: register-field-4 comparison vs #0 + c55plus_cond_reg((ut8)(b & 0x1f), &out->reg); + out->relop = relmap[oper_type]; + } +} + +// Register-register long-branch condition (opcodes 0xda/0xdb): two gr1 registers +// Ra (byte1 & 0x7f) and Rb (byte2 & 0x7f) compared with a 2-bit relop split +// across byte1 bit 7 (high) and byte2 bit 7 (low): 0 ==, 1 !=, 2 <, 3 >=. Signed +// (0xda) vs unsigned (0xdb) is carried by uns_all (per table row), as for the +// reg-immediate forms. The 16-bit field passed in is byte1:byte2. +static void c55plus_x_cond_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + static const C55Relop relmap[4] = { C55_REL_EQ, C55_REL_NE, C55_REL_LT, C55_REL_GE }; + ut16 field = (ut16)((bits >> d->lo) & 0xffff); + ut8 b1 = (ut8)(field >> 8); + ut8 b2 = (ut8)(field & 0xff); + out->kind = C55_OP_COND; + out->cmp_to_reg = true; + out->relop = relmap[((b1 & 0x80) ? 2 : 0) | ((b2 & 0x80) ? 1 : 0)]; + c55plus_gr1((ut8)(b1 & 0x7f), &out->reg); // Ra + c55plus_gr1((ut8)(b2 & 0x7f), &out->index); // Rb +} + +// Register-register compare condition (opcode 0xa4 CMPR_RR): Ra = gr1(byte1 & +// 0x7f), Rb = gr1(byte2 & 0x7f), 2-bit relop in byte3[3:2] (0 ==, 1 !=, 2 <, +// 3 >=). Signed (cmp) vs unsigned (cmpu) is the byte3[5] $ bit, carried by the +// instruction uns_all flag (set per table row), not decoded here. +static void c55plus_x_cmpcond(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + static const C55Relop relmap[4] = { C55_REL_EQ, C55_REL_NE, C55_REL_LT, C55_REL_GE }; + ut8 b1 = (ut8)((bits >> 16) & 0xff); + ut8 b2 = (ut8)((bits >> 8) & 0xff); + ut8 b3 = (ut8)(bits & 0xff); + out->kind = C55_OP_COND; + out->cmp_to_reg = true; + out->relop = relmap[(b3 >> 2) & 0x3]; + c55plus_gr1((ut8)(b1 & 0x7f), &out->reg); // Ra + c55plus_gr1((ut8)(b2 & 0x7f), &out->index); // Rb +} + +// TC status-flag destination of the 0xa4 compare: byte3 bit 0 selects tc1 (0) +// or tc2 (1); rendered/lifted via the shared cond-flag ids (tc1 = 4, tc2 = 5). +static void c55plus_x_tcflag(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(4 + (bits & 0x1)); +} + + +// or tc2 (1); byte1[7] negates it (!tcN). The shared cmpand/cmpor lift maps +// cond-flag ids 4/5 to tc1/tc2 and 20/21 to their negations. +static void c55plus_x_cmp_tcin(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 sel = (ut8)((bits >> 1) & 0x1); // byte3[1] + ut8 neg = (ut8)((bits >> 23) & 0x1); // byte1[7] + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)((neg ? 20 : 4) + sel); +} + + +// compared to a 7-bit immediate. The 2-bit relop is split across byte1 bit 7 +// (high) and byte2 bit 7 (low): 0 ==, 1 !=, 2 <, 3 >= (TI get_cmp_op order). +// Signed (0xdc, bcc) vs unsigned (0xdd, bccu) is carried by the instruction's +// uns_all flag (set per table row), not decoded here. The 16-bit field passed +// in is byte1:byte2. +static void c55plus_x_cond_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + static const C55Relop relmap[4] = { C55_REL_EQ, C55_REL_NE, C55_REL_LT, C55_REL_GE }; + ut16 field = (ut16)((bits >> d->lo) & 0xffff); + ut8 b1 = (ut8)(field >> 8); + ut8 b2 = (ut8)(field & 0xff); + c55plus_gr1((ut8)(b1 & 0x7f), &out->reg); + out->kind = C55_OP_COND; + out->relop = relmap[((b1 & 0x80) ? 2 : 0) | ((b2 & 0x80) ? 1 : 0)]; + out->imm = (ut64)(b2 & 0x7f); + out->cmp_imm = true; +} + +// As c55plus_x_cond_imm, but for the upper-half immediate compare-and-branch +// (opcodes 0xde/0xdf): the 8-bit compare constant has its high bit implicitly +// set, covering the 0x80-0xff range that the 0xdc/0xdd 7-bit forms cannot. +static void c55plus_x_cond_imm_hi(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55plus_x_cond_imm(a, bits, d, out); + if (out->kind == C55_OP_COND) { + out->imm |= 0x80; + } +} + +static const char *c55plus_mnemonic(ut16 id) { + return tms320c55x_insn_name((TMS320C55InsID)id); +} + +// id -> RzAnalysisOp type, matching the legacy byte-driven analyzer so the +// analysis path can be served from the shared decode for the ported opcodes. +static ut32 c55plus_op_type(ut16 id) { + switch ((TMS320C55InsID)id) { + case TMS320C55_INS_NOP: + case TMS320C55_INS_NOP_16: + case TMS320C55_INS_IDLE: return RZ_ANALYSIS_OP_TYPE_NOP; + case TMS320C55_INS_RETCC: return RZ_ANALYSIS_OP_TYPE_CRET; + case TMS320C55_INS_RET: + case TMS320C55_INS_RETI: return RZ_ANALYSIS_OP_TYPE_RET; + case TMS320C55_INS_RESET: return RZ_ANALYSIS_OP_TYPE_TRAP; + case TMS320C55_INS_MOV: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_COPY: return RZ_ANALYSIS_OP_TYPE_MOV; + // A-unit address arithmetic: amov/amar/asub load or adjust an address (LEA), + // aadd is an address-add (ADD). The asub IL comes from the shared AREG_SUB + // lift; its analysis type matches the legacy decoder (LEA). + case TMS320C55_INS_AMOV: return RZ_ANALYSIS_OP_TYPE_LEA; + case TMS320C55_INS_AMAR: return RZ_ANALYSIS_OP_TYPE_LEA; + case TMS320C55_INS_ASUB: return RZ_ANALYSIS_OP_TYPE_LEA; + case TMS320C55_INS_AADD: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_MPYK: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MACK: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_XCC: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_XCCPART: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_ADD: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_SUB: return RZ_ANALYSIS_OP_TYPE_SUB; + // neg/min/max register ops (opcode 0x76 and the 0x34/0x2e/0x30 forms): neg is + // a subtract-from-zero; min/max are modelled as compares, matching the legacy + // analysis. abs has no dedicated op-type (left as the default). + case TMS320C55_INS_NEG: return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_MAX: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_MIN: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_AND: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_OR: return RZ_ANALYSIS_OP_TYPE_OR; + case TMS320C55_INS_XOR: return RZ_ANALYSIS_OP_TYPE_XOR; + case TMS320C55_INS_SFTL: return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_SFTS: return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_SFTSC: return RZ_ANALYSIS_OP_TYPE_SHL; + case TMS320C55_INS_RPT: return RZ_ANALYSIS_OP_TYPE_REP; + case TMS320C55_INS_RPTADD: return RZ_ANALYSIS_OP_TYPE_REP; + case TMS320C55_INS_RPTSUB: return RZ_ANALYSIS_OP_TYPE_REP; + case TMS320C55_INS_RPTB: return RZ_ANALYSIS_OP_TYPE_REP; + case TMS320C55_INS_RPTBLOCAL: return RZ_ANALYSIS_OP_TYPE_REP; + case TMS320C55_INS_RPTCC: return RZ_ANALYSIS_OP_TYPE_REP; + // round / satr / sat / circ: control / rounding moves (no branch effect). + case TMS320C55_INS_ROUND: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_SAT: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_CIRC: return RZ_ANALYSIS_OP_TYPE_MOV; + // software interrupt / trap vectors. + case TMS320C55_INS_INTR: return RZ_ANALYSIS_OP_TYPE_SWI; + case TMS320C55_INS_TRAP: return RZ_ANALYSIS_OP_TYPE_TRAP; + case TMS320C55_INS_ESTOP: return RZ_ANALYSIS_OP_TYPE_TRAP; + case TMS320C55_INS_ECOPR: return RZ_ANALYSIS_OP_TYPE_TRAP; + case TMS320C55_INS_SIM_TRIG: return RZ_ANALYSIS_OP_TYPE_TRAP; + // register exchange. + case TMS320C55_INS_SWAP: return RZ_ANALYSIS_OP_TYPE_XCHG; + // rotate through carry. + case TMS320C55_INS_ROL: return RZ_ANALYSIS_OP_TYPE_ROL; + case TMS320C55_INS_ROR: return RZ_ANALYSIS_OP_TYPE_ROR; + // exponent (leading-bit count). + case TMS320C55_INS_EXP: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BCNT: return RZ_ANALYSIS_OP_TYPE_MOV; + // conditional shift. + case TMS320C55_INS_SFTCC: return RZ_ANALYSIS_OP_TYPE_SHL; + // mantissa / negated-exponent dual helper. + case TMS320C55_INS_MANT: return RZ_ANALYSIS_OP_TYPE_MOV; + // dual-data-memory multiply family. + case TMS320C55_INS_MPYM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MACM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MASM: return RZ_ANALYSIS_OP_TYPE_MUL; + // register multiply family. + case TMS320C55_INS_MPY: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MAC: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MAS: return RZ_ANALYSIS_OP_TYPE_MUL; + // constant-coefficient memory multiply. + case TMS320C55_INS_MPYMK: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_MACMK: return RZ_ANALYSIS_OP_TYPE_MUL; + // memory squaring multiply. + case TMS320C55_INS_SQRM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQAM: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_SQSM: return RZ_ANALYSIS_OP_TYPE_MUL; + // dual compare-and-select-difference (Viterbi) ops. + case TMS320C55_INS_MAXDIFF: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_MINDIFF: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_DMAXDIFF: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_DMINDIFF: return RZ_ANALYSIS_OP_TYPE_CMP; + // dual-data-memory distance / LMS primitives. + case TMS320C55_INS_ABDST: return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_SQDST: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_LMS: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_LMSF: return RZ_ANALYSIS_OP_TYPE_MUL; + // FIR symmetric / antisymmetric primitives. + case TMS320C55_INS_FIRSADD: return RZ_ANALYSIS_OP_TYPE_MUL; + case TMS320C55_INS_FIRSSUB: return RZ_ANALYSIS_OP_TYPE_MUL; + // dual-access subtract-add. + case TMS320C55_INS_SUBADD: return RZ_ANALYSIS_OP_TYPE_ADD; + // conditional subtract. + case TMS320C55_INS_SUBC: return RZ_ANALYSIS_OP_TYPE_SUB; + case TMS320C55_INS_ADDSUBCC: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_ADDSUB2CC: return RZ_ANALYSIS_OP_TYPE_ADD; + case TMS320C55_INS_CMP: return RZ_ANALYSIS_OP_TYPE_CMP; + case TMS320C55_INS_NOT: return RZ_ANALYSIS_OP_TYPE_NOT; + case TMS320C55_INS_BFXTR: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BFXPA: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BTST: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTCLR: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTNOT: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BAND: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTP: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BTSTSET: return RZ_ANALYSIS_OP_TYPE_AND; + case TMS320C55_INS_BCLR: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BSET: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_BNOT: return RZ_ANALYSIS_OP_TYPE_XOR; + case TMS320C55_INS_PSH: return RZ_ANALYSIS_OP_TYPE_PUSH; + case TMS320C55_INS_PSHBOTH: return RZ_ANALYSIS_OP_TYPE_PUSH; + case TMS320C55_INS_POPBOTH: return RZ_ANALYSIS_OP_TYPE_POP; + case TMS320C55_INS_DELAY: return RZ_ANALYSIS_OP_TYPE_MOV; + case TMS320C55_INS_POP: return RZ_ANALYSIS_OP_TYPE_POP; + case TMS320C55_INS_CALL: return RZ_ANALYSIS_OP_TYPE_CALL; + case TMS320C55_INS_CALLCC: return RZ_ANALYSIS_OP_TYPE_CCALL; + // b: the base type is the register-indirect UJMP; c55_effective_type + // refines it to a direct JMP when the operand is an immediate target. + case TMS320C55_INS_B: return RZ_ANALYSIS_OP_TYPE_UJMP; + case TMS320C55_INS_BCC: return RZ_ANALYSIS_OP_TYPE_CJMP; + default: return RZ_ANALYSIS_OP_TYPE_NULL; + } +} + +static ut8 c55plus_insn_len(const ut8 *buf, int len) { + return (len > 0) ? (ut8)get_ins_len(buf[0]) : 0; +} + +// gr1 of the operand byte's low 7 bits: the arithmetic/logic reg-reg operands +// strip the high bit (which selects the operation), with no shift or extended form. +static void c55plus_x_gr7(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> d->lo) & 0x7f), &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} + +// 16-bit immediate operand (rendered hex/unsigned, like the legacy decoder). +static void c55plus_x_imm16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = (bits >> d->lo) & 0xffff; + out->width = 16; +} + +// rptblocal #l8 (opcode 0x6e, 3 bytes): local block-repeat with the 8-bit block- +// end label in byte2 (byte1 unused), rendered as a zero-padded 24-bit address +// (#0x0000NN), mirroring the 0x6f rptb. Lifted to a nop, as the legacy decoder +// does for the repeat op type. +static void c55plus_x_rptblocal_lbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xff; // byte2 + out->width = 24; + out->addr = true; +} + +// rptb #l16 (opcode 0x6f, 3 bytes): block-repeat with the 16-bit block-end label +// in bytes 1:2, rendered as a zero-padded 24-bit address (#0x0000NN). Lifted to a +// nop (the block-repeat control has no data effect of its own), as the legacy +// decoder does for the repeat op type. +static void c55plus_x_rptb_lbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte1:byte2 + out->width = 24; + out->addr = true; +} + +// 6-bit shift count S6 (the sfts/sftl/shift-ALU forms): rendered as the raw +// 6-bit field (#0x..); the lifter sign-extends it from the 6-bit width to pick +// the shift direction and magnitude. +static void c55plus_x_shift6(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = (bits >> d->lo) & 0x3f; + out->width = 6; + out->imm_signed = false; +} + +// 6-bit shift count rendered joined to the preceding operand as "<< #S6" (the +// "Rb << #S6" source of the shift-ALU forms). The lifter takes it as an +// unsigned 8-bit left-shift amount. +static void c55plus_x_shift6_shl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55plus_x_shift6(a, bits, d, out); + out->shl_join = true; +} + +// 4-bit immediate k4 of the 0x7b register-short forms (mov/add/sub #k4, Ra): +// byte2[3:0], zero-extended. +static void c55plus_x_k4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + out->kind = C55_OP_IMM; + out->imm = (bits >> d->lo) & 0xf; + out->width = 4; +} + +// Negative 4-bit immediate of the 0x7b "mov -#k4, Ra" form: the magnitude is +// byte2[3:0]; rendered with the minus sign and left unlifted (as the legacy). +static void c55plus_x_negk4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 k = (ut8)((bits >> d->lo) & 0xf); + out->kind = C55_OP_IMM; + out->imm = (ut64)(-(st64)k); + out->imm_signed = true; + out->neg_imm = true; + out->width = 4; +} + +// Decode the single data-memory (Smem) addressing mode shared by the C55x+ +// register-indirect group. +// +// byte1 (buf[1]) carries the base AR (bits[3:0]) and a 4-bit sub-field +// (bits[7:4]); byte2's top two bits (buf[2] bits[7:6]) select the mode group. +// Only the byte2[7:6]==00 register-modify matrix and plain indirect +// (byte2[7:6]==10 with a zero offset) are decoded here; the indexed short(#K), +// bit-reverse, scaled, xar15, DP-direct and SP-relative forms still need +// formatter support, so the helper returns false and the caller signals +// C55_OP_INVALID to fall back to the legacy decoder. +static bool c55plus_smem_amode(ut8 b_ar, ut8 b_mode, C55Operand *out) { + ut8 grp = (ut8)((b_mode >> 6) & 3); + out->kind = C55_OP_MEM; + out->access = 16; + c55plus_gr1((ut8)(32 + (b_ar & 0x0f)), &out->reg); // base ARn + if (grp == 2) { + // byte2[7:6]==10: byte1[7:4] is a const offset; 0 -> indirect, + // non-zero -> indexed short(#K). + ut8 k = (ut8)((b_ar >> 4) & 0x0f); + if (k) { + out->amode = C55_AM_INDEXED; + out->disp = k; + } else { + out->amode = C55_AM_INDIRECT; + } + return true; + } + if (grp == 1) { + // byte2[7:6]==01: the bit-reverse / scaled / extended addressing group. + // byte1[7:4] selects the sub-mode (byte1[3:0] is the base ARn). The + // const-index sub-modes take a 2-byte extension, filled by the decoder. + ut8 sub = (ut8)((b_ar >> 4) & 0x0f); + switch (sub) { + case 0: // *(arN-t0b) reverse-carry decrement + out->amode = C55_AM_BITREV_SUB; + c55plus_gr1(48, &out->index); // t0 + return true; + case 1: // *(arN+t0b) reverse-carry increment + out->amode = C55_AM_BITREV; + c55plus_gr1(48, &out->index); // t0 + return true; + case 2: // *arN(t0<<#1) + case 3: // *arN(t1<<#1) + out->amode = C55_AM_IDXSCALE; + c55plus_gr1((ut8)(48 + (sub - 2)), &out->index); + return true; + case 7: // *arN(xar15) + out->amode = C55_AM_XAR15; + return true; + case 8: out->amode = C55_AM_CONST_IDX; return true; // *arN(#K16) + case 9: out->amode = C55_AM_CONST_IDX_PRE; return true; // *+arN(#K16) + case 0xe: // *(#k) long absolute (3-byte / 24-bit extension) + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = C55_ABS_EXT; + out->reg.cls = C55_RC_NONE; + return true; + default: + // a (abs16) / b (port) / c,d (24-bit const-index) and the reserved + // 4/5/6/f: not represented here yet. + return false; + } + } + if (grp != 0) { // 11 (direct / sp / mmap) + // byte2[7:6]==11: SP-relative direct or memory-mapped. byte1[7]==1 is + // *sp(#k) with byte1[6:0] the offset; byte1[7]==0 is the @#k data-page + // direct form with byte1[6:0] the address. + if (b_ar & 0x80) { + out->amode = C55_AM_INDEXED; + out->reg.cls = C55_RC_SP; + out->reg.num = 0; + out->disp = (ut8)(b_ar & 0x7f); + return true; + } + out->amode = C55_AM_DIRECT; + out->disp = (ut8)(b_ar & 0x7f); + return true; + } + // byte2[7:6]==00: register-modify matrix, sub = byte1[7:4]. + ut8 sub = (ut8)((b_ar >> 4) & 0x0f); + ut8 op2 = (ut8)((sub >> 1) & 3); // sub[2:1] selects the operation + ut8 tidx = (ut8)(((sub & 8) >> 2) | (sub & 1)); // 0->t0,1->t1,2->t2,3->t3 + switch (op2) { + case 0: // no index: pre/post increment/decrement + out->amode = (sub & 8) ? ((sub & 1) ? C55_AM_PREINC : C55_AM_PREDEC) + : ((sub & 1) ? C55_AM_POSTINC : C55_AM_POSTDEC); + break; + case 1: + out->amode = C55_AM_IDXREG; + c55plus_gr1((ut8)(48 + tidx), &out->index); + break; + case 2: + out->amode = C55_AM_POSTSUB; + c55plus_gr1((ut8)(48 + tidx), &out->index); + break; + default: + out->amode = C55_AM_POSTADD; + c55plus_gr1((ut8)(48 + tidx), &out->index); + break; + } + return true; +} + +// Smem source for mov Smem, ACx (opcode 0x58): byte2 bit5 is the uns qualifier, +// which the formatter cannot render yet, so it forces a legacy fallback. +static void c55plus_x_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // buf[1] + ut8 b_mode = (ut8)(bits & 0xff); // buf[2] + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if ((b_mode >> 5) & 1) { // uns() qualifier: zero-extend on load + out->uns = true; + } +} + +// Smem destination for mov ACx.h/.l, Smem (opcode 0x51). Here byte2 bit5 +// selects the accumulator half rather than the uns qualifier, so the address +// is decoded regardless of it (the half is rendered by c55plus_x_ac_part). +static void c55plus_x_smem_dest(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); + ut8 b_mode = (ut8)(bits & 0xff); + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} + +// Accumulator destination for the Smem load group: ACx where x = byte2[4:0]. +static void c55plus_x_smem_ac(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} + +// Accumulator-half source for the Smem store group: ACx.h (byte2[5]==0) or +// ACx.l (byte2[5]==1), x = byte2[4:0] (gr1 high/low sub-register slots). +static void c55plus_x_ac_part(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_mode = (ut8)(bits & 0xff); + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((b_mode & 0x1f) + (((b_mode >> 5) & 1) ? 96 : 64)), &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} + +// Byte-access memory operand of "mov byte(*Smem), ACx" / "mov ACx, byte(*Smem)" +// (opcode 0x8a, base 4 bytes). The addressing is the standard Smem layout +// (byte1 = base ARn + offset/sub field, byte2[7:6] = group), decoded by +// c55plus_smem_amode; this form marks an 8-bit byte() access (byte_sel 3). +// byte3[5] is the uns() qualifier on a load. The high_byte()/low_byte() variants +// (byte3[7]==0) are handled by separate disasm-only rows. +// Shared addressing decode for the 0x5b/0x5c/0x8a accumulator load/store forms: +// the standard Smem layout (byte1 + byte2[7:6]) plus the absolute *(#addr) form +// (byte2[7:6]==01 with byte1==0xe0; the 24-bit address follows as a 3-byte +// extension) and the SP-relative *sp(#k) form (byte2[7:6]==11 with byte1[7]==1). +// Returns false (-> legacy fallback) for the DP-direct and other modes +// c55plus_smem_amode does not represent. +static bool c55plus_mem_addr(ut8 b_ar, ut8 b_mode, C55Operand *out) { + ut8 grp = (ut8)((b_mode >> 6) & 3); + if (grp == 1 && b_ar == 0xe0) { + out->kind = C55_OP_MEM; + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = C55_ABS_EXT; + return true; + } + if (grp == 1) { + // *arN(#K16): long const-index; the 16-bit offset follows as a 2-byte + // extension (filled by the decoder). Base ARn is byte1[3:0]. + out->kind = C55_OP_MEM; + out->amode = C55_AM_CONST_IDX; + c55plus_gr1((ut8)(32 + (b_ar & 0x0f)), &out->reg); + return true; + } + if (grp == 3 && ((b_ar >> 7) & 1)) { + out->kind = C55_OP_MEM; + out->amode = C55_AM_INDEXED; + c55plus_gr1(53, &out->reg); // sp + out->disp = (ut8)(b_ar & 0x7f); + return true; + } + return c55plus_smem_amode(b_ar, b_mode, out); +} + +static void c55plus_x_byte_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // buf[1] + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // buf[2] + ut8 b_op = (ut8)(bits & 0xff); // buf[3] + if (!c55plus_mem_addr(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 8; + out->byte_sel = 3; // plain byte() + if (((b_op >> 6) & 3) == 3 && ((b_op >> 5) & 1)) { + out->uns = true; // uns() byte load + } +} + +// Register operand of the 0x8a byte-access mov: the register number is +// byte2[4:0] (the low nibble plus the +16 bank bit), and byte3[1:0] selects the +// sub-form (0 = whole accumulator, 1 = ARn, 2 = ACx.h, 3 = ACx.l). +static void c55plus_x_byte_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // buf[2] + ut8 b_op = (ut8)(bits & 0xff); // buf[3] + ut8 num = (ut8)(b_mode & 0x1f); // 0..31 + out->kind = C55_OP_REG; + switch (b_op & 3) { + case 0: // whole accumulator ACn + c55plus_gr1(num, &out->reg); + break; + case 1: // ARn (low 4 bits select ar0-15) + c55plus_gr1((ut8)(32 + (num & 0x0f)), &out->reg); + break; + case 2: // ACn.h + c55plus_gr1((ut8)(64 + num), &out->reg); + break; + default: // ACn.l + c55plus_gr1((ut8)(96 + num), &out->reg); + break; + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// Word (16-bit) memory source of "mov *Smem, ACx.l" (opcode 0x5b, base 3 bytes): +// the standard Smem / absolute / SP-relative addressing, a 16-bit access loaded +// into the low half of an accumulator. +static void c55plus_x_word_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // buf[1] + ut8 b_mode = (ut8)(bits & 0xff); // buf[2] + if (!c55plus_mem_addr(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 16; +} + +// Double-word (32-bit) memory source of "mov dbl(*Smem), ACx" (opcode 0x5c, +// base 3 bytes): the same addressing, a 32-bit dbl() access sign-extended into a +// whole accumulator. +static void c55plus_x_dbl_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // buf[1] + ut8 b_mode = (ut8)(bits & 0xff); // buf[2] + if (!c55plus_mem_addr(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 32; + out->dbl = true; +} + +// Byte() memory destination of "mov #imm, byte(*Smem)" (opcode 0x4c, base 3 +// bytes): the same Smem / absolute / SP-relative addressing as the load forms, +// an 8-bit byte() access. The const-index addressing supplies its 16-bit offset +// as a 2-byte extension (handled by the decoder). +static void c55plus_x_byte_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // buf[1] + ut8 b_mode = (ut8)(bits & 0xff); // buf[2] + if (!c55plus_mem_addr(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 8; + out->byte_sel = 3; // plain byte() +} + +// 6-bit immediate source in byte2[5:0] of "mov #imm, byte(*Smem)" (opcode 0x4c). +static void c55plus_x_imm6(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0x3f; + out->width = 16; +} + +// The 0x4d/0x4e/0x4f immediate byte stores reuse byte2[5:0] for the low six bits +// of the constant; the two high bits come from the opcode (0x4d -> 0x40, 0x4e -> +// 0x80, 0x4f -> 0xc0), so the four 0x4c-0x4f opcodes together cover the full +// 8-bit immediate range. +static void c55plus_x_imm6_40(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55plus_x_imm6(a, bits, d, out); + out->imm |= 0x40; +} +static void c55plus_x_imm6_80(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55plus_x_imm6(a, bits, d, out); + out->imm |= 0x80; +} +static void c55plus_x_imm6_c0(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + c55plus_x_imm6(a, bits, d, out); + out->imm |= 0xc0; +} + +// A-unit pointer register in byte1[6:0] of the amov/asub #k16 forms (opcode +// 0xae): 0x00-0x0f select AR0-15, 0x10-0x13 select T0-3. Other encodings are +// special registers the shared lifter does not model and fall back to legacy. +// A-unit pointer register in byte1[4:0] of the amov/aadd/asub #k16 forms (opcode +// 0xae, the ARn/Tx register-type rows): 0x00-0x0f select AR0-15, 0x10-0x13 select +// T0-3. Other encodings are special registers the shared lifter does not model +// and fall back to legacy. +static void c55plus_x_areg16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 r = (ut8)((bits >> 16) & 0x1f); // byte1[4:0] + out->kind = C55_OP_REG; + if (r < 0x10) { + c55plus_gr1((ut8)(32 + r), &out->reg); // ar0-15 + } else if (r < 0x14) { + c55plus_gr1((ut8)(48 + (r - 0x10)), &out->reg); // t0-3 + } else { + out->kind = C55_OP_INVALID; + return; + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// xar register in byte1[3:0] of the 0xae xar-type rows (amov/aadd/asub #k16). +static void c55plus_x_xar16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + ((bits >> 16) & 0x0f)), &out->reg); // xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// 16-bit immediate in byte2:byte3 of the 0xae amov/asub forms. +static void c55plus_x_k16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; + out->width = 16; +} + +// xar destination in byte1[3:0] of the 0xd2 amov/asub #k24 forms. +static void c55plus_x_xar_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + ((bits >> 24) & 0x0f)), &out->reg); // byte1[3:0] -> xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// 24-bit immediate in byte2:byte3:byte4 of the 0xd2 amov/asub forms. +static void c55plus_x_k24(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffffff; + out->width = 24; +} + +// sp destination and 8-bit immediate of "aadd #k8, sp" (opcode 0x0c). +static void c55plus_x_sp(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)bits; + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1(53, &out->reg); // sp + out->width = c55plus_reg_width(a, &out->reg); +} + +// Absolute *(#addr) memory operand with the 24-bit byte address inline in +// bytes 2-4 (opcode 0xd0 register stores). The access width distinguishes the +// double-word and word forms. +static void c55plus_x_abs_dbl(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_MEM; + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = bits & 0xffffff; + out->access = 32; + out->dbl = true; +} + +static void c55plus_x_abs_word(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_MEM; + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = bits & 0xffffff; + out->access = 16; +} + +// ar source ARn in byte1[3:0] of the 0xd0 ar word store. +static void c55plus_x_ar_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(32 + ((bits >> 24) & 0x0f)), &out->reg); // ar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// Whole-accumulator / xar / accumulator-half source in byte1[4:0] of the 0xd0 +// register stores (the register type is fixed per row). +static void c55plus_x_acc_b1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // ac0-31 + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_xar_b1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + ((bits >> 24) & 0x0f)), &out->reg); // xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// Accumulator half in byte1[4:0] of the 0xd0 ACx.h / ACx.l word store (the half +// is fixed per row by the .hi flag passed through C55OpDesc). +static void c55plus_x_acc_hi_b1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(64 + ((bits >> 24) & 0x1f)), &out->reg); // ac.h + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_acc_lo_b1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(96 + ((bits >> 24) & 0x1f)), &out->reg); // ac.l + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_k8(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xff; + out->width = 16; +} + +// Accumulator branch target in byte1[4:0] of "b ACx" / "call ACx" (opcode 0x02): +// the 24-bit jump address is held in the (whole) accumulator. +static void c55plus_x_acc_b1lo(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte1[4:0] -> ac0-31 + out->width = c55plus_reg_width(a, &out->reg); +} + +// The optional "|| local()" parallel qualifier of the b/call ACx forms (opcode +// 0x02, byte1[6]); rendered after the register, joined by " || ". Absent (no +// operand emitted) when the bit is clear. +static void c55plus_x_b1_local(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + if ((bits >> 6) & 1) { // byte1[6] + out->kind = C55_OP_IMM; // inert; rendered verbatim via raw + out->raw = "local()"; + out->qual_join = true; + } else { + out->kind = C55_OP_NONE; + } +} +// The optional "|| far()" parallel qualifier (opcode 0x02, byte1[5]). +static void c55plus_x_b1_far(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + if ((bits >> 5) & 1) { // byte1[5] + out->kind = C55_OP_IMM; // inert; rendered verbatim via raw + out->raw = "far()"; + out->qual_join = true; + } else { + out->kind = C55_OP_NONE; + } +} + +// Multiply-by-constant family (opcodes 0xc7 5-byte / 0xee 6-byte). Field layout +// of the packed instruction word: +// byte1[4:0] dst ACx, byte1[6:5] selects the rounding (r) / fractional (f) +// variant (decoded by the shared mods mechanism); +// byte2[7] selects mack (1) over mpyk (0); byte2[4:0] the mack accumulate ACx; +// byte3 the source: byte3[4:0] ACx, byte3[6:5] the sub-register (0 whole, +// 2 .h, 3 .l); +// the trailing byte(s) the unsigned constant. +// The k8 immediate of the 5-byte 0xc7 form (byte4). +static void c55plus_x_mpyk_imm8(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xff; + out->width = 16; +} + +// The k16 immediate of the 6-byte 0xee form (byte4:byte5). +static void c55plus_x_mpyk_imm16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; + out->width = 16; +} + +// The source register (byte3): ACx whole / .h / .l. +static void c55plus_x_mpyk_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x7f), &out->reg); // byte3[6:0]: ac / ac.h / ac.l / Tx / special + out->width = c55plus_reg_width(a, &out->reg); +} + +// The destination accumulator (byte1[4:0]). +static void c55plus_x_mpyk_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// The mack accumulate accumulator (byte2[4:0]). +static void c55plus_x_mpyk_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// The 6-byte 0xee form has the same fields shifted up one byte: dst byte1[4:0] +// at bits 36:32, src byte3 at bits 23:16, mack accumulate byte2[4:0] at bits +// 28:24, and the 16-bit immediate in byte4:byte5. +static void c55plus_x_mpyk6_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x7f), &out->reg); // byte3[6:0]: ac / ac.h / ac.l / Tx / special + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_mpyk6_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 32) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_mpyk6_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// Dual-data-memory (Xmem/Ymem) register-modify operand of the 0xc8 multiply +// family. The operand byte carries the base ARn in bits [3:0] and a 3-bit +// addressing mode in bits [6:4]: 0 *arN-, 1 *arN+, 2 *arN(t0), 3 *arN, 4 +// *(arN-t0), 5 *(arN-t1), 6 *(arN+t0), 7 *(arN+t1). The MAC lift promotes the +// ARn base to its XARn pointer when forming the address. `uns` marks an unsigned +// operand (the uns() wrapper). +static void c55plus_x_mac_mem(const C55ArchDesc *a, ut8 ab, bool uns, C55Operand *out) { + (void)a; + out->kind = C55_OP_MEM; + out->access = 16; + c55plus_gr1((ut8)(32 + (ab & 0x0f)), &out->reg); // base ARn + out->uns = uns; + switch ((ab >> 4) & 0x7) { + case 0: out->amode = C55_AM_POSTDEC; break; + case 1: out->amode = C55_AM_POSTINC; break; + case 2: out->amode = C55_AM_IDXREG; c55plus_gr1(48, &out->index); break; // t0 + case 3: out->amode = C55_AM_INDIRECT; break; + case 4: out->amode = C55_AM_POSTSUB; c55plus_gr1(48, &out->index); break; // t0 + case 5: out->amode = C55_AM_POSTSUB; c55plus_gr1(49, &out->index); break; // t1 + case 6: out->amode = C55_AM_POSTADD; c55plus_gr1(48, &out->index); break; // t0 + default: out->amode = C55_AM_POSTADD; c55plus_gr1(49, &out->index); break; // t1 + } +} +// Xmem of the 0xc8 multiply family: byte1[6:4] mode, byte1[3:0] ARn; byte2[5] +// is the Xmem uns() bit. +static void c55plus_x_mac_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 24) & 0x7f), ((bits >> 21) & 1) != 0, out); +} +// Ymem of the 0xc8 multiply family: byte3[6:4] mode, byte3[3:0] ARn; byte4[5] +// is the Ymem uns() bit. +static void c55plus_x_mac_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 8) & 0x7f), ((bits >> 5) & 1) != 0, out); +} +// ACy destination of the 0xc8 multiply family: byte2[4:0]. +static void c55plus_x_mac_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +// ACx accumulator source of the accumulating 0xc8 forms (macm / masm): byte4[4:0]. +static void c55plus_x_mac_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte4[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// Register multiply family (opcode 0xaa, 4 bytes): mpy / mac / mas SRC1, SRC2, +// ACdst (and the round / fractional variants mpyr / macr / macf / ...). SRC1 is +// gr1(byte2[6:0]); SRC2 is gr1(byte3[6:0]) with byte3[7] the uns() wrapper; +// ACdst is ac(byte1[4:0]). The operation is selected by (byte1[7], byte2[7]): +// (0,0) mpy, (0,1) mac, (1,0) mas; the (1,1) four-operand form stays on the +// legacy decoder. byte1[5] is the round (r) flag and byte1[6] the fractional (f) +// flag, decoded through the shared .mods packing. +static void c55plus_x_macr_src1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x7f), &out->reg); // byte2[6:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_macr_src2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x7f), &out->reg); // byte3[6:0] + out->width = c55plus_reg_width(a, &out->reg); + out->uns = (bits & 0x80) != 0; // byte3[7] +} +static void c55plus_x_macr_acdst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// mov [rnd]([uns](*Smem) << Tx), ACx (opcode 0xb4, byte3[6]==1, 5 bytes): load a +// data-memory word shifted left by a register amount into an accumulator. The +// Smem is byte1:byte2 (the shared register-modify / indexed decode), the shift +// register is gr1(byte4[6:0]) rendered as " << ", byte3[5] is the uns() +// wrapper and byte2[5] the rnd() wrapper. The accumulator destination is +// byte2[4:0]. (The byte3[6]==0 store forms and saturating variants stay on the +// legacy decoder.) +// mant ACa, ACb :: nexp ACa, ACc (opcode 0xa9, byte1[7]==0 && byte2[7]==1, 4 +// bytes): the dual mantissa / negated-exponent helper. ops[0] is the shared ACa = +// ac(byte3[4:0]), ops[1] is ACb = ac(byte2[4:0]), ops[2] is ACc = gr1(byte1[6:0]). +// The custom "mant ... :: nexp ..." rendering is keyed on the mant_nexp flag. +// maxdiff / mindiff / dmaxdiff / dmindiff ACc, ACd, ACa, ACb, [pair(]trnN[)] +// (opcode 0xd4, 5 bytes): the dual compare-and-select-difference (Viterbi) ops. +// ops [0]=ACc (byte3[4:0]), [1]=ACd (byte4[4:0]), [2]=ACa (byte1[4:0]), +// [3]=ACb (byte2[4:0]), [4]=trn (byte4[7:5] ^ 6). byte1[7] selects the d-variant +// (bare trn) and byte2[7] the min-variant. Rendered via the diff_form flag. +// abdst / lms / lmsf / sqdst Xmem, Ymem, ACx, ACy (opcode 0xce, 5 bytes): the +// dual-data-memory distance / LMS primitives. Xmem is byte1 (the shared dual-mem +// register-modify matrix), Ymem is byte3, ACx is byte2[4:0] and ACy is byte4[4:0]. +// The operation is selected by (byte1[7], byte3[7], byte2[7]); these lift via the +// shared C55_LOP_SQDST / ABDST / LMS dual-operation path (ops [0]=Xmem [1]=Ymem +// [2]=ACx [3]=ACy). +// subadd Tx, [dual(]*Smem[)], ACx (opcode 0x8f, byte3[7:6]==11, 4 bytes): the +// dual-access subtract-add. Tx is byte3[1:0]; the data-memory operand uses the +// shared register-modify / *sp(#k) / @#k addressing (byte1:byte2) and is wrapped +// in dual(...) when byte3[5] is set; ACx is byte2[4:0]. Left unlifted, as in the +// legacy decoder. +// btstclr / btstset / btst / btstnot #k5, [dbl(]*Smem[)], TCx (opcode 0x91, +// byte2[4:2] selecting op+access, 4 bytes): the extended memory bit-test family. +// Per the C55x+ encoding the addressing is byte1:byte2[7:6], TCx is byte2[5], the +// operation+access selector is byte2[4:2] (byte2[4:3]: 00 btstclr / 01 btstset / +// 10 btst / 11 btstnot; byte2[2]: 0 word / 1 dbl), and the 5-bit bit number is +// byte3[4:0]. The pre-existing word btstset (byte3[7:5]==000) keeps its own row; +// these extended rows reject byte3[7:5]==000 so that form still falls through to +// it. Left unlifted, as in the legacy decoder. +static void c55plus_x_btx_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + if ((((bits >> 5) & 0x7)) == 0) { + // byte3[7:5]==000 is the word btstset form: abandon so it falls through. + out->kind = C55_OP_INVALID; + return; + } + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 (smem_amode reads only [7:6]) + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if (((bits >> 10) & 1) != 0) { + out->dbl = true; // byte2[2]: dbl() 32-bit access + } +} +static void c55plus_x_btx_bit(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0x1f; // byte3[4:0] + out->width = 16; +} +static void c55plus_x_btx_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(4 + ((bits >> 13) & 1)); // byte2[5]: tc1 / tc2 +} + +// btst *Smem, reg, TCx (opcode 0x89, byte3[7:5]==101, 4 bytes): test a memory bit +// against a register-selected bit number. The Smem is byte1:byte2 (the shared +// register-modify / short / @#k decode using byte2[7:6]); the bit-number register +// is gr1((byte3[1:0] << 5) | byte2[4:0]) -- byte3[1:0] selects the register bank +// (00 ac, 01 ar/t, 10 ac.h, 11 ac.l) and byte2[4:0] the index; TCx is byte2[5]. +// (The other 0x89 bit ops -- bclr / bnot / btstp, the swapped "reg, *Smem" orders +// -- stay on the legacy decoder.) Left unlifted. +static void c55plus_x_btm_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_btm_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + ut8 idx = (ut8)((((bits >> 0) & 0x3) << 5) | ((bits >> 8) & 0x1f)); // byte3[1:0]:byte2[4:0] + c55plus_gr1(idx, &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_btm_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(4 + ((bits >> 13) & 1)); // byte2[5]: tc1 / tc2 +} + +static void c55plus_x_subadd_tx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(48 + (bits & 0x3)), &out->reg); // byte3[1:0] -> t0-3 + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_subadd_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->dual_wrap = ((bits >> 5) & 1) != 0; // byte3[5] +} +static void c55plus_x_subadd_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// bfxtr ACc., ACb., *Smem, ACa. (opcode 0xbc, byte3[6]==0 && +// byte4[7:6]==00, 5 bytes): the memory-operand bit-field extract. The three +// accumulator operands carry an explicit half-register selector (bit 5 of their +// field: 1 -> .l, 0 -> .h). Display order is ACc (byte3[4:0]), ACb (byte4[4:0]), +// *Smem (byte1:byte2, the compact register-modify matrix with short/@#k via +// byte2[7:6]) and ACa (byte2[4:0]). (The bfins / bfxtl / dbfxtr variants selected +// by byte3[6] / byte4[6] / byte4[7] stay on the legacy decoder.) Left unlifted. +static void c55plus_x_bfxtr_half(C55Operand *out, const C55ArchDesc *a, ut8 idx5, ut8 half) { + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(idx5 & 0x1f), &out->reg); + out->reg.sub = half ? C55_SUB_LO : C55_SUB_HI; // bit5: 1 -> .l, 0 -> .h + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_bcx_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_bfxtr_half(out, a, (ut8)((bits >> 8) & 0x1f), (ut8)((bits >> 13) & 1)); // byte3[4:0], byte3[5] +} +static void c55plus_x_bcx_acb(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + if (((bits >> 6) & 3) != 0) { + // byte4[7:6] != 00 selects the bfxtl / dbfxtr variants, which stay on the + // legacy decoder: abandon so the decode falls through. + out->kind = C55_OP_INVALID; + return; + } + c55plus_x_bfxtr_half(out, a, (ut8)(bits & 0x1f), (ut8)((bits >> 5) & 1)); // byte4[4:0], byte4[5] +} +static void c55plus_x_bcx_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_bcx_aca(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_bfxtr_half(out, a, (ut8)((bits >> 16) & 0x1f), (ut8)((bits >> 21) & 1)); // byte2[4:0], byte2[5] +} + +// mpym / macm / masm t3 = Smem, ACx, [ACy,] ACz (opcode 0xbb, 5 bytes): the +// single-data-memory multiply / multiply-accumulate with a parallel "t3 = Smem" +// side-load. The Smem is byte1:byte2 (the shared register-modify / short / @#k +// decode), ACz (the destination) is byte2[4:0], ACx is byte4 (a full gr1 source) +// and ACy is byte3[4:0] (macm / masm only). byte3[7:6] selects 00 mpym / 01 macm +// / 10 masm; byte3[5] is the whole-operation uns ('u' suffix); byte2[5] is round +// and byte4[7] fractional, via the shared .mods packing. Left unlifted (the side- +// load three-accumulator form is not modelled), as in the legacy decoder. +static void c55plus_x_bb_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_bb_acz(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_bb_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x7f), &out->reg); // byte4[6:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_bb_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// mpym / macm / masm [uns(]Xmem[)], [uns(]Ymem[)], ACy (opcode 0xe0, 6 bytes): +// the long-form dual-data-memory multiply / multiply-accumulate. Unlike the +// compact 0xc8 forms, the Xmem here uses the full register-modify / bit-reverse / +// short / @#k addressing (byte1:byte2[7:6]); the Ymem uses the compact register- +// modify matrix (byte5). ACy is byte2[4:0]; byte3[7:6] selects the operation (00 +// mpym, 01 macm, 10 masm); byte3[5] is the Xmem uns() and byte4[5] the Ymem uns(); +// byte2[5] is round (r) and byte5[7] fractional (f), via the shared .mods packing. +// These lift via the shared multiply-accumulate path (ops [0]=Xmem [1]=Ymem +// [2]=ACy). +static void c55plus_x_e0_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 32) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 24) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->uns = ((bits >> 21) & 1) != 0; // byte3[5] +} +static void c55plus_x_e0_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)(bits & 0x7f), ((bits >> 13) & 1) != 0, out); // byte5[6:0], byte4[5] uns +} +static void c55plus_x_e0_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// firsadd / firssub Xmem, Ymem, Cmem, ACx, ACy (opcode 0xeb, 6 bytes): the FIR +// symmetric / antisymmetric primitives. The three memory operands reuse the +// shared dual-mem register-modify matrix: Xmem is byte1, Ymem is byte3, Cmem is +// byte5; ACx is byte2[4:0] and ACy is byte4[4:0]. byte2[7]==1 selects the firs +// form (over the byte2[7]==0 "amar :: mpy" form), byte4[6] selects firssub over +// firsadd, and byte5[7] is the fractional (f) modifier. These lift via the shared +// C55_LOP_FIRSADD / FIRSSUB dual-operation path (ops [0]=Xmem [1]=Ymem [2]=Cmem +// [3]=ACx [4]=ACy). +static void c55plus_x_fir_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 32) & 0x7f), false, out); // byte1[6:0] +} +static void c55plus_x_fir_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 16) & 0x7f), false, out); // byte3[6:0] +} +static void c55plus_x_fir_cmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)(bits & 0x7f), false, out); // byte5[6:0] +} +static void c55plus_x_fir_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_fir_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte4[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_dst_xmem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 24) & 0x7f), false, out); // byte1[6:0] +} +static void c55plus_x_dst_ymem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 8) & 0x7f), false, out); // byte3[6:0] +} +static void c55plus_x_dst_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_dst_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte4[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_diff_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_diff_acd(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte4[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_diff_aca(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_diff_acb(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_diff_trn(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_TRN; + out->reg.num = (ut8)(((bits >> 5) & 7) ^ 6); // byte4[7:5] ^ 6 + out->reg.sub = C55_SUB_NONE; + out->width = 16; +} + +// sqrm / sqam / sqsm *Smem, [ACx,] ACy (opcode 0x92, 4 bytes): square a data- +// memory operand, optionally accumulating. The Smem is byte1:byte2 (the shared +// register-modify / @#k decode using byte2[7:6]); ACy is byte2[4:0]; byte2[5] is +// the round (r) modifier. byte3[7:5] selects the operation (000 sqrm, 010 sqam, +// 100 sqsm), byte3[5] is the fractional (f) modifier, and byte3[4:0] is the +// accumulate source ACx (sqam / sqsm only). These lift via the shared squaring +// multiply path. +static void c55plus_x_sq_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_sq_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_sq_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// mpymk / macmk Xmem, #k8, [ACx,] ACy (opcode 0xb8, 5 bytes): multiply (or +// multiply-accumulate) a data-memory operand by an 8-bit signed constant. The +// Xmem reuses the shared register-modify / scaled / bit-reverse / @#k decode +// (byte1:byte2); ACy is byte2[4:0]; #k8 is byte4; byte3[6] selects macmk (adding +// ACx = byte3[4:0]) over mpymk; byte3[5] is the fractional (f) modifier. Left +// unlifted, as the constant-multiply forms are in the legacy decoder. +static void c55plus_x_mpymk_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_mpymk_k8(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xff; // byte4 + out->width = 8; +} +static void c55plus_x_mpymk_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_mpymk_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_mant_aca(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_mant_acb(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_mant_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x7f), &out->reg); // byte1[6:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// subc *Smem, ACx, ACy (opcode 0xb3, byte3[7:5]==111, 5 bytes): conditional +// subtract. The Smem is byte1:byte2 (the shared register-modify / indexed / @#k +// decode using byte2[7:6]); ACx is byte3[4:0] and ACy is byte2[4:0]. The +// addsubcc / addsub2cc forms share the opcode via other byte3[7:5] codes and +// carry extra Tx / TCx operands; those stay on the legacy decoder. Left unlifted, +// as in the legacy decoder. +static void c55plus_x_b3_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } +} +static void c55plus_x_b3_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_b3_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +// TCx of the addsubcc 1-TC form: byte2[5] selects tc1 (0) or tc2 (1). +static void c55plus_x_b3_tcx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(((bits >> 21) & 1) ? 5 : 4); // byte2[5]: tc2 / tc1 +} +// The fixed tc1 / tc2 operands of the addsubcc 2-TC and addsub2cc forms. +static void c55plus_x_b3_tc1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = 4; // tc1 +} +static void c55plus_x_b3_tc2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = 5; // tc2 +} +// The ACz operand of addsub2cc: gr1(byte4[6:0]). +static void c55plus_x_b3_acz(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x7f), &out->reg); // byte4[6:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +static void c55plus_x_b4_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + ut8 b3 = (ut8)((bits >> 8) & 0xff); // byte3 + ut8 b4 = (ut8)(bits & 0xff); // byte4 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->uns = ((b3 >> 5) & 1) != 0; // byte3[5] + out->mem_round = ((b_mode >> 5) & 1) != 0; // byte2[5] + out->sh_mem_reg_set = true; + c55plus_gr1((ut8)(b4 & 0x7f), &out->sh_mem_reg); // byte4[6:0] +} +static void c55plus_x_b4_acdst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// Smem << Tx of the 0xb6 register-shifted add/sub (opcode 0xb6, 5 bytes): byte1 +// mode + ARn / byte2[7:6] offset-mode (via c55plus_smem_amode) with a register +// shift count in byte4[6:0] rendered as " << Tx". (Unlike the 0xb4 mov-shift +// form, byte2[5]/byte3[5] are not round/uns selectors here.) +static void c55plus_x_b6_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + ut8 b4 = (ut8)(bits & 0xff); // byte4 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->sh_mem_reg_set = true; + c55plus_gr1((ut8)(b4 & 0x7f), &out->sh_mem_reg); // byte4[6:0] +} +// Source accumulator ACx of the 0xb6 form: byte3[4:0]. +static void c55plus_x_b6_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// Source accumulator of the 0xb4 store form (mov ACx << Tx, [dbl]Smem, byte3[6]==0): +// ACx is byte2[4:0], shifted by the byte4[6:0] register (rendered " << Tx"). The +// accumulator is wrapped hi()/lo() for the 16-bit-half stores (byte3[1:0]: 00 -> +// hi, 10 -> lo; byte3[0]==1 selects the dbl 32-bit store with no half wrapper), +// and optionally rnd() (byte2[5]) / uns() (byte3[5]). +static void c55plus_x_b4st_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b2 = (ut8)((bits >> 16) & 0xff); // byte2 + ut8 b3 = (ut8)((bits >> 8) & 0xff); // byte3 + ut8 b4 = (ut8)(bits & 0xff); // byte4 + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(b2 & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); + out->sh_by_reg = true; + c55plus_gr1((ut8)(b4 & 0x7f), &out->index); // byte4[6:0] + out->wrap_round = ((b2 >> 5) & 1) != 0; // byte2[5] + out->wrap_uns = ((b3 >> 5) & 1) != 0; // byte3[5] + if (!(b3 & 1)) { // byte3[0]==0 -> 16-bit-half store + out->wrap_half = (b3 & 2) ? 2 : 1; // byte3[1]: 0 -> hi, 1 -> lo + } +} +// Destination memory of the 0xb4 store form: Smem (byte1 mode+ARn / byte2[7:6] +// offset-mode); byte3[0] selects the 32-bit dbl() store. +static void c55plus_x_b4st_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + ut8 b3 = (ut8)((bits >> 8) & 0xff); // byte3 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if (b3 & 1) { // byte3[0] -> dbl 32-bit store + out->access = 32; + out->dbl = true; + } +} + +// bfxtr #k16, ACsrc, ACdst (opcode 0xc6, 5 bytes): bit-field extract. The 16-bit +// mask is byte3:byte4; the source accumulator is byte2[4:0] (byte2[6:5]==11 is +// fixed, checked by the row mask); the destination is byte1, a 7-bit general +// register selector (byte1[6:5] the sub-register type, byte1[4:0] the number). +static void c55plus_x_bfxtr_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)((bits >> 24) & 0x1f); // byte1[4:0] + ut8 typ = (ut8)((bits >> 29) & 3); // byte1[6:5] + out->kind = C55_OP_REG; + switch (typ) { + case 0: c55plus_gr1(num, &out->reg); break; // ACx + case 1: c55plus_gr1((ut8)(32 + num), &out->reg); break; // ARx + case 2: c55plus_gr1((ut8)(64 + num), &out->reg); break; // ACx.h + default: c55plus_gr1((ut8)(96 + num), &out->reg); break; // ACx.l + } + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_bfxtr_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] -> ACx + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_bfxtr_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; +} + +// Bit-test family register-target forms (opcode 0x89, 4 bytes): the "@#k bit +// number, register" variants of btst / bclr / bset / bnot / btstp. byte1[6:0] is +// the bit number; byte2[4:0] is the target register number with byte2[7:5] +// selecting the variant (110 = bclr / btst-tc1, 111 = bset / btst-tc2); byte3[1:0] +// the sub-register (ac / ar / ac.h / ac.l) and byte3[7:5] the operation. Left +// unlifted, as in the legacy decoder. +static void c55plus_x_bit_num(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = (bits >> 16) & 0x7f; // byte1[6:0] + out->width = 16; + out->is_bit = true; +} +static void c55plus_x_bit_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)((bits >> 8) & 0x1f); // byte2[4:0] + ut8 sub = (ut8)(bits & 3); // byte3[1:0] + out->kind = C55_OP_REG; + switch (sub) { + case 0: c55plus_gr1(num, &out->reg); break; // ACx + case 1: c55plus_gr1((ut8)(32 + num), &out->reg); break; // ARx + case 2: c55plus_gr1((ut8)(64 + num), &out->reg); break; // ACx.h + default: c55plus_gr1((ut8)(96 + num), &out->reg); break; // ACx.l + } + out->width = c55plus_reg_width(a, &out->reg); +} +// The TC flag of btst is byte2[5] (0 -> TC1, 1 -> TC2), rendered/lifted via the +// shared cond-flag ids (tc1 = 4, tc2 = 5). +static void c55plus_x_bit_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(4 + ((bits >> 13) & 1)); +} + +// add/mov #k16, [dbl(]*Smem[)] (opcode 0xb1, base 4 bytes): a 16-bit immediate +// applied to a memory operand. byte2[7:6] selects the addressing group and +// byte1[7:4] supplies the sub-mode (the shared Smem decode reads only those, so +// the operation/dbl bits in byte2 do not disturb it); byte2[4:3] selects the +// operation (00 = add, 11 = mov); byte2[5] is the dbl() flag (32-bit access); +// the 16-bit immediate is byte3:byte4. Only mov lifts (a store), as in legacy. +static void c55plus_x_b1_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if ((b_mode >> 5) & 1) { + out->access = 32; // dbl() + } +} +static void c55plus_x_b1_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; +} + +// btstset #k, *Smem, TCx (opcode 0x91, base 4 bytes): test-and-set a memory bit. +// The bit number is byte3[4:0]; the Smem operand is byte1:byte2 but byte2[5] +// carries the TC selector (TC1/TC2) rather than addressing, so it is masked out +// before the shared Smem decode. Left unlifted, as in the legacy decoder. +static void c55plus_x_btstset_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)(((bits >> 8) & 0xff) & ~0x20); // byte2 with the TC bit cleared + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} +static void c55plus_x_btstset_bit(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xf; // byte3[3:0] + out->width = 16; +} +static void c55plus_x_btstset_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(4 + ((bits >> 13) & 1)); // byte2[5] +} + +// Whole-accumulator destination ACx in byte2[4:0] (0x5c dbl load). +static void c55plus_x_acc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // ac0-31 + out->width = c55plus_reg_width(a, &out->reg); +} + +// Low-half accumulator destination ACx.l in byte2[4:0] (0x5b word load). +static void c55plus_x_acc_lo(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(96 + (bits & 0x1f)), &out->reg); // ac0-31 .l + out->width = c55plus_reg_width(a, &out->reg); +} + +// High-half accumulator destination ACx.h in byte2[4:0] (0x5a word load). +static void c55plus_x_acc_hi(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(64 + (bits & 0x1f)), &out->reg); // ac0-31 .h + out->width = c55plus_reg_width(a, &out->reg); +} + +// Word (16-bit) memory source with the byte2[5] uns() qualifier, used by the +// "mov *Smem, ACx.h" load (opcode 0x5a). +static void c55plus_x_word_mem_u(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // buf[1] + ut8 b_mode = (ut8)(bits & 0xff); // buf[2] + if (!c55plus_mem_addr(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 16; + if ((b_mode >> 5) & 1) { + out->uns = true; + } +} + +// Accumulator-half source ACx.h/.l of "mov ACx.h/.l, *Smem" (opcode 0x51 word +// store): byte2[5] selects the half (0 -> high, 1 -> low) and byte2[4:0] the +// accumulator. +static void c55plus_x_acc_half(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)(bits & 0x1f); + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(((bits >> 5) & 1 ? 96 : 64) + num), &out->reg); // .l : .h + out->width = c55plus_reg_width(a, &out->reg); +} + +// Whole-accumulator source ACx in byte2[4:0] (0x50 dbl store). +static void c55plus_x_acc_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // ac0-31 + out->width = c55plus_reg_width(a, &out->reg); +} + +// xar source XARx in byte2[3:0] (0x52 dbl store). +static void c55plus_x_xar_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + (bits & 0x0f)), &out->reg); // xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + + +static void c55plus_x_gr6(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_mode = (ut8)(bits & 0xff); + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(b_mode & 0x3f), &out->reg); + if (out->reg.cls == C55_RC_NONE || out->reg.cls == C55_RC_AC) { + out->kind = C55_OP_INVALID; + return; + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// Double-word memory source of "copy dbl(*Smem), xar" (opcode 0x56, 3 bytes). +// The addressing differs from the standard Smem byte layout: byte1[3:0] is the +// base ARn and byte1[7:4] is a short index k4 (0 -> plain indirect, non-zero -> +// short(#k4)). byte2[7:6] selects the addressing group (10 = the indirect / +// indexed dbl form decoded here); byte2[5] = 0 marks the double-word (dbl) +// access and byte2[3:0] is the xar destination. The register-modify (00), +// direct (11), and byte/half (byte2[5]=1) forms fall back to the legacy decoder. +static void c55plus_x_copy_dbl_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b1 = (ut8)((bits >> 8) & 0xff); + ut8 b2 = (ut8)(bits & 0xff); + ut8 grp = (ut8)((b2 >> 6) & 3); + if ((b2 >> 5) & 1) { + out->kind = C55_OP_INVALID; // byte/half access, not the dbl form + return; + } + if (grp == 2) { + // indirect / indexed-short: byte1 holds the base ARn (low nibble) and a + // short index k4 (high nibble). + out->kind = C55_OP_MEM; + out->access = 32; + out->dbl = true; + c55plus_gr1((ut8)(32 + (b1 & 0x0f)), &out->reg); // base ARn (low half of XARn) + ut8 k4 = (ut8)((b1 >> 4) & 0x0f); + if (k4) { + out->amode = C55_AM_INDEXED; + out->disp = k4; + } else { + out->amode = C55_AM_INDIRECT; + } + return; + } + if (grp == 3 && ((b1 >> 7) & 1)) { + // SP-relative *sp(#k): the data address is (sp + k) << 1 (a byte + // address), 7-bit offset k = byte1[6:0]. + out->kind = C55_OP_MEM; + out->access = 32; + out->dbl = true; + out->amode = C55_AM_INDEXED; + c55plus_gr1(53, &out->reg); // sp + out->disp = (ut8)(b1 & 0x7f); + return; + } + out->kind = C55_OP_INVALID; // register-modify / DP-direct -> legacy +} + +// xar destination of "copy dbl(*Smem), xar" (opcode 0x56): byte2[3:0]. +static void c55plus_x_copy_xar(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + (bits & 0x0f)), &out->reg); // xar0-15 (gr1 slots 128-143) + out->width = c55plus_reg_width(a, &out->reg); +} + +// Absolute double-word memory source of "copy dbl(*(#addr)), xar" (opcode 0xd1, +// 5 bytes): byte1[7:6] selects the destination/access form (10 = the dbl xar +// load decoded here), and the 24-bit byte address is in bytes 2:4. The +// accumulator (00/01/11) and byte/half forms fall back to the legacy decoder. +static void c55plus_x_copy_abs_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b1 = (ut8)((bits >> 24) & 0xff); + if (((b1 >> 6) & 3) != 2 || ((b1 >> 4) & 3)) { + out->kind = C55_OP_INVALID; // not the dbl xar form + return; + } + out->kind = C55_OP_MEM; + out->access = 32; + out->dbl = true; + out->amode = C55_AM_ABSOLUTE; + out->abs_addr = (ut32)(bits & 0xffffff); +} + +// xar destination of the 0xd1 absolute copy: byte1[3:0]. +static void c55plus_x_copy_abs_xar(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + ((bits >> 24) & 0x0f)), &out->reg); // xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// amar *Smem (opcode 0x62, 3 bytes): modify-auxiliary-register -- apply the +// addressing mode's pointer side effect (e.g. *arN+ increments xarN) without a +// memory access. The Smem operand is byte1:byte2 (the shared Smem decode plus +// the *sp(#k) / @#k group). The shared LEA lift emits the post-modify effect for +// the register-modify modes and a nop / no IL otherwise, matching the legacy +// decoder. +static void c55plus_x_amar_smem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 8) & 0xff); // byte1 + ut8 b_mode = (ut8)(bits & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} + + +// forms are decoded: the SP-relative *sp(#k) (3 bytes, byte2[7:6]==11 with +// byte1[7]==1, offset k = byte1[6:0]) and the long const-index *arN(#K16) (5 +// bytes, byte2[7:6]==01 with byte1[7:4]==8, base ARn = byte1[3:0], the unsigned +// 16-bit constant supplied as a 2-byte extension via the decoder). byte2[3:0] +// is the xar destination in both. The DP-direct, register-modify, indexed-short +// and byte-access forms fall back to the legacy decoder. The address is a word +// address (no byte scaling); the LEA lifter writes base+offset into the dest. +static void c55plus_x_amar_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b1 = (ut8)((bits >> 8) & 0xff); + ut8 b2 = (ut8)(bits & 0xff); + ut8 grp = (ut8)((b2 >> 6) & 3); + if (grp == 3 && ((b1 >> 7) & 1)) { + // *sp(#k): SP-relative, 7-bit offset + out->kind = C55_OP_MEM; + out->amode = C55_AM_INDEXED; + c55plus_gr1(53, &out->reg); // sp + out->disp = (ut8)(b1 & 0x7f); + return; + } + if (grp == 1 && ((b1 >> 4) & 0x0f) == 8 && !((b2 >> 4) & 3)) { + // *arN(#K16): long const-index; the K16 extension is filled by the + // decoder (which also sets the size and clears the parallel flag). + out->kind = C55_OP_MEM; + out->amode = C55_AM_CONST_IDX; + c55plus_gr1((ut8)(32 + (b1 & 0x0f)), &out->reg); // base ARn + return; + } + out->kind = C55_OP_INVALID; +} + +// xar destination of "amar Smem, xar" (opcode 0x63): byte2[3:0]. +static void c55plus_x_amar_xar(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(128 + (bits & 0x0f)), &out->reg); // xar0-15 + out->width = c55plus_reg_width(a, &out->reg); +} + +// sftl/sfts/add/sub SRC, SHIFT, DST (opcode 0xa6, 4 bytes): a register-to- +// register variable shift. byte1[7] selects the operation class (0 = add/sub +// shift-and-accumulate, 1 = sftl/sfts), byte2[7] the shift kind (0 = sftl/add, +// 1 = sfts/sub). The three register operands are 7-bit general-register +// selectors (bits [6:5] the sub-register type ac / ar / ac.h / ac.l, bits [4:0] +// the number): DST is byte1[6:0], SRC byte2[6:0], SHIFT byte3[6:0]. Left +// unlifted, as in the legacy decoder. +static void c55plus_gr1_sub7(const C55ArchDesc *a, ut8 v, C55Operand *out) { + ut8 num = (ut8)(v & 0x1f); + switch ((v >> 5) & 3) { + case 0: c55plus_gr1(num, &out->reg); break; // ACx + case 1: c55plus_gr1((ut8)(32 + num), &out->reg); break; // ARx + case 2: c55plus_gr1((ut8)(64 + num), &out->reg); break; // ACx.h + default: c55plus_gr1((ut8)(96 + num), &out->reg); break; // ACx.l + } + out->kind = C55_OP_REG; + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_sft_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)((bits >> 16) & 0x7f), out); // byte1[6:0] +} +static void c55plus_x_sft_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)((bits >> 8) & 0x7f), out); // byte2[6:0] +} +static void c55plus_x_sft_shift(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)(bits & 0x7f), out); // byte3[6:0] +} + +// sftl/sfts REG, #1 / #-1 (opcode 0x7b, 3 bytes): a register shift by a fixed +// literal one. byte1[7] selects sftl (1) vs sfts (0) and byte1[6:0] is the +// 7-bit register selector; byte2[6:5]==01 marks the shift form and byte2[7] +// selects the sign (0 -> #1, 1 -> #-1). The shift literal is rendered as a +// signed decimal "#N". Left unlifted, as in the legacy decoder. +static void c55plus_x_sft7b_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)((bits >> 8) & 0x7f), out); // byte1[6:0] +} +static void c55plus_x_sft7b_one(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = ((bits >> 7) & 1) ? (ut64)(-1) : 1; // byte2[7]: #-1 / #1 + out->width = 16; + out->hash_dec = true; +} + + +// add uns(*Smem), ACx, ACy (opcode 0x8c, 4 bytes): a memory-source add into an +// accumulator. The Smem operand is byte1:byte2 (the shared Smem decode reads +// byte2[7:6] + byte1[7:4] only); byte2[5]==1 is the always-set uns marker and +// byte2[4:0] is ACy; byte3[4:0] is ACx (byte3[7:5]==000). Left unlifted, as in +// the legacy decoder. +static void c55plus_x_8c_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->uns = true; +} +static void c55plus_x_8c_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_8c_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// The fixed "carry" status-bit operand of the add-with-carry forms. Rendered +// through the shared condition-flag formatter (flag id 6 == carry). +static void c55plus_x_carry(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = 6; // carry +} + +// Smem of the 0x8c add-with-carry form: same byte1 (mode + ARn) / byte2[7:6] +// (offset-mode) decode as c55plus_x_8c_mem, but the uns() qualifier is taken +// from byte2[5] (it is not implicit here, unlike the plain uns(*Smem) form). +static void c55plus_x_8c_carry_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->uns = (b_mode >> 5) & 1; // byte2[5] +} + +// mov ACx., mmap(@reg) (opcode 0x24, byte1==0x51, 4 bytes): store an +// accumulator half into a memory-mapped register. byte2 is the memory-mapped +// register (a general-register selector, decoded by c55plus_gr1, covering the +// ac/ar/t/sp and the csr/rptc/brc0/brc1 special registers); byte3 is the source +// accumulator -- byte3[4:0] the number and byte3[5] the half (1 -> .l, 0 -> .h), +// byte3[6] must be 0. Left unlifted, as in the legacy decoder. (The opcode hosts +// many other memory-mapped-register forms, all left to the legacy decoder.) +static void c55plus_x_24_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)(bits & 0x1f); // byte3[4:0] + out->kind = C55_OP_REG; + if ((bits >> 5) & 1) { // byte3[5]: .l / .h + c55plus_gr1((ut8)(96 + num), &out->reg); + } else { + c55plus_gr1((ut8)(64 + num), &out->reg); + } + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_24_mmr(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_MEM; + out->amode = C55_AM_MMR; + out->access = 16; + c55plus_gr1((ut8)((bits >> 8) & 0x7f), &out->reg); // byte2: the mmap register +} + + +// sftcc ACx, TCx (opcode 0xa9, byte1[7]==1 && byte2[7]==1, 4 bytes): conditional +// shift -- shift ACx and write the result-condition to TCx. ACx is ac(byte1[5:0]); +// TCx is tc1/tc2 from byte2[5]. Left unlifted, as in the legacy decoder. +static void c55plus_x_a9_sftcc_ac(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x3f), &out->reg); // ac(byte1[5:0]) + out->width = c55plus_reg_width(a, &out->reg); + if (out->reg.cls != C55_RC_AC) { + out->kind = C55_OP_INVALID; + } +} +static void c55plus_x_a9_sftcc_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = ((bits >> 13) & 1) ? 5 : 4; // byte2[5]: 1 -> tc2, 0 -> tc1 +} + + +// is a signed 16-bit pc-relative displacement (bytes 1-2), taken when the +// condition byte (byte3) holds. The operand is rendered as the raw 16-bit field +// (#0x00NNNN) but the branch target is pc + size + sign_extend(field); the +// condition is decoded by c55plus_x_cond. No IL. +static void c55plus_x_callcc_target(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = (bits >> 8) & 0xffff; // bytes 1-2 + out->width = 16; + out->addr = true; + out->reltarget = true; +} + +// bcnt ACa, ACb, TCx, ACdst (opcode 0xa9, byte1[7]==1, 4 bytes): count the bits +// of ACa selected by ACb, writing the test flag TCx and the count to ACdst. ACa +// is ac(byte2[4:0]); ACb is ac(byte3[4:0]); TCx is tc1/tc2 from byte2[6]; ACdst +// is gr1(byte1[6:0]). Left unlifted, as in the legacy decoder. +static void c55plus_x_a9_bcnt_a(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // ac(byte2[4:0]) + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_a9_bcnt_b(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // ac(byte3[4:0]) + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_a9_bcnt_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = ((bits >> 14) & 1) ? 5 : 4; // byte2[6]: 1 -> tc2, 0 -> tc1 +} +static void c55plus_x_a9_bcnt_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1((ut8)((bits >> 16) & 0x7f), &out->reg); // gr1(byte1[6:0]) + out->kind = C55_OP_REG; + out->width = c55plus_reg_width(a, &out->reg); +} + + +// the exponent (leading-bit count) of ACsrc into the destination. SRC is +// ac(byte3[4:0]); DST is gr1_sub7(byte1) (a whole accumulator or its .h/.l half). +// Left unlifted, as in the legacy decoder. (The 0xa9 opcode also hosts bcnt -- +// byte1[7]==1 -- and mant::nexp -- byte2[7]==1 -- which stay on the legacy +// decoder for now.) +static void c55plus_x_a9_exp_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // ac(byte3[4:0]) + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_a9_exp_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)((bits >> 16) & 0x7f), out); // byte1[6:0] +} + + +// the single-repeat counter from csr, optionally adjusted by a 4-bit immediate +// or a register. byte1[7:6] selects: 00 rptsub #k, 01 rptadd #k, 10 rptadd reg, +// 11 rpt. The first operand is always csr; rpt has no second operand. All lift +// to a nop (a repeat op type), matching the legacy decoder. +static void c55plus_x_01_csr(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = 56; // csr + out->reg.sub = C55_SUB_NONE; + out->width = 16; +} +static void c55plus_x_01_k4(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xf; // byte1[3:0] + out->width = 4; +} +static void c55plus_x_01_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1((ut8)(32 + (bits & 0x1f)), &out->reg); // byte1[4:0] -> ar0-15/t0-3/special + out->kind = C55_OP_REG; + out->width = c55plus_reg_width(a, &out->reg); +} + + +// status flag into ACy, lifted by the shared rol/ror path. byte3[7] selects rol +// (0) / ror (1); the carry-in / carry-out flags are COND operands (carry / tc2) +// selected by byte3 bits via c55plus_x_a8_flag (the .lo field picks the bit). +// SRC = ac(byte2[4:0]), DST = ac(byte1[4:0]); only the whole-accumulator forms +// are decoded here (the half / extended forms are declined so they fall back). +static void c55plus_x_a8_flag(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + ut8 b = (ut8)((bits >> d->lo) & 0x1); + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = b ? 5 : 6; // 0 -> carry, 1 -> tc2 +} +static void c55plus_x_a8_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + if (((bits >> 13) & 0x3) != 0) { // byte2[6:5] != 0: a half-register source, leave to legacy + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // ac(byte2[4:0]) + out->width = c55plus_reg_width(a, &out->reg); + if (out->reg.cls != C55_RC_AC) { + out->kind = C55_OP_INVALID; + } +} +static void c55plus_x_a8_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + if (((bits >> 21) & 0x3) != 0) { // byte1[6:5] != 0: a half-register dest, leave to legacy + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // ac(byte1[4:0]) + out->width = c55plus_reg_width(a, &out->reg); + if (out->reg.cls != C55_RC_AC) { + out->kind = C55_OP_INVALID; + } +} + + +// in the st0_55 status register. byte1[5] selects bset (1) vs bclr (0); byte1[4:0] +// is the bit position, which also selects the bit name (st0_dp07..dp15, +// st0_acov0/1/2/3, st0_carry, st0_tc1/tc2). The bit operand is rendered as the +// named special register and carries the position in its register index. +static void c55plus_x_0a_bit(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = (ut8)(192 + (bits & 0x1f)); // 192 + byte1[4:0] + out->reg.sub = C55_SUB_NONE; + out->width = 16; +} +static void c55plus_x_0a_st0(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)bits; + (void)d; + out->kind = C55_OP_REG; + out->reg.cls = C55_RC_SPECIAL; + out->reg.num = 228; // st0_55 + out->reg.sub = C55_SUB_NONE; + out->width = 16; +} + + +// memory-mapped register. byte2 is the mmap register (a gr1 selector); byte3[3] +// selects pop (1) vs psh (0). Lifted by the shared stack path (the MMR operand +// moves through the stack as a plain register). Only the simple register form is +// decoded here (byte3[7]==0, byte3[6:4]==0); the @#k / dbl / reserved variants +// stay on the legacy decoder. +static void c55plus_x_24_mmr_stack(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + if ((bits & 0xf7) != 0) { // byte3 must be 0 apart from the pop/psh bit (bit 3) + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_MEM; + out->amode = C55_AM_MMR; + out->access = 16; + c55plus_gr1((ut8)((bits >> 8) & 0xff), &out->reg); // byte2: the mmap register +} + + +// rX is gr1(byte1) and rY gr1(byte2), each a full 8-bit gr1 selector (so the +// ac.h / ac.l half-register encodings are covered). The shared stack lift emits +// the per-register load/store and SP adjustment in operand order. +static void c55plus_x_pair_reg1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0xff), &out->reg); // byte1 + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_pair_reg2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0xff), &out->reg); // byte2 + out->width = c55plus_reg_width(a, &out->reg); +} + + +// pshboth / popboth (opcode 0x0d, 2 bytes): push / pop a register pair. The +// register class is byte1[5] (0 ac / 1 xar) and the index is byte1[4:0]; byte1[7] +// selects pshboth (0) over popboth (1). The bare no-operand form (an out-of-range +// xar index) stays on the legacy decoder. The "both" flag marks the pair +// semantics; left unlifted, as the legacy decoder does. +// amar *ptr1, *ptr2, *ptr3 (opcode 0xea, byte1[7]==1, 6 bytes): the triple +// address-register modify. The three pointer operands each use the compact +// register-modify matrix (ptr1 = byte1[6:0], ptr2 = byte3[6:0], ptr3 = byte5[6:0]; +// bytes 2 and 4 are unused). Left unlifted, as in the legacy decoder. +static void c55plus_x_amar3_p1(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 32) & 0x7f), false, out); // byte1[6:0] +} +static void c55plus_x_amar3_p2(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)((bits >> 16) & 0x7f), false, out); // byte3[6:0] +} +static void c55plus_x_amar3_p3(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_x_mac_mem(a, (ut8)(bits & 0x7f), false, out); // byte5[6:0] +} + +static void c55plus_x_pshpopboth_reg(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b1 = (ut8)(bits & 0xff); // byte1 (2-byte instruction) + ut8 idx = b1 & 0x1f; + out->kind = C55_OP_REG; + if ((b1 >> 5) & 1) { + if (idx > 15) { + // out-of-range xar index: the bare no-operand form, left to legacy. + out->kind = C55_OP_INVALID; + return; + } + c55plus_gr1((ut8)(128 + idx), &out->reg); // xar0-15 + } else { + c55plus_gr1(idx, &out->reg); // ac0-31 + } + out->width = c55plus_reg_width(a, &out->reg); +} + +// pair selected by byte1[4:0]. Only the single-register-pair encodings are +// decoded here (they lift via the shared XOR-swap idiom); the pair()/block() +// aggregate forms and the bare no-operand swap stay on the legacy decoder. The +// pair table below maps byte1[4:0] to the two gr1 register indices. +static bool c55plus_swap_pair(ut8 sel, ut8 *rx, ut8 *ry) { + switch (sel) { + case 0x01: *rx = 0; *ry = 2; return true; // ac0, ac2 + case 0x02: *rx = 1; *ry = 3; return true; // ac1, ac3 + case 0x04: *rx = 32; *ry = 33; return true; // ar0, ar1 + case 0x05: *rx = 32; *ry = 34; return true; // ar0, ar2 + case 0x06: *rx = 33; *ry = 35; return true; // ar1, ar3 + case 0x09: *rx = 48; *ry = 50; return true; // t0, t2 + case 0x0a: *rx = 49; *ry = 51; return true; // t1, t3 + case 0x15: *rx = 36; *ry = 48; return true; // ar4, t0 + case 0x16: *rx = 37; *ry = 49; return true; // ar5, t1 + case 0x19: *rx = 38; *ry = 50; return true; // ar6, t2 + case 0x1a: *rx = 39; *ry = 51; return true; // ar7, t3 + default: return false; + } +} +static void c55plus_x_swap_x(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 rx, ry; + if (!c55plus_swap_pair((ut8)(bits & 0x1f), &rx, &ry)) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + c55plus_gr1(rx, &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_swap_y(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 rx, ry; + if (!c55plus_swap_pair((ut8)(bits & 0x1f), &rx, &ry)) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_REG; + c55plus_gr1(ry, &out->reg); + out->width = c55plus_reg_width(a, &out->reg); +} + + +// 5-bit vector number in byte1[4:0]. byte1[7:6] selects intr (00) / trap (01) / +// sim_trig (11). Left unlifted, as in the legacy decoder. +static void c55plus_x_03_k5(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0x1f; // byte1[4:0] + out->width = 5; +} + + +// (rounding) saturate. byte1[7]==0 selects round; byte1[7]==1 selects sat, with +// byte1[5] choosing the rounding variant (satr). SRC is the accumulator +// ac(byte2[4:0]) and DST is ac(byte1[4:0]). Left unlifted, as in the legacy +// decoder. +static void c55plus_x_79_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)(bits & 0x1f), &out->reg); // ac(byte2[4:0]) + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_79_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // ac(byte1[4:0]) + out->width = c55plus_reg_width(a, &out->reg); +} + + +// value / min / max. byte1[7] and byte2[7] together select the operation +// (00 abs, 01 neg, 10 max, 11 min); SRC is byte2[6:0] and DST byte1[6:0], both +// 7-bit register selectors. ops = [SRC, DST], lifted by the shared register-op +// path (whole-register forms; the half-register forms fall back to the legacy +// lifter, as before). +static void c55plus_x_76_src(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)(bits & 0x7f), out); // byte2[6:0] +} +static void c55plus_x_76_dst(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)((bits >> 8) & 0x7f), out); // byte1[6:0] +} + + +// add/sub #k16 << #sh, ACx, ACy (opcode 0xc2, base 5 bytes): an immediate +// shifted by a variable amount combined with an accumulator. byte1[7]==0 selects +// the add/sub group and byte2[7] the operation (0 add, 1 sub); the 4-bit shift +// count is byte1[6:5]*4 + byte2[6:5]; ACx is byte2[4:0], ACy byte1[4:0]; the +// 16-bit immediate is byte3:byte4. ops = [#k16, ACx, ACy], lifted by the shared +// immediate-shift ALU path (C55_LOP_ADDSHL / SUBSHL). +static void c55plus_x_c2_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b1 = (ut8)((bits >> 24) & 0xff); + ut8 b2 = (ut8)((bits >> 16) & 0xff); + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; + out->sh_left = true; + out->shamt = (int8_t)((((b1 >> 5) & 3) << 2) | ((b2 >> 5) & 3)); // byte1[6:5]*4 + byte2[6:5] + out->shamt_hex = true; +} +static void c55plus_x_c2_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_c2_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +// add/sub #k16 << #16, ACx, ACy (opcode 0xc0, base 5 bytes): an immediate +// shifted up 16 bits combined with an accumulator. byte2[7] selects the +// operation (1 sub, 0 add); ACy is byte1[4:0] and ACx byte2[4:0]; the 16-bit +// immediate is byte3:byte4 and the shift is the fixed #16. ops = [#k16, ACx, +// ACy], lifted by the shared immediate-shift ALU path (C55_LOP_ADDSHL / +// SUBSHL). +static void c55plus_x_c0_imm(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; + out->sh_left = true; + out->shamt = 16; // << #16 +} +static void c55plus_x_c0_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_c0_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 24) & 0x1f), &out->reg); // byte1[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + + +// operand against a 16-bit immediate, result into TC1/TC2. The Smem operand is +// byte1:byte2 (the shared Smem decode reads byte2[7:6] + byte1[7:4]); byte2[1:0] +// is the relop (00 ==, 01 !=, 10 <, 11 >=), byte2[2] the dbl() flag, byte2[5] +// the TC selector (0 TC1, 1 TC2); the 16-bit immediate is byte3:byte4. (byte2[3] +// selects the band variant, left to the legacy decoder.) Left unlifted. +static void c55plus_x_b2_cmp(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->kind = C55_OP_COND; + out->cmp_mem = true; + out->cmp_imm = true; + if ((b_mode >> 2) & 1) { + out->access = 32; // dbl() + out->dbl = true; + } + switch (b_mode & 3) { // relop + case 0: out->relop = C55_REL_EQ; break; + case 1: out->relop = C55_REL_NE; break; + case 2: out->relop = C55_REL_LT; break; + default: out->relop = C55_REL_GE; break; + } + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; +} +static void c55plus_x_b2_tc(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_COND; + out->cond_is_flag = true; + out->cond_flag = (ut8)(((bits >> 21) & 1) ? 5 : 4); // byte2[5]: TC2 / TC1 +} + +// band *Smem, #k16, TCx (opcode 0xb2, byte2[3]==1, 5 bytes): test whether +// (Smem & #k16) is zero, writing the result to TCx. It shares the 0xb2 opcode +// with the cmp Smem #k16 compare (byte2[3]==0); here the Smem and the +// 16-bit mask are separate operands (not a relational compare). The TC operand +// reuses c55plus_x_b2_tc. +static void c55plus_x_b2_band_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if ((b_mode >> 2) & 1) { + out->access = 32; // dbl() + out->dbl = true; + } +} +static void c55plus_x_b2_band_k16(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)a; + (void)d; + out->kind = C55_OP_IMM; + out->imm = bits & 0xffff; // byte3:byte4 + out->width = 16; +} + + +// accumulator (the signed counterpart of the 0x8c uns form). The Smem operand +// is byte1:byte2 (the shared Smem decode reads byte2[7:6] + byte1[7:4] only); +// ACx is byte3[4:0] and ACy byte2[4:0]. Left unlifted, as in the legacy decoder. +static void c55plus_x_80_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} + + +// memory move. Operand A is byte1 + byte2[7:6] decoded by the shared Smem +// helper; operand B is byte3, whose [6:4] field selects a register-modify mode +// (0 postdec, 1 postinc, 2 *arN(t0), 3 indirect, 4 *(arN-t0), 5 *(arN-t1), +// 6 *(arN+t0), 7 *(arN+t1)) and [3:0] the ARn. byte2[3] is the direction (1 -> +// A is the source, 0 -> B is the source) and byte2[2] the byte() flag; both are +// resolved by separate table rows / the operand helpers. Left unlifted, as in +// the legacy decoder. +static void c55plus_x_97_memA(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + if ((b_mode >> 2) & 1) { + out->byte_sel = 3; // byte() + } +} +static void c55plus_x_97_memB(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b3 = (ut8)(bits & 0xff); // byte3 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + out->kind = C55_OP_MEM; + out->access = 16; + c55plus_gr1((ut8)(32 + (b3 & 0x0f)), &out->reg); // ARn + switch ((b3 >> 4) & 7) { + case 0: out->amode = C55_AM_POSTDEC; break; + case 1: out->amode = C55_AM_POSTINC; break; + case 2: out->amode = C55_AM_IDXREG; c55plus_gr1(48, &out->index); break; // t0 + case 3: out->amode = C55_AM_INDIRECT; break; + case 4: out->amode = C55_AM_POSTSUB; c55plus_gr1(48, &out->index); break; // *(arN-t0) + case 5: out->amode = C55_AM_POSTSUB; c55plus_gr1(49, &out->index); break; // *(arN-t1) + case 6: out->amode = C55_AM_POSTADD; c55plus_gr1(48, &out->index); break; // *(arN+t0) + default: out->amode = C55_AM_POSTADD; c55plus_gr1(49, &out->index); break; // *(arN+t1) + } + if ((b_mode >> 2) & 1) { + out->byte_sel = 3; // byte() + } +} + + +// memory-source subtract. The Smem operand is byte1:byte2 (the shared Smem +// decode reads byte2[7:6] + byte1[7:4], plus the grp==3 *sp(#k) sub-mode driven +// by byte1); ACx is byte3[6:0] (a 7-bit register selector); ACy is byte2[4:0] +// with byte2[5] selecting its half (1 -> .l, 0 -> .h). Left unlifted, as in the +// legacy decoder. +static void c55plus_x_82_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} +static void c55plus_x_82_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)(bits & 0x7f), out); // byte3[6:0] +} +static void c55plus_x_82_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)((bits >> 8) & 0x1f); // byte2[4:0] + out->kind = C55_OP_REG; + if ((bits >> 13) & 1) { // byte2[5] -> .l + c55plus_gr1((ut8)(96 + num), &out->reg); + } else { + c55plus_gr1((ut8)(64 + num), &out->reg); + } + out->width = c55plus_reg_width(a, &out->reg); +} + + +// into an accumulator. The Smem operand is byte1:byte2 (the shared Smem decode +// reads byte2[7:6] + byte1[7:4] only); ACx is byte3[6:0] (a 7-bit register +// selector); ACy is byte2[4:0] with byte3[7] selecting its high half (.h). Left +// unlifted, as in the legacy decoder. +static void c55plus_x_85_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + } +} +static void c55plus_x_85_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + c55plus_gr1_sub7(a, (ut8)(bits & 0x7f), out); // byte3[6:0] +} +static void c55plus_x_85_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 num = (ut8)((bits >> 8) & 0x1f); // byte2[4:0] + out->kind = C55_OP_REG; + if ((bits >> 7) & 1) { // byte3[7] -> ACy.h + c55plus_gr1((ut8)(64 + num), &out->reg); + } else { + c55plus_gr1(num, &out->reg); + } + out->width = c55plus_reg_width(a, &out->reg); +} + + +// bytes): a double-word memory operand combined with two accumulators. byte3[7:6] +// selects the operation and operand order (00 = add mem-first, 01 = sub +// mem-first, 10 = sub reg-first); the Smem operand is byte1:byte2 and always a +// dbl() access; ACx is byte3[4:0] and ACy byte2[4:0]. Left unlifted, as in the +// legacy decoder. +static void c55plus_x_8d_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 16) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 8) & 0xff); // byte2 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->access = 32; // dbl() + out->dbl = true; +} + +// memory load feeding add / sub / mov. byte3[7:6] is the operation (00 add, +// 01 sub, 11 mov); the Smem operand is byte1:byte2 (the shared Smem decode reads +// byte2[7:6] + byte1[7:4] only); byte2[5] is the uns() flag; byte4[5:0] is the +// shift count and byte4[6] the dbl() flag. For add/sub the middle ACx is +// byte3[4:0] and the destination ACy is byte2[4:0]; for mov the single +// destination is byte2[4:0]. Left unlifted, as in the legacy decoder. +static void c55plus_x_b7_mem(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + ut8 b_ar = (ut8)((bits >> 24) & 0xff); // byte1 + ut8 b_mode = (ut8)((bits >> 16) & 0xff); // byte2 + ut8 b4 = (ut8)(bits & 0xff); // byte4 + if (!c55plus_smem_amode(b_ar, b_mode, out)) { + out->kind = C55_OP_INVALID; + return; + } + out->uns = (bool)((b_mode >> 5) & 1); + if ((b4 >> 6) & 1) { + out->access = 32; // dbl() + } + out->sh_left = true; + out->shamt = (int8_t)(b4 & 0x3f); // shift count + out->shamt_hex = true; +} +static void c55plus_x_b7_acx(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 8) & 0x1f), &out->reg); // byte3[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} +static void c55plus_x_b7_acy(const C55ArchDesc *a, ut64 bits, const C55OpDesc *d, C55Operand *out) { + (void)d; + out->kind = C55_OP_REG; + c55plus_gr1((ut8)((bits >> 16) & 0x1f), &out->reg); // byte2[4:0] + out->width = c55plus_reg_width(a, &out->reg); +} + +static const C55InsnDef c55plus_table[] = { + // no-operand instructions (mnemonic only) + { .mask = 0xff000000, .match = 0x20000000, .id = TMS320C55_INS_NOP }, + // nop_16 (opcode 0x00, byte1 high nibble 0): the 2-byte no-op. byte1 values + // 0x10/0x20/0x80 are distinct 0x00-family ops (idle / to_word / reserved) and + // stay on the legacy decoder; only the nop_16 sub-range is matched here so it + // types as NOP and lifts to nop() through the shared engine. + { .mask = 0xfff00000, .match = 0x00000000, .id = TMS320C55_INS_NOP_16, .len = 2 }, + { .mask = 0xff000000, .match = 0x21000000, .id = TMS320C55_INS_RET }, + // retcc (opcode 0x08, 2 bytes): conditional return, taken when the + // condition byte (byte1) holds. The condition reuses the shared c55plus_x_cond + // decoder. No IL; the conditional-return analysis keeps a fall-through edge + // and the return-address stack adjustment. + { .mask = 0xff000000, .match = 0x08000000, .id = TMS320C55_INS_RETCC, + .ops = { { .lo = 0, .fn = c55plus_x_cond } } }, + { .mask = 0xffff0000, .match = 0x00c00000, .id = TMS320C55_INS_RETI }, + { .mask = 0xffff0000, .match = 0x00340000, .id = TMS320C55_INS_RESET }, + { .mask = 0xffff0000, .match = 0x00200000, .id = TMS320C55_INS_IDLE }, + // rpt #k16 (opcode 0x6c, 3 bytes): arm the single-instruction repeat counter + // with the 16-bit count in bytes 1:2. It has no data effect of its own (the + // shared lifter emits a nop for the REP op type, matching the legacy decoder). + { .mask = 0xff000000, .match = 0x6c000000, .id = TMS320C55_INS_RPT, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 } } }, + // rptcc #k, (opcode 0x6d, 3 bytes): conditionally repeat the next + // instruction #k+1 times when the condition byte (byte1) holds. #k is the + // 8-bit immediate in byte2; the condition reuses the shared c55plus_x_cond + // decoder. Lifts to a nop (a repeat op type), matching the legacy decoder. + { .mask = 0xff000000, .match = 0x6d000000, .id = TMS320C55_INS_RPTCC, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .fn = c55plus_x_k8 }, { .lo = 8, .fn = c55plus_x_cond } } }, + // rptblocal #l8 (opcode 0x6e, 3 bytes): local block-repeat to an 8-bit block-end + // label (byte2). Lifts to a nop, as the legacy decoder does. + { .mask = 0xff000000, .match = 0x6e000000, .id = TMS320C55_INS_RPTBLOCAL, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .fn = c55plus_x_rptblocal_lbl } } }, + // rptb #l16 (opcode 0x6f, 3 bytes): block-repeat to a 16-bit block-end label. + // No data effect of its own (lifts to a nop), matching the legacy decoder. + { .mask = 0xff000000, .match = 0x6f000000, .id = TMS320C55_INS_RPTB, .lop = C55_LOP_NOP, .len = 3, + .ops = { { .fn = c55plus_x_rptb_lbl } } }, + // amar *ptr1, *ptr2, *ptr3 (opcode 0xea, byte1[7]==1, 6 bytes): the triple + // address-register modify (three register-modify pointers). Left unlifted, as + // in the legacy decoder. + { .mask = 0xff800000, .match = 0xea800000, .id = TMS320C55_INS_AMAR, .len = 6, + .ops = { { .fn = c55plus_x_amar3_p1 }, { .fn = c55plus_x_amar3_p2 }, { .fn = c55plus_x_amar3_p3 } } }, + // amar *Smem (opcode 0x62, 3 bytes): modify auxiliary register via the Smem + // addressing-mode side effect. The shared LEA lift emits the AR post-modify. + { .mask = 0xff000000, .match = 0x62000000, .id = TMS320C55_INS_AMAR, .len = 3, + .ops = { { .fn = c55plus_x_amar_smem } } }, + // amar *sp(#k), xar (opcode 0x63, 3 bytes): load the SP-relative word + // effective address into an extended AR register (xar = sp + k). Only the + // *sp(#k) form is decoded here (see c55plus_x_amar_mem); the DP-direct, + // register-modify, and long const-index forms fall back to the legacy decoder. + { .mask = 0xff000000, .match = 0x63000000, .id = TMS320C55_INS_AMAR, + .ops = { { .fn = c55plus_x_amar_mem }, { .fn = c55plus_x_amar_xar } } }, + // psh/pop reg (opcodes 0x0e/0x0f): single register byte + { .mask = 0xff000000, .match = 0x0e000000, .id = TMS320C55_INS_PSH, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_reg_pshpop } } }, + { .mask = 0xff000000, .match = 0x0f000000, .id = TMS320C55_INS_POP, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_reg_pshpop } } }, + // delay *Smem / psh *Smem (opcodes 0x60 / 0x61, base 3 bytes): a single Smem + // operand (bytes 1:2 with the usual extension), left unlifted as in the + // legacy decoder. c55plus_x_smem_dest decodes the full Smem mode set + // (including the register-scaled *arN(Tx<<#k) forms and the absolute / mmap + // extensions). + { .mask = 0xff000000, .match = 0x60000000, .id = TMS320C55_INS_DELAY, + .ops = { { .fn = c55plus_x_smem_dest } } }, + { .mask = 0xff000000, .match = 0x61000000, .id = TMS320C55_INS_PSH, + .ops = { { .fn = c55plus_x_smem_dest } } }, + // mov src, dst (opcode 0x77): src is byte 2, dst is byte 1 + { .mask = 0xff000000, .match = 0x77000000, .id = TMS320C55_INS_MOV, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_reg_src }, { .lo = 8, .width = 8, .fn = c55plus_x_reg_dst } } }, + // b #target (opcode 0x68): 16-bit pc-relative unconditional branch, the + // branch sibling of call 0x69. The displacement is shown as a 24-bit + // address; c55_effective_type sees the immediate operand and types it JMP. + { .mask = 0xff000000, .match = 0x68000000, .id = TMS320C55_INS_B, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr } } }, + // call #target (opcode 0x69): 16-bit field shown as a 24-bit address + { .mask = 0xff000000, .match = 0x69000000, .id = TMS320C55_INS_CALL, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr } } }, + // call #target (opcode 0x9d, 4 bytes): direct call to a 24-bit absolute + // address in bytes 1:3. Lifts (like the legacy decoder) to an unconditional + // transfer to the resolved target; the return-address push lives in the + // analysis stack metadata, not the IL. + { .mask = 0xff000000, .match = 0x9d000000, .id = TMS320C55_INS_CALL, + .ops = { { .fn = c55plus_x_addr24 } } }, + // b #target (opcode 0x9c, 4 bytes): the unconditional-branch sibling of the + // 0x9d call -- a direct transfer to a 24-bit absolute address in bytes 1:3. + // c55_effective_type sees the address immediate and types it JMP. + { .mask = 0xff000000, .match = 0x9c000000, .id = TMS320C55_INS_B, + .ops = { { .fn = c55plus_x_addr24 } } }, + // callcc #target, (opcode 0x9b, 4 bytes): conditional call to a 16-bit + // absolute target (bytes 1:2), taken when the condition byte (byte3) holds. + // No IL; the conditional-call analysis records the jump, the fall-through + // edge, and the return-address stack adjustment. + { .mask = 0xff000000, .match = 0x9b000000, .id = TMS320C55_INS_CALLCC, + .ops = { { .fn = c55plus_x_callcc_target }, { .lo = 0, .fn = c55plus_x_cond } } }, + // b ACx / call ACx (opcode 0x02, 2 bytes): register-indirect branch/call to + // the 24-bit address held in an accumulator (byte1[4:0]). byte1[7] selects + // the form -- 0 b, 1 call -- and the register operand refines the type to + // UJMP/UCALL. byte1[6]/byte1[5] add the "|| local()" / "|| far()" parallel + // qualifiers, rendered after the register. + { .mask = 0xff800000, .match = 0x02000000, .id = TMS320C55_INS_B, .len = 2, + .ops = { { .fn = c55plus_x_acc_b1lo }, { .fn = c55plus_x_b1_local }, { .fn = c55plus_x_b1_far } } }, + { .mask = 0xff800000, .match = 0x02800000, .id = TMS320C55_INS_CALL, .len = 2, + .ops = { { .fn = c55plus_x_acc_b1lo }, { .fn = c55plus_x_b1_local }, { .fn = c55plus_x_b1_far } } }, + // xccpart / xcc (opcodes 0x07 / 0x06, 2 bytes): the + // conditional-execution guards. byte1 is the condition, encoded exactly as in + // the compare-and-branch family, so the shared condition decode and the COND + // operand formatter render it directly. (xccpart guards a parallel slot, xcc + // the next instruction; both are left unlifted, as the legacy lifter does.) + { .mask = 0xff000000, .match = 0x07000000, .id = TMS320C55_INS_XCCPART, .lop = C55_LOP_NOP, .xcc_guard = true, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + // xccpart (opcode 0x05, 2 bytes): the same conditional-execution guard + // with the byte0=0x05 selector (the 0x07 / 0x05 variants differ only in which + // parallel slot they guard); the condition byte is encoded identically, so it + // reuses the shared condition decode. Left unlifted, as in the legacy decoder. + { .mask = 0xff000000, .match = 0x05000000, .id = TMS320C55_INS_XCCPART, .lop = C55_LOP_NOP, .xcc_guard = true, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + { .mask = 0xff000000, .match = 0x06000000, .id = TMS320C55_INS_XCC, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + // bcc #target, (opcode 0x6a): byte 1 target (8-bit), byte 2 condition + { .mask = 0xff000000, .match = 0x6a000000, .id = TMS320C55_INS_BCC, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_addr }, { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + // bcc #target, (opcode 0x9a, 4 bytes): as 0x6a but with a 16-bit + // target (bytes 1:2) and the condition byte in byte 3. The condition reuses + // the shared cond decode/lift, so the tc-flag and register/accumulator + // compare-with-zero forms lift while the overflow and tc-combination + // conditions the shared predicate builder does not model fall back to the + // per-arch lifter (which also leaves them unlifted). + { .mask = 0xff000000, .match = 0x9a000000, .id = TMS320C55_INS_BCC, .len = 4, + .ops = { { .lo = 8, .width = 16, .fn = c55plus_x_addr }, { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + // bcc #target, (opcode 0xd8, 5 bytes) and callcc #target, + // (opcode 0xd9): a 24-bit absolute target in bytes 1:3 with an 8-bit + // condition in byte 4 -- the long-immediate-target conditional transfers. + // The absolute target drives the jump edge; the conditional-transfer + // analysis adds the fall-through (and, for callcc, the return-address + // stack metadata). + { .mask = 0xff000000, .match = 0xd8000000, .id = TMS320C55_INS_BCC, .len = 5, + .ops = { { .lo = 8, .fn = c55plus_x_addr24 }, { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + { .mask = 0xff000000, .match = 0xd9000000, .id = TMS320C55_INS_CALLCC, .len = 5, + .ops = { { .lo = 8, .fn = c55plus_x_addr24 }, { .lo = 0, .width = 8, .fn = c55plus_x_cond } } }, + // bcc/bccu #target, Ra Rb (opcodes 0xda/0xdb, 5 bytes): target bytes + // 3:4 (16-bit), condition bytes 1:2 (two registers + split 2-bit relop). As + // with the reg-immediate forms, 0xda is signed ('bcc') and 0xdb unsigned + // ('bccu'), selected by uns_all. + { .mask = 0xff000000, .match = 0xda000000, .id = TMS320C55_INS_BCC, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_reg } } }, + { .mask = 0xff000000, .match = 0xdb000000, .id = TMS320C55_INS_BCC, .uns_all = true, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_reg } } }, + // bcc/bccu #target, reg #imm (opcodes 0xdc/0xdd, 5 bytes): target + // bytes 3:4 (16-bit), condition bytes 1:2 (register + split 2-bit relop + + // 7-bit immediate). 0xdc is the signed compare ('bcc'); 0xdd the unsigned + // one ('bccu') -- distinguished by uns_all, which both selects the 'u' + // mnemonic suffix and makes the ordered relops lift to ule/uge. + { .mask = 0xff000000, .match = 0xdc000000, .id = TMS320C55_INS_BCC, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_imm } } }, + { .mask = 0xff000000, .match = 0xdd000000, .id = TMS320C55_INS_BCC, .uns_all = true, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_imm } } }, + // bcc/bccu #target, Ra #k8 (opcodes 0xde/0xdf, 5 bytes): the + // upper-half (0x80-0xff) immediate variant of the reg-immediate + // compare-and-branch above. 0xde is signed ('bcc'), 0xdf unsigned ('bccu'). + { .mask = 0xff000000, .match = 0xde000000, .id = TMS320C55_INS_BCC, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_imm_hi } } }, + { .mask = 0xff000000, .match = 0xdf000000, .id = TMS320C55_INS_BCC, .uns_all = true, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_addr }, { .lo = 16, .width = 16, .fn = c55plus_x_cond_imm_hi } } }, + // add/sub src, dst (opcode 0x74): dst byte 1 (must be <0x80), src byte 2; + // bit 7 of the src byte selects add (0) vs sub (1) + { .mask = 0xff808000, .match = 0x74000000, .id = TMS320C55_INS_ADD, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0x74008000, .id = TMS320C55_INS_SUB, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + // logic src, dst (opcode 0x75): byte1.bit7 and byte2.bit7 jointly select + // and (0,0) / or (0,1) / xor (1,0) / not (1,1, unary src->dst) + { .mask = 0xff808000, .match = 0x75000000, .id = TMS320C55_INS_AND, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0x75008000, .id = TMS320C55_INS_OR, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0x75800000, .id = TMS320C55_INS_XOR, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0x75808000, .id = TMS320C55_INS_NOT, + .ops = { { .lo = 0, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + // sfts/sftl Rb, #S6, Ra (opcode 0xa7, 4 bytes): dst Ra byte1, src Rb byte2 + // (both gr1, low 7 bits), 6-bit signed shift S6 in byte3[5:0]. The + // (byte1.7, byte2.7, byte3.7) bits select the operation; the two pure shifts + // are 1/0/0 (sftl, WACa = WACb <<< S6, logical) and 1/1/0 (sfts, WACa = WACb + // << S6, arithmetic). byte3.7 must be 0 here (it is 1 for the xor / carry + // variants). Operand order [src, #S6, dst] matches the SFTL/SFTS lifts. + { .mask = 0xff808080, .match = 0xa7800000, .id = TMS320C55_INS_SFTL, .lop = C55_LOP_SFTL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808080, .match = 0xa7808000, .id = TMS320C55_INS_SFTS, .lop = C55_LOP_SFTS, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + // Rb << #S6, Ra (opcode 0xa7 shift-ALU forms, 4 bytes): Ra = Ra + // (Rb << S6). Same field layout as the pure shifts (dst Ra byte1, src Rb + // byte2, S6 byte3[5:0]); the (byte1.7, byte2.7, byte3.7) bits select the + // operation: add (0,0,0), sub (0,1,0), and (0,0,1), or (0,1,1), xor (1,0,1). + // The shift is rendered "<< #S6" on the source and lifted as an unsigned + // 8-bit left shift (C55_LOP_*SHL, operand order [src, #S6, dst]). + { .mask = 0xff808080, .match = 0xa7000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6_shl }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808080, .match = 0xa7008000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6_shl }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808080, .match = 0xa7000080, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDSHL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6_shl }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808080, .match = 0xa7008080, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORSHL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6_shl }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808080, .match = 0xa7800080, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORSHL, + .ops = { { .lo = 8, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 0, .width = 6, .fn = c55plus_x_shift6_shl }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + // cmp/cmpu Ra Rb, TCx (opcode 0xa4 CMPR_RR_10, 4 bytes): TCx = (Ra + // Rb). Ra byte1, Rb byte2 (both gr1), relop byte3[3:2], TCx dst + // byte3[0]. byte1.7/byte2.7 must be 0 (the cmpand/cmpor and Ra-vs-#0 variants + // set those / byte3.7). byte3[5] is the $ unsigned bit: separate rows pin it, + // the unsigned one carrying uns_all (the 'u' suffix and unsigned compare). + { .mask = 0xff8080a0, .match = 0xa4000000, .id = TMS320C55_INS_CMP, .lop = C55_LOP_CMP, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_tcflag } } }, + { .mask = 0xff8080a0, .match = 0xa4000020, .id = TMS320C55_INS_CMP, .lop = C55_LOP_CMP, .uns_all = true, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_tcflag } } }, + // cmpand/cmpor[u] Ra Rb, [!]TCx, TCz (opcode 0xa4, byte3[7]==1, 4 + // bytes): TCz = (Ra Rb) {&&,||} [!]TCx. byte2[7] selects cmpand (0) + // vs cmpor (1); byte3[5] is the unsigned $ bit (the 'u' suffix, uns_all); + // byte3[1] picks the TCx input (negated when byte1[7] is set); byte3[0] picks + // the TCz output. Lifts via the shared cmpand/cmpor path, which models the + // compare, the negation, and the and/or with the input flag. + { .mask = 0xff0080a0, .match = 0xa4000080, .id = TMS320C55_INS_CMPAND, .lop = C55_LOP_CMPAND, .len = 4, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_cmp_tcin }, { .fn = c55plus_x_tcflag } } }, + { .mask = 0xff0080a0, .match = 0xa40000a0, .id = TMS320C55_INS_CMPAND, .lop = C55_LOP_CMPAND, .len = 4, .uns_all = true, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_cmp_tcin }, { .fn = c55plus_x_tcflag } } }, + { .mask = 0xff0080a0, .match = 0xa4008080, .id = TMS320C55_INS_CMPOR, .lop = C55_LOP_CMPOR, .len = 4, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_cmp_tcin }, { .fn = c55plus_x_tcflag } } }, + { .mask = 0xff0080a0, .match = 0xa40080a0, .id = TMS320C55_INS_CMPOR, .lop = C55_LOP_CMPOR, .len = 4, .uns_all = true, + .ops = { { .fn = c55plus_x_cmpcond }, { .fn = c55plus_x_cmp_tcin }, { .fn = c55plus_x_tcflag } } }, + // mov/add/sub #k4, Ra (opcode 0x7b register-short forms, 3 bytes): Ra is + // byte1 (gr1, low 7 bits), the 4-bit immediate k4 is byte2[3:0]. The + // (byte1.7 A, byte2.7 B) pair selects the operation when byte2[5] (M) is 0: + // add (0,0), sub (0,1), mov #k4 (1,0), mov -#k4 (1,1). The M=1 forms are the + // shift-by-one variants, left to the legacy decoder for now (mask pins M=0). + { .mask = 0xff80a000, .match = 0x7b000000, .id = TMS320C55_INS_ADD, + .ops = { { .lo = 0, .width = 4, .fn = c55plus_x_k4 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff80a000, .match = 0x7b008000, .id = TMS320C55_INS_SUB, + .ops = { { .lo = 0, .width = 4, .fn = c55plus_x_k4 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff80a000, .match = 0x7b800000, .id = TMS320C55_INS_MOV, + .ops = { { .lo = 0, .width = 4, .fn = c55plus_x_k4 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff80a000, .match = 0x7b808000, .id = TMS320C55_INS_MOV, + .ops = { { .lo = 0, .width = 4, .fn = c55plus_x_negk4 }, { .lo = 8, .width = 8, .fn = c55plus_x_gr7 } } }, + // Ra = Rb k16 (opcodes 0xc4/0xc5, 5 bytes): dst Ra byte1, src Rb byte2 + // (both gr1, low 7 bits), 16-bit immediate bytes 3:4. The (byte1.7, byte2.7) + // pair selects the operation -- 0xc4: add (0,0) / sub (0,1); 0xc5: and (0,0) + // / or (0,1) / xor (1,0). Operand order [#k16, src, dst] matches the 0x7b-7f + // *K forms, so the same C55_LOP_*K lifts apply (extended to the gr1 + // sub-register operands these forms can carry). + { .mask = 0xff808000, .match = 0xc4000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDK, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 24, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0xc4008000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBK, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 24, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0xc5000000, .id = TMS320C55_INS_AND, .lop = C55_LOP_ANDK, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 24, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0xc5008000, .id = TMS320C55_INS_OR, .lop = C55_LOP_ORK, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 24, .width = 8, .fn = c55plus_x_gr7 } } }, + { .mask = 0xff808000, .match = 0xc5800000, .id = TMS320C55_INS_XOR, .lop = C55_LOP_XORK, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 }, { .lo = 24, .width = 8, .fn = c55plus_x_gr7 } } }, + // mov #imm16, reg (opcode 0xac, 4 bytes): dst byte 1 (high bit ignored), + // immediate bytes 2:3 + { .mask = 0xff000000, .match = 0xac000000, .id = TMS320C55_INS_MOV, + .ops = { { .lo = 0, .width = 16, .fn = c55plus_x_imm16 }, { .lo = 16, .width = 8, .fn = c55plus_x_gr7 } } }, + // mov Smem, ACx (opcode 0x58, 3 bytes): register-indirect load. The + // register-modify matrix and plain indirect decode here; other Smem forms + // fall back to the legacy decoder (see c55plus_x_smem). + { .mask = 0xff000000, .match = 0x58000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_smem }, { .fn = c55plus_x_smem_ac } } }, + // mov ACx.h/.l, *Smem (opcode 0x51, base 3 bytes): a 16-bit word store of an + // accumulator half (byte2[5] selects the half). This handles the SP-relative + // and absolute destinations via c55plus_x_word_mem and must precede the + // register-indirect-only row below, which still covers the modes + // c55plus_mem_addr declines (e.g. DP-direct). + { .mask = 0xff000000, .match = 0x51000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_acc_half }, { .fn = c55plus_x_word_mem } } }, + // mov ACx.h/.l, Smem (opcode 0x51, 3 bytes): register-indirect store. The + // accumulator half is byte2[5]; the Smem destination uses the same + // addressing matrix as the load, with the same legacy fallback. + { .mask = 0xff000000, .match = 0x51000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_ac_part }, { .fn = c55plus_x_smem_dest } } }, + // copy Smem, reg (opcode 0x54, 3 bytes): byte2[5:0] is the destination + // register; the Smem source reuses the store addressing decode. Accumulator + // destinations (dbl() source) and reserved slots fall back to the legacy + // decoder. + { .mask = 0xff000000, .match = 0x54000000, .id = TMS320C55_INS_COPY, + .ops = { { .fn = c55plus_x_smem_dest }, { .fn = c55plus_x_gr6 } } }, + // copy dbl(*Smem), xar (opcode 0x56, 3 bytes): a double-word load of a memory + // operand into an extended AR register. byte1 holds the base ARn (low nibble) + // and a short index k4 (high nibble); byte2[3:0] is the xar destination. + // Only the indirect / indexed dbl form is decoded here (see + // c55plus_x_copy_dbl_mem); the register-modify, direct, and byte/half forms + // fall back to the legacy decoder (the absolute-address form is opcode 0xd1). + { .mask = 0xff000000, .match = 0x56000000, .id = TMS320C55_INS_COPY, + .ops = { { .fn = c55plus_x_copy_dbl_mem }, { .fn = c55plus_x_copy_xar } } }, + // copy dbl(*(#addr)), xar (opcode 0xd1, 5 bytes): the absolute-address form of + // the double-word load. byte1[7:6]==10 selects the dbl xar destination + // (byte1[3:0]); the 24-bit byte address is bytes 2:4. Accumulator and + // byte/half destination forms fall back to the legacy decoder. + // mov reg, *(#addr) absolute register stores (opcode 0xd0, 5 bytes): byte1[7:5] + // selects the register type -- 000 ACx (dbl), 001 ARn, 010 ACx.h, 011 ACx.l, + // 100 XARn (dbl) -- byte1[4:0] the register, and bytes 2-4 the 24-bit byte + // address. The remaining types (ACx.g, XARn.h, status) fall back to legacy. + { .mask = 0xffe00000, .match = 0xd0000000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_acc_b1 }, { .fn = c55plus_x_abs_dbl } } }, + { .mask = 0xffe00000, .match = 0xd0200000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_ar_src }, { .fn = c55plus_x_abs_word } } }, + { .mask = 0xffe00000, .match = 0xd0400000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_acc_hi_b1 }, { .fn = c55plus_x_abs_word } } }, + { .mask = 0xffe00000, .match = 0xd0600000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_acc_lo_b1 }, { .fn = c55plus_x_abs_word } } }, + { .mask = 0xffe00000, .match = 0xd0800000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_xar_b1 }, { .fn = c55plus_x_abs_dbl } } }, + { .mask = 0xff000000, .match = 0xd1000000, .id = TMS320C55_INS_COPY, + .ops = { { .fn = c55plus_x_copy_abs_mem }, { .fn = c55plus_x_copy_abs_xar } } }, + // mov ACx, dbl(*Smem) (opcode 0x50, base 3 bytes, byte2[5]==0): a 32-bit + // double-word store of the low 32 bits of a whole accumulator. This is the + // accumulator (dbl()) sub-form of the 0x50 store and must precede the general + // gr6 store row below; the byte2[5]==1 sub-form (mov ARx/Tx, Smem) is handled + // there. + { .mask = 0xff002000, .match = 0x50000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_acc_src }, { .fn = c55plus_x_dbl_mem } } }, + // mov reg, Smem (opcode 0x50, 3 bytes): the store mirror of copy, with the + // register source in byte2[5:0]. Accumulator sources (dbl() memory) and + // reserved slots fall back to the legacy decoder. + { .mask = 0xff000000, .match = 0x50000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_gr6 }, { .fn = c55plus_x_smem_dest } } }, + // mov byte(*Smem), reg / mov reg, byte(*Smem) (opcode 0x8a, base 4 bytes): + // an 8-bit byte() load/store with standard Smem addressing. byte3[7]==1 marks + // the plain byte() forms decoded and lifted here; byte3[6] selects load (1) or + // store (0). The load form reads [mem, reg], the store [reg, mem]. The + // high_byte()/low_byte() variants (byte3[7]==0) are decoded disasm-only below. + { .mask = 0xff0000c0, .match = 0x8a0000c0, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_byte_mem }, { .fn = c55plus_x_byte_reg } } }, + { .mask = 0xff0000c0, .match = 0x8a000080, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_byte_reg }, { .fn = c55plus_x_byte_mem } } }, + // mov *Smem, ACx.l (opcode 0x5b, base 3 bytes): a 16-bit word load into the + // low half of an accumulator. byte2[4:0] is the accumulator. + { .mask = 0xff000000, .match = 0x5b000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_word_mem }, { .fn = c55plus_x_acc_lo } } }, + // mov dbl(*Smem), ACx (opcode 0x5c, base 3 bytes): a 32-bit double-word load + // sign-extended into a whole accumulator. + { .mask = 0xff000000, .match = 0x5c000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_dbl_mem }, { .fn = c55plus_x_acc } } }, + // mov *Smem, ACx.h (opcode 0x5a, base 3 bytes): a 16-bit word load into the + // high half of an accumulator (byte2[5] is the uns() qualifier). + { .mask = 0xff000000, .match = 0x5a000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_word_mem_u }, { .fn = c55plus_x_acc_hi } } }, + // mov #imm, *Smem (opcode 0x48, base 3 bytes): a 16-bit word store of a 6-bit + // immediate (byte2[5:0]). + { .mask = 0xff000000, .match = 0x48000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_imm6 }, { .fn = c55plus_x_word_mem } } }, + // mov XARx, dbl(*Smem) (opcode 0x52, base 3 bytes): a 32-bit double-word store + // of an extended AR pointer. + { .mask = 0xff000000, .match = 0x52000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_xar_src }, { .fn = c55plus_x_dbl_mem } } }, + // mov #imm, byte(*Smem) (opcode 0x4c, base 3 bytes): an 8-bit store of a 6-bit + // immediate (byte2[5:0]) to a byte() memory location. + { .mask = 0xff000000, .match = 0x4c000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_imm6 }, { .fn = c55plus_x_byte_dst } } }, + // mov #imm, byte(*Smem) (opcodes 0x4d/0x4e/0x4f): the 0x40/0x80/0xc0 immediate + // ranges of the same byte store. + { .mask = 0xff000000, .match = 0x4d000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_imm6_40 }, { .fn = c55plus_x_byte_dst } } }, + { .mask = 0xff000000, .match = 0x4e000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_imm6_80 }, { .fn = c55plus_x_byte_dst } } }, + { .mask = 0xff000000, .match = 0x4f000000, .id = TMS320C55_INS_MOV, + .ops = { { .fn = c55plus_x_imm6_c0 }, { .fn = c55plus_x_byte_dst } } }, + // A-unit register-register modify (opcode 0x72): mar(WDAa op WDAb). Two + // 6-bit WDA register fields, dst = byte1[5:0] (Aaaaaa), src = byte2[5:0] + // (Bbbbbb); the top bit of each byte selects the operation -- (1,0) amov + // (MX), (0,1) aadd (AX), (0,0) asub (SX). The amov/asub register moves + // reuse the shared AREG lift; aadd's address-add stays analysis-only here + // (no AREG-add lift op), matching the C55x register-AADD treatment. + { .mask = 0xff808000, .match = 0x72800000, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AREG_MOV, .len = 3, + .ops = { { .lo = 0, .fn = c55plus_x_wda }, { .lo = 8, .fn = c55plus_x_wda } } }, + { .mask = 0xff808000, .match = 0x72008000, .id = TMS320C55_INS_AADD, .len = 3, + .ops = { { .lo = 0, .fn = c55plus_x_wda }, { .lo = 8, .fn = c55plus_x_wda } } }, + { .mask = 0xff808000, .match = 0x72000000, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 3, + .ops = { { .lo = 0, .fn = c55plus_x_wda }, { .lo = 8, .fn = c55plus_x_wda } } }, + // Bit-test register-target forms (opcode 0x89, 4 bytes), the "@#bitnum, + // register" variants. byte1[7]==0 selects the @#k bit-number form (the SP-mem + // forms are not handled here), byte2[7:6]==11 the register target. byte3[7:5] + // selects the operation and operand order; only the "@#k, register" orders + // are taken (000/010 reg-first orders fall through). byte2[5] selects + // bclr/bset for the clear/set group and TC1/TC2 for btst. Left unlifted. + { .mask = 0xff80e0e0, .match = 0x8900c020, .id = TMS320C55_INS_BCLR, .len = 4, + .ops = { { .fn = c55plus_x_bit_num }, { .fn = c55plus_x_bit_reg } } }, + { .mask = 0xff80e0e0, .match = 0x8900e020, .id = TMS320C55_INS_BSET, .len = 4, + .ops = { { .fn = c55plus_x_bit_num }, { .fn = c55plus_x_bit_reg } } }, + { .mask = 0xff80c0e0, .match = 0x8900c060, .id = TMS320C55_INS_BNOT, .len = 4, + .ops = { { .fn = c55plus_x_bit_num }, { .fn = c55plus_x_bit_reg } } }, + { .mask = 0xff80c0e0, .match = 0x8900c0a0, .id = TMS320C55_INS_BTST, .len = 4, + .ops = { { .fn = c55plus_x_bit_num }, { .fn = c55plus_x_bit_reg }, { .fn = c55plus_x_bit_tc } } }, + { .mask = 0xff80c0e0, .match = 0x8900c0e0, .id = TMS320C55_INS_BTSTP, .len = 4, + .ops = { { .fn = c55plus_x_bit_num }, { .fn = c55plus_x_bit_reg } } }, +// add/mov #k16, [dbl(]*Smem[)] (opcode 0xb1, base 5 bytes). byte2[4:3] selects + // the operation (00 = add, 11 = mov); only mov lifts (a store). The 16-bit + // immediate occupies bytes 3:4 of the base; addressing extensions follow. + { .mask = 0xff001800, .match = 0xb1000000, .id = TMS320C55_INS_ADD, .len = 5, + .ops = { { .fn = c55plus_x_b1_imm }, { .fn = c55plus_x_b1_mem } } }, + { .mask = 0xff001800, .match = 0xb1001800, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_b1_imm }, { .fn = c55plus_x_b1_mem } } }, + // sftl/sfts REG, #1 / #-1 (opcode 0x7b, 3 bytes, byte2[6:5]==01). byte1[7] + // selects sftl vs sfts; byte2[7] the shift sign. Left unlifted. + { .mask = 0xff806000, .match = 0x7b002000, .id = TMS320C55_INS_SFTS, .len = 3, + .ops = { { .fn = c55plus_x_sft7b_reg }, { .fn = c55plus_x_sft7b_one } } }, + { .mask = 0xff806000, .match = 0x7b802000, .id = TMS320C55_INS_SFTL, .len = 3, + .ops = { { .fn = c55plus_x_sft7b_reg }, { .fn = c55plus_x_sft7b_one } } }, + // sftl/sfts SRC, SHIFT, DST (opcode 0xa6, byte1[7]==1, byte3[7]==0). byte2[7] + // selects sftl (0) vs sfts (1). The byte3[7]==1 saturating sftsc variant is + // handled by the row below; the saturating sftl form is invalid. Left unlifted. + { .mask = 0xff808080, .match = 0xa6800000, .id = TMS320C55_INS_SFTL, .len = 4, + .ops = { { .fn = c55plus_x_sft_src }, { .fn = c55plus_x_sft_shift }, { .fn = c55plus_x_sft_dst } } }, + { .mask = 0xff808080, .match = 0xa6808000, .id = TMS320C55_INS_SFTS, .lop = C55_LOP_SFTS, .len = 4, + .ops = { { .fn = c55plus_x_sft_src }, { .fn = c55plus_x_sft_shift }, { .fn = c55plus_x_sft_dst } } }, + // sftsc SRC, SHIFT, DST (opcode 0xa6, byte1[7]==1, byte2[7]==1, byte3[7]==1): + // the saturating variant of sfts. Same operands as sfts; left unlifted, as in + // the legacy decoder. (The byte2[7]==0 saturating form is invalid.) + { .mask = 0xff808080, .match = 0xa6808080, .id = TMS320C55_INS_SFTSC, .len = 4, + .ops = { { .fn = c55plus_x_sft_src }, { .fn = c55plus_x_sft_shift }, { .fn = c55plus_x_sft_dst } } }, + // mov [byte(]*Smem[)], [byte(]*Smem[)] (opcode 0x97, 4 bytes). byte2[3] is the + // direction: 1 -> operand A (byte1) is the source, 0 -> operand B (byte3) is + // the source. Left unlifted. + { .mask = 0xff000800, .match = 0x97000800, .id = TMS320C55_INS_MOV, .len = 4, + .ops = { { .fn = c55plus_x_97_memA }, { .fn = c55plus_x_97_memB } } }, + { .mask = 0xff000800, .match = 0x97000000, .id = TMS320C55_INS_MOV, .len = 4, + .ops = { { .fn = c55plus_x_97_memB }, { .fn = c55plus_x_97_memA } } }, + // band *Smem, #k16, TCx (opcode 0xb2, byte2[3]==1, 5 bytes): TCx = (Smem & + // #k16) == 0. Shares the opcode with the cmp form below (byte2[3]==0); the + // band row is matched first since it is the more specific pattern. Left + // unlifted, as in the legacy decoder. + { .mask = 0xff000800, .match = 0xb2000800, .id = TMS320C55_INS_BAND, .len = 5, + .ops = { { .fn = c55plus_x_b2_band_mem }, { .fn = c55plus_x_b2_band_k16 }, { .fn = c55plus_x_b2_tc } } }, + // cmp Smem #k16, TCx (opcode 0xb2, base 5 bytes, byte2[3]==0 cmp). + // Left unlifted. + { .mask = 0xff080000, .match = 0xb2000000, .id = TMS320C55_INS_CMP, .len = 5, + .ops = { { .fn = c55plus_x_b2_cmp }, { .fn = c55plus_x_b2_tc } } }, + // mov ACx., mmap(@reg) (opcode 0x24, byte1==0x51, byte3[6]==0). Left + // unlifted. + { .mask = 0xffff0040, .match = 0x24510000, .id = TMS320C55_INS_MOV, .len = 4, + .ops = { { .fn = c55plus_x_24_src }, { .fn = c55plus_x_24_mmr } } }, + // sftcc ACx, TCx (opcode 0xa9, byte1[7]==1 && byte2[7]==1, 4 bytes). Left + // unlifted (C55_LOP_OPAQUE), as in the legacy decoder. + { .mask = 0xff808000, .match = 0xa9808000, .id = TMS320C55_INS_SFTCC, .lop = C55_LOP_OPAQUE, .len = 4, + .ops = { { .fn = c55plus_x_a9_sftcc_ac }, { .fn = c55plus_x_a9_sftcc_tc } } }, + // bcnt ACa, ACb, TCx, ACdst (opcode 0xa9, byte1[7]==1 && byte2[7]==0, 4 bytes). + // Left unlifted (C55_LOP_OPAQUE), as in the legacy decoder. (byte2[7]==1 is + // the sftcc form, which stays on the legacy decoder.) + { .mask = 0xff808000, .match = 0xa9800000, .id = TMS320C55_INS_BCNT, .lop = C55_LOP_OPAQUE, .len = 4, + .ops = { { .fn = c55plus_x_a9_bcnt_a }, { .fn = c55plus_x_a9_bcnt_b }, { .fn = c55plus_x_a9_bcnt_tc }, { .fn = c55plus_x_a9_bcnt_dst } } }, + // exp ACsrc, ACdst (opcode 0xa9, byte1[7]==0 && byte2[7]==0, 4 bytes). Left + // unlifted (C55_LOP_OPAQUE), as in the legacy decoder. + { .mask = 0xff808000, .match = 0xa9000000, .id = TMS320C55_INS_EXP, .lop = C55_LOP_OPAQUE, .len = 4, + .ops = { { .fn = c55plus_x_a9_exp_src }, { .fn = c55plus_x_a9_exp_dst } } }, + // btst *Smem, reg, TCx (opcode 0x89, byte3[7:5]==101, 4 bytes): memory bit test + // against a register-selected bit number. Left unlifted, as in the legacy + // decoder. + { .mask = 0xff0000e0, .match = 0x890000a0, .id = TMS320C55_INS_BTST, .len = 4, + .ops = { { .fn = c55plus_x_btm_smem }, { .fn = c55plus_x_btm_reg }, { .fn = c55plus_x_btm_tc } } }, + // subadd Tx, [dual(]*Smem[)], ACx (opcode 0x8f, byte3[7:6]==11, 4 bytes): the + // dual-access subtract-add. Left unlifted, as in the legacy decoder. + { .mask = 0xff0000c0, .match = 0x8f0000c0, .id = TMS320C55_INS_SUBADD, .len = 4, + .ops = { { .fn = c55plus_x_subadd_tx }, { .fn = c55plus_x_subadd_mem }, { .fn = c55plus_x_subadd_acx } } }, + // bfxtr ACc., ACb., *Smem, ACa. (opcode 0xbc, byte3[6]==0, 5 + // bytes): the memory-operand bit-field extract. byte3[6]==0 excludes bfins; the + // ACb extractor rejects the byte4[7:6] bfxtl / dbfxtr variants. Left unlifted. + { .mask = 0xff000040, .match = 0xbc000000, .id = TMS320C55_INS_BFXTR, .len = 5, + .ops = { { .fn = c55plus_x_bcx_acc }, { .fn = c55plus_x_bcx_acb }, { .fn = c55plus_x_bcx_smem }, { .fn = c55plus_x_bcx_aca } } }, + // mpym / macm / masm t3 = Smem, ACx, [ACy,] ACz (opcode 0xbb, 5 bytes): single- + // memory MAC with a "t3 = Smem" side-load. byte3[7:6] selects 00 mpym (3 ops) / + // 01 macm / 10 masm (4 ops); byte3[5] is the whole-operation uns (mpymu/macmu/ + // masmu). round (byte2[5]) and fractional (byte4[7]) via .mods. Left unlifted + // (the side-load three-accumulator form is not modelled), as in legacy. + { .mask = 0xff0000e0, .match = 0xbb000000, .id = TMS320C55_INS_MPYM, .len = 5, .mods = 0x200016, .side_load = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acz } } }, + { .mask = 0xff0000e0, .match = 0xbb000020, .id = TMS320C55_INS_MPYM, .len = 5, .mods = 0x200016, .side_load = true, .uns_all = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acz } } }, + { .mask = 0xff0000e0, .match = 0xbb000040, .id = TMS320C55_INS_MACM, .len = 5, .mods = 0x200016, .side_load = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acy }, { .fn = c55plus_x_bb_acz } } }, + { .mask = 0xff0000e0, .match = 0xbb000060, .id = TMS320C55_INS_MACM, .len = 5, .mods = 0x200016, .side_load = true, .uns_all = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acy }, { .fn = c55plus_x_bb_acz } } }, + { .mask = 0xff0000e0, .match = 0xbb000080, .id = TMS320C55_INS_MASM, .len = 5, .mods = 0x200016, .side_load = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acy }, { .fn = c55plus_x_bb_acz } } }, + { .mask = 0xff0000e0, .match = 0xbb0000a0, .id = TMS320C55_INS_MASM, .len = 5, .mods = 0x200016, .side_load = true, .uns_all = true, + .ops = { { .fn = c55plus_x_bb_smem }, { .fn = c55plus_x_bb_acx }, { .fn = c55plus_x_bb_acy }, { .fn = c55plus_x_bb_acz } } }, + // mpym / macm / masm Xmem, Ymem, ACy (opcode 0xe0, 6 bytes): the long-form + // dual-data-memory multiply family. byte3[7:6] selects 00 mpym / 01 macm / 10 + // masm; round (byte2[5]), m40 (byte4[0]) and fractional (byte5[7]) via .mods. + // These lift via the shared multiply-accumulate path. (The byte3[7:6]==11 + // macmz zero-accumulate form stays on the legacy decoder.) + { .mask = 0xff0000c0, .match = 0xe0000000, .id = TMS320C55_INS_MPYM, .len = 6, .mods = 0x20901e, + .ops = { { .fn = c55plus_x_e0_xmem }, { .fn = c55plus_x_e0_ymem }, { .fn = c55plus_x_e0_acy } } }, + { .mask = 0xff0000c0, .match = 0xe0000040, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 6, .mods = 0x20901e, + .ops = { { .fn = c55plus_x_e0_xmem }, { .fn = c55plus_x_e0_ymem }, { .fn = c55plus_x_e0_acy } } }, + { .mask = 0xff0000c0, .match = 0xe0000080, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 6, .mods = 0x20901e, + .ops = { { .fn = c55plus_x_e0_xmem }, { .fn = c55plus_x_e0_ymem }, { .fn = c55plus_x_e0_acy } } }, + // firsadd / firssub Xmem, Ymem, Cmem, ACx, ACy (opcode 0xeb, 6 bytes): byte1[7] + // (in the match head) is 0 for the firs form (the byte1[7]==1 amar::mpy form + // stays on the legacy decoder) and byte2[7]==1 selects firs over the other + // byte2[7]==0 form. The firssub vs firsadd selector (byte4[6]) and the + // fractional modifier (byte5[7]) lie beyond the 4-byte match head, so they are + // applied via alt_bit (-> firssub) and the shared .mods fractional packing. + // These lift via the shared C55_LOP_FIRSADD / FIRSSUB dual-operation path. + { .mask = 0xff808000, .match = 0xeb008000, .id = TMS320C55_INS_FIRSADD, .lop = C55_LOP_FIRSADD, .len = 6, + .alt_bit = 15, .alt_id = TMS320C55_INS_FIRSSUB, .alt_lop = C55_LOP_FIRSSUB, .mods = (8u << 18), + .ops = { { .fn = c55plus_x_fir_xmem }, { .fn = c55plus_x_fir_ymem }, { .fn = c55plus_x_fir_cmem }, { .fn = c55plus_x_fir_acx }, { .fn = c55plus_x_fir_acy } } }, + // abdst / lms / lmsf / sqdst Xmem, Ymem, ACx, ACy (opcode 0xce, 5 bytes): the + // operation is selected by (byte1[7], byte3[7], byte2[7]): (0,0,0) sqdst, + // (0,0,1) abdst, (1,1,0) lms, (1,1,1) lmsf. byte4[7] is an additional + // fractional modifier (so lms->lmsf and lmsf->lmsff), decoded through the + // shared .mods packing. These lift via the shared dual-operation path. + { .mask = 0xff808080, .match = 0xce000000, .id = TMS320C55_INS_SQDST, .lop = C55_LOP_SQDST, .len = 5, + .ops = { { .fn = c55plus_x_dst_xmem }, { .fn = c55plus_x_dst_ymem }, { .fn = c55plus_x_dst_acx }, { .fn = c55plus_x_dst_acy } } }, + { .mask = 0xff808080, .match = 0xce008000, .id = TMS320C55_INS_ABDST, .lop = C55_LOP_ABDST, .len = 5, + .ops = { { .fn = c55plus_x_dst_xmem }, { .fn = c55plus_x_dst_ymem }, { .fn = c55plus_x_dst_acx }, { .fn = c55plus_x_dst_acy } } }, + { .mask = 0xff808080, .match = 0xce800080, .id = TMS320C55_INS_LMS, .lop = C55_LOP_LMS, .len = 5, .mods = (8u << 18), + .ops = { { .fn = c55plus_x_dst_xmem }, { .fn = c55plus_x_dst_ymem }, { .fn = c55plus_x_dst_acx }, { .fn = c55plus_x_dst_acy } } }, + { .mask = 0xff808080, .match = 0xce808080, .id = TMS320C55_INS_LMSF, .lop = C55_LOP_LMS, .len = 5, .mods = (8u << 18), + .ops = { { .fn = c55plus_x_dst_xmem }, { .fn = c55plus_x_dst_ymem }, { .fn = c55plus_x_dst_acx }, { .fn = c55plus_x_dst_acy } } }, + // maxdiff / mindiff / dmaxdiff / dmindiff ACc, ACd, ACa, ACb, [pair(]trnN[)] + // (opcode 0xd4, 5 bytes): byte1[7] selects the d-variant (bare trn over the + // pair()-wrapped form), byte2[7] the min-variant. Rendered via diff_form; left + // unlifted, as in the legacy decoder. + { .mask = 0xff808000, .match = 0xd4000000, .id = TMS320C55_INS_MAXDIFF, .len = 5, .diff_form = true, .diff_pair = true, + .ops = { { .fn = c55plus_x_diff_acc }, { .fn = c55plus_x_diff_acd }, { .fn = c55plus_x_diff_aca }, { .fn = c55plus_x_diff_acb }, { .fn = c55plus_x_diff_trn } } }, + { .mask = 0xff808000, .match = 0xd4008000, .id = TMS320C55_INS_MINDIFF, .len = 5, .diff_form = true, .diff_pair = true, + .ops = { { .fn = c55plus_x_diff_acc }, { .fn = c55plus_x_diff_acd }, { .fn = c55plus_x_diff_aca }, { .fn = c55plus_x_diff_acb }, { .fn = c55plus_x_diff_trn } } }, + { .mask = 0xff808000, .match = 0xd4800000, .id = TMS320C55_INS_DMAXDIFF, .len = 5, .diff_form = true, + .ops = { { .fn = c55plus_x_diff_acc }, { .fn = c55plus_x_diff_acd }, { .fn = c55plus_x_diff_aca }, { .fn = c55plus_x_diff_acb }, { .fn = c55plus_x_diff_trn } } }, + { .mask = 0xff808000, .match = 0xd4808000, .id = TMS320C55_INS_DMINDIFF, .len = 5, .diff_form = true, + .ops = { { .fn = c55plus_x_diff_acc }, { .fn = c55plus_x_diff_acd }, { .fn = c55plus_x_diff_aca }, { .fn = c55plus_x_diff_acb }, { .fn = c55plus_x_diff_trn } } }, + // sqrm / sqam / sqsm *Smem, [ACx,] ACy (opcode 0x92, 4 bytes): square a memory + // operand. byte3[7:5] selects the operation: 000 sqrm (ACy = Smem*Smem, no + // accumulate), 010 sqam (ACy = ACx + Smem*Smem), 100 sqsm (subtract). round = + // byte2[5], fractional = byte3[5], both via the shared .mods packing. Lift via + // the shared squaring multiply path. + { .mask = 0xff0000e0, .match = 0x92000000, .id = TMS320C55_INS_SQRM, .len = 4, .mods = 0x18000e, .square = true, + .ops = { { .fn = c55plus_x_sq_mem }, { .fn = c55plus_x_sq_acy } } }, + { .mask = 0xff0000e0, .match = 0x92000040, .id = TMS320C55_INS_SQAM, .lop = C55_LOP_MAC, .len = 4, .mods = 0x18000e, .square = true, + .ops = { { .fn = c55plus_x_sq_mem }, { .fn = c55plus_x_sq_acx }, { .fn = c55plus_x_sq_acy } } }, + { .mask = 0xff0000e0, .match = 0x92000080, .id = TMS320C55_INS_SQSM, .lop = C55_LOP_MAS, .len = 4, .mods = 0x18000e, .square = true, + .ops = { { .fn = c55plus_x_sq_mem }, { .fn = c55plus_x_sq_acx }, { .fn = c55plus_x_sq_acy } } }, + // mpymk Xmem, #k8, ACy (opcode 0xb8, byte3[6]==0, 5 bytes) and macmk Xmem, #k8, + // ACx, ACy (byte3[6]==1): constant-coefficient memory multiply / multiply- + // accumulate. byte3[5] is the fractional (f) modifier. Left unlifted, as in the + // legacy decoder. + { .mask = 0xff000040, .match = 0xb8000000, .id = TMS320C55_INS_MPYMK, .len = 5, .mods = (14u << 18), + .ops = { { .fn = c55plus_x_mpymk_mem }, { .fn = c55plus_x_mpymk_k8 }, { .fn = c55plus_x_mpymk_acy } } }, + { .mask = 0xff000040, .match = 0xb8000040, .id = TMS320C55_INS_MACMK, .len = 5, .mods = (14u << 18), + .ops = { { .fn = c55plus_x_mpymk_mem }, { .fn = c55plus_x_mpymk_k8 }, { .fn = c55plus_x_mpymk_acx }, { .fn = c55plus_x_mpymk_acy } } }, + // mpymk / macmk t3 = Xmem, #k8, [ACx,] ACy (opcode 0xb9, 5 bytes): the side-load + // ("t3 = Xmem") sibling of the 0xb8 constant-coefficient memory multiply, with + // the identical field layout. Left unlifted, as in the legacy decoder. + { .mask = 0xff000040, .match = 0xb9000000, .id = TMS320C55_INS_MPYMK, .len = 5, .mods = (14u << 18), .side_load = true, + .ops = { { .fn = c55plus_x_mpymk_mem }, { .fn = c55plus_x_mpymk_k8 }, { .fn = c55plus_x_mpymk_acy } } }, + { .mask = 0xff000040, .match = 0xb9000040, .id = TMS320C55_INS_MACMK, .len = 5, .mods = (14u << 18), .side_load = true, + .ops = { { .fn = c55plus_x_mpymk_mem }, { .fn = c55plus_x_mpymk_k8 }, { .fn = c55plus_x_mpymk_acx }, { .fn = c55plus_x_mpymk_acy } } }, + // mant ACa, ACb :: nexp ACa, ACc (opcode 0xa9, byte1[7]==0 && byte2[7]==1, 4 + // bytes): dual mantissa / negated-exponent helper, rendered via the mant_nexp + // flag. Left unlifted, as in the legacy decoder. + { .mask = 0xff808000, .match = 0xa9008000, .id = TMS320C55_INS_MANT, .len = 4, .mant_nexp = true, + .ops = { { .fn = c55plus_x_mant_aca }, { .fn = c55plus_x_mant_acb }, { .fn = c55plus_x_mant_acc } } }, + // rpt csr / rptadd csr, #k|reg / rptsub csr, #k (opcode 0x01, 2 bytes). + // byte1[7:6]: 00 rptsub #k, 01 rptadd #k, 10 rptadd reg, 11 rpt. All nop-lift. + { .mask = 0xffc00000, .match = 0x01000000, .id = TMS320C55_INS_RPTSUB, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55plus_x_01_csr }, { .fn = c55plus_x_01_k4 } } }, + { .mask = 0xffc00000, .match = 0x01400000, .id = TMS320C55_INS_RPTADD, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55plus_x_01_csr }, { .fn = c55plus_x_01_k4 } } }, + { .mask = 0xffc00000, .match = 0x01800000, .id = TMS320C55_INS_RPTADD, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55plus_x_01_csr }, { .fn = c55plus_x_01_reg } } }, + { .mask = 0xffc00000, .match = 0x01c00000, .id = TMS320C55_INS_RPT, .lop = C55_LOP_NOP, .len = 2, + .ops = { { .fn = c55plus_x_01_csr } } }, + // rol / ror carry, ACx, carry, ACy (opcode 0xa8, 4 bytes). byte2[7] selects + // rol (0) / ror (1); byte3[0] the carry-in flag and byte3[1] the carry-out + // flag (0 carry, 1 tc2). Whole-accumulator forms only; lifted by the shared + // rol/ror path. + { .mask = 0xff008000, .match = 0xa8000000, .id = TMS320C55_INS_ROL, .lop = C55_LOP_ROL, .len = 4, + .ops = { { .lo = 0, .fn = c55plus_x_a8_flag }, { .fn = c55plus_x_a8_src }, { .lo = 1, .fn = c55plus_x_a8_flag }, { .fn = c55plus_x_a8_dst } } }, + { .mask = 0xff008000, .match = 0xa8008000, .id = TMS320C55_INS_ROR, .lop = C55_LOP_ROR, .len = 4, + .ops = { { .lo = 0, .fn = c55plus_x_a8_flag }, { .fn = c55plus_x_a8_src }, { .lo = 1, .fn = c55plus_x_a8_flag }, { .fn = c55plus_x_a8_dst } } }, + // bclr / bset st0_, st0_55 (opcode 0x0a, 2 bytes). byte1[5] selects + // bset (1) vs bclr (0); byte1[4:0] is the bit. The semantic bits (9..15) + // lift to an st0_55 bit set/clear; the data-page bits produce no IL. + { .mask = 0xffe00000, .match = 0x0a000000, .id = TMS320C55_INS_BCLR, .lop = C55_LOP_STBITCLR, .len = 2, + .ops = { { .fn = c55plus_x_0a_bit }, { .fn = c55plus_x_0a_st0 } } }, + { .mask = 0xffe00000, .match = 0x0a200000, .id = TMS320C55_INS_BSET, .lop = C55_LOP_STBITSET, .len = 2, + .ops = { { .fn = c55plus_x_0a_bit }, { .fn = c55plus_x_0a_st0 } } }, + // pop / psh mmap(@reg) (opcode 0x24, byte1==0x61, 4 bytes). byte3[3] selects + // pop (1) vs psh (0); byte2 is the mmap register. Lifted by the shared stack + // path. + { .mask = 0xffff0008, .match = 0x24610008, .id = TMS320C55_INS_POP, .len = 4, + .ops = { { .fn = c55plus_x_24_mmr_stack } } }, + { .mask = 0xffff0008, .match = 0x24610000, .id = TMS320C55_INS_PSH, .len = 4, + .ops = { { .fn = c55plus_x_24_mmr_stack } } }, + // pop / psh rX, rY (opcodes 0x71 / 0x70, 3 bytes): register-pair pop / push. + // rX = gr1(byte1), rY = gr1(byte2). Lifted by the shared stack path. + { .mask = 0xff000000, .match = 0x71000000, .id = TMS320C55_INS_POP, .len = 3, + .ops = { { .fn = c55plus_x_pair_reg1 }, { .fn = c55plus_x_pair_reg2 } } }, + { .mask = 0xff000000, .match = 0x70000000, .id = TMS320C55_INS_PSH, .len = 3, + .ops = { { .fn = c55plus_x_pair_reg1 }, { .fn = c55plus_x_pair_reg2 } } }, + // pshboth / popboth (opcode 0x0d, 2 bytes): byte1[7] selects pshboth (0) + // over popboth (1); byte1[5] the ac/xar register class. The "both" flag marks + // the register-pair stack semantics. Left unlifted, as the legacy decoder does. + { .mask = 0xff800000, .match = 0x0d000000, .id = TMS320C55_INS_PSHBOTH, .len = 2, .both = true, + .ops = { { .fn = c55plus_x_pshpopboth_reg } } }, + { .mask = 0xff800000, .match = 0x0d800000, .id = TMS320C55_INS_POPBOTH, .len = 2, .both = true, + .ops = { { .fn = c55plus_x_pshpopboth_reg } } }, + // swap rX, rY (opcode 0x03, byte1[7:6]==10, 2 bytes). Single-register-pair + // encodings only (the extractor declines the others, which fall back to the + // legacy decoder). Lifts via the shared XOR-swap (XCHG) idiom. + { .mask = 0xffc00000, .match = 0x03800000, .id = TMS320C55_INS_SWAP, .len = 2, + .ops = { { .fn = c55plus_x_swap_x }, { .fn = c55plus_x_swap_y } } }, + // intr #k5 / trap #k5 / sim_trig (opcode 0x03, 2 bytes). byte1[7:6] selects: + // 00 intr, 01 trap, 11 sim_trig. Left unlifted. + { .mask = 0xffc00000, .match = 0x03000000, .id = TMS320C55_INS_INTR, .len = 2, + .ops = { { .fn = c55plus_x_03_k5 } } }, + { .mask = 0xffc00000, .match = 0x03400000, .id = TMS320C55_INS_TRAP, .len = 2, + .ops = { { .fn = c55plus_x_03_k5 } } }, + { .mask = 0xffc00000, .match = 0x03c00000, .id = TMS320C55_INS_SIM_TRIG, .len = 2, .ops = { { 0 } } }, + // estop (opcode 0x23, 1 byte) and ecopr (opcode 0x0b, 2 bytes): the + // emulation-stop and emulation-coprocessor cpu-control instructions, typed + // as traps. Operandless (the legacy decoder's trailing placeholder bytes are + // a rendering artifact). Left unlifted. + { .mask = 0xff000000, .match = 0x23000000, .id = TMS320C55_INS_ESTOP, .lop = C55_LOP_NOP, .len = 1, .ops = { { 0 } } }, + { .mask = 0xff000000, .match = 0x0b000000, .id = TMS320C55_INS_ECOPR, .lop = C55_LOP_NOP, .len = 2, .ops = { { 0 } } }, + // round/satr/sat SRC, DST (opcode 0x79, 3 bytes). byte1[7]==0 -> round; + // byte1[7]==1 -> sat, byte1[5] selecting satr (the rounding 'r' suffix, round + // bit = byte1[5] = bit 21 of the 3-byte word). Left unlifted. + { .mask = 0xff800000, .match = 0x79000000, .id = TMS320C55_INS_ROUND, .len = 3, + .ops = { { .fn = c55plus_x_79_src }, { .fn = c55plus_x_79_dst } } }, + { .mask = 0xffa00000, .match = 0x79800000, .id = TMS320C55_INS_SAT, .len = 3, + .ops = { { .fn = c55plus_x_79_src }, { .fn = c55plus_x_79_dst } } }, + { .mask = 0xffa00000, .match = 0x79a00000, .id = TMS320C55_INS_SAT, .len = 3, .mods = 22, + .ops = { { .fn = c55plus_x_79_src }, { .fn = c55plus_x_79_dst } } }, + // sat (opcode 0x2a2d, 2 bytes) and circ (opcode 0x27, 1 byte): fixed-encoding + // control instructions. Left unlifted. + { .mask = 0xffff0000, .match = 0x2a2d0000, .id = TMS320C55_INS_SAT, .len = 2, .ops = { { 0 } } }, + { .mask = 0xff000000, .match = 0x27000000, .id = TMS320C55_INS_CIRC, .len = 1, .ops = { { 0 } } }, + // neg/abs/min/max SRC, DST (opcode 0x76, 3 bytes). byte1[7] + byte2[7] select + // the operation. Lifted by the shared register-op path. + { .mask = 0xff808000, .match = 0x76000000, .id = TMS320C55_INS_ABS, .lop = C55_LOP_ABS, .len = 3, + .ops = { { .fn = c55plus_x_76_src }, { .fn = c55plus_x_76_dst } } }, + { .mask = 0xff808000, .match = 0x76008000, .id = TMS320C55_INS_NEG, .lop = C55_LOP_NEG, .len = 3, + .ops = { { .fn = c55plus_x_76_src }, { .fn = c55plus_x_76_dst } } }, + { .mask = 0xff808000, .match = 0x76800000, .id = TMS320C55_INS_MAX, .lop = C55_LOP_MAX, .len = 3, + .ops = { { .fn = c55plus_x_76_src }, { .fn = c55plus_x_76_dst } } }, + { .mask = 0xff808000, .match = 0x76808000, .id = TMS320C55_INS_MIN, .lop = C55_LOP_MIN, .len = 3, + .ops = { { .fn = c55plus_x_76_src }, { .fn = c55plus_x_76_dst } } }, + // add/sub #k16 << #16, ACx, ACy (opcode 0xc0, base 5 bytes). byte2[7] selects + // the operation; lifted by the shared immediate-shift ALU path. + { .mask = 0xff008000, .match = 0xc0000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 5, + .ops = { { .fn = c55plus_x_c0_imm }, { .fn = c55plus_x_c0_acx }, { .fn = c55plus_x_c0_acy } } }, + { .mask = 0xff008000, .match = 0xc0008000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 5, + .ops = { { .fn = c55plus_x_c0_imm }, { .fn = c55plus_x_c0_acx }, { .fn = c55plus_x_c0_acy } } }, + // add/sub #k16 << #sh, ACx, ACy (opcode 0xc2, base 5 bytes, byte1[7]==0). + // byte2[7] selects add vs sub. Lifted by the shared immediate-shift ALU path. + { .mask = 0xff808000, .match = 0xc2000000, .id = TMS320C55_INS_ADD, .lop = C55_LOP_ADDSHL, .len = 5, + .ops = { { .fn = c55plus_x_c2_imm }, { .fn = c55plus_x_c2_acx }, { .fn = c55plus_x_c2_acy } } }, + { .mask = 0xff808000, .match = 0xc2008000, .id = TMS320C55_INS_SUB, .lop = C55_LOP_SUBSHL, .len = 5, + .ops = { { .fn = c55plus_x_c2_imm }, { .fn = c55plus_x_c2_acx }, { .fn = c55plus_x_c2_acy } } }, + // mov #k16 << #sh, ACx (opcode 0xc2, byte1[7]==1). Lifted by the shared + // shifted-immediate load path. + { .mask = 0xff800000, .match = 0xc2800000, .id = TMS320C55_INS_MOV, .lop = C55_LOP_MOVSHL, .len = 5, + .ops = { { .fn = c55plus_x_c2_imm }, { .fn = c55plus_x_c2_acy } } }, + // add *Smem, ACx, ACy (opcode 0x80, 4 bytes). Same operand shape as 0x85. + // Left unlifted. + { .mask = 0xff000000, .match = 0x80000000, .id = TMS320C55_INS_ADD, .len = 4, + .ops = { { .fn = c55plus_x_80_mem }, { .fn = c55plus_x_85_acx }, { .fn = c55plus_x_85_acy } } }, + // or *Smem, ACx, ACy (opcode 0x85, 4 bytes). Left unlifted. + { .mask = 0xff000000, .match = 0x85000000, .id = TMS320C55_INS_OR, .len = 4, + .ops = { { .fn = c55plus_x_85_mem }, { .fn = c55plus_x_85_acx }, { .fn = c55plus_x_85_acy } } }, + // sub ACx., *Smem, ACy. (opcode 0x82, 4 bytes). Left unlifted. + { .mask = 0xff000000, .match = 0x82000000, .id = TMS320C55_INS_SUB, .len = 4, + .ops = { { .fn = c55plus_x_82_acx }, { .fn = c55plus_x_82_mem }, { .fn = c55plus_x_82_acy } } }, + // add uns(*Smem), ACx, ACy (opcode 0x8c, 4 bytes). Left unlifted. + { .mask = 0xff0020e0, .match = 0x8c002000, .id = TMS320C55_INS_ADD, .len = 4, + .ops = { { .fn = c55plus_x_8c_mem }, { .fn = c55plus_x_8c_acx }, { .fn = c55plus_x_8c_acy } } }, + // add [uns(]*Smem[)], carry, ACx, ACy (opcode 0x8c, byte3[7:5]==001, 4 bytes): + // the add-with-carry variant -- a fixed "carry" operand sits between the Smem + // source and the two accumulators (src byte3[4:0], dst byte2[4:0]); uns is + // byte2[5]. Left unlifted, as the plain 0x8c form is. + { .mask = 0xff0000e0, .match = 0x8c000020, .id = TMS320C55_INS_ADD, .len = 4, + .ops = { { .fn = c55plus_x_8c_carry_mem }, { .fn = c55plus_x_carry }, { .fn = c55plus_x_8c_acx }, { .fn = c55plus_x_8c_acy } } }, + // add/sub dbl(*Smem), ACx, ACy and sub ACx, dbl(*Smem), ACy (opcode 0x8d). + // byte3[7:6] selects op + operand order. Left unlifted. + { .mask = 0xff0000c0, .match = 0x8d000000, .id = TMS320C55_INS_ADD, .len = 4, + .ops = { { .fn = c55plus_x_8d_mem }, { .fn = c55plus_x_8c_acx }, { .fn = c55plus_x_8c_acy } } }, + { .mask = 0xff0000c0, .match = 0x8d000040, .id = TMS320C55_INS_SUB, .len = 4, + .ops = { { .fn = c55plus_x_8d_mem }, { .fn = c55plus_x_8c_acx }, { .fn = c55plus_x_8c_acy } } }, + { .mask = 0xff0000c0, .match = 0x8d000080, .id = TMS320C55_INS_SUB, .len = 4, + .ops = { { .fn = c55plus_x_8c_acx }, { .fn = c55plus_x_8d_mem }, { .fn = c55plus_x_8c_acy } } }, + // add/sub/mov [uns](*Smem) << #sh, ... (opcode 0xb7, base 5 bytes). byte3[7:6] + // selects the operation. Left unlifted. + { .mask = 0xff0000c0, .match = 0xb7000000, .id = TMS320C55_INS_ADD, .len = 5, + .ops = { { .fn = c55plus_x_b7_mem }, { .fn = c55plus_x_b7_acx }, { .fn = c55plus_x_b7_acy } } }, + { .mask = 0xff0000c0, .match = 0xb7000040, .id = TMS320C55_INS_SUB, .len = 5, + .ops = { { .fn = c55plus_x_b7_mem }, { .fn = c55plus_x_b7_acx }, { .fn = c55plus_x_b7_acy } } }, + { .mask = 0xff0000c0, .match = 0xb70000c0, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_b7_mem }, { .fn = c55plus_x_b7_acy } } }, + // btstclr / btstset / btst / btstnot #k5, [dbl(]*Smem[)], TCx (opcode 0x91, + // byte2[4:2] selecting op+access): the extended memory bit-test family. Each row + // pins byte2[4:2]; the shared mem extractor rejects byte3[7:5]==000 so the word + // btstset below still matches that form. Left unlifted, as in the legacy decoder. + { .mask = 0xff001c00, .match = 0x91000400, .id = TMS320C55_INS_BTSTCLR, .len = 4, + .ops = { { .fn = c55plus_x_btx_bit }, { .fn = c55plus_x_btx_mem }, { .fn = c55plus_x_btx_tc } } }, + { .mask = 0xff001c00, .match = 0x91000c00, .id = TMS320C55_INS_BTSTSET, .len = 4, + .ops = { { .fn = c55plus_x_btx_bit }, { .fn = c55plus_x_btx_mem }, { .fn = c55plus_x_btx_tc } } }, + { .mask = 0xff001c00, .match = 0x91001400, .id = TMS320C55_INS_BTST, .len = 4, + .ops = { { .fn = c55plus_x_btx_bit }, { .fn = c55plus_x_btx_mem }, { .fn = c55plus_x_btx_tc } } }, + { .mask = 0xff001c00, .match = 0x91001c00, .id = TMS320C55_INS_BTSTNOT, .len = 4, + .ops = { { .fn = c55plus_x_btx_bit }, { .fn = c55plus_x_btx_mem }, { .fn = c55plus_x_btx_tc } } }, + // btstset #k, *Smem, TCx (opcode 0x91, byte3[7:5]==000): test-and-set a memory + // bit. The other 0x91 byte3[7:5] sub-codes (btstclr and the dbl-operand forms) + // are left to the legacy decoder. + { .mask = 0xff0000e0, .match = 0x91000000, .id = TMS320C55_INS_BTSTSET, .len = 4, + .ops = { { .fn = c55plus_x_btstset_bit }, { .fn = c55plus_x_btstset_mem }, { .fn = c55plus_x_btstset_tc } } }, + // bfxtr #k16, ACsrc, ACdst (opcode 0xc6, 5 bytes): bit-field extract, left + // unlifted as in the legacy decoder. byte2[7:5]==011 is fixed (in the mask): + // byte2[7] distinguishes bfxtr from the bfxpa expand form. + { .mask = 0xff00e000, .match = 0xc6006000, .id = TMS320C55_INS_BFXTR, .len = 5, + .ops = { { .fn = c55plus_x_bfxtr_imm }, { .fn = c55plus_x_bfxtr_src }, { .fn = c55plus_x_bfxtr_dst } } }, + // mpy / mac / mas SRC1, SRC2, ACdst (opcode 0xaa, 4 bytes): register multiply, + // optionally accumulating. (byte1[7], byte2[7]) selects (0,0) mpy, (0,1) mac, + // (1,0) mas; the four-operand (1,1) form stays on the legacy decoder. SRC1 is + // gr1(byte2[6:0]), SRC2 is gr1(byte3[6:0]) with byte3[7] the uns() wrapper, + // ACdst is ac(byte1[4:0]); byte1[5]/byte1[6] are the round (r) / fractional (f) + // modifiers. These lift via the shared multiply (-accumulate) path. + { .mask = 0xff808000, .match = 0xaa000000, .id = TMS320C55_INS_MPY, .len = 4, .mods = 0x5c0016, + .ops = { { .fn = c55plus_x_macr_src2 }, { .fn = c55plus_x_macr_src1 }, { .fn = c55plus_x_macr_acdst } } }, + { .mask = 0xff808000, .match = 0xaa008000, .id = TMS320C55_INS_MAC, .lop = C55_LOP_MAC, .len = 4, .mods = 0x5c0016, + .ops = { { .fn = c55plus_x_macr_src1 }, { .fn = c55plus_x_macr_src2 }, { .fn = c55plus_x_macr_acdst } } }, + { .mask = 0xff808000, .match = 0xaa800000, .id = TMS320C55_INS_MAS, .lop = C55_LOP_MAS, .len = 4, .mods = 0x5c0016, + .ops = { { .fn = c55plus_x_macr_src2 }, { .fn = c55plus_x_macr_src1 }, { .fn = c55plus_x_macr_acdst } } }, + // mpym [uns(]Xmem[)], [uns(]Ymem[)], ACy (opcode 0xc8, byte1[7]==0 && + // byte2[7]==0, 5 bytes): ACy = Xmem * Ymem. Xmem is byte1 (reg-modify matrix), + // Ymem is byte3 (reg-modify matrix), ACy is byte2[4:0]. byte2[5]/byte4[0] mark + // the Xmem/Ymem uns() wrappers. Lifts via the shared multiply path. + { .mask = 0xff808000, .match = 0xc8000000, .id = TMS320C55_INS_MPYM, .len = 5, .mods = 0x217000, + .ops = { { .fn = c55plus_x_mac_xmem }, { .fn = c55plus_x_mac_ymem }, { .fn = c55plus_x_mac_acy } } }, + // macm [uns(]Xmem[)], [uns(]Ymem[)], ACx, ACy (opcode 0xc8, byte1[7]==0 && + // byte2[7]==1, 5 bytes): ACy = ACx + Xmem * Ymem. masm (byte1[7]==1 && + // byte2[7]==0) subtracts; the byte1[7]==1 && byte2[7]==1 form is macm with the + // accumulator shifted right 16 first (ACy = (ACx >> #16) + Xmem * Ymem). ACx + // is byte4[4:0]; all three reuse the mpym memory / ACy extractors and lift via + // the shared multiply-accumulate path. + { .mask = 0xff808000, .match = 0xc8008000, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 5, .mods = 0x217000, + .ops = { { .fn = c55plus_x_mac_xmem }, { .fn = c55plus_x_mac_ymem }, { .fn = c55plus_x_mac_acx }, { .fn = c55plus_x_mac_acy } } }, + { .mask = 0xff808000, .match = 0xc8800000, .id = TMS320C55_INS_MASM, .lop = C55_LOP_MAS, .len = 5, .mods = 0x217000, + .ops = { { .fn = c55plus_x_mac_xmem }, { .fn = c55plus_x_mac_ymem }, { .fn = c55plus_x_mac_acx }, { .fn = c55plus_x_mac_acy } } }, + { .mask = 0xff808000, .match = 0xc8808000, .id = TMS320C55_INS_MACM, .lop = C55_LOP_MAC, .len = 5, .mods = 0x217000, .shift16 = true, + .ops = { { .fn = c55plus_x_mac_xmem }, { .fn = c55plus_x_mac_ymem }, { .fn = c55plus_x_mac_acx }, { .fn = c55plus_x_mac_acy } } }, + // bfxpa #k16, ACsrc, ACdst (opcode 0xc6, byte2[7]==1, 5 bytes): bit-field + // expand and pack -- the byte2[7]==1 counterpart of bfxtr (byte2[7]==0), with + // the same operand layout and the same extractors. Left unlifted, as in the + // legacy decoder. + { .mask = 0xff00e000, .match = 0xc600e000, .id = TMS320C55_INS_BFXPA, .len = 5, + .ops = { { .fn = c55plus_x_bfxtr_imm }, { .fn = c55plus_x_bfxtr_src }, { .fn = c55plus_x_bfxtr_dst } } }, + // addsubcc / addsub2cc / subc *Smem, ... (opcode 0xb3, 5 bytes): the Smem is + // byte1:byte2 (shared addressing), ACx is byte3[4:0], ACy is byte2[4:0]. + // byte3[7:6] selects the operation: 00 addsubcc (one TCx from byte2[5]), 01 + // addsubcc (the two-flag tc1,tc2 form), 10 addsub2cc (an extra ACz = gr1 of + // byte4 and tc1,tc2), 11 subc. All left unlifted, as in the legacy decoder. + { .mask = 0xff0000c0, .match = 0xb3000000, .id = TMS320C55_INS_ADDSUBCC, .len = 5, + .ops = { { .fn = c55plus_x_b3_mem }, { .fn = c55plus_x_b3_acx }, { .fn = c55plus_x_b3_tcx }, { .fn = c55plus_x_b3_acy } } }, + { .mask = 0xff0000c0, .match = 0xb3000040, .id = TMS320C55_INS_ADDSUBCC, .len = 5, + .ops = { { .fn = c55plus_x_b3_mem }, { .fn = c55plus_x_b3_acx }, { .fn = c55plus_x_b3_tc1 }, { .fn = c55plus_x_b3_tc2 }, { .fn = c55plus_x_b3_acy } } }, + { .mask = 0xff0000c0, .match = 0xb3000080, .id = TMS320C55_INS_ADDSUB2CC, .len = 5, + .ops = { { .fn = c55plus_x_b3_mem }, { .fn = c55plus_x_b3_acx }, { .fn = c55plus_x_b3_acz }, { .fn = c55plus_x_b3_tc1 }, { .fn = c55plus_x_b3_tc2 }, { .fn = c55plus_x_b3_acy } } }, + { .mask = 0xff0000c0, .match = 0xb30000c0, .id = TMS320C55_INS_SUBC, .len = 5, + .ops = { { .fn = c55plus_x_b3_mem }, { .fn = c55plus_x_b3_acx }, { .fn = c55plus_x_b3_acy } } }, + // mov [rnd]([uns](*Smem) << Tx), ACx (opcode 0xb4, byte3[6]==1 && byte3[0]==0, + // 5 bytes): register-shifted memory load. byte3[6] selects the load over the + // byte3[6]==0 store forms (which stay on the legacy decoder). Left unlifted, as + // in the legacy decoder. + { .mask = 0xff000041, .match = 0xb4000040, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_b4_mem }, { .fn = c55plus_x_b4_acdst } } }, + // mov [uns(][rnd(][hi(|lo(]ACx << Tx[)], [dbl(]*Smem[)] (opcode 0xb4, + // byte3[6]==0, 5 bytes): the register-shifted accumulator store -- source + // ACx (byte2[4:0]) shifted by byte4, destination Smem; byte3[0] selects the + // dbl 32-bit store (else a 16-bit hi/lo half store, byte3[1]). Left unlifted. + { .mask = 0xff000040, .match = 0xb4000000, .id = TMS320C55_INS_MOV, .len = 5, + .ops = { { .fn = c55plus_x_b4st_src }, { .fn = c55plus_x_b4st_mem } } }, + // add/sub *Smem << Tx, ACx, ACy (opcode 0xb6, 5 bytes): the register-shifted + // memory add/subtract -- byte4[6:0] is the shift register (rendered " << Tx"), + // byte3[4:0] the source accumulator, byte2[4:0] the destination, byte3[6] + // selecting add (0) / sub (1). Left unlifted. + { .mask = 0xff000040, .match = 0xb6000000, .id = TMS320C55_INS_ADD, .len = 5, + .ops = { { .fn = c55plus_x_b6_mem }, { .fn = c55plus_x_b6_acx }, { .fn = c55plus_x_b4_acdst } } }, + { .mask = 0xff000040, .match = 0xb6000040, .id = TMS320C55_INS_SUB, .len = 5, + .ops = { { .fn = c55plus_x_b6_mem }, { .fn = c55plus_x_b6_acx }, { .fn = c55plus_x_b4_acdst } } }, + // mpyk/mack #k8, ACsrc[, ACacc], ACdst (opcode 0xc7, 5 bytes): multiply (or + // multiply-accumulate) a source accumulator by an 8-bit constant. byte1[6:5] + // select the fractional (f) / rounding (r) variants via mods (round bit 30, + // fract bit 31 of the packed word); byte2[7] selects mack over mpyk. + { .mask = 0xff008000, .match = 0xc7000000, .id = TMS320C55_INS_MPYK, .lop = C55_LOP_MPYK, .len = 5, + .mods = 30u | (31u << 18), + .ops = { { .fn = c55plus_x_mpyk_imm8 }, { .fn = c55plus_x_mpyk_src }, { .fn = c55plus_x_mpyk_dst } } }, + { .mask = 0xff008000, .match = 0xc7008000, .id = TMS320C55_INS_MACK, .len = 5, + .mods = 30u | (31u << 18), + .ops = { { .fn = c55plus_x_mpyk_imm8 }, { .fn = c55plus_x_mpyk_src }, { .fn = c55plus_x_mpyk_acc }, { .fn = c55plus_x_mpyk_dst } } }, + // mpyk/mack #k16, ... (opcode 0xee, 6 bytes): the 16-bit-immediate counterpart + // of 0xc7. Same field layout shifted up one byte; round bit 38, fract bit 39. + { .mask = 0xff008000, .match = 0xee000000, .id = TMS320C55_INS_MPYK, .len = 6, + .mods = 38u | (39u << 18), + .ops = { { .fn = c55plus_x_mpyk_imm16 }, { .fn = c55plus_x_mpyk6_src }, { .fn = c55plus_x_mpyk6_dst } } }, + { .mask = 0xff008000, .match = 0xee008000, .id = TMS320C55_INS_MACK, .len = 6, + .mods = 38u | (39u << 18), + .ops = { { .fn = c55plus_x_mpyk_imm16 }, { .fn = c55plus_x_mpyk6_src }, { .fn = c55plus_x_mpyk6_acc }, { .fn = c55plus_x_mpyk6_dst } } }, + // A-unit immediate address arithmetic. amov/aadd/asub #k16, reg (opcode 0xae, + // 4 bytes): byte1[7:5] selects the operation and register type -- 000 asub ARn, + // 001 asub XARn, 010 aadd ARn, 011 aadd XARn, 1xx amov ARn -- byte1[4:0] (or + // [3:0] for XAR) the register, and byte2:byte3 the 16-bit constant. + { .mask = 0xffe00000, .match = 0xae000000, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 4, + .ops = { { .fn = c55plus_x_k16 }, { .fn = c55plus_x_areg16 } } }, + { .mask = 0xffe00000, .match = 0xae200000, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 4, + .ops = { { .fn = c55plus_x_k16 }, { .fn = c55plus_x_xar16 } } }, + { .mask = 0xffe00000, .match = 0xae400000, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 4, + .ops = { { .fn = c55plus_x_k16 }, { .fn = c55plus_x_areg16 } } }, + { .mask = 0xffe00000, .match = 0xae600000, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 4, + .ops = { { .fn = c55plus_x_k16 }, { .fn = c55plus_x_xar16 } } }, + { .mask = 0xff800000, .match = 0xae800000, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AMOV, .len = 4, + .ops = { { .fn = c55plus_x_k16 }, { .fn = c55plus_x_areg16 } } }, + // amov/asub #k24, XARn (opcode 0xd2, 5 bytes): byte1[7] selects amov/asub, + // byte1[3:0] the XAR register, byte2:byte3:byte4 the 24-bit constant. + { .mask = 0xff800000, .match = 0xd2800000, .id = TMS320C55_INS_AMOV, .lop = C55_LOP_AMOV, .len = 5, + .ops = { { .fn = c55plus_x_k24 }, { .fn = c55plus_x_xar_dst } } }, + { .mask = 0xff800000, .match = 0xd2000000, .id = TMS320C55_INS_ASUB, .lop = C55_LOP_AREG_SUB, .len = 5, + .ops = { { .fn = c55plus_x_k24 }, { .fn = c55plus_x_xar_dst } } }, + // aadd #k8, sp (opcode 0x0c, 2 bytes): add an 8-bit constant to the stack + // pointer. + { .mask = 0xff000000, .match = 0x0c000000, .id = TMS320C55_INS_AADD, .lop = C55_LOP_AREG_ADD, .len = 2, + .ops = { { .fn = c55plus_x_k8 }, { .fn = c55plus_x_sp } } }, +}; + +const C55ArchDesc c55plus_arch_desc = { + .arch = C55_ARCH_C55XPLUS, + .cpu_name = "c55x+", + .table = c55plus_table, + .table_len = sizeof(c55plus_table) / sizeof(c55plus_table[0]), + .insn_len = c55plus_insn_len, + .reg_info = c55plus_reg_info, + .mnemonic = c55plus_mnemonic, + .op_type = c55plus_op_type, + .lift = NULL, + .mem = { .addr_unit_log2 = 1, .ptr_width = 23, .big_endian = false }, + .ea = NULL, + .cond_exec_prefix = true, + .parallel_prefix = true, +}; diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_arch.h b/librz/arch/isa/tms320/c55x_plus/c55plus_arch.h new file mode 100644 index 0000000000..668aca8793 --- /dev/null +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_arch.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_TMS320_C55PLUS_ARCH_H +#define RZ_TMS320_C55PLUS_ARCH_H + +#include + +/// The TMS320C55x+ arch descriptor: drives the shared c55_ir engine/consumers. +extern const C55ArchDesc c55plus_arch_desc; + +#endif /* RZ_TMS320_C55PLUS_ARCH_H */ diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_decode.c b/librz/arch/isa/tms320/c55x_plus/c55plus_decode.c deleted file mode 100644 index 32b8ec88ed..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/c55plus_decode.c +++ /dev/null @@ -1,870 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include -#include -#include -#include - -#include "ins.h" -#include "decode.h" -#include "hashtable.h" -#include "decode_funcs.h" - -extern char *ins_str[]; -extern ut32 ins_buff_len; - -static ut32 get_q_bits(ut32 val, char *ins, ut32 ins_len, int *err_code) { - ut32 res = 0; - - if (!rz_str_ncasecmp(ins, "q_MMAP", 6)) { - res = val & 1; - } else if (!rz_str_ncasecmp(ins, "q_LOCK", 6)) { - res = val & 1; - } else if (!rz_str_ncasecmp(ins, "q_LINR", 6)) { - res = (val >> 2) & 1; - } else if (!rz_str_ncasecmp(ins, "q_CIRC", 6)) { - res = (val >> 3) & 1; - } else if (!rz_str_ncasecmp(ins, "q_PORT_READ", 11)) { - res = (val >> 4) & 1; - } else if (!rz_str_ncasecmp(ins, "q_PORT_WRITE", 12)) { - res = (val >> 5) & 1; - } else if (!rz_str_ncasecmp(ins, "q_XPORT_READ", 12)) { - res = (val >> 6) & 1; - } else if (!rz_str_ncasecmp(ins, "q_XPORT_WRITE", 13)) { - res = (val >> 7) & 1; - } else if (!rz_str_ncasecmp(ins, "q_SAT", 5)) { - res = (val >> 8) & 1; - } else if (!rz_str_ncasecmp(ins, "q_XC0", 5)) { - res = (val >> 9) & 1; - } else if (!rz_str_ncasecmp(ins, "q_XC1", 5)) { - res = (val >> 10) & 1; - } else { - /* INVALID CONDITION */ - fprintf(stderr, "Invalid token %s\n", ins); - *err_code = -1; - } - return res; -} - -/* - a2 = 0x223; - 0x800 = valor que se crea en sub_40BAE0<) con and 0xfffff800 -*/ -static ut32 get_ins_bits(ut32 hash_code, ut32 ins_pos, char *ins, - ut32 ins_len, ut32 magic_value, int *err_code) { - ut32 res = 0; - ut8 op_b; - ut32 len, x, i; - char *op_str, *aux; - - if (ins[0] == 'q') { - return get_q_bits(magic_value, ins, ins_len, err_code); - } - - op_str = ins_str[1 + hash_code * 4]; - // printf("OPSTR => %s %d\n", ins, ins_len); - - x = 0; - for (i = 0; i < ins_len; i++) { - aux = strchr(&op_str[x], ins[i]); - if (!aux) { - aux = strchr(op_str, ins[i]); - if (!aux) { - fprintf(stderr, "Invalid token %s\n", ins); - *err_code = -1; - return 0; - } - } - - len = (unsigned int)(aux - op_str); - // printf("INS_POS: %d POS: %d\n", ins_pos, len / 8); - op_b = get_ins_part(ins_pos + len / 8, 1); - // printf("OPP: %x\n", op_b); - - x = len + 1; - res = (res * 2) | ((op_b >> ((1023 - len) % 8)) & 1); - if (!op_str[x]) { - x = 0; - } - } - - return res; -} - -static bool check_arg(ut32 ins_bits, int *err_code) { - bool res = false; - - if ((ins_bits <= 31) | (ins_bits >= 128 && ins_bits < 160)) { - res = true; - } else if (ins_bits >= 32 && ins_bits <= 252) { - res = false; - } else { - fprintf(stderr, "Invalid arg: %u\n", ins_bits); - *err_code = -1; - } - - return res; -} - -static char *decode_regis(char *reg_arg, st32 hash_code, ut32 ins_bits, - ut32 *ret_ins_bits, int *err_code) { - char reg_type; - char *res; - - reg_type = *reg_arg; - res = NULL; - - // printf("REG_TYPE %d %d\n", reg_type, ins_bits); - - switch (reg_type) { - case 33: - res = get_reg_name_1((ins_bits >> 1) | - ((ins_bits & 1) << 6)); - break; - case 100: - if (rz_str_ncasecmp(reg_arg, "d(ALLx", 6)) { - fprintf(stderr, "invalid register! %s\n", reg_arg); - *err_code = -1; - return NULL; - } - res = (check_arg(ins_bits, err_code) != 0 && *err_code == 0) ? rz_str_dup("dbl(") : NULL; - if (*err_code < 0) { - return NULL; - } - break; - case 41: - if (rz_str_ncasecmp(reg_arg, ")ALLx", 5)) { - fprintf(stderr, "invalid register! %s\n", reg_arg); - *err_code = -1; - return NULL; - } - res = (check_arg(ins_bits, err_code) && *err_code == 0) ? rz_str_dup(")") : NULL; - if (*err_code < 0) { - return NULL; - } - break; - case 65: - if (!rz_str_ncasecmp(reg_arg, "ACLH", 4)) { - res = get_reg_name_1(ins_bits + 64); - } else if (!rz_str_ncasecmp(reg_arg, "ACxP", 4)) { - res = get_reg_name_1(ins_bits + 1); - } else if (!rz_str_ncasecmp(reg_arg, "ACx", 3) || - !rz_str_ncasecmp(reg_arg, "ADR", 3) || - !rz_str_ncasecmp(reg_arg, "ALL", 3) /* 430ADC */ - ) { - res = get_reg_name_1(ins_bits); - } - if (hash_code == 0xDF || hash_code == 0xE0) { - *ret_ins_bits = ins_bits; - } - break; - case 68: - res = get_reg_name_1(ins_bits + 32); - break; - case 77: - if (!rz_str_ncasecmp(reg_arg, "MA", 2) || !rz_str_ncasecmp(reg_arg, "MR", 2)) { - res = get_reg_name_1(ins_bits); - } else { - res = get_reg_name_2(ins_bits); - } - break; - case 83: - res = get_reg_name_1(ins_bits); - break; - case 82: - if (!rz_str_ncasecmp(reg_arg, "RA", 2) || !rz_str_ncasecmp(reg_arg, "RL", 2)) { - res = get_reg_name_1(ins_bits); - } else if (!rz_str_ncasecmp(reg_arg, "RLP", 3) || !rz_str_ncasecmp(reg_arg, "RxP", 3)) { - res = get_reg_name_1(ins_bits + 1); - } else if (!rz_str_ncasecmp(reg_arg, "RX", 2)) { - res = get_reg_name_1(ins_bits); - } else { - res = get_reg_name_2(ins_bits); - } - break; - case 84: - res = get_reg_name_1(ins_bits + 48); - break; - case 87: - if (!rz_str_ncasecmp(reg_arg, "WD", 2)) { - res = get_reg_name_2(ins_bits); - } else if (!rz_str_ncasecmp(reg_arg, "WA", 2)) { - res = get_reg_name_1(ins_bits); - } else { - res = NULL; - } - break; - case 88: - if (!rz_str_ncasecmp(reg_arg, "XR", 2)) { - res = get_reg_name_3(ins_bits); - } else if (!rz_str_ncasecmp(reg_arg, "XD", 2)) { - res = get_reg_name_2(ins_bits + 32); - } else { - res = NULL; - } - break; - default: - res = NULL; - break; - } - - return res; -} - -static char *decode_ins(st32 hash_code, ut32 ins_pos, ut32 ins_off, ut32 *ins_len_dec, - ut32 *reg_len_dec, ut32 *ret_ins_bits, ut32 magic_value, ut8 two_ins, int *err_code) { - ut32 ins_len; - char *ins, *pos; - char token_aux[80]; - ut32 i, len; - char *reg = NULL; - char *res_decode = NULL; - char *aux = NULL; - - // get instruction length - ins_len = get_ins_len(get_ins_part(ins_pos + ins_off, 1)); - // get pseudo instruction - ins = ins_str[1 + 2 + hash_code * 4]; - if (!ins /*|| ins_str[4 * hash_code] == 0*/) { - fprintf(stderr, "Invalid instruction hash %x\n", hash_code); - *err_code = -1; - return NULL; - } - *reg_len_dec = 0; - if (hash_code == 0x19C) { - ut32 tok_reg_len = 0; - res_decode = get_token_decoded(hash_code, "MMMMxxxxmm", 10, NULL, ret_ins_bits, - &tok_reg_len, magic_value, ins_pos + ins_off, ins_len, two_ins, err_code); - if (*err_code < 0) { - return NULL; - } - *reg_len_dec += tok_reg_len; - } - - pos = ins; - // instruction length - *ins_len_dec = ins_len; - - while (*pos) { - if (*pos == '`') { - pos++; - aux = strchr(pos, '`'); - if (!aux || pos == aux) { - fprintf(stderr, "Invalid instruction %s\n", ins); - free(res_decode); - *err_code = -1; - return NULL; - } - len = (ut32)(size_t)(aux - pos); - if (len >= 80) { - fprintf(stderr, "Invalid length token %d\n", len); - free(res_decode); - *err_code = -1; - return NULL; - } - - memcpy(token_aux, pos, len); - token_aux[len] = '\0'; - pos = aux; - - reg = NULL; - for (i = 0; i < len; i++) { - if (token_aux[i] == ',') { - len = (unsigned int)(size_t)(&token_aux[i] - token_aux); - reg = &token_aux[i + 1]; - - break; - } - } - - ut32 tok_reg_len = 0; - aux = get_token_decoded(hash_code, token_aux, len, reg, ret_ins_bits, - &tok_reg_len, magic_value, ins_pos + ins_off, ins_len, two_ins, err_code); - if (*err_code < 0) { - return NULL; - } - /* get_token_decoded resets its ret_reg_len output to 0 on every - * call, so a later token would otherwise clobber the extra-byte - * count reported by an earlier offset operand (e.g. the - * MMMMxxxxmm Smem token in COPY/MOV, which is followed by more - * register tokens). Accumulate instead. */ - *reg_len_dec += tok_reg_len; - res_decode = rz_str_append_owned(res_decode, aux); - } else { - token_aux[0] = *pos; - token_aux[1] = '\0'; - res_decode = rz_str_append(res_decode, token_aux); - } - pos++; - } - - return res_decode; -} - -static bool is_hash(st32 hash_code) { - bool ret; - - switch (hash_code) { - case 0xE8: - case 0xE9: - case 0xEA: - case 0xEC: - case 0x1A8: - case 0x1DC: - case 0x1E1: - case 0x1E2: - case 0x1E3: - case 0x1E4: - ret = 1; - break; - default: - ret = 0; - } - - return ret; -} - -void set_magic_value(ut32 *magic_value, st32 hash_code, int *err_code) { - switch (hash_code) { - case 232: - *magic_value |= 1; - break; - case 424: - *magic_value |= 2; - break; - case 236: - *magic_value |= 4; - break; - case 233: - *magic_value |= 0x10; - break; - case 234: - *magic_value |= 0x20; - break; - case 483: - *magic_value |= 0x40; - break; - case 484: - *magic_value |= 0x80; - break; - case 476: - *magic_value |= 0x100; - break; - case 481: - *magic_value |= 0x200; - break; - case 482: - *magic_value |= 0x400; - break; - default: - fprintf(stderr, "invalid hash code 0x%x for magic value 0x%x\n", hash_code, *magic_value); - *err_code = -1; - } -} - -static char *do_decode(ut32 ins_off, ut32 ins_pos, ut32 two_ins, ut32 *next_ins_pos, - st32 *ins_hash_code, int *err_code) { - st32 hash_code, hash_aux; - ut32 reg_len_dec, ins_len_dec, ret_ins_bits; - char *ins_res = NULL, *ins_aux = NULL; - ut32 magic_value = 0x800; - - *next_ins_pos = 0; - - reg_len_dec = 0; - ret_ins_bits = 0; - ins_len_dec = 0; - - hash_code = get_hash_code(ins_pos + ins_off); - if (is_hash(hash_code)) { - hash_aux = hash_code; - ins_off++; - set_magic_value(&magic_value, hash_code, err_code); - if (*err_code < 0) { - return NULL; - } - hash_code = get_hash_code(ins_pos + ins_off); - *next_ins_pos = 1; - } else { - hash_aux = 0x223; - } - - if (ins_hash_code != NULL) { - *ins_hash_code = hash_code; - } - - if (hash_aux == 0x1E1 || hash_aux == 0x1E2) { - ins_aux = decode_ins(hash_aux, ins_pos, ins_off, &ins_len_dec, ®_len_dec, - &ret_ins_bits, magic_value, two_ins, err_code); - if (*err_code < 0) { - return NULL; - } - ins_aux = rz_str_append(ins_aux, " "); - } - - if (hash_code == 0x223) { - char hex_buf[8]; - ins_res = rz_str_append(ins_aux, ".byte 0x"); - ins_res = rz_str_append(ins_res, rz_strf(hex_buf, "%02x", get_ins_part(ins_pos, 1) & 0xff)); - *next_ins_pos = *next_ins_pos + 1; - } else { - free(ins_aux); - ins_aux = decode_ins(hash_code, ins_pos, ins_off, &ins_len_dec, - ®_len_dec, &ret_ins_bits, magic_value, two_ins, err_code); - if (*err_code < 0) { - free(ins_aux); - return NULL; - } - ins_res = rz_str_append_owned(ins_aux, ins_res); - /* ins_len_dec is the base encoding length; reg_len_dec carries the - * extra bytes consumed by a k16/k24 offset operand (Smem modes - * *arN(#K16) / *arN(#K24) / *abs16 / *(#K24)). These must be added - * or the decoder under-reports the size and the following bytes are - * re-decoded as a spurious extra instruction. */ - *next_ins_pos += ins_len_dec + reg_len_dec; - } - - return ins_res; -} - -char *c55plus_decode(ut32 ins_pos, ut32 *next_ins_pos) { - ut8 opcode, two_ins = 0; - ut32 next_ins1_pos, next_ins2_pos; - st32 hash_code; - char *ins1, *ins2, *aux, *ins_res; - int err_code; - - if (ins_pos >= ins_buff_len) { - return NULL; - } - ins_res = NULL; - err_code = 0; - - opcode = get_ins_part(ins_pos, 1); - if ((opcode & 0xF0) == 0x30) { - two_ins = opcode & 0x0F; - if (two_ins < 4) { - two_ins += 0xF; - } - } else { - two_ins = 0; - } - - // two instruction execution? - if (two_ins) { - ins1 = do_decode(1, ins_pos, two_ins, &next_ins1_pos, &hash_code, &err_code); - if (err_code < 0) { - free(ins1); - return NULL; - } - ins2 = do_decode(next_ins1_pos + 1, ins_pos, two_ins, &next_ins2_pos, NULL, &err_code); - if (err_code < 0) { - free(ins1); - free(ins2); - return NULL; - } - *next_ins_pos = next_ins2_pos; - - if (hash_code == 0xF0 || hash_code == 0xF1) { - aux = rz_str_append(ins2, " || "); - ins_res = rz_str_append_owned(aux, ins1); - } else { - aux = rz_str_append(ins1, " || "); - ins_res = rz_str_append_owned(aux, ins2); - } - *next_ins_pos = next_ins1_pos + next_ins2_pos + 1; - if (*next_ins_pos != two_ins) { - // ins_res = strcat_dup(ins_res, " P-tag problem", 1); - err_code = -1; - free(ins_res); - return NULL; - } - } else { - ins_res = do_decode(0, ins_pos, two_ins, &next_ins1_pos, &hash_code, &err_code); - if (err_code < 0) { - free(ins_res); - return NULL; - } - *next_ins_pos = next_ins1_pos; - } - - return ins_res; -} - -static bool is_linear_circular(ut32 ins_bits) { - ut8 op, op2, op3; - op = (ins_bits >> 6) | 16 * (ins_bits & 3); - op2 = (ins_bits >> 2) & 0xF; - op3 = op2 & 0xF; - return (op == 26 || op == 30 || (op3 > 7 && op3 != 15)); -} - -static char *get_token_decoded(st32 hash_code, char *ins_token, ut32 ins_token_len, - char *reg_arg, ut32 *ret_ins_bits, ut32 *ret_reg_len, ut32 magic_value, - ut32 ins_pos, ut32 ins_len, ut8 two_ins, int *err_code) { - ut32 tok_op, ins_bits; - char *res = NULL; - char *aux = NULL; - ut32 ret_len = 0, flag; - - *ret_ins_bits = 0; - *ret_reg_len = 0; - - ins_bits = get_ins_bits(hash_code, ins_pos, ins_token, ins_token_len, magic_value, err_code); - if (*err_code < 0) { - return NULL; - } - tok_op = *ins_token - 0x23; - - switch (tok_op) { - case 30: - case 31: - case 32: - case 33: - case 43: - case 62: - case 63: - case 64: - case 65: - if (!reg_arg || *reg_arg == '\0') { - res = rz_str_dup(""); - goto ret_decode; - } - res = decode_regis(reg_arg, hash_code, ins_bits, ret_ins_bits, err_code); - if (*err_code < 0) { - return NULL; - } - break; - case 35: res = ins_bits ? rz_str_dup(" || far()") : NULL; break; - case 36: res = ins_bits ? rz_str_dup(" || local()") : NULL; break; - case 37: res = get_opers(ins_bits); break; - case 38: - res = ins_bits ? "lo" : "hi"; - res = rz_str_dup(res); - break; - case 39: res = get_cmp_op(ins_bits); break; - case 40: - case 48: - res = rz_str_newf("#0x%x", (ins_bits << (32 - ins_token_len) >> (32 - ins_token_len))); - break; - case 70: - case 72: - case 80: - if (reg_arg) { - if (*reg_arg == '!') { - res = get_reg_pair(ins_bits); - break; - } else if (!rz_str_ncasecmp(reg_arg, "ST", 2)) { - res = get_status_regs_and_bits(reg_arg, ins_bits); - break; - } - } - if (hash_code == 0xDF || hash_code == 0xE0) { - *ret_ins_bits = ins_bits; - } - if (!reg_arg || *reg_arg != '-') { - res = rz_str_newf("#0x%lx", (long unsigned int)ins_bits); - } else { - res = rz_str_newf("-#0x%lx", (long unsigned int)ins_bits); - } - if (!reg_arg || *reg_arg != 'm') { - break; - } - - res = rz_str_append(res, ")"); - res = rz_str_prepend(res, "*("); - - if (magic_value & 0xC0) { - res = rz_str_append(res, ")"); - res = rz_str_prepend(res, "volatile("); - } else if (magic_value & 0x30) { - res = rz_str_append(res, ")"); - res = rz_str_prepend(res, "port("); - } - break; - case 41: - case 73: - if (reg_arg && *reg_arg == 'L') { - ins_bits = ins_bits << (32 - ins_token_len) >> (32 - ins_token_len); - } - if (reg_arg && *reg_arg == 'i') { - res = rz_str_dup(""); - } else { - res = rz_str_newf("#0x%06lx", (long unsigned int)ins_bits); - } - break; - case 42: - flag = 0; - if (reg_arg && *reg_arg == '3') { - flag = ins_bits & 1; - ins_bits = ins_bits >> 1; - reg_arg++; - } - if (magic_value & 1) { - aux = get_sim_reg(reg_arg, ins_bits); - } else if (reg_arg) { - switch (*reg_arg) { - case 'b': - case 'd': - reg_arg++; - break; - case '!': - // strncpy(buff_aux, reg_arg + 1, 8); - reg_arg += 10; - // ins_bits2 = get_ins_bits(hash_code, ins_pos, buff_aux, 8); - break; - } - aux = get_AR_regs_class2(ins_bits, &ret_len, ins_len + ins_pos, 1); - } - if (magic_value & 1) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "mmap("); - } else if ((magic_value & 4) && is_linear_circular(ins_bits)) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "linear("); - } else if ((magic_value & 8) && is_linear_circular(ins_bits)) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "circular("); - } else if (magic_value & 2) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "lock("); - } else if (reg_arg) { - if (((magic_value & 0x10) && strchr(reg_arg, 'r')) || - ((magic_value & 0x20) && strchr(reg_arg, 'w'))) { - - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "port("); - } else if ( - ((magic_value & 0x40) && strchr(reg_arg, 'r')) || - ((magic_value & 0x80000000) && strchr(reg_arg, 'w'))) { - - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "volatile("); - } - } - - if (flag) { - res = rz_str_prepend(aux, "t3 = "); - } else { - res = aux; - *ret_reg_len = ret_len; - } - break; - case 79: - res = get_trans_reg(ins_bits); - if (!res) { - *err_code = -1; - } - break; - case 49: - if (reg_arg) { - if (*reg_arg == '1') { - res = get_tc2_tc1(ins_bits >> 1); - } else if (*reg_arg == '2') { - res = get_tc2_tc1(ins_bits & 1); - } - } else { - res = get_tc2_tc1(ins_bits); - } - if (!res) { - *err_code = -1; - return NULL; - } - break; - case 51: - /* V / VV template field -- Carry or TC2 (see decode_funcs.c - * get_tc2_or_carry comment). With a 'VV,1' suffix the high - * bit of the 2-bit field is consumed; with 'VV,2' the low bit. - * With no suffix the whole field is consumed. ROL/ROR use the - * paired (VV,1 ; VV,2) form. */ - if (reg_arg) { - if (*reg_arg == '1') { - res = get_tc2_or_carry(ins_bits >> 1); - } else if (*reg_arg == '2') { - res = get_tc2_or_carry(ins_bits & 1); - } - } else { - res = get_tc2_or_carry(ins_bits); - } - if (!res) { - *err_code = -1; - return NULL; - } - break; - case 52: - if (ins_bits == 0) { - break; - } - if (reg_arg) { - if (*reg_arg == 'H') { - res = "hi("; - } else if (*reg_arg == 'L') { - res = "lo("; - } else if (*reg_arg == 'd') { - res = "dbl("; - } else if (*reg_arg == ')') { - res = ")"; - } else { - res = ""; - } - } else { - res = ""; - } - res = rz_str_dup(res); - break; - case 53: - case 54: - case 55: - flag = 0; - if (reg_arg && *reg_arg == '3') { - flag = ins_bits & 1; - ins_bits = ins_bits >> 1; - reg_arg++; - } - aux = get_AR_regs_class1(ins_bits); - tok_op = ins_bits & 0xF; - if (magic_value & 4) { - if (tok_op <= 7 || tok_op == 0xF) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "linear("); - } - } else if (magic_value & 8) { - if (tok_op <= 7 || tok_op == 0xF) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "circular("); - } - } else if (magic_value & 2) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "lock("); - } else if (reg_arg) { - if ( - ((magic_value & 0x10) && *ins_token == 'X' && strchr(reg_arg, 'r')) || - ((magic_value & 0x20) && *ins_token == 'Y' && strchr(reg_arg, 'w'))) { - - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "port("); - } else if ( - ((magic_value & 0x40) && *ins_token == 'X' && strchr(reg_arg, 'r')) || - ((magic_value & 0x80000000) && *ins_token == 'Y' && strchr(reg_arg, 'w')) - - ) { - aux = rz_str_append(aux, ")"); - aux = rz_str_prepend(aux, "volatile("); - } - } - res = flag ? rz_str_prepend(aux, "t3 = ") : aux; - break; - case 0: - case 1: - if (!ins_bits) { - break; - } - if (!reg_arg) { - res = "U"; - } else { - if (*reg_arg == '(') { - res = "uns("; - } else if (*reg_arg == ')') { - res = ")"; - } else { - res = "<$/#>"; - } - } - res = rz_str_dup(res); - break; - case 2: - if (!ins_bits) { - break; - } - if (!reg_arg) { - res = "R"; - } else { - if (*reg_arg == '(') { - res = "rnd("; - } else if (*reg_arg == ')') { - res = ")"; - } else { - res = "<%>"; - } - } - res = rz_str_dup(res); - break; - case 12: - if (!ins_bits) { - break; - } - if (!reg_arg) { - res = "F"; - } else { - if (*reg_arg == '(') { - res = "frct("; - } else if (*reg_arg == ')') { - res = ")"; - } else if (*reg_arg == 'a') { - res = "<%>"; - } else { - res = ""; - } - } - res = rz_str_dup(res); - break; - case 29: - if (!ins_bits) { - break; - } - if (!reg_arg) { - res = "saturate"; - } else { - if (*reg_arg == '(') { - res = "saturate("; - } else if (*reg_arg == ')') { - res = ")"; - } else { - res = ""; - } - } - res = rz_str_dup(res); - break; - case 16: - res = (ins_bits != 0) ? rz_str_dup("t3 = ") : NULL; - break; - case 17: - if (!ins_bits) { - break; - } - if (!reg_arg) { - res = "40"; - } else { - if (*reg_arg == '(') { - res = "m40("; - } else if (*reg_arg == ')') { - res = ")"; - } else { - res = "<4>"; - } - } - res = rz_str_dup(res); - break; - case 78: - if (!rz_str_ncasecmp(ins_token, "q_SAT", 5)) { - res = ins_bits ? "s" : NULL; - } else if (!rz_str_ncasecmp(ins_token, "q_CIRC", 6)) { - res = ins_bits ? ".cr" : NULL; - } else if (!rz_str_ncasecmp(ins_token, "q_LINR", 6)) { - res = ins_bits ? ".lr" : NULL; - } else { - fprintf(stderr, "Invalid instruction %s\n!", ins_token); - *err_code = -1; - return NULL; - } - if (res != NULL) { - res = rz_str_dup(res); - } - break; - } - -ret_decode: - return res; -} diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_il.c b/librz/arch/isa/tms320/c55x_plus/c55plus_il.c deleted file mode 100644 index 55da76e30f..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/c55plus_il.c +++ /dev/null @@ -1,2873 +0,0 @@ -// SPDX-FileCopyrightText: 2026 RizinOrg -// SPDX-License-Identifier: LGPL-3.0-only - -/** - * \file c55plus_il.c - * - * RzIL lifting for the TMS320C55x+ (shared with plain C55x: same integer core - * and register model; C55x+ only adds the high accumulators AC16..AC31 in the - * operand encoding and the parallel-instruction wrapper, neither of which adds - * new IL op-semantics). - * - * Operand model. Rizin architecture lifters normally consume a *structured* - * operand list (the Capstone-style `cs_detail.operands[]`, or an arch-specific - * decoded-instruction struct). The TMS320C55x+ decoder in this tree is the - * th0rpe token engine, which produces only an algebraic-syntax *string* (e.g. - * "mov #0x5, t0") and no structured operands. To give the lifter the same - * structured interface the rest of rizin uses, this file defines a small - * instruction-information structure -- `C55Insn` with a typed `C55Operand[]` - * array -- and a normaliser, `c55_decode_insn()`, that fills it. The per- - * instruction lifters then switch on operand *types* (register / immediate / - * other) and read typed fields (resolved register name + IL width, immediate - * value), instead of each doing ad-hoc string slicing. - * - * Today `c55_decode_insn()` derives the typed operands from the decoder's - * algebraic output (the only operand information the th0rpe decoder exposes). - * The lower-level follow-on -- having the token decoder record these typed - * operands directly from the instruction bit-fields, so the intermediate - * string disappears entirely -- is the same operand-decoding work tracked in - * docs/rizin-analysis-gaps.md; the `C55Insn` interface here is exactly what it - * will populate, so the lifters do not change when it lands. - * - * Invariant: **correct-or-NULL**. Any form that cannot be lifted with certain - * semantics (memory operands, shifted operands, half-register .l/.h sub-fields, - * parallel slots, unbound high accumulators, saturating accumulator - * arithmetic) returns NULL -- no IL -- rather than approximate IL that would - * corrupt emulation. - */ - -#include -#include -#include - -#include "c55plus_analysis.h" - -#include - -// IL variable bindings. Names must match the tms320 C55x/C55x+ register -// profile (get_reg_profile). The integer-core register set the lifter can -// reference is bound here. -static const char *c55x_plus_il_regs[] = { - "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ac6", "ac7", - "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", - "xar0", "xar1", "xar2", "xar3", "xar4", "xar5", "xar6", "xar7", - "t0", "t1", "t2", "t3", - "sp", "ssp", "dp", "dph", "sph", "pdp", "pc", - "cdp", "cdph", "xcdp", "csr", "rptc", - "brc0", "brc1", "brs1", "trn0", "trn1", - "bk03", "bk47", "bkc", - "bsa01", "bsa23", "bsa45", "bsa67", "bsac", - "st0_55", "st1_55", "st2_55", "st3_55", - NULL -}; - -// Static register-name tables. The IL SET/VAR ops *borrow* the variable-name -// pointer, so it must have static lifetime -- never a pointer into the decoded -// syntax buffer (which is on the stack of the analysis op and freed by the -// time the IL is evaluated/printed). -static const char *const C55_AC[8] = { "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ac6", "ac7" }; -static const char *const C55_AR[8] = { "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7" }; -static const char *const C55_XAR[8] = { "xar0", "xar1", "xar2", "xar3", "xar4", "xar5", "xar6", "xar7" }; -static const char *const C55_T[4] = { "t0", "t1", "t2", "t3" }; - -/** - * If \p tok is exactly a simple bound register name (ac0-3, ar0-7, xar0-7, - * t0-3), return a pointer to its *static* canonical name and set \p width to - * its IL bit-width; otherwise return NULL. Rejects half-register sub-fields - * (".l"/".h"), memory syntax ("*", "("), and any non-alphanumeric token. - */ -static const char *c55_reg(const char *tok, ut32 *width) { - if (!tok || !*tok) { - return NULL; - } - // Control / system registers (varied lengths and underscores, so matched - // here before the simple-register length/charset checks below). The widths - // match the analysis register profile; the names are static-lifetime. - static const struct { - const char *name; - ut32 width; - } c55_ctrl[] = { - { "sp", 16 }, { "ssp", 16 }, { "dp", 16 }, { "dph", 7 }, { "sph", 7 }, - { "pdp", 9 }, { "cdp", 16 }, { "cdph", 7 }, { "csr", 16 }, { "rptc", 16 }, - { "brc0", 16 }, { "brc1", 16 }, { "brs1", 16 }, { "trn0", 16 }, { "trn1", 16 }, - { "bk03", 16 }, { "bk47", 16 }, { "bkc", 16 }, - { "bsa01", 16 }, { "bsa23", 16 }, { "bsa45", 16 }, { "bsa67", 16 }, { "bsac", 16 }, - { "st0_55", 16 }, { "st1_55", 16 }, { "st2_55", 16 }, { "st3_55", 16 } - }; - for (size_t ci = 0; ci < sizeof(c55_ctrl) / sizeof(c55_ctrl[0]); ci++) { - if (!strcmp(tok, c55_ctrl[ci].name)) { - *width = c55_ctrl[ci].width; - return c55_ctrl[ci].name; - } - } - size_t n = strlen(tok); - if (n < 2 || n > 4) { - return NULL; - } - for (size_t i = 0; i < n; i++) { - if (!isalnum((unsigned char)tok[i])) { - return NULL; // ".l", "*ar3", "(", etc. are not simple registers - } - } - char *end = NULL; - if (tok[0] == 'x' && tok[1] == 'a' && tok[2] == 'r') { - long idx = strtol(tok + 3, &end, 10); - if (end && *end == '\0' && idx >= 0 && idx <= 7) { - *width = 23; - return C55_XAR[idx]; - } - return NULL; - } - if (tok[0] == 'a' && tok[1] == 'c') { - long idx = strtol(tok + 2, &end, 10); - if (end && *end == '\0' && idx >= 0 && idx <= 7) { - *width = 40; - return C55_AC[idx]; - } - } else if (tok[0] == 'a' && tok[1] == 'r') { - long idx = strtol(tok + 2, &end, 10); - if (end && *end == '\0' && idx >= 0 && idx <= 7) { - *width = 16; - return C55_AR[idx]; - } - } else if (tok[0] == 't') { - long idx = strtol(tok + 1, &end, 10); - if (end && *end == '\0' && idx >= 0 && idx <= 3) { - *width = 16; - return C55_T[idx]; - } - } - return NULL; -} - -/** Parse "#0x..." / "#123" into an unsigned value. Returns false on any - * sign, shift, or trailing garbage (kept conservative). */ -static bool c55_parse_imm(const char *tok, ut64 *out) { - if (!tok) { - return false; - } - const char *p = tok; - if (*p == '#') { - p++; // C55x+ prefixes immediates with '#'; plain C55x often omits it - } - if (*p == '-' || *p == '+' || *p == '\0') { - return false; // signed immediates not modelled yet - } - size_t n = strlen(p); - char *end = NULL; - errno = 0; - // TI 'h' hex suffix (e.g. "4Dh", "#0h"): parse the digits as base 16 - if (n >= 2 && (p[n - 1] == 'h' || p[n - 1] == 'H')) { - char buf[40]; - if (n - 1 >= sizeof(buf)) { - return false; - } - memcpy(buf, p, n - 1); - buf[n - 1] = '\0'; - ut64 v = strtoull(buf, &end, 16); - if (errno || !end || *end != '\0') { - return false; - } - *out = v; - return true; - } - // "0x..." hex, "0..." octal, or decimal (strtoull base 0 auto-detects) - ut64 v = strtoull(p, &end, 0); - if (errno || !end || *end != '\0') { - return false; // e.g. "#0x1 << #0xe" leaves " << ..." -> reject - } - *out = v; - return true; -} - -// ---- Instruction-information structure (the structured operand interface) -- - -typedef enum { - C55_OP_NONE = 0, - C55_OP_REG, // a simple bound register: .reg (static name) + .width - C55_OP_REGHALF, // low/high 16-bit half of an accumulator: .reg = ACx, .half - C55_OP_IMM, // an unsigned immediate: .imm + .width (width = 0 if unsized) - C55_OP_MEM, // a single data-memory operand: *arN / *arN+ / *arN- (.reg = XAR base) - C55_OP_OTHER // shifted, half-register dst, condition, indexed/dbl/uns memory, etc. -} C55OpKind; - -// Data-memory addressing mode for a C55_OP_MEM operand. The effective-address -// and pointer side effect of each represented mode are explicitly known; any -// other memory syntax (register-indexed *arN(tM), *(arN+tM), mmap, pre-modify, -// circular) is classified C55_OP_OTHER so lifting falls through to NULL. -// -// Pointer-unit convention (word-pointer mode, matching the rest of this lifter): -// a register base (XARn / SP) holds a 23-bit word address, so its byte address -// is (reg << 1). The K16 displacement is added in word units before the shift -// ((base + K16) << 1, keeping word/dbl accesses aligned); the k24 absolute -// constant is already a 24-bit byte address. -typedef enum { - C55_AM_INDIRECT, // *arN : EA = XARn<<1, no side effect - C55_AM_POSTINC, // *arN+ : EA = XARn<<1, then XARn += 1 word - C55_AM_POSTDEC, // *arN- : EA = XARn<<1, then XARn -= 1 word - C55_AM_INDEXED, // *arN(short(#K)) / *sp(#K) : EA = (base + sx(K))<<1 - C55_AM_ABSOLUTE, // *(#K) : EA = K (24-bit byte address) - C55_AM_IDXOFF, // *arN(tM) : EA = (XARn + sx(Tm))<<1, no side effect - C55_AM_POSTADD, // *(arN+tM) : EA = XARn<<1, then XARn += sx(Tm) - C55_AM_POSTSUB // *(arN-tM) : EA = XARn<<1, then XARn -= sx(Tm) -} C55AddrMode; - -typedef struct { - C55OpKind kind; - const char *reg; // REG: static reg name. MEM: static base-register name (XARn/sp/xcdp), NULL if absolute. - const char *index; // MEM register-indexed modes: static index-register name (Tm), else NULL - ut32 width; // IL bit-width of the register / immediate - ut64 imm; // immediate value when kind == C55_OP_IMM - C55AddrMode amode; // addressing mode when kind == C55_OP_MEM - int half; // C55_OP_REGHALF: 1 = .l (bits 15:0), 2 = .h (bits 31:16) - int access_bits; // MEM access width in bits: 8 (byte), 16 (word, default), 32 (dbl) - bool mem_uns; // MEM: uns() wrapper -> zero-extend on load (default sign-extend) - st64 disp; // MEM C55_AM_INDEXED: signed byte displacement (K16, sign-extended) - ut64 abs_addr; // MEM C55_AM_ABSOLUTE: 24-bit byte address (k24) -} C55Operand; - -#define C55_MAX_OPS 4 - -typedef struct { - char mnem[16]; // lowercased mnemonic ("mov", "add", ...) - C55Operand ops[C55_MAX_OPS]; - int n_ops; - bool parallel; // instruction has a "||" parallel slot (not modelled) - bool truncated; // operand list overflowed C55_MAX_OPS (be conservative) -} C55Insn; - -/** Map an "arN" token (N=0..7) to its static XAR base-register name, or NULL. - * In word-pointer mode, data addressing uses the full 23-bit XARn (ARn is its - * low 16 bits), so "*arN" addresses through XARn. */ -static const char *c55_xar_for_ar(const char *tok) { - if (tok && tok[0] == 'a' && tok[1] == 'r' && tok[2] >= '0' && tok[2] <= '7' && tok[3] == '\0') { - return C55_XAR[tok[2] - '0']; - } - return NULL; -} - -/** Parse a single data-memory operand "*arN" / "*arN+" / "*arN-" into \p op - * (kind C55_OP_MEM, .reg = static XAR base, .amode set). Returns false for any - * other memory syntax (indexed *arN(tM), *(arN+tM), *sp(#k), dbl()/uns() - * wrappers, mmap, pre-modify, ar8..15 which are unbound) so the caller - * classifies it C55_OP_OTHER. */ -static bool c55_parse_mem(const char *tok, C55Operand *op) { - if (!tok) { - return false; - } - // Peel uns()/byte()/dbl() access-size and extension wrappers. They may - // nest (e.g. "uns(byte(...))"); each requires a matching trailing ')'. - char inner[80]; - size_t n = strlen(tok); - if (n == 0 || n >= sizeof(inner)) { - return false; - } - memcpy(inner, tok, n + 1); - int access = 16; - bool uns = false; - for (;;) { - size_t L = strlen(inner); - const char *body = NULL; - int acc = 0; - bool is_uns = false; - if (!strncmp(inner, "uns(", 4)) { - body = inner + 4; - is_uns = true; - } else if (!strncmp(inner, "byte(", 5)) { - body = inner + 5; - acc = 8; - } else if (!strncmp(inner, "dbl(", 4)) { - body = inner + 4; - acc = 32; - } else { - break; - } - if (L == 0 || inner[L - 1] != ')') { - return false; - } - if (is_uns) { - uns = true; - } else { - access = acc; - } - size_t off = (size_t)(body - inner); - size_t newlen = L - off - 1; // also drop the trailing ')' - memmove(inner, body, newlen); - inner[newlen] = '\0'; - } - if (inner[0] != '*') { - return false; - } - const char *p = inner + 1; - - // "*(...)" : absolute *(#k24), or register-indexed *(base +/- Tm) - if (p[0] == '(') { - const char *q = p + 1; - size_t ql = strlen(q); - if (ql < 2 || q[ql - 1] != ')') { - return false; - } - char body[64]; - size_t bl = ql - 1; - if (bl >= sizeof(body)) { - return false; - } - memcpy(body, q, bl); - body[bl] = '\0'; - // register-indexed post-modify: " + " / " - " - char *plus = strchr(body, '+'); - char *minus = strchr(body, '-'); - char *opc = plus ? plus : minus; - if (opc) { - char bb[32], ix[16]; - size_t blen = (size_t)(opc - body); - while (blen > 0 && body[blen - 1] == ' ') { - blen--; - } - const char *ixs = opc + 1; - while (*ixs == ' ') { - ixs++; - } - if (blen == 0 || blen >= sizeof(bb) || strlen(ixs) >= sizeof(ix)) { - return false; - } - memcpy(bb, body, blen); - bb[blen] = '\0'; - rz_str_ncpy(ix, ixs, sizeof(ix)); - const char *b2 = c55_xar_for_ar(bb); - if (!b2 && !strcmp(bb, "cdp")) { - b2 = "xcdp"; - } - ut32 iw = 0; - const char *idx = c55_reg(ix, &iw); - if (!b2 || !idx || iw != 16 || idx[0] != 't') { - return false; // e.g. bit-reversed "t0b" -> unmodelled - } - op->kind = C55_OP_MEM; - op->reg = b2; - op->index = idx; - op->amode = plus ? C55_AM_POSTADD : C55_AM_POSTSUB; - op->access_bits = access; - op->mem_uns = uns; - op->width = 16; - return true; - } - // absolute *(#k24) - ut64 k = 0; - if (!c55_parse_imm(body, &k)) { - return false; - } - op->kind = C55_OP_MEM; - op->reg = NULL; - op->amode = C55_AM_ABSOLUTE; - op->abs_addr = k & 0xffffffULL; - op->access_bits = access; - op->mem_uns = uns; - op->width = 16; - return true; - } - - // base register: arN (-> XARn), sp, or cdp (-> xcdp) - const char *base = NULL; - const char *rest = NULL; - if (p[0] == 'a' && p[1] == 'r' && p[2] >= '0' && p[2] <= '9') { - char reg[4] = { 'a', 'r', p[2], '\0' }; - base = c55_xar_for_ar(reg); - rest = p + 3; - } else if (p[0] == 's' && p[1] == 'p') { - base = "sp"; - rest = p + 2; - } else if (!strncmp(p, "cdp", 3)) { - base = "xcdp"; - rest = p + 3; - } - if (!base) { - return false; - } - - if (rest[0] == '\0') { - op->amode = C55_AM_INDIRECT; - } else if (rest[0] == '+' && rest[1] == '\0') { - op->amode = C55_AM_POSTINC; - } else if (rest[0] == '-' && rest[1] == '\0') { - op->amode = C55_AM_POSTDEC; - } else if (rest[0] == '(') { - // indexed: (short(#K16)) / (#K16) [constant], or (tM) [register index] - const char *q = rest + 1; - size_t ql = strlen(q); - if (ql < 2 || q[ql - 1] != ')') { - return false; - } - char dispbuf[48]; - size_t dl = ql - 1; - if (dl >= sizeof(dispbuf)) { - return false; - } - memcpy(dispbuf, q, dl); - dispbuf[dl] = '\0'; - // register index *base(tM) -> IDXOFF (EA = (base + sx(Tm))<<1, no modify) - ut32 iw = 0; - const char *idx = c55_reg(dispbuf, &iw); - if (idx && iw == 16 && idx[0] == 't') { - op->amode = C55_AM_IDXOFF; - op->index = idx; - } else { - char *d = dispbuf; - if (!strncmp(d, "short(", 6)) { - size_t L = strlen(d); - if (L == 0 || d[L - 1] != ')') { - return false; - } - d[L - 1] = '\0'; - d += 6; - } - ut64 k = 0; - if (!c55_parse_imm(d, &k)) { - return false; // "t0<<#1" (scaled index), etc. -> unmodelled - } - // K16 is a signed 16-bit displacement, sign-extended into the byte space - op->amode = C55_AM_INDEXED; - op->disp = (k & 0x8000ULL) ? (st64)(k | 0xffffffffffff0000ULL) : (st64)(k & 0xffffULL); - } - } else { - return false; // "*arN+ << t2", pre-modify "*+arN", etc. - } - op->kind = C55_OP_MEM; - op->reg = base; - op->access_bits = access; - op->mem_uns = uns; - op->width = 16; - return true; -} - -/** Parse an accumulator half-register source "acN.l" / "acN.h" (N=0..3) into - * \p op (kind C55_OP_REGHALF, .reg = static ACx name, .half = 1/2/3). Returns - * false otherwise. Recognises .l (bits 15:0), .h (bits 31:16) and .g (guard - * bits 39:32). */ -static bool c55_parse_half(const char *tok, C55Operand *op) { - if (!(tok && tok[0] == 'a' && tok[1] == 'c' && tok[2] >= '0' && tok[2] <= '7' && - tok[3] == '.' && tok[5] == '\0')) { - return false; - } - int half; - if (tok[4] == 'l') { - half = 1; - } else if (tok[4] == 'h') { - half = 2; - } else if (tok[4] == 'g') { - half = 3; - } else { - return false; - } - op->kind = C55_OP_REGHALF; - op->reg = C55_AC[tok[2] - '0']; - op->width = 16; - op->half = half; - return true; -} - -/** - * Normalise a decoded C55x+ syntax string into the structured `C55Insn`. - * Splits the mnemonic and the comma-separated operands and classifies each - * operand into a register / immediate / other. Returns false if there is no - * mnemonic. (Operand classification is exact for the register and immediate - * forms; everything else -- memory, shifts, conditions, half-registers -- is - * deliberately classified C55_OP_OTHER so the lifters fall through to NULL.) - */ -static bool c55_decode_insn(const char *syntax, C55Insn *insn) { - if (!syntax || !*syntax) { - return false; - } - memset(insn, 0, sizeof(*insn)); - // A parallel instruction packs two operations around "||"; we do not model - // the slot interaction, so flag it and only look at the first half. - const char *par = strstr(syntax, "||"); - size_t scan_len = par ? (size_t)(par - syntax) : strlen(syntax); - if (par) { - insn->parallel = true; - } - const char *sp = memchr(syntax, ' ', scan_len); - size_t mlen = sp ? (size_t)(sp - syntax) : scan_len; - if (mlen == 0 || mlen >= sizeof(insn->mnem)) { - return false; - } - memcpy(insn->mnem, syntax, mlen); - insn->mnem[mlen] = '\0'; - if (!sp) { - return true; // operandless mnemonic (e.g. "ret", "nop") - } - // Walk the comma-separated operand list within the first (pre-"||") half. - const char *p = sp + 1; - const char *end = syntax + scan_len; - while (p < end) { - // skip leading spaces - while (p < end && *p == ' ') { - p++; - } - if (p >= end) { - break; - } - const char *comma = memchr(p, ',', end - p); - const char *tok_end = comma ? comma : end; - // right-trim spaces - const char *te = tok_end; - while (te > p && te[-1] == ' ') { - te--; - } - size_t toklen = te - p; - if (insn->n_ops >= C55_MAX_OPS) { - insn->truncated = true; - break; - } - C55Operand *op = &insn->ops[insn->n_ops++]; - char buf[64]; - if (toklen > 0 && toklen < sizeof(buf)) { - memcpy(buf, p, toklen); - buf[toklen] = '\0'; - ut64 imm = 0; - ut32 w = 0; - const char *rn = c55_reg(buf, &w); - if (rn) { - op->kind = C55_OP_REG; - op->reg = rn; - op->width = w; - } else if (c55_parse_imm(buf, &imm)) { - op->kind = C55_OP_IMM; - op->imm = imm; - } else if (c55_parse_mem(buf, op)) { - // op filled by c55_parse_mem (kind C55_OP_MEM) - } else if (c55_parse_half(buf, op)) { - // op filled by c55_parse_half (kind C55_OP_REGHALF) - } else { - op->kind = C55_OP_OTHER; - } - } else { - op->kind = C55_OP_OTHER; - } - p = comma ? comma + 1 : end; - } - return true; -} - -// ---- Typed-operand lifters -------------------------------------------------- - -// Effective *byte* address of a C55_OP_MEM operand. Word-pointer unit -// convention (matching the rest of this lifter): a register base (XARn / SP) is -// a 23-bit *word* address, and the K16 displacement is added in word units, so -// the byte address is (base + K16) << 1 -- which keeps word/dbl accesses -// aligned. The k24 absolute constant is already a 24-bit byte address. Suitable -// for LOADW/STOREW. -// -// Caveat: if this firmware actually runs the A-unit in C55x+ byte-pointer mode -// (plausible given the byte() access density and odd k24 byte addresses), the -// register base is itself a byte address and these register-relative EAs are -// scaled by an extra 2; that correction is localised to this one helper. -static RzILOpBitVector *c55_mem_byte_addr(const C55Operand *m) { - if (m->amode == C55_AM_ABSOLUTE) { - return UN(24, m->abs_addr); - } - if (m->amode == C55_AM_INDEXED) { - // (word base + signed word displacement) << 1 - return MUL(ADD(UNSIGNED(24, VARG(m->reg)), SN(24, m->disp)), UN(24, 2)); - } - if (m->amode == C55_AM_IDXOFF) { - // (word base + signed word index Tm) << 1, base unchanged - return MUL(ADD(UNSIGNED(24, VARG(m->reg)), SIGNED(24, VARG(m->index))), UN(24, 2)); - } - return MUL(UNSIGNED(24, VARG(m->reg)), UN(24, 2)); -} - -// The pointer side effect of a post-modify mode (NULL otherwise): XARn +/- 1 word. -static RzAnalysisLiftedILOp c55_mem_post_effect(const C55Operand *m) { - switch (m->amode) { - case C55_AM_POSTINC: return SETG(m->reg, ADD(VARG(m->reg), UN(23, 1))); - case C55_AM_POSTDEC: return SETG(m->reg, SUB(VARG(m->reg), UN(23, 1))); - case C55_AM_POSTADD: return SETG(m->reg, ADD(VARG(m->reg), SIGNED(23, VARG(m->index)))); - case C55_AM_POSTSUB: return SETG(m->reg, SUB(VARG(m->reg), SIGNED(23, VARG(m->index)))); - default: return NULL; - } -} - -// Resize a bitvector from \p src_bits to \p dst_bits: low-bit truncation when -// narrowing (exact), and sign- or zero-extension (per \p uns) when widening. -static RzILOpBitVector *c55_resize(RzILOpBitVector *v, int src_bits, ut32 dst_bits, bool uns) { - if (dst_bits == (ut32)src_bits) { - return v; - } - if (dst_bits < (ut32)src_bits) { - return CAST(dst_bits, IL_FALSE, v); - } - return uns ? UNSIGNED(dst_bits, v) : SIGNED(dst_bits, v); -} - -/** The 16-bit value of a 16-bit source operand: a 16-bit register (T/AR), a - * full accumulator truncated to its low word, or an accumulator half .l/.h. - * Returns NULL for anything else. */ -static RzILOpBitVector *c55_src16(const C55Operand *op) { - if (op->kind == C55_OP_REG) { - if (op->width == 16) { - return VARG(op->reg); - } - if (op->width == 40) { - return CAST(16, IL_FALSE, VARG(op->reg)); // low word of AC - } - return NULL; - } - if (op->kind == C55_OP_REGHALF) { - if (op->half == 1) { - return CAST(16, IL_FALSE, VARG(op->reg)); // .l = bits 15:0 - } - if (op->half == 2) { - return CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(op->reg), UN(8, 16))); // .h = bits 31:16 - } - if (op->half == 3) { - return CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(op->reg), UN(8, 32))); // .g = guard bits 39:32 - } - } - return NULL; -} - -/** The 16-bit value of a read16-able operand: a 16-bit register, an accumulator - * (low word) or half via c55_src16, or an immediate truncated to 16 bits. - * Returns NULL otherwise. */ -static RzILOpBitVector *c55_read16(const C55Operand *op) { - if (op->kind == C55_OP_IMM) { - return UN(16, op->imm & 0xffff); - } - return c55_src16(op); -} - -/** Write a 16-bit value \p val16 (consumed) to a 16-bit destination operand: a - * 16-bit register (T/AR) via SETG, or an accumulator half .l/.h via read-modify- - * write of the 40-bit accumulator -- clearing the target half and OR-ing the new - * value in, leaving the other half and the guard bits untouched. Returns NULL - * (after freeing \p val16) for any other destination kind. */ -static RzAnalysisLiftedILOp c55_write16(const C55Operand *dst, RzILOpBitVector *val16) { - if (dst->kind == C55_OP_REG && dst->width == 16) { - return SETG(dst->reg, val16); - } - if (dst->kind == C55_OP_REGHALF) { - RzILOpBitVector *wide = UNSIGNED(40, val16); - if (dst->half == 1) { - // .l : clear bits 15:0, OR in the value - return SETG(dst->reg, LOGOR(LOGAND(VARG(dst->reg), UN(40, 0xffffff0000ULL)), wide)); - } - if (dst->half == 3) { - // .g : clear guard bits 39:32, OR in (value & 0xff) << 32 - return SETG(dst->reg, - LOGOR(LOGAND(VARG(dst->reg), UN(40, 0x00ffffffffULL)), - SHIFTL(IL_FALSE, LOGAND(wide, UN(40, 0xff)), UN(6, 32)))); - } - // .h : clear bits 31:16, OR in (value << 16) - return SETG(dst->reg, - LOGOR(LOGAND(VARG(dst->reg), UN(40, 0xff0000ffffULL)), - SHIFTL(IL_FALSE, wide, UN(6, 16)))); - } - rz_il_op_pure_free(val16); - return NULL; -} - -/** - * mov / copy: immediate load, register copy (equal-width, cross-width narrowing, - * and sign-extending widen from a 16-bit data register), and data-memory load / - * store. Memory access size is 8 (byte()), 16 (word, default) or 32 (dbl()), at - * the byte effective address from c55_mem_byte_addr (word-pointer mode, little- - * endian). Loads extend to the destination width with sign extension (SXMD = 1, - * the reset default) unless an uns() wrapper forces zero extension; stores write - * the low \p access_bits of the source (16-bit register, AC low word, AC half, - * or immediate). Post-increment/decrement apply the XARn +/- 1-word side effect - * after the access, sequenced via SEQ. Forms outside this set return NULL. - */ -static RzAnalysisLiftedILOp c55_lift_mov(const C55Insn *in) { - if (in->n_ops != 2) { - return NULL; - } - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - - // ---- destination: full register ---- - if (dst->kind == C55_OP_REG) { - if (src->kind == C55_OP_IMM) { - return SETG(dst->reg, UN(dst->width, src->imm)); - } - if (src->kind == C55_OP_REG) { - if (src->width == dst->width) { - return SETG(dst->reg, VARG(src->reg)); - } - if (dst->width < src->width) { - // narrowing: exact low-bit truncation (e.g. ac -> xar, ac -> t) - return SETG(dst->reg, CAST(dst->width, IL_FALSE, VARG(src->reg))); - } - if (src->width == 16) { - // widen a 16-bit data register with sign extension (SXMD = 1) - return SETG(dst->reg, SIGNED(dst->width, VARG(src->reg))); - } - if (src->width == 23) { - // widen a 23-bit address register: it holds an unsigned data - // address, so zero-extend (e.g. "mov xar0, ac2"). - return SETG(dst->reg, UNSIGNED(dst->width, VARG(src->reg))); - } - return NULL; // widening from another register class: extension unclear - } - if (src->kind == C55_OP_MEM) { - int ab = src->access_bits ? src->access_bits : 16; - RzILOpBitVector *load = LOADW(ab, c55_mem_byte_addr(src)); - RzILOpBitVector *val = c55_resize(load, ab, dst->width, src->mem_uns); - RzAnalysisLiftedILOp set = SETG(dst->reg, val); - RzAnalysisLiftedILOp post = c55_mem_post_effect(src); - return post ? SEQ2(set, post) : set; - } - if (src->kind == C55_OP_REGHALF) { - // half-register source into a register: a 16-bit value sign-extended - // to the destination width (SXMD = 1, reset default), matching the - // 16-bit-register widening case above. - RzILOpBitVector *v16 = c55_src16(src); - if (!v16) { - return NULL; - } - return (dst->width == 16) - ? SETG(dst->reg, v16) - : SETG(dst->reg, SIGNED(dst->width, v16)); - } - return NULL; - } - - // ---- destination: accumulator half (.l / .h) ---- - if (dst->kind == C55_OP_REGHALF) { - RzILOpBitVector *val16 = NULL; - RzAnalysisLiftedILOp post = NULL; - if (src->kind == C55_OP_IMM) { - val16 = UN(16, src->imm & 0xffff); - } else if (src->kind == C55_OP_MEM) { - int ab = src->access_bits ? src->access_bits : 16; - RzILOpBitVector *load = LOADW(ab, c55_mem_byte_addr(src)); - val16 = c55_resize(load, ab, 16, src->mem_uns); - post = c55_mem_post_effect(src); - } else { - val16 = c55_src16(src); - } - if (!val16) { - return NULL; - } - RzILOpBitVector *wide = UNSIGNED(40, val16); - RzAnalysisLiftedILOp set; - if (dst->half == 1) { - // .l : clear bits 15:0, OR in the value - set = SETG(dst->reg, LOGOR(LOGAND(VARG(dst->reg), UN(40, 0xffffff0000ULL)), wide)); - } else { - // .h : clear bits 31:16, OR in (value << 16) - set = SETG(dst->reg, - LOGOR(LOGAND(VARG(dst->reg), UN(40, 0xff0000ffffULL)), - SHIFTL(IL_FALSE, wide, UN(6, 16)))); - } - return post ? SEQ2(set, post) : set; - } - - // ---- memory-to-memory move (load src, store dst, equal access width) ---- - if (dst->kind == C55_OP_MEM && src->kind == C55_OP_MEM) { - int sab = src->access_bits ? src->access_bits : 16; - int dab = dst->access_bits ? dst->access_bits : 16; - if (sab != dab) { - return NULL; - } - RzILOpBitVector *load = LOADW(sab, c55_mem_byte_addr(src)); - RzAnalysisLiftedILOp eff = STOREW(c55_mem_byte_addr(dst), load); - RzAnalysisLiftedILOp sp = c55_mem_post_effect(src); - RzAnalysisLiftedILOp dp = c55_mem_post_effect(dst); - if (sp) { - eff = SEQ2(eff, sp); - } - if (dp) { - eff = SEQ2(eff, dp); - } - return eff; - } - - // ---- destination: memory (store) ---- - if (dst->kind == C55_OP_MEM) { - int ab = dst->access_bits ? dst->access_bits : 16; - RzILOpBitVector *sval = NULL; - if (ab == 32) { - if (src->kind == C55_OP_IMM) { - sval = UN(32, src->imm & 0xffffffffULL); - } else if (src->kind == C55_OP_REG) { - sval = c55_resize(VARG(src->reg), src->width, 32, true); - } else if (src->kind == C55_OP_REGHALF) { - RzILOpBitVector *h = c55_src16(src); - if (h) { - sval = UNSIGNED(32, h); - } - } - } else { - RzILOpBitVector *v16 = (src->kind == C55_OP_IMM) - ? UN(16, src->imm & 0xffff) - : c55_src16(src); - if (v16) { - sval = (ab == 8) ? CAST(8, IL_FALSE, v16) : v16; - } - } - if (!sval) { - return NULL; - } - RzAnalysisLiftedILOp st = STOREW(c55_mem_byte_addr(dst), sval); - RzAnalysisLiftedILOp post = c55_mem_post_effect(dst); - return post ? SEQ2(st, post) : st; - } - return NULL; -} - -/** - * Bitwise and/or/xor: " src, dst" -> dst := dst src, restricted to two - * equal-width registers. Bitwise ops take no extension, carry no value- - * affecting status side effects, and do not saturate, so the lift is exact. - * The 3-operand same-register form ("xor #imm, ARx, ARx") is also lifted when - * both register operands are the same 16-bit register. - */ -typedef enum { - C55_AND, - C55_OR, - C55_XOR -} C55BitOp; - -static RzILOpBitVector *c55_apply_bitop(C55BitOp kind, RzILOpBitVector *a, RzILOpBitVector *b) { - switch (kind) { - case C55_AND: return LOGAND(a, b); - case C55_OR: return LOGOR(a, b); - case C55_XOR: return LOGXOR(a, b); - } - return NULL; -} - -static RzAnalysisLiftedILOp c55_lift_bitop(C55BitOp kind, const C55Insn *in) { - if (in->n_ops == 2) { - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (src->kind == C55_OP_REG && dst->kind == C55_OP_REG && src->width == dst->width) { - return SETG(dst->reg, c55_apply_bitop(kind, VARG(dst->reg), VARG(src->reg))); - } - // 16-bit form: " SRC, DSThalf" / " SRC, REG16" -> DST := DST - // SRC at 16 bits, where DST is an accumulator half or a 16-bit register - // and SRC is a 16-bit value (immediate, 16-bit register, AC low word or - // AC half). Bitwise ops take no extension/saturation, and the half write - // preserves the rest of the accumulator, so the 16-bit lift is exact. - if (dst->kind == C55_OP_REGHALF || (dst->kind == C55_OP_REG && dst->width == 16)) { - RzILOpBitVector *sval = c55_read16(src); - if (!sval) { - return NULL; - } - RzILOpBitVector *dval = c55_read16(dst); - if (!dval) { - rz_il_op_pure_free(sval); - return NULL; - } - return c55_write16(dst, c55_apply_bitop(kind, dval, sval)); - } - // 16-bit source into a 40-bit accumulator, e.g. "xor t1, ac0". The - // source is zero-extended to 40 bits: XOR/OR then leave the upper 24 - // bits (high + guard) unchanged (op with 0 is identity), while AND - // clears them (AND with a zero-extended mask). Modelled exactly per - // that per-op upper-bit behaviour. - if (dst->kind == C55_OP_REG && dst->width == 40) { - RzILOpBitVector *sval = c55_read16(src); - if (!sval) { - return NULL; - } - RzILOpBitVector *res = c55_apply_bitop(kind, CAST(16, IL_FALSE, VARG(dst->reg)), sval); - if (kind == C55_AND) { - return SETG(dst->reg, UNSIGNED(40, res)); - } - return SETG(dst->reg, - LOGOR(LOGAND(VARG(dst->reg), UN(40, 0xffffff0000ULL)), UNSIGNED(40, res))); - } - return NULL; - } - if (in->n_ops == 3) { - // " A, S2, DST" -> DST := A S2. A is an immediate, a memory - // word (sized to DST), or a register; DST need not equal S2 (e.g. - // "xor *ar3, ar1, ar2"). Bitwise, so exact at the bound width with no - // saturation/extension side effects. - const C55Operand *a = &in->ops[0]; - const C55Operand *s2 = &in->ops[1]; - const C55Operand *dst = &in->ops[2]; - // full-width register form: S2 and DST are registers of the same width. - if (s2->kind == C55_OP_REG && dst->kind == C55_OP_REG && s2->width == dst->width) { - const ut32 w = dst->width; - RzILOpBitVector *aval = NULL; - RzAnalysisLiftedILOp post = NULL; - if (a->kind == C55_OP_IMM) { - aval = UN(w, a->imm); - } else if (a->kind == C55_OP_MEM) { - int ab = a->access_bits ? a->access_bits : 16; - RzILOpBitVector *load = LOADW(ab, c55_mem_byte_addr(a)); - aval = c55_resize(load, ab, w, a->mem_uns); - post = c55_mem_post_effect(a); - } else if (a->kind == C55_OP_REG && a->width == w) { - aval = VARG(a->reg); - } else { - return NULL; - } - RzAnalysisLiftedILOp set = SETG(dst->reg, c55_apply_bitop(kind, VARG(s2->reg), aval)); - return post ? SEQ2(set, post) : set; - } - // 16-bit form: DST and S2 are accumulator halves / 16-bit registers and - // A is a 16-bit value -> DST := S2 A at 16 bits (bitwise, exact), - // e.g. "and #0xff, ac0.l, ac2.l". - bool dst16 = dst->kind == C55_OP_REGHALF || (dst->kind == C55_OP_REG && dst->width == 16); - bool s216 = s2->kind == C55_OP_REGHALF || - (s2->kind == C55_OP_REG && (s2->width == 16 || s2->width == 40)); - if (dst16 && s216) { - RzILOpBitVector *aval = c55_read16(a); - if (!aval) { - return NULL; - } - RzILOpBitVector *s2val = c55_read16(s2); - if (!s2val) { - rz_il_op_pure_free(aval); - return NULL; - } - return c55_write16(dst, c55_apply_bitop(kind, s2val, aval)); - } - return NULL; - } - return NULL; -} - -/** - * Bitwise complement: "not src, dst" -> dst := ~src, or the in-place "not dst" - * -> dst := ~dst. Restricted to register operands of matching width (for the - * two-operand form). Like and/or/xor this carries no extension, saturation, or - * value-affecting status side effects, so the complement over the register's - * bound width is exact. - */ -static RzAnalysisLiftedILOp c55_lift_not(const C55Insn *in) { - if (in->n_ops == 1) { - const C55Operand *dst = &in->ops[0]; - if (dst->kind == C55_OP_REG) { - return SETG(dst->reg, LOGNOT(VARG(dst->reg))); - } - return NULL; - } - if (in->n_ops == 2) { - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (src->kind == C55_OP_REG && dst->kind == C55_OP_REG && src->width == dst->width) { - return SETG(dst->reg, LOGNOT(VARG(src->reg))); - } - return NULL; - } - return NULL; -} - -/** - * Arithmetic / logical accumulator shift by a compile-time 6-bit signed count: - * "sfts ACx, #SHIFTW[, ACy]" (arithmetic) and "sftl ..." (logical). SHIFTW is a - * 6-bit signed field — positive shifts left, negative shifts right. A left shift - * is modulo 2^40; a right shift fills with the sign bit (sfts) or with zero - * (sftl). Restricted to accumulator (40-bit) register operands with an immediate - * count (the common form). The default non-saturating shift is modelled; the - * optional SATD/M40 saturation modes are not represented (the same convention - * the accumulator add/sub lift uses). Register-count and memory forms return - * NULL. - */ -static RzAnalysisLiftedILOp c55_lift_shift(bool arith, const C55Insn *in) { - const C55Operand *src; - const C55Operand *cnt; - const C55Operand *dst; - if (in->n_ops == 3) { - src = &in->ops[0]; - cnt = &in->ops[1]; - dst = &in->ops[2]; - } else if (in->n_ops == 2) { - src = &in->ops[0]; - cnt = &in->ops[1]; - dst = &in->ops[0]; - } else { - return NULL; - } - if (src->kind != C55_OP_REG || dst->kind != C55_OP_REG || - src->width != dst->width || src->width != 40) { - return NULL; - } - // Register shift count (Tm, 16-bit signed): positive shifts left, negative - // shifts right (arithmetic fills with the sign bit, logical with zero). - if (cnt->kind == C55_OP_REG && cnt->width == 16) { - RzILOpBool *fill = arith ? MSB(VARG(src->reg)) : IL_FALSE; - return SETG(dst->reg, - ITE(SLT(VARG(cnt->reg), SN(16, 0)), - SHIFTR(fill, VARG(src->reg), SUB(SN(16, 0), VARG(cnt->reg))), - SHIFTL(IL_FALSE, VARG(src->reg), VARG(cnt->reg)))); - } - if (cnt->kind != C55_OP_IMM || cnt->imm > 0x3f) { - return NULL; - } - // SHIFTW is a 6-bit signed shift count (positive = left, negative = right). - long imm = (long)(cnt->imm & 0x3f); - long c = (imm & 0x20) ? imm - 0x40 : imm; - if (c == 0) { - return SETG(dst->reg, VARG(src->reg)); - } - if (c > 0) { - return SETG(dst->reg, SHIFTL(IL_FALSE, VARG(src->reg), UN(6, (ut64)c))); - } - RzILOpBool *fill = arith ? MSB(VARG(src->reg)) : IL_FALSE; - return SETG(dst->reg, SHIFTR(fill, VARG(src->reg), UN(6, (ut64)(-c)))); -} - -/** - * Value-exact 16-bit-destination add/sub. Two-operand " #imm/src, dst16" - * (dst16 is a T or AR register) and the three-operand " #imm/src16, ACx, - * Tx" form, where the low word of (ACx +/- sx(src)) is written to the 16-bit - * destination. A 16-bit destination uses plain modulo-2^16 arithmetic with no - * saturation, and truncation commutes with add/sub, so the low-word result is - * exact regardless of whether the ALU is 16- or 40-bit. Accumulator-destination - * add/sub is handled elsewhere (saturation / M40 modes). - */ -typedef enum { - C55_ADD, - C55_SUB -} C55ArithOp; - -static RzAnalysisLiftedILOp c55_lift_arith16(C55ArithOp kind, const C55Insn *in) { - if (in->n_ops == 3) { - // " src, ACx, Tx" : Tx = low16(ACx +/- sx(src)) - const C55Operand *s = &in->ops[0]; - const C55Operand *a = &in->ops[1]; - const C55Operand *d = &in->ops[2]; - if (a->kind != C55_OP_REG || a->width != 40 || d->kind != C55_OP_REG || d->width != 16) { - return NULL; - } - RzILOpBitVector *sv = NULL; - if (s->kind == C55_OP_IMM) { - sv = SN(40, (st64)(st16)(ut16)s->imm); - } else if (s->kind == C55_OP_REG && s->width == 16) { - sv = SIGNED(40, VARG(s->reg)); - } else { - return NULL; - } - RzILOpBitVector *r = (kind == C55_ADD) ? ADD(VARG(a->reg), sv) : SUB(VARG(a->reg), sv); - return SETG(d->reg, CAST(16, IL_FALSE, r)); - } - if (in->n_ops != 2) { - return NULL; - } - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (dst->kind != C55_OP_REG || dst->width != 16) { - return NULL; - } - RzILOpBitVector *operand = NULL; - if (src->kind == C55_OP_IMM) { - operand = UN(16, src->imm); - } else if (src->kind == C55_OP_REG && src->width == 16) { - operand = VARG(src->reg); - } else if (src->kind == C55_OP_REG && src->width == 40) { - operand = CAST(16, IL_FALSE, VARG(src->reg)); // ACx low word - } else { - return NULL; - } - switch (kind) { - case C55_ADD: return SETG(dst->reg, ADD(VARG(dst->reg), operand)); - case C55_SUB: return SETG(dst->reg, SUB(VARG(dst->reg), operand)); - } - return NULL; -} - -/** - * Value-exact accumulator add/sub: " #imm, ACx", " ACx, ACy", or - * " Tx, ACy". The 40-bit accumulator ALU is plain modulo-2^40 when - * saturation is disabled (ST1 SATD = 0 -- the reset default, and the mode this - * firmware runs in), so the result is exact: the M40 bit only changes overflow - * flagging and the sign-bit position, not the stored value when not saturating. - * 16-bit source operands are sign-extended into the 40-bit datapath - * (SXMD = 1, reset default). Saturation (SATD = 1) and the status-flag side - * effects are deliberately not modelled -- like the carry/overflow flags they - * await a status model; this lift is exact for SATD = 0. - */ -static RzAnalysisLiftedILOp c55_lift_acarith(C55ArithOp kind, const C55Insn *in) { - if (in->n_ops != 2) { - return NULL; - } - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (dst->kind != C55_OP_REG || dst->width != 40) { - return NULL; // accumulator destination only - } - RzILOpBitVector *operand = NULL; - if (src->kind == C55_OP_IMM) { - operand = UN(40, src->imm); // parser admits only non-negative immediates - } else if (src->kind == C55_OP_REG && src->width == 40) { - operand = VARG(src->reg); - } else if (src->kind == C55_OP_REG && src->width == 16) { - operand = SIGNED(40, VARG(src->reg)); // SXMD = 1 - } else { - return NULL; - } - switch (kind) { - case C55_ADD: return SETG(dst->reg, ADD(VARG(dst->reg), operand)); - case C55_SUB: return SETG(dst->reg, SUB(VARG(dst->reg), operand)); - } - return NULL; -} - -/** - * Build the IL predicate for a C55x+ conditional-branch condition string, taken - * from the decoded syntax after the target operand. Two condition families are - * handled exactly: - * - status flags `tc1` / `tc2` / `carry` (and their `!`-negated forms), read - * as bits 13 / 12 / 11 of ST0_55; - * - a register / accumulator-half compared against an immediate, e.g. - * `ac5 >= #0x7`, `ac1.l < #0x3`. Comparisons are signed for `bcc` and - * unsigned for `bccu` (the \p is_unsigned flag). - * Anything else (register-vs-register conditions, unknown flags) returns NULL so - * the branch is left unlifted rather than approximated. - */ -static RzILOpBool *c55_cond_value(const char *tok, ut32 *width) { - ut32 w = 0; - const char *rn = c55_reg(tok, &w); - if (rn) { - *width = w; - return VARG(rn); - } - C55Operand h; - if (c55_parse_half(tok, &h)) { - *width = 16; - if (h.half == 1) { - return CAST(16, IL_FALSE, VARG(h.reg)); // .l = bits 15:0 - } - return CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(h.reg), UN(8, 16))); // .h = bits 31:16 - } - return NULL; -} - -static RzILOpBool *c55_lift_cond(const char *syntax, bool is_unsigned) { - if (!syntax) { - return NULL; - } - const char *comma = strchr(syntax, ','); - if (!comma) { - return NULL; - } - const char *c = comma + 1; - while (*c == ' ') { - c++; - } - char buf[48]; - size_t n = strlen(c); - while (n > 0 && (c[n - 1] == ' ' || c[n - 1] == '\n')) { - n--; - } - if (n == 0 || n >= sizeof(buf)) { - return NULL; - } - memcpy(buf, c, n); - buf[n] = '\0'; - - // status-flag conditions: [!]tc1 / [!]tc2 / [!]carry - { - char *p = buf; - bool neg = false; - if (*p == '!') { - neg = true; - p++; - } - int bit = -1; - if (!strcmp(p, "tc1")) { - bit = 13; - } else if (!strcmp(p, "tc2")) { - bit = 12; - } else if (!strcmp(p, "carry")) { - bit = 11; - } - if (bit >= 0) { - RzILOpBool *b = LSB(SHIFTR(IL_FALSE, VARG("st0_55"), UN(4, (ut64)bit))); - return neg ? INV(b) : b; - } - } - - // comparison: #imm - static const struct { - const char *s; - int kind; - } relops[] = { { "==", 0 }, { "!=", 1 }, { "<=", 2 }, { ">=", 3 }, { "<", 4 }, { ">", 5 } }; - char *op = NULL; - int kind = -1; - size_t oplen = 0; - for (size_t i = 0; i < sizeof(relops) / sizeof(relops[0]); i++) { - char *f = strstr(buf, relops[i].s); - if (f) { - op = f; - kind = relops[i].kind; - oplen = strlen(relops[i].s); - break; - } - } - if (!op) { - return NULL; - } - // left/right tokens - char left[24]; - char right[24]; - size_t ll = (size_t)(op - buf); - while (ll > 0 && buf[ll - 1] == ' ') { - ll--; - } - const char *r = op + oplen; - while (*r == ' ') { - r++; - } - size_t rl = strlen(r); - while (rl > 0 && r[rl - 1] == ' ') { - rl--; - } - if (ll == 0 || ll >= sizeof(left) || rl == 0 || rl >= sizeof(right)) { - return NULL; - } - memcpy(left, buf, ll); - left[ll] = '\0'; - memcpy(right, r, rl); - right[rl] = '\0'; - ut32 lw = 0; - RzILOpBitVector *lhs = c55_cond_value(left, &lw); - if (!lhs) { - return NULL; - } - // RHS is a register/half (width-matched to the LHS, extended per signedness) - // or an immediate -- the same operand shapes c55_lift_cmp accepts. - RzILOpBitVector *rhs = NULL; - ut32 rw = 0; - RzILOpBitVector *rv = c55_cond_value(right, &rw); - if (rv) { - if (rw < lw) { - rv = is_unsigned ? UNSIGNED(lw, rv) : SIGNED(lw, rv); - } else if (rw > lw) { - lhs = is_unsigned ? UNSIGNED(rw, lhs) : SIGNED(rw, lhs); - lw = rw; - } - rhs = rv; - } else { - ut64 imm = 0; - if (!c55_parse_imm(right, &imm)) { - rz_il_op_pure_free(lhs); - return NULL; - } - rhs = UN(lw, imm); - } - switch (kind) { - case 0: return EQ(lhs, rhs); - case 1: return INV(EQ(lhs, rhs)); - case 2: return is_unsigned ? ULE(lhs, rhs) : SLE(lhs, rhs); - case 3: return INV(is_unsigned ? ULT(lhs, rhs) : SLT(lhs, rhs)); - case 4: return is_unsigned ? ULT(lhs, rhs) : SLT(lhs, rhs); - case 5: return is_unsigned ? UGT(lhs, rhs) : SGT(lhs, rhs); - default: break; - } - rz_il_op_pure_free(lhs); - rz_il_op_pure_free(rhs); - return NULL; -} - -// Map a status-bit token to its ST0_55 register and bit position. Accepts the -// disassembler's names ("st0_carry", "st0_tc1", "st0_acov0", ...) and the bare -// "carry"/"tc1"/"tc2" forms. ST0_55 layout: ACOV1=9, ACOV0=10, CARRY=11, TC2=12, -// TC1=13, ACOV3=14, ACOV2=15. Returns false for an unknown name. -static bool c55_status_bit(const char *name, const char **reg, int *bit) { - *reg = "st0_55"; - if (!strcmp(name, "carry") || !strcmp(name, "st0_carry")) { - *bit = 11; - } else if (!strcmp(name, "tc1") || !strcmp(name, "st0_tc1")) { - *bit = 13; - } else if (!strcmp(name, "tc2") || !strcmp(name, "st0_tc2")) { - *bit = 12; - } else if (!strcmp(name, "st0_acov0")) { - *bit = 10; - } else if (!strcmp(name, "st0_acov1")) { - *bit = 9; - } else if (!strcmp(name, "st0_acov2")) { - *bit = 15; - } else if (!strcmp(name, "st0_acov3")) { - *bit = 14; - } else { - return false; - } - return true; -} - -// Status bit `bit` of `reg` isolated as a \p width-bit value that is 0 or 1. -static RzILOpBitVector *c55_bit_val(const char *reg, int bit, ut32 width) { - RzILOpBitVector *v = LOGAND(SHIFTR(IL_FALSE, VARG(reg), UN(8, (ut64)bit)), UN(16, 1)); - return (width == 16) ? v : UNSIGNED(width, v); -} - -// Status bit `bit` of `reg` as an IL bool (true iff set). -static RzILOpBool *c55_bit_bool(const char *reg, int bit) { - return INV(IS_ZERO(LOGAND(VARG(reg), UN(16, 1u << bit)))); -} - -// Read-modify-write: set status bit `bit` of `reg` to the bool `cond` (consumed). -static RzAnalysisLiftedILOp c55_set_bit(const char *reg, int bit, RzILOpBool *cond) { - ut32 mask = 1u << bit; - return SETG(reg, - ITE(cond, - LOGOR(VARG(reg), UN(16, mask)), - LOGAND(VARG(reg), UN(16, (~mask) & 0xffff)))); -} - -// Parse a relational expression "A B" into an IL bool (signed relational -// operators unless \p is_unsigned). A and B are register / accumulator-half / -// immediate values, width-matched by extending the narrower side. Returns NULL -// for a form that does not parse. -static RzILOpBool *c55_relexpr_bool(const char *expr, bool is_unsigned) { - static const struct { - const char *s; - int kind; - } relops[] = { { "==", 0 }, { "!=", 1 }, { "<=", 2 }, { ">=", 3 }, { "<", 4 }, { ">", 5 } }; - char *op = NULL; - int kind = -1; - size_t oplen = 0; - for (size_t i = 0; i < sizeof(relops) / sizeof(relops[0]); i++) { - char *f = strstr(expr, relops[i].s); - if (f) { - op = f; - kind = relops[i].kind; - oplen = strlen(relops[i].s); - break; - } - } - if (!op) { - return NULL; - } - char left[24]; - char right[24]; - size_t ll = (size_t)(op - expr); - while (ll > 0 && expr[ll - 1] == ' ') { - ll--; - } - const char *r = op + oplen; - while (*r == ' ') { - r++; - } - size_t rl = strlen(r); - while (rl > 0 && r[rl - 1] == ' ') { - rl--; - } - if (ll == 0 || ll >= sizeof(left) || rl == 0 || rl >= sizeof(right)) { - return NULL; - } - memcpy(left, expr, ll); - left[ll] = '\0'; - memcpy(right, r, rl); - right[rl] = '\0'; - ut32 lw = 0; - RzILOpBitVector *lhs = c55_cond_value(left, &lw); - if (!lhs) { - return NULL; - } - RzILOpBitVector *rhs = NULL; - ut32 rw = 0; - RzILOpBitVector *rv = c55_cond_value(right, &rw); - if (rv) { - if (rw < lw) { - rv = is_unsigned ? UNSIGNED(lw, rv) : SIGNED(lw, rv); - } else if (rw > lw) { - lhs = is_unsigned ? UNSIGNED(rw, lhs) : SIGNED(rw, lhs); - lw = rw; - } - rhs = rv; - } else { - ut64 imm = 0; - if (!c55_parse_imm(right, &imm)) { - rz_il_op_pure_free(lhs); - return NULL; - } - rhs = UN(lw, imm); - } - switch (kind) { - case 0: return EQ(lhs, rhs); - case 1: return INV(EQ(lhs, rhs)); - case 2: return is_unsigned ? ULE(lhs, rhs) : SLE(lhs, rhs); - case 3: return INV(is_unsigned ? ULT(lhs, rhs) : SLT(lhs, rhs)); - case 4: return is_unsigned ? ULT(lhs, rhs) : SLT(lhs, rhs); - case 5: return is_unsigned ? UGT(lhs, rhs) : SGT(lhs, rhs); - } - rz_il_op_pure_free(lhs); - rz_il_op_pure_free(rhs); - return NULL; -} - -/** - * Lift "cmp[u] , tcN": evaluate the relational comparison and - * write its boolean result into the test-control flag TC1 (ST0_55 bit 13) or - * TC2 (bit 12). \p is_unsigned selects the unsigned relational operators (the - * "cmpu" form) over the signed ones ("cmp"). is a register or accumulator - * half; is another such value or an immediate. The two sides are width- - * matched by extending the narrower one (zero-/sign-extend per signedness). - * Returns NULL for any operand or destination shape that is not modelled, - * preserving the correct-or-NULL contract. - */ -static RzAnalysisLiftedILOp c55_lift_cmp(const char *syntax, bool is_unsigned) { - if (!syntax) { - return NULL; - } - const char *s = strchr(syntax, ' '); - if (!s) { - return NULL; - } - while (*s == ' ') { - s++; - } - const char *comma = strrchr(s, ','); - if (!comma) { - return NULL; - } - // destination flag (token after the last comma) - const char *d = comma + 1; - while (*d == ' ') { - d++; - } - int bit = -1; - if (!strcmp(d, "tc1")) { - bit = 13; - } else if (!strcmp(d, "tc2")) { - bit = 12; - } else { - return NULL; - } - // comparison expression "A relop B" (everything before the comma) - char expr[48]; - size_t el = (size_t)(comma - s); - while (el > 0 && s[el - 1] == ' ') { - el--; - } - if (el == 0 || el >= sizeof(expr)) { - return NULL; - } - memcpy(expr, s, el); - expr[el] = '\0'; - RzILOpBool *cond = c55_relexpr_bool(expr, is_unsigned); - if (!cond) { - return NULL; - } - // ST0_55 is 16-bit; set or clear the TC bit per the comparison result. - ut32 mask = 1u << bit; - return SETG("st0_55", - ITE(cond, - LOGOR(VARG("st0_55"), UN(16, mask)), - LOGAND(VARG("st0_55"), UN(16, (~mask) & 0xffff)))); -} - -/** - * "mov #K << #SHIFT, ACx": load the immediate, sign-extended from 16 bits and - * shifted left by a compile-time amount, into a 40-bit accumulator. The - * compiler uses this to build a constant's upper bits, typically paired with a - * following "or #lo, ACx, ACx" (so "mov #0 << #16, AC1; or #0x8004, AC1, AC1" - * materialises 0x8004). The " << #" form is a single disassembly token - * the structured operand parser leaves as OTHER, so it is matched on the - * syntax string here. Returns NULL for non-accumulator destinations or shifts - * the model cannot represent. - */ -static RzAnalysisLiftedILOp c55_lift_mov_shl(const char *syntax) { - if (!syntax || strncmp(syntax, "mov ", 4)) { - return NULL; - } - const char *shl = strstr(syntax, " << #"); - if (!shl) { - return NULL; - } - char immbuf[48]; - size_t ilen = (size_t)(shl - (syntax + 4)); - if (ilen == 0 || ilen >= sizeof(immbuf)) { - return NULL; - } - memcpy(immbuf, syntax + 4, ilen); - immbuf[ilen] = '\0'; - const char *sh = shl + 5; // past " << #" - const char *comma = strchr(sh, ','); - if (!comma) { - return NULL; - } - char shbuf[16]; - size_t slen = (size_t)(comma - sh); - if (slen == 0 || slen >= sizeof(shbuf)) { - return NULL; - } - memcpy(shbuf, sh, slen); - shbuf[slen] = '\0'; - const char *rp = comma + 1; - while (*rp == ' ') { - rp++; - } - char regbuf[16]; - size_t rl = strlen(rp); - if (rl == 0 || rl >= sizeof(regbuf)) { - return NULL; - } - memcpy(regbuf, rp, rl + 1); - ut64 k = 0, s = 0; - if (!c55_parse_imm(immbuf, &k) || !c55_parse_imm(shbuf, &s) || s >= 40) { - return NULL; - } - ut32 w = 0; - const char *reg = c55_reg(regbuf, &w); - if (!reg || w != 40) { - return NULL; - } - // sign-extend the 16-bit immediate, shift, and truncate to 40 bits - st64 sval = (k & 0x8000ULL) ? (st64)(k | 0xffffffffffff0000ULL) : (st64)(k & 0xffffULL); - ut64 v = ((ut64)((ut64)sval << s)) & 0xffffffffffULL; - return SETG(reg, UN(40, v)); -} - -/** - * A-unit register move / modular add / sub: "amov SRC, DST", "aadd SRC, DST", - * "asub SRC, DST". DST is a bound register (XAR 23-bit, AR/T 16-bit, or AC - * 40-bit); SRC is a bound register or an immediate. amov assigns SRC to DST - * (resized -- address registers hold unsigned addresses, so widening - * zero-extends); aadd/asub compute DST +/- SRC at DST's width. A-unit pointer - * arithmetic and the 16-bit ALU are plain modulo with no saturation, so the - * result is exact at the bound width. An immediate is lifted only when it fits - * the destination width, so an out-of-range constant (e.g. a 24-bit byte - * constant moved into a 23-bit XAR) is left unlifted rather than truncated. SP / - * XSP destinations are not handled here (SP has no fixed width in this model; - * its arithmetic belongs with the deferred stack ops). - */ -typedef enum { - C55_AMOV, - C55_AADD, - C55_ASUB -} C55AddrOp; -static RzAnalysisLiftedILOp c55_lift_addr(C55AddrOp kind, const C55Insn *in) { - if (in->n_ops != 2) { - return NULL; - } - const C55Operand *src = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (dst->kind != C55_OP_REG) { - return NULL; - } - const ut32 w = dst->width; - RzILOpBitVector *sval = NULL; - if (src->kind == C55_OP_IMM) { - if (w < 64 && src->imm >= (1ULL << w)) { - return NULL; // constant does not fit the destination register - } - sval = UN(w, src->imm); - } else if (src->kind == C55_OP_REG) { - sval = c55_resize(VARG(src->reg), src->width, w, true); - } else { - return NULL; - } - switch (kind) { - case C55_AMOV: return SETG(dst->reg, sval); - case C55_AADD: return SETG(dst->reg, ADD(VARG(dst->reg), sval)); - case C55_ASUB: return SETG(dst->reg, SUB(VARG(dst->reg), sval)); - } - rz_il_op_pure_free(sval); - return NULL; -} - -/** - * "amar , DST": load the *address* the memory operand designates into the - * address register DST, applying the operand's pointer side effect but no memory - * access. In word-pointer mode an indirect "*arN" yields the base word address, - * and an indexed "*arN(short(#K))" / "*sp(#K)" yields (base + sx(K)); the result - * is resized to DST's width. Post-increment/decrement modes apply their XARn - * +/- 1-word side effect (sequenced after the assignment). Absolute and other - * memory modes, and non-register destinations, return NULL. - */ -static RzAnalysisLiftedILOp c55_lift_amar(const C55Insn *in) { - // Form 1: "amar Smem, REG" -> REG = the computed word address of Smem, plus - // the operand's post-modify side effect. - if (in->n_ops == 2 && in->ops[1].kind == C55_OP_REG) { - const C55Operand *mem = &in->ops[0]; - const C55Operand *dst = &in->ops[1]; - if (mem->kind != C55_OP_MEM || !mem->reg) { - return NULL; - } - RzILOpBitVector *addr = NULL; - switch (mem->amode) { - case C55_AM_INDIRECT: - case C55_AM_POSTINC: - case C55_AM_POSTDEC: - addr = UNSIGNED(24, VARG(mem->reg)); - break; - case C55_AM_INDEXED: - addr = ADD(UNSIGNED(24, VARG(mem->reg)), SN(24, mem->disp)); - break; - default: - return NULL; - } - addr = c55_resize(addr, 24, dst->width, true); - RzAnalysisLiftedILOp set = SETG(dst->reg, addr); - RzAnalysisLiftedILOp post = c55_mem_post_effect(mem); - return post ? SEQ2(set, post) : set; - } - // Form 2: "amar Smem [, Smem ...]" with no register destination -> the - // instruction only applies each operand's address-register post-modify; a - // plain indirect modifies nothing and so lifts to nop. - if (in->n_ops < 1) { - return NULL; - } - RzAnalysisLiftedILOp seq = NULL; - for (int i = 0; i < in->n_ops; i++) { - if (in->ops[i].kind != C55_OP_MEM || !in->ops[i].reg) { - return NULL; - } - RzAnalysisLiftedILOp post = c55_mem_post_effect(&in->ops[i]); - if (post) { - seq = seq ? SEQ2(seq, post) : post; - } - } - return seq ? seq : NOP(); -} - -/** - * "neg SRC, DST" / in-place "neg DST": two's-complement negation, modelled as - * 0 - SRC at the operand width. Full-accumulator (40-bit) and 16-bit - * register/half forms are handled; a half destination negates its 16-bit value - * in place. SATD/M40 saturation is not modelled (matching the add/sub - * convention), so the lift is exact for SATD = 0. Other shapes return NULL. - */ -static RzAnalysisLiftedILOp c55_lift_neg(const C55Insn *in) { - const C55Operand *src; - const C55Operand *dst; - if (in->n_ops == 2) { - src = &in->ops[0]; - dst = &in->ops[1]; - } else if (in->n_ops == 1) { - src = dst = &in->ops[0]; - } else { - return NULL; - } - if (src->kind == C55_OP_REG && dst->kind == C55_OP_REG && src->width == dst->width) { - return SETG(dst->reg, SUB(UN(dst->width, 0), VARG(src->reg))); - } - bool dst16 = dst->kind == C55_OP_REGHALF || (dst->kind == C55_OP_REG && dst->width == 16); - if (dst16) { - RzILOpBitVector *v = c55_read16(src); - if (!v) { - return NULL; - } - return c55_write16(dst, SUB(UN(16, 0), v)); - } - return NULL; -} - -/** - * Lift one already-decoded C55x+ op to IL. \p syntax is the decoded lowercase - * mnemonic string (may be NULL). - */ -// Shift a 40-bit value left by an immediate ("#16" / "0x33") or a 16-bit -// register count ("t0"). Consumes \p v40; returns NULL (freeing it) for any -// other shift token. -static RzILOpBitVector *c55_shift40(RzILOpBitVector *v40, const char *sh) { - ut32 w = 0; - const char *rn = c55_reg(sh, &w); - if (rn && w == 16) { - return SHIFTL(IL_FALSE, v40, VARG(rn)); - } - ut64 n = 0; - if (c55_parse_imm(sh, &n)) { - return SHIFTL(IL_FALSE, v40, UN(8, n & 0xff)); - } - rz_il_op_pure_free(v40); - return NULL; -} - -// Accumulator ALU: add/sub/and/or/xor in the 2-operand ("OP src, ACdst" -> -// ACdst = ACdst OP src) and 3-operand ("OP src, ACsrc, ACdst" -> ACdst = -// ACsrc OP src) forms, where \p src is an accumulator or a 16-bit immediate, -// optionally shifted left ("src << #k" / "src << tN"). Arithmetic is 40-bit; -// the immediate is sign-extended for add/sub and zero-extended for the bitwise -// ops. Parses the raw syntax (the shifted source arrives as one C55_OP_OTHER -// token). Returns NULL for any other shape. This complements the dedicated -// 16-bit / simple-accumulator lifters, which are tried first. -static RzAnalysisLiftedILOp c55_lift_acc_alu(const char *syntax) { - if (!syntax) { - return NULL; - } - const char *sp = strchr(syntax, ' '); - if (!sp) { - return NULL; - } - size_t mlen = (size_t)(sp - syntax); - char mn[8]; - if (mlen >= sizeof(mn)) { - return NULL; - } - memcpy(mn, syntax, mlen); - mn[mlen] = '\0'; - enum { OP_ADD, - OP_SUB, - OP_AND, - OP_OR, - OP_XOR } op; - if (!strcmp(mn, "add")) { - op = OP_ADD; - } else if (!strcmp(mn, "sub")) { - op = OP_SUB; - } else if (!strcmp(mn, "and")) { - op = OP_AND; - } else if (!strcmp(mn, "or")) { - op = OP_OR; - } else if (!strcmp(mn, "xor")) { - op = OP_XOR; - } else { - return NULL; - } - // split the (pre-"||") operand list by comma, up to 3 - char ops[3][48]; - int nops = 0; - const char *p = sp + 1; - const char *par = strstr(p, "||"); - const char *limit = par ? par : p + strlen(p); - while (p < limit && nops < 3) { - while (p < limit && *p == ' ') { - p++; - } - const char *c = NULL; - for (const char *q = p; q < limit; q++) { - if (*q == ',') { - c = q; - break; - } - } - const char *e = c ? c : limit; - const char *te = e; - while (te > p && te[-1] == ' ') { - te--; - } - size_t L = (size_t)(te - p); - if (L == 0 || L >= sizeof(ops[0])) { - return NULL; - } - memcpy(ops[nops], p, L); - ops[nops][L] = '\0'; - nops++; - if (!c) { - break; - } - p = c + 1; - } - if (nops < 2 || nops > 3) { - return NULL; - } - ut32 dw = 0; - const char *dst = c55_reg(ops[nops - 1], &dw); - if (!dst || dw != 40) { - return NULL; - } - const char *lhs = dst; - if (nops == 3) { - ut32 lw = 0; - lhs = c55_reg(ops[1], &lw); - if (!lhs || lw != 40) { - return NULL; - } - } - // source operand ops[0] = " [<< ]" - char base[48]; - const char *shtok = NULL; - char *shp = strstr(ops[0], " << "); - if (shp) { - size_t bl = (size_t)(shp - ops[0]); - if (bl >= sizeof(base)) { - return NULL; - } - memcpy(base, ops[0], bl); - base[bl] = '\0'; - shtok = shp + 4; - } else { - if (strlen(ops[0]) >= sizeof(base)) { - return NULL; - } - rz_str_ncpy(base, ops[0], sizeof(base)); - } - bool is_arith = (op == OP_ADD || op == OP_SUB); - RzILOpBitVector *src; - ut32 bw = 0; - const char *brn = c55_reg(base, &bw); - if (brn && bw == 40) { - src = VARG(brn); - } else { - ut64 imm = 0; - if (!c55_parse_imm(base, &imm)) { - return NULL; - } - src = is_arith ? SN(40, (st64)(st16)(ut16)imm) : UN(40, imm & 0xffff); - } - if (shtok) { - src = c55_shift40(src, shtok); - if (!src) { - return NULL; - } - } - RzILOpBitVector *res; - switch (op) { - case OP_ADD: res = ADD(VARG(lhs), src); break; - case OP_SUB: res = SUB(VARG(lhs), src); break; - case OP_AND: res = LOGAND(VARG(lhs), src); break; - case OP_OR: res = LOGOR(VARG(lhs), src); break; - default: res = LOGXOR(VARG(lhs), src); break; - } - return SETG(dst, res); -} - -// abs / min / max on equal-width register operands (accumulator 40-bit, or T/AR -// 16-bit): "abs ACx" (1-op) or "OP src, dst" (2-op). \p kind: 0 = abs, 1 = min, -// 2 = max. Comparisons are signed. Returns NULL for mixed widths, non-register -// operands, or unexpected operand counts. -static RzAnalysisLiftedILOp c55_lift_minmaxabs(const C55Insn *in, int kind) { - const C55Operand *s, *d; - if (in->n_ops == 1) { - s = d = &in->ops[0]; - } else if (in->n_ops == 2) { - s = &in->ops[0]; - d = &in->ops[1]; - } else { - return NULL; - } - if (s->kind != C55_OP_REG || d->kind != C55_OP_REG || s->width != d->width) { - return NULL; - } - ut32 w = d->width; - if (w != 40 && w != 16) { - return NULL; - } - if (kind == 0) { // abs: (s < 0) ? -s : s - return SETG(d->reg, ITE(SLT(VARG(s->reg), SN(w, 0)), SUB(SN(w, 0), VARG(s->reg)), VARG(s->reg))); - } - if (kind == 1) { // min: (s < d) ? s : d - return SETG(d->reg, ITE(SLT(VARG(s->reg), VARG(d->reg)), VARG(s->reg), VARG(d->reg))); - } - // max: (s > d) ? s : d - return SETG(d->reg, ITE(SGT(VARG(s->reg), VARG(d->reg)), VARG(s->reg), VARG(d->reg))); -} - -// swap: exchange two equal-width registers via the temp-free XOR swap -// (a ^= b; b ^= a; a ^= b). Returns NULL for mixed widths / non-registers. -static RzAnalysisLiftedILOp c55_lift_swap(const C55Insn *in) { - if (in->n_ops != 2) { - return NULL; - } - const C55Operand *a = &in->ops[0], *b = &in->ops[1]; - if (a->kind != C55_OP_REG || b->kind != C55_OP_REG || a->width != b->width) { - return NULL; - } - return SEQ3( - SETG(a->reg, LOGXOR(VARG(a->reg), VARG(b->reg))), - SETG(b->reg, LOGXOR(VARG(b->reg), VARG(a->reg))), - SETG(a->reg, LOGXOR(VARG(a->reg), VARG(b->reg)))); -} - -// bset / bclr of a single bit in a status/other register: "bset #bit, REG" / -// "bclr #bit, REG" where #bit is a numeric bit index. Returns NULL for the -// named-bit form (e.g. "bset st0_acov0, st0_55") or non-register destinations. -static RzAnalysisLiftedILOp c55_lift_bitset(const C55Insn *in, bool set) { - if (in->n_ops != 2 || in->ops[0].kind != C55_OP_IMM || in->ops[1].kind != C55_OP_REG) { - return NULL; - } - ut32 w = in->ops[1].width; - ut64 bit = in->ops[0].imm; - if (bit >= w) { - return NULL; - } - const char *r = in->ops[1].reg; - if (set) { - return SETG(r, LOGOR(VARG(r), UN(w, 1ULL << bit))); - } - ut64 wmask = (w >= 64) ? ~0ULL : ((1ULL << w) - 1); - return SETG(r, LOGAND(VARG(r), UN(w, (~(1ULL << bit)) & wmask))); -} - -// 40-bit signed value of a multiplicand operand (documented integer-mode model): -// a 16-bit register / accumulator low word (.l) / accumulator half, sign-extended; -// a 16-bit immediate, sign-extended; or a memory word, sign-extended (zero-extended -// under an uns() wrapper). Returns NULL for any other operand kind. -static RzILOpBitVector *c55_mul_val(const C55Operand *op) { - switch (op->kind) { - case C55_OP_IMM: - return SN(40, (st64)(st16)(ut16)op->imm); - case C55_OP_REG: - if (op->width == 16) { - return SIGNED(40, VARG(op->reg)); - } - if (op->width == 40) { - return SIGNED(40, CAST(16, IL_FALSE, VARG(op->reg))); // ACx.l - } - return NULL; - case C55_OP_REGHALF: { - RzILOpBitVector *v = c55_src16(op); - return v ? SIGNED(40, v) : NULL; - } - case C55_OP_MEM: { - int ab = op->access_bits ? op->access_bits : 16; - RzILOpBitVector *l = LOADW(ab, c55_mem_byte_addr(op)); - return c55_resize(l, ab, 40, op->mem_uns); - } - default: - return NULL; - } -} - -// One multiply-family operand: a decoded operand plus the two decorations that -// appear in this syntax -- a "tN=" memory side-load target and a ">> #k" right -// shift applied to the accumulator source operand. -typedef struct { - C55Operand op; - const char *sideload; // "tN=Smem": static T-register name to also receive the loaded word - int shr; // ">> #k": arithmetic right-shift applied to this (accumulator) operand - bool ok; -} C55MulArg; - -static C55MulArg c55_parse_mul_arg(const char *tok) { - C55MulArg a; - memset(&a, 0, sizeof(a)); - char buf[80]; - if (strlen(tok) >= sizeof(buf)) { - return a; - } - rz_str_ncpy(buf, tok, sizeof(buf)); - char *s = buf; - // "tN=Smem" side-load prefix - char *eq = strchr(s, '='); - if (eq) { - *eq = '\0'; - ut32 w = 0; - const char *t = c55_reg(s, &w); - if (!t || w != 16 || t[0] != 't') { - return a; - } - a.sideload = t; - s = eq + 1; - } - // ">> #k" accumulator pre-shift suffix - char *sh = strstr(s, " >> "); - if (sh) { - const char *shtok = sh + 4; - *sh = '\0'; - ut64 k = 0; - if (!c55_parse_imm(shtok, &k)) { - return a; - } - a.shr = (int)(k & 0x3f); - } - // trim trailing spaces - size_t L = strlen(s); - while (L > 0 && s[L - 1] == ' ') { - s[--L] = '\0'; - } - ut32 w = 0; - const char *rn = c55_reg(s, &w); - ut64 imm = 0; - if (rn) { - a.op.kind = C55_OP_REG; - a.op.reg = rn; - a.op.width = w; - } else if (c55_parse_imm(s, &imm)) { - a.op.kind = C55_OP_IMM; - a.op.imm = imm; - } else if (c55_parse_mem(s, &a.op)) { - // filled - } else { - return a; - } - a.ok = true; - return a; -} - -// Multiply / multiply-accumulate (documented integer-mode model: FRCT = 0, no -// fractional <<1, no saturation). mpy* assigns the product; mac* adds it to the -// accumulator source; mas* subtracts it. An "r" suffix rounds the result to the -// upper word ((v + 0x8000) & ~0xffff). Multiplicand values come from c55_mul_val -// (accumulators contribute their low word .l). A leading "tN=Smem" also writes -// the loaded word to tN; memory post-modify side effects are applied last. -// Operand layout: dst is the last operand; for mac/mas a 4-operand form names a -// distinct accumulator source second-to-last, otherwise the destination doubles -// as the accumulator source. Unrepresented shapes return NULL. -static RzAnalysisLiftedILOp c55_lift_mul(const char *syntax) { - if (!syntax) { - return NULL; - } - const char *sp = strchr(syntax, ' '); - if (!sp) { - return NULL; - } - size_t mlen = (size_t)(sp - syntax); - char mn[12]; - if (mlen >= sizeof(mn)) { - return NULL; - } - memcpy(mn, syntax, mlen); - mn[mlen] = '\0'; - int kind; // 0 = set (mpy), 1 = add (mac), 2 = sub (mas) - if (!strncmp(mn, "mpy", 3)) { - kind = 0; - } else if (!strncmp(mn, "mac", 3)) { - kind = 1; - } else if (!strncmp(mn, "mas", 3)) { - kind = 2; - } else { - return NULL; - } - bool round = strchr(mn + 3, 'r') != NULL; - // split operands (first half only; "::" is handled by the parallel splitter) - const char *p = sp + 1; - const char *par = strstr(p, " :: "); - const char *limit = par ? par : p + strlen(p); - C55MulArg args[4]; - int n = 0; - while (p < limit && n < 4) { - while (p < limit && *p == ' ') { - p++; - } - const char *c = NULL; - for (const char *q = p; q < limit; q++) { - if (*q == ',') { - c = q; - break; - } - } - const char *e = c ? c : limit; - const char *te = e; - while (te > p && te[-1] == ' ') { - te--; - } - char tok[80]; - size_t tl = (size_t)(te - p); - if (tl == 0 || tl >= sizeof(tok)) { - return NULL; - } - memcpy(tok, p, tl); - tok[tl] = '\0'; - args[n] = c55_parse_mul_arg(tok); - if (!args[n].ok) { - return NULL; - } - n++; - if (!c) { - break; - } - p = c + 1; - } - if (n < 2) { - return NULL; - } - // destination = last operand, must be an accumulator - C55MulArg *dst = &args[n - 1]; - if (dst->op.kind != C55_OP_REG || dst->op.width != 40) { - return NULL; - } - // identify multiplicands, and (for mac/mas) the accumulator source - C55MulArg *m1, *m2, *accsrc = NULL; - if (kind == 0) { - if (n == 2) { - m1 = &args[0]; - m2 = dst; // dst.l as the second multiplicand - } else if (n == 3) { - m1 = &args[0]; - m2 = &args[1]; - } else { - return NULL; - } - } else { - if (n == 3) { - m1 = &args[0]; - m2 = &args[1]; - accsrc = dst; - } else if (n == 4 && args[2].op.kind == C55_OP_REG && args[2].op.width == 40) { - m1 = &args[0]; - m2 = &args[1]; - accsrc = &args[2]; - } else { - return NULL; - } - } - RzILOpBitVector *v1 = c55_mul_val(&m1->op); - RzILOpBitVector *v2 = c55_mul_val(&m2->op); - if (!v1 || !v2) { - rz_il_op_pure_free(v1); - rz_il_op_pure_free(v2); - return NULL; - } - RzILOpBitVector *prod = MUL(v1, v2); - RzILOpBitVector *res; - if (kind == 0) { - res = prod; - } else { - RzILOpBitVector *acc = VARG(accsrc->op.reg); - if (accsrc->shr > 0) { - acc = SHIFTR(MSB(VARG(accsrc->op.reg)), acc, UN(6, (ut64)accsrc->shr)); - } - res = (kind == 1) ? ADD(acc, prod) : SUB(acc, prod); - } - if (round) { - res = LOGAND(ADD(res, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); - } - RzAnalysisLiftedILOp eff = SETG(dst->op.reg, res); - // optional memory side-load into tN (load the same word again) - for (int i = 0; i < n; i++) { - if (args[i].sideload && args[i].op.kind == C55_OP_MEM) { - int ab = args[i].op.access_bits ? args[i].op.access_bits : 16; - RzILOpBitVector *l = LOADW(ab, c55_mem_byte_addr(&args[i].op)); - RzILOpBitVector *v = c55_resize(l, ab, 16, args[i].op.mem_uns); - eff = SEQ2(SETG(args[i].sideload, v), eff); - } - } - // memory post-modify side effects (after the access) - for (int i = 0; i < n; i++) { - if (args[i].op.kind == C55_OP_MEM) { - RzAnalysisLiftedILOp post = c55_mem_post_effect(&args[i].op); - if (post) { - eff = SEQ2(eff, post); - } - } - } - return eff; -} - -// Dual-memory accumulator arithmetic: "add Xmem, Ymem, ACx" / "sub Xmem, Ymem, -// ACx" -> ACx = sx(load X) +/- sx(load Y) (zero-extended under an uns() wrapper). -// 40-bit, no saturation. Memory post-modify side effects are applied. Returns -// NULL unless both sources are memory and the destination an accumulator. -static RzAnalysisLiftedILOp c55_lift_dualmem(const C55Insn *in, bool sub) { - if (in->n_ops != 3) { - return NULL; - } - const C55Operand *x = &in->ops[0], *y = &in->ops[1], *d = &in->ops[2]; - if (x->kind != C55_OP_MEM || y->kind != C55_OP_MEM || d->kind != C55_OP_REG || d->width != 40) { - return NULL; - } - RzILOpBitVector *vx = c55_mul_val(x); - RzILOpBitVector *vy = c55_mul_val(y); - if (!vx || !vy) { - rz_il_op_pure_free(vx); - rz_il_op_pure_free(vy); - return NULL; - } - RzAnalysisLiftedILOp eff = SETG(d->reg, sub ? SUB(vx, vy) : ADD(vx, vy)); - RzAnalysisLiftedILOp px = c55_mem_post_effect(x); - RzAnalysisLiftedILOp py = c55_mem_post_effect(y); - if (px) { - eff = SEQ2(eff, px); - } - if (py) { - eff = SEQ2(eff, py); - } - return eff; -} - -// round / square (documented integer-mode model). "round ACx, ACy": ACy = -// (ACx + 0x8000) & ~0xffff (round to the upper word). "sqar ACx, ACy": ACy = -// ACx.l * ACx.l, the signed square of the low word; "sqrr" rounds that result. -// No saturation. Returns NULL for shapes other than two accumulator operands. -static RzAnalysisLiftedILOp c55_lift_round_sq(const C55Insn *in, int kind) { - // kind: 0 = round, 1 = sqar (square), 2 = sqrr (square + round) - if (in->n_ops != 2 || in->ops[0].kind != C55_OP_REG || in->ops[0].width != 40 || - in->ops[1].kind != C55_OP_REG || in->ops[1].width != 40) { - return NULL; - } - const char *s = in->ops[0].reg, *d = in->ops[1].reg; - RzILOpBitVector *v; - if (kind == 0) { - v = VARG(s); - } else { - v = MUL(SIGNED(40, CAST(16, IL_FALSE, VARG(s))), SIGNED(40, CAST(16, IL_FALSE, VARG(s)))); - } - if (kind == 0 || kind == 2) { - v = LOGAND(ADD(v, UN(40, 0x8000)), UN(40, 0xffffff0000ULL)); - } - return SETG(d, v); -} - -// Split the operand list of `syntax` (everything after the first space) into up -// to `max` comma-separated, space-trimmed tokens. Returns the token count. -static int c55_split_ops(const char *syntax, char out[][24], int max) { - const char *s = strchr(syntax, ' '); - if (!s) { - return 0; - } - while (*s == ' ') { - s++; - } - int n = 0; - const char *p = s; - while (n < max && *p) { - const char *c = strchr(p, ','); - const char *e = c ? c : p + strlen(p); - const char *b = p; - while (b < e && *b == ' ') { - b++; - } - const char *te = e; - while (te > b && te[-1] == ' ') { - te--; - } - size_t l = (size_t)(te - b); - if (l == 0 || l >= 24) { - return n; - } - memcpy(out[n], b, l); - out[n][l] = '\0'; - n++; - if (!c) { - break; - } - p = c + 1; - } - return n; -} - -// cmpand / cmpor (+ unsigned cmpandu / cmporu): TCdst = (A relop B) AND/OR TCsrc, -// where TCsrc is a TC bit optionally negated with a leading '!'. Exact: the -// relational result and the bitwise TC combination are both modelled directly. -static RzAnalysisLiftedILOp c55_lift_cmpbit(const char *syntax, bool is_unsigned, bool is_and) { - char t[3][24]; - if (c55_split_ops(syntax, t, 3) != 3) { - return NULL; - } - bool neg = false; - const char *srcname = t[1]; - if (srcname[0] == '!') { - neg = true; - srcname++; - } - const char *sreg, *dreg; - int sbit, dbit; - if (!c55_status_bit(srcname, &sreg, &sbit) || !c55_status_bit(t[2], &dreg, &dbit)) { - return NULL; - } - RzILOpBool *cond = c55_relexpr_bool(t[0], is_unsigned); - if (!cond) { - return NULL; - } - RzILOpBool *tcsrc = c55_bit_bool(sreg, sbit); - if (neg) { - tcsrc = INV(tcsrc); - } - RzILOpBool *res = is_and ? AND(cond, tcsrc) : OR(cond, tcsrc); - return c55_set_bit(dreg, dbit, res); -} - -// rol / ror through a status bit: " BitIn, Src, BitOut, Dst". BitIn/BitOut -// are carry/TC bits in ST0_55, Src/Dst accumulators. rol: Dst = (Src << 1) | -// BitIn, BitOut = MSB(Src); ror: Dst = (Src >> 1) | (BitIn << 39), BitOut = -// LSB(Src). Exact for the 40-bit rotate. (Src and Dst are distinct here, so the -// destination write and the bit-out update do not interfere.) -static RzAnalysisLiftedILOp c55_lift_rotate(const char *syntax, bool left) { - char t[4][24]; - if (c55_split_ops(syntax, t, 4) != 4) { - return NULL; - } - const char *binr, *boutr; - int binbit, boutbit; - if (!c55_status_bit(t[0], &binr, &binbit) || !c55_status_bit(t[2], &boutr, &boutbit)) { - return NULL; - } - ut32 sw = 0, dw = 0; - const char *src = c55_reg(t[1], &sw); - const char *dst = c55_reg(t[3], &dw); - if (!src || !dst || sw != 40 || dw != 40) { - return NULL; - } - RzILOpBitVector *carin = c55_bit_val(binr, binbit, 40); - RzILOpBitVector *rotated; - RzILOpBool *bitout; - if (left) { - rotated = LOGOR(SHIFTL(IL_FALSE, VARG(src), UN(6, 1)), carin); - bitout = MSB(VARG(src)); // bit 39 shifts out on a left rotate - } else { - rotated = LOGOR(SHIFTR(IL_FALSE, VARG(src), UN(6, 1)), SHIFTL(IL_FALSE, carin, UN(6, 39))); - bitout = LSB(VARG(src)); // bit 0 shifts out on a right rotate - } - RzAnalysisLiftedILOp set_dst = SETG(dst, rotated); - RzAnalysisLiftedILOp set_out = c55_set_bit(boutr, boutbit, bitout); - return SEQ2(set_dst, set_out); -} - -// Named-bit bset / bclr: "bset st0_acovN, st0_55" / "bclr ...". Sets or clears -// the named ST0_55 status bit. Exact. -static RzAnalysisLiftedILOp c55_lift_bitset_named(const char *syntax, bool set) { - char t[2][24]; - if (c55_split_ops(syntax, t, 2) != 2) { - return NULL; - } - const char *reg; - int bit; - if (!c55_status_bit(t[0], ®, &bit) || strcmp(t[1], reg) != 0) { - return NULL; - } - return c55_set_bit(reg, bit, set ? IL_TRUE : IL_FALSE); -} - -// Build the byte effective address SP<<1 (word-pointer stack in data memory). -static RzILOpBitVector *c55_sp_byte_addr(void) { - return MUL(UNSIGNED(24, VARG("sp")), UN(24, 2)); -} - -// psh / pop (documented stack model): SP is a 16-bit word pointer; the stack is -// in data memory at byte address SP<<1 and grows toward lower addresses. A push -// pre-decrements SP and stores; a pop loads and post-increments. Each operand is -// processed left to right at its natural width: an accumulator or dbl(ACx) uses -// 32 bits / 2 words (the low 32 bits of the accumulator; a popped accumulator is -// zero-extended into its guard bits), a 16-bit register or mmap(@reg) uses 16 -// bits / 1 word. The pshboth/popboth dual-stack forms and the exact inter-operand -// ordering are NOT modelled here. Returns NULL for an operand shape outside this -// set. -static RzAnalysisLiftedILOp c55_lift_stack(const char *syntax, bool is_push) { - char t[2][24]; - int n = c55_split_ops(syntax, t, 2); - if (n < 1) { - return NULL; - } - RzAnalysisLiftedILOp seq = NULL; - for (int i = 0; i < n; i++) { - char *tok = t[i]; - size_t L = strlen(tok); - // Classify the operand into a base register plus a transfer "kind": - // 0 = 16-bit register / mmap(@reg) (1 word) - // 1 = accumulator, 32-bit (dbl / bare) (2 words, guard preserved on pop) - // 2 = xar, 32-bit (dbl / bare) (2 words, written back as 23-bit) - // 3/4/5 = accumulator .l / .h / .g sub-field (1 word) - const char *reg = NULL; - int kind = -1; - ut32 w = 0; - if (!strncmp(tok, "dbl(", 4) && L > 5 && tok[L - 1] == ')') { - tok[L - 1] = '\0'; - reg = c55_reg(tok + 4, &w); - if (!reg) { - return NULL; - } - kind = (w == 40) ? 1 : (w == 23) ? 2 - : -1; - } else if (!strncmp(tok, "mmap(@", 6) && L > 7 && tok[L - 1] == ')') { - tok[L - 1] = '\0'; - reg = c55_reg(tok + 6, &w); - if (!reg || w != 16) { - return NULL; - } - kind = 0; - } else if (L > 3 && tok[L - 2] == '.') { - char sub = tok[L - 1]; - tok[L - 2] = '\0'; - reg = c55_reg(tok, &w); - if (!reg || w != 40) { - return NULL; - } - kind = (sub == 'l') ? 3 : (sub == 'h') ? 4 - : (sub == 'g') ? 5 - : -1; - } else { - reg = c55_reg(tok, &w); - if (!reg) { - return NULL; - } - kind = (w == 40) ? 1 : (w == 23) ? 2 - : (w == 16) ? 0 - : -1; - } - if (kind < 0) { - return NULL; - } - int words = (kind == 1 || kind == 2) ? 2 : 1; - RzAnalysisLiftedILOp eff; - if (is_push) { - RzILOpBitVector *val; - switch (kind) { - case 0: val = VARG(reg); break; - case 1: val = CAST(32, IL_FALSE, VARG(reg)); break; - case 2: val = UNSIGNED(32, VARG(reg)); break; - case 3: val = CAST(16, IL_FALSE, VARG(reg)); break; - case 4: val = CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(reg), UN(8, 16))); break; - default: val = CAST(16, IL_FALSE, SHIFTR(IL_FALSE, VARG(reg), UN(8, 32))); break; // .g - } - eff = SEQ2( - SETG("sp", SUB(VARG("sp"), UN(16, (ut64)words))), - STOREW(c55_sp_byte_addr(), val)); - } else { - RzILOpBitVector *load = LOADW(words == 2 ? 32 : 16, c55_sp_byte_addr()); - RzAnalysisLiftedILOp wr; - switch (kind) { - case 0: wr = SETG(reg, load); break; - case 1: // restore 32 bits, preserve guard (39:32) - wr = SETG(reg, LOGOR(LOGAND(VARG(reg), UN(40, 0xff00000000ULL)), UNSIGNED(40, load))); - break; - case 2: wr = SETG(reg, CAST(23, IL_FALSE, load)); break; - case 3: wr = SETG(reg, LOGOR(LOGAND(VARG(reg), UN(40, 0xffffff0000ULL)), UNSIGNED(40, load))); break; - case 4: - wr = SETG(reg, LOGOR(LOGAND(VARG(reg), UN(40, 0xff0000ffffULL)), SHIFTL(IL_FALSE, UNSIGNED(40, load), UN(6, 16)))); - break; - default: // .g : restore guard bits 39:32 - wr = SETG(reg, LOGOR(LOGAND(VARG(reg), UN(40, 0x00ffffffffULL)), SHIFTL(IL_FALSE, LOGAND(UNSIGNED(40, load), UN(40, 0xff)), UN(6, 32)))); - break; - } - eff = SEQ2(wr, SETG("sp", ADD(VARG("sp"), UN(16, (ut64)words)))); - } - seq = seq ? SEQ2(seq, eff) : eff; - } - return seq; -} - -// Successor register `delta` positions after `reg` (ac0->ac1, ar4->ar5, t0->t1), -// validated and returned in canonical form via c55_reg, or NULL if the result is -// not a bound register. -static const char *c55_reg_succ(const char *reg, int delta, ut32 *w) { - size_t L = strlen(reg); - size_t i = L; - while (i > 0 && reg[i - 1] >= '0' && reg[i - 1] <= '9') { - i--; - } - if (i == L || i >= 12) { - return NULL; - } - int num = atoi(reg + i) + delta; - if (num < 0 || num > 99) { - return NULL; - } - char buf[16]; - memcpy(buf, reg, i); - snprintf(buf + i, sizeof(buf) - i, "%d", num); - return c55_reg(buf, w); -} - -// swapp / swap4: exchange a register pair (swapp Rx,Ry swaps Rx<->Ry and -// Rx+1<->Ry+1) or quad (swap4 extends to +0..+3) using temp-free XOR swaps. -// Exact. Returns NULL if a successor is not a bound register or the two sides -// differ in width. -static RzAnalysisLiftedILOp c55_lift_swapp(const C55Insn *in, int count) { - if (in->n_ops != 2 || in->ops[0].kind != C55_OP_REG || in->ops[1].kind != C55_OP_REG || - in->ops[0].width != in->ops[1].width) { - return NULL; - } - RzAnalysisLiftedILOp seq = NULL; - for (int k = 0; k < count; k++) { - ut32 wa = 0, wb = 0; - const char *a = (k == 0) ? in->ops[0].reg : c55_reg_succ(in->ops[0].reg, k, &wa); - const char *b = (k == 0) ? in->ops[1].reg : c55_reg_succ(in->ops[1].reg, k, &wb); - if (!a || !b) { - rz_il_op_effect_free(seq); - return NULL; - } - RzAnalysisLiftedILOp sw = SEQ3( - SETG(a, LOGXOR(VARG(a), VARG(b))), - SETG(b, LOGXOR(VARG(b), VARG(a))), - SETG(a, LOGXOR(VARG(a), VARG(b)))); - seq = seq ? SEQ2(seq, sw) : sw; - } - return seq; -} - -// satr: saturate a 40-bit accumulator to the signed 32-bit range and store it in -// the destination accumulator (documented model: 32-bit saturation, no rounding). -static RzAnalysisLiftedILOp c55_lift_satr(const C55Insn *in) { - if (in->n_ops != 2 || in->ops[0].kind != C55_OP_REG || in->ops[0].width != 40 || - in->ops[1].kind != C55_OP_REG || in->ops[1].width != 40) { - return NULL; - } - const char *s = in->ops[0].reg, *d = in->ops[1].reg; - RzILOpBitVector *hi = SN(40, 0x7fffffffLL); - RzILOpBitVector *lo = SN(40, -0x80000000LL); - return SETG(d, - ITE(SGT(VARG(s), SN(40, 0x7fffffffLL)), hi, - ITE(SLT(VARG(s), SN(40, -0x80000000LL)), lo, VARG(s)))); -} - -// callcc / retcc-with-target: "callcc TARGET, COND" -> BRANCH(cond, jmp TARGET, -// nop). The return-linkage save is not modelled (as with the unconditional call). -static RzAnalysisLiftedILOp c55_lift_callcc(const char *syntax) { - const char *s = strchr(syntax, ' '); - if (!s) { - return NULL; - } - while (*s == ' ') { - s++; - } - const char *comma = strchr(s, ','); - if (!comma) { - return NULL; - } - char tgt[24]; - size_t tl = (size_t)(comma - s); - while (tl > 0 && s[tl - 1] == ' ') { - tl--; - } - if (tl == 0 || tl >= sizeof(tgt)) { - return NULL; - } - memcpy(tgt, s, tl); - tgt[tl] = '\0'; - ut64 target = 0; - if (!c55_parse_imm(tgt, &target)) { - return NULL; - } - const char *ce = comma + 1; - while (*ce == ' ') { - ce++; - } - RzILOpBool *cond = c55_relexpr_bool(ce, false); - if (!cond) { - return NULL; - } - return BRANCH(cond, JMP(UN(24, target & 0xffffff)), NOP()); -} - -// Lift a single (non-parallel) data-path instruction syntax to IL, or NULL if -// the form is not modelled. Factored out of the main entry so the "::" parallel -// splitter can lift each half independently. Control-only forms (handled from -// the analysis op fields) are not covered here. -static RzAnalysisLiftedILOp c55_lift_data(const char *syntax) { - if (!syntax) { - return NULL; - } - // "mov #K << #SHIFT, ACx" carries a shifted-immediate token the structured - // operand parser cannot classify; match it on the syntax string first. - if (!strncmp(syntax, "mov ", 4) && strstr(syntax, " << #")) { - RzAnalysisLiftedILOp e = c55_lift_mov_shl(syntax); - if (e) { - return e; - } - } - // Comparisons write a test-control flag (TC1/TC2), not a data register, and - // their operand is a relational expression the structured model does not - // classify, so dispatch them from the syntax ("cmpu" unsigned, "cmp" signed). - if (!strncmp(syntax, "cmpu ", 5)) { - RzAnalysisLiftedILOp e = c55_lift_cmp(syntax, true); - if (e) { - return e; - } - } else if (!strncmp(syntax, "cmp ", 4)) { - RzAnalysisLiftedILOp e = c55_lift_cmp(syntax, false); - if (e) { - return e; - } - } - C55Insn in; - if (!c55_decode_insn(syntax, &in) || in.parallel || in.truncated) { - return NULL; - } - RzAnalysisLiftedILOp e = NULL; - if (!strcmp(in.mnem, "mov") || !strcmp(in.mnem, "copy")) { - e = c55_lift_mov(&in); - } else if (!strcmp(in.mnem, "and")) { - e = c55_lift_bitop(C55_AND, &in); - if (!e) { - e = c55_lift_acc_alu(syntax); - } - } else if (!strcmp(in.mnem, "or")) { - e = c55_lift_bitop(C55_OR, &in); - if (!e) { - e = c55_lift_acc_alu(syntax); - } - } else if (!strcmp(in.mnem, "xor")) { - e = c55_lift_bitop(C55_XOR, &in); - if (!e) { - e = c55_lift_acc_alu(syntax); - } - } else if (!strcmp(in.mnem, "not")) { - e = c55_lift_not(&in); - } else if (!strcmp(in.mnem, "sfts")) { - e = c55_lift_shift(true, &in); - } else if (!strcmp(in.mnem, "sftl")) { - e = c55_lift_shift(false, &in); - } else if (!strcmp(in.mnem, "add")) { - e = c55_lift_arith16(C55_ADD, &in); - if (!e) { - e = c55_lift_acarith(C55_ADD, &in); - } - if (!e) { - e = c55_lift_acc_alu(syntax); - } - if (!e) { - e = c55_lift_dualmem(&in, false); - } - } else if (!strcmp(in.mnem, "sub")) { - e = c55_lift_arith16(C55_SUB, &in); - if (!e) { - e = c55_lift_acarith(C55_SUB, &in); - } - if (!e) { - e = c55_lift_acc_alu(syntax); - } - if (!e) { - e = c55_lift_dualmem(&in, true); - } - } else if (!strcmp(in.mnem, "neg")) { - e = c55_lift_neg(&in); - } else if (!strcmp(in.mnem, "abs")) { - e = c55_lift_minmaxabs(&in, 0); - } else if (!strcmp(in.mnem, "min")) { - e = c55_lift_minmaxabs(&in, 1); - } else if (!strcmp(in.mnem, "max")) { - e = c55_lift_minmaxabs(&in, 2); - } else if (!strcmp(in.mnem, "swap")) { - e = c55_lift_swap(&in); - } else if (!strcmp(in.mnem, "swapp")) { - e = c55_lift_swapp(&in, 2); - } else if (!strcmp(in.mnem, "swap4")) { - e = c55_lift_swapp(&in, 4); - } else if (!strcmp(in.mnem, "satr")) { - e = c55_lift_satr(&in); - } else if (!strcmp(in.mnem, "callcc")) { - e = c55_lift_callcc(syntax); - } else if ((!strcmp(in.mnem, "b") || !strcmp(in.mnem, "call")) && - in.n_ops == 1 && in.ops[0].kind == C55_OP_REG && in.ops[0].width == 40) { - // Register-indirect branch / call: transfer to the low 24 bits of the - // accumulator. The call return-linkage save is tracked separately. - e = JMP(CAST(24, IL_FALSE, VARG(in.ops[0].reg))); - } else if (!strcmp(in.mnem, "round")) { - e = c55_lift_round_sq(&in, 0); - } else if (!strcmp(in.mnem, "sqar")) { - e = c55_lift_round_sq(&in, 1); - } else if (!strcmp(in.mnem, "sqrr")) { - e = c55_lift_round_sq(&in, 2); - } else if (!strcmp(in.mnem, "bset")) { - e = c55_lift_bitset(&in, true); - if (!e) { - e = c55_lift_bitset_named(syntax, true); - } - } else if (!strcmp(in.mnem, "bclr")) { - e = c55_lift_bitset(&in, false); - if (!e) { - e = c55_lift_bitset_named(syntax, false); - } - } else if (!strcmp(in.mnem, "cmpand")) { - e = c55_lift_cmpbit(syntax, false, true); - } else if (!strcmp(in.mnem, "cmpandu")) { - e = c55_lift_cmpbit(syntax, true, true); - } else if (!strcmp(in.mnem, "cmpor")) { - e = c55_lift_cmpbit(syntax, false, false); - } else if (!strcmp(in.mnem, "cmporu")) { - e = c55_lift_cmpbit(syntax, true, false); - } else if (!strcmp(in.mnem, "rol")) { - e = c55_lift_rotate(syntax, true); - } else if (!strcmp(in.mnem, "ror")) { - e = c55_lift_rotate(syntax, false); - } else if (!strcmp(in.mnem, "psh")) { - e = c55_lift_stack(syntax, true); - } else if (!strcmp(in.mnem, "pop")) { - e = c55_lift_stack(syntax, false); - } else if (!strcmp(in.mnem, "amov")) { - e = c55_lift_addr(C55_AMOV, &in); - } else if (!strcmp(in.mnem, "aadd")) { - e = c55_lift_addr(C55_AADD, &in); - } else if (!strcmp(in.mnem, "asub")) { - e = c55_lift_addr(C55_ASUB, &in); - } else if (!strcmp(in.mnem, "amar")) { - e = c55_lift_amar(&in); - } else if (!strcmp(in.mnem, "rpt") || !strcmp(in.mnem, "rptcc") || - !strcmp(in.mnem, "rptb") || !strcmp(in.mnem, "rptblocal") || - !strcmp(in.mnem, "rptadd") || !strcmp(in.mnem, "rptsub")) { - // Repeat-control: sets a hardware loop counter / active state that is not - // part of the modelled register file and has no data-path effect, so it - // lifts to nop (the repeated body is lifted on its own). - e = NOP(); - } else if (!strncmp(in.mnem, "mpy", 3) || !strncmp(in.mnem, "mac", 3) || - !strncmp(in.mnem, "mas", 3)) { - e = c55_lift_mul(syntax); - } - return e; -} - -RZ_IPI RzAnalysisLiftedILOp tms320_c55x_plus_il_lift(RZ_NONNULL RzAnalysisOp *op, const char *syntax) { - rz_return_val_if_fail(op, NULL); - - // A parallel pair is decoded as two separate ops; the second carries a - // leading "||" marker. The architecture constrains the two slots of a pair - // to be independent (no slot reads what the other writes in the same cycle), - // so lifting the marked op on its own and letting the VM execute the pair in - // sequence preserves the result. Strip the marker and lift the inner op. - if (syntax) { - while (syntax[0] == '|' && syntax[1] == '|') { - syntax += 2; - while (*syntax == ' ') { - syntax++; - } - } - } - - // xcc / xccpart: a conditional-execution qualifier. With a parallel op - // ("xcc COND || OP") the op runs only when COND holds -- modelled as - // BRANCH(cond, OP, nop). Standalone ("xcc COND") the qualifier gates the - // *following* instruction, which per-instruction lifting cannot express, so - // it lifts to nop: the gated instruction is lifted on its own and the VM - // executes it unconditionally (a documented predication limitation). - if (syntax && (!strncmp(syntax, "xcc ", 4) || !strncmp(syntax, "xccpart ", 8))) { - const char *par = strstr(syntax, " || "); - if (!par) { - return NOP(); - } - const char *cstart = strchr(syntax, ' '); - while (*cstart == ' ') { - cstart++; - } - char cond_s[64]; - size_t cl = (size_t)(par - cstart); - if (cl < sizeof(cond_s)) { - memcpy(cond_s, cstart, cl); - cond_s[cl] = '\0'; - RzILOpBool *cond = strpbrk(cond_s, "=<>") ? c55_relexpr_bool(cond_s, false) : NULL; - if (!cond) { - const char *reg; - int bit; - if (c55_status_bit(cond_s, ®, &bit)) { - cond = c55_bit_bool(reg, bit); - } - } - RzAnalysisLiftedILOp body = cond ? c55_lift_data(par + 4) : NULL; - if (cond && body) { - return BRANCH(cond, body, NOP()); - } - rz_il_op_pure_free(cond); - rz_il_op_effect_free(body); - } - } - - // "mov #K << #SHIFT, ACx" carries a shifted-immediate token the structured - // operand parser cannot classify; match it on the syntax string first. - if (syntax) { - const char *par = strstr(syntax, " :: "); - if (par) { - // A "::" dual operation packs two independent data-path ops into one - // instruction; lift each half on its own and sequence them. Both must - // lift -- a half-modelled pair would drop an effect, so fall through - // to NULL otherwise. - char left[256], right[256]; - size_t ll = (size_t)(par - syntax); - const char *r = par + 4; - if (ll < sizeof(left) && strlen(r) < sizeof(right)) { - memcpy(left, syntax, ll); - left[ll] = '\0'; - rz_str_ncpy(right, r, sizeof(right)); - RzAnalysisLiftedILOp a = c55_lift_data(left); - RzAnalysisLiftedILOp b = a ? c55_lift_data(right) : NULL; - if (a && b) { - return SEQ2(a, b); - } - rz_il_op_effect_free(a); - rz_il_op_effect_free(b); - } - } else { - RzAnalysisLiftedILOp e = c55_lift_data(syntax); - if (e) { - return e; - } - } - } - - // Control-only forms via the analysis-resolved op fields. - switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) { - case RZ_ANALYSIS_OP_TYPE_NOP: - return NOP(); - case RZ_ANALYSIS_OP_TYPE_JMP: - return JMP(UN(24, (ut64)op->jump)); - case RZ_ANALYSIS_OP_TYPE_CJMP: { - // bcc/bccu: branch to op->jump when the condition holds, else fall - // through. Conditions are signed for bcc and unsigned for bccu. - bool is_unsigned = syntax && !strncmp(syntax, "bccu", 4); - RzILOpBool *cond = c55_lift_cond(syntax, is_unsigned); - if (!cond) { - return NULL; // condition form not modelled -> leave unlifted - } - return BRANCH(cond, JMP(UN(24, (ut64)op->jump)), NOP()); - } - case RZ_ANALYSIS_OP_TYPE_CALL: - // Models the control transfer to the callee. The return-address save - // (stack / RETA) is intentionally not modelled here; rizin's call/return - // analysis tracks linkage separately. - return JMP(UN(24, (ut64)op->jump)); - default: - return NULL; // not yet lifted - } -} - -#include - -/** IL configuration for C55x/C55x+: 24-bit program counter, little-endian. */ -RZ_IPI RzAnalysisILConfig *tms320_c55x_plus_il_config(RZ_NONNULL RzAnalysis *analysis) { - rz_return_val_if_fail(analysis, NULL); - RzAnalysisILConfig *cfg = rz_analysis_il_config_new(24, false, 24); - if (!cfg) { - return NULL; - } - cfg->reg_bindings = c55x_plus_il_regs; - return cfg; -} - -// ---- Plain C55x front-end --------------------------------------------------- -// -// The plain-C55x (C5500) disassembler emits the same integer-core semantics as -// C55x+ but with a different operand *syntax*: hex immediates use a trailing `h` -// (`#4Dh`) instead of a `0x` prefix, and accumulator halves are written -// `hi(acN)` / `lo(acN)` instead of `acN.h` / `acN.l`. Rather than duplicate the -// semantic lifter, the plain-C55x syntax is normalized to the C55x+ form and fed -// to the same `tms320_c55x_plus_il_lift`. Forms that do not normalize cleanly -// (register-indexed `*(arN+tM)`, shifted memory `*arN << #k`, etc.) simply fail -// to parse there and yield NULL, preserving the correct-or-NULL contract. - -static bool c55x_is_hex(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -RZ_IPI void tms320_c55x_normalize_syntax(const char *in, char *out, size_t outsz) { - size_t o = 0; - if (outsz == 0) { - return; - } - for (size_t i = 0; in && in[i] && o + 1 < outsz;) { - char c = in[i]; - // hi(acN) / lo(acN) -> acN.h / acN.l - if ((!strncmp(in + i, "hi(", 3) || !strncmp(in + i, "lo(", 3))) { - char half = (in[i] == 'h') ? 'h' : 'l'; - const char *q = in + i + 3; - const char *close = strchr(q, ')'); - // only rewrite a simple register operand (no nested parens) - if (close && !memchr(q, '(', close - q)) { - size_t inner = (size_t)(close - q); - if (o + inner + 2 < outsz) { - memcpy(out + o, q, inner); - o += inner; - out[o++] = '.'; - out[o++] = half; - i = (size_t)(close - in) + 1; - continue; - } - } - } - // #h -> #0x (immediate with TI 'h' hex suffix) - if (c == '#') { - size_t j = i + 1; - while (in[j] && c55x_is_hex(in[j])) { - j++; - } - // require at least one hex digit, a trailing 'h', and a delimiter after - if (j > i + 1 && in[j] == 'h' && !c55x_is_hex(in[j + 1])) { - size_t ndig = j - (i + 1); - if (o + ndig + 3 < outsz) { - out[o++] = '#'; - out[o++] = '0'; - out[o++] = 'x'; - for (size_t k = i + 1; k < j; k++) { - char d = in[k]; - out[o++] = (d >= 'A' && d <= 'F') ? (char)(d - 'A' + 'a') : d; - } - i = j + 1; // skip the 'h' - continue; - } - } - } - out[o++] = c; - i++; - } - out[o] = '\0'; -} - -/** Lift one decoded plain-C55x op to IL by normalizing its syntax to the C55x+ - * form and reusing the shared semantic lifter. A NULL syntax still lifts the - * control-only forms (JMP/CALL/NOP) from the analysis-resolved op fields. */ -RZ_IPI RzAnalysisLiftedILOp tms320_c55x_il_lift(RZ_NONNULL RzAnalysisOp *op, const char *syntax) { - if (!syntax) { - return tms320_c55x_plus_il_lift(op, NULL); - } - char norm[512]; - tms320_c55x_normalize_syntax(syntax, norm, sizeof(norm)); - return tms320_c55x_plus_il_lift(op, norm); -} diff --git a/librz/arch/isa/tms320/c55x_plus/c55plus_il_config.c b/librz/arch/isa/tms320/c55x_plus/c55plus_il_config.c new file mode 100644 index 0000000000..e868fe64fb --- /dev/null +++ b/librz/arch/isa/tms320/c55x_plus/c55plus_il_config.c @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2026 RizinOrg +// SPDX-License-Identifier: LGPL-3.0-only + +#include + +#include "c55plus_analysis.h" + +// IL-VM register bindings for the TMS320C55x and C55x+ cores. The lifter emits +// SET/VAR ops that name these registers; the binding list tells the RzIL VM +// which variables to materialise. C55x+ extends the C55x file with the high +// accumulators (ac8-31) and the high address registers (ar8-15 / xar8-15). +static const char *c55x_il_regs[] = { + "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ac6", "ac7", + "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", + "xar0", "xar1", "xar2", "xar3", "xar4", "xar5", "xar6", "xar7", + "t0", "t1", "t2", "t3", + "sp", "ssp", "dp", "dph", "sph", "pdp", "pc", + "cdp", "cdph", "xcdp", "csr", "rptc", + "brc0", "brc1", "brs1", "trn0", "trn1", + "bk03", "bk47", "bkc", + "bsa01", "bsa23", "bsa45", "bsa67", "bsac", + "st0_55", "st1_55", "st2_55", "st3_55", + NULL +}; +static const char *c55x_plus_il_regs[] = { + "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ac6", "ac7", + "ac8", "ac9", "ac10", "ac11", "ac12", "ac13", "ac14", "ac15", + "ac16", "ac17", "ac18", "ac19", "ac20", "ac21", "ac22", "ac23", + "ac24", "ac25", "ac26", "ac27", "ac28", "ac29", "ac30", "ac31", + "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", + "ar8", "ar9", "ar10", "ar11", "ar12", "ar13", "ar14", "ar15", + "xar0", "xar1", "xar2", "xar3", "xar4", "xar5", "xar6", "xar7", + "xar8", "xar9", "xar10", "xar11", "xar12", "xar13", "xar14", "xar15", + "t0", "t1", "t2", "t3", + "sp", "ssp", "dp", "dph", "sph", "pdp", "pc", + "cdp", "cdph", "xcdp", "csr", "rptc", + "brc0", "brc1", "brs1", "trn0", "trn1", + "bk03", "bk47", "bkc", + "bsa01", "bsa23", "bsa45", "bsa67", "bsac", + "st0_55", "st1_55", "st2_55", "st3_55", + NULL +}; + +RZ_IPI RzAnalysisILConfig *tms320_c55x_plus_il_config(RZ_NONNULL RzAnalysis *analysis) { + rz_return_val_if_fail(analysis, NULL); + RzAnalysisILConfig *cfg = rz_analysis_il_config_new(24, false, 24); + if (!cfg) { + return NULL; + } + const char *cpu = rz_analysis_get_cpu(analysis); + bool plus = cpu && rz_str_casecmp(cpu, "c55x+") == 0; + cfg->reg_bindings = plus ? c55x_plus_il_regs : c55x_il_regs; + return cfg; +} diff --git a/librz/arch/isa/tms320/c55x_plus/decode.h b/librz/arch/isa/tms320/c55x_plus/decode.h deleted file mode 100644 index 639ad5f54e..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/decode.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef DECODE_H -#define DECODE_H - -#include - -char *decode(ut32 ins_pos, ut32 *next_ins_pos); - -#ifndef USE_DECODE - -static bool is_linear_circular(ut32 ins_bits); -static bool is_hash(st32 hash_code); -static bool check_arg(ut32 ins_bits, int *err_code); - -static ut32 get_ins_bits(ut32 hash_code, ut32 ins_pos, char *ins, ut32 ins_len, ut32 magic_value, int *err_code); -static ut32 get_q_bits(ut32 val, char *ins, ut32 ins_len, int *err_code); - -static char *do_decode(ut32 ins_off, ut32 ins_pos, ut32 two_ins, ut32 *next_ins_pos, st32 *ins_hash_code, int *err_code); -static char *decode_ins(st32 hash_code, ut32 ins_pos, ut32 ins_off, ut32 *ins_len_dec, ut32 *reg_len_dec, ut32 *ret_ins_bits, ut32 magic_value, ut8 two_ins, int *err_code); - -static char *decode_regis(char *reg_arg, st32 hash_code, ut32 ins_bits, ut32 *ret_ins_bits, int *err_code); - -static char *get_token_decoded(st32 hash_code, char *ins_token, ut32 ins_token_len, char *reg_arg, ut32 *ret_ins_bits, ut32 *ret_reg_len, ut32 magic_value, ut32 ins_pos, ut32 ins_len, ut8 two_ins, int *err_code); - -#endif - -#endif diff --git a/librz/arch/isa/tms320/c55x_plus/decode_funcs.c b/librz/arch/isa/tms320/c55x_plus/decode_funcs.c deleted file mode 100644 index 4b0cd0a673..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/decode_funcs.c +++ /dev/null @@ -1,1025 +0,0 @@ -// SPDX-FileCopyrightText: 2013 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include -#include -#include "ins.h" - -char *get_tc2_tc1(ut32 ins_bits) { - char *res = "tc1"; - if (ins_bits) { - if (ins_bits != 1) { - fprintf(stderr, "Invalid instruction TC2 or TC1 (%d)\n", ins_bits); - return NULL; - } - res = "tc2"; - } - return rz_str_dup(res); -} - -/* Decode the 'V'/'VV' template field used by ROL/ROR/and a handful of - * other algebraic forms in SWPU104. The same 1-bit slot encodes either - * the carry-flag or the TC2 condition register, depending on the - * surrounding instruction. dis55.exe v4.3.6 calls this function - * 'TC2_or_Carry' internally; matching that name keeps the cross- - * reference obvious for anyone tracing a divergence. - * - * bit = 0 -> "Carry" (the C bit of ST0_55) - * bit = 1 -> "TC2" (the TC2 bit of ST0_55) - * - * For ROL/ROR specifically the field is always 0 in the assembler - * output (no syntax exists for the TC2 alternative) so the operand - * comes out as 'Carry' in the disassembled form. */ -char *get_tc2_or_carry(ut32 ins_bits) { - char *res = "Carry"; - if (ins_bits) { - if (ins_bits != 1) { - fprintf(stderr, "Invalid instruction TC2 or Carry (%d)\n", ins_bits); - return NULL; - } - res = "TC2"; - } - return rz_str_dup(res); -} - -char *get_trans_reg(ut32 ins_bits) { - char *res = NULL; - - switch (ins_bits) { - case 6: - res = "trn0"; - break; - case 7: - res = "trn1"; - break; - case 4: - res = "trn2"; - break; - case 5: - res = "trn3"; - break; - case 2: - res = "trn4"; - break; - case 3: - res = "trn5"; - break; - case 0: - res = "trn6"; - break; - case 1: - res = "trn7"; - break; - - default: - fprintf(stderr, "Invalid transaction instruction 0x%x\n", ins_bits); - } - return rz_str_dup(res); -} - -char *get_AR_regs_class1(ut32 ins_bits) { - const ut32 op = (ins_bits >> 4) & 7; - const long n = (long int)ins_bits & 0xF; - switch (op) { - case 0: return rz_str_newf("*ar%ld-", n); - case 1: return rz_str_newf("*ar%ld+", n); - case 2: return rz_str_newf("*ar%ld(t0)", n); - case 3: return rz_str_newf("*ar%ld", n); - case 4: return rz_str_newf("*(ar%ld-t0)", n); - case 5: return rz_str_newf("*(ar%ld-t1)", n); - case 6: return rz_str_newf("*(ar%ld+t0)", n); - case 7: return rz_str_newf("*(ar%ld+t1)", n); - } - return NULL; -} - -char *get_AR_regs_class2(ut32 ins_bits, ut32 *ret_len, ut32 ins_pos, ut32 idx) { - const ut8 op = ins_bits >> 6; - const ut8 op2 = ins_bits & 3; - const long reg_num = (ins_bits >> 2) & 0xF; - const ut32 scale = idx; /* data-size scale for offset operands (1=word) */ - - if (ret_len) { - *ret_len = 0; - } - if (op2 == 2) { - if (op) { - return rz_str_newf("*ar%ld(short(#0x%lx))", reg_num, (long int)idx * op); - } - return rz_str_newf("*ar%ld", reg_num); - } - - ut8 type = (op >> 3 | 2 * op2); - if (type == 6) { - return rz_str_newf("@#0x%lx", (long int)idx * (reg_num | 16 * (op & 7))); - } else if (type == 7) { - return rz_str_newf("*sp(#0x%lx)", (long int)idx * (reg_num | 16 * (op & 7))); - } - - /* Reaching here, op2 (the 2-bit mm field) is 0 or 1: register-bit - * indirect addressing. The mode index is the 4-bit MMMM field (op); - * mm==1 selects the extended bank (t0b / t2 / t3 / xar15 / k16/k24 - * offsets), i.e. mode + 16. The earlier code multiplied the wrong - * field (idx, an operand-scaling parameter) by 16, which mapped e.g. - * "*arN+" (MMMM=1, mm=0) onto "*(arN+t0b)" (type 17) and emitted an - * empty operand for the MMMM>=2 indexed modes. */ - type = op | (op2 == 1 ? 16 : 0); - switch (type) { - case 0: return rz_str_newf("*ar%ld-", reg_num); - case 1: return rz_str_newf("*ar%ld+", reg_num); - case 2: return rz_str_newf("*ar%ld(t0)", reg_num); - case 3: return rz_str_newf("*ar%ld(t1)", reg_num); - case 4: return rz_str_newf("*(ar%ld-t0)", reg_num); - case 5: return rz_str_newf("*(ar%ld-t1)", reg_num); - case 6: return rz_str_newf("*(ar%ld+t0)", reg_num); - case 7: return rz_str_newf("*(ar%ld+t1)", reg_num); - case 8: return rz_str_newf("*-ar%ld", reg_num); - case 9: return rz_str_newf("*+ar%ld", reg_num); - case 10: return rz_str_newf("*ar%ld(t2)", reg_num); - case 11: return rz_str_newf("*ar%ld(t3)", reg_num); - case 12: return rz_str_newf("*(ar%ld-t2)", reg_num); - case 13: return rz_str_newf("*(ar%ld-t3)", reg_num); - case 14: return rz_str_newf("*(ar%ld+t2)", reg_num); - case 15: return rz_str_newf("*(ar%ld+t3)", reg_num); - case 16: return rz_str_newf("*(ar%ld-t0b)", reg_num); - case 17: return rz_str_newf("*(ar%ld+t0b)", reg_num); - case 18: return rz_str_newf("*ar%ld(t0<<#1)", reg_num); - case 19: return rz_str_newf("*ar%ld(t1<<#1)", reg_num); - case 23: return rz_str_newf("*ar%ld(xar15)", reg_num); - case 24: - case 25: - case 26: - case 27: - idx = get_ins_part(ins_pos, 2); - if (ret_len) { - *ret_len = 2; - } - switch (type) { - case 24: return rz_str_newf("*ar%ld(#%ld)", reg_num, (long int)scale * idx); - case 25: return rz_str_newf("*+ar%ld(#%ld)", reg_num, (long int)scale * idx); - case 26: return rz_str_newf("*abs16(#0x%lx)", (long int)idx); - default: return rz_str_newf("*port(#0x%lx)", (long int)idx); - } - case 28: - case 29: - case 30: - idx = get_ins_part(ins_pos, 3); - if (ret_len) { - *ret_len = 3; - } - switch (type) { - case 28: return rz_str_newf("*ar%ld(#0x%lx)", reg_num, (long int)idx * scale); - case 29: return rz_str_newf("*+ar%ld(#0x%lx)", reg_num, (long int)idx * scale); - default: return rz_str_newf("*(#0x%lx)", (long int)idx); - } - } - - return NULL; -} - -char *get_reg_pair(ut32 idx) { - char *res = NULL; - - switch (idx) { - case 1: res = "ac0, ac2"; break; - case 2: res = "ac1, ac3"; break; - case 3: res = "pair(ac0), pair(ac2)"; break; - case 4: res = "ar0, ar1"; break; - case 5: res = "ar0, ar2"; break; - case 6: res = "ar1, ar3"; break; - case 7: res = "pair(ar0), pair(ar2)"; break; - case 9: res = "t0, t2"; break; - case 10: res = "t1, t3"; break; - case 11: res = "pair(t0), pair(t2)"; break; - case 21: res = "ar4, t0"; break; - case 22: res = "ar5, t1"; break; - case 23: res = "pair(ar4), pair(t0)"; break; - case 25: res = "ar6, t2"; break; - case 26: res = "ar7, t3"; break; - case 27: res = "pair(ar6), pair(t2)"; break; - case 31: res = "block(ar4), block(t0)"; break; - default: res = NULL; - } - - if (res != NULL) { - res = rz_str_dup(res); - } - - return res; -} - -char *get_reg_name_3(ut32 idx) { - char *res = NULL; - - switch (idx) { - case 0: res = "ac0"; break; - case 1: res = "ac1"; break; - case 2: res = "ac2"; break; - case 3: res = "ac3"; break; - case 4: res = "ac4"; break; - case 5: res = "ac5"; break; - case 6: res = "ac6"; break; - case 7: res = "ac7"; break; - case 8: res = "ac8"; break; - case 9: res = "ac9"; break; - case 10: res = "ac10"; break; - case 11: res = "ac11"; break; - case 12: res = "ac12"; break; - case 13: res = "ac13"; break; - case 14: res = "ac14"; break; - case 15: res = "ac15"; break; - /* Indices 16-31 ARE real accumulators on this C55x+ silicon variant: - * TI's own asm55/dis55 round-trip them (e.g. `MOV #0, AC31.L` <-> 7bff00, - * `MOV #0, AC16.L` <-> 7bf000). The generic SWPU086 C55x+ has only - * AC0-AC15, but this variant extends the accumulator file to AC0-AC31; - * omitting them produced disassembly with empty/dangling operands. */ - case 16: res = "ac16"; break; - case 17: res = "ac17"; break; - case 18: res = "ac18"; break; - case 19: res = "ac19"; break; - case 20: res = "ac20"; break; - case 21: res = "ac21"; break; - case 22: res = "ac22"; break; - case 23: res = "ac23"; break; - case 24: res = "ac24"; break; - case 25: res = "ac25"; break; - case 26: res = "ac26"; break; - case 27: res = "ac27"; break; - case 28: res = "ac28"; break; - case 29: res = "ac29"; break; - case 30: res = "ac30"; break; - case 31: res = "ac31"; break; - case 32: res = "xar0"; break; - case 33: res = "xar1"; break; - case 34: res = "xar2"; break; - case 35: res = "xar3"; break; - case 36: res = "xar4"; break; - case 37: res = "xar5"; break; - case 38: res = "xar6"; break; - case 39: res = "xar7"; break; - case 40: res = "xar8"; break; - case 41: res = "xar9"; break; - case 42: res = "xar10"; break; - case 43: res = "xar11"; break; - case 44: res = "xar12"; break; - case 45: res = "xar13"; break; - case 46: res = "xar14"; break; - case 47: res = "xar15"; break; - case 52: res = "xssp"; break; - case 53: res = "xsp"; break; - case 54: res = "xdp"; break; - default: res = NULL; - } - - if (res != NULL) { - res = rz_str_dup(res); - } - return res; -} - -char *get_reg_name_2(ut32 idx) { - char *res = NULL; - - switch (idx) { - case 0: res = "ar0"; break; - case 1: res = "ar1"; break; - case 2: res = "ar2"; break; - case 3: res = "ar3"; break; - case 4: res = "ar4"; break; - case 5: res = "ar5"; break; - case 6: res = "ar6"; break; - case 7: res = "ar7"; break; - case 8: res = "ar8"; break; - case 9: res = "ar9"; break; - case 10: res = "ar10"; break; - case 11: res = "ar11"; break; - case 12: res = "ar12"; break; - case 13: res = "ar13"; break; - case 14: res = "ar14"; break; - case 15: res = "ar15"; break; - case 16: res = "t0"; break; - case 17: res = "t1"; break; - case 18: res = "t2"; break; - case 19: res = "t3"; break; - case 20: res = "ssp"; break; - case 21: res = "sp"; break; - case 22: res = "dp"; break; - case 32: res = "xar0"; break; - case 33: res = "xar1"; break; - case 34: res = "xar2"; break; - case 35: res = "xar3"; break; - case 36: res = "xar4"; break; - case 37: res = "xar5"; break; - case 38: res = "xar6"; break; - case 39: res = "xar7"; break; - case 40: res = "xar8"; break; - case 41: res = "xar9"; break; - case 42: res = "xar10"; break; - case 43: res = "xar11"; break; - case 44: res = "xar12"; break; - case 45: res = "xar13"; break; - case 46: res = "xar14"; break; - case 47: res = "xar15"; break; - case 52: res = "xssp"; break; - case 53: res = "xsp"; break; - case 54: res = "xdp"; break; - default: res = NULL; - } - - if (res != NULL) { - res = rz_str_dup(res); - } - - return res; -} - -char *get_reg_name_1(ut32 idx) { - char *res = NULL; - - switch (idx) { - case 0: res = "ac0"; break; - case 1: res = "ac1"; break; - case 2: res = "ac2"; break; - case 3: res = "ac3"; break; - case 4: res = "ac4"; break; - case 5: res = "ac5"; break; - case 6: res = "ac6"; break; - case 7: res = "ac7"; break; - case 8: res = "ac8"; break; - case 9: res = "ac9"; break; - case 10: res = "ac10"; break; - case 11: res = "ac11"; break; - case 12: res = "ac12"; break; - case 13: res = "ac13"; break; - case 14: res = "ac14"; break; - case 15: res = "ac15"; break; - /* Indices 16-31 ARE real accumulators on this C55x+ silicon variant - * (TI asm55/dis55 round-trip AC16-AC31; e.g. 7bff00 = MOV #0, AC31.L). - * Generic SWPU086 C55x+ has AC0-AC15; this variant extends to AC0-AC31. */ - case 16: res = "ac16"; break; - case 17: res = "ac17"; break; - case 18: res = "ac18"; break; - case 19: res = "ac19"; break; - case 20: res = "ac20"; break; - case 21: res = "ac21"; break; - case 22: res = "ac22"; break; - case 23: res = "ac23"; break; - case 24: res = "ac24"; break; - case 25: res = "ac25"; break; - case 26: res = "ac26"; break; - case 27: res = "ac27"; break; - case 28: res = "ac28"; break; - case 29: res = "ac29"; break; - case 30: res = "ac30"; break; - case 31: res = "ac31"; break; - case 32: res = "ar0"; break; - case 33: res = "ar1"; break; - case 34: res = "ar2"; break; - case 35: res = "ar3"; break; - case 36: res = "ar4"; break; - case 37: res = "ar5"; break; - case 38: res = "ar6"; break; - case 39: res = "ar7"; break; - case 40: res = "ar8"; break; - case 41: res = "ar9"; break; - case 42: res = "ar10"; break; - case 43: res = "ar11"; break; - case 44: res = "ar12"; break; - case 45: res = "ar13"; break; - case 46: res = "ar14"; break; - case 47: res = "ar15"; break; - case 48: res = "t0"; break; - case 49: res = "t1"; break; - case 50: res = "t2"; break; - case 51: res = "t3"; break; - case 52: res = "ssp"; break; - case 53: res = "sp"; break; - case 54: res = "dp"; break; - case 56: res = "csr"; break; - case 57: res = "rptc"; break; - case 58: res = "brc0"; break; - case 59: res = "brc1"; break; - case 62: res = "config"; break; - case 63: res = "cpurev"; break; - case 64: res = "ac0.h"; break; - case 65: res = "ac1.h"; break; - case 66: res = "ac2.h"; break; - case 67: res = "ac3.h"; break; - case 68: res = "ac4.h"; break; - case 69: res = "ac5.h"; break; - case 70: res = "ac6.h"; break; - case 71: res = "ac7.h"; break; - case 72: res = "ac8.h"; break; - case 73: res = "ac9.h"; break; - case 74: res = "ac10.h"; break; - case 75: res = "ac11.h"; break; - case 76: res = "ac12.h"; break; - case 77: res = "ac13.h"; break; - case 78: res = "ac14.h"; break; - case 79: res = "ac15.h"; break; - /* AC16-AC31 high halves (this variant's extended accumulators). */ - case 80: res = "ac16.h"; break; - case 81: res = "ac17.h"; break; - case 82: res = "ac18.h"; break; - case 83: res = "ac19.h"; break; - case 84: res = "ac20.h"; break; - case 85: res = "ac21.h"; break; - case 86: res = "ac22.h"; break; - case 87: res = "ac23.h"; break; - case 88: res = "ac24.h"; break; - case 89: res = "ac25.h"; break; - case 90: res = "ac26.h"; break; - case 91: res = "ac27.h"; break; - case 92: res = "ac28.h"; break; - case 93: res = "ac29.h"; break; - case 94: res = "ac30.h"; break; - case 95: res = "ac31.h"; break; - case 96: res = "ac0.l"; break; - case 97: res = "ac1.l"; break; - case 98: res = "ac2.l"; break; - case 99: res = "ac3.l"; break; - case 100: res = "ac4.l"; break; - case 101: res = "ac5.l"; break; - case 102: res = "ac6.l"; break; - case 103: res = "ac7.l"; break; - case 104: res = "ac8.l"; break; - case 105: res = "ac9.l"; break; - case 106: res = "ac10.l"; break; - case 107: res = "ac11.l"; break; - case 108: res = "ac12.l"; break; - case 109: res = "ac13.l"; break; - case 110: res = "ac14.l"; break; - case 111: res = "ac15.l"; break; - /* AC16-AC31 low halves (this variant's extended accumulators). */ - case 112: res = "ac16.l"; break; - case 113: res = "ac17.l"; break; - case 114: res = "ac18.l"; break; - case 115: res = "ac19.l"; break; - case 116: res = "ac20.l"; break; - case 117: res = "ac21.l"; break; - case 118: res = "ac22.l"; break; - case 119: res = "ac23.l"; break; - case 120: res = "ac24.l"; break; - case 121: res = "ac25.l"; break; - case 122: res = "ac26.l"; break; - case 123: res = "ac27.l"; break; - case 124: res = "ac28.l"; break; - case 125: res = "ac29.l"; break; - case 126: res = "ac30.l"; break; - case 127: res = "ac31.l"; break; - case 128: res = "xar0"; break; - case 129: res = "xar1"; break; - case 130: res = "xar2"; break; - case 131: res = "xar3"; break; - case 132: res = "xar4"; break; - case 133: res = "xar5"; break; - case 134: res = "xar6"; break; - case 135: res = "xar7"; break; - case 136: res = "xar8"; break; - case 137: res = "xar9"; break; - case 138: res = "xar10"; break; - case 139: res = "xar11"; break; - case 140: res = "xar12"; break; - case 141: res = "xar13"; break; - case 142: res = "xar14"; break; - case 143: res = "xar15"; break; - case 148: res = "xssp"; break; - case 149: res = "xsp"; break; - case 150: res = "xdp"; break; - case 152: res = "rsa0"; break; - case 153: res = "rsa1"; break; - case 154: res = "rea0"; break; - case 155: res = "rea1"; break; - case 156: res = "dbgpaddr"; break; - case 157: res = "dbgpdata"; break; - case 159: res = "reta"; break; - case 160: res = "xar0.h"; break; - case 161: res = "xar1.h"; break; - case 162: res = "xar2.h"; break; - case 163: res = "xar3.h"; break; - case 164: res = "xar4.h"; break; - case 165: res = "xar5.h"; break; - case 166: res = "xar6.h"; break; - case 167: res = "xar7.h"; break; - case 168: res = "xar8.h"; break; - case 169: res = "xar9.h"; break; - case 170: res = "xar10.h"; break; - case 171: res = "xar11.h"; break; - case 172: res = "xar12.h"; break; - case 173: res = "xar13.h"; break; - case 174: res = "xar14.h"; break; - case 175: res = "xar15.h"; break; - case 180: res = "xssp.h"; break; - case 181: res = "xsp.h"; break; - case 182: res = "xdp.h"; break; - case 183: res = "pdp"; break; - case 184: res = "bsa01"; break; - case 185: res = "bsa23"; break; - case 186: res = "bsa45"; break; - case 187: res = "bsa67"; break; - case 188: res = "bsac"; break; - case 189: // res = (char *)&off_42FBE8; - res = "bkc"; - break; - case 190: res = "bk03"; break; - case 191: res = "bk47"; break; - case 192: res = "ac0.g"; break; - case 193: res = "ac1.g"; break; - case 194: res = "ac2.g"; break; - case 195: res = "ac3.g"; break; - case 196: res = "ac4.g"; break; - case 197: res = "ac5.g"; break; - case 198: res = "ac6.g"; break; - case 199: res = "ac7.g"; break; - case 200: res = "ac8.g"; break; - case 201: res = "ac9.g"; break; - case 202: res = "ac10.g"; break; - case 203: res = "ac11.g"; break; - case 204: res = "ac12.g"; break; - case 205: res = "ac13.g"; break; - case 206: res = "ac14.g"; break; - case 207: res = "ac15.g"; break; - case 224: res = "st0"; break; - case 225: res = "st1"; break; - case 226: res = "st2"; break; - case 227: res = "st3"; break; - case 228: res = "st0_55"; break; - case 229: res = "st1_55"; break; - case 231: res = "st3_55"; break; - case 232: res = "ier0"; break; - case 233: res = "ier1"; break; - case 234: res = "ifr0"; break; - case 235: res = "ifr1"; break; - case 236: res = "dbier0"; break; - case 237: res = "dbier1"; break; - case 238: res = "ivpd"; break; - case 239: res = "ivph"; break; - case 240: res = "rsa0.h"; break; - case 241: res = "rsa1.h"; break; - case 242: res = "rea0.h"; break; - case 243: res = "rea1.h"; break; - case 244: res = "bios"; break; - case 245: res = "brs1"; break; - case 246: res = "iir"; break; - case 247: res = "ber"; break; - case 248: res = "rsa0.l"; break; - case 249: res = "rsa1.l"; break; - case 250: res = "rea0.l"; break; - case 251: res = "rea1.l"; break; - case 252: res = "tsdr"; break; - default: res = NULL; - } - - if (res != NULL) { - res = rz_str_dup(res); - } - - return res; -} - -char *get_status_regs_and_bits(char *reg_arg, int reg_bit) { - char *res = NULL; - if (!strncmp(reg_arg, "ST0", 3)) { - switch (reg_bit) { - case 0: - res = "st0_dp07"; - break; - case 1: - res = "st0_dp08"; - break; - case 2: - res = "st0_dp09"; - break; - case 3: - res = "st0_dp10"; - break; - case 4: - res = "st0_dp11"; - break; - case 5: - res = "st0_dp12"; - break; - case 6: - res = "st0_dp13"; - break; - case 7: - res = "st0_dp14"; - break; - case 8: - res = "st0_dp15"; - break; - case 9: - res = "st0_acov1"; - break; - case 10: - res = "st0_acov0"; - break; - case 11: - res = "st0_carry"; - break; - case 12: - res = "st0_tc2"; - break; - case 13: - res = "st0_tc1"; - break; - case 14: - res = "st0_acov3"; - break; - case 15: - res = "st0_acov2"; - break; - } - } else if (!strncmp(reg_arg, "ST1", 3)) { - switch (reg_bit) { - case 0: - res = "st1_dr2_00"; - break; - case 1: - res = "st1_dr2_01"; - break; - case 2: - res = "st1_dr2_02"; - break; - case 3: - res = "st1_dr2_03"; - break; - case 4: - res = "st1_dr2_04"; - break; - case 5: - res = "st1_c54cm"; - break; - case 6: - res = "st1_frct"; - break; - case 7: - res = "st1_c16"; - break; - case 8: - res = "st1_sxmd"; - break; - case 9: - res = "st1_satd"; - break; - case 10: - res = "st1_m40"; - break; - case 11: - res = "st1_intm"; - break; - case 12: - res = "st1_hm"; - break; - case 13: - res = "st1_xf"; - break; - case 14: - res = "st1_cpl"; - break; - case 15: - res = "st1_braf"; - break; - } - } else if (!strncmp(reg_arg, "ST2", 3)) { - switch (reg_bit) { - case 0: - res = "st2_ar0lc"; - break; - case 1: - res = "st2_ar1lc"; - break; - case 2: - res = "st2_ar2lc"; - break; - case 3: - res = "st2_ar3lc"; - break; - case 4: - res = "st2_ar4lc"; - break; - case 5: - res = "st2_ar5lc"; - break; - case 6: - res = "st2_ar6lc"; - break; - case 7: - res = "st2_ar7lc"; - break; - case 8: - res = "st2_cdplc"; - break; - case 9: - res = "st2_govf"; - break; - case 10: - res = "st2_rdm"; - break; - case 11: - res = "st2_eallow"; - break; - case 12: - res = "st2_dbgm"; - break; - case 13: - res = "st2_xcnd"; - break; - case 14: - res = "st2_xcna"; - break; - case 15: - res = "st2_arms"; - break; - } - } else if (!strncmp(reg_arg, "ST3", 3)) { - switch (reg_bit) { - case 0: - res = "st3_sst"; - break; - case 1: - res = "st3_smul"; - break; - case 2: - res = "st3_clkoff"; - break; - case 3: - res = "st3_bptr"; - break; - case 4: - res = "st3_avis"; - break; - case 5: - res = "st3_sata"; - break; - case 6: - res = "st3_mpnmc"; - break; - case 7: - res = "st3_cberr"; - break; - case 8: - res = "st3_homp"; - break; - case 9: - res = "st3_homr"; - break; - case 10: - res = "st3_homx"; - break; - case 11: - res = "st3_homy"; - break; - case 12: - res = "st3_hint"; - break; - case 13: - res = "st3_caclr"; - break; - case 14: - res = "st3_caen"; - break; - case 15: - res = "st3_cafrz"; - break; - } - } - - if (res != NULL) { - res = rz_str_dup(res); - } - - return res; -} - -char *get_reg_name_4(ut32 idx) { - char *res = NULL; - - switch (idx) { - case 0: - res = "ac0"; - break; - case 1: - res = "ac1"; - break; - case 2: - res = "ac2"; - break; - case 3: - res = "ac3"; - break; - case 4: - res = "ac4"; - break; - case 5: - res = "ac5"; - break; - case 6: - res = "ac6"; - break; - case 7: - res = "ac7"; - break; - case 8: - res = "t0"; - break; - case 9: - res = "t1"; - break; - case 10: - res = "t2"; - break; - case 11: - res = "t3"; - break; - case 16: - res = "ar0"; - break; - case 17: - res = "ar1"; - break; - case 18: - res = "ar2"; - break; - case 19: - res = "ar3"; - break; - case 20: - res = "ar4"; - break; - case 21: - res = "ar5"; - break; - case 22: - res = "ar6"; - break; - case 23: - res = "ar7"; - break; - case 24: - res = "ac0.l"; - break; - case 25: - res = "ac1.l"; - break; - case 26: - res = "ac2.l"; - break; - case 27: - res = "ac3.l"; - break; - case 28: - res = "ac4.l"; - break; - case 29: - res = "ac5.l"; - break; - case 30: - res = "ac6.l"; - break; - case 31: - res = "ac7.l"; - break; - } - return rz_str_dup(res); -} - -char *get_opers(ut8 oper_byte) { - switch (oper_byte) { - case 0xE0u: - return rz_str_dup("overflow(ac0)"); - case 0xE1u: - return rz_str_dup("overflow(ac1)"); - case 0xE2u: - return rz_str_dup("overflow(ac2)"); - case 0xE3u: - return rz_str_dup("overflow(ac3)"); - case 0xE4u: - return rz_str_dup("tc1"); - case 0xE5u: - return rz_str_dup("tc2"); - case 0xE6u: - return rz_str_dup("carry"); - case 0xE7u: - return rz_str_dup("overflow(govf)"); - case 0xE8u: - return rz_str_dup("tc1 & tc2"); - case 0xE9u: - return rz_str_dup("tc1 & !tc2"); - case 0xEAu: - return rz_str_dup("!tc1 & tc2"); - case 0xEBu: - return rz_str_dup("!tc1 & !tc2"); - case 0xECu: - return rz_str_dup("word_mode"); - case 0xEDu: - return rz_str_dup("byte_mode"); - case 0xF0u: - return rz_str_dup("!overflow(ac0)"); - case 0xF1u: - return rz_str_dup("!overflow(ac1)"); - case 0xF2u: - return rz_str_dup("!overflow(ac2)"); - case 0xF3u: - return rz_str_dup("!overflow(ac3)"); - case 0xF4u: - return rz_str_dup("!tc1"); - case 0xF5u: - return rz_str_dup("!tc2"); - case 0xF6u: - return rz_str_dup("!carry"); - case 0xF7u: - return rz_str_dup("!overflow(govf)"); - case 0xF8u: - return rz_str_dup("tc1 | tc2"); - case 0xF9u: - return rz_str_dup("tc1 | !tc2"); - case 0xFAu: - return rz_str_dup("!tc1 | tc2"); - case 0xFBu: - return rz_str_dup("!tc1 | !tc2"); - case 0xFCu: - return rz_str_dup("tc1 ^ tc2"); - case 0xFDu: - return rz_str_dup("tc1 ^ !tc2"); - case 0xFEu: - return rz_str_dup("!tc1 ^ tc2"); - case 0xFFu: - return rz_str_dup("!tc1 ^ !tc2"); - default: { - ut8 oper_type = oper_byte >> 5; - if (oper_type != 6) { - char *reg_name = get_reg_name_4(oper_byte & 0x1F); - switch (oper_type) { - case 1u: - return rz_str_append(reg_name, " != #0"); - case 0u: - return rz_str_append(reg_name, " == #0"); - case 2u: - return rz_str_append(reg_name, " < #0"); - case 3u: - return rz_str_append(reg_name, " >= #0"); - case 4u: - return rz_str_append(reg_name, " > #0"); - case 5u: - return rz_str_append(reg_name, " <= #0"); - default: - free(reg_name); - return NULL; - } - } - char *reg_name = get_reg_name_1((oper_byte & 0xF) + 128); - oper_type = (oper_byte >> 4) - 12; - if (oper_type) { - if (oper_type != 1) { - free(reg_name); - return NULL; - } - return rz_str_append(reg_name, " != #0"); - } else { - return rz_str_append(reg_name, " == #0"); - } - } - } -} - -char *get_cmp_op(ut32 idx) { - const char *res = NULL; - switch (idx) { - case 0: res = "=="; break; - case 1: res = "!="; break; - case 2: res = "<"; break; - case 3: res = ">="; break; - } - return rz_str_dup(res); -} - -char *get_sim_reg(char *reg_arg, ut32 ins_bits) { - st32 code; - char *res = NULL; - char *aux; - code = ins_bits & 3; - switch (code) { - case 0: - if (reg_arg && strchr(reg_arg, 'w')) { - if (code == 62) { - return rz_str_dup("sim0"); - } - if (code == 63) { - return rz_str_dup("sim0"); - } - } - aux = get_reg_name_1(ins_bits >> 2); - res = rz_str_prepend(aux, "@"); - break; - case 2: - res = rz_str_newf("@#0x%x", code); - break; - case 1: - case 3: - res = rz_str_dup(""); - break; - } - return res; -} diff --git a/librz/arch/isa/tms320/c55x_plus/decode_funcs.h b/librz/arch/isa/tms320/c55x_plus/decode_funcs.h deleted file mode 100644 index 0f7b94644b..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/decode_funcs.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef DECODE_FUNCS_H -#define DECODE_FUNCS_H - -#include - -char *get_tc2_tc1(ut32 ins_bits); -char *get_tc2_or_carry(ut32 ins_bits); -char *get_trans_reg(ut32 ins_bits); -char *get_AR_regs_class1(ut32 ins_bits); -char *get_AR_regs_class2(ut32 ins_bits, ut32 *ret_len, ut32 ins_pos, ut32 idx); -char *get_reg_pair(ut32 idx); -char *get_reg_name_3(ut32 idx); -char *get_reg_name_2(ut32 idx); -char *get_reg_name_1(ut32 idx); -char *get_status_regs_and_bits(char *reg_arg, ut32 reg_bit); -char *get_reg_name_4(ut32 idx); -char *get_opers(ut8 oper_byte); -char *get_cmp_op(ut32 idx); -char *get_sim_reg(char *reg_arg, ut32 ins_bits); - -#endif diff --git a/librz/arch/isa/tms320/c55x_plus/hashtable.c b/librz/arch/isa/tms320/c55x_plus/hashtable.c deleted file mode 100644 index 8b3bb9abf0..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/hashtable.c +++ /dev/null @@ -1,2838 +0,0 @@ -// SPDX-FileCopyrightText: 2013 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#include "ins.h" -#include "hashvector.h" -#include - -static const st32 hash_const_01 = 0x2474f685; -static const st32 hash_const_02 = 0x42fbc0b8; -static const st32 hash_const_03 = 0x086a18eb; -static const st32 hash_const_04 = 0x001d02e8; -static const st32 hash_const_05 = 0; - -extern ut8 *ins_buff; -extern ut32 ins_buff_len; - -st32 get_hashfunc_01(st32 arg1, st32 arg2) { - return arg1; -} - -st32 get_hashfunc_02(st32 arg1, st32 arg2) { - char v4 = 0; - st32 v2 = arg2 & 0xFE000000; - if ((arg2 & 0xFE000000u) > 0x72000000) { - if ((ut32)v2 <= 0xD8000000) { - if (v2 != 0xd8000000) { - if ((ut32)v2 > 0xC4000000) { - if ((ut32)v2 > 0xCE000000) { - if ((ut32)v2 > 0xD4000000) { - if (v2 == -704643072) { - return 95; - } - return arg1; - } - if (v2 != -738197504 && v2 != -805306368) { - if (v2 == -771751936) { - return 95; - } - return arg1; - } - } else { - if (v2 != -838860800) { - if ((ut32)v2 > 0xCA000000) { - if (v2 == -872415232) { - return 95; - } - return arg1; - } - if (v2 != -905969664 && v2 != -973078528) { - if (v2 == -939524096) { - return 95; - } - return arg1; - } - } - } - } else { - if (v2 != -1006632960) { - if ((ut32)v2 <= 0x7E000000) { - if (v2 != 2113929216) { - if ((ut32)v2 > 0x78000000) { - if (v2 != 2046820352 && v2 != 2080374784) { - return arg1; - } - } else { - if (v2 != 2013265920 && v2 != 1946157056) { - if (v2 == 1979711488) { - return 226; - } - return arg1; - } - } - } - return 226; - } - if ((ut32)v2 > 0xC0000000) { - if (v2 == -1040187392) { - return 95; - } - return arg1; - } - if (v2 != 0xC0000000) { - if (v2 == -1610612736) { - return 540; - } - if (v2 == -1577058304) { - return 541; - } - return arg1; - } - } - } - } - return 95; - } - if ((ut32)v2 > 0xEC000000) { - if ((ut32)v2 > 0xF6000000) { - if ((ut32)v2 > 0xFC000000) { - if (v2 != -33554432) { - return arg1; - } - return 96; - } - if (v2 == -67108864 || v2 == -134217728) { - return 96; - } - v4 = v2 == -100663296; - } else { - if (v2 == -167772160) { - return 96; - } - if ((ut32)v2 > 0xF2000000) { - v4 = v2 == -201326592; - } else { - if (v2 == -234881024 || v2 == -301989888) { - return 96; - } - v4 = v2 == -268435456; - } - } - } else { - if (v2 == -335544320) { - return 96; - } - if ((ut32)v2 > 0xE2000000) { - if ((ut32)v2 > 0xE8000000) { - v4 = v2 == -369098752; - } else { - if (v2 == -402653184 || v2 == -469762048) { - return 96; - } - v4 = v2 == -436207616; - } - } else { - if (v2 == -503316480) { - return 96; - } - if ((ut32)v2 <= 0xDE000000) { - if (v2 != -570425344 && v2 != -637534208 && v2 != -603979776) { - return arg1; - } - return 95; - } - v4 = v2 == -536870912; - } - } - if (!v4) { - return arg1; - } - return 96; - } - if ((arg2 & 0xFE000000) == 1912602624) { - return 226; - } - if ((ut32)v2 > 0x48000000) { - if ((ut32)v2 <= 0x5E000000) { - if (v2 != 1577058304) { - if ((ut32)v2 > 0x54000000) { - if ((ut32)v2 > 0x5A000000) { - if (v2 != 1543503872) { - return arg1; - } - } else { - if (v2 != 1509949440 && v2 != 1442840576) { - if (v2 == 1476395008) { - return 178; - } - return arg1; - } - } - } else { - if (v2 != 1409286144) { - if ((ut32)v2 > 0x4E000000) { - if (v2 != 1342177280) { - if (v2 == 1375731712) { - return 178; - } - return arg1; - } - } else { - if (v2 != 1308622848 && v2 != 1241513984) { - if (v2 == 1275068416) { - return 178; - } - return arg1; - } - } - } - } - } - return 178; - } - if ((ut32)v2 > 0x68000000) { - if ((ut32)v2 > 0x6E000000) { - if (v2 == 1879048192) { - return 226; - } - return arg1; - } - if (v2 != 1845493760 && v2 != 1778384896) { - if (v2 == 1811939328) { - return 226; - } - return arg1; - } - } else { - if (v2 != 1744830464) { - if ((ut32)v2 > 0x64000000) { - if (v2 == 1711276032) { - return 226; - } - return arg1; - } - if (v2 != 1677721600 && v2 != 1610612736) { - if (v2 == 1644167168) { - return 226; - } - return arg1; - } - } - } - return 226; - } - if (v2 == 1207959552) { - return 178; - } - if ((ut32)v2 <= 0x14000000) { - if (v2 != 335544320) { - if ((ut32)v2 > 0xA000000) { - if ((ut32)v2 > 0x10000000) { - if (v2 == 301989888) { - return 142; - } - return arg1; - } - if (v2 != 268435456 && v2 != 201326592) { - if (v2 == 234881024) { - return 142; - } - return arg1; - } - } else { - if (v2 != 167772160) { - if ((ut32)v2 > 0x4000000) { - if (v2 != 100663296) { - if (v2 == 134217728) { - return 142; - } - return arg1; - } - } else { - if (v2 != 67108864 && v2) { - if (v2 == 33554432) { - return 142; - } - return arg1; - } - } - } - } - } - return 142; - } - if ((ut32)v2 > 0x1E000000) { - if ((ut32)v2 > 0x44000000) { - if (v2 == 1174405120) { - return 178; - } - return arg1; - } - if (v2 != 1140850688 && v2 != 0x40000000) { - if (v2 == 1107296256) { - return 178; - } - return arg1; - } - return 178; - } - if (v2 == 503316480) { - return 142; - } - if ((ut32)v2 <= 0x1A000000) { - if (v2 != 436207616 && v2 != 369098752) { - if (v2 == 402653184) { - return 142; - } - return arg1; - } - return 142; - } - if (v2 == 469762048) { - return 142; - } - return arg1; -} - -st32 get_hashfunc_03(st32 arg1, st32 arg2) { - st32 v2 = arg2 & 0xE0000000; - if ((arg2 & 0xE0000000u) <= 0x80000000) { - if ((arg2 & 0xE0000000) == 0x80000000) { - return 102; - } - if (!v2) { - return 485; - } - if (v2 == 536870912) { - return 486; - } - return arg1; - } - if (v2 != 0xA0000000) { - return arg1; - } - return 475; -} - -st32 get_hashfunc_04(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((arg2 & 0x80000000) == 0x80000000) { - result = 99; - } else { - result = arg1; - } - } else { - result = 100; - } - return result; -} - -st32 get_hashfunc_05(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((arg2 & 0x80000000) == 0x80000000) { - result = 97; - } else { - result = arg1; - } - } else { - result = 98; - } - return result; -} - -st32 get_hashfunc_06(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((st32)(arg2 & 0x80000000) == 0x80000000) { - result = 228; - } else { - result = arg1; - } - } else { - result = 227; - } - return result; -} - -st32 get_hashfunc_07(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((arg2 & 0x80000000) == 0x80000000) { - result = 52; - } else { - result = arg1; - } - } else { - result = 140; - } - return result; -} - -st32 get_hashfunc_08(st32 arg1, st32 arg2) { - st32 tmp; - - tmp = arg2 & 0xC0000000; - if ((arg2 & 0xC0000000u) <= 0x80000000) { - if ((arg2 & 0xC0000000) == 0x80000000) { - return 87; - } - if (!tmp) { - return 85; - } - if (tmp == 0x40000000) { - return 86; - } - return arg1; - } - if (tmp != 0xC0000000) { - return arg1; - } - return 88; -} - -st32 get_hashfunc_09(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0xC0000000; - if ((arg2 & 0xC0000000u) <= 0x80000000) { - if ((arg2 & 0xC0000000) == 0x80000000) { - return 91; - } - if (!v2) { - return 89; - } - if (v2 == 0x40000000) { - return 90; - } - return arg1; - } - if (v2 != 0xC0000000) { - return arg1; - } - return 92; -} - -st32 get_hashfunc_10(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - - v2 = arg2; - v3 = v2 & 0x500000; - if ((ut32)v3 <= 0x400000) { - if (v3 == 4194304) { - return 247; - } - if (!v3) { - return 245; - } - if (v3 == 1048576) { - return 249; - } - return arg1; - } - if (v3 != 5242880) { - return arg1; - } - return 248; -} - -st32 get_hashfunc_11(st32 arg1, st32 arg2) { - // The following code is wrong because it will always produce 244 - // since `hash_const_05` is always 0 and as result it will always - // jump into the else scope. - // The code is kept because this is the product of RE. - - // st32 result; - // if ((ut32)hash_const_05 & arg2) { - // if (((ut32)hash_const_05 & arg2) == 524288) { - // result = 460; - // } else { - // result = arg1; - // } - // } else { - // result = 244; - // } - // return result; - return 244; -} - -st32 get_hashfunc_12(st32 arg1, st32 arg2) { - st32 tmp; - st32 v3; - st32 result; - - tmp = arg2; - v3 = tmp & 0x400000; - if (v3) { - if (v3 == 4194304) { - result = 521; - } else { - result = arg1; - } - } else { - result = 374; - } - return result; -} - -st32 get_hashfunc_13(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 30; - } else { - result = arg1; - } - } else { - result = 32; - } - return result; -} - -st32 get_hashfunc_14(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 0x1000000) { - result = 61; - } else { - result = arg1; - } - } else { - result = 60; - } - return result; -} - -st32 get_hashfunc_15(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 63; - } else { - result = arg1; - } - } else { - result = 62; - } - return result; -} - -st32 get_hashfunc_16(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 69; - } else { - result = arg1; - } - } else { - result = 64; - } - return result; -} - -st32 get_hashfunc_17(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 68; - } else { - result = arg1; - } - } else { - result = 67; - } - return result; -} - -st32 get_hashfunc_18(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 66; - } else { - result = arg1; - } - } else { - result = 65; - } - return result; -} - -st32 get_hashfunc_19(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0xC1000000; - if ((arg2 & 0xC1000000u) > 0x40000000) { - if (v2 != 0x80000000 && v2 != 0xC0000000) { - return arg1; - } - } else { - if ((arg2 & 0xC1000000) != 0x40000000 && v2) { - if (v2 == 16777216) { - return 469; - } - return arg1; - } - } - return 59; -} - -st32 get_hashfunc_20(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0x1400000; - if ((arg2 & 0x1400000u) <= 0x1000000) { - if ((arg2 & 0x1400000) == 16777216) { - return 75; - } - if (!v2) { - return 74; - } - if (v2 == 4194304) { - return 78; - } - return arg1; - } - if (v2 != 20971520) { - return arg1; - } - return 77; -} - -st32 get_hashfunc_21(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0x1400000; - if ((arg2 & 0x1400000u) <= 0x1000000) { - if ((arg2 & 0x1400000) == 16777216) { - return 73; - } - if (!v2) { - return 72; - } - if (v2 == 4194304) { - return 108; - } - return arg1; - } - if (v2 != 20971520) { - return arg1; - } - return 109; -} - -st32 get_hashfunc_22(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x8200) { - if ((unsigned short)(arg2 & 0x8200) == 512) { - result = 364; - } else { - result = arg1; - } - } else { - result = 357; - } - return result; -} - -st32 get_hashfunc_23(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - - v2 = arg2; - v3 = v2 & 0x41C000; - if ((ut32)v3 <= 0x400000) { - if (v3 == 4194304) { - return 323; - } - if ((ut32)v3 <= 0xC000) { - if (v3 != 49152) { - if (!v3) { - return 324; - } - if (v3 == 16384) { - return 370; - } - if (v3 == 32768) { - return 325; - } - return arg1; - } - return 372; - } - if (v3 != 65536) { - if (v3 != 81920) { - if (v3 == 114688) { - return 371; - } - return arg1; - } - return 373; - } - return 314; - } - if ((ut32)v3 <= 0x410000) { - if (v3 != 4259840) { - if (v3 == 4210688) { - return 369; - } - // if ( (st32 (*)(char))v3 == (char *)hash_const_01 ) - if (v3 == hash_const_01) { - return 325; - } - if (v3 != 4243456) { - return arg1; - } - return 372; - } - return 314; - } - if (v3 != 4276224) { - if (v3 == 4308992) { - return 371; - } - return arg1; - } - return 373; -} - -st32 get_hashfunc_24(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - - v2 = arg2; - v3 = v2 & 0x418000; - if ((ut32)v3 <= 0x400000) { - if (v3 == 4194304) { - return 330; - } - if ((ut32)v3 <= 0x10000) { - if (v3 != 65536) { - if (!v3) { - return 329; - } - if (v3 == 32768) { - return 307; - } - return arg1; - } - return 480; - } - if (v3 == 98304) { - return 467; - } - return arg1; - } - // if ( (st32 (*)(char))v3 != (char *)hash_const_01 ) { - if (v3 != hash_const_01) { - if (v3 == 4259840) { - return 480; - } - if (v3 == 4292608) { - return 467; - } - return arg1; - } - return 308; -} - -st32 get_hashfunc_25(st32 arg1, st32 arg2) { - ut32 v2; - - v2 = (ut32)hash_const_02 & arg2; - if (((ut32)hash_const_02 & arg2) <= 0x8000) { - if (((ut32)hash_const_02 & arg2) != 32768) { - if (v2 <= 0x400) { - if (v2 != 1024 && v2) { - if (v2 == 512) { - return 365; - } - return arg1; - } - return 365; - } - if (v2 == 1536) { - return 365; - } - return arg1; - } - return 382; - } - if (v2 <= 0x8600) { - if (v2 != 34304 && v2 != 33280 && v2 != 33792) { - return arg1; - } - return 382; - } - // if ( (st32 (*)(char))v2 != (char *)hash_const_01 ) - if (v2 != hash_const_01) { - return arg1; - } - return 380; -} - -st32 get_hashfunc_26(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - - v2 = arg2; - v3 = v2 & 0x41C000; - if ((ut32)v3 <= 0x404000) { - if (v3 == 4210688 || v3 == 16384) { - return 310; - } - if (v3 != 49152) { - if (v3 == 4194304) { - return 312; - } - return arg1; - } - return 311; - } - // if ( (st32 (*)(char))v3 != (st32 (*)(char))hash_const_01 ) { - if (v3 != hash_const_01) { - if (v3 != 4243456) { - return arg1; - } - return 311; - } - return 313; -} - -st32 get_hashfunc_27(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18000; - if (v3) { - if (v3 == 32768) { - result = 376; - } else { - if (v3 == 65536) { - result = 377; - } else { - result = arg1; - } - } - } else { - result = 375; - } - return result; -} - -st32 get_hashfunc_28(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - char v5; - - v2 = arg2; - v3 = v2 & 0x1F800; - if ((ut32)v3 <= 0xA000) { - if (v3 != 40960) { - if ((ut32)v3 <= 0x3000) { - if (v3 == 12288) { - return 384; - } - if ((ut32)v3 <= 0x1800) { - if (v3 != 6144 && v3 && v3 != 2048) { - if (v3 == 4096) { - return 384; - } - return arg1; - } - return 384; - } - if (v3 == 8192 || v3 == 10240) { - return 384; - } - return arg1; - } - if ((ut32)v3 > 0x8800) { - if (v3 != 36864) { - if (v3 == 38912) { - return 385; - } - return arg1; - } - } else { - if (v3 != 34816) { - if (v3 != 14336) { - if (v3 == 24576) { - return 388; - } - if (v3 == 32768) { - return 385; - } - return arg1; - } - return 384; - } - } - } - return 385; - } - if ((ut32)v3 <= 0x11000) { - if (v3 == 69632) { - return 386; - } - if ((ut32)v3 <= 0xE000) { - if (v3 == 57344) { - return 387; - } - if (v3 != 43008 && v3 != 45056 && v3 != 47104) { - return arg1; - } - return 385; - } - if (v3 == 65536) { - return 386; - } - v5 = v3 == 67584; - LABEL_35: - if (!v5) { - return arg1; - } - return 386; - } - if ((ut32)v3 <= 0x13000) { - if (v3 == 77824 || v3 == 71680 || v3 == 73728) { - return 386; - } - v5 = v3 == 75776; - goto LABEL_35; - } - if (v3 == 79872) { - return 386; - } - if (v3 != 90112) { - return arg1; - } - return 389; -} - -st32 get_hashfunc_29(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - - v2 = arg2; - v3 = v2 & 0x40F800; - if (v3 <= (ut32)hash_const_03) { - // if ( (st32 (*)(int, int, int))v3 == (st32 (*)(int, int, int))hash_const_03) - if (v3 == hash_const_03) { - return 305; - } - if (v3 == 40960) { - return 306; - } - if (v3 == 57344) { - return 391; - } - return arg1; - } - if (v3 != 4218880) { - return arg1; - } - return 390; -} - -st32 get_hashfunc_30(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18000; - if (v3) { - if (v3 == 32768) { - result = 303; - } else { - if (v3 == 65536) { - result = 304; - } else { - result = arg1; - } - } - } else { - result = 302; - } - return result; -} - -st32 get_hashfunc_31(st32 arg1, st32 arg2) { - st32 result; - st32 v3 = arg2 & 0x380000; - - if ((ut32)v3 <= 0x200000) { - if (v3 == 2097152) { - return 271; - } - if ((ut32)v3 > 0x100000) { - if (v3 == 1572864) { - return 534; - } - } else { - if (v3 == 1048576) { - return 317; - } - if (!v3) { - return 319; - } - if (v3 == 524288) { - return 533; - } - } - return arg1; - } - if (v3 == 2621440) { - result = 535; - } else { - if (v3 == 3145728) { - result = 321; - } else { - if (v3 != 3670016) { - return arg1; - } - result = 536; - } - } - return result; -} - -st32 get_hashfunc_32(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18000; - if (v3) { - if (v3 == 32768) { - result = 258; - } else { - if (v3 == 65536) { - result = 259; - } else { - result = arg1; - } - } - } else { - result = 261; - } - return result; -} - -st32 get_hashfunc_33(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x8000) { - if ((unsigned short)(arg2 & 0x8000) == 32768) { - result = 327; - } else { - result = arg1; - } - } else { - result = 326; - } - return result; -} - -st32 get_hashfunc_34(st32 arg1, st32 arg2) { - st32 result; - st32 v2 = arg2; - st32 v3 = v2 & 0x580000; - if ((ut32)v3 <= 0x180000) { - if (v3 == 1572864) { - return 471; - } - if (!v3) { - return 392; - } - if (v3 == 524288) { - return 470; - } - if (v3 == 1048576) { - return 393; - } - return arg1; - } - if (v3 == 4194304) { - result = 394; - } else { - if (v3 != 5242880) { - return arg1; - } - result = 395; - } - return result; -} - -st32 get_hashfunc_35(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 189; - } else { - result = arg1; - } - } else { - result = 186; - } - return result; -} - -st32 get_hashfunc_36(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1C00000) { - if ((arg2 & 0x1C00000) == 16777216) { - result = 188; - } else { - result = arg1; - } - } else { - result = 187; - } - return result; -} - -st32 get_hashfunc_37(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1800000) == 8388608) { - result = 473; - } else { - if ((arg2 & 0x1800000) == 25165824) { - result = 474; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_38(st32 arg1, st32 arg2) { - st32 v2 = arg2 & 0x1010000; - if ((arg2 & 0x1010000u) <= 0x1000000) { - if ((arg2 & 0x1010000) == 16777216) { - return 472; - } - if (!v2) { - return 23; - } - if (v2 == 65536) { - return 24; - } - return arg1; - } - if (v2 != 16842752) { - return arg1; - } - return 26; -} - -st32 get_hashfunc_39(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1010000) == 65536) { - result = 25; - } else { - if ((arg2 & 0x1010000) == 16842752) { - result = 27; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_40(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1010000) { - if ((arg2 & 0x1010000) == 16777216) { - result = 135; - } else { - result = arg1; - } - } else { - result = 134; - } - return result; -} - -st32 get_hashfunc_41(st32 arg1, st32 arg2) { - st32 result; - st32 v2 = arg2 & 0x1010000; - if (arg2 & 0x1010000) { - if (v2 == 16777216) { - result = 138; - } else { - if (v2 == 16842752) { - result = 139; - } else { - result = arg1; - } - } - } else { - result = 137; - } - return result; -} - -st32 get_hashfunc_42(st32 arg1, st32 arg2) { - st32 v2 = arg2 & 0x1010000; - if ((arg2 & 0x1010000u) <= 0x1000000) { - if ((arg2 & 0x1010000) == 16777216) { - return 12; - } - if (!v2) { - return 11; - } - if (v2 == 65536) { - return 8; - } - return arg1; - } - if (v2 != 16842752) { - return arg1; - } - return 9; -} - -st32 get_hashfunc_43(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0x1010000; - if ((arg2 & 0x1010000u) <= 0x1000000) { - if ((arg2 & 0x1010000) == 16777216) { - return 13; - } - if (!v2) { - return 15; - } - if (v2 == 65536) { - return 10; - } - return arg1; - } - if (v2 != 16842752) { - return arg1; - } - return 14; -} - -st32 get_hashfunc_44(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 29; - } else { - result = arg1; - } - } else { - result = 28; - } - return result; -} - -st32 get_hashfunc_45(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 17; - } else { - result = arg1; - } - } else { - result = 16; - } - return result; -} - -st32 get_hashfunc_46(st32 arg1, st32 arg2) { - st32 v2; - - v2 = arg2 & 0xC1000000; - if ((arg2 & 0xC1000000u) > 0x40000000) { - if (v2 != 0x80000000 && v2 != 0xC0000000) { - return arg1; - } - } else { - if ((arg2 & 0xC1000000) != 0x40000000 && v2) { - if (v2 == 16777216) { - return 136; - } - return arg1; - } - } - return 18; -} - -st32 get_hashfunc_47(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 130; - } else { - result = arg1; - } - } else { - result = 132; - } - return result; -} - -st32 get_hashfunc_48(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 133; - } else { - result = arg1; - } - } else { - result = 131; - } - return result; -} - -st32 get_hashfunc_49(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((arg2 & 0x80000000) == 0x80000000) { - result = 33; - } else { - result = arg1; - } - } else { - result = 35; - } - return result; -} - -st32 get_hashfunc_50(st32 arg1, st32 arg2) { - st32 v3 = arg2 & 0x780000; - if ((ut32)v3 <= 0x400000) { - if (v3 == 4194304) { - return 522; - } - if ((ut32)v3 > 0x180000) { - if (v3 == 2621440) { - return 402; - } - if (v3 == 3145728) { - return 411; - } - } else { - if (v3 == 1572864) { - return 401; - } - if (!v3) { - return 403; - } - if (v3 == 524288) { - return 400; - } - } - return arg1; - } - if ((ut32)v3 <= 0x680000) { - if (v3 == 0x680000) { - return 526; - } - if (v3 == hash_const_05) { - return 524; - } - if (v3 == 0x580000) { - return 525; - } - return arg1; - } - if (v3 != 7340032) { - return arg1; - } - return 523; -} - -st32 get_hashfunc_51(st32 arg1, st32 arg2) { - st32 v3 = arg2 & 0x180000; - if ((ut32)v3 > 0x100000) { - if (v3 != 1572864) { - return arg1; - } - } else { - if (v3 != 1048576) { - if (!v3) { - return 396; - } - if (v3 == 524288) { - return 532; - } - return arg1; - } - } - return 398; -} - -st32 get_hashfunc_52(st32 arg1, st32 arg2) { - st32 v3 = arg2 & 0x18000; - if ((ut32)v3 <= 0x10000) { - if (v3 == 0x10000) { - return 296; - } - if (v3 == 0x8000) { - return 300; - } - if (!v3) { - return 298; - } - return arg1; - } - if (v3 != 0x18000) { - return arg1; - } - return 301; -} - -st32 get_hashfunc_53(st32 arg1, st32 arg2) { - st32 v2 = arg2 & 0x8200; - st32 result; - - if (arg2 & 0x8200) { - if (v2 == 512) { - result = 530; - } else { - result = (v2 == 0x8000) ? 297 : arg1; - } - } else { - result = 355; - } - return result; -} - -st32 get_hashfunc_54(st32 arg1, st32 arg2) { - st32 v2 = arg2 & 0x8200; - if ((ut32)v2 <= 0x8000) { - if (v2 == 32768) { - return 316; - } - if (!(arg2 & 0x8200)) { - return 410; - } - if (v2 == 512) { - return 531; - } - return arg1; - } - if (v2 != 33280) { - return arg1; - } - return 315; -} - -st32 get_hashfunc_55(st32 arg1, st32 arg2) { - st32 result; - if ((arg2 & 0x8000)) { - if ((unsigned short)(arg2 & 0x8000) == 32768) { - result = 295; - } else { - result = arg1; - } - } else { - result = 294; - } - return result; -} - -st32 get_hashfunc_56(st32 arg1, st32 arg2) { - st32 result; - st32 v3 = arg2 & 0x18180; - if ((ut32)v3 <= 0x8080) { - if (v3 == 32896) { - return 528; - } - if (!v3) { - return 406; - } - if (v3 == 128) { - return 527; - } - if (v3 == 32768) { - return 407; - } - return arg1; - } - if (v3 == 98304) { - result = 408; - } else { - if (v3 != 98432) { - return arg1; - } - result = 529; - } - return result; -} - -st32 get_hashfunc_57(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x8000) { - if ((unsigned short)(arg2 & 0x8000) == 32768) { - result = 405; - } else { - result = arg1; - } - } else { - result = 404; - } - return result; -} - -st32 get_hashfunc_58(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18000; - if (v3) { - if (v3 == 32768) { - result = 263; - } else { - if (v3 == 65536) { - result = 264; - } else { - result = arg1; - } - } - } else { - result = 262; - } - return result; -} - -st32 get_hashfunc_59(st32 arg1, st32 arg2) { - st32 result; - st32 v2 = arg2 & 0x8180; - if ((ut32)v2 <= 0x100) { - if (v2 == 256) { - return 505; - } - if (!(arg2 & 0x8180)) { - return 503; - } - if (v2 == 128) { - return 504; - } - return arg1; - } - if (v2 == 384) { - result = 506; - } else { - if (v2 != 32768) { - return arg1; - } - result = 507; - } - return result; -} - -st32 get_hashfunc_60(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 173; - } else { - result = arg1; - } - } else { - result = 172; - } - return result; -} - -st32 get_hashfunc_61(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 175; - } else { - result = arg1; - } - } else { - result = 174; - } - return result; -} - -st32 get_hashfunc_62(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 152; - } else { - result = arg1; - } - } else { - result = 151; - } - return result; -} - -st32 get_hashfunc_63(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 154; - } else { - result = arg1; - } - } else { - result = 153; - } - return result; -} - -st32 get_hashfunc_64(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 180; - } else { - result = arg1; - } - } else { - result = 179; - } - return result; -} - -st32 get_hashfunc_65(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 182; - } else { - result = arg1; - } - } else { - result = 181; - } - return result; -} - -st32 get_hashfunc_66(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1C00000) == 12582912) { - result = 157; - } else { - if ((arg2 & 0x1C00000) == 29360128) { - result = 158; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_67(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 57; - } else { - result = arg1; - } - } else { - result = 56; - } - return result; -} - -st32 get_hashfunc_68(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 208; - } else { - result = arg1; - } - } else { - result = 207; - } - return result; -} - -st32 get_hashfunc_69(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 209; - } else { - result = arg1; - } - } else { - result = 210; - } - return result; -} - -st32 get_hashfunc_70(st32 arg1, st32 arg2) { - st32 v2; - st32 result; - - v2 = arg2 & 0x1400000; - if (arg2 & 0x1400000) { - if (v2 == 16777216) { - result = 217; - } else { - if (v2 == 20971520) { - result = 212; - } else { - result = arg1; - } - } - } else { - result = 216; - } - return result; -} - -st32 get_hashfunc_71(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1400000) { - if ((arg2 & 0x1400000) == 4194304) { - result = 211; - } else { - result = arg1; - } - } else { - result = 218; - } - return result; -} - -st32 get_hashfunc_72(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 221; - } else { - result = arg1; - } - } else { - result = 220; - } - return result; -} - -st32 get_hashfunc_73(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1010000) { - if ((arg2 & 0x1010000) == 16777216) { - result = 215; - } else { - result = arg1; - } - } else { - result = 214; - } - return result; -} - -st32 get_hashfunc_74(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1010000) == 65536) { - result = 213; - } else { - if ((arg2 & 0x1010000) == 16842752) { - result = 426; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_75(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x80000000) { - if ((arg2 & 0x80000000) == 0x80000000) { - result = 457; - } else { - result = arg1; - } - } else { - result = 459; - } - return result; -} - -st32 get_hashfunc_76(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 21; - } else { - result = arg1; - } - } else { - result = 19; - } - return result; -} - -st32 get_hashfunc_77(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 22; - } else { - result = arg1; - } - } else { - result = 20; - } - return result; -} - -st32 get_hashfunc_78(st32 arg1, st32 arg2) { - st32 v3 = arg2 & 0x18000; - if ((ut32)v3 <= 0x10000) { - if (v3 == 65536) { - return 429; - } - if (!v3) { - return 427; - } - if (v3 == 32768) { - return 428; - } - - return arg1; - } - if (v3 != 98304) { - return arg1; - } - return 252; -} - -st32 get_hashfunc_79(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18180; - if ((ut32)v3 <= 0x8100) { - if (v3 == 33024) { - return 437; - } - if ((ut32)v3 > 0x180) { - if (v3 == 32768) { - return 431; - } - if (v3 == 32896) { - return 435; - } - } else { - if (v3 == 384) { - return 440; - } - if (!v3) { - return 430; - } - if (v3 == 128) { - return 432; - } - if (v3 == 256) { - return 434; - } - } - return arg1; - } - if ((ut32)v3 <= 0x10100) { - if (v3 == 65792) { - return 442; - } - if (v3 == 33152) { - return 441; - } - if (v3 == 65536) { - return 433; - } - if (v3 == 65664) { - return 436; - } - return arg1; - } - if (v3 == 65920) { - result = 439; - } else { - if (v3 != 98688) { - return arg1; - } - result = 438; - } - return result; -} - -st32 get_hashfunc_80(st32 arg1, st32 arg2) { - st32 v2; - st32 v3; - st32 result; - - v2 = arg2; - v3 = v2 & 0x18180; - if ((ut32)v3 <= 0x8100) { - if (v3 == 33024) { - return 450; - } - if ((ut32)v3 > 0x180) { - if (v3 == 32768) { - return 444; - } - if (v3 == 32896) { - return 448; - } - } else { - if (v3 == 384) { - return 453; - } - if (!v3) { - return 443; - } - if (v3 == 128) { - return 445; - } - if (v3 == 256) { - return 447; - } - } - return arg1; - } - if ((ut32)v3 <= 0x10100) { - if (v3 == 65792) { - return 455; - } - if (v3 == 33152) { - return 454; - } - if (v3 == 65536) { - return 446; - } - if (v3 == 65664) { - return 449; - } - return arg1; - } - if (v3 == 65920) { - result = 452; - } else { - if (v3 != 98688) { - return arg1; - } - result = 451; - } - return result; -} - -st32 get_hashfunc_81(st32 arg1, st32 arg2) { - st32 result; - st32 v2 = arg2 & 0x1000180; - if ((arg2 & 0x1000180u) <= 0x1000000) { - if ((arg2 & 0x1000180) == 16777216) { - return 191; - } - if ((ut32)v2 > 0x100) { - if (v2 == 384) { - return 200; - } - } else { - if (v2 == 256) { - return 538; - } - if (!v2) { - return 190; - } - if (v2 == 128) { - return 537; - } - } - return arg1; - } - if (v2 == 16777344) { - result = 194; - } else { - if (v2 == 16777472) { - result = 539; - } else { - if (v2 != 16777600) { - return arg1; - } - result = 201; - } - } - return result; -} - -st32 get_hashfunc_82(st32 arg1, st32 arg2) { - st32 v2; - st32 result; - - v2 = arg2 & 0x1000180; - if ((arg2 & 0x1000180u) <= 0x100) { - if ((arg2 & 0x1000180) == 256) { - return 203; - } - if (!v2) { - return 192; - } - if (v2 == 128) { - return 195; - } - return arg1; - } - if (v2 == 384) { - result = 198; - } else { - if (v2 != 16777600) { - return arg1; - } - result = 196; - } - return result; -} - -st32 get_hashfunc_83(st32 arg1, st32 arg2) { - st32 v2; - st32 result; - char v4; - - v2 = arg2 & 0x1810180; - if ((arg2 & 0x1810180u) <= 0x800080) { - if ((arg2 & 0x1810180) != 8388736) { - if ((ut32)v2 <= 0x10000) { - if (v2 == 65536) { - return 193; - } - if ((ut32)v2 <= 0x100) { - if (v2 != 256) { - if (v2) { - if (v2 == 128) { - return 197; - } - return arg1; - } - return 193; - } - return 202; - } - v4 = v2 == 384; - LABEL_11: - if (v4) { - return 199; - } - return arg1; - } - if ((ut32)v2 > 0x10180) { - if (v2 == 8388608) { - return 193; - } - return arg1; - } - if (v2 == 65920) { - return 199; - } - if (v2 != 65664) { - if (v2 == 65792) { - return 202; - } - return arg1; - } - } - return 197; - } - if ((ut32)v2 <= 0x810100) { - if (v2 == 8454400) { - return 202; - } - if ((ut32)v2 <= 0x810000) { - if (v2 == 8454144) { - return 193; - } - if (v2 == 8388864) { - return 202; - } - v4 = v2 == 8388992; - goto LABEL_11; - } - if (v2 != 8454272) { - return arg1; - } - return 197; - } - if (v2 == 8454528) { - return 199; - } - if (v2 == 16777216) { - result = 205; - } else { - if (v2 != 16777344) { - return arg1; - } - result = 206; - } - return result; -} - -st32 get_hashfunc_84(st32 arg1, st32 arg2) { - st32 result; - st32 v2 = arg2 & 0x1000180; - if (arg2 & 0x1000180) { - if (v2 == 16777344) { - result = 509; - } else { - if (v2 == 16777472) { - result = 510; - } else { - result = arg1; - } - } - } else { - result = 508; - } - return result; -} - -st32 get_hashfunc_85(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1000180) == 128) { - result = 511; - } else { - if ((arg2 & 0x1000180) == 256) { - result = 512; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_86(st32 arg1, st32 arg2) { - st32 result; - - if (arg2 & 0x1000000) { - if ((arg2 & 0x1000000) == 16777216) { - result = 171; - } else { - result = arg1; - } - } else { - result = 170; - } - return result; -} - -st32 get_hashfunc_87(st32 arg1, st32 v2) { - st32 result; - st32 v3 = v2 & 0x79B981; - if (v3 == 33024 || v3 == 4227328) { - result = 490; - } else { - result = (v3 == 4260097) ? 491 : arg1; - } - return result; -} - -st32 get_hashfunc_88(st32 arg1, st32 arg2) { - st32 v2 = arg2; - st32 v3 = v2 & 0x79B981; - if ((ut32)v3 <= 0x410101) { - if (v3 == 0x410101) { - return 493; - } - if (v3 == 0x8100 || v3 == 0x408100) { - return 492; - } - return arg1; - } - if (v3 != hash_const_04) { - return arg1; - } - return 494; -} - -st32 get_hashfunc_89(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1B901B9) == 16777600) { - result = 488; - } else { - if ((arg2 & 0x1B901B9) == 25231616) { - result = 495; - } else { - result = arg1; - } - } - return result; -} - -st32 get_hashfunc_90(st32 arg1, st32 arg2) { - st32 v2; - st32 v4; - st32 v5; - - v2 = arg2 & 0x1F901B9; - if ((arg2 & 0x1F901B9u) <= 0x1810101) { - if ((arg2 & 0x1F901B9) == 25231617) { - return 498; - } - if ((ut32)v2 <= 0x1000180) { - if (v2 != 16777600) { - if (v2 == 8454401) { - return 497; - } - if (v2 == 8454529) { - return 499; - } - return arg1; - } - return 489; - } - if (v2 == 20971904) { - return 489; - } - return arg1; - } - v4 = v2 - 25231744; - if (v4) { - v5 = v4 - 1; - if (!v5) { - return 500; - } - - /* FIX */ - v5 -= 0x3FFFFF; - if (v5 != 0) { - return arg1; - } - /* - if ( (_UNKNOWN *)v5 != &unk_3FFFFF ) - return arg1; - */ - } - return 496; -} - -st32 get_hashfunc_91(st32 arg1, st32 arg2) { - st32 result; - - if ((arg2 & 0x1F901BF) == 8454145) { - result = 501; - } else { - if ((arg2 & 0x1F901BF) == 25231361) { - result = 502; - } else { - result = arg1; - } - } - return result; -} - -static HASHCODE_ENTRY_T ins_hash[] = { - { 0x223, get_hashfunc_02 }, - { 0x223, get_hashfunc_03 }, - { 0x223, get_hashfunc_04 }, - { 0x223, get_hashfunc_05 }, - { 0xDF, get_hashfunc_01 }, - { 0xE0, get_hashfunc_01 }, - { 0x223, get_hashfunc_06 }, - { 0x223, get_hashfunc_07 }, - { 0xEE, get_hashfunc_01 }, - { 0xEE, get_hashfunc_01 }, - { 0xEF, get_hashfunc_01 }, - { 0xEF, get_hashfunc_01 }, - { 0xF0, get_hashfunc_01 }, - { 0xF0, get_hashfunc_01 }, - { 0xF1, get_hashfunc_01 }, - { 0xF1, get_hashfunc_01 }, - { 0x1, get_hashfunc_01 }, - { 0x1, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_08 }, - { 0x223, get_hashfunc_09 }, - { 0x1E7, get_hashfunc_01 }, - { 0x1E7, get_hashfunc_01 }, - { 0x6B, get_hashfunc_01 }, - { 0x6B, get_hashfunc_01 }, - { 0x71, get_hashfunc_01 }, - { 0x70, get_hashfunc_01 }, - { 0x72, get_hashfunc_01 }, - { 0x72, get_hashfunc_01 }, - { 0x6E, get_hashfunc_01 }, - { 0x6E, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x3A, get_hashfunc_01 }, - { 0x3A, get_hashfunc_01 }, - { 0x65, get_hashfunc_01 }, - { 0x65, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0xE7, get_hashfunc_01 }, - { 0xE7, get_hashfunc_01 }, - { 0xE8, get_hashfunc_01 }, - { 0xE8, get_hashfunc_01 }, - { 0x1A8, get_hashfunc_01 }, - { 0x1A8, get_hashfunc_01 }, - { 0xEC, get_hashfunc_01 }, - { 0xEC, get_hashfunc_01 }, - { 0xED, get_hashfunc_01 }, - { 0xED, get_hashfunc_01 }, - { 0xE9, get_hashfunc_01 }, - { 0xE9, get_hashfunc_01 }, - { 0xEA, get_hashfunc_01 }, - { 0xEA, get_hashfunc_01 }, - { 0x1E3, get_hashfunc_01 }, - { 0x1E3, get_hashfunc_01 }, - { 0x1E4, get_hashfunc_01 }, - { 0x1E4, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x1DC, get_hashfunc_01 }, - { 0x1DC, get_hashfunc_01 }, - { 0x1E1, get_hashfunc_01 }, - { 0x1E1, get_hashfunc_01 }, - { 0x1E2, get_hashfunc_01 }, - { 0x1E2, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x160, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DF, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1DD, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x1D0, get_hashfunc_01 }, - { 0x135, get_hashfunc_01 }, - { 0x135, get_hashfunc_01 }, - { 0xF3, get_hashfunc_01 }, - { 0xF3, get_hashfunc_01 }, - { 0x1CE, get_hashfunc_01 }, - { 0x1CE, get_hashfunc_01 }, - { 0x1CF, get_hashfunc_01 }, - { 0x1CF, get_hashfunc_01 }, - { 0x17B, get_hashfunc_01 }, - { 0x17B, get_hashfunc_01 }, - { 0x16E, get_hashfunc_01 }, - { 0x16E, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0xF6, get_hashfunc_01 }, - { 0xF6, get_hashfunc_01 }, - { 0x223, get_hashfunc_10 }, - { 0x223, get_hashfunc_10 }, - { 0x223, get_hashfunc_11 }, - { 0x223, get_hashfunc_11 }, - { 0x223, get_hashfunc_12 }, - { 0x223, get_hashfunc_12 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x3, get_hashfunc_01 }, - { 0x3, get_hashfunc_01 }, - { 0x4, get_hashfunc_01 }, - { 0x4, get_hashfunc_01 }, - { 0x2, get_hashfunc_01 }, - { 0x2, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x6, get_hashfunc_01 }, - { 0x6, get_hashfunc_01 }, - { 0x0, get_hashfunc_01 }, - { 0x0, get_hashfunc_01 }, - { 0x69, get_hashfunc_01 }, - { 0x69, get_hashfunc_01 }, - { 0x7, get_hashfunc_01 }, - { 0x7, get_hashfunc_01 }, - { 0x46, get_hashfunc_01 }, - { 0x46, get_hashfunc_01 }, - { 0x47, get_hashfunc_01 }, - { 0x47, get_hashfunc_01 }, - { 0x223, get_hashfunc_13 }, - { 0x1F, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_14 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_15 }, - { 0x223, get_hashfunc_16 }, - { 0x223, get_hashfunc_17 }, - { 0x223, get_hashfunc_18 }, - { 0x223, get_hashfunc_19 }, - { 0x19D, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x7B, get_hashfunc_01 }, - { 0x80, get_hashfunc_01 }, - { 0x81, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_20 }, - { 0x223, get_hashfunc_21 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x109, get_hashfunc_01 }, - { 0x109, get_hashfunc_01 }, - { 0x10A, get_hashfunc_01 }, - { 0x10A, get_hashfunc_01 }, - { 0x10B, get_hashfunc_01 }, - { 0x10B, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x10C, get_hashfunc_01 }, - { 0x10C, get_hashfunc_01 }, - { 0x10D, get_hashfunc_01 }, - { 0x10D, get_hashfunc_01 }, - { 0x10E, get_hashfunc_01 }, - { 0x10E, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_22 }, - { 0x223, get_hashfunc_22 }, - { 0x223, get_hashfunc_23 }, - { 0x223, get_hashfunc_23 }, - { 0x223, get_hashfunc_24 }, - { 0x223, get_hashfunc_24 }, - { 0x223, get_hashfunc_25 }, - { 0x223, get_hashfunc_25 }, - { 0x223, get_hashfunc_26 }, - { 0x223, get_hashfunc_26 }, - { 0x223, get_hashfunc_27 }, - { 0x223, get_hashfunc_27 }, - { 0x223, get_hashfunc_28 }, - { 0x223, get_hashfunc_28 }, - { 0x223, get_hashfunc_29 }, - { 0x223, get_hashfunc_29 }, - { 0x223, get_hashfunc_30 }, - { 0x223, get_hashfunc_30 }, - { 0x223, get_hashfunc_31 }, - { 0x223, get_hashfunc_31 }, - { 0x223, get_hashfunc_32 }, - { 0x223, get_hashfunc_32 }, - { 0x223, get_hashfunc_32 }, - { 0x223, get_hashfunc_32 }, - { 0x223, get_hashfunc_33 }, - { 0x223, get_hashfunc_33 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_34 }, - { 0x223, get_hashfunc_34 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x94, get_hashfunc_01 }, - { 0x94, get_hashfunc_01 }, - { 0x95, get_hashfunc_01 }, - { 0x95, get_hashfunc_01 }, - { 0x92, get_hashfunc_01 }, - { 0x92, get_hashfunc_01 }, - { 0x93, get_hashfunc_01 }, - { 0x93, get_hashfunc_01 }, - { 0x92, get_hashfunc_01 }, - { 0x92, get_hashfunc_01 }, - { 0x93, get_hashfunc_01 }, - { 0x93, get_hashfunc_01 }, - { 0x223, get_hashfunc_35 }, - { 0x223, get_hashfunc_35 }, - { 0x223, get_hashfunc_36 }, - { 0x223, get_hashfunc_37 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_38 }, - { 0x223, get_hashfunc_39 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_40 }, - { 0x223, get_hashfunc_41 }, - { 0x223, get_hashfunc_42 }, - { 0x223, get_hashfunc_43 }, - { 0x223, get_hashfunc_44 }, - { 0x223, get_hashfunc_44 }, - { 0x223, get_hashfunc_45 }, - { 0x223, get_hashfunc_46 }, - { 0x223, get_hashfunc_47 }, - { 0x223, get_hashfunc_48 }, - { 0x223, get_hashfunc_47 }, - { 0x223, get_hashfunc_48 }, - { 0x9F, get_hashfunc_01 }, - { 0x9F, get_hashfunc_01 }, - { 0x1D1, get_hashfunc_01 }, - { 0x1D1, get_hashfunc_01 }, - { 0x223, get_hashfunc_49 }, - { 0xA0, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x19C, get_hashfunc_01 }, - { 0x19C, get_hashfunc_01 }, - { 0x223, get_hashfunc_50 }, - { 0x223, get_hashfunc_50 }, - { 0x223, get_hashfunc_51 }, - { 0x223, get_hashfunc_51 }, - { 0x223, get_hashfunc_52 }, - { 0x223, get_hashfunc_52 }, - { 0x223, get_hashfunc_53 }, - { 0x223, get_hashfunc_53 }, - { 0x223, get_hashfunc_54 }, - { 0x223, get_hashfunc_54 }, - { 0x223, get_hashfunc_55 }, - { 0x223, get_hashfunc_55 }, - { 0x223, get_hashfunc_56 }, - { 0x223, get_hashfunc_56 }, - { 0x223, get_hashfunc_57 }, - { 0x223, get_hashfunc_57 }, - { 0x223, get_hashfunc_57 }, - { 0x223, get_hashfunc_57 }, - { 0x223, get_hashfunc_58 }, - { 0x223, get_hashfunc_58 }, - { 0x223, get_hashfunc_58 }, - { 0x223, get_hashfunc_58 }, - { 0x223, get_hashfunc_59 }, - { 0x223, get_hashfunc_59 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_60 }, - { 0xB1, get_hashfunc_01 }, - { 0x223, get_hashfunc_61 }, - { 0xB0, get_hashfunc_01 }, - { 0x223, get_hashfunc_62 }, - { 0x9C, get_hashfunc_01 }, - { 0x223, get_hashfunc_63 }, - { 0x9B, get_hashfunc_01 }, - { 0x223, get_hashfunc_64 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_65 }, - { 0xB7, get_hashfunc_01 }, - { 0x223, get_hashfunc_66 }, - { 0x223, get_hashfunc_66 }, - { 0x223, get_hashfunc_67 }, - { 0x223, get_hashfunc_67 }, - { 0x223, get_hashfunc_68 }, - { 0x223, get_hashfunc_69 }, - { 0x223, get_hashfunc_68 }, - { 0x223, get_hashfunc_69 }, - { 0x223, get_hashfunc_70 }, - { 0x223, get_hashfunc_71 }, - { 0x223, get_hashfunc_70 }, - { 0x223, get_hashfunc_71 }, - { 0xDB, get_hashfunc_01 }, - { 0x223, get_hashfunc_72 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_73 }, - { 0x223, get_hashfunc_74 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x1DE, get_hashfunc_01 }, - { 0x1DE, get_hashfunc_01 }, - { 0x1D2, get_hashfunc_01 }, - { 0x1D2, get_hashfunc_01 }, - { 0x223, get_hashfunc_75 }, - { 0x1CA, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_76 }, - { 0x223, get_hashfunc_77 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x90, get_hashfunc_01 }, - { 0x90, get_hashfunc_01 }, - { 0x91, get_hashfunc_01 }, - { 0x91, get_hashfunc_01 }, - { 0x1CD, get_hashfunc_01 }, - { 0x1CD, get_hashfunc_01 }, - { 0x1CD, get_hashfunc_01 }, - { 0x1CD, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x96, get_hashfunc_01 }, - { 0x223, get_hashfunc_78 }, - { 0x223, get_hashfunc_78 }, - { 0x223, get_hashfunc_78 }, - { 0x223, get_hashfunc_78 }, - { 0x223, get_hashfunc_79 }, - { 0x223, get_hashfunc_79 }, - { 0x223, get_hashfunc_80 }, - { 0x223, get_hashfunc_80 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_81 }, - { 0x223, get_hashfunc_82 }, - { 0x223, get_hashfunc_81 }, - { 0x223, get_hashfunc_82 }, - { 0x223, get_hashfunc_83 }, - { 0xCC, get_hashfunc_01 }, - { 0x223, get_hashfunc_83 }, - { 0xCC, get_hashfunc_01 }, - { 0x223, get_hashfunc_84 }, - { 0x223, get_hashfunc_85 }, - { 0x223, get_hashfunc_84 }, - { 0x223, get_hashfunc_85 }, - { 0x223, get_hashfunc_86 }, - { 0x223, get_hashfunc_86 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_87 }, - { 0x223, get_hashfunc_87 }, - { 0x223, get_hashfunc_88 }, - { 0x223, get_hashfunc_88 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_89 }, - { 0x223, get_hashfunc_90 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_91 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 }, - { 0x223, get_hashfunc_01 } -}; - -// get hashcode from instruction bytecode -st32 get_hash_code(ut32 ins_pos) { - ut32 len, ins_part1; - ut32 opcode, pos; - st32 (*get_hashcode_func)(st32 arg, st32 arg2); - ut32 ins_len; - st32 arg, ins_part2, hash_code; - - ins_part1 = 0; - ins_part2 = 0; - - opcode = get_ins_part(ins_pos, 1); - ins_len = get_ins_len(opcode); - - if (ins_len > 1) { - len = ins_len - 1; - if (len >= 4) { - len = 4; - } - - ins_part1 = get_ins_part(ins_pos + 1, len) << (8 * (4 - len)); - ins_part2 = 0; - if (ins_len > 5) { - ins_part2 = get_ins_part(ins_pos + 5, 1); - } - } - - pos = (2 * opcode | (ins_part1 >> 31)); - // arg = *(ut32 *)(((ut8 *)ins_hash)+ pos * 8); - arg = ins_hash[pos].code; - - ins_part2 >>= 7; - ins_part2 |= (ins_part1 * 2); - - // get_hashcode_func = *(ut32 *)(((ut8 *)ins_hash + sizeof(ut32)) + pos * 8); - get_hashcode_func = ins_hash[pos].hash_func; - - hash_code = get_hashcode_func(arg, ins_part2); - - return hash_code; -} diff --git a/librz/arch/isa/tms320/c55x_plus/hashtable.h b/librz/arch/isa/tms320/c55x_plus/hashtable.h deleted file mode 100644 index d594b5c7f8..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/hashtable.h +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef HASHTABLE_H -#define HASHTABLE_H - -st32 get_hash_code(ut32 ins_pos); - -#endif diff --git a/librz/arch/isa/tms320/c55x_plus/hashvector.c b/librz/arch/isa/tms320/c55x_plus/hashvector.c deleted file mode 100644 index 0ceb40ff81..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/hashvector.c +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2015 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only -/* this .c is included , not compiled */ -#include "hashvector.h" - -extern st32 get_hashfunc_01(st32 A1, st32 A2); -extern st32 get_hashfunc_02(st32 A1, st32 A2); -extern st32 get_hashfunc_03(st32 A1, st32 A2); -extern st32 get_hashfunc_04(st32 A1, st32 A2); -extern st32 get_hashfunc_05(st32 A1, st32 A2); -extern st32 get_hashfunc_06(st32 A1, st32 A2); -extern st32 get_hashfunc_07(st32 A1, st32 A2); -extern st32 get_hashfunc_08(st32 A1, st32 A2); -extern st32 get_hashfunc_09(st32 A1, st32 A2); -extern st32 get_hashfunc_10(st32 A1, st32 A2); -extern st32 get_hashfunc_11(st32 A1, st32 A2); -extern st32 get_hashfunc_12(st32 A1, st32 A2); -extern st32 get_hashfunc_13(st32 A1, st32 A2); -extern st32 get_hashfunc_14(st32 A1, st32 A2); -extern st32 get_hashfunc_15(st32 A1, st32 A2); -extern st32 get_hashfunc_16(st32 A1, st32 A2); -extern st32 get_hashfunc_17(st32 A1, st32 A2); -extern st32 get_hashfunc_18(st32 A1, st32 A2); -extern st32 get_hashfunc_19(st32 A1, st32 A2); -extern st32 get_hashfunc_20(st32 A1, st32 A2); -extern st32 get_hashfunc_21(st32 A1, st32 A2); -extern st32 get_hashfunc_22(st32 A1, st32 A2); -extern st32 get_hashfunc_23(st32 A1, st32 A2); -extern st32 get_hashfunc_24(st32 A1, st32 A2); -extern st32 get_hashfunc_25(st32 A1, st32 A2); -extern st32 get_hashfunc_26(st32 A1, st32 A2); -extern st32 get_hashfunc_27(st32 A1, st32 A2); -extern st32 get_hashfunc_28(st32 A1, st32 A2); -extern st32 get_hashfunc_29(st32 A1, st32 A2); -extern st32 get_hashfunc_30(st32 A1, st32 A2); -extern st32 get_hashfunc_31(st32 A1, st32 A2); -extern st32 get_hashfunc_32(st32 A1, st32 A2); -extern st32 get_hashfunc_33(st32 A1, st32 A2); -extern st32 get_hashfunc_34(st32 A1, st32 A2); -extern st32 get_hashfunc_35(st32 A1, st32 A2); -extern st32 get_hashfunc_36(st32 A1, st32 A2); -extern st32 get_hashfunc_37(st32 A1, st32 A2); -extern st32 get_hashfunc_38(st32 A1, st32 A2); -extern st32 get_hashfunc_39(st32 A1, st32 A2); -extern st32 get_hashfunc_40(st32 A1, st32 A2); -extern st32 get_hashfunc_41(st32 A1, st32 A2); -extern st32 get_hashfunc_42(st32 A1, st32 A2); -extern st32 get_hashfunc_43(st32 A1, st32 A2); -extern st32 get_hashfunc_44(st32 A1, st32 A2); -extern st32 get_hashfunc_45(st32 A1, st32 A2); -extern st32 get_hashfunc_46(st32 A1, st32 A2); -extern st32 get_hashfunc_47(st32 A1, st32 A2); -extern st32 get_hashfunc_48(st32 A1, st32 A2); -extern st32 get_hashfunc_49(st32 A1, st32 A2); -extern st32 get_hashfunc_50(st32 A1, st32 A2); -extern st32 get_hashfunc_51(st32 A1, st32 A2); -extern st32 get_hashfunc_52(st32 A1, st32 A2); -extern st32 get_hashfunc_53(st32 A1, st32 A2); -extern st32 get_hashfunc_54(st32 A1, st32 A2); -extern st32 get_hashfunc_55(st32 A1, st32 A2); -extern st32 get_hashfunc_56(st32 A1, st32 A2); -extern st32 get_hashfunc_57(st32 A1, st32 A2); -extern st32 get_hashfunc_58(st32 A1, st32 A2); -extern st32 get_hashfunc_59(st32 A1, st32 A2); -extern st32 get_hashfunc_60(st32 A1, st32 A2); -extern st32 get_hashfunc_61(st32 A1, st32 A2); -extern st32 get_hashfunc_62(st32 A1, st32 A2); -extern st32 get_hashfunc_63(st32 A1, st32 A2); -extern st32 get_hashfunc_64(st32 A1, st32 A2); -extern st32 get_hashfunc_65(st32 A1, st32 A2); -extern st32 get_hashfunc_66(st32 A1, st32 A2); -extern st32 get_hashfunc_67(st32 A1, st32 A2); -extern st32 get_hashfunc_68(st32 A1, st32 A2); -extern st32 get_hashfunc_69(st32 A1, st32 A2); -extern st32 get_hashfunc_70(st32 A1, st32 A2); -extern st32 get_hashfunc_71(st32 A1, st32 A2); -extern st32 get_hashfunc_72(st32 A1, st32 A2); -extern st32 get_hashfunc_73(st32 A1, st32 A2); -extern st32 get_hashfunc_74(st32 A1, st32 A2); -extern st32 get_hashfunc_75(st32 A1, st32 A2); -extern st32 get_hashfunc_76(st32 A1, st32 A2); -extern st32 get_hashfunc_77(st32 A1, st32 A2); -extern st32 get_hashfunc_78(st32 A1, st32 A2); -extern st32 get_hashfunc_79(st32 A1, st32 A2); -extern st32 get_hashfunc_80(st32 A1, st32 A2); -extern st32 get_hashfunc_81(st32 A1, st32 A2); -extern st32 get_hashfunc_82(st32 A1, st32 A2); -extern st32 get_hashfunc_83(st32 A1, st32 A2); -extern st32 get_hashfunc_84(st32 A1, st32 A2); -extern st32 get_hashfunc_85(st32 A1, st32 A2); -extern st32 get_hashfunc_86(st32 A1, st32 A2); -extern st32 get_hashfunc_87(st32 A1, st32 A2); -extern st32 get_hashfunc_88(st32 A1, st32 A2); -extern st32 get_hashfunc_89(st32 A1, st32 A2); -extern st32 get_hashfunc_90(st32 A1, st32 A2); -extern st32 get_hashfunc_91(st32 A1, st32 A2); diff --git a/librz/arch/isa/tms320/c55x_plus/hashvector.h b/librz/arch/isa/tms320/c55x_plus/hashvector.h deleted file mode 100644 index 54e124b2bf..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/hashvector.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef HASHVECTOR_H -#define HASHVECTOR_H - -#include - -typedef struct { - st32 code; - st32 (*hash_func)(st32 A1, st32 A2); -} HASHCODE_ENTRY_T; - -#endif diff --git a/librz/arch/isa/tms320/c55x_plus/utils.c b/librz/arch/isa/tms320/c55x_plus/utils.c deleted file mode 100644 index 5f855ed166..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/utils.c +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2013 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include -#include -#include "utils.h" - -static char hex_str[] = "01234567890abcdef"; - -// TODO: Add in a Coverity modelling file -char *strcat_dup(char *s1, char *s2, st32 n_free) { - char *res; - ut32 len_s1 = s1 ? strlen(s1) : 0; - ut32 len_s2 = s2 ? strlen(s2) : 0; - - if (!(res = (char *)malloc(len_s1 + len_s2 + 1))) { - return NULL; - } - if (len_s1 > 0) { - memcpy(res, s1, len_s1); - } - if (len_s2 > 0) { - memcpy(res + len_s1, s2, len_s2); - } - res[len_s1 + len_s2] = '\0'; - if (n_free == 1) { - RZ_FREE(s1); - } else if (n_free == 2) { - RZ_FREE(s2); - } else if (n_free == 3) { - RZ_FREE(s1); - RZ_FREE(s2); - } - return res; -} - -char *get_hex_str(ut32 hex_num) { - char aux[3]; - - aux[2] = '\0'; - aux[1] = hex_str[hex_num & 0xF]; - aux[0] = hex_str[(hex_num >> 4) & 0xF]; - - return rz_str_dup(aux); -} diff --git a/librz/arch/isa/tms320/c55x_plus/utils.h b/librz/arch/isa/tms320/c55x_plus/utils.h deleted file mode 100644 index 137ccc10f0..0000000000 --- a/librz/arch/isa/tms320/c55x_plus/utils.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2013-2021 th0rpe -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef UUTILS_H -#define UUTILS_H - -#include -#include -#define C55PLUS_DEBUG 0 - -char *strcat_dup(char *s1, char *s2, st32 n_free); -char *get_hex_str(ut32 hex_num); - -#endif diff --git a/librz/arch/isa/tms320/tms320_dasm.c b/librz/arch/isa/tms320/tms320_dasm.c deleted file mode 100644 index bea60f272e..0000000000 --- a/librz/arch/isa/tms320/tms320_dasm.c +++ /dev/null @@ -1,1284 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov -// SPDX-License-Identifier: LGPL-3.0-only - -#include -#include - -/* public headers */ -#include -#include -#include - -/* private headers */ -#include "tms320_dasm.h" -#include "tms320c55x_insn.h" - -#include "c55x_plus/c55plus.h" - -#ifndef get_bits -#define get_bits(av, af, an) (((av) >> (af)) & ((2 << (an - 1)) - 1)) -#endif - -/** - * \return unsigned int to be used as "0x%04X" - */ -static inline unsigned int be16(ut16 v) { - return ((v & 0xff) << 8) | (v >> 8); -} - -/** - * \return unsigned int to be used as "0x%06X" - */ -static inline unsigned int be24(ut32 v) { - return ((v & 0xff) << 16) | (v & 0xff00) | ((v & 0xff0000) >> 16); -} - -/* - * TMS320 disassembly engine implementation - */ - -int run_f_list(tms320_dasm_t *dasm) { - ut32 temp; - insn_flag_t *flag; - - if (!dasm->insn->f_list) { - return 1; - } - - for (flag = dasm->insn->f_list; !f_list_last(flag); flag++) { - switch (flag->v) { - case TMS320_FLAG_E: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, E, temp); - break; - case TMS320_FLAG_R: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, R, temp); - break; - case TMS320_FLAG_U: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, U, temp); - break; - case TMS320_FLAG_u: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, u, temp); - break; - case TMS320_FLAG_g: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, g, temp); - break; - case TMS320_FLAG_r: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, r, temp); - break; - case TMS320_FLAG_t: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, t, temp); - break; - - case TMS320_FLAG_k3: - temp = get_bits(dasm->opcode64, flag->f, 3); - set_field_value(dasm, k3, temp); - break; - case TMS320_FLAG_k4: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, k4, temp); - break; - case TMS320_FLAG_k5: - temp = get_bits(dasm->opcode64, flag->f, 5); - set_field_value(dasm, k5, temp); - break; - case TMS320_FLAG_k6: - temp = get_bits(dasm->opcode64, flag->f, 6); - set_field_value(dasm, k6, temp); - break; - case TMS320_FLAG_k8: - temp = get_bits(dasm->opcode64, flag->f, 8); - set_field_value(dasm, k8, temp); - break; - case TMS320_FLAG_k12: - temp = get_bits(dasm->opcode64, flag->f, 12); - set_field_value(dasm, k12, temp); - break; - case TMS320_FLAG_k16: - temp = get_bits(dasm->opcode64, flag->f, 16); - set_field_value(dasm, k16, temp); - break; - - case TMS320_FLAG_l1: - temp = get_bits(dasm->opcode64, flag->f, 1); - set_field_value(dasm, l1, temp); - break; - case TMS320_FLAG_l3: - temp = get_bits(dasm->opcode64, flag->f, 3); - set_field_value(dasm, l3, temp); - break; - case TMS320_FLAG_l7: - temp = get_bits(dasm->opcode64, flag->f, 7); - set_field_value(dasm, l7, temp); - break; - case TMS320_FLAG_l16: - temp = get_bits(dasm->opcode64, flag->f, 16); - set_field_value(dasm, l16, temp); - break; - - case TMS320_FLAG_K8: - temp = get_bits(dasm->opcode64, flag->f, 8); - set_field_value(dasm, K8, temp); - break; - case TMS320_FLAG_K16: - temp = get_bits(dasm->opcode64, flag->f, 16); - set_field_value(dasm, K16, temp); - break; - - case TMS320_FLAG_L7: - temp = get_bits(dasm->opcode64, flag->f, 7); - set_field_value(dasm, L7, temp); - break; - case TMS320_FLAG_L8: - temp = get_bits(dasm->opcode64, flag->f, 8); - set_field_value(dasm, L8, temp); - break; - case TMS320_FLAG_L16: - temp = get_bits(dasm->opcode64, flag->f, 16); - set_field_value(dasm, L16, temp); - break; - - case TMS320_FLAG_P8: - temp = get_bits(dasm->opcode64, flag->f, 8); - set_field_value(dasm, P8, temp); - break; - case TMS320_FLAG_P24: - temp = get_bits(dasm->opcode64, flag->f, 24); - set_field_value(dasm, P24, temp); - break; - - case TMS320_FLAG_D16: - temp = get_bits(dasm->opcode64, flag->f, 16); - set_field_value(dasm, D16, temp); - break; - - case TMS320_FLAG_SHFT: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, SHFT, temp); - break; - case TMS320_FLAG_SHIFTW: - temp = get_bits(dasm->opcode64, flag->f, 6); - set_field_value(dasm, SHIFTW, temp); - break; - - case TMS320_FLAG_CCCCCCC: - temp = get_bits(dasm->opcode64, flag->f, 7); - set_field_value(dasm, CCCCCCC, temp); - break; - case TMS320_FLAG_AAAAAAAI: - temp = get_bits(dasm->opcode64, flag->f, 8); - set_field_value(dasm, AAAAAAAI, temp); - break; - - case TMS320_FLAG_uu: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, uu, temp); - break; - case TMS320_FLAG_cc: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, cc, temp); - break; - case TMS320_FLAG_ss: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, ss, temp); - break; - case TMS320_FLAG_dd: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, dd, temp); - break; - case TMS320_FLAG_mm: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, mm, temp); - break; - case TMS320_FLAG_vv: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, vv, temp); - break; - case TMS320_FLAG_tt: - temp = get_bits(dasm->opcode64, flag->f, 2); - set_field_value(dasm, tt, temp); - break; - - case TMS320_FLAG_XSSS: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, XSSS, temp); - break; - case TMS320_FLAG_XDDD: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, XDDD, temp); - break; - case TMS320_FLAG_FSSS: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, FSSS, temp); - break; - case TMS320_FLAG_FDDD: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, FDDD, temp); - break; - case TMS320_FLAG_XACS: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, XACS, temp); - break; - case TMS320_FLAG_XACD: - temp = get_bits(dasm->opcode64, flag->f, 4); - set_field_value(dasm, XACD, temp); - break; - - case TMS320_FLAG_SS: - temp = get_bits(dasm->opcode64, flag->f, 2); - if (!field_valid(dasm, SS)) { - set_field_value(dasm, SS, temp); - } else { - set_field_value(dasm, SS2, temp); - } - break; - case TMS320_FLAG_DD: - temp = get_bits(dasm->opcode64, flag->f, 2); - if (!field_valid(dasm, DD)) { - set_field_value(dasm, DD, temp); - } else { - set_field_value(dasm, DD2, temp); - } - break; - - case TMS320_FLAG_XXX: - temp = get_bits(dasm->opcode64, flag->f, 3); - set_field_value(dasm, Xmem_reg, temp); - break; - case TMS320_FLAG_MMM: - temp = get_bits(dasm->opcode64, flag->f, 3); - if (!field_valid(dasm, Xmem_mmm)) { - set_field_value(dasm, Xmem_mmm, temp); - } else { - set_field_value(dasm, Ymem_mmm, temp); - } - break; - case TMS320_FLAG_Y: - temp = get_bits(dasm->opcode64, flag->f, 1) << 0; - if (!field_valid(dasm, Ymem_reg)) { - set_field_value(dasm, Ymem_reg, temp); - } else { - field_value(dasm, Ymem_reg) |= temp; - } - break; - case TMS320_FLAG_YY: - temp = get_bits(dasm->opcode64, flag->f, 2) << 1; - if (!field_valid(dasm, Ymem_reg)) { - set_field_value(dasm, Ymem_reg, temp); - } else { - field_value(dasm, Ymem_reg) |= temp; - } - break; - - default: - printf("TODO: unknown opcode flag %02x\n", flag->v); - return 0; - } - } - - return 1; -} - -int run_m_list(tms320_dasm_t *dasm) { - insn_mask_t *mask; - - if (!dasm->insn->m_list) { - return 1; - } - - for (mask = dasm->insn->m_list; !m_list_last(mask); mask++) { - /* match bits in range [f, f + n] with mask's value */ - if (get_bits(dasm->opcode64, mask->f, mask->n) != mask->v) { - return 0; - } - } - - return 1; -} - -int vreplace(char *string, const char *token, const char *fmt, va_list args) { - char data[64]; - char *pos; - - pos = strstr(string, token); - if (!pos) { - return 0; - } - - vsnprintf(data, sizeof(data), fmt, args); - - memmove(pos + strlen(data), pos + strlen(token), strlen(pos + strlen(token)) + 1); - memmove(pos, data, strlen(data)); - - return 1; -} - -int replace(char *string, const char *token, const char *fmt, ...) { - int result; - va_list args; - - va_start(args, fmt); - result = vreplace(string, token, fmt, args); - va_end(args); - - return result; -} - -void substitute(char *string, const char *token, const char *fmt, ...) { - int result; - va_list args; - - do { - va_start(args, fmt); - result = vreplace(string, token, fmt, args); - va_end(args); - } while (result); -} - -const char *get_xreg_str(ut8 key, char *str) { - static const char *table[16] = { - "ac0", - "ac1", - "ac2", - "ac3", - "xsp", - "xssp", - "xdp", - "xcdp", - "xar0", - "xar1", - "xar2", - "xar3", - "xar4", - "xar5", - "xar6", - "xar7", - }; - - return table[key & 15]; -} - -const char *get_freg_str(ut8 key, char *str) { - static const char *table[16] = { - "ac0", - "ac1", - "ac2", - "ac3", - "t0", - "t1", - "t2", - "t3", - "ar0", - "ar1", - "ar2", - "ar3", - "ar4", - "ar5", - "ar6", - "ar7", - }; - - return table[key & 15]; -} - -const char *get_swap_str(ut8 key, char *str) { - switch (key) { - case 0: return "swap ac0, ac2"; - case 1: return "swap ac1, ac3"; - case 4: return "swap t0, t2"; - case 5: return "swap t1, t3"; - case 8: return "swap ar0, ar2"; - case 9: return "swap ar1, ar3"; - case 12: return "swap ar4, t0"; - case 13: return "swap ar5, t1"; - case 14: return "swap ar6, t2"; - case 15: return "swap ar7, t3"; - case 16: return "swapp ac0, ac2"; - case 20: return "swapp t0, t2"; - case 24: return "swapp ar0, ar2"; - case 28: return "swapp ar4, t0"; - case 30: return "swapp ar6, t2"; - case 44: return "swap4 ar4, t0"; - case 56: return "swap ar0, ar1"; - } - - return "invalid"; -} - -const char *get_relop_str(ut8 key, char *str) { - static const char *table[] = { - "==", "<", ">=", "!=" - }; - - return table[key & 3]; -} - -const char *get_cond_str(ut8 key, char *str, size_t str_sz) { - /* 000 FSSS ... 101 FSSS */ - if ((key >> 4) <= 5) { - static const char *op[6] = { "==", "!=", "<", "<=", ">", ">=" }; - snprintf(str, str_sz, "%s %s 0", get_freg_str(key & 15, NULL), op[(key >> 4) & 7]); - return str; - } - - /* 110 00SS */ - if ((key >> 2) == 0x18) { - snprintf(str, str_sz, "overflow(ac%d)", key & 3); - return str; - } - - /* 111 00SS */ - if ((key >> 2) == 0x1C) { - snprintf(str, str_sz, "!overflow(ac%d)", key & 3); - return str; - } - - switch (key) { - case 0x64: return "tc1"; - case 0x65: return "tc2"; - case 0x66: return "carry"; - case 0x74: return "!tc1"; - case 0x75: return "!tc2"; - case 0x76: - return "!carry"; - /* "&" operation */ - case 0x68: return "tc1 & tc2"; - case 0x69: return "tc1 & !tc2"; - case 0x6A: return "!tc1 & tc2"; - case 0x6B: - return "!tc1 & !tc2"; - /* "|" operation */ - case 0x78: return "tc1 | tc2"; - case 0x79: return "tc1 | !tc2"; - case 0x7A: return "!tc1 | tc2"; - case 0x7B: - return "!tc1 | !tc2"; - /* "^" operation */ - case 0x7C: return "tc1 ^ tc2"; - case 0x7D: return "tc1 ^ !tc2"; - case 0x7E: return "!tc1 ^ tc2"; - case 0x7F: return "!tc1 ^ !tc2"; - } - - return "invalid"; -} - -const char *get_v_str(ut8 key, char *str) { - static const char *table[2] = { - "carry", - "tc2", - }; - - return table[key & 1]; -} - -const char *get_t_str(ut8 key, char *str) { - static const char *table[2] = { - "tc1", - "tc2", - }; - - return table[key & 1]; -} - -const char *get_cmem_str(ut8 key, char *str) { - static const char *table[4] = { - "*cdp", - "*cdp+", - "*cdp-", - "*(cdp+t0)", - }; - - return table[key & 3]; -} - -const char *get_smem_str(ut8 key, char *str, size_t str_sz) { - // direct memory - - if ((key & 0x01) == 0) { -#ifdef IDA_COMPATIBLE_MODE - snprintf(str, str_sz, "*sp(#%Xh)", key >> 1); -#else - snprintf(str, str_sz, "@0x%02X", key >> 1); -#endif - return str; - } - - // indirect memory - - switch (key) { - case 0x11: return "abs16(k16)"; - case 0x31: return "*(k23)"; - case 0x51: return "port(k16)"; - case 0x71: return "*cdp"; - case 0x91: return "*cdp+"; - case 0xB1: return "*cdp-"; - case 0xD1: return "*cdp(K16)"; - case 0xF1: return "*+cdp(K16)"; - } - - switch (key & 0x1F) { - case 0x01: return "*ARn"; - case 0x03: return "*ARn+"; - case 0x05: - return "*ARn-"; - // TODO: - // C54CM:0 => *(ARn + T0) - // C54CM:1 => *(ARn + AR0) - case 0x07: - return "*(ARn + t0)"; - // TODO: - // C54CM:0 => *(ARn - t0) - // C54CM:1 => *(ARn - AR0) - case 0x09: - return "*(ARn - t0)"; - // TODO: - // C54CM:0 => *ARn(t0) - // C54CM:1 => *ARn(AR0) - case 0x0B: return "*ARn(t0)"; - case 0x0D: return "*ARn(k16)"; - case 0x0F: - return "*+ARn(k16)"; - // TODO: - // ARMS:0 => *(ARn + T1) - // ARMS:1 => *ARn(short(1)) - case 0x13: - return "*(ARn + t1)"; - // TODO: - // ARMS:0 => *(ARn - T1) - // ARMS:1 => *ARn(short(2)) - case 0x15: - return "*(ARn - t1)"; - // TODO: - // ARMS:0 => *ARn(T1) - // ARMS:1 => *ARn(short(3)) - case 0x17: - return "*ARn(t1)"; - // TODO: - // ARMS:0 => *+ARn - // ARMS:1 => *ARn(short(4)) - case 0x19: - return "*+ARn"; - // TODO: - // ARMS:0 => *-ARn - // ARMS:1 => *ARn(short(5)) - case 0x1B: - return "*-ARn"; - // TODO: - // ARMS:0 => *(ARn + t0b) - // ARMS:1 => *ARn(short(6)) - case 0x1D: - return "*(ARn + t0b)"; - // TODO: - // ARMS:0 => *(arn - t0b) - // ARMS:1 => *arn(short(7)) - case 0x1F: return "*(ARn - t0b)"; - } - - return "invalid"; -} - -const char *get_mmm_str(ut8 key, char *str) { - switch (key & 7) { - default: - case 0x00: return "*ARn"; - case 0x01: return "*ARn+"; - case 0x02: - return "*ARn-"; - // TODO: - // C54CM:0 => *(ARn + T0) - // C54CM:1 => *(ARn + AR0) - case 0x03: return "*(ARn + t0)"; - case 0x04: - return "*(ARn + t1)"; - // TODO: - // C54CM:0 => *(ARn - t0) - // C54CM:1 => *(ARn - AR0) - case 0x05: return "*(ARn - t0)"; - case 0x06: - return "*(ARn - t1)"; - // TODO: - // C54CM:0 => *ARn(T0) - // C54CM:1 => *ARn(AR0) - case 0x07: return "*ARn(t0)"; - }; -} - -/* - * syntax decoders - */ - -void decode_bits(tms320_dasm_t *dasm) { - // rounding - if (field_valid(dasm, R)) { - substitute(dasm->syntax, "[r]", "%s", field_value(dasm, R) ? "r" : ""); - } - - // unsigned - if (field_valid(dasm, u)) { - substitute(dasm->syntax, "[u]", "%s", field_value(dasm, u) ? "u" : ""); - } - - // 40 keyword - if (field_valid(dasm, g)) { - substitute(dasm->syntax, "[40]", "%s", field_value(dasm, g) ? "40" : ""); - } - - // T3 update - if (field_valid(dasm, U)) { - substitute(dasm->syntax, "[T3 = ]", "%s", field_value(dasm, U) ? "t3=" : ""); - } -} - -void decode_braces(tms320_dasm_t *dasm) { - char *pos; - - pos = strstr(dasm->syntax, "[(saturate]"); - if (pos) { - replace(pos, "[)", ")["); - replace(dasm->syntax, "[(saturate]", "%s", "(saturate"); - } - - if (field_valid(dasm, R)) { - pos = strstr(dasm->syntax, "[rnd(]"); - if (pos) { - replace(pos, "[)", "%s", field_value(dasm, R) ? ")[" : "["); - replace(dasm->syntax, "[rnd(]", "%s", field_value(dasm, R) ? "rnd(" : ""); - } - } - - if (field_valid(dasm, u)) { - pos = strstr(dasm->syntax, "[uns(]"); - if (pos) { - replace(pos, "[)", "%s", field_value(dasm, u) ? ")[" : "["); - replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, u) ? "uns(" : ""); - } - } - - if (field_valid(dasm, uu)) { - bool parallel = !!strstr(dasm->syntax, "::"); - - // first - replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 2 ? "uns(" : ""); - replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 2 ? ")" : ""); - - if (parallel) { - replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 2 ? "uns(" : ""); - replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 2 ? ")" : ""); - } - - // second - replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 1 ? "uns(" : ""); - replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 1 ? ")" : ""); - - if (parallel) { - replace(dasm->syntax, "[uns(]", "%s", field_value(dasm, uu) & 1 ? "uns(" : ""); - replace(dasm->syntax, "[)]", "%s", field_value(dasm, uu) & 1 ? ")" : ""); - } - } - - // remove rudiments - - substitute(dasm->syntax, "[]", "%s", ""); -} - -void decode_constants(tms320_dasm_t *dasm) { - // signed constant - - if (field_valid(dasm, K8)) { - substitute(dasm->syntax, "K8", "0x%02X", field_value(dasm, K8)); - } - if (field_valid(dasm, K16)) { - substitute(dasm->syntax, "K16", "0x%04X", be16(field_value(dasm, K16))); - } - - // unsigned constant - - if (field_valid(dasm, k4)) { - substitute(dasm->syntax, "K4", "0x%01X", field_value(dasm, k4)); - } - if (field_valid(dasm, k5)) { - substitute(dasm->syntax, "k5", "0x%02X", field_value(dasm, k5)); - } - if (field_valid(dasm, k8)) { - substitute(dasm->syntax, "k8", "0x%02X", field_value(dasm, k8)); - } - - if (field_valid(dasm, k12)) { - substitute(dasm->syntax, "k12", "0x%03X", be16(field_value(dasm, k12))); - } - if (field_valid(dasm, k16)) { - substitute(dasm->syntax, "k16", "0x%04X", be16(field_value(dasm, k16))); - } - - if (field_valid(dasm, k4) && field_valid(dasm, k3)) { - substitute(dasm->syntax, "k7", "0x%02X", (field_value(dasm, k3) << 4) | field_value(dasm, k4)); - } - if (field_valid(dasm, k4) && field_valid(dasm, k5)) { - substitute(dasm->syntax, "k9", "0x%03X", (field_value(dasm, k5) << 4) | field_value(dasm, k4)); - } - if (field_valid(dasm, k4) && field_valid(dasm, k8)) { - substitute(dasm->syntax, "k12", "0x%03X", (field_value(dasm, k8) << 4) | field_value(dasm, k4)); - } - - // dasm address label - - if (field_valid(dasm, D16)) { - substitute(dasm->syntax, "D16", "0x%04X", be16(field_value(dasm, D16))); - } - - // immediate shift value - - if (field_valid(dasm, SHFT)) { - substitute(dasm->syntax, "#SHFT", "0x%01X", field_value(dasm, SHFT)); - } - if (field_valid(dasm, SHIFTW)) { - substitute(dasm->syntax, "#SHIFTW", "0x%02X", field_value(dasm, SHIFTW)); - } -} - -void decode_addresses(tms320_dasm_t *dasm) { - // program address label - - if (field_valid(dasm, L7)) { - substitute(dasm->syntax, "L7", "0x%02X", field_value(dasm, L7)); - } - if (field_valid(dasm, L8)) { - substitute(dasm->syntax, "L8", "0x%02X", field_value(dasm, L8)); - } - if (field_valid(dasm, L16)) { - substitute(dasm->syntax, "L16", "0x%04X", be16(field_value(dasm, L16))); - } - - // program address label - - if (field_valid(dasm, l1) && field_valid(dasm, l3)) { - substitute(dasm->syntax, "l4", "0x%01X", (field_value(dasm, l3) << 1) | field_value(dasm, l1)); - } - - // program memory address - - if (field_valid(dasm, l7)) { - substitute(dasm->syntax, "pmad", "0x%02X", field_value(dasm, l7)); - } - if (field_valid(dasm, l16)) { - substitute(dasm->syntax, "pmad", "0x%04X", be16(field_value(dasm, l16))); - } - - // program or dasm address label - - if (field_valid(dasm, P8)) { - substitute(dasm->syntax, "P8", "0x%02X", field_value(dasm, P8)); - } - if (field_valid(dasm, P24)) { - substitute(dasm->syntax, "P24", "0x%06X", be24(field_value(dasm, P24))); - } -} - -void decode_swap(tms320_dasm_t *dasm) { - char tmp[64]; - - if (field_valid(dasm, k6)) { - substitute(dasm->syntax, "SWAP ( )", get_swap_str(field_value(dasm, k6), tmp)); - } -} - -void decode_relop(tms320_dasm_t *dasm) { - if (field_valid(dasm, cc)) { - substitute(dasm->syntax, "RELOP", get_relop_str(field_value(dasm, cc), NULL)); - } -} - -void decode_cond(tms320_dasm_t *dasm) { - char tmp[64]; - - if (field_valid(dasm, CCCCCCC)) { - substitute(dasm->syntax, "cond", "%s", get_cond_str(field_value(dasm, CCCCCCC), tmp, sizeof(tmp))); - } - - substitute(dasm->syntax, "[label, ]", ""); -} - -void decode_registers(tms320_dasm_t *dasm) { - ut8 code = 0; - - // transition register - - if (field_valid(dasm, r)) { - substitute(dasm->syntax, "TRNx", "trn%d", field_value(dasm, r)); - } - - // source and destination temporary registers - - if (field_valid(dasm, ss)) { - substitute(dasm->syntax, "Tx", "t%d", field_value(dasm, ss)); - } - - if (field_valid(dasm, dd)) { - substitute(dasm->syntax, "Tx", "t%d", field_value(dasm, dd)); - } - - // shifted in/out bit values - - if (field_valid(dasm, vv)) { - substitute(dasm->syntax, "BitIn", "%s", get_v_str(field_value(dasm, vv) >> 1, NULL)); - substitute(dasm->syntax, "BitOut", "%s", get_v_str(field_value(dasm, vv) >> 0, NULL)); - } - - // source and destination of CRC instruction - - if (field_valid(dasm, t)) { - substitute(dasm->syntax, "TCx", "%s", get_t_str(field_value(dasm, t), NULL)); - } - - if (field_valid(dasm, tt)) { - substitute(dasm->syntax, "TCx", "%s", get_t_str(field_value(dasm, tt) >> 0, NULL)); - substitute(dasm->syntax, "TCy", "%s", get_t_str(field_value(dasm, tt) >> 1, NULL)); - } - - // source or destination accumulator or extended register - - if (field_valid(dasm, XSSS)) { - substitute(dasm->syntax, "xsrc", "%s", get_xreg_str(field_value(dasm, XSSS), NULL)); - substitute(dasm->syntax, "XAsrc", "%s", get_xreg_str(field_value(dasm, XSSS), NULL)); - } - - if (field_valid(dasm, XDDD)) { - substitute(dasm->syntax, "xdst", "%s", get_xreg_str(field_value(dasm, XDDD), NULL)); - substitute(dasm->syntax, "XAdst", "%s", get_xreg_str(field_value(dasm, XDDD), NULL)); - } - - // source or destination accumulator, auxiliary or temporary register - - if (field_valid(dasm, FSSS) && field_valid(dasm, FDDD)) { - if (field_value(dasm, FSSS) == field_value(dasm, FDDD)) { - substitute(dasm->syntax, "[src,] dst", "dst"); - } else { - substitute(dasm->syntax, "[src,] dst", "src, dst"); - } - } - - if (field_valid(dasm, FSSS) && field_valid(dasm, FDDD)) { - substitute(dasm->syntax, "src1", "%s", get_freg_str(field_value(dasm, FSSS), NULL)); - substitute(dasm->syntax, "src2", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - - substitute(dasm->syntax, "dst1", "%s", get_freg_str(field_value(dasm, FSSS), NULL)); - substitute(dasm->syntax, "dst2", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - } - - code &= 0; - code |= field_valid(dasm, FSSS) ? 0x01 : 0x00; - code |= field_valid(dasm, FDDD) ? 0x02 : 0x00; - - switch (code) { - case 0x01: // FSSS - substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FSSS), NULL)); - break; - case 0x02: // FDDD - substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - substitute(dasm->syntax, "TAy", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - break; - case 0x03: // FSSS FDDD - substitute(dasm->syntax, "TAx", "%s", get_freg_str(field_value(dasm, FSSS), NULL)); - substitute(dasm->syntax, "TAy", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - break; - } - - if (field_valid(dasm, FSSS)) { - substitute(dasm->syntax, "src", "%s", get_freg_str(field_value(dasm, FSSS), NULL)); - } - - if (field_valid(dasm, FDDD)) { - substitute(dasm->syntax, "dst", "%s", get_freg_str(field_value(dasm, FDDD), NULL)); - } - - if (field_valid(dasm, XACS)) { - substitute(dasm->syntax, "XACsrc", "%s", get_xreg_str(field_value(dasm, XACS), NULL)); - } - - if (field_valid(dasm, XACD)) { - substitute(dasm->syntax, "XACdst", "%s", get_xreg_str(field_value(dasm, XACD), NULL)); - } - - // source and destination accumulator registers - - code &= 0; - code |= field_valid(dasm, SS) ? 0x01 : 0x00; - code |= field_valid(dasm, SS2) ? 0x02 : 0x00; - code |= field_valid(dasm, DD) ? 0x10 : 0x00; - code |= field_valid(dasm, DD2) ? 0x20 : 0x00; - - switch (code) { - case 0x01: // SS - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS)); - break; - case 0x03: // SSSS - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS)); - substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, SS2)); - break; - case 0x11: // SS DD - if (field_value(dasm, SS) == field_value(dasm, DD)) { - substitute(dasm->syntax, "[, ACy]", ""); - substitute(dasm->syntax, "[ACx,] ACy", "ACy"); - } else { - substitute(dasm->syntax, "[, ACy]", ", ACy"); - substitute(dasm->syntax, "[ACx,] ACy", "ACx, ACy"); - } - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS)); - substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, DD)); - break; - case 0x33: // SSSS DDDD - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, SS)); - substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, SS2)); - substitute(dasm->syntax, "ACz", "ac%d", field_value(dasm, DD)); - substitute(dasm->syntax, "ACw", "ac%d", field_value(dasm, DD2)); - break; - case 0x10: // DD - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, DD)); - break; - case 0x30: // DDDD - substitute(dasm->syntax, "ACx", "ac%d", field_value(dasm, DD)); - substitute(dasm->syntax, "ACy", "ac%d", field_value(dasm, DD2)); - break; - } -} - -void decode_addressing_modes(tms320_dasm_t *dasm) { - // Cmem - - if (field_valid(dasm, mm)) { - substitute(dasm->syntax, "Cmem", "%s", get_cmem_str(field_value(dasm, mm), NULL)); - } - - // Xmem and Ymem - - if (field_valid(dasm, Xmem_reg) && field_valid(dasm, Xmem_mmm)) { - substitute(dasm->syntax, "Xmem", "%s", get_mmm_str(field_value(dasm, Xmem_mmm), NULL)); - substitute(dasm->syntax, "ARn", "ar%d", field_value(dasm, Xmem_reg)); - } - - if (field_valid(dasm, Ymem_reg) && field_valid(dasm, Ymem_mmm)) { - substitute(dasm->syntax, "Ymem", "%s", get_mmm_str(field_value(dasm, Ymem_mmm), NULL)); - substitute(dasm->syntax, "ARn", "ar%d", field_value(dasm, Ymem_reg)); - } - - // Lmem and Smem - - if (field_valid(dasm, AAAAAAAI)) { - char str[64], tmp[64]; - - snprintf(tmp, sizeof(tmp), "%s", get_smem_str(field_value(dasm, AAAAAAAI), str, sizeof(str))); - - if (field_value(dasm, AAAAAAAI) & 1) { - if (strstr(tmp, "k16")) { - substitute(tmp, "k16", "0x%04X", rz_read_be16(&dasm->stream + dasm->length)); - dasm->length += 2; - } else if (strstr(tmp, "k23")) { - substitute(tmp, "k23", "0x%06X", rz_read_be24(&dasm->stream + dasm->length)); - dasm->length += 3; - } else if (strstr(tmp, "K16")) { - substitute(tmp, "K16", "0x%04X", rz_read_be16(&dasm->stream + dasm->length)); - dasm->length += 2; - } - - substitute(tmp, "ARn", "ar%d", field_value(dasm, AAAAAAAI) >> 5); - } - - substitute(dasm->syntax, "Smem", "%s", tmp); - substitute(dasm->syntax, "Lmem", "%s", tmp); - } -} - -void decode_qualifiers(tms320_dasm_t *dasm) { - switch (dasm->stream[dasm->length]) { - case 0x98: - // 1001 1000 - mmap - break; - - case 0x99: - // 1001 1001 - port(Smem) - break; - case 0x9a: - // 1001 1010 - port(Smem) - break; - - case 0x9c: - // 1001 1100 - .LR - set_field_value(dasm, q_lr, 1); - break; - case 0x9d: - // 1001 1101 - .CR - set_field_value(dasm, q_cr, 1); - break; - } -} - -static insn_item_t *finalize(tms320_dasm_t *dasm) { - // remove odd spaces - - substitute(dasm->syntax, " ", "%s", " "); - - // add some qualifiers - - if (field_value(dasm, q_lr)) { - replace(dasm->syntax, " ", ".lr "); - } - if (field_value(dasm, q_cr)) { - replace(dasm->syntax, " ", ".cr "); - } - - return dasm->insn; -} - -insn_item_t *decode_insn(tms320_dasm_t *dasm) { - dasm->length = dasm->head->size; - - snprintf(dasm->syntax, sizeof(dasm->syntax), - field_valid(dasm, E) && field_value(dasm, E) ? "|| %s" : "%s", dasm->insn->syntax); - - decode_bits(dasm); - decode_braces(dasm); - decode_qualifiers(dasm); - - decode_constants(dasm); - decode_addresses(dasm); - - decode_swap(dasm); - decode_relop(dasm); - decode_cond(dasm); - - decode_registers(dasm); - decode_addressing_modes(dasm); - - return finalize(dasm); -} - -insn_item_t *decode_insn_head(tms320_dasm_t *dasm) { - run_f_list(dasm); - - if (dasm->insn->i_list) { - dasm->insn = dasm->insn->i_list; - while (!i_list_last(dasm->insn)) { - if (run_m_list(dasm) && run_f_list(dasm)) { - break; - } - dasm->insn++; - } - } - - if (!i_list_last(dasm->insn)) { - return decode_insn(dasm); - } - - return NULL; -} - -static ut8 c55x_e_list[] = { - 0xF8, - 0x60, /* 0110 0lll */ - 0xF0, - 0xA0, /* 1010 FDDD */ - 0xFC, - 0xB0, /* 1011 00DD */ - 0xF0, - 0xC0, /* 1100 FSSS */ - 0xFC, - 0xBC, /* 1011 11SS */ - 0x00, - 0x00, -}; - -insn_head_t *lookup_insn_head(tms320_dasm_t *dasm) { - ut8 *e_list = NULL; - /* handle some exceptions */ - - if (tms320_f_get_cpu(dasm) == TMS320_F_CPU_C55X) { - e_list = c55x_e_list; - } - while (e_list && (e_list[0] && e_list[1])) { - if ((dasm->opcode & e_list[0]) == e_list[1]) { - dasm->head = ht_up_find(dasm->map, e_list[1], NULL); - break; - } - e_list += 2; - } - if (!dasm->head) { - dasm->head = ht_up_find(dasm->map, dasm->opcode, NULL); - if (!dasm->head) { - dasm->head = ht_up_find(dasm->map, (dasm->opcode & 0xfe), NULL); - } - } - dasm->insn = dasm->head ? &dasm->head->insn : NULL; - return dasm->head; -} - -static void init_dasm(tms320_dasm_t *dasm, const ut8 *stream, int len) { - strcpy(dasm->syntax, "invalid"); - memset(dasm->stream, 0, sizeof(dasm->stream)); - memcpy(dasm->stream, stream, RZ_MIN(sizeof(dasm->stream), len)); - dasm->opcode64 = rz_read_le64(dasm->stream); - - dasm->status = 0; - dasm->length = 0; - - memset(&dasm->f, 0, sizeof(dasm->f)); - - dasm->head = NULL; - dasm->insn = NULL; -} - -static int full_insn_size(tms320_dasm_t *dasm) { - int qualifier_size = 0; - - if (field_value(dasm, q_cr)) { - qualifier_size = 1; - } - if (field_value(dasm, q_lr)) { - qualifier_size = 1; - } - - return dasm->length + qualifier_size; -} - -/* - * TMS320 disassembly engine public interface - */ - -int tms320_dasm(tms320_dasm_t *dasm, const ut8 *stream, int len) { - init_dasm(dasm, stream, len); - - if (tms320_f_get_cpu(dasm) != TMS320_F_CPU_C55X_PLUS) { - if (lookup_insn_head(dasm) && decode_insn_head(dasm)) { - if (dasm->length > len) { - dasm->status |= TMS320_S_INVAL; - } - } - } else { - c55x_plus_disassemble(dasm, stream, len); - } - - if (strstr(dasm->syntax, "invalid")) { - dasm->status |= TMS320_S_INVAL; - } - - if (dasm->status & TMS320_S_INVAL) { - strcpy(dasm->syntax, "invalid"), dasm->length = 1; - dasm->insn_id = TMS320C55_INS_INVALID; - } else { - /* Resolve the instruction ID from the decoded mnemonic. The - * syntax string is the disassembler's ground truth: it reflects - * the exact sub-instruction selected by the operand mask lists, - * which a static leading-byte->head mapping cannot capture (e.g. - * 0x95 0x8F decodes to TRAP, not INTR; 0x48 0x05 to RETI, not - * RPT). Falls back to the matched table head's id for C55x when - * the mnemonic isn't in the table (should not normally happen). */ - dasm->insn_id = tms320c55x_insn_id_from_syntax(dasm->syntax); - if (dasm->insn_id == TMS320C55_INS_INVALID && - tms320_f_get_cpu(dasm) != TMS320_F_CPU_C55X_PLUS && dasm->head) { - dasm->insn_id = dasm->head->id; - } - } - - return full_insn_size(dasm); -} - -// insn_head_t c55x_list[] -#include "c55x/table.h" - -/* Resolve the C55x instruction ID for a byte sequence by running the - * disassembler and mapping its decoded mnemonic to a TMS320C55InsID. This - * is the accurate path: unlike a static leading-byte table, it honours - * the per-instruction mask lists, so multi-form heads (e.g. 0x50, which - * decodes to either POPBOTH or SFTL depending on operand bits) resolve - * to the mnemonic actually decoded. - * - * A single cached dasm instance is used to avoid per-call init/fini. The - * cache is process-wide and the disassembler is stateless across calls - * (init_dasm() resets it each time), so this is safe for the analyzer's - * single-threaded use. */ -ut16 tms320c55x_insn_id_decode(const ut8 *buf, int len) { - static tms320_dasm_t dasm; - static int initialized = 0; - if (!initialized) { - tms320_dasm_init(&dasm); - tms320_f_set_cpu(&dasm, TMS320_F_CPU_C55X); - initialized = 1; - } - if (!buf || len < 1) { - return TMS320C55_INS_INVALID; - } - tms320_dasm(&dasm, buf, len); - return dasm.insn_id; -} - -const char *tms320c55x_insn_syntax_decode(const ut8 *buf, int len) { - static tms320_dasm_t dasm; - static int initialized = 0; - if (!initialized) { - tms320_dasm_init(&dasm); - tms320_f_set_cpu(&dasm, TMS320_F_CPU_C55X); - initialized = 1; - } - if (!buf || len < 1) { - return NULL; - } - tms320_dasm(&dasm, buf, len); - return dasm.syntax; -} - -/* C55x+ counterpart of tms320c55x_insn_id_decode(): run the C55x+ - * token decoder over the byte sequence and return the TMS320C55InsID of the - * decoded mnemonic. Uses a separate cached dasm instance pinned to the - * C55x+ feature set. */ -ut16 tms320c55x_plus_insn_id_decode(const ut8 *buf, int len) { - static tms320_dasm_t dasm; - static int initialized = 0; - if (!initialized) { - tms320_dasm_init(&dasm); - tms320_f_set_cpu(&dasm, TMS320_F_CPU_C55X_PLUS); - initialized = 1; - } - if (!buf || len < 1) { - return TMS320C55_INS_INVALID; - } - tms320_dasm(&dasm, buf, len); - return dasm.insn_id; -} - -int tms320_dasm_init(tms320_dasm_t *dasm) { - int i = 0; - - if (dasm->map) { - /* already initialized */ - return 0; - } - - dasm->map = ht_up_new(NULL, NULL); - if (!dasm->map) { - return 0; - } - for (i = 0; i < RZ_ARRAY_SIZE(c55x_list); i++) { - ht_up_insert(dasm->map, c55x_list[i].byte, &c55x_list[i]); - } - - tms320_f_set_cpu(dasm, TMS320_F_CPU_C55X); - - return 0; -} - -int tms320_dasm_fini(tms320_dasm_t *dasm) { - if (dasm) { - if (dasm->map) { - ht_up_free(dasm->map); - } - /* avoid double free */ - memset(dasm, 0, sizeof(tms320_dasm_t)); - } - return 0; -} diff --git a/librz/arch/isa/tms320/tms320_dasm.h b/librz/arch/isa/tms320/tms320_dasm.h deleted file mode 100644 index 38124b941f..0000000000 --- a/librz/arch/isa/tms320/tms320_dasm.h +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov -// SPDX-License-Identifier: LGPL-3.0-only - -#ifndef __TMS320_DASM_H__ -#define __TMS320_DASM_H__ - -#define IDA_COMPATIBLE_MODE 1 - -/* forward declarations */ - -struct tms320_instruction; -typedef struct tms320_instruction insn_item_t; - -struct tms320_instruction_mask; -typedef struct tms320_instruction_mask insn_mask_t; - -struct tms320_instruction_flag; -typedef struct tms320_instruction_flag insn_flag_t; - -struct tms320_instruction_head; -typedef struct tms320_instruction_head insn_head_t; - -typedef enum { - TMS320_FLAG_E = 0x10, - TMS320_FLAG_R, - TMS320_FLAG_U, - TMS320_FLAG_u, - TMS320_FLAG_g, - TMS320_FLAG_r, - TMS320_FLAG_t, - - TMS320_FLAG_uu, - TMS320_FLAG_mm, - TMS320_FLAG_cc, - TMS320_FLAG_tt, - TMS320_FLAG_vv, - TMS320_FLAG_ss, - TMS320_FLAG_dd, - TMS320_FLAG_SS, - TMS320_FLAG_DD, - - TMS320_FLAG_k3, - TMS320_FLAG_k4, - TMS320_FLAG_k5, - TMS320_FLAG_k6, - TMS320_FLAG_k8, - TMS320_FLAG_k12, - TMS320_FLAG_k16, - - TMS320_FLAG_K8, - TMS320_FLAG_K16, - - TMS320_FLAG_l1, - TMS320_FLAG_l3, - TMS320_FLAG_l7, - TMS320_FLAG_l16, - - TMS320_FLAG_L7, - TMS320_FLAG_L8, - TMS320_FLAG_L16, - - TMS320_FLAG_P8, - TMS320_FLAG_P24, - TMS320_FLAG_D16, - - TMS320_FLAG_SHFT, - TMS320_FLAG_SHIFTW, - TMS320_FLAG_CCCCCCC, - TMS320_FLAG_AAAAAAAI, - - TMS320_FLAG_FSSS, - TMS320_FLAG_FDDD, - TMS320_FLAG_XSSS, - TMS320_FLAG_XDDD, - TMS320_FLAG_XACS, - TMS320_FLAG_XACD, - - TMS320_FLAG_XXX, - TMS320_FLAG_MMM, - TMS320_FLAG_Y, - TMS320_FLAG_YY, -} insn_flag_e; - -struct tms320_instruction { -#define i_list_last(x) !(((x)->i_list || (x)->m_list || (x)->f_list || (x)->syntax)) - insn_item_t *i_list; - - insn_mask_t *m_list; - insn_flag_t *f_list; - - char *syntax; -}; - -struct tms320_instruction_mask { -#define m_list_last(x) !(((x)->f || (x)->n || (x)->v)) - ut8 f, n, v; /* from, number, value */ -}; - -struct tms320_instruction_flag { -#define f_list_last(x) !(((x)->f || (x)->v)) - ut8 f, v; /* from, value */ -}; - -struct tms320_instruction_head { - ut8 byte; - ut8 size; - ut16 id; ///< TMS320C55InsID for this leading-byte entry (0 = unset/invalid) - insn_item_t insn; -}; - -/* - * TMS320 dasm instance - */ - -typedef struct { - insn_head_t *head; - insn_item_t *insn; - - union { - ut8 opcode; - ut8 stream[8]; - }; - ut64 opcode64; ///< same as rz_read_le64(stream) - -#define TMS320_S_INVAL 0x01 - ut8 status; - ut8 length; - ut16 insn_id; ///< TMS320C55InsID of the decoded instruction (0 if unknown) - char syntax[1024]; - -#define def_field(name, size) \ - unsigned int bf_##name##_valid : 1; \ - unsigned int bf_##name##_value : size - - struct { - def_field(E, 1); - def_field(R, 1); - def_field(U, 1); - def_field(u, 1); - def_field(g, 1); - def_field(r, 1); - def_field(t, 1); - - def_field(k3, 3); - def_field(k4, 4); - def_field(k5, 5); - def_field(k6, 6); - def_field(k8, 8); - def_field(k12, 12); - def_field(k16, 16); - - def_field(l1, 1); - def_field(l3, 3); - def_field(l7, 7); - def_field(l16, 16); - - def_field(K8, 8); - def_field(K16, 16); - - def_field(L7, 7); - def_field(L8, 8); - def_field(L16, 16); - - def_field(P8, 8); - def_field(P24, 24); - - def_field(D16, 16); - - def_field(SHFT, 4); - def_field(SHIFTW, 6); - - def_field(ss, 2); - def_field(dd, 2); - - def_field(uu, 2); - def_field(cc, 2); - def_field(mm, 2); - def_field(vv, 2); - def_field(tt, 2); - - def_field(FSSS, 4); - def_field(FDDD, 4); - def_field(XSSS, 4); - def_field(XDDD, 4); - def_field(XACS, 4); - def_field(XACD, 4); - - def_field(CCCCCCC, 7); - def_field(AAAAAAAI, 8); - - def_field(SS, 2); - def_field(SS2, 2); - def_field(DD, 2); - def_field(DD2, 2); - - // aggregates - - def_field(Xmem_mmm, 3); - def_field(Xmem_reg, 3); - - def_field(Ymem_mmm, 3); - def_field(Ymem_reg, 3); - - // qualifiers - - def_field(q_lr, 1); - def_field(q_cr, 1); - } f; - HtUP *map; - -#define TMS320_F_CPU_C54X 0x0000001 -#define TMS320_F_CPU_C55X 0x0000002 -#define TMS320_F_CPU_C55X_PLUS 0x0000003 -#define TMS320_F_CPU_MASK 0x00000FF - ut32 features; -#define tms320_f_get_cpu(d) ((d)->features & TMS320_F_CPU_MASK) -#define tms320_f_set_cpu(d, v) ((d)->features = ((d)->features & ~TMS320_F_CPU_MASK) | (v)) -} tms320_dasm_t; - -#define field_valid(d, name) \ - (d)->f.bf_##name##_valid -#define field_value(d, name) \ - (d)->f.bf_##name##_value - -#ifdef _MSC_VER -#define set_field_value(d, name, value) \ - { \ - field_valid(d, name) = 1; \ - field_value(d, name) = value; \ - } -#else -#define set_field_value(d, name, value) \ - ({ \ - field_valid(d, name) = 1; \ - field_value(d, name) = value; \ - }) -#endif - -#define LIST_END \ - { 0 } - -#define INSN_MASK(af, an, av) \ - { .f = af, .n = an, .v = av } -#define INSN_FLAG(af, av) \ - { .f = af, .v = TMS320_FLAG_##av } -#define INSN_SYNTAX(...) (char *)#__VA_ARGS__ - -extern int tms320_dasm(tms320_dasm_t *, const ut8 *, int); - -/** Disassemble a byte sequence as plain C55x and return the decoded syntax - * string (owned by a cached static dasm instance; valid until the next call). - * Used by the analysis layer to feed the (syntax-based) RzIL lifter. */ -extern const char *tms320c55x_insn_syntax_decode(const ut8 *, int); - -/** Resolve the C55x instruction ID for a byte sequence by disassembling - * it and mapping the decoded mnemonic to a TMS320C55InsID. Honours the - * per-instruction operand masks (so multi-form leading bytes resolve to - * the mnemonic actually decoded). Returns 0 when the bytes don't decode - * to a known instruction. */ -extern ut16 tms320c55x_insn_id_decode(const ut8 *buf, int len); - -/** C55x+ counterpart of tms320c55x_insn_id_decode(). Resolves the - * TMS320C55InsID by running the C55x+ token decoder over the bytes. */ -extern ut16 tms320c55x_plus_insn_id_decode(const ut8 *buf, int len); - -extern int tms320_dasm_init(tms320_dasm_t *); -extern int tms320_dasm_fini(tms320_dasm_t *); - -#endif /* __TMS320_DASM_H__ */ diff --git a/librz/arch/isa/tms320/tms320c55x_insn.c b/librz/arch/isa/tms320/tms320c55x_insn.c index fab9e4d85b..c1077e7f4b 100644 --- a/librz/arch/isa/tms320/tms320c55x_insn.c +++ b/librz/arch/isa/tms320/tms320c55x_insn.c @@ -14,6 +14,8 @@ static const char *const tms320c55x_insn_names[] = { [TMS320C55_INS_ABDST] = "abdst", [TMS320C55_INS_ABS] = "abs", [TMS320C55_INS_ADD] = "add", + [TMS320C55_INS_ADDV] = "addv", + [TMS320C55_INS_ADDRV] = "addrv", [TMS320C55_INS_ADDSUB] = "addsub", [TMS320C55_INS_ADDSUB2CC] = "addsub2cc", [TMS320C55_INS_ADDSUBCC] = "addsubcc", @@ -41,6 +43,9 @@ static const char *const tms320c55x_insn_names[] = { [TMS320C55_INS_CMP] = "cmp", [TMS320C55_INS_CMPAND] = "cmpand", [TMS320C55_INS_CMPOR] = "cmpor", + [TMS320C55_INS_COPY] = "copy", + [TMS320C55_INS_SWAPP] = "swapp", + [TMS320C55_INS_SWAP4] = "swap4", [TMS320C55_INS_DELAY] = "delay", [TMS320C55_INS_DMAXDIFF] = "dmaxdiff", [TMS320C55_INS_DMINDIFF] = "dmindiff", @@ -69,6 +74,9 @@ static const char *const tms320c55x_insn_names[] = { [TMS320C55_INS_MPYMK] = "mpymk", [TMS320C55_INS_NEG] = "neg", [TMS320C55_INS_NOP] = "nop", + [TMS320C55_INS_NOP_16] = "nop_16", + [TMS320C55_INS_ESTOP] = "estop", + [TMS320C55_INS_ECOPR] = "ecopr", [TMS320C55_INS_NOT] = "not", [TMS320C55_INS_OR] = "or", [TMS320C55_INS_POP] = "pop", diff --git a/librz/arch/isa/tms320/tms320c55x_insn.h b/librz/arch/isa/tms320/tms320c55x_insn.h index 52f7a1a7ba..5b2ee77216 100644 --- a/librz/arch/isa/tms320/tms320c55x_insn.h +++ b/librz/arch/isa/tms320/tms320c55x_insn.h @@ -32,6 +32,8 @@ typedef enum { TMS320C55_INS_ABDST, TMS320C55_INS_ABS, TMS320C55_INS_ADD, + TMS320C55_INS_ADDV, + TMS320C55_INS_ADDRV, TMS320C55_INS_ADDSUB, TMS320C55_INS_ADDSUB2CC, TMS320C55_INS_ADDSUBCC, @@ -127,6 +129,14 @@ typedef enum { TMS320C55_INS_XCC, TMS320C55_INS_XCCPART, TMS320C55_INS_XOR, + // Appended at the end to keep the numeric ids of the entries above stable + // (op->id values are asserted by the analysis tests). + TMS320C55_INS_COPY, + TMS320C55_INS_SWAPP, + TMS320C55_INS_SWAP4, + TMS320C55_INS_NOP_16, + TMS320C55_INS_ESTOP, + TMS320C55_INS_ECOPR, } TMS320C55InsID; /** Human-readable mnemonic for a TMS320C55InsID (never NULL). */ diff --git a/librz/arch/meson.build b/librz/arch/meson.build index d2184c9acf..5366e3b45b 100644 --- a/librz/arch/meson.build +++ b/librz/arch/meson.build @@ -318,17 +318,13 @@ arch_isa_sources = [ 'isa/sparc/sparc_il.c', 'isa/sparc/sparc_il_ops.c', 'isa/spc700/spc700dis.c', + 'isa/tms320/c55_ir.c', + 'isa/tms320/c55x_plus/c55plus_arch.c', 'isa/tms320/c55x/c55x_analysis.c', - 'isa/tms320/c55x_plus/c55plus.c', 'isa/tms320/c55x_plus/c55plus_analysis.c', - 'isa/tms320/c55x_plus/c55plus_il.c', - 'isa/tms320/c55x_plus/c55plus_decode.c', - 'isa/tms320/c55x_plus/decode_funcs.c', - 'isa/tms320/c55x_plus/hashtable.c', - 'isa/tms320/c55x_plus/hashvector.c', + 'isa/tms320/c55x_plus/c55plus_il_config.c', 'isa/tms320/c55x_plus/ins.c', 'isa/tms320/c64x/c64x.c', - 'isa/tms320/tms320_dasm.c', 'isa/tms320/tms320c55x_insn.c', 'isa/v810/v810_disas.c', 'isa/v810/v810_il.c', diff --git a/librz/arch/p/analysis/analysis_tms320.c b/librz/arch/p/analysis/analysis_tms320.c index 296f585641..65b2ba9897 100644 --- a/librz/arch/p/analysis/analysis_tms320.c +++ b/librz/arch/p/analysis/analysis_tms320.c @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -290,6 +289,56 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) { ; } + // C55x+ (Ryujin) has 32 accumulators and 16 (extended) auxiliary registers, + // vs the 8/8 of the base C55x profile above. Append the extra ac8-31, + // ar8-15 and xar8-15 so the shared lifter's IL variables for them resolve + // (the byte offsets continue past ac7 at 684; C55x never references these). + const char *cpu = rz_analysis_get_cpu(a); + if (cpu && rz_str_casecmp(cpu, "c55x+") == 0) { + static const char *ext = + "ctr ac8 .40 684 0 # Accumulator 8\n" + "ctr ac9 .40 689 0 # Accumulator 9\n" + "ctr ac10 .40 694 0 # Accumulator 10\n" + "ctr ac11 .40 699 0 # Accumulator 11\n" + "ctr ac12 .40 704 0 # Accumulator 12\n" + "ctr ac13 .40 709 0 # Accumulator 13\n" + "ctr ac14 .40 714 0 # Accumulator 14\n" + "ctr ac15 .40 719 0 # Accumulator 15\n" + "ctr ac16 .40 724 0 # Accumulator 16\n" + "ctr ac17 .40 729 0 # Accumulator 17\n" + "ctr ac18 .40 734 0 # Accumulator 18\n" + "ctr ac19 .40 739 0 # Accumulator 19\n" + "ctr ac20 .40 744 0 # Accumulator 20\n" + "ctr ac21 .40 749 0 # Accumulator 21\n" + "ctr ac22 .40 754 0 # Accumulator 22\n" + "ctr ac23 .40 759 0 # Accumulator 23\n" + "ctr ac24 .40 764 0 # Accumulator 24\n" + "ctr ac25 .40 769 0 # Accumulator 25\n" + "ctr ac26 .40 774 0 # Accumulator 26\n" + "ctr ac27 .40 779 0 # Accumulator 27\n" + "ctr ac28 .40 784 0 # Accumulator 28\n" + "ctr ac29 .40 789 0 # Accumulator 29\n" + "ctr ac30 .40 794 0 # Accumulator 30\n" + "ctr ac31 .40 799 0 # Accumulator 31\n" + "gpr ar8 .16 804 0 # Auxiliary register 8\n" + "gpr ar9 .16 806 0 # Auxiliary register 9\n" + "gpr ar10 .16 808 0 # Auxiliary register 10\n" + "gpr ar11 .16 810 0 # Auxiliary register 11\n" + "gpr ar12 .16 812 0 # Auxiliary register 12\n" + "gpr ar13 .16 814 0 # Auxiliary register 13\n" + "gpr ar14 .16 816 0 # Auxiliary register 14\n" + "gpr ar15 .16 818 0 # Auxiliary register 15\n" + "gpr xar8 .23 820 0 # Extended auxiliary register 8\n" + "gpr xar9 .23 823 0 # Extended auxiliary register 9\n" + "gpr xar10 .23 826 0 # Extended auxiliary register 10\n" + "gpr xar11 .23 829 0 # Extended auxiliary register 11\n" + "gpr xar12 .23 832 0 # Extended auxiliary register 12\n" + "gpr xar13 .23 835 0 # Extended auxiliary register 13\n" + "gpr xar14 .23 838 0 # Extended auxiliary register 14\n" + "gpr xar15 .23 841 0 # Extended auxiliary register 15\n"; + return rz_str_newf("%s%s", p, ext); + } + return rz_str_dup(p); } diff --git a/librz/arch/p/asm/asm_tms320.c b/librz/arch/p/asm/asm_tms320.c index 545b1eaad0..8c7a872fac 100644 --- a/librz/arch/p/asm/asm_tms320.c +++ b/librz/arch/p/asm/asm_tms320.c @@ -5,31 +5,43 @@ #include #include #include "asm_private.h" -#include +#include +#include #include typedef struct tms_cs_context_t { void *c64x; - tms320_dasm_t engine; } 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, "c54x") == 0) { - tms320_f_set_cpu(&ctx->engine, TMS320_F_CPU_C54X); - } else if (a->cpu && rz_str_casecmp(a->cpu, "c55x+") == 0) { - tms320_f_set_cpu(&ctx->engine, TMS320_F_CPU_C55X_PLUS); - } else if (a->cpu && rz_str_casecmp(a->cpu, "c55x") == 0) { - tms320_f_set_cpu(&ctx->engine, TMS320_F_CPU_C55X); - } else if (a->cpu && !rz_str_casecmp(a->cpu, "c64x")) { + if (a->cpu && !rz_str_casecmp(a->cpu, "c64x")) { return tms320_c64x_disassemble(a, op, buf, len, ctx->c64x); - } else { + } + // C55x / C55x+ are decoded by the shared decode-IR engine. C54x has no + // instruction decoder yet, so it reports invalid; any other cpu is unknown. + const C55ArchDesc *desc = NULL; + if (a->cpu && !rz_str_casecmp(a->cpu, "c55x+")) { + desc = &c55plus_arch_desc; + } else if (a->cpu && !rz_str_casecmp(a->cpu, "c55x")) { + desc = &c55x_arch_desc; + } else if (!a->cpu || rz_str_casecmp(a->cpu, "c54x")) { rz_asm_op_set_asm(op, "unknown asm.cpu"); return op->size = -1; } - op->size = tms320_dasm(&ctx->engine, buf, len); - rz_asm_op_set_asm(op, ctx->engine.syntax); - return op->size; + if (desc) { + C55Insn insn; + if (c55_decode(desc, buf, len, &insn)) { + char *s = c55_format(desc, &insn); + 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 = 1; } static bool tms320_init(void **user) { @@ -37,9 +49,7 @@ static bool tms320_init(void **user) { if (!ctx) { return false; } - ctx->c64x = tms320_c64x_new(); - tms320_dasm_init(&ctx->engine); *user = ctx; return true; } @@ -48,7 +58,6 @@ static bool tms320_fini(void *user) { rz_return_val_if_fail(user, false); TmsContext *ctx = (TmsContext *)user; tms320_c64x_free(ctx->c64x); - tms320_dasm_fini(&ctx->engine); free(ctx); return true; } diff --git a/test/db/analysis/tms320.c55x+_32 b/test/db/analysis/tms320.c55x+_32 index 0aa34faa5e..9b600a53f5 100644 --- a/test/db/analysis/tms320.c55x+_32 +++ b/test/db/analysis/tms320.c55x+_32 @@ -242,7 +242,7 @@ type: mul type: mul type: push type: pop -type: upush +type: push type: lea type: lea type: null @@ -255,11 +255,11 @@ type: swi type: trap type: mov type: mov -type: null +type: ill type: cjmp jump: 0x00000008 fail: 0x00000003 -type: xchg +type: ill type: trap family: cpu type: trap @@ -271,13 +271,13 @@ type: add type: add type: add type: mov -type: cmp +type: ill type: trap family: cpu type: trap family: cpu -type: mov -type: xchg +type: ill +type: ill type: trap family: cpu type: trap @@ -289,12 +289,12 @@ type: add type: add type: add type: mov -type: cmp +type: ill type: trap family: cpu type: trap family: cpu -type: mov +type: ill EOF RUN @@ -471,7 +471,7 @@ family: cpu type: cret reg: sp stackptr: -2 -type: upush +type: push reg: sp direction: write stackptr: 2 @@ -517,8 +517,8 @@ CMDS=<id carries the named TMS320C55InsID resolved by the C55x+ token -# decoder (positions in tms320_insn.h): AADD=1, INTR=39, NOP=59, POP=62, -# PSH=64, RET=67, RETI=69, TRAP=96. +# decoder (positions in tms320_insn.h): AADD=3, INTR=41, NOP=61, POP=64, +# PSH=66, RET=69, RETI=71, TRAP=98. wx 21 @ 0 ao 1 @ 0~id wx 0e00 @ 0 @@ -537,13 +537,13 @@ wx 0c10 @ 0 ao 1 @ 0~id EOF EXPECT=<id (Capstone-style). These values are the -# positions in the TMS320C55_INS_* enum (see tms320_insn.h): B=12, CALL=26, -# RET=67, NOP=59, PSH=64, POP=62, AND=10, MOV=53, INTR=39, TRAP=96. +# positions in the TMS320C55_INS_* enum (see tms320_insn.h): B=14, CALL=28, +# RET=69, NOP=61, PSH=66, POP=64, AND=12, MOV=55, INTR=41, TRAP=98. wx 060020 @ 0 ao 1 @ 0~id wx 080020 @ 0 @@ -726,16 +727,16 @@ wx 958f @ 0 ao 1 @ 0~id EOF EXPECT=< 0x00000108 mov xar0, ac3 -| :| 0x0000010a mov dbl(*(0x64626C)), xar3 +| :| 0x0000010a mov dbl(*(0x008000)), xar3 | :| 0x00000110 add ac1, ac3 | :| 0x00000112 mov ac3, xar2 | :| 0x00000114 mov xar3, ac2 -| :| 0x00000116 add dbl(*(0x64626C)), ac2 +| :| 0x00000116 add dbl(*(0x008002)), ac2 | :| 0x0000011c mov ac2, xar3 | :| 0x0000011e mov *ar2, *ar3 -| :| 0x00000121 mov dbl(*(0x64626C)), ac2 +| :| 0x00000121 mov dbl(*(0x008002)), ac2 | :| 0x00000127 add 0x1, ac2 -| :| 0x00000129 mov ac2, dbl(*(0x616332)) +| :| 0x00000129 mov ac2, dbl(*(0x008002)) | :| 0x0000012f add 0x1, ac1 | :| 0x00000131 cmpu ac1 < ac0, tc1 | `==< 0x00000134 bcc 0xD1, tc1 @@ -868,3 +869,57 @@ EXPECT=< 0x00000137 ret EOF RUN + +NAME=c55x analysis: flag-condition branch targets +FILE=malloc://64 +CMDS=<= ac5, tc1" a400050c 0x0 (set st0_55 (ite (! (&& (sle (var ac0) (var ac5)) (! (== (var ac0) (var ac5))))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmp ac0 == ac1, tc2" a4000101 0x0 (set st0_55 (ite (== (var ac0) (var ac1)) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) +d "cmp ac0 < ac1, tc1" a4000108 0x0 (set st0_55 (ite (&& (sle (var ac0) (var ac1)) (! (== (var ac0) (var ac1)))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmpu ac0.l != ac0.h, tc1" a4604024 0x0 (set st0_55 (ite (! (== (cast 16 false (var ac0)) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmp ac0.h != ac1.l, tc1" a4406104 0x0 (set st0_55 (ite (! (== (cast 16 false (>> (var ac0) (bv 8 0x10) false)) (cast 16 false (var ac1)))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "macm *ar2, *ar4, ac0, ac0" c832803400 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2))))))) d "mov *ar0+ << t2, ac0" b410004032 +d "mov uns(*ar0+) << t2, ac0" b410006032 +d "mov rnd(*ar0+ << t2), ac0" b410204032 +d "mov *ar0+ << ssp, ac0" b410004034 d "mov ac0.l, *ar2 || mov *ar1+ << t3, ac1" 395102a0b411014033 d "mov ac0.l, *ar2 || mov *(ar1+t0b) << t3, ac1" 395102a0b411414033 d "mov t0, ac0" 770030 0x0 (set ac0 (cast 40 (msb (var t0)) (var t0))) d "mpym *ar2, *ar4, ac0" c832003400 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))))) +d "mpym40 uns(*ar2), *ar4, ac0" c832603400 +d "mpymf *ar2, uns(*ar4), ac0" c8320034a0 d "sub ac1, ac0" 740081 0x0 (set ac0 (- (var ac0) (var ac1))) d "pop t2, t3" 713233 0x0 (seq (set t2 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1))) (set t3 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) +d "psh t2, t3" 703233 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var t2)) (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var t3))) d "pop mmap(@st1_55)" 2461e508 0x0 (seq (set st1_55 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) d "psh mmap(@st0_55)" 2461e400 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var st0_55))) d "pshboth xar5" 0d25 @@ -36,18 +68,34 @@ d "sub #0x1, ac0" 7b0081 0x0 (set ac0 (- (var ac0) (bv 40 0x1))) d "sub ar0, ac0" 7400a0 0x0 (set ac0 (- (var ac0) (cast 40 (msb (var ar0)) (var ar0)))) d "sub ac1, ac0" 740081 0x0 (set ac0 (- (var ac0) (var ac1))) d "mov #0x42, ac0" ac000042 0x0 (set ac0 (bv 40 0x42)) +d "add #0x5, ac0" 7b0005 0x0 (set ac0 (+ (var ac0) (bv 40 0x5))) +d "sub #0x5, ac0" 7b0085 0x0 (set ac0 (- (var ac0) (bv 40 0x5))) +d "mov #0x5, ac0" 7b8005 0x0 (set ac0 (bv 40 0x5)) +d "mov -#0x5, ac0" 7b8085 +d "mov #0x1, ac0.h" 7bc001 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (bv 16 0x1)) (bv 6 0x10) false))) +d "mov #0x1, ac0.l" 7be001 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (bv 16 0x1)))) +d "mov #0x64, ac0.h" ac400064 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (bv 16 0x64)) (bv 6 0x10) false))) d "mov ar0, ar1" 772120 0x0 (set ar1 (var ar0)) d "mov ar2, ar3" 772322 0x0 (set ar3 (var ar2)) d "nop" 20 0x0 nop d "and #0xff, ac0, ac0" c5000000ff 0x0 (set ac0 (& (var ac0) (bv 40 0xff))) +d "and #0x100, ac1.l, ac0.l" c560610100 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (& (cast 16 false (var ac1)) (bv 16 0x100))))) +d "and #0x100, ac1, ac0.h" c540010100 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (& (cast 16 false (var ac1)) (bv 16 0x100))) (bv 8 0x10) false))) +d "and #0x100, ac1.h, ac0.l" c560410100 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (& (cast 16 false (>> (var ac1) (bv 8 0x10) false)) (bv 16 0x100))))) d "or #0xf, ac1, ac1" c50181000f 0x0 (set ac1 (| (var ac1) (bv 40 0xf))) d "xor #0xaa, ac2, ac2" c5820200aa 0x0 (set ac2 (^ (var ac2) (bv 40 0xaa))) d "and ac0, ac1" 750100 0x0 (set ac1 (& (var ac1) (var ac0))) d "or ac0, ac2" 750280 0x0 (set ac2 (| (var ac2) (var ac0))) d "xor ac0, ac3" 758300 0x0 (set ac3 (^ (var ac3) (var ac0))) +d "sub ac1 << #0x1, ac0" a7008101 0x0 (set ac0 (- (var ac0) (<< (var ac1) (bv 8 0x1) false))) +d "and ac1 << #0x1, ac0" a7000181 0x0 (set ac0 (& (var ac0) (<< (var ac1) (bv 8 0x1) false))) +d "xor ac1 << #0x1, ac0" a7800181 0x0 (set ac0 (^ (var ac0) (<< (var ac1) (bv 8 0x1) false))) d "sfts ac1, t3, ac1" a6818133 0x0 (set ac1 (ite (&& (sle (var t3) (bv 16 0x0)) (! (== (var t3) (bv 16 0x0)))) (>> (var ac1) (- (bv 16 0x0) (var t3)) (msb (var ac1))) (<< (var ac1) (var t3) false))) +d "sfts ac5, #0x0, ac0" a7808500 0x0 (set ac0 (var ac5)) +d "sfts ac5, #0x30, ac0" a7808530 0x0 (set ac0 (>> (var ac5) (bv 6 0x10) (msb (var ac5)))) d "sftl ac1, #0xf, ac1" a781010f 0x0 (set ac1 (<< (var ac1) (bv 6 0xf) false)) +d "sftl ac1, #0x3f, ac1" a781013f 0x0 (set ac1 (>> (var ac1) (bv 6 0x1) false)) d "bclr st0_acov0, st0_55" 0a0a 0x0 (set st0_55 (ite false (| (var st0_55) (bv 16 0x400)) (& (var st0_55) (bv 16 0xfbff)))) d "xor #0x1, ar3, ar3" c5a3230001 0x0 (set ar3 (^ (var ar3) (bv 16 0x1))) d "and ac1, ac0" 750001 0x0 (set ac0 (& (var ac0) (var ac1))) @@ -58,6 +106,9 @@ d "sftl ac0, #0x4, ac0" a7800004 0x0 (set ac0 (<< (var ac0) (bv 6 0x4) false)) d "b #0x00002d" 68002d 0x0 (jmp (bv 24 0x30)) d "call #0x000028" 690028 0x0 (jmp (bv 24 0x2b)) +d "call #0x61499a" 9d61499a 0x0 (jmp (bv 24 0x61499a)) +d "call #0x000040" 9d000040 0x0 (jmp (bv 24 0x40)) +d "call #0xffffff" 9dffffff 0x0 (jmp (bv 24 0xffffff)) d "bcc #0x000023, ac0 != #0" 6a2320 0x0 (branch (! (== (var ac0) (bv 40 0x0))) (jmp (bv 24 0x26)) nop) d "bcc #0x00001f, ac0 == #0" 6a1f00 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x22)) nop) d "bcc #0x00001c, ac1 != #0" 6a1c21 0x0 (branch (! (== (var ac1) (bv 40 0x0))) (jmp (bv 24 0x1f)) nop) @@ -65,18 +116,23 @@ d "bcc #0x000019, t0 < #0" 6a1948 0x0 (branch (&& (sle (var t0) (bv 16 0x0)) (! d "bcc #0x000016, ar0 > #0" 6a1690 0x0 (branch (! (sle (var ar0) (bv 16 0x0))) (jmp (bv 24 0x19)) nop) d "bccu #0x000010, ar0 < ar1" dba0210010 0x0 (branch (&& (ule (var ar0) (var ar1)) (! (== (var ar0) (var ar1)))) (jmp (bv 24 0x15)) nop) d "bccu #0x00000b, ar2 < ar3" dba223000b 0x0 (branch (&& (ule (var ar2) (var ar3)) (! (== (var ar2) (var ar3)))) (jmp (bv 24 0x10)) nop) +d "bccu #0x000021, ac0.h == ac1.l" db40610021 0x0 (branch (== (cast 16 false (>> (var ac0) (bv 8 0x10) false)) (cast 16 false (var ac1))) (jmp (bv 24 0x26)) nop) +d "bcc #0x00002f, ac8 != ac4" da0884002f 0x0 (branch (! (== (var ac8) (var ac4))) (jmp (bv 24 0x34)) nop) d "bcc #0x000005, ac0 == #0" 6a0500 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x8)) nop) +d "bcc #0x00041d, ac6.l == #0x1" dc6601041d 0x0 (branch (== (cast 16 false (var ac6)) (bv 16 0x1)) (jmp (bv 24 0x422)) nop) +d "bcc #0x000713, ac0.h == #0x1" dc40010713 0x0 (branch (== (cast 16 false (>> (var ac0) (bv 8 0x10) false)) (bv 16 0x1)) (jmp (bv 24 0x718)) nop) +d "bcc #0x00106e, ac5 >= #0x7" dc8587106e 0x0 (branch (! (&& (sle (var ac5) (bv 40 0x7)) (! (== (var ac5) (bv 40 0x7))))) (jmp (bv 24 0x1073)) nop) +d "bccu #0x00000e, ac0.h == #0x1" dd4001000e 0x0 (branch (== (cast 16 false (>> (var ac0) (bv 8 0x10) false)) (bv 16 0x1)) (jmp (bv 24 0x13)) nop) +d "bccu #0x00003b, ac5.l < #0x8" dde508003b 0x0 (branch (&& (ule (cast 16 false (var ac5)) (bv 16 0x8)) (! (== (cast 16 false (var ac5)) (bv 16 0x8)))) (jmp (bv 24 0x40)) nop) +d "bccu #0x000037, ac2.l != #0xb" dd628b0037 0x0 (branch (! (== (cast 16 false (var ac2)) (bv 16 0xb))) (jmp (bv 24 0x3c)) nop) d "call #0x00ffd0" 69ffd0 0x0 (jmp (bv 24 0xffffd3)) -d "macm *ar2, *ar4, ac0, ac0" c832803400 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2))))))) -d "mpym *ar2, *ar4, ac0" c832003400 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar4)) (bv 24 0x2)))))) d "pop t2, t3" 713233 0x0 (seq (set t2 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1))) (set t3 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) d "pshboth xar5" 0d25 d "psh mmap(@st0_55)" 2461e400 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var st0_55))) d "pop mmap(@st1_55)" 2461e508 0x0 (seq (set st1_55 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) -d "cmp t2 == t3, tc1" a4323300 0x0 (set st0_55 (ite (== (var t2) (var t3)) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "cmp t0 == t1, tc2" a4303101 0x0 (set st0_55 (ite (== (var t0) (var t1)) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) d "cmp ar0 < ar1, tc1" a4202108 0x0 (set st0_55 (ite (&& (sle (var ar0) (var ar1)) (! (== (var ar0) (var ar1)))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "cmp ar2 >= ar3, tc2" a422230d 0x0 (set st0_55 (ite (! (&& (sle (var ar2) (var ar3)) (! (== (var ar2) (var ar3))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) @@ -137,11 +193,189 @@ d "copy *(ar0-t0b), t1" 540071 d "copy *(ar0+t0b), t1" 541071 d "copy *ar0(t0<<#1), t1" 542071 d "copy *ar0, t1" 5400b1 0x0 (set t1 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) +d "copy dbl(*ar5), xar0" 560580 0x0 (set xar0 (cast 23 false (loadw 0 32 (* (cast 24 false (var xar5)) (bv 24 0x2))))) +d "copy dbl(*ar5(short(#0x1))), xar0" 561580 0x0 (set xar0 (cast 23 false (loadw 0 32 (* (+ (cast 24 false (var xar5)) (bv 24 0x1)) (bv 24 0x2))))) +d "copy dbl(*ar5), xar15" 56058f 0x0 (set xar15 (cast 23 false (loadw 0 32 (* (cast 24 false (var xar5)) (bv 24 0x2))))) +d "copy dbl(*ar6(short(#0x8))), xar0" 568680 0x0 (set xar0 (cast 23 false (loadw 0 32 (* (+ (cast 24 false (var xar6)) (bv 24 0x8)) (bv 24 0x2))))) +d "copy dbl(*sp(#0x6)), xar0" 5686c0 0x0 (set xar0 (cast 23 false (loadw 0 32 (* (+ (cast 24 false (var sp)) (bv 24 0x6)) (bv 24 0x2))))) +d "copy dbl(*sp(#0x1)), xar1" 5681c1 0x0 (set xar1 (cast 23 false (loadw 0 32 (* (+ (cast 24 false (var sp)) (bv 24 0x1)) (bv 24 0x2))))) +d "copy dbl(*sp(#0x6)), xar8" 5686c8 0x0 (set xar8 (cast 23 false (loadw 0 32 (* (+ (cast 24 false (var sp)) (bv 24 0x6)) (bv 24 0x2))))) +d "copy dbl(*(#0x978ce8)), xar0" d180978ce8 0x0 (set xar0 (cast 23 false (loadw 0 32 (bv 24 0x978ce8)))) +d "copy dbl(*(#0x9bbac0)), xar1" d1819bbac0 0x0 (set xar1 (cast 23 false (loadw 0 32 (bv 24 0x9bbac0)))) +d "copy dbl(*(#0x123456)), xar8" d188123456 0x0 (set xar8 (cast 23 false (loadw 0 32 (bv 24 0x123456)))) d "mov *ar0-, ac0" 580000 0x0 (seq (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (set xar0 (- (var xar0) (bv 23 0x1)))) d "mov *ar0+, ac0" 581000 0x0 (seq (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (set xar0 (+ (var xar0) (bv 23 0x1)))) d "mov *ar0(t0), ac0" 582000 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2))))) +d "mov uns(byte(*ar7)), ac1" 8a0781e0 0x0 (set ac1 (cast 40 false (loadw 0 8 (* (cast 24 false (var xar7)) (bv 24 0x2))))) +d "mov uns(byte(*ar7)), ar1" 8a0781e1 0x0 (set ar1 (cast 16 false (loadw 0 8 (* (cast 24 false (var xar7)) (bv 24 0x2))))) +d "mov uns(byte(*ar7)), ac1.l" 8a0781e3 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (cast 16 false (loadw 0 8 (* (cast 24 false (var xar7)) (bv 24 0x2))))))) +d "mov byte(*ar7), ac1.l" 8a0781c3 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (cast 16 (msb (loadw 0 8 (* (cast 24 false (var xar7)) (bv 24 0x2)))) (loadw 0 8 (* (cast 24 false (var xar7)) (bv 24 0x2))))))) +d "mov ac1.l, byte(*ar7)" 8a0781a3 0x0 (storew 0 (* (cast 24 false (var xar7)) (bv 24 0x2)) (cast 8 false (cast 16 false (var ac1)))) +d "mov uns(byte(*(#0x9bbd47))), ac1.l" 8ae041e39bbd47 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (cast 16 false (loadw 0 8 (bv 24 0x9bbd47)))))) +d "mov byte(*(#0x9bbd47)), ac1.l" 8ae041c39bbd47 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (cast 16 (msb (loadw 0 8 (bv 24 0x9bbd47))) (loadw 0 8 (bv 24 0x9bbd47)))))) +d "mov uns(byte(*sp(#0x60))), ac1.l" 8ae0c1e3 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (cast 16 false (loadw 0 8 (* (+ (cast 24 false (var sp)) (bv 24 0x60)) (bv 24 0x2))))))) +d "mov dbl(*ar7), ac0" 5c0780 0x0 (set ac0 (cast 40 (msb (loadw 0 32 (* (cast 24 false (var xar7)) (bv 24 0x2)))) (loadw 0 32 (* (cast 24 false (var xar7)) (bv 24 0x2))))) +d "mov dbl(*(#0x95a004)), ac0" 5ce04095a004 0x0 (set ac0 (cast 40 (msb (loadw 0 32 (bv 24 0x95a004))) (loadw 0 32 (bv 24 0x95a004)))) +d "mov *ar5(short(#0x8)), ac0.l" 5b8580 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var xar5)) (bv 24 0x8)) (bv 24 0x2)))))) +d "mov *(#0x95a000), ac0.l" 5be04095a000 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (loadw 0 16 (bv 24 0x95a000))))) +d "mov ac0.l, *sp(#0x0)" 5180e0 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)) (cast 16 false (var ac0))) +d "mov ac0.h, *ar0" 510080 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) +d "mov ac1, dbl(*sp(#0x8))" 5088c1 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x8)) (bv 24 0x2)) (cast 32 false (var ac1))) +d "mov xar0, dbl(*ar0)" 520080 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 32 false (var xar0))) +d "mov ac0, dbl(*(#0x95a004))" d00095a004 0x0 (storew 0 (bv 24 0x95a004) (cast 32 false (var ac0))) +d "mov ar0, *(#0x123456)" d020123456 0x0 (storew 0 (bv 24 0x123456) (var ar0)) +d "mov ac0.h, *(#0x123456)" d040123456 0x0 (storew 0 (bv 24 0x123456) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) +d "mov xar0, dbl(*(#0x123456))" d080123456 0x0 (storew 0 (bv 24 0x123456) (cast 32 false (var xar0))) +d "mov #0x0, byte(*ar6(#16))" 4c86400010 0x0 (storew 0 (* (+ (cast 24 false (var xar6)) (bv 24 0x10)) (bv 24 0x2)) (cast 8 false (bv 16 0x0))) +d "mov #0x1, byte(*ar6)" 4c0681 0x0 (storew 0 (* (cast 24 false (var xar6)) (bv 24 0x2)) (cast 8 false (bv 16 0x1))) +d "mov #0x1, byte(*sp(#0x64))" 4ce4c1 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x64)) (bv 24 0x2)) (cast 8 false (bv 16 0x1))) +d "mov #0x0, byte(*(#0x123456))" 4ce040123456 0x0 (storew 0 (bv 24 0x123456) (cast 8 false (bv 16 0x0))) +d "mov #0x40, byte(*ar0)" 4d0080 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 8 false (bv 16 0x40))) +d "mov #0x80, byte(*ar0)" 4e0080 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 8 false (bv 16 0x80))) +d "mov #0xff, byte(*ar0)" 4f00bf 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 8 false (bv 16 0xff))) +d "mov *sp(#0x8), ac0.h" 5a88c0 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x8)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "mov *ar5(short(#0x7)), ac1.h" 5a7581 0x0 (set ac1 (| (& (var ac1) (bv 40 0xff0000ffff)) (<< (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var xar5)) (bv 24 0x7)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "mov #0x0, *sp(#0x5)" 4885c0 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x5)) (bv 24 0x2)) (bv 16 0x0)) +d "mov #0x0, *(#0x123456)" 48e040123456 0x0 (storew 0 (bv 24 0x123456) (bv 16 0x0)) +d "amov #0x5f0, ar0" ae8005f0 0x0 (set ar0 (bv 16 0x5f0)) +d "asub #0x5f0, ar0" ae0005f0 0x0 (set ar0 (- (var ar0) (bv 16 0x5f0))) +d "amov #0x5f0, t0" ae9005f0 0x0 (set t0 (bv 16 0x5f0)) +d "aadd #0x5f0, ar0" ae4005f0 0x0 (set ar0 (+ (var ar0) (bv 16 0x5f0))) +d "aadd #0x5f0, xar0" ae6005f0 0x0 (set xar0 (+ (var xar0) (bv 23 0x5f0))) +d "asub #0x5f0, xar0" ae2005f0 0x0 (set xar0 (- (var xar0) (bv 23 0x5f0))) +d "amov #0xff, xar0" d2800000ff 0x0 (set xar0 (bv 23 0xff)) +d "asub #0xffff, xar0" d20000ffff 0x0 (set xar0 (- (var xar0) (bv 23 0xffff))) +d "aadd #0xeb, sp" 0ceb 0x0 (set sp (+ (var sp) (bv 16 0xeb))) +d "b ac1" 0201 0x0 (jmp (cast 24 false (var ac1))) +d "call ac0" 0280 0x0 (jmp (cast 24 false (var ac0))) +d "b ac7" 0207 0x0 (jmp (cast 24 false (var ac7))) +d "b ac16" 0210 0x0 (jmp (cast 24 false (var ac16))) +d "mpyk #0x60, ac0, ac0" c700000060 0x0 (set ac0 (* (bv 40 0x60) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "mov #0x0, ac0.l" 2e7be000 0x0 (set ac0 (| (& (var ac0) (bv 40 0xffffff0000)) (cast 40 false (bv 16 0x0)))) +d "mov ac0.l, *ar0" 2e5100a0 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 16 false (var ac0))) +d "mov #0x0, *(#0x95a000)" 2e48e04095a000 0x0 (storew 0 (bv 24 0x95a000) (bv 16 0x0)) +d "copy dbl(*(#0x9bba18)), xar0" 2ed1809bba18 +d "xccpart ac0.l == #0" 0718 +d "xccpart ac0 == #0" 0700 +d "xccpart xar0 == #0" 07c0 +d "xccpart !tc1" 07f4 +d "xccpart overflow(ac0)" 07e0 +d "xcc !tc1" 06f4 +d "xcc ac0.l == #0" 0618 +d "delay *ar0(t0<<#1)" 602043 +d "delay *ar0" 600080 +d "delay *ar0-" 600000 +d "psh *ar0(t0<<#1)" 612043 +d "psh *ar0" 610080 +d "psh *(#0x95a000)" 61e04095a000 +d "bfxtr #0x2, ac0, ac0.l" c660600002 +d "bfxtr #0x2, ac1, ac0.l" c660610002 +d "bfxtr #0xff02, ac0, ac0.l" c66060ff02 +d "bfxtr #0x2, ac0, ac0.h" c640600002 +d "bfxtr #0x2, ac0, ar0" c620600002 +d "bfxtr #0x2, ac0, ac0" c600600002 +d "btst @#0x5, ac1.l, tc1" 8905c1a3 +d "btst @#0x5, ac5.h, tc1" 8905c5a2 +d "btst @#0xf, t3, tc1" 890fd3a1 +d "btst @#0x5, ac0.l, tc2" 8905e0a3 +d "bclr @#0x0, ac0.l" 8900c023 +d "bset @#0x4, ac0.l" 8904e023 +d "bclr @#0x0, ac0" 8900c020 +d "bnot @#0x5, ac0" 8905c060 +d "btstp @#0x5, ac0" 8905c0e0 +d "btstset #0x5, *ar0, tc2" 9100a805 +d "btstset #0x0, *ar0, tc2" 9100a800 +d "add #0x1, *(#0x95a008)" b1e040000195a008 +d "add #0x1234, *ar0" b100801234 +d "add #0xadc, *ar1(t3)" b1b1010adc +d "mov #0x1234, *ar0" b100981234 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (bv 16 0x1234)) +d "mov #0xa00, *ar0(#26)" b180580a00001a 0x0 (storew 0 (* (+ (cast 24 false (var xar0)) (bv 24 0x1a)) (bv 24 0x2)) (bv 16 0xa00)) +d "mov uns(*(#0x95a000)) << #0x4, ac0" b7e060c00495a000 +d "add uns(*ar5(short(#0x7))) << #0x3, ac0, ac0" b775a00003 +d "mov uns(*ar5(short(#0x7))) << #0x3, ac1" b775a1c003 +d "add *(#0x9bbcc8) << #0x4, ac0, ac0" b7e04000049bbcc8 +d "sub *ar0 << #0x0, ac0, ac0" b700804000 +d "mov *ar0 << #0x0, ac0" b70080c000 +d "add *ar5(short(#0x7)) << #0x3, ac2, ac0" b775800203 +d "sftl ac0, ac5.l, ac2" a6820065 +d "sftl ac2, ac0.l, ac0" a6800260 +d "sftl ac0.l, ac1.l, ac0.l" a6e06061 +d "sftl ac0.h, ac2.l, ac0.h" a6c04062 +d "sftl ac8, ac0.l, ac0" a6800860 +d "sfts ac0, ac0.l, ac0" a6808060 +d "sfts ac0.l, #-1" 7b60a0 +d "sftl ac0, #-1" 7b80a0 +d "sfts ac0, #-1" 7b00a0 +d "sfts ac0.l, #1" 7b6020 +d "sfts ac5.l, #1" 7b6520 +d "sftl ac1, #-1" 7b81a0 +d "add uns(*ar5(short(#0x7))), ac0, ac0" 8c75a000 +d "add uns(*ar5(short(#0x1))), ac5, ac0" 8c15a005 +d "add uns(*ar5), ac5, ac0" 8c05a005 +d "add uns(*(#0x9bbcd2)), ac5, ac0" 8ce060059bbcd2 +d "add uns(*(#0x9bbcd2)), ac5, ac5" 8ce065059bbcd2 +d "add uns(*ar5(short(#0x7))), ac16, ac0" 8c75a010 +d "sub ac0, dbl(*ar0(short(#0x1))), ac0" 8d108080 +d "add dbl(*ar5(short(#0x5))), ac5, ac1" 8d558105 +d "sub dbl(*ar0+), ac0, ac0" 8d100040 +d "sub ac0, dbl(*ar0+), ac0" 8d100080 +d "sub ac1, dbl(*ar0(short(#0x1))), ac0" 8d108081 +d "or *ar1(#25), ac0, ac0" 858140000019 +d "or *ar1(#43), ac0, ac0" 85814000002b +d "or *ar1(#25), ac5, ac0" 858140050019 +d "or *ar1(#25), ac0, ac0.h" 858140800019 +d "or *(ar5-t0b), ac0, ac0" 85054000 +d "sub ac0.l, *sp(#0x4), ac1.l" 8284e1e0 +d "sub ac1.l, *sp(#0x16), ac1.l" 8296e1e1 +d "sub ac6.l, *ar5(short(#0x2)), ac0.l" 8225a0e6 +d "sub ac6.h, *ar5(short(#0x2)), ac0.l" 8225a0c6 +d "sub ac0.l, *sp(#0x4), ac1.h" 8284c1e0 +d "mov *ar2+, *ar0+" 97120810 +d "mov *ar2, *ar1" 97028831 +d "mov byte(*ar5(short(#0xc))), byte(*ar0)" 97c58c30 +d "mov byte(*ar0+), byte(*ar1+)" 97100c11 +d "mov byte(*ar0), byte(*ar1(short(#0x1)))" 97118430 +d "mov byte(*ar5(#23)), byte(*ar1)" 97854c310017 +d "mov byte(*ar0), byte(*(#0x9bba9f))" 97e044309bba9f +d "mov byte(*ar1(t0)), byte(*ar0(#2002))" 9780442107d2 +d "add *(#0x9bbcc8), ac0, ac0" 80e040009bbcc8 +d "add *(#0x9bbcc8), ac0, ac1" 80e041009bbcc8 +d "add *(#0x9bbcc8), ac1, ac0" 80e040019bbcc8 +d "add *(#0x9bbcc8), ac0, ac0.h" 80e040809bbcc8 +d "cmp *(#0x9bba14) == #0x52c, tc1" b2e040052c9bba14 +d "cmp *(#0x9bba14) == #0x100, tc1" b2e04001009bba14 +d "cmp *(#0x9bba14) < #0x100, tc1" b2e04201009bba14 +d "cmp *(#0x9bba14) >= #0x100, tc1" b2e04301009bba14 +d "cmp *(#0x9bba14) == #0x100, tc2" b2e06001009bba14 +d "sub #0x80 << #16, ac0, ac0" c000800080 0x0 (set ac0 (- (var ac0) (<< (bv 40 0x80) (bv 8 0x10) false))) +d "sub #0x80 << #16, ac1, ac0" c000810080 0x0 (set ac0 (- (var ac1) (<< (bv 40 0x80) (bv 8 0x10) false))) +d "sub #0x80 << #16, ac0, ac1" c001800080 0x0 (set ac1 (- (var ac0) (<< (bv 40 0x80) (bv 8 0x10) false))) +d "add #0x80 << #16, ac0, ac0" c000000080 0x0 (set ac0 (+ (var ac0) (<< (bv 40 0x80) (bv 8 0x10) false))) +d "neg ac5, ac0" 760085 0x0 (set ac0 (- (bv 40 0x0) (var ac5))) +d "neg ac5.l, ac1.l" 7661e5 0x0 (set ac1 (| (& (var ac1) (bv 40 0xffffff0000)) (cast 40 false (- (bv 16 0x0) (cast 16 false (var ac5)))))) +d "mov ac0.l, mmap(@brc0)" 24513a20 +d "mov ac0.h, mmap(@brc0)" 24513a00 +d "mov ac1.l, mmap(@brc0)" 24513a21 +d "mov ac0.l, mmap(@brc1)" 24513b20 +d "mov ac0.l, mmap(@csr)" 24513820 +d "sub #0x4e20, ac0.l, ac0.l" 2fc460e04e20 +d "mpykr #0x60, ac0, ac0" c720000060 +d "mpykf #0x60, ac0, ac0" c740000060 +d "mpykfr #0x60, ac0, ac0" c760000060 +d "mpyk #0x60, ac0.h, ac0" c700004060 +d "mack #0x60, ac2, ac0, ac1" c701800260 +d "mpyk #0x140, ac0.l, ac0" ee0000600140 +d "mack #0x96, ac0.l, ac1, ac0" ee0081600096 +d "bcc #0x000190, ac7.h == #0xff" de477f0190 0x0 (branch (== (cast 16 false (>> (var ac7) (bv 8 0x10) false)) (bv 16 0xff)) (jmp (bv 24 0x195)) nop) +d "bccu #0x000190, ac7.h == #0xff" df477f0190 0x0 (branch (== (cast 16 false (>> (var ac7) (bv 8 0x10) false)) (bv 16 0xff)) (jmp (bv 24 0x195)) nop) +d "bcc #0x000781, !tc1" 9a0781f4 0x0 (branch (! (lsb (>> (var st0_55) (bv 4 0xd) false))) (jmp (bv 24 0x785)) nop) +d "bcc #0x000000, ac0 != #0" 9a000020 0x0 (branch (! (== (var ac0) (bv 40 0x0))) (jmp (bv 24 0x4)) nop) +d "bcc #0x000781, xar0 == #0" 9a0781c0 0x0 (branch (== (var xar0) (bv 23 0x0)) (jmp (bv 24 0x785)) nop) +d "bcc #0x000781, !overflow(ac0)" 9a0781f0 d "macm *ar3+, *ar15-, ac0" e0130040000f +d "mpym *ar3+, *ar15-, ac0" e0130000000f +d "masm *ar3+, *ar15-, ac0" e0130080000f d "macm *ar3+, *ar15+, ac0" e0130040001f d "macm *ar3+, *ar15(t0), ac0" e0130040002f d "macm *ar3+, *ar15, ac0" e0130040003f @@ -150,6 +384,8 @@ d "aadd #0xf8, sp" 0cf8 d "abdst *ar14, *(ar7-t1), ac28, ac18" ce3e9c5752 d "addsub2cc *-ar0, ac2, t3, tc1, tc2, ac2" b38022a2b3 d "addsubcc *ar7(t3), ac6, tc2, ac18" b3b7320607 +d "addsubcc *ar7(t3), ac6, tc1, tc2, ac18" b3b7324607 +d "addsub2cc *ar7(t3), ac6, ac7, tc1, tc2, ac18" b3b7328607 d "band *ar12(t1), #0x4c, tc1" b23c18004c d "bcnt ac2, ac8, tc2," a9bd6208 d "bfxpa #0x500, ac11," c637eb0500 @@ -162,12 +398,23 @@ d "btstp @#0x45, ac19" 8945d3e8 d "btstset #0x1d, dbl(*ar1(t1<<#1)), tc2" 91316f3d d "callcc #0x008cce," 9b8cceef d "circ" 27 +d "rpt csr" 01dd 0x0 nop +d "rptadd csr, #0x7" 0177 0x0 nop +d "rptsub csr, #0x6" 0106 0x0 nop +d "rptadd csr, ar0" 0180 0x0 nop d "cmpand ac29.h < ac25, !tc1, tc2" a4dd1989 d "cmporu ac13.l >= t0, !tc2, tc2" a4edb0af d "delay @#0x1b" 601bf4 +d "amar @#0x12" 6212c0 +d "amar @#0x7f" 627fc0 d "dmaxdiff ac13, ac12, ac22, ac6, trn4" d4d6260d4c d "dmindiff ac13, ac22, ac31, ac4, trn1" d49fe4adf6 d "exp ac23, ac12.h" a94c1457 +d "mant ac31, ac0 :: nexp ac31, ac0" a900801f +d "mant ac0, ac16 :: nexp ac0, ac0.h" a9409000 +d "cmpand ac1 < ac25, tc1, tc2" a4011989 0x0 (set st0_55 (ite (&& (&& (sle (var ac1) (var ac25)) (! (== (var ac1) (var ac25)))) (! (is_zero (& (var st0_55) (bv 16 0x2000))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) +d "cmpor ac1 < ac0, tc1, tc2" a4018089 0x0 (set st0_55 (ite (|| (&& (sle (var ac1) (var ac0)) (! (== (var ac1) (var ac0)))) (! (is_zero (& (var st0_55) (bv 16 0x2000))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) +d "bcnt ac2, ac8, tc2, ac0" a9806208 d "firsadd *ar6, *ar15+, *ar4, ac6, ac22" eb36861f3634 d "firssub *ar3-, *ar5-, *ar6-, ac10, ac18" eb038a055206 d "lms *(ar5+t0), *(ar15+t0), ac31, ac8" cee55fef48 @@ -175,20 +422,29 @@ d "lmsf *(ar9+t1), *(ar3-t1), ac12, ac9" cef92cd3a9 d "mac ar8, ac22, ac5" aa05a816 d "mack #0x3d, rptc, ac12, ac15" c70fecb93d d "macmk t3 = *ar6(t0), #0xcb, ac7, ac28" b9261cc7cb +d "mpymk t3 = *ar6(t0), #0xcb, ac28" b9261c00cb d "mant ac29, ac27 :: nexp ac29, ac22.l" a976fb1d d "mas ac13, ac6, ac25" aa99060d d "masm t3 = *(ar10-t0), ac6, ac30, ac6" bb4a069e06 +d "mpym t3 = *(ar10-t0), ac6, ac6" bb4a060006 +d "macm t3 = *(ar10-t0), ac6, ac0, ac6" bb4a064006 d "maxdiff ac8, ac12, ac5, ac3, pair(trn4)" d44543c84c d "mindiff ac17, ac7, ac8, ac7, pair(trn0)" d408c7b1c7 d "mpyr ar5, ac16, ac7" aa271025 d "mpyk #0x59, t2, ac1" c7012c3259 d "mpymk *(ar0-t0b), #0xd9, ac10" b8004a1ed9 +d "macmk *(ar0-t0b), #0xd9, ac0, ac10" b8004a40d9 +d "mpymkf *(ar0-t0b), #0xd9, ac10" b8004a20d9 d "not ac6.l, ar0" 75a0e6 d "popboth xar13" 0ded d "reset" 0034 d "retcc ac3.l > #0" 089b d "round ac19, ac2" 7922d3 +d "satr ac19, ac2" 79a2d3 +d "sat ac19, ac2" 79c2d3 d "rpt csr" 01dd +d "rpt #0x3" 6c0003 0x0 nop +d "rpt #0xffff" 6cffff 0x0 nop d "rptadd csr, #0x7" 0177 d "rptb #0x004976" 6f4976 d "rptcc #0xed, == #0" 6d0ded @@ -198,9 +454,13 @@ d "sftcc ac7, tc2" a987b9b3 d "sftsc ac27, ac17.h," a6bd9bd1 d "sim_trig" 03fc d "sqam *ar3, ac6, ac6" 92038646 +d "sqrm *ar3, ac6" 92038600 0x0 (set ac6 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))))) d "sqdst *(ar3+t0), *ar5+, ac10, ac2" ce636a1562 d "sqrmr *ar13, ac14" 920dae0b d "sqsm @#0x5e, ac22, ac9" 925ec996 d "subadd t3, dual(*sp(#0x60)), ac3" 8fe0c3f3 d "subc *ar0, ac3, ac3" b30083e31a +d "subc @#0x0, ac3, ac3" b300c3e31a d "swap ar1, ar3" 0386 +d "swap ac0, ac2" 0381 0x0 (seq (set ac0 (^ (var ac0) (var ac2))) (set ac2 (^ (var ac2) (var ac0))) (set ac0 (^ (var ac0) (var ac2)))) +d "swap t0, t2" 0389 0x0 (seq (set t0 (^ (var t0) (var t2))) (set t2 (^ (var t2) (var t0))) (set t0 (^ (var t0) (var t2)))) diff --git a/test/db/asm/tms320_c55x_32 b/test/db/asm/tms320_c55x_32 index 8f63c413b0..f2d04d46e1 100644 --- a/test/db/asm/tms320_c55x_32 +++ b/test/db/asm/tms320_c55x_32 @@ -49,6 +49,7 @@ d "b 0x1230" 061230 0x0 (jmp (bv 24 0x1233)) d "b 0x0123" 060123 0x0 (jmp (bv 24 0x126)) d "call 0xDEAD" 08dead 0x0 (jmp (bv 24 0xffdeb0)) d "rpt 0xDEAD" 0cdead 0x0 nop +d "|| rpt 0x1234" 0d1234 0x0 nop d "rptb 0xDEAD" 0edead 0x0 nop d "and ac0 << 0x00" 100000 d "or ac1 << 0x11, ac0" 101111 0x0 (set ac0 (| (var ac0) (<< (var ac1) (bv 8 0x11) false))) @@ -85,6 +86,21 @@ d "dmaxdiff ac0, ac1, ac2, ac3, trn1" 108d71 d "mindiff ac3, ac2, ac1, ac0" 107e80 d "dmindiff ac3, ac2, ac1, ac0, trn0" 107f80 d "dmindiff ac3, ac2, ac1, ac0, trn1" 107f81 +d "sftl ac0, 0x01" 100701 0x0 (set ac0 (<< (var ac0) (bv 6 0x1) false)) +d "sftl ac0, 0x37" 100737 0x0 (set ac0 (>> (var ac0) (bv 6 0x9) false)) +d "sfts ac0, 0x01" 100501 0x0 (set ac0 (<< (var ac0) (bv 6 0x1) false)) +d "sftl ac0, 0x01, ac1" 104701 0x0 (set ac1 (<< (var ac0) (bv 6 0x1) false)) +d "sftsc ac0, 0x01" 100601 +d "and ac0 << 0x01" 100001 +d "or ac0 << 0x01" 100101 +d "xor ac0 << 0x01" 100201 +d "and ac0 << 0x01, ac1" 104001 0x0 (set ac1 (& (var ac1) (<< (var ac0) (bv 8 0x1) false))) +d "xor ac0 << 0x01, ac1" 104201 0x0 (set ac1 (^ (var ac1) (<< (var ac0) (bv 8 0x1) false))) +d "add ac0 << 0x01, ac0" 100301 0x0 (set ac0 (+ (var ac0) (<< (var ac0) (bv 8 0x1) false))) +d "sub ac0 << 0x01, ac0" 100401 0x0 (set ac0 (- (var ac0) (<< (var ac0) (bv 8 0x1) false))) +d "add ac0 << 0x01, ac1" 104301 0x0 (set ac1 (+ (var ac1) (<< (var ac0) (bv 8 0x1) false))) +d "add ac0 << 0x37, ac1" 104337 0x0 (set ac1 (+ (var ac1) (<< (var ac0) (bv 8 0x37) false))) +d "add ac0 << 0x3F, ac1" 10433f 0x0 (set ac1 (+ (var ac1) (<< (var ac0) (bv 8 0x3f) false))) d "cmp ac0 == ar2, tc1" 1200a0 0x0 (set st0_55 (ite (== (var ac0) (cast 40 (msb (var ar2)) (var ar2))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "cmpand ac1 == ar3, tc1, tc2" 1211b1 0x0 (set st0_55 (ite (&& (== (var ac1) (cast 40 (msb (var ar3)) (var ar3))) (! (is_zero (& (var st0_55) (bv 16 0x2000))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) d "cmpand ac2 == ar4, !tc1, tc2" 1221c9 0x0 (set st0_55 (ite (&& (== (var ac2) (cast 40 (msb (var ar4)) (var ar4))) (! (! (is_zero (& (var st0_55) (bv 16 0x2000)))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) @@ -95,10 +111,31 @@ d "cmpandu t2 == ac0, tc2, tc1" 126106 0x0 (set st0_55 (ite (&& (== (cast 40 fal d "cmpandu t3 == ac1, !tc2, tc1" 12711e 0x0 (set st0_55 (ite (&& (== (cast 40 false (var t3)) (var ac1)) (! (! (is_zero (& (var st0_55) (bv 16 0x1000)))))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "cmporu ar0 == ac2, tc1, tc2" 128225 0x0 (set st0_55 (ite (|| (== (cast 40 false (var ar0)) (var ac2)) (! (is_zero (& (var st0_55) (bv 16 0x2000))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) d "cmporu ar1 == ac3, !tc1, tc2" 12923d 0x0 (set st0_55 (ite (|| (== (cast 40 false (var ar1)) (var ac3)) (! (! (is_zero (& (var st0_55) (bv 16 0x2000)))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) +d "cmp ac0 == ac0, tc1" 120000 0x0 (set st0_55 (ite (== (var ac0) (var ac0)) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmp ac0 < ac1, tc1" 120410 0x0 (set st0_55 (ite (&& (sle (var ac0) (var ac1)) (! (== (var ac0) (var ac1)))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmp ac3 >= ac0, tc2" 123801 0x0 (set st0_55 (ite (! (&& (sle (var ac3) (var ac0)) (! (== (var ac3) (var ac0))))) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff)))) +d "cmp t0 != ac0, tc1" 124c00 0x0 (set st0_55 (ite (! (== (cast 40 (msb (var t0)) (var t0)) (var ac0))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmpand ac0 == ac1, tc1, tc1" 120110 0x0 (set st0_55 (ite (&& (== (var ac0) (var ac1)) (! (is_zero (& (var st0_55) (bv 16 0x2000))))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) +d "cmpor ac0 == ac0, tc2, tc1" 120202 0x0 (set st0_55 (ite (|| (== (var ac0) (var ac0)) (! (is_zero (& (var st0_55) (bv 16 0x1000))))) (| (var st0_55) (bv 16 0x2000)) (& (var st0_55) (bv 16 0xdfff)))) d "rol carry, ac0, carry, ac3" 120330 0x0 (seq (set ac3 (| (<< (var ac0) (bv 6 0x1) false) (cast 40 false (& (>> (var st0_55) (bv 8 0xb) false) (bv 16 0x1))))) (set st0_55 (ite (msb (var ac0)) (| (var st0_55) (bv 16 0x800)) (& (var st0_55) (bv 16 0xf7ff))))) d "rol tc2, ac1, carry, ac2" 121321 0x0 (seq (set ac2 (| (<< (var ac1) (bv 6 0x1) false) (cast 40 false (& (>> (var st0_55) (bv 8 0xc) false) (bv 16 0x1))))) (set st0_55 (ite (msb (var ac1)) (| (var st0_55) (bv 16 0x800)) (& (var st0_55) (bv 16 0xf7ff))))) d "ror tc2, ac2, carry, ac1" 12231a 0x0 (seq (set ac1 (| (>> (var ac2) (bv 6 0x1) false) (<< (cast 40 false (& (>> (var st0_55) (bv 8 0xc) false) (bv 16 0x1))) (bv 6 0x27) false))) (set st0_55 (ite (lsb (var ac2)) (| (var st0_55) (bv 16 0x800)) (& (var st0_55) (bv 16 0xf7ff))))) d "ror tc2, ac3, tc2, ac0" 12330b 0x0 (seq (set ac0 (| (>> (var ac3) (bv 6 0x1) false) (<< (cast 40 false (& (>> (var st0_55) (bv 8 0xc) false) (bv 16 0x1))) (bv 6 0x27) false))) (set st0_55 (ite (lsb (var ac3)) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff))))) +d "rol carry, ac0, tc2, ac3" 120332 0x0 (seq (set ac3 (| (<< (var ac0) (bv 6 0x1) false) (cast 40 false (& (>> (var st0_55) (bv 8 0xb) false) (bv 16 0x1))))) (set st0_55 (ite (msb (var ac0)) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff))))) +d "ror carry, ac1, tc2, ac1" 121319 0x0 (seq (set ac1 (| (>> (var ac1) (bv 6 0x1) false) (<< (cast 40 false (& (>> (var st0_55) (bv 8 0xb) false) (bv 16 0x1))) (bv 6 0x27) false))) (set st0_55 (ite (lsb (var ac1)) (| (var st0_55) (bv 16 0x1000)) (& (var st0_55) (bv 16 0xefff))))) +d "rol carry, ac0, carry, ar2" 1203a0 +d "mpyk 0x1234, ac0" 79123400 0x0 (set ac0 (* (bv 40 0x1234) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "mpyk 0x12, ac0, ac3" 1e1234 0x0 (set ac3 (* (bv 40 0x12) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "mpykr 0x1234, ac0" 79123401 0x0 (set ac0 (& (+ (* (bv 40 0x1234) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpyk 0x80, ac0" 1e8000 0x0 (set ac0 (* (bv 40 0x80) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "mpykr 0x00, ac0" 1e0001 0x0 (set ac0 (& (+ (* (bv 40 0x0) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpyk 0x1234, ac0, ac1" 79123410 0x0 (set ac1 (* (bv 40 0x1234) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "mack t0, 0x1234, ac0" 79123402 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (var t0)) (var t0)) (bv 40 0x1234)))) +d "mackr t0, 0x1234, ac0" 79123403 0x0 (set ac0 (& (+ (+ (var ac0) (* (cast 40 (msb (var t0)) (var t0)) (bv 40 0x1234))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mack t3, 0x80, ac0" 1e800e 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (var t3)) (var t3)) (bv 40 0x80)))) +d "mack t2, 0x12, ac1, ac3" 1e127a 0x0 (set ac3 (+ (var ac1) (* (cast 40 (msb (var t2)) (var t2)) (bv 40 0x12)))) +d "|| mpyk 0x12, ac0, ac3" 1f1234 0x0 (set ac3 (* (bv 40 0x12) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) +d "|| mack t2, 0x12, ac1, ac3" 1f127a 0x0 (set ac3 (+ (var ac1) (* (cast 40 (msb (var t2)) (var t2)) (bv 40 0x12)))) d "aadd ac0, ac0" 140000 0x0 (set ac0 (+ (var ac0) (var ac0))) d "amov ac0, ac0" 140001 0x0 (set ac0 (var ac0)) d "asub ac0, ac0" 140002 0x0 (set ac0 (- (var ac0) (var ac0))) @@ -117,6 +154,37 @@ d "asub xar2, xar3" 14a1b2 0x0 (set xar3 (- (var xar3) (var xar2))) d "aadd xar3, xar2" 14b1a8 0x0 (set xar2 (+ (var xar2) (var xar3))) d "amov xar4, xar1" 14c199 0x0 (set xar1 (var xar4)) d "asub xar5, xar0" 14d18a 0x0 (set xar0 (- (var xar0) (var xar5))) +d "amov ac0, ac0" 140001 0x0 (set ac0 (var ac0)) +d "asub ac0, ac1" 140012 0x0 (set ac1 (- (var ac1) (var ac0))) +d "asub ac0, t0" 140042 0x0 (set t0 (- (var t0) (cast 16 false (var ac0)))) +d "amov ac0, xar0" 140081 0x0 (set xar0 (cast 23 false (var ac0))) +d "rptcc 0x00, ac0 == 0" 000000 0x0 nop +d "rptcc 0xFF, ac0 == 0" 0000ff 0x0 nop +d "rptcc 0x00, ac0 > 0" 004000 0x0 nop +d "rptcc 0x00, !tc1 ^ !tc2" 00ff00 0x0 nop +d "rptb 0x0000" 0e0000 0x0 nop +d "rptb 0xFF00" 0eff00 0x0 nop +d "rpt csr" 4800 0x0 nop +d "rptadd csr, ac0" 4801 0x0 nop +d "rptadd csr, ar7" 48f1 0x0 nop +d "rptadd csr, 0xF" 48f2 0x0 nop +d "rptsub csr, 0x0" 4803 0x0 nop +d "rptsub csr, 0xF" 48f3 0x0 nop +d "exp ac0, t0" 100800 +d "exp ac1, t0" 101800 +d "exp ac2, t1" 102810 +d "exp ac3, t3" 103830 +d "bcnt ac0, ac1, tc2, t0" 100a41 +d "maxdiff ac1, ac0, ac0, ac0" 101c00 +d "maxdiff ac0, ac3, ac0, ac3" 100cff +d "mindiff ac0, ac0, ac0, ac0" 100e00 +d "dmaxdiff ac0, ac0, ac0, ac0, trn1" 100d01 +d "dmindiff ac0, ac0, ac0, ac1, trn0" 100f10 +d "mov *+ar0, ac0" a019 +d "mov *-ar0, ac0" a01b +d "mov *+ar3, ac1" a179 +d "mov ac0, *+ar0" c019 +d "mov ac0, *-ar0" c01b d "mov 0x1A, dph" 1601a0 0x0 (set dph (bv 7 0x1a)) d "mov 0x25, dph" 160250 0x0 (set dph (bv 7 0x25)) d "mov 0x4A, dph" 1604a0 0x0 (set dph (bv 7 0x4a)) @@ -131,6 +199,8 @@ d "mov 0x5B1, brc1" 165b1a 0x0 (set brc1 (bv 16 0x5b1)) d "and 0x11, ac0, ac1" 181110 0x0 (set ac1 (& (var ac0) (bv 40 0x11))) d "or 0x22, ac2, ac3" 1a2232 0x0 (set ac3 (| (var ac2) (bv 40 0x22))) d "xor 0x33, t0, t1" 1c3354 0x0 (set t1 (^ (var t0) (bv 16 0x33))) +d "|| or 0x12, ac0, ac1" 1b1210 0x0 (set ac1 (| (var ac0) (bv 40 0x12))) +d "and 0x12, ac0, t0" 181240 0x0 (set t0 (& (cast 16 false (var ac0)) (bv 16 0x12))) d "mpyk 0x11, ac0, ac1" 1e1110 0x0 (set ac1 (* (bv 40 0x11) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) d "mpykr 0x22, ac1, ac2" 1e2261 0x0 (set ac2 (& (+ (* (bv 40 0x22) (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "mack t1, 0x33, ac0, ac1" 1e3316 0x0 (set ac1 (+ (var ac0) (* (cast 40 (msb (var t1)) (var t1)) (bv 40 0x33)))) @@ -155,13 +225,29 @@ d "neg t1, ar4" 345c 0x0 (set ar4 (- (bv 16 0x0) (var t1))) d "not t2, ar5" 366d 0x0 (set ar5 (~ (var t2))) d "psh ac1, ac2" 3812 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac1))) (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac2)))) d "pop ac3, t0" 3a34 0x0 (seq (set ac3 (| (& (var ac3) (bv 40 0xff00000000)) (cast 40 false (loadw 0 32 (* (cast 24 false (var sp)) (bv 24 0x2)))))) (set sp (+ (var sp) (bv 16 0x2))) (set t0 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) +d "|| psh ac0, t0" 3904 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac0))) (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var t0))) +d "psh ar0, ar4" 388c 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var ar0)) (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var ar4))) d "mov 0xF, t0" 3cf4 0x0 (set t0 (bv 16 0xf)) d "mov -0xF, t1" 3ef5 d "add 0xF, t2" 40f6 0x0 (set t2 (+ (var t2) (bv 16 0xf))) d "sub 0xF, t3" 42f7 0x0 (set t3 (- (var t3) (bv 16 0xf))) d "mov hi(ac0), ar0" 4408 0x0 (set ar0 (cast 16 false (>> (var ac0) (bv 8 0x10) false))) -d "sfts ac0, #-1" 4440 +d "mov hi(ac0), ac0" 4400 0x0 (set ac0 (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) +d "mov hi(ac1), ac0" 4410 0x0 (set ac0 (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) +d "mov hi(ac0), ar7" 440f 0x0 (set ar7 (cast 16 false (>> (var ac0) (bv 8 0x10) false))) +d "|| mov hi(ac0), ac0" 4500 0x0 (set ac0 (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) +d "sfts ac0, #-1" 4440 0x0 (set ac0 (>> (var ac0) (bv 6 0x1) (msb (var ac0)))) d "sfts ac0, #1" 4450 0x0 (set ac0 (<< (var ac0) (bv 6 0x1) false)) +d "sfts t0, #1" 4454 0x0 (set t0 (<< (var t0) (bv 6 0x1) false)) +d "sfts ar0, #1" 4458 0x0 (set ar0 (<< (var ar0) (bv 6 0x1) false)) +d "|| sfts ac0, #1" 4550 0x0 (set ac0 (<< (var ac0) (bv 6 0x1) false)) +d "mov sp, ac0" 4480 0x0 (set ac0 (cast 40 (msb (var sp)) (var sp))) +d "mov ssp, ac0" 4490 0x0 (set ac0 (cast 40 (msb (var ssp)) (var ssp))) +d "mov cdp, ac0" 44a0 0x0 (set ac0 (cast 40 (msb (var cdp)) (var cdp))) +d "mov brc0, ac0" 44c0 0x0 (set ac0 (cast 40 (msb (var brc0)) (var brc0))) +d "mov rptc, ac0" 44e0 0x0 (set ac0 (cast 40 (msb (var rptc)) (var rptc))) +d "mov sp, ar0" 4488 0x0 (set ar0 (var sp)) +d "|| mov sp, ac0" 4580 0x0 (set ac0 (cast 40 (msb (var sp)) (var sp))) d "mov sp, ar0" 4488 0x0 (set ar0 (var sp)) d "mov ssp, ar1" 4499 0x0 (set ar1 (var ssp)) d "mov cdp, ar2" 44aa 0x0 (set ar2 (var cdp)) @@ -187,15 +273,40 @@ d "b 0x7F" 4a7f 0x0 (jmp (bv 24 0x81)) d "rptblocal 0x7F" 4aff 0x0 nop d "rpt 0xFF" 4cff 0x0 nop d "aadd 0xFF, sp" 4eff 0x0 (set sp (+ (var sp) (bv 16 0xff))) +d "aadd 0x08, sp" 4e08 0x0 (set sp (+ (var sp) (bv 16 0x8))) +d "|| aadd 0x01, sp" 4f01 0x0 (set sp (+ (var sp) (bv 16 0x1))) d "sftl ac0, #1" 5000 0x0 (set ac0 (<< (var ac0) (bv 6 0x1) false)) -d "sftl ac0, #-1" 5001 +d "sftl ac0, #-1" 5001 0x0 (set ac0 (>> (var ac0) (bv 6 0x1) false)) +d "sftl t0, #1" 5040 0x0 (set t0 (<< (var t0) (bv 6 0x1) false)) +d "sftl ar0, #1" 5080 0x0 (set ar0 (<< (var ar0) (bv 6 0x1) false)) d "pop ac0" 5002 0x0 (seq (set ac0 (| (& (var ac0) (bv 40 0xff00000000)) (cast 40 false (loadw 0 32 (* (cast 24 false (var sp)) (bv 24 0x2)))))) (set sp (+ (var sp) (bv 16 0x2)))) d "pop dbl(ac0)" 5003 0x0 (seq (set ac0 (| (& (var ac0) (bv 40 0xff00000000)) (cast 40 false (loadw 0 32 (* (cast 24 false (var sp)) (bv 24 0x2)))))) (set sp (+ (var sp) (bv 16 0x2)))) d "psh ac0" 5006 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac0)))) d "psh dbl(ac0)" 5007 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac0)))) d "popboth ac0" 5004 d "pshboth ac0" 5005 +d "popboth xsp" 5044 +d "popboth xcdp" 5074 +d "popboth xar0" 5084 +d "pshboth xar7" 50f5 +d "|| popboth ac0" 5104 +d "pop t0" 5042 0x0 (seq (set t0 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) +d "psh t0" 5046 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var t0))) +d "pop ar0" 5082 0x0 (seq (set ar0 (loadw 0 16 (* (cast 24 false (var sp)) (bv 24 0x2)))) (set sp (+ (var sp) (bv 16 0x1)))) +d "psh ar0" 5086 0x0 (seq (set sp (- (var sp) (bv 16 0x1))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (var ar0))) +d "pop dbl(ac0)" 5083 0x0 (seq (set ac0 (| (& (var ac0) (bv 40 0xff00000000)) (cast 40 false (loadw 0 32 (* (cast 24 false (var sp)) (bv 24 0x2)))))) (set sp (+ (var sp) (bv 16 0x2)))) +d "psh dbl(ac2)" 5067 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac2)))) +d "|| pop ac0" 5102 0x0 (seq (set ac0 (| (& (var ac0) (bv 40 0xff00000000)) (cast 40 false (loadw 0 32 (* (cast 24 false (var sp)) (bv 24 0x2)))))) (set sp (+ (var sp) (bv 16 0x2)))) +d "|| psh ac0" 5106 0x0 (seq (set sp (- (var sp) (bv 16 0x2))) (storew 0 (* (cast 24 false (var sp)) (bv 24 0x2)) (cast 32 false (var ac0)))) d "mov ac0, hi(ac0)" 5200 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (cast 16 false (var ac0))) (bv 6 0x10) false))) +d "mov ac0, hi(ac1)" 5201 0x0 (set ac1 (| (& (var ac1) (bv 40 0xff0000ffff)) (<< (cast 40 false (cast 16 false (var ac0))) (bv 6 0x10) false))) +d "mov t0, hi(ac0)" 5240 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (var t0)) (bv 6 0x10) false))) +d "mov ar0, hi(ac0)" 5280 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (var ar0)) (bv 6 0x10) false))) +d "|| mov ac0, hi(ac0)" 5300 0x0 (set ac0 (| (& (var ac0) (bv 40 0xff0000ffff)) (<< (cast 40 false (cast 16 false (var ac0))) (bv 6 0x10) false))) +d "mov ac0, sp" 5208 0x0 (set sp (cast 16 false (var ac0))) +d "mov ac0, csr" 520c 0x0 (set csr (cast 16 false (var ac0))) +d "mov ac0, brc0" 520e 0x0 (set brc0 (cast 16 false (var ac0))) +d "mov t0, sp" 5248 0x0 (set sp (var t0)) d "mov ac0, sp" 5208 0x0 (set sp (cast 16 false (var ac0))) d "mov ac0, ssp" 5209 0x0 (set ssp (cast 16 false (var ac0))) d "mov ac0, cdp" 520a 0x0 (set cdp (cast 16 false (var ac0))) @@ -203,24 +314,86 @@ d "mov ac0, csr" 520c 0x0 (set csr (cast 16 false (var ac0))) d "mov ac0, brc1" 520d 0x0 (set brc1 (cast 16 false (var ac0))) d "mov ac0, brc0" 520e 0x0 (set brc0 (cast 16 false (var ac0))) d "addrv ac1, ac2" 5491 -d "sqar ac1, ac2" 5493 0x0 (set ac2 (* (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1))) (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1))))) +d "addv ac0" 5400 0x0 (set ac0 (+ (var ac0) (ite (msb (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "addrv ac0" 5401 0x0 (set ac0 (+ (var ac0) (ite (msb (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "addv ac1, ac2" 5490 0x0 (set ac2 (+ (var ac2) (ite (msb (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "addrv ac1, ac2" 5491 0x0 (set ac2 (+ (var ac2) (ite (msb (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "addv ac1, ac0" 5410 0x0 (set ac0 (+ (var ac0) (ite (msb (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "sqar ac1, ac2" 5493 0x0 (set ac2 (& (+ (+ (var ac2) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "sqsr ac1, ac2" 5495 -d "mpyr ac1, ac2" 5497 0x0 (set ac2 (& (+ (* (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1))) (cast 40 (msb (cast 16 false (var ac2))) (cast 16 false (var ac2)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) -d "sqrr ac1, ac2" 5499 0x0 (set ac2 (& (+ (* (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1))) (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpyr ac1, ac2" 5497 0x0 (set ac2 (& (+ (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac2) (bv 8 0x10) false))) (cast 16 false (>> (var ac2) (bv 8 0x10) false)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpy ac1, ac2" 5496 0x0 (set ac2 (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac2) (bv 8 0x10) false))) (cast 16 false (>> (var ac2) (bv 8 0x10) false))))) +d "sqr ac1, ac2" 5498 0x0 (set ac2 (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))))) +d "sqa ac1, ac2" 5492 0x0 (set ac2 (+ (var ac2) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "sqs ac1, ac2" 5494 0x0 (set ac2 (- (var ac2) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "mpy ac0" 5406 0x0 (set ac0 (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "sqr ac0" 5408 0x0 (set ac0 (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "sqa ac0" 5402 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "sqr ac0, ac3" 54c8 0x0 (set ac3 (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "sqrr ac1, ac2" 5499 0x0 (set ac2 (& (+ (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "round ac1, ac2" 549b 0x0 (set ac2 (& (+ (var ac1) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "satr ac1, ac2" 549d 0x0 (set ac2 (ite (! (sle (var ac1) (bv 40 0x7fffffff))) (bv 40 0x7fffffff) (ite (&& (sle (var ac1) (bv 40 0xff80000000)) (! (== (var ac1) (bv 40 0xff80000000)))) (bv 40 0xff80000000) (var ac1)))) -d "macr ac0, t0, ac0" 5601 0x0 (set ac0 (& (+ (+ (var ac0) (* (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) -d "macr ac0, t0, ac1, ac1" 5641 0x0 (set ac1 (& (+ (+ (var ac1) (* (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) -d "macr ac1, t0, ac0, ac1" 5843 0x0 (set ac1 (& (+ (+ (var ac0) (* (cast 40 (msb (cast 16 false (var ac1))) (cast 16 false (var ac1))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) -d "masr t0, ac0, ac1" 5643 0x0 (set ac1 (& (+ (- (var ac1) (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) -d "mpyr t0, ac0, ac1" 5841 0x0 (set ac1 (& (+ (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (var ac0))) (cast 16 false (var ac0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "round ac0" 540a 0x0 (set ac0 (& (+ (var ac0) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "round ac1, ac2" 549a 0x0 (set ac2 (& (+ (var ac1) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "sat ac0" 540c 0x0 (set ac0 (ite (! (sle (var ac0) (bv 40 0x7fffffff))) (bv 40 0x7fffffff) (ite (&& (sle (var ac0) (bv 40 0xff80000000)) (! (== (var ac0) (bv 40 0xff80000000)))) (bv 40 0xff80000000) (var ac0)))) +d "sat ac1, ac2" 549c 0x0 (set ac2 (ite (! (sle (var ac1) (bv 40 0x7fffffff))) (bv 40 0x7fffffff) (ite (&& (sle (var ac1) (bv 40 0xff80000000)) (! (== (var ac1) (bv 40 0xff80000000)))) (bv 40 0xff80000000) (var ac1)))) +d "satr ac0" 540d 0x0 (set ac0 (ite (! (sle (var ac0) (bv 40 0x7fffffff))) (bv 40 0x7fffffff) (ite (&& (sle (var ac0) (bv 40 0xff80000000)) (! (== (var ac0) (bv 40 0xff80000000)))) (bv 40 0xff80000000) (var ac0)))) +d "macr ac0, t0, ac0" 5601 0x0 (set ac0 (& (+ (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macr ac0, t0, ac1, ac1" 5641 0x0 (set ac1 (& (+ (+ (var ac1) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macr ac1, t0, ac0, ac1" 5843 0x0 (set ac1 (& (+ (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (var t0)) (var t0)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "masr t0, ac0, ac1" 5643 0x0 (set ac1 (& (+ (- (var ac1) (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mac ac0, t0, ac0" 5600 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (var t0)) (var t0))))) +d "mac ac1, t0, ac0, ac0" 5610 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (var t0)) (var t0))))) +d "mas t0, ac0" 5602 0x0 (set ac0 (- (var ac0) (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "mas t0, ac1, ac0" 5612 0x0 (set ac0 (- (var ac0) (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false)))))) +d "mac ac0, t2, ac3, ac3" 56c8 0x0 (set ac3 (+ (var ac3) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (var t2)) (var t2))))) +d "mas t2, ac0, ac3" 56ca 0x0 (set ac3 (- (var ac3) (* (cast 40 (msb (var t2)) (var t2)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "mpyr t0, ac0, ac1" 5841 0x0 (set ac1 (& (+ (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpy t0, ac0" 5800 0x0 (set ac0 (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "mpy t0, ac1, ac0" 5810 0x0 (set ac0 (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))))) +d "mpyr t0, ac0" 5801 0x0 (set ac0 (& (+ (* (cast 40 (msb (var t0)) (var t0)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpy t2, ac0, ac3" 58c8 0x0 (set ac3 (* (cast 40 (msb (var t2)) (var t2)) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "sftl ac0, t0" 5c00 0x0 (set ac0 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac0) (- (bv 16 0x0) (var t0)) false) (<< (var ac0) (var t0) false))) +d "sfts ac0, t0" 5c01 0x0 (set ac0 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac0) (- (bv 16 0x0) (var t0)) (msb (var ac0))) (<< (var ac0) (var t0) false))) +d "sftl ac1, t0, ac0" 5c10 0x0 (set ac0 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac1) (- (bv 16 0x0) (var t0)) false) (<< (var ac1) (var t0) false))) +d "sfts ac1, t0, ac0" 5c11 0x0 (set ac0 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac1) (- (bv 16 0x0) (var t0)) (msb (var ac1))) (<< (var ac1) (var t0) false))) +d "sftl ac0, t1, ac1" 5c44 0x0 (set ac1 (ite (&& (sle (var t1) (bv 16 0x0)) (! (== (var t1) (bv 16 0x0)))) (>> (var ac0) (- (bv 16 0x0) (var t1)) false) (<< (var ac0) (var t1) false))) +d "sftl ac3, t2, ac2" 5cb8 0x0 (set ac2 (ite (&& (sle (var t2) (bv 16 0x0)) (! (== (var t2) (bv 16 0x0)))) (>> (var ac3) (- (bv 16 0x0) (var t2)) false) (<< (var ac3) (var t2) false))) +d "mpym *sp(#0h), *cdp, ac0" d10000 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) +d "mpym *sp(#0h), *cdp+, ac0" d10001 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xcdp (+ (var xcdp) (bv 23 0x1)))) +d "mpym *ar0, *cdp, ac0" d10100 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) +d "mpymr *sp(#0h), *cdp, ac0" d10040 0x0 (set ac0 (& (+ (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macm *sp(#0h), *cdp, ac0" d10004 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "masm *sp(#0h), *cdp, ac0" d10008 0x0 (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "macmr *sp(#0h), *cdp, ac0" d10044 0x0 (set ac0 (& (+ (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macm *sp(#0h), ac0, ac1" d20010 0x0 (set ac1 (+ (var ac1) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "masm *sp(#0h), ac0, ac1" d20014 0x0 (set ac1 (- (var ac1) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) +d "macmr *sp(#0h), ac0, ac1" d20050 0x0 (set ac1 (& (+ (+ (var ac1) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macm *sp(#0h), t0, ac0" d40000 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) +d "macm *sp(#0h), t0, ac0, ac1" d40010 0x0 (set ac1 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) +d "masm *sp(#0h), t0, ac0, ac1" d50010 0x0 (set ac1 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) +d "mpym *sp(#0h), ac0, ac1" d30010 0x0 (set ac1 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))))) +d "mpym *sp(#0h), t0, ac0" d30004 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0)))) +d "mpymr *sp(#0h), ac0" d30040 0x0 (set ac0 (& (+ (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "mpy *sp(#0h), uns(*cdp), ac0" d00004 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) +d "mac *sp(#0h), uns(*cdp), ac0" d00008 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "mas *sp(#0h), uns(*cdp), ac0" d0000c 0x0 (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "mpym *sp(#0h), *(cdp+t0), ac0" d10003 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xcdp (+ (var xcdp) (cast 23 (msb (var t0)) (var t0))))) +d "mpy *sp(#0h), uns(*(cdp+t0)), ac0" d00007 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xcdp (+ (var xcdp) (cast 23 (msb (var t0)) (var t0))))) +d "mpym t3=*sp(#0h), *cdp, ac0" d10080 0x0 (seq (set t3 (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "macm t3=*ar0, t0, ac0" d40180 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0)))))) d "add ac0 << t0, ac0" 5a00 0x0 (set ac0 (+ (var ac0) (<< (var ac0) (var t0) false))) d "sub ac0 << t0, ac0" 5a01 0x0 (set ac0 (- (var ac0) (<< (var ac0) (var t0) false))) d "sftcc ac0, tc1" 5a02 d "sftcc ac0, tc2" 5a03 +d "sftcc ac1, tc1" 5a42 +d "sftcc ac2, tc1" 5a82 +d "sftcc ac3, tc1" 5ac2 d "sftl ac0, t0, ac1" 5c40 0x0 (set ac1 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac0) (- (bv 16 0x0) (var t0)) false) (<< (var ac0) (var t0) false))) d "sfts ac0, t0, ac1" 5c41 0x0 (set ac1 (ite (&& (sle (var t0) (bv 16 0x0)) (! (== (var t0) (bv 16 0x0)))) (>> (var ac0) (- (bv 16 0x0) (var t0)) (msb (var ac0))) (<< (var ac0) (var t0) false))) d "sftsc ac0, t0, ac1" 5c42 +d "sftsc ac0, t0" 5c02 +d "sftsc ac1, t0, ac0" 5c12 d "swap ac0, ac2" 5e00 0x0 (seq (set ac0 (^ (var ac0) (var ac2))) (set ac2 (^ (var ac2) (var ac0))) (set ac0 (^ (var ac0) (var ac2)))) d "swap ac1, ac3" 5e01 0x0 (seq (set ac1 (^ (var ac1) (var ac3))) (set ac3 (^ (var ac3) (var ac1))) (set ac1 (^ (var ac1) (var ac3)))) d "swap t0, t2" 5e04 0x0 (seq (set t0 (^ (var t0) (var t2))) (set t2 (^ (var t2) (var t0))) (set t0 (^ (var t0) (var t2)))) @@ -238,14 +411,37 @@ d "swapp ar4, t0" 5e1c 0x0 (seq (set ar4 (^ (var ar4) (var t0))) (set t0 (^ (var d "swapp ar6, t2" 5e1e 0x0 (seq (set ar6 (^ (var ar6) (var t2))) (set t2 (^ (var t2) (var ar6))) (set ar6 (^ (var ar6) (var t2))) (set ar7 (^ (var ar7) (var t3))) (set t3 (^ (var t3) (var ar7))) (set ar7 (^ (var ar7) (var t3)))) d "swap4 ar4, t0" 5e2c 0x0 (seq (set ar4 (^ (var ar4) (var t0))) (set t0 (^ (var t0) (var ar4))) (set ar4 (^ (var ar4) (var t0))) (set ar5 (^ (var ar5) (var t1))) (set t1 (^ (var t1) (var ar5))) (set ar5 (^ (var ar5) (var t1))) (set ar6 (^ (var ar6) (var t2))) (set t2 (^ (var t2) (var ar6))) (set ar6 (^ (var ar6) (var t2))) (set ar7 (^ (var ar7) (var t3))) (set t3 (^ (var t3) (var ar7))) (set ar7 (^ (var ar7) (var t3)))) d "swap ar0, ar1" 5e38 0x0 (seq (set ar0 (^ (var ar0) (var ar1))) (set ar1 (^ (var ar1) (var ar0))) (set ar0 (^ (var ar0) (var ar1)))) +d "add ac0 << t0, ac0" 5a00 0x0 (set ac0 (+ (var ac0) (<< (var ac0) (var t0) false))) +d "sub ac0 << t0, ac0" 5a01 0x0 (set ac0 (- (var ac0) (<< (var ac0) (var t0) false))) +d "add ac1 << t0, ac0" 5a10 0x0 (set ac0 (+ (var ac0) (<< (var ac1) (var t0) false))) +d "add ac0 << t1, ac1" 5a44 0x0 (set ac1 (+ (var ac1) (<< (var ac0) (var t1) false))) +d "add ac3 << t3, ac3" 5afc 0x0 (set ac3 (+ (var ac3) (<< (var ac3) (var t3) false))) +d "sub ac3 << t3, ac3" 5afd 0x0 (set ac3 (- (var ac3) (<< (var ac3) (var t3) false))) +d "bclr 0x0, st0_55" 4600 0x0 (set st0_55 (& (var st0_55) (bv 16 0xfffe))) +d "bset 0x0, st0_55" 4601 0x0 (set st0_55 (| (var st0_55) (bv 16 0x1))) +d "bclr 0x0, st1_55" 4602 0x0 (set st1_55 (& (var st1_55) (bv 16 0xfffe))) +d "bclr 0x0, st3_55" 4606 0x0 (set st3_55 (& (var st3_55) (bv 16 0xfffe))) +d "bclr 0xE, st0_55" 46e0 0x0 (set st0_55 (& (var st0_55) (bv 16 0xbfff))) +d "bset 0xE, st0_55" 46e1 0x0 (set st0_55 (| (var st0_55) (bv 16 0x4000))) +d "amov 0x0000, ac0" 77000000 0x0 (set ac0 (bv 40 0x0)) +d "amov 0xFF12, ac3" 77ff1230 0x0 (set ac3 (bv 40 0xff12)) +d "amov 0x0000, t0" 77000040 0x0 (set t0 (bv 16 0x0)) +d "amov 0x0000, ar4" 770000c0 0x0 (set ar4 (bv 16 0x0)) +d "amov 0x00FF, ac0" 7700ff00 0x0 (set ac0 (bv 40 0xff)) d "b 0x123456" 6a123456 0x0 (jmp (bv 24 0x123456)) d "call 0x123456" 6c123456 0x0 (jmp (bv 24 0x123456)) d "bcc 0xF, ac0 == 0" 6780 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x11)) nop) -d "bcc 0x1234, ac0 == 0" 6d001234 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x38)) nop) -d "bcc 0x123456, ac0 == 0" 6800123456 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x5b)) nop) +d "bcc 0x1234, ac0 == 0" 6d001234 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x1238)) nop) +d "bcc 0x123456, ac0 == 0" 6800123456 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x123456)) nop) d "bcc 0x22, ac0 == 0x11" 6f001122 0x0 (branch (== (var ac0) (bv 40 0x11)) (jmp (bv 24 0x26)) nop) d "callcc 0x123456, ac0 == 0" 6900123456 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x123456)) nop) -d "callcc 0x1234, ac0 == 0" 6e001234 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x1234)) nop) +d "callcc 0x1234, ac0 == 0" 6e001234 0x0 (branch (== (var ac0) (bv 40 0x0)) (jmp (bv 24 0x1238)) nop) +d "xcc tc1" 9664 0x0 nop +d "bcc 0x1234, tc1" 6d641234 0x0 (branch (lsb (>> (var st0_55) (bv 4 0xd) false)) (jmp (bv 24 0x1238)) nop) +d "bcc 0x1234, !carry" 6d761234 0x0 (branch (! (lsb (>> (var st0_55) (bv 4 0xb) false))) (jmp (bv 24 0x1238)) nop) +d "bcc 0x123456, tc1" 6864123456 0x0 (branch (lsb (>> (var st0_55) (bv 4 0xd) false)) (jmp (bv 24 0x123456)) nop) +d "callcc 0x1234, tc1" 6e641234 0x0 (branch (lsb (>> (var st0_55) (bv 4 0xd) false)) (jmp (bv 24 0x1238)) nop) +d "retcc tc1" 026400 d "add 0x1122 << 0x0, ac1, ac0" 70112240 0x0 (set ac0 (+ (var ac1) (<< (bv 40 0x1122) (bv 8 0x0) false))) d "sub 0x1122 << 0x1, ac1, ac0" 71112241 0x0 (set ac0 (- (var ac1) (<< (bv 40 0x1122) (bv 8 0x1) false))) d "and 0x1122 << 0x2, ac1, ac0" 72112242 0x0 (set ac0 (& (var ac1) (<< (bv 40 0x1122) (bv 8 0x2) false))) @@ -253,7 +449,11 @@ d "or 0x1122 << 0x3, ac1, ac0" 73112243 0x0 (set ac0 (| (var ac1) (<< (bv 40 0x1 d "xor 0x1122 << 0x4, ac1, ac0" 74112244 0x0 (set ac0 (^ (var ac1) (<< (bv 40 0x1122) (bv 8 0x4) false))) d "mov 0x1122 << 0x5, ac0" 75112245 d "bfxtr 0x1122, ac0, ac0" 76112200 +d "bfxtr 0x0000, ac1, ac0" 76000001 +d "bfxtr 0x0000, ac0, ac1" 76000010 +d "bfxtr 0x0000, ac0, t0" 76000040 d "bfxpa 0x1122, ac0, ac0" 76112204 +d "bfxpa 0x0000, ac1, ac0" 76000005 d "mov 0x1122, ac0" 76112208 0x0 (set ac0 (bv 40 0x1122)) d "amov 0x1122, ac0" 77112200 0x0 (set ac0 (bv 40 0x1122)) d "mov 0x1122, dp" 78112200 0x0 (set dp (bv 16 0x1122)) @@ -278,6 +478,9 @@ d "sub 0x1122, ac0, t0" 7c112240 0x0 (set t0 (cast 16 false (- (var ac0) (bv 40 d "and 0x1122, ac0, t0" 7d112240 0x0 (set t0 (& (cast 16 false (var ac0)) (bv 16 0x1122))) d "or 0x1122, ac0, t0" 7e112240 0x0 (set t0 (| (cast 16 false (var ac0)) (bv 16 0x1122))) d "xor 0x1122, ac0, t0" 7f112240 0x0 (set t0 (^ (cast 16 false (var ac0)) (bv 16 0x1122))) +d "add 0x8000, ac0" 7b800000 0x0 (set ac0 (+ (var ac0) (bv 40 0x8000))) +d "sub 0x8000, ac0, ac1" 7c800010 0x0 (set ac1 (- (var ac0) (bv 40 0xffffff8000))) +d "and 0x8000, ac0, ac0" 7d800000 0x0 (set ac0 (& (var ac0) (bv 40 0x8000))) d "idle" 7a00000c d "mov dbl(*ar0), dbl(*ar7(t0))" 8003f0 0x0 (storew 0 (* (+ (cast 24 false (var xar7)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)) (loadw 0 32 (* (cast 24 false (var xar0)) (bv 24 0x2)))) d "mov dbl(*ar1+), dbl(*(ar6 - t1))" 802760 0x0 (seq (storew 0 (* (cast 24 false (var xar6)) (bv 24 0x2)) (loadw 0 32 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (set xar1 (+ (var xar1) (bv 23 0x1))) (set xar6 (- (var xar6) (cast 23 (msb (var t1)) (var t1))))) @@ -311,9 +514,51 @@ d "amar *ar0 :: mac *ar0, *cdp, ac0" 83000c00 0x0 (seq nop (set ac0 (+ (var ac0) d "amar *ar0 :: mac *ar0, *cdp, ac0 >> #16" 84000400 0x0 (seq nop (set ac0 (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))))) d "amar *ar0 :: mas *ar0, *cdp, ac0" 85000000 0x0 (seq nop (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))))) d "amar *ar0, *ar0, *cdp" 85000800 0x0 nop -d "firsadd *ar0, *ar0, *cdp, ac0, ac0" 85000c00 -d "firssub *ar0, *ar0, *cdp, ac0, ac0" 85000c10 +d "firsadd *ar0, *ar0, *cdp, ac0, ac0" 85000c00 0x0 (seq (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set ac0 (+ (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "firssub *ar0, *ar0, *cdp, ac0, ac0" 85000c10 0x0 (seq (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "firsadd *ar1+, *ar3-, *cdp, ac1, ac1" 8525ac44 0x0 (seq (set ac1 (+ (var ac1) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set ac1 (+ (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (bv 8 0x10) false))) (set xar1 (+ (var xar1) (bv 23 0x1))) (set xar3 (- (var xar3) (bv 23 0x1)))) +d "firsadd *ar0, *ar0, *cdp+, ac0, ac0" 85000d00 0x0 (seq (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set ac0 (+ (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) (set xcdp (+ (var xcdp) (bv 23 0x1)))) +d "firsadd *ar0+, *ar0, *cdp, ac1, ac2" 85040c84 0x0 (seq (set ac2 (+ (var ac2) (* (cast 40 (msb (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 16 false (>> (var ac1) (bv 8 0x10) false))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set ac1 (+ (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) (set xar0 (+ (var xar0) (bv 23 0x1)))) d "mpym *ar0, *ar0, ac0" 86000000 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) +d "mpym uns(*ar0), uns(*ar0), ac0" 8600000c 0x0 (set ac0 (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) +d "mpym40 *ar0, *ar0, ac0" 86000010 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) +d "mpym *ar1+, *ar2, ac0" 86250000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))))) (set xar1 (+ (var xar1) (bv 23 0x1)))) +d "macm *ar0, *ar0, ac0" 86000020 0x0 (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm *ar0, *ar0, ac1, ac0" 86000420 0x0 (set ac0 (+ (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm uns(*ar1+), *ar3-, ac3, ac0" 8625ac28 0x0 (seq (set ac0 (+ (var ac3) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2))))))) (set xar1 (+ (var xar1) (bv 23 0x1))) (set xar3 (- (var xar3) (bv 23 0x1)))) +d "masm *ar0, *ar0, ac0" 86000060 0x0 (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "masm *ar0, *ar0, ac1, ac0" 86000460 0x0 (set ac0 (- (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm t3=*ar0, *ar0, ac0" 86000022 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))))) +d "macm *ar0, *ar0, ac0 >> #16" 86000040 0x0 (set ac0 (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm *ar0, *ar0, ac1 >> #16, ac0" 86000440 0x0 (set ac0 (+ (>> (var ac1) (bv 6 0x10) (msb (var ac1))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm *ar0, *ar0, ac0 >> #16, ac1" 86000140 0x0 (set ac1 (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "macm *ar1+, *ar2, ac0 >> #16" 86250040 0x0 (seq (set ac0 (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2))))))) (set xar1 (+ (var xar1) (bv 23 0x1)))) +d "macmr *ar0, *ar0, ac0 >> #16" 86000041 0x0 (set ac0 (& (+ (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) +d "macm40 *ar0, *ar0, ac0 >> #16" 86000050 0x0 (set ac0 (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) +d "sqdst *ar0, *ar0, ac0, ac0" 860000e0 0x0 (seq (set ac0 (+ (var ac0) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "sqdst *ar1+, *ar2, ac0, ac3" 86250ce0 0x0 (seq (set ac3 (+ (var ac3) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (bv 8 0x10) false))) (set xar1 (+ (var xar1) (bv 23 0x1)))) +d "sqdst *ar0, *ar0, ac0, ac1" 860004e0 0x0 (seq (set ac1 (+ (var ac1) (* (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "abdst *ar0, *ar0, ac0, ac0" 860000f0 0x0 (seq (set ac0 (+ (var ac0) (ite (sle (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (bv 40 0x0)) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "abdst *ar1+, *ar2, ac0, ac3" 86250cf0 0x0 (seq (set ac3 (+ (var ac3) (ite (sle (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (bv 40 0x0)) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (bv 8 0x10) false))) (set xar1 (+ (var xar1) (bv 23 0x1)))) +d "abdst *ar0, *ar0, ac0, ac1" 860004f0 0x0 (seq (set ac1 (+ (var ac1) (ite (sle (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (bv 40 0x0)) (- (bv 40 0x0) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))) (cast 40 (msb (cast 16 false (>> (var ac0) (bv 8 0x10) false))) (cast 16 false (>> (var ac0) (bv 8 0x10) false)))))) (set ac0 (- (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)))) +d "lms *ar1+, *ar2, ac0, ac3" 86250cc0 0x0 (seq (set lms_acx (var ac0)) (set ac3 (+ (var ac3) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2))))))) (set ac0 (& (+ (+ (var lms_acx) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (bv 8 0x10) false)) (bv 40 0x8000)) (bv 40 0xffffff0000))) (set xar1 (+ (var xar1) (bv 23 0x1)))) +d "lms *ar0, *ar0, ac0, ac1" 860004c0 0x0 (seq (set lms_acx (var ac0)) (set ac1 (+ (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) (set ac0 (& (+ (+ (var lms_acx) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)) (bv 40 0x8000)) (bv 40 0xffffff0000)))) +d "lms *ar0, *ar0, ac0, ac0" 860000c0 0x0 (seq (set lms_acx (var ac0)) (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))))) (set ac0 (& (+ (+ (var lms_acx) (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false)) (bv 40 0x8000)) (bv 40 0xffffff0000)))) +d "amar *ar0+, *ar0, *cdp+" 85040900 0x0 (seq (set xar0 (+ (var xar0) (bv 23 0x1))) (set xcdp (+ (var xcdp) (bv 23 0x1)))) +d "amar *(ar0 + t0), *ar0, *(cdp+t0)" 850c0b00 0x0 (seq (set xar0 (+ (var xar0) (cast 23 (msb (var t0)) (var t0)))) (set xcdp (+ (var xcdp) (cast 23 (msb (var t0)) (var t0))))) +d "amar *ar0, *(ar0 + t0), *cdp" 85003800 0x0 (set xar0 (+ (var xar0) (cast 23 (msb (var t0)) (var t0)))) +d "amar *(ar0 + t1), *ar0, *cdp" 85100800 0x0 (set xar0 (+ (var xar0) (cast 23 (msb (var t1)) (var t1)))) +d "mpy *(ar0 + t1), *cdp, ac0 :: mpy *ar0, *cdp, ac0" 82100000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xar0 (+ (var xar0) (cast 23 (msb (var t1)) (var t1)))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "mpy *ar0(t0), *cdp, ac0 :: mpy *ar0, *cdp, ac0" 821c0000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "mpy *ar0, *cdp, ac0 :: mpy *(ar0 - t0), *cdp, ac0" 82005000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xar0 (- (var xar0) (cast 23 (msb (var t0)) (var t0))))) +d "mpy *ar0, *cdp, ac0 :: mpy *(ar0 - t1), *cdp, ac0" 82006000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xar0 (- (var xar0) (cast 23 (msb (var t1)) (var t1))))) +d "mpy *ar0, *cdp, ac0 :: mpy *ar0(t0), *cdp, ac0" 82007000 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t0)) (var t0))) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "amar *ar0 :: mpy uns(*ar0), uns(*cdp), ac1" 82000c84 0x0 (seq nop (set ac1 (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) +d "amar *ar4 :: mac uns(*ar7-), uns(*cdp), ac1 >> #16" 8483a4c4 0x0 (seq nop (set ac1 (+ (>> (var ac1) (bv 6 0x10) (msb (var ac1))) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar7)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set xar7 (- (var xar7) (bv 23 0x1)))) +d "amar *ar0 :: mac uns(*(ar0 + t1)), uns(*cdp), ac1" 83004c84 0x0 (seq nop (set ac1 (+ (var ac1) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set xar0 (+ (var xar0) (cast 23 (msb (var t1)) (var t1))))) +d "mas *ar1+, *cdp, ac1 :: mas uns(*ar3-), uns(*cdp), ac0" 8525a444 0x0 (seq (set ac1 (- (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set xar1 (+ (var xar1) (bv 23 0x1))) (set ac0 (- (var ac0) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar3)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2))))))) (set xar3 (- (var xar3) (bv 23 0x1)))) +d "macr40 *ar0, *(cdp+t0), ac0 :: macr40 uns(*ar0), uns(*(cdp+t0)), ac0" 83000343 0x0 (seq (set ac0 (& (+ (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) (set xcdp (+ (var xcdp) (cast 23 (msb (var t0)) (var t0)))) (set ac0 (& (+ (+ (var ac0) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) (set xcdp (+ (var xcdp) (cast 23 (msb (var t0)) (var t0))))) +d "mpy40 *ar7, *cdp+, ac0 :: mpy40 *ar2, *cdp+, ac0" 82e10102 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar7)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar7)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xcdp (+ (var xcdp) (bv 23 0x1))) (set ac0 (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xcdp (+ (var xcdp) (bv 23 0x1)))) d "mpymr *ar0, *ar0, ac0" 86000001 0x0 (set ac0 (& (+ (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "mpymr40 *ar0, *ar0, ac0" 86000011 0x0 (set ac0 (& (+ (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "mpymr40 t3=*ar0, *ar0, ac0" 86000013 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac0 (& (+ (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (bv 40 0x8000)) (bv 40 0xffffff0000)))) @@ -325,19 +570,54 @@ d "macmr40 *ar0, *ar0, ac0, ac1" 86000131 0x0 (set ac1 (& (+ (+ (var ac0) (* (ca d "macmr40 *ar0, *ar0, ac0 >> #16" 86000051 0x0 (set ac0 (& (+ (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "macmr40 *ar0, *ar0, ac0 >> #16, ac1" 86000151 0x0 (set ac1 (& (+ (+ (>> (var ac0) (bv 6 0x10) (msb (var ac0))) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000))) d "masmr40 t3=uns(*ar0), uns(*ar0), ac0, ac1" 8600017f 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac1 (& (+ (- (var ac0) (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 false (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))))) (bv 40 0x8000)) (bv 40 0xffffff0000)))) -d "masm *ar0, t0, ac0 :: mov *ar0 << #16, ac0" 86000080 -d "macm *ar0, t0, ac0 :: mov *ar0 << #16, ac0" 860000a0 +d "masm *ar0, t0, ac0 :: mov *ar0 << #16, ac0" 86000080 0x0 (seq (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) (set ac0 (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "macm *ar0, t0, ac0 :: mov *ar0 << #16, ac0" 860000a0 0x0 (seq (set ac0 (+ (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) (set ac0 (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "masm t3=*ar1+, t2, ac0 :: mov *ar2 << #16, ac3" 86250c8a 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (set ac0 (- (var ac0) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 (msb (var t2)) (var t2))))) (set xar1 (+ (var xar1) (bv 23 0x1))) (set ac3 (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar2)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "masm t3=*ar0, t0, ac1 :: mov *ar0 << #16, ac0" 86000182 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac1 (- (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) (set ac0 (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) +d "macm t3=*ar0, t0, ac1 :: mov *ar0 << #16, ac1" 860005a2 0x0 (seq (set t3 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (set ac1 (+ (var ac1) (* (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (cast 40 (msb (var t0)) (var t0))))) (set ac1 (<< (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (bv 8 0x10) false))) d "mpym *ar0, t0, ac0 :: mov hi(ac0 << t2), *ar0" 87000000 d "macm *ar0, t0, ac0 :: mov hi(ac0 << t2), *ar0" 87000020 d "masm *ar0, t0, ac0 :: mov hi(ac0 << t2), *ar0" 87000040 -d "lms *ar0, *ar0, ac0, ac0" 860000c0 -d "sqdst *ar0, *ar0, ac0, ac0" 860000e0 -d "abdst *ar0, *ar0, ac0, ac0" 860000f0 d "lmsf *ar0, *ar0, ac0, ac0" 87000061 d "add *ar0 << #16, ac0, ac0 :: mov hi(ac0 << t2), *ar0" 87000080 d "sub *ar0 << #16, ac0, ac0 :: mov hi(ac0 << t2), *ar0" 870000a0 d "mov *ar0 << #16, ac0 :: mov hi(ac0 << t2), *ar0" 870000c0 d "mov ac0, ac0" 9000 0x0 (set ac0 (var ac0)) +d "mov ac0, ac1" 9001 0x0 (set ac1 (var ac0)) +d "mov xar0, ac0" 9080 0x0 (set ac0 (cast 40 false (var xar0))) +d "mov ac0, xar0" 9008 0x0 (set xar0 (cast 23 false (var ac0))) +d "mov xar4, xar4" 90cc 0x0 (set xar4 (var xar4)) +d "not ac0" 3600 0x0 (set ac0 (~ (var ac0))) +d "not ac0, ac1" 3601 0x0 (set ac1 (~ (var ac0))) +d "not ac0, t0" 3604 +d "|| not ac3" 3733 0x0 (set ac3 (~ (var ac3))) +d "neg ac0" 3400 0x0 (set ac0 (- (bv 40 0x0) (var ac0))) +d "abs ac0" 3200 0x0 (set ac0 (ite (&& (sle (var ac0) (bv 40 0x0)) (! (== (var ac0) (bv 40 0x0)))) (- (bv 40 0x0) (var ac0)) (var ac0))) +d "max ac0" 2e00 0x0 (set ac0 (ite (! (sle (var ac0) (var ac0))) (var ac0) (var ac0))) +d "min ac0, ac1" 3001 0x0 (set ac1 (ite (&& (sle (var ac0) (var ac1)) (! (== (var ac0) (var ac1)))) (var ac0) (var ac1))) +d "mov 0x44AA, dp" 7844aa00 0x0 (set dp (bv 16 0x44aa)) +d "mov 0x1234, bsac" 7812340e 0x0 (set bsac (bv 16 0x1234)) +d "mov 0x1234, sp" 78123410 0x0 (set sp (bv 16 0x1234)) +d "add 0x0044 << #16, ac0" 7a004400 0x0 (set ac0 (+ (var ac0) (<< (bv 40 0x44) (bv 8 0x10) false))) +d "sub 0x8000 << #16, ac1, ac0" 7a800042 0x0 (set ac0 (- (var ac1) (<< (bv 40 0xffffff8000) (bv 8 0x10) false))) +d "and 0x8000 << #16, ac0" 7a800004 0x0 (set ac0 (& (var ac0) (<< (bv 40 0x8000) (bv 8 0x10) false))) +d "xor 0xFFFF << #16, ac0, ac1" 7affff18 0x0 (set ac1 (^ (var ac0) (<< (bv 40 0xffff) (bv 8 0x10) false))) +d "mov 0x8000 << #16, ac2" 7a80002a 0x0 (set ac2 (bv 40 0xff80000000)) +d "add 0x1234 << 0x8, ac0" 70123408 0x0 (set ac0 (+ (var ac0) (<< (bv 40 0x1234) (bv 8 0x8) false))) +d "sub 0x8000 << 0xF, ac1, ac0" 7180004f 0x0 (set ac0 (- (var ac1) (<< (bv 40 0xffffff8000) (bv 8 0xf) false))) +d "and 0x8000 << 0x4, ac0" 72800004 0x0 (set ac0 (& (var ac0) (<< (bv 40 0x8000) (bv 8 0x4) false))) +d "or 0x1234 << 0x4, ac0" 73123404 0x0 (set ac0 (| (var ac0) (<< (bv 40 0x1234) (bv 8 0x4) false))) +d "mov 0x1234 << 0xA, ac2" 7512342a +d "add ac0" 2400 0x0 (set ac0 (+ (var ac0) (var ac0))) +d "sub ac3" 2633 0x0 (set ac3 (- (var ac3) (var ac3))) +d "add t0" 2444 0x0 (set t0 (+ (var t0) (var t0))) +d "mov -0x4, t0" 3e44 +d "mov -0xF, ar7" 3eff +d "mov -0x0, ac0" 3e00 +d "mov 0x44A, brc1" 1644aa 0x0 (set brc1 (bv 16 0x44a)) +d "mov 0xFF0, bk03" 16ff04 0x0 (set bk03 (bv 16 0xff0)) +d "mov 0x000, csr" 160008 0x0 (set csr (bv 16 0x0)) +d "xor 0x1234 << 0x0, ac1, ac2" 74123460 0x0 (set ac2 (^ (var ac1) (<< (bv 40 0x1234) (bv 8 0x0) false))) d "b ac0" 9100 0x0 (jmp (cast 24 false (var ac0))) d "call ac0" 9200 0x0 (jmp (cast 24 false (var ac0))) d "reset" 9400 @@ -351,19 +631,169 @@ d "xcc ac0 == 0" 9f00 0x0 nop d "xccpart ac0 == 0" 9f80 0x0 nop# --- coverage expansion: additional instruction-class disassembly tests --- d "addsub t3, dual(*sp(#57h)), ac2" eeaeed d "addsub2cc *ar4+, ac3, t2, TC1, TC2, ac0" dd83ca +d "addsub2cc *sp(#0h), ac3, t2, TC1, TC2, ac1" dd00da +d "mov *sp(#0h) << t0, ac0" dd0003 +d "mov rnd(*sp(#0h) << t0), ac0" dd0043 +d "mov *sp(#9h) << t1, ac1" dd1217 d "addsubcc *-ar7, ac0, TC2, ac3" defb31 +d "addsubcc *sp(#0h), ac1, TC1, ac0" de0040 +d "addsubcc *sp(#0h), ac3, TC1, ac0" de00c0 +d "addsubcc *sp(#0h), ac0, TC2, ac0" de0001 +d "addsubcc *sp(#0h), ac0, TC1, TC2, ac0" de0002 d "band *cdp+, 0x1E70, TC1" f2911e70 d "bnot ar3, *sp(#3Eh)" e37cbe +d "bset ac0, *sp(#0h)" e3000c +d "bclr t0, *sp(#0h)" e3004d +d "bnot ar0, *sp(#0h)" e3008e d "btst 0x3, *ar0, TC2" dc013d +d "btst 0x0, *sp(#0h), TC1" dc0000 +d "btst 0xF, *sp(#0h), TC1" dc00f0 +d "btst 0x0, *sp(#8h), TC1" dc1000 +d "btst 0x4, *sp(#0h), TC2" dc0041 +d "btst ac0, *ar0, tc1" e00100 +d "btst ac0, *ar0, tc2" e00101 +d "btst t0, *ar0, tc1" e00140 +d "btst ar7, *ar0, tc1" e001f0 +d "btst ac0, *ar0+, tc1" e00300 +d "band *ar0, 0x0000, TC1" f2010000 +d "band *ar0, 0x0000, TC2" f3010000 +d "band *ar0, 0xABCD, TC1" f201abcd +d "band *ar2, 0x1234, TC1" f2411234 +d "mov *sp(#0h), dp" dc0002 0x0 (set dp (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) +d "mov *sp(#0h), cdp" dc0012 0x0 (set cdp (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) +d "mov *sp(#0h), dph" dc00c2 0x0 (set dph (cast 7 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov *sp(#0h), pdp" dc00f2 0x0 (set pdp (cast 9 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov *sp(#0h), csr" dc0003 0x0 (set csr (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) +d "mov *sp(#0h), trn1" dc0043 0x0 (set trn1 (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) d "btstclr 0x8, *sp(#54h), TC1" e3a885 d "btstnot 0xB, *ar2(t1), TC1" e357b8 d "btstp Baddr, ac0" ece304 d "btstset 0x5, *sp(#55h), TC1" e3aa51 +d "btstset 0x0, *sp(#0h), TC1" e30000 +d "btstset 0x0, *sp(#0h), TC2" e30002 +d "btstset 0xF, *sp(#0h), TC1" e300f0 +d "btstclr 0x0, *sp(#0h), TC2" e30006 +d "btstnot 0x0, *sp(#0h), TC2" e3000a d "delay *(ar0 - t1)" b615 +d "delay *ar0" b601 +d "delay *ar0+" b603 +d "delay *+ar0" b619 +d "mov *(ar0 + t0b), ac0" a01d +d "mov *(ar0 - t0b), ac0" a01f +d "mov *(ar4 + t0b), ac0" a09d +d "mov ac0, *(ar0 + t0b)" c01d +d "mov ac0, *(ar0 - t0b)" c01f +d "delay *(ar0 + t0b)" b61d +d "amar *(ar0 + t0b)" b41d +d "amar *(ar0 - t0b)" b41f d "macmk *(ar2 + t0b), 0xB1, ac0, ac3" f85db134 d "mpymk t3=*sp(#2h), 0x1C, ac2" f8041cea d "sqamr *-ar2, ac2" d25b6a -d "sqrm *sp(#64h), ac2" d3c828 -d "sqsm *sp(#55h), ac3" d2aa3f +d "sqrm *sp(#64h), ac2" d3c828 0x0 (set ac2 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x64)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x64)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x64)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x64)) (bv 24 0x2)))))) +d "sqsm *sp(#55h), ac3" d2aa3f 0x0 (set ac3 (- (var ac3) (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x55)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x55)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x55)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x55)) (bv 24 0x2))))))) d "subadd t0, dual(*(ar5 - t0b)), ac2" eebf2f d "subc *ar4, ac3" de81f3 +d "subc *sp(#0h), ac1, ac0" de0043 +d "subc *sp(#0h), ac0, ac1" de0013 +d "subc *sp(#0h), ac1" de0053 +d "add *sp(#0h) << #16, ac0" de0004 +d "add *sp(#0h) << #16, ac1, ac0" de0044 +d "sub *sp(#0h) << #16, ac0" de0005 +d "sub ac0, *sp(#0h) << #16, ac0" de0006 +d "sub ac1, *sp(#0h) << #16, ac0" de0046 +d "addsub t0, *sp(#0h), ac0" de0008 +d "addsub t1, *sp(#0h), ac1" de0058 +d "subadd t0, *sp(#0h), ac0" de0009 +d "subadd t3, *sp(#0h), ac0" de00c9 +d "add *sp(#0h), ac0" df000c +d "add uns(*sp(#0h)), ac0" df000d +d "add *sp(#0h), ac1, ac0" df004c +d "sub *sp(#0h), ac0" df000e +d "sub uns(*sp(#0h)), ac3, ac0" df00cf +d "mov *sp(#0h), ac0" df0004 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov uns(*sp(#0h)), ac0" df0005 0x0 (set ac0 (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov *sp(#0h), ac1" df0014 0x0 (set ac1 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov *ar0, ac0" df0104 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) +d "mov high_byte(*sp(#0h)), ac0" df0000 +d "mov uns(high_byte(*sp(#0h))), ac0" df0001 +d "mov low_byte(*sp(#0h)), ac0" df0002 +d "mov low_byte(*sp(#0h)), ac3" df0032 +d "mov high_byte(*ar0), ac0" df0100 +d "add *sp(#0h), CARRY, ac0" df0008 +d "add uns(*sp(#0h)), CARRY, ac0" df0009 +d "add *sp(#0h), CARRY, ac1, ac0" df0048 +d "sub *sp(#0h), BORROW, ac0" df000a +d "sub *sp(#0h), BORROW, ac1, ac0" df004a +d "mov *ar0, ac0" a001 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) +d "mov *ar0+, ac0" a003 0x0 (seq (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (set xar0 (+ (var xar0) (bv 23 0x1)))) +d "mov *(ar0 + t0), ac0" a007 0x0 (seq (set ac0 (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))))) (set xar0 (+ (var xar0) (cast 23 (msb (var t0)) (var t0))))) +d "mov *ar0(t1), ac0" a017 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t1)) (var t1))) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (cast 24 (msb (var t1)) (var t1))) (bv 24 0x2))))) +d "mov *ar0, t0" a401 0x0 (set t0 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) +d "mov *ar0, ar0" a801 0x0 (set ar0 (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2)))) +d "mov *sp(#0h), ac0" a000 0x0 (set ac0 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2))))) +d "mov *sp(#10h), ac1" a120 0x0 (set ac1 (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x10)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x10)) (bv 24 0x2))))) +d "mov ac0, *ar0" c001 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 16 false (var ac0))) +d "mov ac0, *sp(#0h)" c000 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)) (cast 16 false (var ac0))) +d "mov t0, *ar0" c401 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (var t0)) +d "mov ar0, *ar0" c801 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (var ar0)) +d "mov hi(ac0), *ar0" bc01 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) +d "mov hi(ac0), *sp(#0h)" bc00 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)) (cast 16 false (>> (var ac0) (bv 8 0x10) false))) +d "mov 0x12, *sp(#0h)" e60012 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)) (bv 16 0x12)) +d "mov 0xAB, *sp(#4h)" e608ab 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x4)) (bv 24 0x2)) (bv 16 0xab)) +d "mov 0x1234, *sp(#0h)" fb001234 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)) (bv 16 0x1234)) +d "mov *sp(#0h) << #16, ac0" b000 +d "mov *ar0 << #16, ac0" b001 +d "amar *sp(#0h)" b400 0x0 nop +d "amar *ar0+" b403 0x0 (set xar0 (+ (var xar0) (bv 23 0x1))) +d "psh *sp(#0h)" b500 +d "pop *ar0" bb01 +d "psh dbl(*sp(#0h))" b700 +d "pop dbl(*ar0)" b801 + +d "mpymu *sp(#0h), t0, ac0" d3000c 0x0 (set ac0 (* (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (var t0)))) +d "mpymu *ar1-, t0, ac0" d3250c 0x0 (seq (set ac0 (* (cast 40 false (loadw 0 16 (* (cast 24 false (var xar1)) (bv 24 0x2)))) (cast 40 false (var t0)))) (set xar1 (- (var xar1) (bv 23 0x1)))) +d "mpymu *sp(#0h), t0, ac1" d3001c 0x0 (set ac1 (* (cast 40 false (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (cast 40 false (var t0)))) +d "add *sp(#0h), ac0" d60000 +d "add *sp(#0h), ac0, ac1" d60010 +d "add *sp(#0h), ac0, t0" d60040 +d "add *ar0, ac1, ac0" d60101 +d "add *sp(#8h), ac0" d61000 +d "sub *sp(#0h), ac0" d70000 +d "sub *sp(#0h), ac0, ac1" d70010 +d "sub *sp(#0h), ac1, ac0" d70001 +d "and *sp(#0h), ac0, ac0" d90000 0x0 (set ac0 (& (var ac0) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))))) +d "and *sp(#0h), ac1, ac0" d90001 0x0 (set ac0 (& (var ac1) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))))) +d "and *sp(#0h), ac0, t0" d90040 +d "or *sp(#0h), ac0, ac0" da0000 0x0 (set ac0 (| (var ac0) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))))) +d "or *sp(#0h), ac2, ac0" da0002 0x0 (set ac0 (| (var ac2) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))))) +d "xor *sp(#0h), ac1, ac0" db0001 0x0 (set ac0 (^ (var ac1) (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x0)) (bv 24 0x2)))))) +d "and *sp(#0h), ar0, ac0" d90008 +d "xor *sp(#0h), ac0, t0" db0040 +d "sub ac0, *sp(#0h), ac0" d80000 +d "sub ac0, *sp(#0h), ac1" d80010 +d "sub ac1, *sp(#0h), ac0" d80001 +d "sub ac0, *sp(#0h), t0" d80040 +d "sub ac0, *ar0, ac0" d80100 +d "sub ar0, *sp(#0h), ac0" d80008 +d "add *sp(#0h) << t0, ac0" dd0000 +d "add *sp(#0h) << t0, ac0, ac1" dd0010 +d "sub *sp(#0h) << t0, ac0" dd0001 +d "add *sp(#0h) << t0, ac1, ac0" dd0040 +d "add *sp(#0h) << t2, ac0" dd0008 +d "add *ar0 << t0, ac0" dd0100 +d "add *ar0+ << t0, ac0" dd0300 +d "sub *sp(#0h) << t0, ac0, ac1" dd0011 +d "mpym *ar0(0x3412), *cdp, ac0" d10d003412 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (bv 24 0x3412)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (bv 24 0x3412)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) +d "mpym *+ar0(0x3412), *cdp, ac0" d10f003412 0x0 (seq (set ac0 (* (cast 40 (msb (loadw 0 16 (* (+ (cast 24 false (var xar0)) (bv 24 0x3412)) (bv 24 0x2)))) (loadw 0 16 (* (+ (cast 24 false (var xar0)) (bv 24 0x3412)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) (set xar0 (+ (var xar0) (bv 23 0x3412)))) +d "mpym abs16(0x3412), *cdp, ac0" d111003412 0x0 (set ac0 (* (cast 40 (msb (loadw 0 16 (* (| (<< (cast 24 false (var dph)) (bv 8 0x10) false) (bv 24 0x3412)) (bv 24 0x2)))) (loadw 0 16 (* (| (<< (cast 24 false (var dph)) (bv 8 0x10) false) (bv 24 0x3412)) (bv 24 0x2)))) (cast 40 (msb (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))) (loadw 0 16 (* (cast 24 false (var xcdp)) (bv 24 0x2)))))) +d "and 0xF91F, *sp(#3h)" f406f91f 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x3)) (bv 24 0x2)) (& (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x3)) (bv 24 0x2))) (bv 16 0xf91f))) +d "and 0xFA00, *sp(#4Bh)" f496fa00 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x4b)) (bv 24 0x2)) (& (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x4b)) (bv 24 0x2))) (bv 16 0xfa00))) +d "or 0x4100, *sp(#3h)" f5064100 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x3)) (bv 24 0x2)) (| (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x3)) (bv 24 0x2))) (bv 16 0x4100))) +d "or 0x8000, *sp(#4Bh)" f5968000 0x0 (storew 0 (* (+ (cast 24 false (var sp)) (bv 24 0x4b)) (bv 24 0x2)) (| (loadw 0 16 (* (+ (cast 24 false (var sp)) (bv 24 0x4b)) (bv 24 0x2))) (bv 16 0x8000))) +d "and 0x1234, *ar0" f4011234 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (& (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))) (bv 16 0x1234))) +d "or 0x2345, *ar0" f5012345 0x0 (storew 0 (* (cast 24 false (var xar0)) (bv 24 0x2)) (| (loadw 0 16 (* (cast 24 false (var xar0)) (bv 24 0x2))) (bv 16 0x2345))) +d "amar *ar3, xar0" ec618e +d "amar *ar6+, xar0" ecc38e +d "amar *ar3, xar1" ec619e +d "amar *ar3, t0" ec614e +d "amar *sp(#20h), xar0" ec408e 0x0 (set xar0 (cast 23 false (+ (cast 24 false (var sp)) (bv 24 0x20)))) diff --git a/test/db/rzil/tms320 b/test/db/rzil/tms320 index 60e675d8ae..e092d6e1e9 100644 --- a/test/db/rzil/tms320 +++ b/test/db/rzil/tms320 @@ -1,5 +1,5 @@ -NAME=C55x+ RzIL VM: mov-imm, 16-bit add, bitwise xor, xar move execute -FILE=malloc://64 +NAME=C55x/C55x+ RzIL VM: register, immediate, memory and multiply execute +FILE=malloc://0x100 CMDS=<reg, and-half execute -FILE=malloc://64 -CMDS=<