Add new TMS320C2x (legacy) and TMS320C5x (legacy) series support - #6582
Conversation
40bd096 to
4cca7b6
Compare
4cca7b6 to
8c44ed0
Compare
393a716 to
b178bb5
Compare
b178bb5 to
671f403
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends Rizin’s TMS320 support to include legacy TMS320C2x and TMS320C5x cores, including disassembly, analysis integration, and RzIL uplifting, and updates COFF handling to correctly scale word-addressed TI DSP addresses into Rizin’s byte-based address space.
Changes:
- Add new C2x/C5x ISA implementations (decoder, analysis op filling, and RzIL lift) and wire them into the existing
tms320asm/analysis plugins. - Fix TI COFF loader address/size scaling for word-addressed targets (and add original TI COFF magic/target-id handling for C1x/C2x/C5x).
- Add/adjust regression tests for plugin listings, analysis behavior, disassembly, and RzIL VM execution for C2x/C5x (and updated C54x expectations).
Reviewed changes
Copilot reviewed 30 out of 34 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/db/tools/rz_asm | Updates expected rz-asm CPU list to include c2x/c5x. |
| test/db/rzil/tms320_legacy | Adds RzIL VM execution tests for legacy C2x/C5x binaries. |
| test/db/rzil/tms320 | Updates C54x RzIL test and adds small C2x/C5x RzIL VM smoke tests. |
| test/db/cmd/cmd_list | Updates plugin listing expectations for tms320 bits/cpu list. |
| test/db/cmd/cmd_aL | Updates aL expectations for tms320 bits/cpu list and adds CPU descriptions. |
| test/db/asm/tms320_c54x_32 | Updates expected lifted targets for word-addressed jump scaling. |
| test/db/asm/tms320_c2x_16 | Adds C2x disassembly + IL golden tests. |
| test/db/analysis/tms320.typezoo_16 | Adds C2x/C5x analysis tests for function recovery and calling conventions. |
| test/db/analysis/tms320.c5x_16 | Adds C5x analysis tests (regs, op classification, RzIL lift, recovery). |
| test/db/analysis/tms320.c54x_32 | Updates C54x analysis expectations to match new address scaling. |
| test/db/analysis/tms320.c2x_16 | Adds C2x analysis tests (regs, op classification, COFF open, recovery). |
| librz/bin/p/bin_coff.c | Scales TI DSP symbols/sections/maps by address unit (word->byte). |
| librz/bin/format/coff/coff.h | Exposes rz_coff_addr_scale() in the COFF API. |
| librz/bin/format/coff/coff.c | Implements address-unit scaling and recognizes original TI COFF magic-as-target-id. |
| librz/bin/format/coff/coff_specs.h | Adds TI target-id constant for C1x/C2x/C5x. |
| librz/arch/types/meson.build | Installs the new cc-tms320-16 calling convention DB. |
| librz/arch/types/cc-tms320-16.sdb.txt | Adds C2x/C5x calling convention definitions for 16-bit TMS320. |
| librz/arch/p/asm/asm_tms320.c | Wires c2x/c5x into the asm plugin; adds 16-bit support; uses C5x decoder. |
| librz/arch/p/analysis/analysis_tms320.c | Wires c2x/c5x into analysis plugin; adds reg profiles + IL configs; adds 16-bit support. |
| librz/arch/meson.build | Adds new C2x/C5x ISA sources to the build. |
| librz/arch/isa/tms320/c5x/c5x.h | Introduces C5x ISA interface (ids, decoder, lift, analysis entry). |
| librz/arch/isa/tms320/c5x/c5x.c | Implements C5x arch descriptor, analysis op entry, and IL VM reg bindings. |
| librz/arch/isa/tms320/c5x/c5x_il.c | Implements C5x-only mnemonic/op-type/lift pieces on top of shared C2x semantics. |
| librz/arch/isa/tms320/c5x/c5x_decode.c | Adds the C5x “real encoding” decoder producing shared C55Insn. |
| librz/arch/isa/tms320/c55_ir.h | Adds C55_ARCH_C2X/C55_ARCH_C5X to the shared IR arch enum. |
| librz/arch/isa/tms320/c55_ir.c | Adds word-address scaling for absolute targets and disables parallel-bit decoding for C2x/C5x; tweaks formatting for C2x/C5x. |
| librz/arch/isa/tms320/c54x/c54x_il.c | Scales C54x branch targets in IL to byte address space. |
| librz/arch/isa/tms320/c2x/c2x.h | Introduces C2x ISA interface (ids, decoder hooks, lift, analysis entry). |
| librz/arch/isa/tms320/c2x/c2x.c | Implements C2x arch descriptor, opcode table rows, operand extractors, and analysis op filling. |
| librz/arch/isa/tms320/c2x/c2x_rowundefs.h | Provides macro cleanup for C2x/C5x decode row definitions. |
| librz/arch/isa/tms320/c2x/c2x_rowdefs.h | Adds shared row macros used to build the C2x decode table. |
| librz/arch/isa/tms320/c2x/c2x_core_rows.inc | Adds the C2x core instruction decode table rows. |
Suppressed comments (1)
librz/arch/isa/tms320/c5x/c5x_il.c:116
- SPLK is lifted for any memory addressing mode, but c5x_mem_write()/c5x_addr() do not model indirect addressing post-modify or next-ARP updates. For non-direct forms this yields incorrect IL state. Consider restricting the lift to direct operands until indirect semantics are implemented.
case C5X_INS_SPLK:
// store a long immediate constant to the memory operand
return c5x_mem_write(&insn->ops[0], UN(16, (ut32)insn->ops[insn->n_ops - 1].imm & 0xffff));
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
wargio
left a comment
There was a problem hiding this comment.
remove the @ 0 from the malloc:// tests
The C54x and C28x address 16-bit words, so their loadable section sizes and every address in the file count words. Rizin's address space is byte-based, so reading those unscaled mapped only half of each loadable section and placed every symbol at half its true offset: _main in the c54x emulateme object landed mid-instruction instead of on its prologue. Scale section VAs, loadable section sizes and symbol addresses by the target's address unit. Debug sections are byte streams even on these targets and keep a scale of one, matching the loadable mask already used for mapping. The C55x addresses program memory by byte and is left alone; the ids were checked against the objects in rizin-testbins by comparing each section's declared size against its extent in the file. Branch and call targets on the C54x count program words too, and reached analysis and RzIL unscaled. That went unnoticed while the sections were half-mapped: the emulateme RzIL test ran from a mid-instruction address whose decode happened to lift, so it pinned values produced by garbage. Scale those as well and drive the test the way its C55x sibling does, decrypting seckrit with the real key. With the sections fully mapped, analysis now finds every routine the c54x fixtures declare, so the function counts change.
The legacy TMS320 fixed-point families ship as 16-bit big-endian COFF objects whose magic and machine identifiers were not recognised by the COFF loader. Teach the COFF format backend and the bin plugin to detect and load them so the new tms320 c2x/c5x analysis can operate on real object files.
Add support for the legacy TMS320C2x (C25-class) fixed-point DSP family to the shared C55 decode-IR engine: the C2x opcode table, operand extractors, mnemonic and op-type tables, and the register profile, wired into the tms320 asm and analysis plugins under cpu "c2x". The decoder fills the shared C55 instruction representation so the common formatter and analysis filler apply unchanged. Includes disassembly and opcode classification tests, plus a COFF-loading test exercising the new bin autodetect.
Add the real TMS320C5x (C50/C51/C53) object encoding. The C5x is source-compatible with the C2x but encodes instructions differently, so it cannot reuse the C2x decode table: a dedicated C5x decode front-end fills the shared C55 instruction representation, carrying the C2x ids for shared-semantics instructions (so the common consumers apply unchanged) and new C5x-only ids for the C5x additions (ACCB ops, parallel-logic, memory-mapped register access, conditional execute/call/return, block moves, ...). Wired into the tms320 asm and analysis plugins under cpu "c5x", with the C5x mnemonic and op-type tables and register profile. Includes disassembly and opcode classification tests.
671f403 to
b86df72
Compare
| return c2x_seq_post(SEQ2(SETL("m", c2x_mem_read(&insn->ops[0])), SEQ4(SETG("arp", LOGAND(SHIFTR0(VARL("m"), UN(4, 13)), UN(16, 7))), SETG("ov", LSB(SHIFTR0(VARL("m"), UN(4, 12)))), SETG("ovm", LSB(SHIFTR0(VARL("m"), UN(4, 11)))), SETG("dp", LOGAND(VARL("m"), UN(16, 0x1ff))))), c2x_ar_post(insn, 0)); | ||
| case C2X_INS_LST1: | ||
| return c2x_seq_post(SEQ2(SETL("m", c2x_mem_read(&insn->ops[0])), SEQ6(SETG("tc", LSB(SHIFTR0(VARL("m"), UN(4, 11)))), SETG("sxm", LSB(SHIFTR0(VARL("m"), UN(4, 10)))), SETG("c", LSB(SHIFTR0(VARL("m"), UN(4, 9)))), SETG("pm", CAST(2, IL_FALSE, VARL("m"))), SETG("arp", LOGAND(SHIFTR0(VARL("m"), UN(4, 13)), UN(16, 7))), SETG("arb", LOGAND(SHIFTR0(VARL("m"), UN(4, 13)), UN(16, 7))))), c2x_ar_post(insn, 0)); | ||
| case C2X_INS_SST: { |
There was a problem hiding this comment.
just a comment: we need to find a better way for keeping the formatting. this is unreadable
Lift the TMS320C2x instruction set to RzIL: the 32-bit accumulator and product paths, the auxiliary-register file with all indirect addressing modes, the status bits (carry, overflow, OVM saturation, SXM sign extension, product-mode shifts, TC), and the load/store, multiply, accumulate, shift, logical and control-flow instructions. Wired into the analysis plugin's il_config under cpu "c2x". The asm tests now assert the lifted RzIL for every instruction, with execution tests covering the IL VM end to end.
Lift the TMS320C5x instruction set to RzIL. Shared-semantics instructions reuse the C2x lifter (the C5x-only forms that are a renamed C2x op, such as the delayed branches, map onto their non-delayed equivalents); the C5x-only core instructions (the ACCB accumulator-buffer loads and logical ops, the swap, the zero-accumulator forms, the control bits and the store-long-immediate) are lifted directly. Instructions with effects the per-instruction RzIL cannot model cleanly (conditional control transfers, block moves, parallel-logic, memory-mapped register access, the MAC fetch forms) are decoded and analysed but left without IL. Wired into the analysis plugin's il_config under cpu "c5x", with the C5x register bindings (ACCB and the C5x-specific registers). The asm tests assert the lifted RzIL, with execution tests covering the IL VM.
BSAR (Barrel Shift ACC Right) was grouped with the left-shift ops and reported RZ_ANALYSIS_OP_TYPE_SHL. It shifts the accumulator right (1..16 bits, sign-extended under SXM), so it belongs with the SHR group next to RORB/SFRB. The decode (shift code + 1) was already correct and continues to match the reference disassembler bit-for-bit.
b86df72 to
36154e7
Compare
DO NOT SQUASH
Your checklist for this pull request
RZ_APIfunction and struct this PR changes.RZ_API).Detailed description
Legacy TMS320C2x (pre-C28x) and TMS320C5x (pre-C54x) were quite common in many places, and they share some of the infrastructure with already implemented TMS320C54x/C55x in Rizin.
Add both disassembly and RzIL uplifting.
Because no public toolchain available, I vibe-coded and verified against some other projects and pairs of source-binary the small toolchain for C2x and C5x in Rust - assembler, disassembler, C compiler, linker, and emulator: https://codeberg.org/xvilka/tms320-rs
Requires this PR for testbins: rizinorg/rizin-testbins#302Test plan
CI is green