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>
10 KiB
10 KiB
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 XML:
tools/ppc-instructions.xml— search formnem="bcx" - Canary emitter:
src/xenia/cpu/ppc/ppc_emit_control.cc:173 - Sylpheed opcode:
crates/sylpheed-ppc/src/opcode.rs:24 - Sylpheed decoder:
crates/sylpheed-ppc/src/decoder.rs:455
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.
BDis 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 shortbcover an unconditionalb. - CTR decrement happens before the test.
BO[2]=0decrements CTR first, thenctr_okevaluates against the new value. The classicbdnz looploopsNtimes when CTR is initialised toN. - LR write is unconditional in Canary. Canary writes
LR ← CIA + 4wheneverLK=1, even on the not-taken path. This matches the PowerISA:bclalways setsLRregardless of branch outcome — exploited bybcl 20, 31, $+4as a self-PC capture (PIC trick). BOencoding — seebclrx.mdfor the full 5-bit table.bcxsupports 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,bcxis context-synchronising. Trivial in interpretation; matters for JIT reorder windows. - No
Rc. B-form has no record bit; the apparentRcoperand-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).
Related Instructions
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 singlebc.mtctr,mfctr— set/get loop counter forbdnz/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.