The generator had not been able to run correctly since the manual moved into
`tools/ppc-manual/`: it computed the repository root as `HERE.parent.parent`,
which now names `tools/`, so the XML, Canary's emitters and xenia-rs all stopped
resolving — silently, because both scrapers skipped what they could not find.
Every page's references had been pointing at paths that exist nowhere.
What each source contributed, measured on the 350 pages before this change:
Operation (pseudocode) 251 pages: fixed boilerplate "derives from the xenia-rs
interpreter"; 99 carry real hand-written seeds
C translation 337 pages: the same kind of boilerplate
xenia-rs snapshot 336 pages: the interpreter arm, pasted in — the only
per-instruction semantics on unseeded pages
links xenia-rs opcode/decoder/interpreter + Canary emitter
Now:
* semantics come from **Xenia Canary**, the reference emulator, read through
`git show` at a pinned upstream commit (`origin/canary_experimental`,
f21ebd49e9). Not our checkout: it carries instrumentation and lacked
upstream's `mcrf` fix, so it would have published probes and a wrong `mcrf`.
Each page embeds the emitter (`InstrEmit_<mnem>`), and for the 128 pure
one-line delegations also the helper that holds the semantics.
* decode references point at `crates/sylpheed-ppc` — the decoder that
produces `sylpheed.db` — as in-repo relative links.
* the boilerplate now says what is true, and the C translation guide maps
Canary's actual HIR calls, checked against `ppc_hir_builder.h` (including
that `UpdateCR(n, v)` truncates to 32 bits).
* `rust_scraper.py` -> `decoder_scraper.py` (interpreter half dropped);
missing sources are now errors, not empty results.
Verified:
consistency checks 455 XML entries, 350 families, 598 index keys
hand-written tails 386/386 byte-identical after regeneration
xenia-rs in generated 0
pages with a snapshot 349/350 (was 336) — `dcbi` has no Canary emitter at all
in-repo decoder links 910/910 resolve to a line holding the identifier
emitter boundaries brace counter == column-0 `}` rule on 521/521;
preprocessor model unit-tested (#if 0/#else/#elif)
idempotency re-run: 0 pages updated, 0 working-tree changes
Hand-written notes (outside the generated regions) are not rewritten here:
* 110 links into `../../xenia-rs/...` were dead; they now point at the file in
the archived repository (git.mc02.dev/fabi/xenia-rs @ 8401d4d). Line anchors
were dropped because the notes predate that commit — 0 of 441 old line
ranges match it — and a precise-looking wrong anchor is worse than none. The
link text, which carries the author's line numbers, is unchanged.
* 140 prose claims about xenia-rs's behaviour remain. 23 are verified to hold
for Canary too (the 32-bit CR0 truncation, OE left unimplemented); the other
114 need checking one by one, and some invert — e.g. `divdx` notes a correct
64-bit CR0 update in xenia-rs where Canary's `UpdateCR` truncates. Left for
a deliberate pass rather than a blind substitution.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.7 KiB
5.7 KiB
bx — Branch
Category: Branch & System · Form: I · Opcode:
0x48000000· sync
Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
|---|---|---|---|
b |
bx |
— | Branch |
bl |
bx |
LK=1 | Branch |
Syntax
b[LK][AA] [ADDR]
Encoding
bx — form I
- Opcode word:
0x48000000 - Primary opcode (bits 0–5):
18 - Extended opcode: —
- Synchronising: yes
| Bits | Field | Meaning |
|---|---|---|
| 0–5 | OPCD |
primary opcode |
| 6–29 | LI |
signed 24-bit word-offset target |
| 30 | AA |
absolute-address flag |
| 31 | LK |
link flag (bl/ba/bla) |
Operands
| Field | Role | Description |
|---|---|---|
LK |
bx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
AA |
bx: read | Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address. |
ADDR |
bx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). |
LR |
bx: write (conditional) | Link register. Written by bl/bla/bcl/bclrl/bcctrl; read by bclr/bclrl. |
Register Effects
bx
- Reads (always):
LK,AA,ADDR - Reads (conditional): none
- Writes (always): none
- Writes (conditional):
LR
Status-Register Effects
No condition-register or status-register effects.
Operation (pseudocode)
NIA <- (CIA + EXTS(LI || 0b00)) if AA=0
<- EXTS(LI || 0b00) if AA=1
if LK then LR <- CIA + 4
C Translation Example
/* b / bl / ba / bla — unconditional branch (I-form, primary 18) */
int32_t li = (int32_t)(insn.LI << 2); /* sign-extended word-offset */
uint32_t target = insn.AA ? (uint32_t)li : (uint32_t)(pc + li);
uint32_t next = pc + 4;
if (insn.LK) lr = next; /* bl / bla save return addr */
pc = target;
Implementation References
bx
- Canary XML:
tools/ppc-instructions.xml— search formnem="bx" - Canary emitter:
src/xenia/cpu/ppc/ppc_emit_control.cc:154 - Sylpheed opcode:
crates/sylpheed-ppc/src/opcode.rs:25 - Sylpheed decoder:
crates/sylpheed-ppc/src/decoder.rs:457
Canary emitter (frozen snapshot @ f21ebd49e9)
int InstrEmit_bx(PPCHIRBuilder& f, const InstrData& i) {
// if AA then
// NIA <- EXTS(LI || 0b00)
// else
// NIA <- CIA + EXTS(LI || 0b00)
// if LK then
// LR <- CIA + 4
uint32_t nia;
if (i.I.AA) {
nia = (uint32_t)XEEXTS26(i.I.LI << 2);
} else {
nia = (uint32_t)(i.address + XEEXTS26(i.I.LI << 2));
}
return InstrEmit_branch(f, "bx", i.address, f.LoadConstantUint32(nia),
i.I.LK);
}
Special Cases & Edge Conditions
- 24-bit word-aligned target.
LIis a 24-bit signed word-count. Hardware concatenatesLI || 0b00(adds the implicit two low zero bits) and sign-extends to 64 bits before using it as an address. The displacement range is therefore ±32 MiB in bytes (−2^25 … +2^25 − 4). - Four mnemonics, one opcode. The four runtime variants selected by
AAandLK:b—AA = 0, LK = 0— PC-relative, no link.bl—AA = 0, LK = 1— PC-relative,LR = CIA + 4(the ubiquitous function-call primitive).ba—AA = 1, LK = 0— absolute, no link.bla—AA = 1, LK = 1— absolute, link. Xbox 360 code almost exclusively usesbandbl;ba/blaappear only in kernel / firmware stubs.
- Target alignment.
LIis scaled by 4, so all targets are 4-byte aligned by construction. There is no low-bit encoding of ARM-style Thumb — PPC has one instruction width. - LR write is before the branch. In
bl,LRreceivesCIA + 4(the address of the instruction after the branch) before execution transfers to the target. Nested calls naturally overwrite LR; callees must spill it (mflr+std) before making their ownbl. - Indirect tail calls. A tail call to an indirect target is encoded as
mtctr+bctr(seebcctrx), notbx—bxhas no register-based form. - No condition test. Use
bcxfor conditional displacement branches orbclrx/bcctrxfor conditional LR/CTR jumps. - Speculative execution. The Xenon fetches past
bx; translators that mask control flow must treat the target as a single-destination control transfer.
Related Instructions
bcx— conditional displacement branch (B-form, ±32 KiB range).bclrx,bcctrx— branch to LR / CTR, conditional and unconditional.mtlr,mflr— LR save/restore for nestedblcalls.sc— system call; an alternative control-flow exit to the kernel.
Simplified Mnemonics
Assemblers emit b, bl, ba, bla for the four runtime combinations. There is no further simplification.