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

10 KiB
Raw Blame History

bcx — Branch Conditional

Category: Branch & System · Form: B · Opcode: 0x40000000 · sync

Assembler Mnemonics

Mnemonic XML entry Flags Description
bc bcx — Branch Conditional
bcl bcx LK=1 Branch Conditional

Syntax

bc[LK][AA] [BO], [BI], [ADDR]

Encoding

bcx — form B

  • Opcode word: 0x40000000
  • Primary opcode (bits 0–5): 16
  • Extended opcode: —
  • Synchronising: yes
Bits Field Meaning
0–5 OPCD primary opcode
6–10 BO branch options
11–15 BI CR bit to test
16–29 BD signed 14-bit word-offset target
30 AA absolute-address flag
31 LK link flag

Operands

Field Role Description
LK bcx: read Link bit. When 1, LR ← address-of-next-instruction before the branch is taken.
AA bcx: 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.
BO bcx: read 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See forms/XL.md.
BI bcx: read CR bit index (0–31) selected by BO's condition test.
ADDR bcx: read Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted).
CR bcx: 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 bcx: read (conditional); bcx: write (conditional) Count register. Decremented and optionally tested by conditional branches when BO[2]=0.
LR bcx: write (conditional) Link register. Written by bl/bla/bcl/bclrl/bcctrl; read by bclr/bclrl.

Register Effects

bcx

  • Reads (always): LK, AA, BO, BI, ADDR
  • 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 <- CIA + EXTS(BD || 0b00)  (AA=0)
                                       EXTS(BD || 0b00)  (AA=1)
if LK then LR <- CIA + 4

C Translation Example

/* No hand-written C yet. Translate the Canary emitter snapshot   */
/* under Implementation References; its HIR maps directly:        */
/*   f.LoadGPR(n) / f.StoreGPR(n, v)  -> r[n] / r[n] = v          */
/*   f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n]        */
/*   f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters   */
/*     wrap them in f.ByteSwap for the big-endian guest value      */
/*   f.UpdateCR(n, v)  -> CR field n from v's LOW 32 BITS vs 0     */
/*   f.LoadCA / f.StoreCA -> xer.CA;  f.StoreSAT -> vscr.SAT       */
/*   i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands  */
/* The Register Effects and Status-Register Effects tables above  */
/* enumerate every side effect a faithful translation must emit.  */

Implementation References

bcx

Canary emitter (frozen snapshot @ f21ebd49e9)
int InstrEmit_bcx(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
  //   if AA then
  //     NIA <- EXTS(BD || 0b00)
  //   else
  //     NIA <- CIA + EXTS(BD || 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.B.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.B.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.B.BO, 4, 4)) {
    // Ignore cond.
  } else {
    Value* cr = f.LoadCRField(i.B.BI >> 2, i.B.BI & 3);
    cond_ok = cr;
    if (select_bits(i.B.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;
  }

  uint32_t nia;
  if (i.B.AA) {
    nia = (uint32_t)XEEXTS16(i.B.BD << 2);
  } else {
    nia = (uint32_t)(i.address + XEEXTS16(i.B.BD << 2));
  }
  return InstrEmit_branch(f, "bcx", i.address, f.LoadConstantUint32(nia),
                          i.B.LK, ok, expect_true);
}

Special Cases & Edge Conditions

  • 14-bit signed displacement. BD is a 14-bit signed word-count, scaled by 4 — yielding a ±32 KiB byte range (−2^15 … +2^15 − 4). For longer-range conditional control flow, compilers emit a short bc over an unconditional b.
  • CTR decrement happens before the test. BO[2]=0 decrements CTR first, then ctr_ok evaluates against the new value. The classic bdnz loop loops N times when CTR is initialised to N.
  • LR write is unconditional in Canary. Canary writes LR ← CIA + 4 whenever LK=1, even on the not-taken path. This matches the PowerISA: bcl always sets LR regardless of branch outcome — exploited by bcl 20, 31, $+4 as a self-PC capture (PIC trick).
  • BO encoding — see bclrx.md for the full 5-bit table. bcx supports the full set, including CTR-only branches (bdnz, bdz).
  • Branch hint encoding. PPC overloads BO[4] as a static prediction hint: 0 = "predict not taken", 1 = "predict taken". The Xenon honours it for forward branches; backwards conditional branches are predicted taken regardless. Translators may ignore the hint.
  • Synchronisation. Marked sync — like all branches, bcx is context-synchronising. Trivial in interpretation; matters for JIT reorder windows.
  • No Rc. B-form has no record bit; the apparent Rc operand-table entry under "Status-Register Effects" is N/A here.

BO/BI encoding (compact table)

BO Effect Common simplified
0000z dec CTR, branch if CTR≠0 & ¬CR[BI] bdnzf BI, addr
0001z dec CTR, branch if CTR=0 & ¬CR[BI] bdzf BI, addr
0010y dec CTR, branch if CTR≠0 bdnz addr
0011y dec CTR, branch if CTR=0 bdz addr
0100z branch if ¬CR[BI] bf BI, addr (or bne/bge/...)
0101z branch if CR[BI] bt BI, addr (or beq/blt/...)
1z1zz branch always b addr (prefer plain b though)

Bit z is the prediction hint (0 = not taken, 1 = taken).

  • bx — unconditional displacement branch (24-bit range).
  • bclrx — branch conditional to LR (function return).
  • bcctrx — branch conditional to CTR (indirect call / dispatch).
  • crand, cror, … — combine multiple CR bits before a single bc.
  • mtctr, mfctr — set/get loop counter for bdnz/bdz.
  • sc — alternative control-flow exit.

Simplified Mnemonics

The bc mnemonic is rarely written directly; assemblers fold most uses into form-specific aliases:

Simplified Expansion
beq crN, addr bc 0b01100, 4·N+2, addr — branch if crN.EQ
bne crN, addr bc 0b00100, 4·N+2, addr — branch if crN.NE
blt crN, addr bc 0b01100, 4·N+0, addr — branch if crN.LT
bge crN, addr bc 0b00100, 4·N+0, addr — branch if crN.GE
bgt crN, addr bc 0b01100, 4·N+1, addr — branch if crN.GT
ble crN, addr bc 0b00100, 4·N+1, addr — branch if crN.LE
bso crN, addr bc 0b01100, 4·N+3, addr — branch on summary overflow
bns crN, addr bc 0b00100, 4·N+3, addr — branch on no SO
bdnz addr bc 0b10000, 0, addr — decrement CTR, branch if non-zero
bdz addr bc 0b10010, 0, addr — decrement CTR, branch if zero
bdnzt BI, addr combined CTR + CR test (rare)

When crN is omitted in disassembly, cr0 is implied.

IBM Reference