Files
Sylpheed/tools/ppc-manual/branch/bclrx.md
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

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