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>
167 lines
6.4 KiB
Markdown
167 lines
6.4 KiB
Markdown
# `mtspr` — Move to Special-Purpose Register
|
||
|
||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XFX](../forms/XFX.md) · **Opcode:** `0x7c0003a6`
|
||
|
||
<!-- GENERATED: BEGIN -->
|
||
|
||
## Assembler Mnemonics
|
||
|
||
| Mnemonic | XML entry | Flags | Description |
|
||
| --- | --- | --- | --- |
|
||
| `mtspr` | `mtspr` | — | Move to Special-Purpose Register |
|
||
|
||
## Syntax
|
||
|
||
```asm
|
||
mtspr [SPR], [RS]
|
||
```
|
||
|
||
## Encoding
|
||
|
||
### `mtspr` — form `XFX`
|
||
|
||
- **Opcode word:** `0x7c0003a6`
|
||
- **Primary opcode (bits 0–5):** `31`
|
||
- **Extended opcode:** `467`
|
||
- **Synchronising:** no
|
||
|
||
| Bits | Field | Meaning |
|
||
| --- | --- | --- |
|
||
| 0–5 | `OPCD` | primary opcode (31) |
|
||
| 6–10 | `RT` | destination / source GPR |
|
||
| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask |
|
||
| 21–30 | `XO` | extended opcode |
|
||
| 31 | `—` | reserved |
|
||
|
||
## Operands
|
||
|
||
| Field | Role | Description |
|
||
| --- | --- | --- |
|
||
| `RS` | mtspr: read | Source GPR (alias for RD in some stores). |
|
||
| `SPR` | mtspr: write | Special-Purpose-Register number. Encoded with the two 5-bit halves swapped (bits 11-15 become the high half, bits 16-20 the low half). |
|
||
|
||
## Register Effects
|
||
|
||
### `mtspr`
|
||
|
||
- **Reads (always):** `RS`
|
||
- **Reads (conditional):** _none_
|
||
- **Writes (always):** `SPR`
|
||
- **Writes (conditional):** _none_
|
||
|
||
## Status-Register Effects
|
||
|
||
_No condition-register or status-register effects._
|
||
|
||
## Operation (pseudocode)
|
||
|
||
```
|
||
n <- spr_number(SPR)
|
||
SPR(n) <- (RS)
|
||
```
|
||
|
||
## C Translation Example
|
||
|
||
```c
|
||
/* 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
|
||
|
||
**`mtspr`**
|
||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mtspr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:773`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L773)
|
||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:185`](../../../crates/sylpheed-ppc/src/opcode.rs#L185)
|
||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:925`](../../../crates/sylpheed-ppc/src/decoder.rs#L925)
|
||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||
|
||
```cpp
|
||
int InstrEmit_mtspr(PPCHIRBuilder& f, const InstrData& i) {
|
||
// n <- spr[5:9] || spr[0:4]
|
||
// if length(SPR(n)) = 64 then
|
||
// SPR(n) <- (RS)
|
||
// else
|
||
// SPR(n) <- (RS)[32:63]
|
||
|
||
Value* rt = f.LoadGPR(i.XFX.RT);
|
||
|
||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||
switch (n) {
|
||
case 1:
|
||
// XER
|
||
f.StoreXER(rt);
|
||
break;
|
||
case 8:
|
||
// LR
|
||
f.StoreLR(rt);
|
||
break;
|
||
case 9:
|
||
// CTR
|
||
f.StoreCTR(rt);
|
||
break;
|
||
case 256:
|
||
|
||
f.StoreContext(offsetof(PPCContext, vrsave), f.Truncate(rt, INT32_TYPE));
|
||
// VRSAVE
|
||
break;
|
||
default:
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
</details>
|
||
|
||
<!-- GENERATED: END -->
|
||
|
||
## Special Cases & Edge Conditions
|
||
|
||
- **SPR halves are swapped in the encoding.** As with [`mfspr`](mfspr.md), the 10-bit `spr` field stores the two 5-bit halves transposed. Software always names the *logical* SPR number; assemblers handle the swap. Decoded number `n = ((field & 0x1F) << 5) | ((field >> 5) & 0x1F)`.
|
||
- **SPRs writable from userspace (Xenon) — the only ones Canary implements.**
|
||
|
||
| Decoded # | Name | Effect |
|
||
| --- | --- | --- |
|
||
| 1 | XER | `StoreXER(RS)` |
|
||
| 8 | LR | `StoreLR(RS)` |
|
||
| 9 | CTR | `StoreCTR(RS)` |
|
||
| 256 | VRSAVE | low 32 bits of `RS` into `vrsave` |
|
||
|
||
- **Every other SPR is unimplemented in Canary.** SPRG0..3, HID0, HID1, DAR, DSISR and the rest are not swallowed: translating the `mtspr` logs "Unimplemented instr" and, with the default `break_on_unimplemented_instructions`, breaks.
|
||
- **Privileged SPRs.** On real hardware, writes to MSR-visible kernel SPRs (SPRG0..3, HID0/1, DSISR, DAR, PIR, etc.) require supervisor mode and trap from problem state. Canary does **not** enforce privilege — it implements only XER, LR, CTR and VRSAVE and treats `mtspr` to any other SPR as an unimplemented instruction.
|
||
- **Time-base writes are privileged.** `mtspr 268/269` (TBL/TBU) only works in supervisor mode on real hardware, and Canary treats them as unimplemented — do **not** assume the time base can be guest-written.
|
||
- **Simplified mnemonics.** `mtxer RS` ≡ `mtspr 1, RS`, `mtlr RS` ≡ `mtspr 8, RS`, `mtctr RS` ≡ `mtspr 9, RS`. These dominate Xbox 360 disassembly.
|
||
- **No CR / XER side effects.** `mtspr` itself doesn't record (the *target* SPR may itself be XER, in which case XER is being directly overwritten).
|
||
- **Not synchronising.** Canary's `tools/ppc-instructions.xml` omits the `sync` flag; PowerISA does require some `mtspr` cases (e.g. SDR1, MMU regs) to be context-synchronising — none of them appear in title binaries.
|
||
|
||
## Related Instructions
|
||
|
||
- [`mfspr`](mfspr.md) — inverse: read an SPR into a GPR.
|
||
- [`mftb`](mftb.md) — read time-base (preferred over `mfspr TBL/TBU`).
|
||
- [`mtmsr`](mtmsr.md), [`mtmsrd`](mtmsrd.md) — write MSR (separate opcode).
|
||
- [`mcrxr`](mcrxr.md) — sample-and-clear XER's overflow/carry bits.
|
||
|
||
### Simplified Mnemonics
|
||
|
||
| Simplified | Expansion |
|
||
| --- | --- |
|
||
| `mtxer RS` | `mtspr 1, RS` |
|
||
| `mtlr RS` | `mtspr 8, RS` |
|
||
| `mtctr RS` | `mtspr 9, RS` |
|
||
|
||
## IBM Reference
|
||
|
||
- [AIX 7.3 — `mtspr` (Move to Special Purpose Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtspr-move-special-purpose-register-instruction)
|
||
- PowerISA v2.07B, Book III §4 — SPR number table and privilege rules.
|