Files
Sylpheed/tools/ppc-manual/branch/bclrx.md
sim 84856fc081
All checks were successful
CI / Native — linux (pull_request) Successful in 2h1m57s
CI / WASM — Web (pull_request) Successful in 28m8s
CI / Formatting (pull_request) Successful in 1m26s
docs(ppc-manual): check every xenia-rs claim against Canary's source
The hand-written parts of the manual still described how the retired
xenia-rs interpreter behaved: its snapshots, Rust casts and helpers. Each of
those 490 statements is now either restated as what Canary's emitters and
x64 backend actually do (at the pinned canary_experimental commit), or
dropped where it only made sense for xenia-rs.

Checking them turned up claims that were wrong, not just outdated:

- VSCR[SAT] is never modelled in Canary (DID_SATURATE is a stub and mfvscr
  cannot see it); the pages said saturating ops set it stickily.
- Canary does not implement lswi/lswx/stswi/stswx, dcbi, mtfsb0/mtfsb1,
  vmsum*, vmhaddshs, vupkhpx/vupklpx, and most SPRs; pages described them
  as working.
- Traps evaluate TO in Canary; stvebx/stvehx/stvewx store one element, not
  16 bytes; mtmsrd writes only EE; fres/frsqrte/vrsqrtefp precision claims
  and the stfs "rounds under RN / sets FPSCR" claim contradicted the spec.
- Reservations are a 64 KiB block bitmap plus a value compare, not
  per-address tracking.

Claims that neither Canary's source nor a public spec settles are marked
unverified (NI at boot, vmaddcfp128 operand order, estimate bit-exactness).

Generated regions are untouched; re-running the generator changes nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 21:52:38 +02:00

9.4 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 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

Canary emitter (frozen snapshot @ f21ebd49e9)
int InstrEmit_bclrx(PPCHIRBuilder& f, const InstrData& i) {
  // if ¬BO[2] then
  //   CTR <- CTR - 1
  // ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
  // cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
  // if ctr_ok & cond_ok then
  //   NIA <- LR[0:61] || 0b00
  // if LK then
  //   LR <- CIA + 4

  // NOTE: the condition bits are reversed!
  // 01234 (docs)
  // 43210 (real)

  Value* ctr_ok = NULL;
  if (select_bits(i.XL.BO, 2, 2)) {
    // Ignore ctr.
  } else {
    // Decrement counter.
    Value* ctr = f.LoadCTR();
    ctr = f.Sub(ctr, f.LoadConstantUint64(1));
    f.StoreCTR(ctr);
    // Ctr check.
    ctr = f.Truncate(ctr, INT32_TYPE);
    // TODO(benvanik): could do something similar to cond and avoid the
    // is_true/branch_true pairing.
    if (select_bits(i.XL.BO, 1, 1)) {
      ctr_ok = f.IsFalse(ctr);
    } else {
      ctr_ok = f.IsTrue(ctr);
    }
  }

  Value* cond_ok = NULL;
  bool not_cond_ok = false;
  if (select_bits(i.XL.BO, 4, 4)) {
    // Ignore cond.
  } else {
    Value* cr = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
    cond_ok = cr;
    if (select_bits(i.XL.BO, 3, 3)) {
      // Expect true.
      not_cond_ok = false;
    } else {
      // Expect false.
      not_cond_ok = true;
    }
  }

  // We do a bit of optimization here to make the llvm assembly easier to read.
  Value* ok = NULL;
  bool expect_true = true;
  if (ctr_ok && cond_ok) {
    if (not_cond_ok) {
      cond_ok = f.IsFalse(cond_ok);
    }
    ok = f.And(ctr_ok, cond_ok);
  } else if (ctr_ok) {
    ok = ctr_ok;
  } else if (cond_ok) {
    ok = cond_ok;
    expect_true = !not_cond_ok;
  }

  return InstrEmit_branch(f, "bclrx", i.address, f.LoadLR(), i.XL.LK, ok,
                          expect_true, true);
}

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 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. Canary implements it this way: InstrEmit_bclrx loads LR as the target before InstrEmit_branch stores CIA + 4.
  • 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 Canary's tools/ppc-instructions.xml). Translators must ensure side-effecting instructions preceding the branch have committed — trivial in a sequential C translation but relevant for JIT backends.
  • Canary's 0xBCBCBCBC return sentinel. When the host calls into a guest function, Canary's Processor::Execute sets LR to 0xBCBCBCBC, so the top-level blr hands control back to the host. 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