Bundles state that lives OUTSIDE the xenia-rs repo so a fresh clone on
another machine can be brought up to identical configuration via
migration/setup.sh:
- claude-memory/ ~/.claude/projects/-home-fabi-RE-Project-Sylpheed/memory/
(103 files, 1.1 MB - MEMORY.md + every
project_xenia_rs_*.md from audits
addis_signext through audit-058)
- project-root/dot-claude/ <project-root>/.claude/settings.json
(Stop hook + permissions)
- project-root/ppc-manual/ <project-root>/ppc-manual/
(PowerPC reference docs, 397 files, 3.7 MB)
- project-root/run-canary.sh <project-root>/run-canary.sh
- README.md Human-readable setup checklist
- setup.sh Idempotent installer (also reclones
xenia-canary at pinned HEAD 6de80dffe)
- MANIFEST.md Per-file mapping + per-file-not-bundled
restoration recipe
Excluded from bundle (not shippable via git):
- Sylpheed ISO (7.8 GB; copyright; manual copy required)
- sylpheed.db (395 MB; regenerable from XEX via analysis tooling)
- target/ build artifacts (rebuild on target)
- audit-runs probe firehoses (.log/.stdout/.stderr ~11 GB; rerun if needed)
- audit-runs memory dumps (.bin ~4.5 GB; rerun audit-026/027/029 if needed)
- xenia-canary checkout (setup.sh reclones from
git.mc02.dev/fabi/Xenia-Canary.git at HEAD 6de80dffe)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
165 lines
7.3 KiB
Markdown
165 lines
7.3 KiB
Markdown
# `bcctrx` — Branch Conditional to Count Register
|
||
|
||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000420` · _sync_
|
||
|
||
<!-- GENERATED: BEGIN -->
|
||
|
||
## Assembler Mnemonics
|
||
|
||
| Mnemonic | XML entry | Flags | Description |
|
||
| --- | --- | --- | --- |
|
||
| `bcctr` | `bcctrx` | — | Branch Conditional to Count Register |
|
||
| `bcctrl` | `bcctrx` | LK=1 | Branch Conditional to Count Register |
|
||
|
||
## Syntax
|
||
|
||
```asm
|
||
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
|
||
|
||
```c
|
||
/* C translation: the xenia-rs interpreter arm below in */
|
||
/* Implementation References is the authoritative semantic */
|
||
/* snapshot. Translate it line-by-line: */
|
||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||
/* The Register Effects and Status-Register Effects tables above */
|
||
/* enumerate every side effect a faithful translation must emit. */
|
||
```
|
||
|
||
## Implementation References
|
||
|
||
**`bcctrx`**
|
||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcctrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:250`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L250)
|
||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11)
|
||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:721`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L721)
|
||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:962-981`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L962-L981)
|
||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||
|
||
```rust
|
||
PpcOpcode::bcctrx => {
|
||
let bo = instr.bo();
|
||
let bi = instr.bi();
|
||
|
||
let cond_ok = (bo & 0b10000) != 0
|
||
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
|
||
|
||
if cond_ok {
|
||
let next_pc = ctx.pc + 4;
|
||
ctx.pc = (ctx.ctr as u32) & !3;
|
||
if instr.lk() {
|
||
ctx.lr = next_pc as u64;
|
||
}
|
||
} else {
|
||
if instr.lk() {
|
||
ctx.lr = (ctx.pc + 4) as u64;
|
||
}
|
||
ctx.pc += 4;
|
||
}
|
||
}
|
||
```
|
||
</details>
|
||
|
||
<!-- GENERATED: END -->
|
||
|
||
## Special Cases & Edge Conditions
|
||
|
||
- **No CTR decrement.** Unlike [`bcx`](bcx.md) and [`bclrx`](bclrx.md), `bcctr` cannot decrement CTR (the CTR is the *target*). The PowerISA reserves `BO[2] = 0` encodings — they are *invalid* on `bcctrx`. xenia silently ignores `BO[2]`/`BO[3]` and treats every `bcctr` as a pure CR-conditional branch, which matches both the canary emit and real Xenon hardware behaviour.
|
||
- **CTR alignment mask.** The target is `CTR & ~3`. Like `bclr`, 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 `BO` patterns are meaningful:
|
||
|
||
| BO (binary) | Meaning |
|
||
| --- | --- |
|
||
| `0100z` | branch if `CR[BI]` false |
|
||
| `0101z` | branch if `CR[BI]` true |
|
||
| `1z1zz` | branch always (`bctr`) |
|
||
| `0000z`/`001at`/etc. | reserved — implementation-defined |
|
||
|
||
- **Indirect call/dispatch idiom.** `mtctr rN; bctrl` is the canonical PPC indirect call: load function pointer into CTR, call. The xenia interpreter writes `LR ← CIA + 4` only when the branch is taken — this matches the PowerISA, but contrast with `bcx` where `LK` always writes LR (even if the branch is not taken). The C-translation reference handles this asymmetry explicitly.
|
||
- **`bctr` for switch tables.** Compilers emit `bctr` (not `bctrl`) for jump-table dispatch, with CTR loaded from a base + (index*4) lookup. Xenia honours this by simply jumping to `CTR & ~3`.
|
||
- **Synchronisation.** Marked `sync` in xenia's 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 for `bcctr`.
|
||
|
||
## Related Instructions
|
||
|
||
- [`bclrx`](bclrx.md) — branch conditional to **LR** (function returns).
|
||
- [`bcx`](bcx.md) — branch conditional to displacement (B-form).
|
||
- [`bx`](bx.md) — unconditional displacement branch (I-form).
|
||
- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) — load/read CTR via `mtspr 9` / `mfspr 9`.
|
||
- [`sc`](sc.md) — 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).
|
||
|
||
## IBM Reference
|
||
|
||
- [AIX 7.3 — `bcctr` (Branch Conditional to Count Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bcctr-bcctrl-branch-conditional-count-register-instruction)
|
||
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)
|