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:
179
migration/project-root/ppc-manual/control/mfspr.md
Normal file
179
migration/project-root/ppc-manual/control/mfspr.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# `mfspr` — Move from Special-Purpose Register
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XFX](../forms/XFX.md) · **Opcode:** `0x7c0002a6`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mfspr` | `mfspr` | — | Move from Special-Purpose Register |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mfspr [RD], [SPR]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mfspr` — form `XFX`
|
||||
|
||||
- **Opcode word:** `0x7c0002a6`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `339`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `SPR` | mfspr: read | 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). |
|
||||
| `RD` | mfspr: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mfspr`
|
||||
|
||||
- **Reads (always):** `SPR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- spr_number(SPR) ; SPR field has its two 5-bit halves swapped
|
||||
RT <- SPR(n)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* mfspr RT, SPR — SPR field has swapped halves */
|
||||
uint32_t n = ((insn.SPR & 0x1F) << 5) | ((insn.SPR >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1: r[insn.RT] = xer_pack(); break; /* XER */
|
||||
case 8: r[insn.RT] = lr; break; /* LR */
|
||||
case 9: r[insn.RT] = ctr; break; /* CTR */
|
||||
case 256: r[insn.RT] = vrsave; break; /* VRSAVE*/
|
||||
case 268: r[insn.RT] = tb & 0xFFFFFFFFu; break; /* TBL */
|
||||
case 269: r[insn.RT] = tb >> 32; break; /* TBU */
|
||||
default: r[insn.RT] = 0; break;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mfspr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfspr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:666`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L666)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:799`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L799)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1567-1595`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1567-L1595)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfspr => {
|
||||
let spr = instr.spr();
|
||||
ctx.gpr[instr.rd()] = match spr {
|
||||
crate::context::spr::XER => ctx.xer() as u64,
|
||||
crate::context::spr::LR => ctx.lr,
|
||||
crate::context::spr::CTR => ctx.ctr,
|
||||
crate::context::spr::DEC => ctx.dec as u64,
|
||||
crate::context::spr::TBL => ctx.timebase & 0xFFFF_FFFF,
|
||||
crate::context::spr::TBU => ctx.timebase >> 32,
|
||||
crate::context::spr::VRSAVE => ctx.vrsave as u64,
|
||||
// Xbox 360 Xenon processor signature (from canary).
|
||||
crate::context::spr::PVR => 0x0071_0800,
|
||||
// Benign SPRs — titles read these but we don't model them.
|
||||
crate::context::spr::SPRG0
|
||||
| crate::context::spr::SPRG1
|
||||
| crate::context::spr::SPRG2
|
||||
| crate::context::spr::SPRG3
|
||||
| crate::context::spr::HID0
|
||||
| crate::context::spr::HID1
|
||||
| crate::context::spr::DAR
|
||||
| crate::context::spr::DSISR
|
||||
| crate::context::spr::PIR => 0,
|
||||
_ => {
|
||||
tracing::warn!("mfspr: unimplemented SPR {}", spr);
|
||||
0
|
||||
}
|
||||
};
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## SPR Number Encoding — the "halves swap"
|
||||
|
||||
The 10-bit `spr` field in the XFX form is **stored in a transposed order**: the bits that software names the *high* half (bits 5..9 of the SPR number) occupy instruction bits **16..20**, and the *low* half (bits 0..4) occupies instruction bits **11..15**. Software (and this manual) always refers to the logical, unswapped SPR number.
|
||||
|
||||
```
|
||||
decoded_spr = ((field & 0x1F) << 5) | ((field >> 5) & 0x1F)
|
||||
```
|
||||
|
||||
So a programmer writing `mfspr RT, 8` (read LR) encodes `spr-field = 0x100` — *not* `8`. Assemblers handle this transparently; disassemblers reverse it. When writing a translator that parses raw instruction words, swap the halves explicitly.
|
||||
|
||||
## SPR Map (Xenon subset modelled by xenia)
|
||||
|
||||
| Decoded # | Name | Meaning | xenia-rs behaviour |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `XER` | Fixed-point exception register (CA / OV / SO + length field) | packed with `ctx.xer()` |
|
||||
| 8 | `LR` | Link register | `ctx.lr` |
|
||||
| 9 | `CTR` | Count register | `ctx.ctr` |
|
||||
| 18 | `DSISR` | Data-storage interrupt syndrome | returns 0 (stubbed) |
|
||||
| 19 | `DAR` | Data-access register | returns 0 (stubbed) |
|
||||
| 256 | `VRSAVE` | Vector-register save mask | `ctx.vrsave` |
|
||||
| 268 | `TBL` | Time-base lower 32 bits | `ctx.timebase & 0xFFFFFFFF` |
|
||||
| 269 | `TBU` | Time-base upper 32 bits | `ctx.timebase >> 32` |
|
||||
| 272–275 | `SPRG0..3` | Software scratch registers (kernel) | returns 0 (stubbed) |
|
||||
| 287 | `PVR` | Processor-version register | `0x00710800` (Xenon signature) |
|
||||
| 1008–1009 | `HID0/1` | Hardware implementation registers | returns 0 (stubbed) |
|
||||
| 1023 | `PIR` | Processor-ID register | returns 0 (stubbed) |
|
||||
|
||||
Unrecognised SPRs return 0 and log a warning. Games rarely read unmodelled SPRs; when they do it's usually clock-skew or sanity checks.
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Privilege.** Some SPRs are privileged on real hardware (MSR, HID0/1, SPRG0..3, DSISR, DAR, PIR). Xbox 360 titles run in a mixed privilege model under the hypervisor; xenia exposes all SPRs without a privilege check because the captured title binaries never contain a real privileged read that should trap.
|
||||
- **`LR` and `CTR` have dedicated simplified mnemonics.** Assemblers recognise `mflr RT` ≡ `mfspr RT, 8` and `mfctr RT` ≡ `mfspr RT, 9`. Similarly `mfxer RT` ≡ `mfspr RT, 1`. Disassemblers emit the simplified forms; the translation agent should map both forms to the same abstract operation.
|
||||
- **`mftb` vs. `mfspr TBL/TBU`.** Reading the time-base has a dedicated X-form variant [`mftb`](mftb.md) that uses a separate opcode. Post-Xbox-360 PowerISA deprecated `mfspr TBL/TBU`, but xenia accepts both. Prefer `mftb` in new translations.
|
||||
- **Side-effect-free.** `mfspr` has no effect on any register beyond `RT`. It can be freely reordered with non-SPR-touching instructions.
|
||||
- **No `Rc` / `OE`.** This is an XFX-form instruction; bit 31 is reserved (0).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mtspr`](mtspr.md) — the inverse; write a GPR to an SPR.
|
||||
- [`mftb`](mftb.md) — read time-base (preferred over `mfspr TBL/TBU`).
|
||||
- [`mflr`](mfspr.md), [`mfctr`](mfspr.md), [`mfxer`](mfspr.md) — simplified mnemonics of this instruction.
|
||||
- [`mcrxr`](mcrxr.md) — move `XER[SO..CA]` to a CR field and clear them.
|
||||
|
||||
## Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion |
|
||||
| --- | --- |
|
||||
| `mfxer RT` | `mfspr RT, 1` |
|
||||
| `mflr RT` | `mfspr RT, 8` |
|
||||
| `mfctr RT` | `mfspr RT, 9` |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mfspr` (Move from Special Purpose Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfspr-move-from-special-purpose-register-instruction)
|
||||
- [PowerISA v2.07B — SPR number table and privilege rules](https://openpowerfoundation.org/specifications/isa/)
|
||||
Reference in New Issue
Block a user