chore: add migration/ bundle for cross-machine setup

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>
This commit is contained in:
MechaCat02
2026-05-10 21:38:38 +02:00
parent 8e709b0a24
commit e6d43a23ac
505 changed files with 86028 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
# `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 05):** `19`
- **Extended opcode:** `528`
- **Synchronising:** yes
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (19) |
| 610 | `BT/BO` | target / branch options |
| 1115 | `BA/BI` | source A / CR bit to test |
| 1620 | `BB` | source B |
| 2130 | `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 (031) 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)

View File

@@ -0,0 +1,180 @@
# `bclrx` — Branch Conditional to Link Register
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000020` · _sync_
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `bclr` | `bclrx` | — | Branch Conditional to Link Register |
| `bclrl` | `bclrx` | LK=1 | Branch Conditional to Link Register |
## Syntax
```asm
bclr[LK] [BO], [BI]
```
## Encoding
### `bclrx` — form `XL`
- **Opcode word:** `0x4c000020`
- **Primary opcode (bits 05):** `19`
- **Extended opcode:** `16`
- **Synchronising:** yes
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (19) |
| 610 | `BT/BO` | target / branch options |
| 1115 | `BA/BI` | source A / CR bit to test |
| 1620 | `BB` | source B |
| 2130 | `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 (031) 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
```c
/* 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`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bclrx"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:282`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L282)
- 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:711`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L711)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:939-961`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L939-L961)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::bclrx => {
let bo = instr.bo();
let bi = instr.bi();
if bo & 0b00100 == 0 {
ctx.ctr = ctx.ctr.wrapping_sub(1);
}
let ctr_ok = (bo & 0b00100) != 0
|| (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0));
let cond_ok = (bo & 0b10000) != 0
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
let next_pc = ctx.pc + 4;
if ctr_ok && cond_ok {
ctx.pc = (ctx.lr as u32) & !3;
} else {
ctx.pc = next_pc;
}
if instr.lk() {
ctx.lr = next_pc as u64;
}
}
```
</details>
<!-- GENERATED: END -->
## 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. Xenia implements it this way (`next_pc` captured first, then `lr` written).
- **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 xenia's XML). Translators must ensure side-effecting instructions preceding the branch have committed — trivial in a sequential C translation but relevant for JIT backends.
- **xenia's `LR_HALT_SENTINEL`.** Xenia sets `LR` to `0xBCBCBCBC` at thread start; when the top-level guest function returns via `blr`, the interpreter loop halts cleanly. Translators replicating guest behaviour don't need this — but if you generate a test harness, the sentinel is a convenient "function returned" signal.
## Related Instructions
- [`bcctrx`](bcctrx.md) — branch conditional to **CTR** (used by indirect calls / vtables).
- [`bcx`](bcx.md) — branch conditional to an immediate displacement (D-form).
- [`bx`](bx.md) — unconditional branch (I-form).
- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) — set/get LR via `mtspr 8, …` / `mfspr …, 8`.
- [`sc`](sc.md) — 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
- [AIX 7.3 — `bclr` (Branch Conditional to Link Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bclr-bclrl-branch-conditional-link-register-instruction)
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)

View File

@@ -0,0 +1,192 @@
# `bcx` — Branch Conditional
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [B](../forms/B.md) · **Opcode:** `0x40000000` · _sync_
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `bc` | `bcx` | — | Branch Conditional |
| `bcl` | `bcx` | LK=1 | Branch Conditional |
## Syntax
```asm
bc[LK][AA] [BO], [BI], [ADDR]
```
## Encoding
### `bcx` — form `B`
- **Opcode word:** `0x40000000`
- **Primary opcode (bits 05):** `16`
- **Extended opcode:** —
- **Synchronising:** yes
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `BO` | branch options |
| 1115 | `BI` | CR bit to test |
| 1629 | `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 (031) 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
```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
**`bcx`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcx"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:173`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L173)
- 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:340`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L340)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:908-938`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L908-L938)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::bcx => {
let bo = instr.bo();
let bi = instr.bi();
// Decrement CTR if needed
if bo & 0b00100 == 0 {
ctx.ctr = ctx.ctr.wrapping_sub(1);
}
let ctr_ok = (bo & 0b00100) != 0
|| (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0));
let cond_ok = (bo & 0b10000) != 0
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
if ctr_ok && cond_ok {
let target = if instr.aa() {
instr.bd() as u32
} else {
ctx.pc.wrapping_add(instr.bd() as u32)
};
if instr.lk() {
ctx.lr = (ctx.pc + 4) as u64;
}
ctx.pc = target;
} else {
if instr.lk() {
ctx.lr = (ctx.pc + 4) as u64;
}
ctx.pc += 4;
}
}
```
</details>
<!-- GENERATED: END -->
## 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 xenia.** Xenia 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).
## Related Instructions
- [`bx`](bx.md) — unconditional displacement branch (24-bit range).
- [`bclrx`](bclrx.md) — branch conditional to LR (function return).
- [`bcctrx`](bcctrx.md) — branch conditional to CTR (indirect call / dispatch).
- [`crand`](../control/crand.md), [`cror`](../control/cror.md), … — combine multiple CR bits before a single `bc`.
- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) — set/get loop counter for `bdnz`/`bdz`.
- [`sc`](sc.md) — 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
- [AIX 7.3 — `bc` (Branch Conditional)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bc-branch-conditional-instruction)
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)

View File

@@ -0,0 +1,133 @@
# `bx` — Branch
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [I](../forms/I.md) · **Opcode:** `0x48000000` · _sync_
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `b` | `bx` | — | Branch |
| `bl` | `bx` | LK=1 | Branch |
## Syntax
```asm
b[LK][AA] [ADDR]
```
## Encoding
### `bx` — form `I`
- **Opcode word:** `0x48000000`
- **Primary opcode (bits 05):** `18`
- **Extended opcode:** —
- **Synchronising:** yes
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 629 | `LI` | signed 24-bit word-offset target |
| 30 | `AA` | absolute-address flag |
| 31 | `LK` | link flag (bl/ba/bla) |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `LK` | bx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
| `AA` | bx: 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. |
| `ADDR` | bx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). |
| `LR` | bx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. |
## Register Effects
### `bx`
- **Reads (always):** `LK`, `AA`, `ADDR`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** `LR`
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
NIA <- (CIA + EXTS(LI || 0b00)) if AA=0
<- EXTS(LI || 0b00) if AA=1
if LK then LR <- CIA + 4
```
## C Translation Example
```c
/* b / bl / ba / bla — unconditional branch (I-form, primary 18) */
int32_t li = (int32_t)(insn.LI << 2); /* sign-extended word-offset */
uint32_t target = insn.AA ? (uint32_t)li : (uint32_t)(pc + li);
uint32_t next = pc + 4;
if (insn.LK) lr = next; /* bl / bla save return addr */
pc = target;
```
## Implementation References
**`bx`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bx"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:154`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L154)
- 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:342`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L342)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:897-907`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L897-L907)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::bx => {
let target = if instr.aa() {
instr.li() as u32
} else {
ctx.pc.wrapping_add(instr.li() as u32)
};
if instr.lk() {
ctx.lr = (ctx.pc + 4) as u64;
}
ctx.pc = target;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **24-bit word-aligned target.** `LI` is a 24-bit signed word-count. Hardware concatenates `LI || 0b00` (adds the implicit two low zero bits) and sign-extends to 64 bits before using it as an address. The displacement range is therefore ±32 MiB in bytes (`2^25 … +2^25 4`).
- **Four mnemonics, one opcode.** The four runtime variants selected by `AA` and `LK`:
- `b``AA = 0, LK = 0` — PC-relative, no link.
- `bl``AA = 0, LK = 1` — PC-relative, `LR = CIA + 4` (the ubiquitous function-call primitive).
- `ba``AA = 1, LK = 0` — absolute, no link.
- `bla``AA = 1, LK = 1` — absolute, link.
Xbox 360 code almost exclusively uses `b` and `bl`; `ba` / `bla` appear only in kernel / firmware stubs.
- **Target alignment.** `LI` is scaled by 4, so all targets are 4-byte aligned by construction. There is no low-bit encoding of ARM-style Thumb — PPC has one instruction width.
- **LR write is *before* the branch.** In `bl`, `LR` receives `CIA + 4` (the address of the instruction *after* the branch) before execution transfers to the target. Nested calls naturally overwrite LR; callees must spill it ([`mflr`](../control/mfspr.md) + `std`) before making their own `bl`.
- **Indirect tail calls.** A tail call to an indirect target is encoded as `mtctr` + `bctr` (see [`bcctrx`](bcctrx.md)), not `bx``bx` has no register-based form.
- **No condition test.** Use [`bcx`](bcx.md) for conditional displacement branches or [`bclrx`](bclrx.md) / [`bcctrx`](bcctrx.md) for conditional LR/CTR jumps.
- **Speculative execution.** The Xenon fetches past `bx`; translators that mask control flow must treat the target as a single-destination control transfer.
## Related Instructions
- [`bcx`](bcx.md) — conditional displacement branch (B-form, ±32 KiB range).
- [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) — branch to LR / CTR, conditional and unconditional.
- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) — LR save/restore for nested `bl` calls.
- [`sc`](sc.md) — system call; an alternative control-flow exit to the kernel.
## Simplified Mnemonics
Assemblers emit `b`, `bl`, `ba`, `bla` for the four runtime combinations. There is no further simplification.
## IBM Reference
- [AIX 7.3 — `b` (Branch)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-b-branch-instruction)
- [AIX 7.3 — `bl` (Branch with Link, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)

View File

@@ -0,0 +1,129 @@
# `sc` — System Call
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [SC](../forms/SC.md) · **Opcode:** `0x44000002` · _sync_
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `sc` | `sc` | — | System Call |
## Syntax
```asm
sc [LEV]
```
## Encoding
### `sc` — form `SC`
- **Opcode word:** `0x44000002`
- **Primary opcode (bits 05):** `17`
- **Extended opcode:** —
- **Synchronising:** yes
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (17) |
| 619 | `—` | reserved |
| 2026 | `LEV` | exception level |
| 2729 | `—` | reserved |
| 30 | `1` | fixed 1 |
| 31 | `—` | reserved |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `LEV` | sc: read | System-call exception level (for `sc`). |
## Register Effects
### `sc`
- **Reads (always):** `LEV`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
system_call_exception(LEV)
```
## 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
**`sc`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sc"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:455`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L455)
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:63`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L63)
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:341`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L341)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:984-997`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L984-L997)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::sc => {
// PPCBUG-064: log non-zero LEV (`sc 2` is the Xbox 360 hypervisor-call
// convention; canary dispatches it to a different handler than `sc 0`).
// Routing LEV=2 requires a StepResult variant extension; deferred.
let lev = (instr.raw >> 5) & 0x7F;
if lev != 0 {
tracing::warn!(
"sc with LEV={} at {:#010x}: dispatched as plain SystemCall (HVcall routing not implemented)",
lev, ctx.pc
);
}
ctx.pc += 4;
return StepResult::SystemCall;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **`LEV` field — kernel vs hypervisor.** The 7-bit `LEV` operand selects the privilege level of the syscall:
- `LEV = 0` — supervisor (kernel) syscall. Standard application → kernel transition; targets the `0xC00` system-call vector.
- `LEV = 1` — reserved.
- `LEV = 2`**hypervisor syscall** (`HVcall`). On the Xenon, `sc 2` traps to the Xbox 360 hypervisor; this is how the kernel itself talks to the supervisor below it (e.g., for security operations, encrypted-memory accesses, page table updates).
- **`sc` as written by titles.** Almost all guest game code uses `LEV = 0` to call `XboxKrnl.exe`. Game disassembly will show large jump tables of small thunks each ending in `li r0, syscall_no; sc; blr`.
- **No condition or status side effects.** `sc` updates *no* general-purpose register on entry — neither LR nor CR. The kernel sees the GPR/FPR snapshot as-is and reads the syscall number out of `r0` (Xbox 360 ABI convention, not architectural).
- **Return path.** Hardware returns from `sc` via [`rfid`](../control/mtmsrd.md)-class instructions in the kernel handler; from the application's perspective execution resumes at `CIA + 4`. Xenia's interpreter realises this by simply pre-incrementing `pc` then returning `StepResult::SystemCall` — the host driver dispatches the syscall and re-enters the loop.
- **xenia divergence vs hardware.** xenia-rs *does not* model the `0xC00` exception vector or save SRR0/SRR1; the `LEV` operand is currently ignored. All `sc` instructions are treated identically and serviced by the host. This is sufficient because Xbox 360 titles don't observe SRR registers and the host kernel is implemented natively.
- **Synchronisation.** Marked `sync` in xenia's XML — `sc` is context-synchronising (hardware completes all prior instructions before raising the exception). JITs must flush pending state before emitting the host call.
- **Reserved bits.** Bit 30 is fixed `1`; bits 619 and 2729 are reserved (must be 0). The 1-bit field at position 30 distinguishes the `sc` encoding from `scv` (later PowerISA addition, not present on the Xenon).
## Related Instructions
- [`bx`](bx.md), [`bcx`](bcx.md), [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) — ordinary control flow alternatives.
- [`tw`](tw.md), [`twi`](twi.md), [`td`](td.md), [`tdi`](tdi.md) — synchronous *trap* exceptions; another way to enter the kernel.
- [`mtmsr`](../control/mtmsr.md), [`mtmsrd`](../control/mtmsrd.md) — machine-state changes used by the kernel's `sc` handler on return (`rfid`/`hrfid` chain not separately documented in this manual).
- [`isync`](../control/mtmsr.md) — context-synchronising sibling; `sc` itself implies an isync-like fence.
## IBM Reference
- [AIX 7.3 — `sc` (System Call)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sc-system-call-instruction)
- PowerISA v2.07B, Book III §7 — System Linkage interrupt definitions and `LEV` field semantics.

View File

@@ -0,0 +1,184 @@
# `td` — Trap Doubleword
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000088`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `td` | `td` | — | Trap Doubleword |
## Syntax
```asm
td [TO], [RA], [RB]
```
## Encoding
### `td` — form `X`
- **Opcode word:** `0x7c000088`
- **Primary opcode (bits 05):** `31`
- **Extended opcode:** `68`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `XO` | extended opcode (10 bits) |
| 31 | `Rc` | record-form flag |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `TO` | td: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
| `RA` | td: read | Source GPR (`r0``r31`). |
| `RB` | td: read | Source GPR. |
## Register Effects
### `td`
- **Reads (always):** `TO`, `RA`, `RB`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
; Pseudocode derives directly from the xenia-rs interpreter
; arm (see Implementation References). Operation semantics:
; - Read source operands from the fields listed under Operands.
; - Apply the arithmetic / logical / memory action described
; in the Description field above.
; - Write results to the destination register(s); update any
; status bits enumerated under Status-Register Effects.
; Consult the IBM AIX reference link under IBM Reference for
; canonical PPC-style pseudocode where xenia's expression is
; terse.
```
## 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
**`td`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="td"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:552`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L552)
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:769`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L769)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
// PPCBUG-063: save CIA before incrementing so a trap handler reads
// the faulting instruction address, not CIA+4.
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
// typed-trap convention used by the CRT/kernel for C++ exception
// class dispatch). The audit notes this is relevant to the Sylpheed
// throw investigation; routing the type code via a payload requires
// a StepResult enum extension that's deferred for now.
let trap_pc = ctx.pc;
let a = ctx.gpr[instr.ra()];
let b = match instr.opcode {
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
_ => ctx.gpr[instr.rb()],
};
let width = match instr.opcode {
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
_ => trap::TrapWidth::Doubleword,
};
let fired = trap::evaluate(instr.to(), a, b, width);
if fired {
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
&& instr.to() == 31 && instr.ra() == 0 {
Some(instr.simm16() as u16)
} else { None };
tracing::warn!(
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
trap_pc, instr.opcode, instr.to(), a, b,
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
);
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
// can read the faulting instruction address from ctx.pc.
return StepResult::Trap;
}
ctx.pc += 4;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **`TO` mask encoding (5 bits, MSB-first).** Each bit selects one comparison; the trap fires if *any* selected condition is true. Both operands are treated as 64-bit doublewords:
| Bit | Mnemonic | Triggered when |
| --- | --- | --- |
| `TO[0]` (16) | LT | `(int64) RA < (int64) RB` |
| `TO[1]` (8) | GT | `(int64) RA > (int64) RB` |
| `TO[2]` (4) | EQ | `RA == RB` |
| `TO[3]` (2) | LGT | `(uint64) RA < (uint64) RB` (logical less) |
| `TO[4]` (1) | LLT | `(uint64) RA > (uint64) RB` (logical greater — historical naming) |
- **`TO = 31` is unconditional trap.** All five bits set ⇒ the trap fires regardless of operand values; the simplified mnemonic `trap` is `tw 31, 0, 0` for words and **`td 31, 0, 0`** for doublewords. The PowerISA also uses `tdi 31, 0, 0` (or `twi`) as a debugger break.
- **64-bit comparison.** Unlike [`tw`](tw.md), `td` always compares the full 64-bit GPRs. On the Xenon (64-bit) this is meaningful; PPC32 implementations don't have `td`.
- **No register effects.** Only the side effect is the trap. No CR/LR/CTR/XER updates.
- **Hardware behaviour.** When the trap fires, hardware raises a Program interrupt with `SRR1[TRAP]` set and vectors to `0x700`. The Xbox 360 hypervisor / kernel handles it (assertion failure, debugger trap, etc.).
- **xenia simplification.** xenia-rs collapses all four trap variants (`td`, `tdi`, `tw`, `twi`) into one match arm that *unconditionally* logs and returns `StepResult::Trap` — it does **not** evaluate `TO` against the operands. This is a material divergence from the spec: in xenia every trap fires even if the condition is false. Real Xenon code rarely uses non-trivial `TO` masks (typical use is the unconditional `trap` for `__assert` / debugger break), so the divergence is normally invisible.
- **Distinguishing assert vs. break.** Compilers commonly emit `tdne r3, r3` (impossible) or `tdi 0, r0, 0` patterns that *cannot* trap as inert markers. Xenia's blanket trap would mis-fire on these — a small known bug; track it if you see spurious traps.
## Related Instructions
- [`tdi`](tdi.md) — same condition test against a 16-bit signed immediate (D-form).
- [`tw`](tw.md) / [`twi`](twi.md) — 32-bit (word) variants for comparing low halves of GPRs.
- [`sc`](sc.md) — synchronous kernel entry via system-call exception (different vector, different intent).
- [`mtmsr`](../control/mtmsr.md) — kernel returns from the trap handler via `rfid`-family instructions.
### Simplified Mnemonics
| Simplified | Expansion | Triggered when |
| --- | --- | --- |
| `td RA, RB` (=`tdu`) | `td 31, RA, RB` | unconditional trap |
| `tdeq RA, RB` | `td 4, RA, RB` | `RA == RB` |
| `tdne RA, RB` | `td 24, RA, RB` | `RA != RB` |
| `tdlt RA, RB` | `td 16, RA, RB` | signed less than |
| `tdle RA, RB` | `td 20, RA, RB` | signed less or equal |
| `tdgt RA, RB` | `td 8, RA, RB` | signed greater than |
| `tdge RA, RB` | `td 12, RA, RB` | signed greater or equal |
| `tdllt RA, RB` | `td 2, RA, RB` | unsigned less than |
| `tdlge RA, RB` | `td 5, RA, RB` | unsigned greater or equal |
| `tdlgt RA, RB` | `td 1, RA, RB` | unsigned greater than |
| `tdlle RA, RB` | `td 6, RA, RB` | unsigned less or equal |
## IBM Reference
- [AIX 7.3 — `td` (Trap Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-td-trap-doubleword-instruction)
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions (`TO` semantics).

View File

@@ -0,0 +1,173 @@
# `tdi` — Trap Doubleword Immediate
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x08000000`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `tdi` | `tdi` | — | Trap Doubleword Immediate |
## Syntax
```asm
tdi [TO], [RA], [SIMM]
```
## Encoding
### `tdi` — form `D`
- **Opcode word:** `0x08000000`
- **Primary opcode (bits 05):** `2`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `D/SI/UI` | 16-bit signed or unsigned immediate |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `TO` | tdi: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
| `RA` | tdi: read | Source GPR (`r0``r31`). |
| `SIMM` | tdi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
## Register Effects
### `tdi`
- **Reads (always):** `TO`, `RA`, `SIMM`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
; Pseudocode derives directly from the xenia-rs interpreter
; arm (see Implementation References). Operation semantics:
; - Read source operands from the fields listed under Operands.
; - Apply the arithmetic / logical / memory action described
; in the Description field above.
; - Write results to the destination register(s); update any
; status bits enumerated under Status-Register Effects.
; Consult the IBM AIX reference link under IBM Reference for
; canonical PPC-style pseudocode where xenia's expression is
; terse.
```
## 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
**`tdi`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tdi"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:568`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L568)
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:327`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L327)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
// PPCBUG-063: save CIA before incrementing so a trap handler reads
// the faulting instruction address, not CIA+4.
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
// typed-trap convention used by the CRT/kernel for C++ exception
// class dispatch). The audit notes this is relevant to the Sylpheed
// throw investigation; routing the type code via a payload requires
// a StepResult enum extension that's deferred for now.
let trap_pc = ctx.pc;
let a = ctx.gpr[instr.ra()];
let b = match instr.opcode {
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
_ => ctx.gpr[instr.rb()],
};
let width = match instr.opcode {
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
_ => trap::TrapWidth::Doubleword,
};
let fired = trap::evaluate(instr.to(), a, b, width);
if fired {
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
&& instr.to() == 31 && instr.ra() == 0 {
Some(instr.simm16() as u16)
} else { None };
tracing::warn!(
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
trap_pc, instr.opcode, instr.to(), a, b,
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
);
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
// can read the faulting instruction address from ctx.pc.
return StepResult::Trap;
}
ctx.pc += 4;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **Immediate is sign-extended.** `SIMM` is treated as a 16-bit signed value, then sign-extended to 64 bits before comparison. To trap against a small unsigned constant, the same encoding works because both signed and unsigned interpretations agree for `SIMM ∈ [0, 0x7FFF]`.
- **`TO` mask.** Identical bit layout to [`td`](td.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true.
- **`TO = 31` is unconditional.** `tdi 31, 0, 0` is a debugger / assert trap. Compilers sometimes use it as a "should not reach" marker.
- **64-bit comparison only.** Unlike [`twi`](twi.md), `tdi` always compares the full 64-bit GPR — it has no PPC32 analogue. The Xenon's PPC64 mode makes this meaningful.
- **No register effects.** Pure side effect on success: Program interrupt → vector `0x700` with `SRR1[TRAP]=1`.
- **xenia simplification.** xenia-rs unconditionally treats `tdi` as a fired trap, regardless of `TO`/`RA`/`SIMM` values. This diverges from the spec — real hardware would silently fall through when no `TO` bit's condition holds. Most title code uses only the unconditional `trap` form, so the divergence is normally invisible; non-firing assertion patterns (e.g. `tdi 0, r0, 0`) will mis-fire under xenia.
- **Reserved bits.** Bits 610 carry the `TO` field; there is no `Rc` / `OE` on D-form trap immediates.
## Related Instructions
- [`td`](td.md) — register-register doubleword trap (X-form).
- [`twi`](twi.md) / [`tw`](tw.md) — 32-bit-comparison siblings.
- [`sc`](sc.md) — kernel-entry counterpart via system call exception.
- [`mtmsrd`](mtmsrd.md) (control category) — kernel `rfid`-style return path after handling.
### Simplified Mnemonics
| Simplified | Expansion | Triggered when |
| --- | --- | --- |
| `tdi RA, value` | `tdi 31, RA, value` | unconditional trap |
| `tdeqi RA, value` | `tdi 4, RA, value` | `RA == EXTS(value)` |
| `tdnei RA, value` | `tdi 24, RA, value` | `RA != EXTS(value)` |
| `tdlti RA, value` | `tdi 16, RA, value` | signed less than |
| `tdlei RA, value` | `tdi 20, RA, value` | signed less or equal |
| `tdgti RA, value` | `tdi 8, RA, value` | signed greater than |
| `tdgei RA, value` | `tdi 12, RA, value` | signed greater or equal |
| `tdllti RA, value` | `tdi 2, RA, value` | unsigned less than |
| `tdlgei RA, value` | `tdi 5, RA, value` | unsigned greater or equal |
| `tdlgti RA, value` | `tdi 1, RA, value` | unsigned greater than |
| `tdllei RA, value` | `tdi 6, RA, value` | unsigned less or equal |
## IBM Reference
- [AIX 7.3 — `tdi` (Trap Doubleword Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tdi-trap-doubleword-immediate-instruction)
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.

View File

@@ -0,0 +1,184 @@
# `tw` — Trap Word
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000008`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `tw` | `tw` | — | Trap Word |
## Syntax
```asm
tw [TO], [RA], [RB]
```
## Encoding
### `tw` — form `X`
- **Opcode word:** `0x7c000008`
- **Primary opcode (bits 05):** `31`
- **Extended opcode:** `4`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `XO` | extended opcode (10 bits) |
| 31 | `Rc` | record-form flag |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `TO` | tw: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
| `RA` | tw: read | Source GPR (`r0``r31`). |
| `RB` | tw: read | Source GPR. |
## Register Effects
### `tw`
- **Reads (always):** `TO`, `RA`, `RB`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
; Pseudocode derives directly from the xenia-rs interpreter
; arm (see Implementation References). Operation semantics:
; - Read source operands from the fields listed under Operands.
; - Apply the arithmetic / logical / memory action described
; in the Description field above.
; - Write results to the destination register(s); update any
; status bits enumerated under Status-Register Effects.
; Consult the IBM AIX reference link under IBM Reference for
; canonical PPC-style pseudocode where xenia's expression is
; terse.
```
## 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
**`tw`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tw"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:583`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L583)
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:750`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L750)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
// PPCBUG-063: save CIA before incrementing so a trap handler reads
// the faulting instruction address, not CIA+4.
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
// typed-trap convention used by the CRT/kernel for C++ exception
// class dispatch). The audit notes this is relevant to the Sylpheed
// throw investigation; routing the type code via a payload requires
// a StepResult enum extension that's deferred for now.
let trap_pc = ctx.pc;
let a = ctx.gpr[instr.ra()];
let b = match instr.opcode {
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
_ => ctx.gpr[instr.rb()],
};
let width = match instr.opcode {
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
_ => trap::TrapWidth::Doubleword,
};
let fired = trap::evaluate(instr.to(), a, b, width);
if fired {
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
&& instr.to() == 31 && instr.ra() == 0 {
Some(instr.simm16() as u16)
} else { None };
tracing::warn!(
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
trap_pc, instr.opcode, instr.to(), a, b,
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
);
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
// can read the faulting instruction address from ctx.pc.
return StepResult::Trap;
}
ctx.pc += 4;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **32-bit comparison only.** `tw` compares the *low 32 bits* of `RA` and `RB`. The high halves of the 64-bit GPRs on the Xenon are ignored. Use [`td`](td.md) for full 64-bit comparisons.
- **`TO` mask (5 bits, MSB-first).** Same encoding as the doubleword form:
| Bit | Mnemonic | Triggered when |
| --- | --- | --- |
| `TO[0]` (16) | LT | `(int32) RA < (int32) RB` |
| `TO[1]` (8) | GT | `(int32) RA > (int32) RB` |
| `TO[2]` (4) | EQ | `(uint32) RA == (uint32) RB` |
| `TO[3]` (2) | LGT | `(uint32) RA < (uint32) RB` |
| `TO[4]` (1) | LLT | `(uint32) RA > (uint32) RB` |
- **`tw 31, 0, 0` is `trap`.** The simplified mnemonic `trap` expands to `tw 31, r0, r0` — all five `TO` bits set ⇒ unconditional trap. Compilers and the kernel use this as the assertion / debugger break primitive; it appears as `0x7FE00008` in raw bytes.
- **Conditional asserts.** GCC's `__builtin_trap` and MSVC's `__assert` macros emit `tw` variants like `twge`/`twlt` to fault on bound-check failures.
- **No register effects.** Side effect only: Program interrupt (`0x700`) with `SRR1[TRAP]=1`.
- **xenia simplification.** xenia-rs collapses `td/tdi/tw/twi` into a single arm that *unconditionally* logs and returns `StepResult::Trap` — the `TO` operand is **not evaluated**. Real hardware would silently fall through when no `TO` bit's condition holds. In practice titles use mostly the unconditional `trap`, so the divergence rarely manifests, but inert-marker patterns like `tw 0, r0, r0` will fire under xenia.
- **Inert encoding.** `tw 0, r0, r0` (no `TO` bits set) can never trap on real hardware. It encodes as `0x7C000008` — sometimes used as a structured-NOP marker. Watch for it in xenia traces.
## Related Instructions
- [`twi`](twi.md) — same 32-bit comparison against a 16-bit signed immediate.
- [`td`](td.md) / [`tdi`](tdi.md) — 64-bit (doubleword) variants.
- [`sc`](sc.md) — kernel entry via system-call exception (different vector).
- [`mtmsr`](../control/mtmsr.md) — kernel returns from `0x700` via `rfid`.
### Simplified Mnemonics
| Simplified | Expansion | Triggered when |
| --- | --- | --- |
| `trap` | `tw 31, 0, 0` | unconditional |
| `tweq RA, RB` | `tw 4, RA, RB` | `RA == RB` |
| `twne RA, RB` | `tw 24, RA, RB` | `RA != RB` |
| `twlt RA, RB` | `tw 16, RA, RB` | signed less than |
| `twle RA, RB` | `tw 20, RA, RB` | signed less or equal |
| `twgt RA, RB` | `tw 8, RA, RB` | signed greater than |
| `twge RA, RB` | `tw 12, RA, RB` | signed greater or equal |
| `twllt RA, RB` | `tw 2, RA, RB` | unsigned less than |
| `twlge RA, RB` | `tw 5, RA, RB` | unsigned greater or equal |
| `twlgt RA, RB` | `tw 1, RA, RB` | unsigned greater than |
| `twlle RA, RB` | `tw 6, RA, RB` | unsigned less or equal |
## IBM Reference
- [AIX 7.3 — `tw` (Trap Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tw-trap-word-instruction)
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.

View File

@@ -0,0 +1,172 @@
# `twi` — Trap Word Immediate
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x0c000000`
<!-- GENERATED: BEGIN -->
## Assembler Mnemonics
| Mnemonic | XML entry | Flags | Description |
| --- | --- | --- | --- |
| `twi` | `twi` | — | Trap Word Immediate |
## Syntax
```asm
tw [TO], [RA], [SIMM]
```
## Encoding
### `twi` — form `D`
- **Opcode word:** `0x0c000000`
- **Primary opcode (bits 05):** `3`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `D/SI/UI` | 16-bit signed or unsigned immediate |
## Operands
| Field | Role | Description |
| --- | --- | --- |
| `TO` | twi: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
| `RA` | twi: read | Source GPR (`r0``r31`). |
| `SIMM` | twi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
## Register Effects
### `twi`
- **Reads (always):** `TO`, `RA`, `SIMM`
- **Reads (conditional):** _none_
- **Writes (always):** _none_
- **Writes (conditional):** _none_
## Status-Register Effects
_No condition-register or status-register effects._
## Operation (pseudocode)
```
; Pseudocode derives directly from the xenia-rs interpreter
; arm (see Implementation References). Operation semantics:
; - Read source operands from the fields listed under Operands.
; - Apply the arithmetic / logical / memory action described
; in the Description field above.
; - Write results to the destination register(s); update any
; status bits enumerated under Status-Register Effects.
; Consult the IBM AIX reference link under IBM Reference for
; canonical PPC-style pseudocode where xenia's expression is
; terse.
```
## 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
**`twi`**
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="twi"`](../../xenia-canary/tools/ppc-instructions.xml)
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:601`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L601)
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:328`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L328)
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
```rust
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
// PPCBUG-063: save CIA before incrementing so a trap handler reads
// the faulting instruction address, not CIA+4.
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
// typed-trap convention used by the CRT/kernel for C++ exception
// class dispatch). The audit notes this is relevant to the Sylpheed
// throw investigation; routing the type code via a payload requires
// a StepResult enum extension that's deferred for now.
let trap_pc = ctx.pc;
let a = ctx.gpr[instr.ra()];
let b = match instr.opcode {
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
_ => ctx.gpr[instr.rb()],
};
let width = match instr.opcode {
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
_ => trap::TrapWidth::Doubleword,
};
let fired = trap::evaluate(instr.to(), a, b, width);
if fired {
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
&& instr.to() == 31 && instr.ra() == 0 {
Some(instr.simm16() as u16)
} else { None };
tracing::warn!(
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
trap_pc, instr.opcode, instr.to(), a, b,
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
);
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
// can read the faulting instruction address from ctx.pc.
return StepResult::Trap;
}
ctx.pc += 4;
}
```
</details>
<!-- GENERATED: END -->
## Special Cases & Edge Conditions
- **32-bit comparison against sign-extended immediate.** `SIMM` is a 16-bit signed value, sign-extended to 32 bits, then compared against the low 32 bits of `RA`. The high half of `RA` is *ignored*.
- **`TO` mask.** Identical to [`tw`](tw.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true.
- **`twi 31, 0, 0` is unconditional trap.** All `TO` bits set ⇒ guaranteed trap. The simplified mnemonic family (`twnei`, `twgei`, …) is much more common in real code: bound checks, null checks, integer-divide-by-zero pre-checks.
- **Compiler usage.** Xbox 360 GCC emits `twnei rN, -1` and similar to validate handle-style return values; the kernel handler turns the trap into an exception delivered to the title.
- **No register effects.** Side effect: Program interrupt → vector `0x700` with `SRR1[TRAP]=1`.
- **xenia simplification.** Same as the other three trap forms — xenia-rs unconditionally returns `StepResult::Trap` whenever it decodes any of `tdi`/`twi`/`td`/`tw`, regardless of the `TO` mask or operands. This means `twi 0, r0, 0` (architecturally a guaranteed-no-trap encoding) will spuriously fire under xenia. Keep this in mind when triaging unexpected trap signals.
- **No `Rc` / `OE`.** D-form trap immediates have neither.
## Related Instructions
- [`tw`](tw.md) — register-register 32-bit trap (X-form).
- [`tdi`](tdi.md) / [`td`](td.md) — 64-bit (doubleword) siblings.
- [`sc`](sc.md) — alternative synchronous kernel entry.
- [`cmpi`](../alu/cmpi.md), [`cmpli`](../alu/cmpli.md) — set CR for a subsequent [`bcx`](bcx.md) when you want a regular branch instead of a trap.
### Simplified Mnemonics
| Simplified | Expansion | Triggered when |
| --- | --- | --- |
| `tweqi RA, value` | `twi 4, RA, value` | `RA == EXTS(value)` |
| `twnei RA, value` | `twi 24, RA, value` | `RA != EXTS(value)` |
| `twlti RA, value` | `twi 16, RA, value` | signed less than |
| `twlei RA, value` | `twi 20, RA, value` | signed less or equal |
| `twgti RA, value` | `twi 8, RA, value` | signed greater than |
| `twgei RA, value` | `twi 12, RA, value` | signed greater or equal |
| `twllti RA, value` | `twi 2, RA, value` | unsigned less than |
| `twlgei RA, value` | `twi 5, RA, value` | unsigned greater or equal |
| `twlgti RA, value` | `twi 1, RA, value` | unsigned greater than |
| `twllei RA, value` | `twi 6, RA, value` | unsigned less or equal |
## IBM Reference
- [AIX 7.3 — `twi` (Trap Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-twi-trap-word-immediate-instruction)
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.