Bundles state that lives OUTSIDE the xenia-rs repo so a fresh clone on
another machine can be brought up to identical configuration via
migration/setup.sh:
- claude-memory/ ~/.claude/projects/-home-fabi-RE-Project-Sylpheed/memory/
(103 files, 1.1 MB - MEMORY.md + every
project_xenia_rs_*.md from audits
addis_signext through audit-058)
- project-root/dot-claude/ <project-root>/.claude/settings.json
(Stop hook + permissions)
- project-root/ppc-manual/ <project-root>/ppc-manual/
(PowerPC reference docs, 397 files, 3.7 MB)
- project-root/run-canary.sh <project-root>/run-canary.sh
- README.md Human-readable setup checklist
- setup.sh Idempotent installer (also reclones
xenia-canary at pinned HEAD 6de80dffe)
- MANIFEST.md Per-file mapping + per-file-not-bundled
restoration recipe
Excluded from bundle (not shippable via git):
- Sylpheed ISO (7.8 GB; copyright; manual copy required)
- sylpheed.db (395 MB; regenerable from XEX via analysis tooling)
- target/ build artifacts (rebuild on target)
- audit-runs probe firehoses (.log/.stdout/.stderr ~11 GB; rerun if needed)
- audit-runs memory dumps (.bin ~4.5 GB; rerun audit-026/027/029 if needed)
- xenia-canary checkout (setup.sh reclones from
git.mc02.dev/fabi/Xenia-Canary.git at HEAD 6de80dffe)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.3 KiB
bclrx — Branch Conditional to Link Register
Category: Branch & System · Form: XL · Opcode:
0x4c000020· sync
Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
|---|---|---|---|
bclr |
bclrx |
— | Branch Conditional to Link Register |
bclrl |
bclrx |
LK=1 | Branch Conditional to Link Register |
Syntax
bclr[LK] [BO], [BI]
Encoding
bclrx — form XL
- Opcode word:
0x4c000020 - Primary opcode (bits 0–5):
19 - Extended opcode:
16 - Synchronising: yes
| Bits | Field | Meaning |
|---|---|---|
| 0–5 | OPCD |
primary opcode (19) |
| 6–10 | BT/BO |
target / branch options |
| 11–15 | BA/BI |
source A / CR bit to test |
| 16–20 | BB |
source B |
| 21–30 | XO |
extended opcode (10 bits) |
| 31 | LK |
link flag |
Operands
| Field | Role | Description |
|---|---|---|
LK |
bclrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
BO |
bclrx: read | 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See forms/XL.md. |
BI |
bclrx: read | CR bit index (0–31) selected by BO's condition test. |
CR |
bclrx: read (conditional) | Condition-register update. When Rc=1, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
CTR |
bclrx: read (conditional); bclrx: write (conditional) | Count register. Decremented and optionally tested by conditional branches when BO[2]=0. |
LR |
bclrx: write (conditional) | Link register. Written by bl/bla/bcl/bclrl/bcctrl; read by bclr/bclrl. |
Register Effects
bclrx
- Reads (always):
LK,BO,BI - Reads (conditional):
CR,CTR - Writes (always): none
- Writes (conditional):
CTR,LR
Status-Register Effects
No condition-register or status-register effects.
Operation (pseudocode)
if ¬BO[2] then CTR <- CTR − 1
ctr_ok <- BO[2] | ((CTR ≠ 0) XOR BO[3])
cond_ok <- BO[0] | (CR[BI] ≡ BO[1])
if ctr_ok & cond_ok then NIA <- LR[0:61] || 0b00
if LK then LR <- CIA + 4
C Translation Example
/* bclr/bclrl — branch conditional to LR */
if (!(insn.BO & 4)) ctr -= 1;
bool ctr_ok = (insn.BO & 4) || ((ctr != 0) ^ !!(insn.BO & 2));
bool cond_ok = (insn.BO & 16) || (cr_bit(insn.BI) == !!(insn.BO & 8));
uint32_t next = pc + 4;
if (ctr_ok && cond_ok) pc = lr & ~3u; else pc = next;
if (insn.LK) lr = next;
Implementation References
bclrx
- xenia-canary XML:
tools/ppc-instructions.xml— search formnem="bclrx" - xenia-canary emit:
src/xenia/cpu/ppc/ppc_emit_control.cc:282 - xenia-rs opcode:
crates/xenia-cpu/src/opcode.rs:11 - xenia-rs decoder:
crates/xenia-cpu/src/decoder.rs:711 - xenia-rs interpreter:
crates/xenia-cpu/src/interpreter.rs:939-961
xenia-rs interpreter body (frozen snapshot)
PpcOpcode::bclrx => {
let bo = instr.bo();
let bi = instr.bi();
if bo & 0b00100 == 0 {
ctx.ctr = ctx.ctr.wrapping_sub(1);
}
let ctr_ok = (bo & 0b00100) != 0
|| (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0));
let cond_ok = (bo & 0b10000) != 0
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
let next_pc = ctx.pc + 4;
if ctr_ok && cond_ok {
ctx.pc = (ctx.lr as u32) & !3;
} else {
ctx.pc = next_pc;
}
if instr.lk() {
ctx.lr = next_pc as u64;
}
}
BO Encoding (5 bits)
BO controls two independent tests and two "hints". Bit 0 is the MSB.
| BO (binary) | CTR decrement? | CTR test | CR test | Meaning |
|---|---|---|---|---|
0000z |
yes | CTR ≠ 0 |
¬CR[BI] |
decrement, branch if CTR ≠ 0 and CR[BI] false |
0001z |
yes | CTR = 0 |
¬CR[BI] |
decrement, branch if CTR = 0 and CR[BI] false |
001at |
yes | CTR ≠ 0 / CTR = 0 |
— | decrement, branch on CTR only |
0100z |
no | — | ¬CR[BI] |
branch if CR[BI] false |
0101z |
no | — | CR[BI] |
branch if CR[BI] true |
011at |
no | — | — | branch always (z and t are prediction hints) |
1z00z |
yes | CTR ≠ 0 |
— | decrement, branch if CTR ≠ 0 |
1z01z |
yes | CTR = 0 |
— | decrement, branch if CTR = 0 |
1z1zz |
no | — | — | branch always |
Bit BO[0] = 1 disables the CR test; BO[2] = 1 disables the CTR decrement/test. BO[1] and BO[3] select the polarity of each test. BO[4] is a branch-prediction hint (0 = not taken, 1 = taken; ignored on the Xenon in most cases).
The most common bclr instance in Xbox 360 disassembly is BO = 0b10100 → blr (branch always to LR), the function epilogue. BO = 0b01100, BI = 2 → beqlr (return if cr0.EQ), also common.
Special Cases & Edge Conditions
- LR alignment mask. The target address is
LR & ~3— the low 2 bits are cleared. This silently ignores a misaligned LR; incoming code should always produce 4-byte-aligned LR values. - Ordering of CTR decrement and branch. The CTR is decremented first, then compared to zero after the decrement. So after
bdnzatCTR = 1, the CTR becomes0and the branch is not taken. - Self-referential LR write.
bclrlwritesLR ← CIA + 4before readingLRto setNIA. Per the PowerISA,bclrlreads the oldLRfor the branch target and writes the newLRwith the return address, atomically from software's perspective. Xenia implements it this way (next_pccaptured first, thenlrwritten). - Branch prediction hints (
BO[4]). The Xenon does static prediction on the basis of these hints, but behaviour is architecturally unobservable. Translators may ignore them. - Synchronisation.
bclris context-synchronising (hence thesyncflag in xenia's XML). Translators must ensure side-effecting instructions preceding the branch have committed — trivial in a sequential C translation but relevant for JIT backends. - xenia's
LR_HALT_SENTINEL. Xenia setsLRto0xBCBCBCBCat thread start; when the top-level guest function returns viablr, the interpreter loop halts cleanly. Translators replicating guest behaviour don't need this — but if you generate a test harness, the sentinel is a convenient "function returned" signal.
Related Instructions
bcctrx— branch conditional to CTR (used by indirect calls / vtables).bcx— branch conditional to an immediate displacement (D-form).bx— unconditional branch (I-form).mtlr,mflr— set/get LR viamtspr 8, …/mfspr …, 8.sc— system call (alternative control-flow exit).
Simplified Mnemonics
Assemblers fold common BO/BI patterns to single mnemonics:
| Simplified | Expansion |
|---|---|
blr |
bclr BO=0b10100, BI=0 — branch always to LR |
blrl |
bclrl BO=0b10100, BI=0 — branch always to LR with link (tail-call trampoline) |
beqlr crN |
bclr BO=0b01100, BI=4·N+2 — return if crN.EQ |
bnelr crN |
bclr BO=0b00100, BI=4·N+2 — return if crN.NE |
bltlr crN |
bclr BO=0b01100, BI=4·N+0 — return if crN.LT |
bgelr crN |
bclr BO=0b00100, BI=4·N+0 — return if crN.GE |
bgtlr crN |
bclr BO=0b01100, BI=4·N+1 — return if crN.GT |
blelr crN |
bclr BO=0b00100, BI=4·N+1 — return if crN.LE |
Xbox 360 disassemblers almost always emit the simplified form; the translation agent should learn to recognise them.