# `td` — Trap Doubleword > **Category:** [Branch & System](../categories/branch.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000088` ## 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 0–5):** `31` - **Extended opcode:** `68` - **Synchronising:** no | Bits | Field | Meaning | | --- | --- | --- | | 0–5 | `OPCD` | primary opcode | | 6–10 | `RT/FRT/VRT` | destination | | 11–15 | `RA/FRA/VRA` | source A | | 16–20 | `RB/FRB/VRB` | source B | | 21–30 | `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)
xenia-rs interpreter body (frozen snapshot) ```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; } ```
## 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).