Independent review of P3 batch 2 (52ece4b) found that all three VMX128 register accessors disagreed with canary's FormatVX128/VX128_R bitfield struct (`xenia-canary/src/xenia/cpu/ppc/ppc_decode_data.h:484-663`). The audit at line 2958 had marked these "confirmed-clean" but had miscounted LSB-first bitfield offsets. Canary's actual layout (LSB-first, GCC/Clang/MSVC on x86): VA128 = VA128l(5) | VA128h(1)<<5 | VA128H(1)<<6 = PPC[11:15] | PPC[26]<<5 | PPC[21]<<6 (7-bit selector, 3 fields) VB128 = VB128l(5) | VB128h(2)<<5 = PPC[16:20] | PPC[30:31]<<5 (7-bit selector, 2 fields) VD128 = VD128l(5) | VD128h(2)<<5 = PPC[6:10] | PPC[28:29]<<5 (7-bit selector, 2 fields) VX128_R Rc = PPC[25] (host bit 6) not PPC[27] as prior fix had The buggy convention was internally consistent with hand-crafted test fixtures (which set bits 29/21/22 to encode the high registers, matching the buggy accessor). Real Xbox 360 game code follows canary's convention, so any production VMX128 instruction with VR >= 32 was silently mis-decoded — but no unit test exercised that path until the va128 fix in52ece4bexposed the inconsistency. Changes: - decoder.rs: rewrite va128/vb128/vd128/vx128r_rc_bit to canary positions. Drop the speculative `key4_dt` dot-form dispatch in decode_op6 — canary has no separate dot-form opcodes for VX128_R compute ops; Rc is a runtime modifier read by the interpreter via vx128r_rc_bit(). - decoder.rs tests: rewrite vmx128_test_word helper for canary layout; rename/re-encode vmx128_vd128_*, vmx128_va128_*, vmx128_vb128_* tests. - interpreter.rs: update encode_vpkd3d128 test helper to encode VD via canary's VD128h field; tests now pass vd=96 explicitly. - tests/disasm_goldens.rs: replace the vrlimi128/vsrw128/vpermwi128/ vperm128 hand-encoded raws with canary-compliant encodings; introduce a shared `encode_vx128` helper. - tests/golden/vmx128_registers.json: re-encode 9 entries (vperm128, vsrw128 ×2, vpermwi128, vrlimi128 ×2, vmaddfp128, vmaddcfp128, vnmsubfp128) to canary-compliant raws preserving the same expected operand strings. - audit-findings.md: new PPCBUG-700 entry documenting the discovery and invalidating the audit's "confirmed-clean" assessment. Affects all VMX128 binary ops (vaddfp128, vsubfp128, vmulfp128, vand128, vor128, vxor128, vnor128, vandc128, vsel128, vslo128, vsro128, vperm128, vsrw128, vmaddfp128, vmaddcfp128, vnmsubfp128, vpkd3d128, vpkshss128, vpkshus128, vpkswss128, vpkswus128, vpkuhum128, vpkuhus128, vpkuwum128, vpkuwus128, vmsum3fp128, vmsum4fp128, vrlimi128, vpermwi128 — 30+ opcodes), plus VX128_R compare dot-forms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
xenia-rs
Rust reimplementation of the Xbox 360 emulator xenia, focused on reverse-engineering and preservation rather than full-speed play. The initial target is Project Sylpheed — Arc of Deception; getting the title disassembled, traced, and far enough into its init path to understand its engine.
Heavy cross-reference to xenia-canary for CPU context setup, kernel export behavior, and XEX loading semantics.
Status
- XEX loader — XEX2 header parsing, LZX decompression, AES decryption, PE section parsing.
- VFS / XISO — XGD2 dual-layer disc images (with the 0x0FD90000 partition offset).
- PPC interpreter — 200+ opcodes, PowerPC 32/64-bit GPR/FPR, VMX128 decoding.
- Static analyzer — function discovery (prolog/epilog heuristics), cross-references, labels, save/restore helper detection, assembly text + SQLite database output.
- Kernel HLE — minimal subset driving Project Sylpheed: ~170 xboxkrnl + xam exports (critical sections, events, TLS, virtual memory, Vd stubs, XAM input/user/content).
- Debugger — in-memory step/break, SQLite execution + import-call + branch tracing.
Not yet: GPU (xenos/xe-shader), APU audio, HID, kernel scheduler, full threading, exception delivery.
Workspace
crates/
xenia-types # shared primitive types, bitflags
xenia-memory # guest memory, paged allocator, page table
xenia-cpu # PPC decoder, interpreter, context
xenia-xex # XEX2 loader, PE parser, LZX, AES
xenia-vfs # XISO / disc-image reader
xenia-kernel # HLE kernel state, exports, XAM
xenia-gpu # (stub) Xenos command processor
xenia-apu # (stub) XAudio
xenia-hid # (stub) XInput
xenia-debugger # in-memory trace, breakpoints, step modes
xenia-analysis # function/xref analysis, assembly formatter, SQLite DbWriter
xenia-app # `xenia-rs` CLI binary
CLI
Build:
cargo build --release
The binary xenia-rs accepts XEX2 files or ISO / XISO disc images as input
(the loader auto-detects discs and extracts default.xex).
info / browse / disasm
Quick header / disc / first-N-instructions inspection. See --help.
extract — unpack PE + metadata
xenia-rs extract <xex-or-iso> [-o <out-dir>] [--db <sqlite-path>]
Writes <name>.pe (decompressed/decrypted PE image) and <name>.xex.json
(header metadata). With --db, also emits a SQLite database containing the
base tables: metadata, sections, imports.
dis — full disassembly
xenia-rs dis <xex-or-iso> [-o <asm-file>] [--db <sqlite-path>] [--quiet]
Runs function + cross-reference analysis and produces:
- assembly text to stdout or
-o <file>(unless--quiet) - optional SQLite DB with the base tables + disasm tables:
functions,labels,instructions,xrefs
exec — interpret with tracing
xenia-rs exec <xex-or-iso> [-n <max-instrs>] [--db <sqlite-path>]
[--trace-instructions] [--trace-imports] [--trace-branches]
Loads the title, initializes CPU state per xenia-canary, intercepts import
thunks with HLE kernel calls, and interprets from the entry point. Without
-n, runs until halt/fault. With --db, produces a DB that is a superset
of dis --db plus opt-in trace tables:
| flag | table | rows |
|---|---|---|
--trace-instructions |
exec_trace |
one row per interpreted instruction (PC, r3/r4, LR, SP) |
--trace-imports |
import_calls |
one row per kernel/XAM call (module, ordinal, args) |
--trace-branches |
branch_trace |
taken branches classified as call/return/jump/branch |
Cumulative DB layering
Each command's DB is a superset of the previous. A single
xenia-rs exec <iso> --db full.db --trace-instructions --trace-imports --trace-branches
produces the full picture in one pass — base tables, complete static
disassembly, and runtime traces correlatable by address/cycle.
Performance knobs
XENIA_DB_BATCH_SIZE— rows per streaming commit / trace-buffer flush (default100_000). Lower values reduce memory use; higher values reduce fsync overhead on slow disks.
The DB writer uses journal_mode=OFF, synchronous=OFF, locking_mode=EXCLUSIVE
and commits in batches; no ANALYZE is run at finalize. Indices are created
after bulk insertion with progress messages.
Example queries
-- Top 20 kernel functions called during early init
SELECT name, COUNT(*) FROM import_calls GROUP BY name ORDER BY 2 DESC LIMIT 20;
-- All basic-block leaders (targets of taken branches) not already labelled
SELECT DISTINCT bt.target
FROM branch_trace bt LEFT JOIN labels l ON l.address = bt.target
WHERE l.address IS NULL;
-- Correlate a traced call site with its static disassembly
SELECT et.cycle, i.disasm, i.ext_disasm
FROM exec_trace et JOIN instructions i ON i.address = et.address
WHERE et.address = 0x824AB748 ORDER BY et.cycle;
License
BSD-3-Clause, matching upstream xenia.