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>
7.6 KiB
bcctrx — Branch Conditional to Count Register
Category: Branch & System · Form: XL · Opcode:
0x4c000420· sync
Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
|---|---|---|---|
bcctr |
bcctrx |
— | Branch Conditional to Count Register |
bcctrl |
bcctrx |
LK=1 | Branch Conditional to Count Register |
Syntax
bcctr[LK] [BO], [BI]
Encoding
bcctrx — form XL
- Opcode word:
0x4c000420 - Primary opcode (bits 0–5):
19 - Extended opcode:
528 - 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 |
bcctrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
BO |
bcctrx: read | 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See forms/XL.md. |
BI |
bcctrx: read | CR bit index (0–31) selected by BO's condition test. |
CR |
bcctrx: read | Condition-register update. When Rc=1, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
CTR |
bcctrx: read | Count register. Decremented and optionally tested by conditional branches when BO[2]=0. |
LR |
bcctrx: write (conditional) | Link register. Written by bl/bla/bcl/bclrl/bcctrl; read by bclr/bclrl. |
Register Effects
bcctrx
- Reads (always):
LK,BO,BI,CR,CTR - Reads (conditional): none
- Writes (always): none
- Writes (conditional):
LR
Status-Register Effects
No condition-register or status-register effects.
Operation (pseudocode)
cond_ok <- BO[0] | (CR[BI] ≡ BO[1])
if cond_ok then NIA <- CTR[0:61] || 0b00
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
bcctrx
- Canary XML:
tools/ppc-instructions.xml— search formnem="bcctrx" - Canary emitter:
src/xenia/cpu/ppc/ppc_emit_control.cc:250 - Sylpheed opcode:
crates/sylpheed-ppc/src/opcode.rs:22 - Sylpheed decoder:
crates/sylpheed-ppc/src/decoder.rs:836
Canary emitter (frozen snapshot @ f21ebd49e9)
int InstrEmit_bcctrx(PPCHIRBuilder& f, const InstrData& i) {
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
// if cond_ok then
// NIA <- CTR[0:61] || 0b00
// if LK then
// LR <- CIA + 4
// NOTE: the condition bits are reversed!
// 01234 (docs)
// 43210 (real)
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;
}
}
bool expect_true = !not_cond_ok;
return InstrEmit_branch(f, "bcctrx", i.address, f.LoadCTR(), i.XL.LK, cond_ok,
expect_true);
}
Special Cases & Edge Conditions
-
No CTR decrement. Unlike
bcxandbclrx,bcctrcannot decrement CTR (the CTR is the target). The PowerISA reservesBO[2] = 0encodings — they are invalid onbcctrx. Canary ignoresBO[2]/BO[3]and treats everybcctras a pure CR-conditional branch. -
CTR alignment mask. The target is
CTR & ~3. Likebclr, the low two bits are stripped — a misaligned CTR is silently rounded down rather than trapping. -
BO encoding (CR-only subset). Because CTR-test bits are unused, only four
BOpatterns are meaningful:BO (binary) Meaning 0100zbranch if CR[BI]false0101zbranch if CR[BI]true1z1zzbranch always ( bctr)0000z/001at/etc.reserved — implementation-defined -
Indirect call/dispatch idiom.
mtctr rN; bctrlis the canonical PPC indirect call: load function pointer into CTR, call. WithLK=1, PowerISA writesLR ← CIA + 4whether or not the branch is taken, and Canary does the same — itsInstrEmit_branchstoresLRbefore the conditional transfer, exactly as forbcx. -
bctrfor switch tables. Compilers emitbctr(notbctrl) for jump-table dispatch, with CTR loaded from a base + (index*4) lookup. Canary emits an indirect call to theCTRvalue (CallIndirect). -
Synchronisation. Marked
syncin Canary'stools/ppc-instructions.xml— context-synchronising. JIT backends must ensure prior side effects have committed before the indirect transfer. -
No prediction hint sensitivity. Xenon predicts indirect branches via a separate target cache; the
BO[4]hint is mostly cosmetic forbcctr.
Related Instructions
bclrx— branch conditional to LR (function returns).bcx— branch conditional to displacement (B-form).bx— unconditional displacement branch (I-form).mtctr,mfctr— load/read CTR viamtspr 9/mfspr 9.sc— alternative control-flow exit (system call).
Simplified Mnemonics
| Simplified | Expansion |
|---|---|
bctr |
bcctr BO=0b10100, BI=0 — unconditional indirect branch |
bctrl |
bcctrl BO=0b10100, BI=0 — unconditional indirect call |
beqctr crN |
bcctr BO=0b01100, BI=4·N+2 — call CTR if crN.EQ |
bnectr crN |
bcctr BO=0b00100, BI=4·N+2 — call CTR if crN.NE |
bltctr crN |
bcctr BO=0b01100, BI=4·N+0 — call CTR if crN.LT |
bgectr crN |
bcctr BO=0b00100, BI=4·N+0 — call CTR if crN.GE |
bgtctr crN |
bcctr BO=0b01100, BI=4·N+1 — call CTR if crN.GT |
blectr crN |
bcctr BO=0b00100, BI=4·N+1 — call CTR if crN.LE |
The unconditional bctr/bctrl are by far the most common in Xbox 360 disassembly (compiler-emitted indirect calls and switch dispatch).