Files
Sylpheed/tools/ppc-manual/generator
MechaCat02 10dc260f0c chore(tools): adopt the PPC manual and a canary launcher that works anywhere
CONSOLIDATION.md Phase 6. Both lived untracked in the project root -- on one
disk, backed up by nothing.

  tools/ppc-manual/   393 files, 3.7 MB. 455 instructions, 350 family pages,
                      598 mnemonics resolvable through index.json, plus the
                      generator that produced them.
  tools/run-canary.sh the oracle launcher.

🔴 THE LAUNCHER WAS BROKEN IN TWO WAYS AND IS REWRITTEN, not copied:

  * it pointed at `xenia-rs/sylpheed.iso`, a SYMLINK. Wine cannot resolve one
    and says "path invalid", which reads as a corrupt image rather than a path
    problem -- it has cost a session before. It now points at the real file and
    warns if handed a symlink.
  * it hardcoded one machine's absolute paths, and named `xenia-rs`, which this
    consolidation retires. Now derived from the script's own location, with
    SYLPH_CANARY_BIN / SYLPH_ISO overrides and a check that each exists.

The standing constraints are in its header where someone will read them: one
emulator at a time, Canary runs MUTED, and never judge a crash or a hang from
a Bash-launched run -- a SIGKILL that looked like the binary was the editor's
process supervisor.

⚠️ The manual's GENERATOR reads the xenia-rs source tree, which is going away.
Its decoder now lives here as crates/sylpheed-ppc, so the generator must be
repointed before it is run again. Recorded in the README rather than left for
someone to discover; the manual's content is checked in and regenerates from
nothing implicitly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 21:18:00 +02:00
..

Manual generator

Python scripts that build the ppc-manual/ tree from the two authoritative sources in this repository:

  • xenia-canary/tools/ppc-instructions.xml — metadata for all 455 Xbox 360 PPC instructions (mnemonic, form, group, opcode, in/out fields, disasm template).
  • xenia-rs/crates/xenia-cpu/src/ — the Rust interpreter. Individual instruction semantics live in interpreter.rs match arms.
  • xenia-canary/src/xenia/cpu/ppc/ppc_emit_*.cc — the C++ emit functions; referenced by line number only.

Files

File Purpose
generate_manual.py Main entry point. Parses XML, builds families, renders pages, writes index.json.
xml_model.py XML parser + expand_runtime_variants() (produces the set of Rc/OE/LK-expanded mnemonics a single XML entry covers).
bit_layout.py Per-form bit-field tables (rendered into the Encoding section of every page and into forms/*.md).
rust_scraper.py Locates each PpcOpcode::<mnem> enum variant, decoder arm, and interpreter match-arm line range.
cxx_scraper.py Locates InstrEmit_<mnem> in the xenia-canary emit .cc files.

Running

python3 ppc-manual/generator/generate_manual.py            # full generate
python3 ppc-manual/generator/generate_manual.py --dry-run  # parse + consistency checks only
python3 ppc-manual/generator/generate_manual.py --out /tmp/out   # alternate output root
python3 ppc-manual/generator/generate_manual.py --xml /path/to/ppc-instructions.xml

No third-party dependencies; Python 3.10+ standard library only.

Idempotency

The generator is re-runnable without data loss:

  1. Each page has a pair of sentinel comments:
    • <!-- GENERATED: BEGIN -->
    • <!-- GENERATED: END -->
  2. On re-run, only the text between the sentinels is rewritten. Everything after END (Special Cases, Related Instructions, IBM Reference) is preserved verbatim.
  3. If the END sentinel is missing, the generator assumes a reviewer has fully taken over the file and skips it entirely.

Consistency checks (enforced by --dry-run as well)

  • XML entry count ≡ 455 — warns if the XML has been modified.
  • family membership total ≡ XML entry count — every XML entry must land in exactly one family.
  • index coverage ≡ runtime-expanded mnemonic count — the JSON index must contain a key for every runtime variant (add, add., addo, addo., bclr, bclrl, …).

Family grouping rules

Three rules applied in order (see _family_head in generate_manual.py):

  1. If a mnemonic ends in 128 and the non-128 sibling exists, it joins the sibling's family. So vaddfp128 is consolidated into the vaddfp page.
  2. For memory ops (group m), trailing u, x, or ux suffixes are stripped when the base exists. So lwz, lwzu, lwzx, lwzux all land on the lwz page.
  3. Otherwise the mnemonic is its own family head.

All other flag variants (Rc, OE, LK) are runtime — they are NOT separate XML entries; they are listed in the page's "Assembler Mnemonics" table.

Category mapping

XML group Category dir Notes
i (integer) alu/
m (memory) memory/
b (branch) branch/ Includes sc and traps
c (control) control/ CR logical, SPR, sync
f (fpu) fpu/
v (vector) vmx/ or vmx128/ Split by form: VX128*vmx128/

Extending the generator

  • Pseudocode seeds. The PSEUDOCODE_SEEDS dict in generate_manual.py maps an XML mnemonic to a PPC-style pseudocode block. Add entries here to pre-fill the Operation section for additional mnemonics. Phase 2 reviewers can still override by writing content outside the sentinels.
  • C translation seeds. Similar dict of C snippets keyed by family head.
  • Field descriptions. FIELD_DESCRIPTIONS maps XML field names to IBM-style prose. Missing entries are marked "Phase 2: document this field."

Known limitations

  • Extended-opcode extraction in xml_model.Instruction.extended_opcode is best-effort per form. For VMX128 variants the extracted value may not match the exact pattern used by xenia's decoder tree — the page still shows it as a reference but the decoder source (linked on every page) is authoritative.
  • rust_scraper uses a naive brace counter to delimit interpreter match arms. It works for the current interpreter because the match arms use balanced braces and no string literals with unbalanced braces. If the interpreter ever adopts such literals the scraper will need a Rust-aware parser.
  • The generator treats mnemonics ending in x as xenia convention ("extended/XO form") and strips them for assembly display — except for the memory group, where x is the natural indexed-form suffix. If future xenia XML adds a new group where x is structural, the heuristic in xml_model.expand_runtime_variants needs updating.