Files
xenia-rs/migration/project-root/ppc-manual/branch/bclrx.md
MechaCat02 e6d43a23ac chore: add migration/ bundle for cross-machine setup
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>
2026-05-10 21:38:38 +02:00

8.3 KiB
Raw Blame History

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 05): 19
  • Extended opcode: 16
  • Synchronising: yes
Bits Field Meaning
05 OPCD primary opcode (19)
610 BT/BO target / branch options
1115 BA/BI source A / CR bit to test
1620 BB source B
2130 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 (031) 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-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 = 0b10100blr (branch always to LR), the function epilogue. BO = 0b01100, BI = 2beqlr (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 bdnz at CTR = 1, the CTR becomes 0 and the branch is not taken.
  • Self-referential LR write. bclrl writes LR ← CIA + 4 before reading LR to set NIA. Per the PowerISA, bclrl reads the old LR for the branch target and writes the new LR with the return address, atomically from software's perspective. Xenia implements it this way (next_pc captured first, then lr written).
  • 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. bclr is context-synchronising (hence the sync flag 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 sets LR to 0xBCBCBCBC at thread start; when the top-level guest function returns via blr, 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.
  • 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 via mtspr 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.

IBM Reference