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:
118
migration/project-root/ppc-manual/memory/dcbf.md
Normal file
118
migration/project-root/ppc-manual/memory/dcbf.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# `dcbf` — Data Cache Block Flush
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0000ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbf` | `dcbf` | — | Data Cache Block Flush |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbf [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbf` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000ac`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `86`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbf: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbf: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbf`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbf`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbf"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1125`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1125)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:773`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L773)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Flush = write-back + invalidate.** If the addressed line is dirty in the data cache, it is written to memory; whether dirty or clean, the line is then removed from the cache. Subsequent loads must refill from memory.
|
||||
- **Cache line size.** Xenon's L1/L2 lines are **128 bytes**. The hardware ignores the low seven bits of `EA`, so `dcbf RA, RB` flushes the line containing `EA` regardless of where in that line `EA` lies. There is no `dcbf128` variant — the hint is sized to the architectural line.
|
||||
- **`RA0` semantics.** When `RA = 0`, the base is the literal zero — `dcbf 0, RB` flushes the line containing address `RB`. The instruction has no destination register.
|
||||
- **Xenia models a no-op.** Xenia-rs's emulator does not maintain a coherent cache model; the decode entry exists but the interpreter typically advances PC without further effect, since target memory is always coherent on the host. This is correct behaviour for an emulator.
|
||||
- **Unprivileged.** `dcbf` is a problem-state instruction — usable from user code. Storage protection still applies; flushing an unmapped page raises a DSI exception.
|
||||
- **Pair with `sync`.** Hardware `dcbf` does not by itself impose ordering; software that needs the flushed data visible to other masters (DMA, GPU) issues a [`sync`](sync.md) afterwards.
|
||||
- **Self-modifying code companion.** When patching code, the recipe is `dcbst` (push dirty data through to memory) → `sync` → [`icbi`](icbi.md) (invalidate I-cache) → [`isync`](isync.md). `dcbf` is the heavier alternative when the writer also wants the line out of D-cache.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbst`](dcbst.md) — write-back without invalidate (lighter than `dcbf`).
|
||||
- [`dcbi`](dcbi.md) — invalidate without write-back (privileged; loses dirty data).
|
||||
- [`dcbt`](dcbt.md), [`dcbtst`](dcbtst.md) — touch hints to bring lines in.
|
||||
- [`dcbz`](dcbz.md), `dcbz128` — allocate-and-zero a line.
|
||||
- [`icbi`](icbi.md) — instruction-cache invalidate, used together for self-modifying code.
|
||||
- [`sync`](sync.md) — full memory barrier, typically follows `dcbf`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbf` (Data Cache Block Flush)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbf-data-cache-block-flush-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Storage Control Instructions" for cache-coherence semantics.
|
||||
117
migration/project-root/ppc-manual/memory/dcbi.md
Normal file
117
migration/project-root/ppc-manual/memory/dcbi.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# `dcbi` — Data Cache Block Invalidate
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0003ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbi` | `dcbi` | — | Data Cache Block Invalidate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbi [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbi` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0003ac`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `470`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbi: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbi: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbi`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:811`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L811)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Privileged.** Unlike `dcbf` and `dcbst`, `dcbi` is supervisor-only. Executing in problem state raises a privileged-instruction (program) interrupt. Game code never issues `dcbi` directly; only the kernel.
|
||||
- **Drops dirty data.** The line is removed from cache **without** writing back, so any modifications that have not already been pushed to memory are lost. Used only when the underlying memory is being repurposed (e.g. DMA window flip, page demap) and stale dirty data would be incorrect.
|
||||
- **Cache line size.** Xenon lines are 128 bytes. The low seven bits of `EA` are ignored — the operation targets the cache line that contains `EA`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero, so `dcbi 0, RB` invalidates the line containing address `RB`.
|
||||
- **Xenia treats it as a no-op.** With no modelled cache, the emulator decodes and advances PC; memory is already authoritative.
|
||||
- **Sequencing.** Not synchronising. Pair with [`sync`](sync.md) when invalidation must precede a subsequent load on another thread.
|
||||
- **Architecturally subsumed by `dcbf` for problem state.** Userspace that wants "this line is no longer valuable" must use [`dcbf`](dcbf.md), accepting the write-back cost.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbf`](dcbf.md) — flush (write-back + invalidate); the unprivileged alternative.
|
||||
- [`dcbst`](dcbst.md) — write-back without invalidate.
|
||||
- [`dcbz`](dcbz.md), `dcbz128` — allocate-and-zero a line.
|
||||
- [`dcbt`](dcbt.md), [`dcbtst`](dcbtst.md) — prefetch hints.
|
||||
- [`icbi`](icbi.md) — instruction-cache analog (also problem-state, not privileged).
|
||||
- [`sync`](sync.md), [`isync`](isync.md) — pair with cache-control ops for ordering.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbi` (Data Cache Block Invalidate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbi-data-cache-block-invalidate-instruction)
|
||||
- `PowerISA v2.07B Book II` for the privilege model and cache-coherence rules.
|
||||
118
migration/project-root/ppc-manual/memory/dcbst.md
Normal file
118
migration/project-root/ppc-manual/memory/dcbst.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# `dcbst` — Data Cache Block Store
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00006c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbst` | `dcbst` | — | Data Cache Block Store |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbst [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbst` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00006c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `54`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbst: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbst: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbst`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbst`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbst"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1134`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1134)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:765`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L765)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Write-through, no invalidate.** If the addressed line is dirty, it is written back to memory; the line itself remains in the cache (clean afterwards). Lighter than `dcbf` — the cache stays warm.
|
||||
- **Cache line size.** Xenon's line is 128 bytes; the low seven bits of `EA` are ignored. There is no `dcbst128`; the operation is sized to the architectural line.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero as base. `dcbst 0, RB` pushes the line containing address `RB` to memory.
|
||||
- **Self-modifying code stage 1.** The canonical "patch then run" sequence is `stw` (modify) → `dcbst` (push dirty data to memory) → [`sync`](sync.md) → [`icbi`](icbi.md) (invalidate I-cache for the same address) → [`isync`](isync.md). `dcbst` is preferred over `dcbf` here because it leaves the data in D-cache for any subsequent normal reads.
|
||||
- **DMA hand-off.** Used before initiating a GPU or DMA read of a buffer the CPU has just written, to ensure memory holds the latest data.
|
||||
- **Unprivileged.** Available from problem state.
|
||||
- **Xenia models as no-op.** No cache state is simulated; PC advances and memory is already authoritative.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbf`](dcbf.md) — flush + invalidate (heavier alternative).
|
||||
- [`dcbi`](dcbi.md) — invalidate without write-back (privileged).
|
||||
- [`dcbz`](dcbz.md), `dcbz128` — allocate-and-zero.
|
||||
- [`dcbt`](dcbt.md), [`dcbtst`](dcbtst.md) — prefetch hints.
|
||||
- [`icbi`](icbi.md) — instruction-cache invalidate, sequenced after `dcbst` in self-modifying-code recipes.
|
||||
- [`sync`](sync.md), [`isync`](isync.md) — ordering primitives that bracket cache control.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbst` (Data Cache Block Store)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbst-data-cache-block-store-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Storage Control Instructions".
|
||||
117
migration/project-root/ppc-manual/memory/dcbt.md
Normal file
117
migration/project-root/ppc-manual/memory/dcbt.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# `dcbt` — Data Cache Block Touch
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00022c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbt` | `dcbt` | — | Data Cache Block Touch |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbt [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbt` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00022c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `278`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbt: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbt: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbt`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbt`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbt"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1142`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1142)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:794`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L794)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Hint, not a guarantee.** `dcbt` requests that the cache line containing `EA` be brought into L1 in anticipation of a future load. The processor is free to ignore the hint (e.g. under cache pressure or for non-cacheable storage).
|
||||
- **Read-intent prefetch.** Pair-mate of [`dcbtst`](dcbtst.md) (which signals write intent and may prefer an exclusive cache state). Use `dcbt` when the next access is a read.
|
||||
- **No exception on bad address.** Unlike a real load, `dcbt` to an unmapped or protected page does not raise; the hint is silently dropped. This makes it safe to "speculatively" prefetch one line past the end of a buffer.
|
||||
- **Cache line size.** Xenon line is 128 bytes; low seven bits of `EA` are ignored.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero — `dcbt 0, RB` prefetches the line containing address `RB`.
|
||||
- **Stream-engine hints.** The Xenon supports up to four hardware data-streams set up by sequences of `dcbt` with a stride; refer to the XDK for the stream-engine encoding (uses bits ignored by the architectural decode).
|
||||
- **Xenia treats as no-op.** Hints have no observable effect under the emulated memory model.
|
||||
- **Unprivileged.** Always available.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbtst`](dcbtst.md) — write-intent prefetch.
|
||||
- [`dcbf`](dcbf.md), [`dcbst`](dcbst.md), [`dcbi`](dcbi.md) — push / invalidate counterparts.
|
||||
- [`dcbz`](dcbz.md), `dcbz128` — allocate-and-zero (a stronger "I want this line" signal).
|
||||
- [`icbi`](icbi.md) — instruction-cache analog (no instruction-cache prefetch in PowerPC).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbt` (Data Cache Block Touch)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbt-data-cache-block-touch-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Storage Control Instructions" for hint semantics.
|
||||
116
migration/project-root/ppc-manual/memory/dcbtst.md
Normal file
116
migration/project-root/ppc-manual/memory/dcbtst.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# `dcbtst` — Data Cache Block Touch for Store
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0001ec`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbtst` | `dcbtst` | — | Data Cache Block Touch for Store |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbtst [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbtst` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0001ec`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `246`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbtst: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbtst: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbtst`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbtst`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbtst"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1150`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1150)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:792`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L792)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Hint, not a guarantee.** `dcbtst` requests the addressed cache line be brought into L1 in anticipation of a future **store**. Hardware may treat this as a hint to fetch in an exclusive coherence state to avoid a follow-up upgrade.
|
||||
- **Pair of [`dcbt`](dcbt.md).** `dcbt` signals read intent; `dcbtst` signals write intent. Use `dcbtst` before a planned store loop to avoid stalling on cache-line acquisition.
|
||||
- **No exception on bad address.** Like `dcbt`, prefetch hints to unmapped or protected pages are silently dropped — no DSI exception. Safe to issue speculatively.
|
||||
- **Cache line size.** Xenon line is 128 bytes; the low seven bits of `EA` are ignored.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero — `dcbtst 0, RB` prefetches the line containing address `RB`.
|
||||
- **Often replaced by `dcbz128`.** When code knows it will write the **entire** line, `dcbz128` is preferable: it allocates the line and zeros it without reading from memory at all, beating `dcbtst` + first-store.
|
||||
- **Xenia treats as no-op.** Hints have no observable effect under the emulated memory model.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbt`](dcbt.md) — read-intent prefetch.
|
||||
- [`dcbz`](dcbz.md), `dcbz128` — allocate-and-zero (skip the read entirely when writing the whole line).
|
||||
- [`dcbf`](dcbf.md), [`dcbst`](dcbst.md), [`dcbi`](dcbi.md) — push / invalidate counterparts.
|
||||
- [`icbi`](icbi.md) — instruction-cache invalidate.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbtst` (Data Cache Block Touch for Store)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbtst-data-cache-block-touch-store-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Storage Control Instructions".
|
||||
185
migration/project-root/ppc-manual/memory/dcbz.md
Normal file
185
migration/project-root/ppc-manual/memory/dcbz.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# `dcbz` — Data Cache Block Clear to Zero
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [DCBZ](../forms/DCBZ.md) · **Opcode:** `0x7c0007ec`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `dcbz` | `dcbz` | — | Data Cache Block Clear to Zero |
|
||||
| `dcbz128` | `dcbz128` | — | Data Cache Block Clear to Zero 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
dcbz [RA0], [RB]
|
||||
dcbz128 [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `dcbz` — form `DCBZ`
|
||||
|
||||
- **Opcode word:** `0x7c0007ec`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `1014`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `—` | reserved |
|
||||
| 11–15 | `RA` | base register (0 ⇒ literal 0) |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–30 | `XO` | extended opcode (1014 for dcbz / 1010 for dcbz128) |
|
||||
| 31 | `—` | reserved |
|
||||
|
||||
### `dcbz128` — form `DCBZ`
|
||||
|
||||
- **Opcode word:** `0x7c2007ec`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `1014`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `—` | reserved |
|
||||
| 11–15 | `RA` | base register (0 ⇒ literal 0) |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–30 | `XO` | extended opcode (1014 for dcbz / 1010 for dcbz128) |
|
||||
| 31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | dcbz: read; dcbz128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | dcbz: read; dcbz128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `dcbz`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `dcbz128`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`dcbz`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbz"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1159`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1159)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:886`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L886)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1694-1705`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1694-L1705)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::dcbz => {
|
||||
// Zero 32 bytes at effective address
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) as u32) & !31;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
for i in 0..8 {
|
||||
mem.write_u32(ea + i * 4, 0);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`dcbz128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="dcbz128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1171`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1171)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:19`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L19)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:887`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L887)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1706-1717`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1706-L1717)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::dcbz128 => {
|
||||
// Zero 128 bytes
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) as u32) & !127;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
for i in 0..32 {
|
||||
mem.write_u32(ea + i * 4, 0);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Cache-line size mismatch.** Stock PowerPC `dcbz` zeroes one architectural cache line — 32 bytes on classic POWER, but the **Xenon's L1 line is 128 bytes**. Microsoft added `dcbz128` (encoded with bit-9 set so `RT` field reads as `1`) to clear a true Xenon line in one instruction. Most Xbox 360 code therefore emits `dcbz128`; a stray `dcbz` only zeroes 32 bytes and silently leaves the rest of the line uncleared.
|
||||
- **Alignment is forced via mask.** The effective address is masked by `~31` (`dcbz`) or `~127` (`dcbz128`) before writing — the low bits are dropped, not validated. Calling `dcbz r0, r3` with `r3 = 0x10037` writes zeros to `0x10000..0x1007F`, not `0x10037..0x100B6`.
|
||||
- **No memory read; pure write.** Real hardware allocates the line in cache and may skip a read-from-memory fill ("cache-line zero" optimisation). Xenia simulates the architectural effect — 32 (or 128) bytes of zero in target memory — without modelling cache state.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero as the base, so `dcbz128 0, RB` zeros the line containing address `RB`. The update form does not exist for cache-control instructions.
|
||||
- **Block-fill idiom.** Compilers and hand-written copy loops pair `dcbz128` with `stvx` / `stw` sequences to avoid the cache-line read-allocate that a cold store would trigger. Skipping the read is the entire point.
|
||||
- **Privilege.** `dcbz` is unprivileged (problem-state); does not require supervisor mode. It can fault on protection or unmapped memory like an ordinary store.
|
||||
- **Sequencing.** Not synchronising. Pair with [`sync`](sync.md) / [`lwsync`](sync.md) when the zeros must be visible before subsequent loads on another thread.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbf`](dcbf.md) — flush a line back to memory.
|
||||
- [`dcbst`](dcbst.md) — store-through (write-back without invalidate).
|
||||
- [`dcbi`](dcbi.md) — invalidate (privileged on most cores).
|
||||
- [`dcbt`](dcbt.md), [`dcbtst`](dcbtst.md) — touch / touch-for-store hints.
|
||||
- [`icbi`](icbi.md) — instruction-cache invalidate (companion to data-cache control).
|
||||
- [`stvx`](stvx.md), [`stw`](stw.md) — typical pair-mates in block-fill loops.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `dcbz` (Data Cache Block Set to Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-dcbz-data-cache-block-set-zero-instruction)
|
||||
- Microsoft Xbox 360 XDK / `Xenon Programming Guide` — for `dcbz128` specifics; `PowerISA v2.07B Book II` § "Storage Control Instructions" for the architectural baseline.
|
||||
117
migration/project-root/ppc-manual/memory/icbi.md
Normal file
117
migration/project-root/ppc-manual/memory/icbi.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# `icbi` — Instruction Cache Block Invalidate
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0007ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `icbi` | `icbi` | — | Instruction Cache Block Invalidate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
icbi [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `icbi` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0007ac`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `982`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | icbi: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | icbi: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `icbi`
|
||||
|
||||
- **Reads (always):** `RA0`, `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
|
||||
|
||||
**`icbi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="icbi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1183`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1183)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:32`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L32)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:850`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L850)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Self-modifying code primitive.** Removes the line containing `EA` from the instruction cache so a subsequent fetch reads from memory. Required after writing new instructions because the I-cache is not coherent with the D-cache or with main memory.
|
||||
- **Standard recipe.** The full sequence is: `stw` (write new code) → [`dcbst`](dcbst.md) (push dirty data through D-cache to memory) → [`sync`](sync.md) (wait for memory) → `icbi` (drop stale I-cache line) → [`isync`](isync.md) (drain prefetch / refetch). Skipping any of these can leave the CPU executing stale instructions.
|
||||
- **Cache line size.** Xenon's I-cache line is 128 bytes; the low seven bits of `EA` are ignored.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is the literal zero. `icbi 0, RB` invalidates the line containing address `RB`.
|
||||
- **Unprivileged.** `icbi` is problem-state, unlike its data-side cousin [`dcbi`](dcbi.md).
|
||||
- **No exception on bad address.** Treated as a hint at the hardware level — invalidating an absent line is harmless.
|
||||
- **Per-thread effect.** On the multithreaded Xenon core, `icbi` propagates across hardware threads sharing the same L1 I-cache; cross-core invalidation requires bus broadcast handled implicitly by the cache coherence protocol.
|
||||
- **Xenia models as no-op.** No I-cache is simulated; rebuilds of generated code (when applicable) are triggered by the JIT cache-watcher, not by `icbi` itself.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`dcbst`](dcbst.md) — D-cache write-back (paired step before `icbi`).
|
||||
- [`dcbf`](dcbf.md), [`dcbi`](dcbi.md) — D-cache push / invalidate.
|
||||
- [`isync`](isync.md) — instruction-stream barrier (paired step after `icbi`).
|
||||
- [`sync`](sync.md) — full memory barrier between `dcbst` and `icbi`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `icbi` (Instruction Cache Block Invalidate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-icbi-instruction-cache-block-invalidate-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Instruction Storage" for the canonical self-modifying-code sequence.
|
||||
248
migration/project-root/ppc-manual/memory/lbz.md
Normal file
248
migration/project-root/ppc-manual/memory/lbz.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# `lbz` — Load Byte and Zero
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x88000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lbz` | `lbz` | — | Load Byte and Zero |
|
||||
| `lbzu` | `lbzu` | — | Load Byte and Zero with Update |
|
||||
| `lbzux` | `lbzux` | — | Load Byte and Zero with Update Indexed |
|
||||
| `lbzx` | `lbzx` | — | Load Byte and Zero Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lbz [RD], [d]([RA0])
|
||||
lbzu [RD], [d]([RA])
|
||||
lbzux [RD], [RA], [RB]
|
||||
lbzx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lbz` — form `D`
|
||||
|
||||
- **Opcode word:** `0x88000000`
|
||||
- **Primary opcode (bits 0–5):** `34`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lbzu` — form `D`
|
||||
|
||||
- **Opcode word:** `0x8c000000`
|
||||
- **Primary opcode (bits 0–5):** `35`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lbzux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000ee`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `119`
|
||||
- **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 |
|
||||
|
||||
### `lbzx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `87`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lbz: read; lbzx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lbz: read; lbzu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RD` | lbz: write; lbzu: write; lbzux: write; lbzx: write | Destination GPR. |
|
||||
| `RA` | lbzu: read; lbzu: write; lbzux: read; lbzux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lbzux: read; lbzx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lbz`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lbzu`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lbzux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lbzx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- 0x00000000_000000_00 || MEM(EA, 1)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`lbz`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lbz"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:72`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L72)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:34`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L34)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:357`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L357)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1024-1029`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1024-L1029)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lbz => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u8(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lbzu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lbzu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:92`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L92)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:34`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L34)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:358`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L358)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1030-1035`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1030-L1035)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lbzu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u8(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lbzux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lbzux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:104`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L104)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:34`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L34)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:776`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L776)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1042-1047`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1042-L1047)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lbzux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u8(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lbzx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lbzx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:115`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L115)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:34`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L34)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:774`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L774)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1036-1041`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1036-L1041)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lbzx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u8(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single-byte read.** The smallest scalar load. No endian concerns at the byte level — `MEM(EA, 1)` returns the literal byte at address `EA`, regardless of host or target byte order.
|
||||
- **Zero-extension to 64 bits.** The high 56 bits of `RT` become zero. Use [`lha`](lha.md) / [`lhax`](lha.md) family for sign-extending byte-equivalent semantics; there is no PowerPC "load byte sign-extended" — you must `lbz` then `extsb` (or use `lha` on a half).
|
||||
- **`RA0` (non-update forms).** When `RA = 0` in `lbz` / `lbzx`, the base is the literal zero, so `lbz RT, 0x4000(0)` reads from absolute address `0x4000`. Update forms `lbzu` / `lbzux` invoke `RA = 0` (and `RA = RT`) as invalid forms; xenia's interpreter does not check, so well-formed compiler output is assumed.
|
||||
- **Update-form post-write.** `lbzu` / `lbzux` write the computed `EA` back to `RA` after the load; the snapshot first reads, then assigns `RA ← EA`, matching IBM's "the load and update happen as one operation" wording.
|
||||
- **No alignment requirement.** A byte load is intrinsically aligned. Xenon does not raise alignment exceptions for any byte access.
|
||||
- **Common in string and table-lookup code.** Most uses are character-string scans, jump-table dispatches, and packed-bool reads. Compilers also use `lbz` to materialise small immediate constants stored in `.rodata`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lhz`](lhz.md), [`lwz`](lwz.md), [`ld`](ld.md) — wider zero-extending loads in the same family.
|
||||
- [`lha`](lha.md), [`lwa`](lwa.md) — sign-extending siblings (no `lba` exists; use `lbz` + `extsb`).
|
||||
- [`stb`](stb.md), [`stbu`](stb.md), [`stbx`](stb.md), [`stbux`](stb.md) — the corresponding stores.
|
||||
- [`lwbrx`](lwbrx.md), [`lhbrx`](lhbrx.md) — byte-reversed multi-byte loads (no byte-equivalent needed).
|
||||
- [`lmw`](lmw.md), [`lswi`](lswi.md), [`lswx`](lswx.md) — multi-word / string loads for bulk transfer.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lbz` (Load Byte and Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lbz-load-byte-zero-instruction)
|
||||
- [AIX 7.3 — `lbzu` (Load Byte and Zero with Update)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lbzu-load-byte-zero-update-instruction)
|
||||
251
migration/project-root/ppc-manual/memory/ld.md
Normal file
251
migration/project-root/ppc-manual/memory/ld.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# `ld` — Load Doubleword
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [DS](../forms/DS.md) · **Opcode:** `0xe8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `ld` | `ld` | — | Load Doubleword |
|
||||
| `ldu` | `ldu` | — | Load Doubleword with Update |
|
||||
| `ldux` | `ldux` | — | Load Doubleword with Update Indexed |
|
||||
| `ldx` | `ldx` | — | Load Doubleword Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
ld [RD], [ds]([RA0])
|
||||
ldu [RD], [ds]([RA])
|
||||
ldux [RD], [RA], [RB]
|
||||
ldx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `ld` — form `DS`
|
||||
|
||||
- **Opcode word:** `0xe8000000`
|
||||
- **Primary opcode (bits 0–5):** `58`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0) |
|
||||
| 16–29 | `DS` | 14-bit signed word-scaled displacement |
|
||||
| 30–31 | `XO` | extended opcode |
|
||||
|
||||
### `ldu` — form `DS`
|
||||
|
||||
- **Opcode word:** `0xe8000001`
|
||||
- **Primary opcode (bits 0–5):** `58`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0) |
|
||||
| 16–29 | `DS` | 14-bit signed word-scaled displacement |
|
||||
| 30–31 | `XO` | extended opcode |
|
||||
|
||||
### `ldux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00006a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `53`
|
||||
- **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 |
|
||||
|
||||
### `ldx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00002a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `21`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | ld: read; ldx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `ds` | ld: read; ldu: read | 14-bit signed word-aligned displacement (`DS << 2`). |
|
||||
| `RD` | ld: write; ldu: write; ldux: write; ldx: write | Destination GPR. |
|
||||
| `RA` | ldu: read; ldu: write; ldux: read; ldux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | ldux: read; ldx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `ld`
|
||||
|
||||
- **Reads (always):** `RA0`, `ds`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `ldu`
|
||||
|
||||
- **Reads (always):** `RA`, `ds`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `ldux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `ldx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(ds || 0b00)
|
||||
RT <- MEM(EA, 8)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`ld`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ld"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:347`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L347)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:380`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L380)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1096-1101`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1096-L1101)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ld => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.ds() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u64(ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`ldu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ldu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:367`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L367)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:381`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L381)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1126-1131`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1126-L1131)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ldu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.ds() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u64(ea);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`ldux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ldux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:378`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L378)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:764`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L764)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1132-1137`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1132-L1137)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ldux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u64(ea);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`ldx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ldx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:389`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L389)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:755`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L755)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1102-1107`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1102-L1107)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ldx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u64(ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **DS-form, not D-form.** The displacement is 14 bits scaled by 4 (`EXTS(ds || 0b00)`), giving a signed range of ±32 KiB in 4-byte steps. Bits 30–31 are the extended opcode used to distinguish `ld` (XO=0) from `ldu` (XO=1). The assembler accepts a normal byte displacement and verifies divisibility by 4.
|
||||
- **Big-endian read.** The 64 bits at `EA..EA+7` form the loaded value, most-significant byte first. Xenia-rs's `mem.read_u64` returns the host-native value of that big-endian doubleword.
|
||||
- **No zero/sign-extension question.** `ld` already fills the entire 64-bit register; there is no `lda` (load doubleword algebraic) — the doubleword is the architectural maximum.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `ld` and `ldx` means base is literal zero. `ld RT, 0x100(0)` reads from absolute `0x100`.
|
||||
- **Update-form invalid forms.** `ldu` / `ldux` invoke "RA = 0" and "RA = RT" as invalid forms. AIX docs say results are undefined; xenia performs the read first, then writes back `RA ← EA`, which would silently destroy the loaded value if `RA == RT`.
|
||||
- **Alignment.** Xenon does not enforce doubleword alignment for `ld` itself — unaligned 8-byte loads are tolerated. However, real POWER cores may take an alignment exception on some implementations; portable code keeps doublewords 8-byte aligned.
|
||||
- **64-bit pointer / counter loads.** Although Xbox 360 user code is 32-bit, kernel structures and TOC entries are doublewords; `ld` is the standard load for them.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwz`](lwz.md), [`lhz`](lhz.md), [`lbz`](lbz.md) — narrower zero-extending loads.
|
||||
- [`lwa`](lwa.md), [`lha`](lha.md) — sign-extending loads (no `lda` exists; `ld` already fills the register).
|
||||
- [`ldbrx`](ldbrx.md) — byte-reversed doubleword load.
|
||||
- [`ldarx`](ldarx.md) / [`stdcx`](stdcx.md) — load-reserve / store-conditional doubleword pair.
|
||||
- [`std`](std.md), [`stdu`](std.md), [`stdx`](std.md), [`stdux`](std.md) — corresponding stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `ld` (Load Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ld-load-doubleword-instruction)
|
||||
- [AIX 7.3 — `ldu` / `ldux` / `ldx`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ldu-load-doubleword-update-instruction)
|
||||
138
migration/project-root/ppc-manual/memory/ldarx.md
Normal file
138
migration/project-root/ppc-manual/memory/ldarx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `ldarx` — Load Doubleword and Reserve Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0000a8`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `ldarx` | `ldarx` | — | Load Doubleword and Reserve Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
ldarx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `ldarx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000a8`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `84`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | ldarx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | ldarx: read | Source GPR. |
|
||||
| `RD` | ldarx: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `ldarx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **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
|
||||
|
||||
**`ldarx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ldarx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:765`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L765)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:772`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L772)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4559-4573`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4559-L4573)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ldarx => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
let val = mem.read_u64(ea);
|
||||
ctx.gpr[instr.rd()] = val;
|
||||
ctx.reserved_line = ea & !RESERVATION_MASK;
|
||||
ctx.reserved_val = val;
|
||||
ctx.has_reservation = true;
|
||||
ctx.reservation_width = 8; // PPCBUG-151: doubleword reservation
|
||||
if let Some(t) = &ctx.reservation_table {
|
||||
if t.is_enabled() {
|
||||
ctx.reserved_generation = t.reserve(ea, ctx.hw_id);
|
||||
}
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Reservation set.** Loads the doubleword at `EA` and atomically establishes a *reservation* on that address. A subsequent [`stdcx`](stdcx.md) at the same address completes only if the reservation is still valid. Together they form a standard load-linked / store-conditional pair for lock-free updates.
|
||||
- **One reservation per thread.** Xenia tracks `reserved_addr` / `reserved_val` / `has_reservation` per-context (see snapshot). Hardware behaves the same: each hardware thread holds at most one reservation at a time. A new `ldarx` (or `lwarx`) discards the prior reservation.
|
||||
- **Granule.** Architecturally the reservation covers a single naturally-aligned doubleword (8 bytes). On Xenon the practical reservation granule is one **cache line** (128 bytes) — any store to that line by another agent loses the reservation. Xenia simplifies to per-address tracking.
|
||||
- **Alignment requirement.** `EA` must be 8-byte aligned. An unaligned `ldarx` raises an alignment exception on hardware. Xenia does not check; pass aligned addresses.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero — `ldarx RT, 0, RB` reads at exact `RB`. Used in synthetic-zero atomic-init idioms, but rare.
|
||||
- **Reservation-loss events.** Any exception, context switch, or store by another thread to the reserved line clears the reservation. Application code must treat the `stdcx` failure as a normal retry condition, not as an error.
|
||||
- **Pair atomically.** Code must be `ldarx ... do work ... stdcx.` with no intervening loads or stores that could be re-ordered. Optionally fence with [`lwsync`](sync.md) inside the loop. The conditional store sets `CR0[EQ]` to report success.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stdcx`](stdcx.md) — store-conditional doubleword (the matching half of the pair).
|
||||
- [`lwarx`](lwarx.md) / [`stwcx`](stwcx.md) — 32-bit reservation pair.
|
||||
- [`ld`](ld.md), [`ldx`](ld.md) — non-reserving doubleword loads.
|
||||
- [`sync`](sync.md), [`lwsync`](sync.md) — barriers commonly placed around reservation pairs.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `ldarx` (Load Doubleword and Reserve Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ldarx-load-double-word-reserve-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Atomic Update Primitives" for full reservation semantics and granule rules.
|
||||
128
migration/project-root/ppc-manual/memory/ldbrx.md
Normal file
128
migration/project-root/ppc-manual/memory/ldbrx.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# `ldbrx` — Load Doubleword Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000428`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `ldbrx` | `ldbrx` | — | Load Doubleword Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
ldbrx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `ldbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000428`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `532`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | ldbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | ldbrx: read | Source GPR. |
|
||||
| `RD` | ldbrx: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `ldbrx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **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
|
||||
|
||||
**`ldbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ldbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:654`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L654)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:36`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L36)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:816`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L816)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4627-4631`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4627-L4631)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ldbrx => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.gpr[instr.rd()] = mem.read_u64(ea).swap_bytes();
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Reads little-endian.** `ldbrx` loads 8 bytes and reverses byte order before placing them in `RT`. With Xenon's PowerPC big-endian world view, the architectural effect is "load a little-endian doubleword as if it were big-endian" — useful when consuming network buffers, file headers (PNG IHDR, ZIP CRC32, etc.), or PC-side data structures that store little-endian.
|
||||
- **Implementation detail.** The xenia snapshot calls `mem.read_u64(ea).swap_bytes()`. `read_u64` already returns the host-native value of the big-endian doubleword at `EA`; `swap_bytes` then flips it, giving the little-endian interpretation. Equivalent to four sequential `lbz` plus shifts, but issued as one micro-op.
|
||||
- **No update form, X-form only.** PowerPC byte-reverse loads come in indexed form only (no `ldbrxu` or DS-form). `EA = (RA|0) + RB`. To increment a pointer, fold the increment into `RB` or use a separate `addi`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is the literal zero; `ldbrx RT, 0, RB` reads at exact `RB`.
|
||||
- **Alignment.** Like the rest of the byte-reverse family, `ldbrx` does **not** require natural alignment on hardware; the load is done as eight byte reads internally. Xenon may take an alignment exception on cache-inhibited storage.
|
||||
- **No corresponding sign-extension.** The output is the literal byte-reversed bit pattern; it occupies the full 64-bit register. Use shifts or `extsw`/`extsh` afterwards if a sign-extended narrower datum is desired.
|
||||
- **Pair with [`stdbrx`](stdbrx.md).** The store side performs the inverse: takes the GPR value, reverses, writes 8 bytes.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stdbrx`](stdbrx.md) — store doubleword byte-reverse indexed.
|
||||
- [`lwbrx`](lwbrx.md), [`lhbrx`](lhbrx.md) — narrower byte-reverse loads (word, halfword).
|
||||
- [`stwbrx`](stwbrx.md), [`sthbrx`](sthbrx.md) — narrower byte-reverse stores.
|
||||
- [`ld`](ld.md), [`ldx`](ld.md) — non-reversing doubleword loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `ldbrx` (Load Doubleword Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ldbrx-load-double-word-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
248
migration/project-root/ppc-manual/memory/lfd.md
Normal file
248
migration/project-root/ppc-manual/memory/lfd.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# `lfd` — Load Floating-Point Double
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xc8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lfd` | `lfd` | — | Load Floating-Point Double |
|
||||
| `lfdu` | `lfdu` | — | Load Floating-Point Double with Update |
|
||||
| `lfdux` | `lfdux` | — | Load Floating-Point Double with Update Indexed |
|
||||
| `lfdx` | `lfdx` | — | Load Floating-Point Double Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lfd [FD], [d]([RA0])
|
||||
lfdu [FD], [d]([RA])
|
||||
lfdux [FD], [RA], [RB]
|
||||
lfdx [FD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lfd` — form `D`
|
||||
|
||||
- **Opcode word:** `0xc8000000`
|
||||
- **Primary opcode (bits 0–5):** `50`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lfdu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xcc000000`
|
||||
- **Primary opcode (bits 0–5):** `51`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lfdux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0004ee`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `631`
|
||||
- **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 |
|
||||
|
||||
### `lfdx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0004ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `599`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lfd: read; lfdx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lfd: read; lfdu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `FD` | lfd: write; lfdu: write; lfdux: write; lfdx: write | Destination floating-point register. |
|
||||
| `RA` | lfdu: read; lfdu: write; lfdux: read; lfdux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lfdux: read; lfdx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lfd`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfdu`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfdux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfdx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
FRT <- MEM(EA, 8)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`lfd`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfd"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:912`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L912)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:373`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L373)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1152-1157`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1152-L1157)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfd => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f64(ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfdu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfdu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:925`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L925)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:374`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L374)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1176-1181`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1176-L1181)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfdu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f64(ea);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfdux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfdux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:936`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L936)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:827`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L827)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1182-1187`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1182-L1187)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfdux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f64(ea);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:947`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L947)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:826`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L826)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1158-1163`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1158-L1163)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfdx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f64(ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Bit-exact double load.** Reads 8 bytes and places them directly into `FRT` as IEEE-754 binary64. No format conversion is performed (contrast `lfs`, which expands single→double).
|
||||
- **No FPSCR side effects.** `lfd` cannot raise IEEE exceptions: it neither rounds nor inspects the value. A signalling NaN read this way stays a signalling NaN until it is consumed by an arithmetic op.
|
||||
- **`RA0` semantics.** In the non-update forms (`lfd`, `lfdx`), `RA = 0` selects literal zero — `lfd FT, 0(0)` loads from absolute address 0. Update forms `lfdu` / `lfdux` invoke `RA = 0` and `RA = RT` (here `RA` is GPR; `RT` is FPR, so the latter cannot collide) as invalid forms when `RA = 0`.
|
||||
- **Alignment.** Xenon tolerates unaligned 8-byte FP loads; PowerISA technically permits implementations to raise alignment exceptions for FP loads, so portable code uses 8-byte aligned addresses.
|
||||
- **Big-endian read.** Bytes are interpreted big-endian: byte at `EA` is bits 0–7 of the IEEE pattern (sign + part of exponent), byte at `EA+7` is bits 56–63 of the mantissa. `mem.read_f64` in xenia handles the host-side byte-swap.
|
||||
- **MSR[FP] required.** Like all FP-register accesses, `lfd` requires the FP unit be enabled (MSR[FP]=1). Otherwise a Floating-Point Unavailable interrupt is raised. Xenia assumes FP is always enabled in user code.
|
||||
- **Pair with [`stfd`](stfd.md).** Store-double is the symmetric counterpart.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lfs`](lfs.md) — single-precision load with format conversion to double.
|
||||
- [`stfd`](stfd.md), [`stfdu`](stfd.md), [`stfdx`](stfd.md), [`stfdux`](stfd.md) — corresponding stores.
|
||||
- [`stfiwx`](stfiwx.md) — store-FP-as-integer-word (the asymmetric oddity in the FP load/store family).
|
||||
- [`ld`](ld.md) — integer doubleword load (same width, GPR target).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lfd` (Load Floating-Point Double)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lfd-load-floating-point-double-instruction)
|
||||
- [AIX 7.3 — `lfdu` / `lfdx` / `lfdux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lfdu-load-floating-point-double-update-instruction)
|
||||
249
migration/project-root/ppc-manual/memory/lfs.md
Normal file
249
migration/project-root/ppc-manual/memory/lfs.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# `lfs` — Load Floating-Point Single
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xc0000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lfs` | `lfs` | — | Load Floating-Point Single |
|
||||
| `lfsu` | `lfsu` | — | Load Floating-Point Single with Update |
|
||||
| `lfsux` | `lfsux` | — | Load Floating-Point Single with Update Indexed |
|
||||
| `lfsx` | `lfsx` | — | Load Floating-Point Single Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lfs [FD], [d]([RA0])
|
||||
lfsu [FD], [d]([RA])
|
||||
lfsux [FD], [RA], [RB]
|
||||
lfsx [FD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lfs` — form `D`
|
||||
|
||||
- **Opcode word:** `0xc0000000`
|
||||
- **Primary opcode (bits 0–5):** `48`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lfsu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xc4000000`
|
||||
- **Primary opcode (bits 0–5):** `49`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lfsux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00046e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `567`
|
||||
- **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 |
|
||||
|
||||
### `lfsx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00042e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `535`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lfs: read; lfsx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lfs: read; lfsu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `FD` | lfs: write; lfsu: write; lfsux: write; lfsx: write | Destination floating-point register. |
|
||||
| `RA` | lfsu: read; lfsu: write; lfsux: read; lfsux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lfsux: read; lfsx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lfs`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfsu`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfsux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lfsx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `FD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
FRT <- DoubleFromSingle(MEM(EA, 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
|
||||
|
||||
**`lfs`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfs"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:960`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L960)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:371`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L371)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1140-1145`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1140-L1145)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfs => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f32(ea) as f64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfsu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfsu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:974`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L974)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:372`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L372)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1164-1169`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1164-L1169)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfsu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f32(ea) as f64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfsux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfsux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:986`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L986)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:823`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L823)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1170-1175`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1170-L1175)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfsux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f32(ea) as f64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lfsx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lfsx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:998`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L998)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:38`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L38)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:819`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L819)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1146-1151`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1146-L1151)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lfsx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.fpr[instr.rd()] = mem.read_f32(ea) as f64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single → double in-register.** Reads 4 bytes as IEEE binary32, then exactly converts to binary64 (every binary32 has a representation in binary64). The result occupies all 64 bits of the FPR; subsequent FP arithmetic operates in double regardless of the value's origin.
|
||||
- **No FPSCR side effects.** The single→double widening is exact, so `lfs` cannot raise inexact, overflow, underflow, or invalid. A signalling NaN passes through unchanged into the FPR — it will signal at the next FP arithmetic instruction.
|
||||
- **Subnormals.** A binary32 subnormal expands to a binary64 normal — `lfs` quietly normalises. There is no "FPSCR[NI] non-IEEE mode" subnormal-to-zero behaviour applied at this stage on Xenon (NI affects arithmetic, not loads).
|
||||
- **`RA0` semantics.** In `lfs` / `lfsx`, `RA = 0` selects literal zero. Update forms `lfsu` / `lfsux` are invalid with `RA = 0`.
|
||||
- **Alignment.** Xenon tolerates unaligned 4-byte loads; PowerISA permits implementations to raise alignment exceptions for FP loads on cache-inhibited storage.
|
||||
- **Big-endian read.** Bytes `EA..EA+3` form the binary32 pattern, sign bit at `EA[7]`. Xenia's `mem.read_f32` handles host byte-swap.
|
||||
- **MSR[FP] required.** Disabled FP unit raises Floating-Point Unavailable.
|
||||
- **Pair with [`stfs`](stfs.md).** Store-single performs the inverse double→single rounding (which **can** raise FPSCR exceptions because that direction may be inexact).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lfd`](lfd.md) — double-precision load (no format conversion).
|
||||
- [`stfs`](stfs.md), [`stfsu`](stfs.md), [`stfsx`](stfs.md), [`stfsux`](stfs.md) — corresponding stores; these can round.
|
||||
- [`stfiwx`](stfiwx.md) — store-FP-as-integer-word.
|
||||
- [`lwz`](lwz.md) — integer word load (same width, GPR target).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lfs` (Load Floating-Point Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lfs-load-floating-point-single-instruction)
|
||||
- [AIX 7.3 — `lfsu` / `lfsx` / `lfsux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lfsu-load-floating-point-single-update-instruction)
|
||||
249
migration/project-root/ppc-manual/memory/lha.md
Normal file
249
migration/project-root/ppc-manual/memory/lha.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# `lha` — Load Half Word Algebraic
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xa8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lha` | `lha` | — | Load Half Word Algebraic |
|
||||
| `lhau` | `lhau` | — | Load Half Word Algebraic with Update |
|
||||
| `lhaux` | `lhaux` | — | Load Half Word Algebraic with Update Indexed |
|
||||
| `lhax` | `lhax` | — | Load Half Word Algebraic Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lha [RD], [d]([RA0])
|
||||
lhau [RD], [d]([RA])
|
||||
lhaux [RD], [RA], [RB]
|
||||
lhax [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lha` — form `D`
|
||||
|
||||
- **Opcode word:** `0xa8000000`
|
||||
- **Primary opcode (bits 0–5):** `42`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lhau` — form `D`
|
||||
|
||||
- **Opcode word:** `0xac000000`
|
||||
- **Primary opcode (bits 0–5):** `43`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lhaux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0002ee`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `375`
|
||||
- **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 |
|
||||
|
||||
### `lhax` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0002ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `343`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lha: read; lhax: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lha: read; lhau: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RD` | lha: write; lhau: write; lhaux: write; lhax: write | Destination GPR. |
|
||||
| `RA` | lhau: read; lhau: write; lhaux: read; lhaux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lhaux: read; lhax: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lha`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhau`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhaux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhax`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- SEXT16_to_64(MEM(EA, 2))
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`lha`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lha"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:128`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L128)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:365`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L365)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1066-1071`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1066-L1071)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lha => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as i16 as i32 as u32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhau`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhau"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:149`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L149)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:366`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L366)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1084-1089`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1084-L1089)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhau => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as i16 as i32 as u32 as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhaux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhaux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:162`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L162)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:805`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L805)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1090-1095`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1090-L1095)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhaux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as i16 as i32 as u32 as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhax`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhax"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:173`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L173)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:801`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L801)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1072-1077`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1072-L1077)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhax => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as i16 as i32 as u32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Sign-extending half-word load.** Reads 2 bytes big-endian, treats them as a signed 16-bit integer, sign-extends to 64 bits. Compare with [`lhz`](lhz.md), which zero-extends. Xenia's snapshot does the cast chain `u16 -> i16 -> i64 -> u64` to obtain the canonical sign-extended bit pattern.
|
||||
- **Big-endian read.** Byte at `EA` is the most-significant 8 bits of the half; byte at `EA+1` is the least-significant. On little-endian hosts `mem.read_u16` returns the big-endian word in host-native form already.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `lha` and `lhax` selects literal zero — useful for absolute-address access patterns.
|
||||
- **Update-form invalid forms.** `lhau` / `lhaux` invoke `RA = 0` and `RA = RT` as invalid forms; xenia performs the load before writing back `RA ← EA`, so an `RA = RT` collision silently destroys the loaded value.
|
||||
- **No alignment requirement.** Xenon executes unaligned half-word loads without a fault.
|
||||
- **Common in audio / graphics code.** `lha` is the standard load for signed 16-bit PCM samples and signed 16-bit packed vertex deltas.
|
||||
- **Use `lha` rather than `lhz` + `extsh`.** Both produce the same result, but `lha` is one fused instruction and the compiler will pick it whenever the source type is `int16_t` / `short`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lhz`](lhz.md), [`lhzu`](lhz.md), [`lhzx`](lhz.md), [`lhzux`](lhz.md) — zero-extending counterparts.
|
||||
- [`lwa`](lwa.md), [`lwax`](lwa.md), [`lwaux`](lwaux.md) — sign-extending word loads (32→64).
|
||||
- [`lbz`](lbz.md) — byte load (no sign-extending byte load exists; use `lbz` + `extsb`).
|
||||
- [`lhbrx`](lhbrx.md) — byte-reversed half-word load (zero-extending).
|
||||
- [`sth`](sth.md), [`sthu`](sth.md), [`sthx`](sth.md), [`sthux`](sth.md) — corresponding stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lha` (Load Half Algebraic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lha-load-half-algebraic-instruction)
|
||||
- [AIX 7.3 — `lhau` / `lhax` / `lhaux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lhau-load-half-algebraic-update-instruction)
|
||||
129
migration/project-root/ppc-manual/memory/lhbrx.md
Normal file
129
migration/project-root/ppc-manual/memory/lhbrx.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# `lhbrx` — Load Half Word Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00062c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lhbrx` | `lhbrx` | — | Load Half Word Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lhbrx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lhbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00062c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `790`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lhbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lhbrx: read | Source GPR. |
|
||||
| `RD` | lhbrx: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lhbrx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **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
|
||||
|
||||
**`lhbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:628`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L628)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:839`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L839)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1806-1812`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1806-L1812)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhbrx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let val = mem.read_u16(ea);
|
||||
ctx.gpr[instr.rd()] = val.swap_bytes() as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Reads little-endian half.** Loads 2 bytes and swaps them: byte at `EA` becomes the low 8 bits of `RT[16:23]`, byte at `EA+1` becomes the upper 8 bits. The xenia snapshot does `mem.read_u16(ea).swap_bytes()`. Effective for parsing little-endian on-disk or network half-word fields.
|
||||
- **Zero-extension to 64 bits.** Result occupies the full 64-bit GPR; high 48 bits are zero. There is no sign-extending byte-reverse load (`lhbrx` + `extsh` if you need one).
|
||||
- **X-form only — no update form.** Like all byte-reverse loads, only the indexed form exists. `EA = (RA|0) + RB`. Pointer-bumping requires a separate `addi`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is the literal zero — `lhbrx RT, 0, RB` reads at exact `RB`.
|
||||
- **Alignment.** Hardware tolerates unaligned half-word reads. Xenon may take alignment exceptions on cache-inhibited storage.
|
||||
- **Common in stream parsers.** PNG, ZIP, BMP, WAV chunk decoders use `lhbrx` to read little-endian length fields.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwbrx`](lwbrx.md), [`ldbrx`](ldbrx.md) — wider byte-reverse loads.
|
||||
- [`sthbrx`](sthbrx.md) — store-half byte-reverse counterpart.
|
||||
- [`lhz`](lhz.md), [`lhzx`](lhz.md) — non-reversing zero-extending half loads.
|
||||
- [`lha`](lha.md) — non-reversing sign-extending half load.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lhbrx` (Load Half Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lhbrx-load-half-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
248
migration/project-root/ppc-manual/memory/lhz.md
Normal file
248
migration/project-root/ppc-manual/memory/lhz.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# `lhz` — Load Half Word and Zero
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xa0000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lhz` | `lhz` | — | Load Half Word and Zero |
|
||||
| `lhzu` | `lhzu` | — | Load Half Word and Zero with Update |
|
||||
| `lhzux` | `lhzux` | — | Load Half Word and Zero with Update Indexed |
|
||||
| `lhzx` | `lhzx` | — | Load Half Word and Zero Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lhz [RD], [d]([RA0])
|
||||
lhzu [RD], [d]([RA])
|
||||
lhzux [RD], [RA], [RB]
|
||||
lhzx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lhz` — form `D`
|
||||
|
||||
- **Opcode word:** `0xa0000000`
|
||||
- **Primary opcode (bits 0–5):** `40`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lhzu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xa4000000`
|
||||
- **Primary opcode (bits 0–5):** `41`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lhzux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00026e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `311`
|
||||
- **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 |
|
||||
|
||||
### `lhzx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00022e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `279`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lhz: read; lhzx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lhz: read; lhzu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RD` | lhz: write; lhzu: write; lhzux: write; lhzx: write | Destination GPR. |
|
||||
| `RA` | lhzu: read; lhzu: write; lhzux: read; lhzux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lhzux: read; lhzx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lhz`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhzu`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhzux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lhzx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- ZEXT16_to_64(MEM(EA, 2))
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`lhz`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhz"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:186`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L186)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:363`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L363)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1048-1053`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1048-L1053)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhz => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhzu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhzu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:207`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L207)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:364`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L364)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1054-1059`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1054-L1059)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhzu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhzux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhzux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:220`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L220)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:797`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L797)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1078-1083`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1078-L1083)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhzux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lhzx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lhzx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:231`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L231)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:40`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L40)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:795`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L795)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1060-1065`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1060-L1065)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lhzx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u16(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Big-endian read, zero-extension.** Reads 2 bytes big-endian, treats them as an unsigned 16-bit integer, zero-extends to 64 bits. The high 48 bits of `RT` become zero. Compare with [`lha`](lha.md), which sign-extends.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `lhz` / `lhzx` selects literal zero for absolute-address access. Update forms `lhzu` / `lhzux` invoke `RA = 0` and `RA = RT` as invalid forms.
|
||||
- **Update-form ordering.** Xenia computes `EA`, performs the load, then writes `RA ← EA`. If `RA == RT` (an invalid form per IBM), the load result is overwritten by `EA` immediately.
|
||||
- **No alignment requirement.** Xenon executes unaligned half-word loads without faulting. `MEM(EA, 2)` reads the two consecutive bytes at `EA`.
|
||||
- **Common as Unicode codepoint loader.** Xbox 360 system strings are UTF-16; `lhz` is the canonical load for a single 16-bit codepoint.
|
||||
- **Use `lhz` rather than `lbz` × 2 + shift.** One fused instruction is faster and lets the load-store unit handle alignment.
|
||||
- **Indexed variant operand order.** `lhzx RT, RA, RB` — `RA` is the base (with `RA0` semantics), `RB` is the offset.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lha`](lha.md), [`lhau`](lha.md), [`lhax`](lha.md), [`lhaux`](lha.md) — sign-extending counterparts.
|
||||
- [`lbz`](lbz.md), [`lwz`](lwz.md), [`ld`](ld.md) — narrower / wider zero-extending loads.
|
||||
- [`lhbrx`](lhbrx.md) — byte-reversed half load (little-endian half).
|
||||
- [`sth`](sth.md), [`sthu`](sth.md), [`sthx`](sth.md), [`sthux`](sth.md) — corresponding stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lhz` (Load Half and Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lhz-load-half-zero-instruction)
|
||||
- [AIX 7.3 — `lhzu` / `lhzx` / `lhzux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lhzu-load-half-zero-update-instruction)
|
||||
133
migration/project-root/ppc-manual/memory/lmw.md
Normal file
133
migration/project-root/ppc-manual/memory/lmw.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# `lmw` — Load Multiple Word
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xb8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lmw` | `lmw` | — | Load Multiple Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lmw` — form `D`
|
||||
|
||||
- **Opcode word:** `0xb8000000`
|
||||
- **Primary opcode (bits 0–5):** `46`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lmw`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`lmw`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lmw"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:705`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L705)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:42`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L42)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:369`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L369)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1720-1734`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1720-L1734)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lmw => {
|
||||
// PPCBUG-125: PowerISA marks `lmw` invalid when rA is in [rT..31];
|
||||
// canary skips the write to rA in that case to preserve the EA base.
|
||||
let mut ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
ea = ea.wrapping_add(instr.d() as i64 as u64);
|
||||
for r in instr.rd()..32 {
|
||||
if r == instr.ra() {
|
||||
ea = ea.wrapping_add(4);
|
||||
continue;
|
||||
}
|
||||
ctx.gpr[r] = mem.read_u32(ea as u32) as u64;
|
||||
ea = ea.wrapping_add(4);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Bulk register restore.** Loads `(32 - RT)` consecutive 32-bit words starting at `EA` into `RT`, `RT+1`, …, `r31`. Used by AIX/PowerPC ABI prologues/epilogues to restore non-volatile GPRs in one instruction. Modern compilers prefer multiple `lwz` for scheduling; `lmw` survives in older code and hand-rolled context-switch routines.
|
||||
- **Loop bound from encoding.** Xenia's snapshot iterates `for r in instr.rd()..32`, exactly matching IBM's "load until r31 inclusive" semantic. With `RT = 28`, four registers (r28..r31) are loaded.
|
||||
- **Each word is zero-extended.** Like `lwz`, every loaded 32-bit word zero-extends into the destination's 64-bit GPR. The high 32 bits of each `r[k]` become zero.
|
||||
- **Big-endian read.** Word at `EA` goes to `r[RT]`, word at `EA+4` goes to `r[RT+1]`, etc. Each word is itself loaded most-significant-byte-first.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero. Useful for absolute-address restoration.
|
||||
- **Invalid forms.** AIX docs declare it invalid for `RA` to be in the destination range `[RT, 31]` — a load could overwrite the base register mid-sequence. Xenia performs loads in order without this check.
|
||||
- **Alignment.** PowerISA requires word-aligned `EA`; an unaligned `lmw` may raise an alignment exception on real hardware. Xenia tolerates it.
|
||||
- **Performance trap.** On modern PowerPC implementations `lmw` is microcoded — slower than the equivalent sequence of `lwz`. Compilers avoid it.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stmw`](stmw.md) — symmetric "store multiple words" (the matching epilogue/prologue partner).
|
||||
- [`lwz`](lwz.md), [`lwzx`](lwz.md) — single-word loads; the modern preferred form.
|
||||
- [`lswi`](lswi.md), [`lswx`](lswx.md) — load string (byte-granular bulk transfer).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lmw` (Load Multiple Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lmw-load-multiple-word-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store Multiple" for invalid-form rules.
|
||||
139
migration/project-root/ppc-manual/memory/lswi.md
Normal file
139
migration/project-root/ppc-manual/memory/lswi.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `lswi` — Load String Word Immediate
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0004aa`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lswi` | `lswi` | — | Load String Word Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lswi` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0004aa`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `597`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lswi`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`lswi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lswi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:727`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L727)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:42`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L42)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:824`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L824)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1521-1539`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1521-L1539)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lswi => {
|
||||
let mut ea = if instr.ra() == 0 { 0u32 } else { ctx.gpr[instr.ra()] as u32 };
|
||||
let nb = if instr.nb() == 0 { 32 } else { instr.nb() };
|
||||
let mut rd = instr.rd();
|
||||
let mut bytes_left = nb;
|
||||
while bytes_left > 0 {
|
||||
let mut val = 0u32;
|
||||
for byte_idx in 0..4 {
|
||||
if bytes_left == 0 { break; }
|
||||
let b = mem.read_u8(ea) as u32;
|
||||
val |= b << (24 - byte_idx * 8);
|
||||
ea = ea.wrapping_add(1);
|
||||
bytes_left -= 1;
|
||||
}
|
||||
ctx.gpr[rd] = val as u64;
|
||||
rd = (rd + 1) % 32;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Byte-granular bulk load.** Reads `NB` bytes starting at `EA` and packs them, big-endian, into successive GPRs starting at `RT`. Each filled GPR holds 4 bytes in its low word; partial last words are left- (most-significant-byte-) aligned with trailing zero bytes. The byte count `NB` is held in the `RB` field of the instruction encoding (1..31), with the special case `NB = 0` meaning "32 bytes".
|
||||
- **Register wraparound at r31 → r0.** The snapshot uses `rd = (rd + 1) % 32`. If the byte count is large enough to spill past `r31`, the next register is `r0`, then `r1`, etc. AIX docs flag the "RA in destination range" and "RB in destination range" cases as invalid; xenia does not check.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero. There is no `RA` post-write — `lswi` is not an update form.
|
||||
- **Big-endian byte ordering inside each word.** First byte read goes into bits 0–7 of the destination GPR (most-significant byte). Xenia's loop builds `val |= b << (24 - byte_idx * 8)`, matching that bit position.
|
||||
- **Last partial word.** When `NB` is not a multiple of 4, the final GPR's unused low bytes are zero. The high bits remain whatever the load placed there.
|
||||
- **Alignment.** The architecture allows arbitrary alignment, but real implementations may take alignment exceptions on cache-inhibited storage; xenia tolerates any address.
|
||||
- **Vanishingly rare in compiled code.** Compilers don't emit `lswi`. Hand-written `memcpy` cores from the PowerPC SDK era used it for short copies; otherwise it appears mostly in byte-string init helpers.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lswx`](lswx.md) — register-supplied byte-count variant.
|
||||
- [`stswi`](stswi.md), [`stswx`](stswx.md) — symmetric stores.
|
||||
- [`lmw`](lmw.md) — word-granular bulk load (multiple of 4 bytes only, no register wrap).
|
||||
- [`lwz`](lwz.md), [`lbz`](lbz.md) — scalar loads that compilers emit instead.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lswi` (Load String Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lswi-load-string-word-immediate-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store String" for the invalid-form checks.
|
||||
139
migration/project-root/ppc-manual/memory/lswx.md
Normal file
139
migration/project-root/ppc-manual/memory/lswx.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `lswx` — Load String Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00042a`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lswx` | `lswx` | — | Load String Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lswx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00042a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `533`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lswx`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`lswx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lswx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:732`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L732)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:42`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L42)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:817`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L817)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4644-4662`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4644-L4662)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lswx => {
|
||||
let mut ea = ea_indexed(ctx, instr);
|
||||
let nb = ctx.xer() & 0x7F; // XER[25..31]
|
||||
let mut rd = instr.rd();
|
||||
let mut bytes_left = nb;
|
||||
while bytes_left > 0 {
|
||||
let mut val = 0u32;
|
||||
for byte_idx in 0..4 {
|
||||
if bytes_left == 0 { break; }
|
||||
let b = mem.read_u8(ea) as u32;
|
||||
val |= b << (24 - byte_idx * 8);
|
||||
ea = ea.wrapping_add(1);
|
||||
bytes_left -= 1;
|
||||
}
|
||||
ctx.gpr[rd] = val as u64;
|
||||
rd = (rd + 1) % 32;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Byte count from `XER[25..31]`.** Unlike `lswi` (where the count is encoded as `RB`), `lswx` reads `XER[25..31]` for the byte count `NB` (0..127). Xenia's snapshot does `let nb = (ctx.xer() & 0x7F) as u32;`. `NB = 0` is **not** the "32 bytes" special case here — zero means literally zero bytes, no registers touched.
|
||||
- **Register packing identical to `lswi`.** Bytes are packed big-endian into successive GPRs starting at `RT`, four bytes per register, with wraparound `r31 → r0`. Trailing bytes in the last register are zero-padded on the right.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero. The instruction has no update form — `RA` is not modified.
|
||||
- **Invalid forms.** AIX flags as invalid: `RT` collides with `RA` or `RB` within the destination range; `XER[25..31]` and `NB` byte stream wraps around through both `RA` and `RB`. Xenia performs writes regardless, with last-write-wins semantics.
|
||||
- **Used for non-multiple-of-4 copies.** Together with `lswi`, gives a way to load a runtime-determined byte count without per-byte loops. Compilers don't emit it; rare hand-written copy primitives may.
|
||||
- **Alignment.** Architecture allows arbitrary alignment; cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **No FPSCR / CR effects.** Pure data movement.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lswi`](lswi.md) — sibling with byte count encoded as `RB` field (immediate-style).
|
||||
- [`stswx`](stswx.md), [`stswi`](stswi.md) — symmetric stores.
|
||||
- [`lmw`](lmw.md) — word-granular bulk load (no byte tail handling).
|
||||
- [`lwz`](lwz.md), [`lbz`](lbz.md) — scalar loads compilers actually emit.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lswx` (Load String Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lswx-load-string-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store String" for invalid-form rules and `XER` interaction.
|
||||
135
migration/project-root/ppc-manual/memory/lvebx.md
Normal file
135
migration/project-root/ppc-manual/memory/lvebx.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# `lvebx` — Load Vector Element Byte Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00000e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvebx` | `lvebx` | — | Load Vector Element Byte Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvebx [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvebx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00000e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `7`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvebx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvebx: read | Source GPR. |
|
||||
| `VD` | lvebx: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvebx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvebx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvebx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:73`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L73)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:752`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L752)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1872-1883`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1872-L1883)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvebx => {
|
||||
// Load 1 byte from EA into vD[EA & 0xF]. PowerISA marks the
|
||||
// other lanes as "undefined" but real Xenon (and Canary)
|
||||
// preserve their prior contents, so seed from vD.
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let slot = (ea & 0xF) as usize;
|
||||
let mut bytes = ctx.vr[instr.rd()].as_bytes();
|
||||
bytes[slot] = mem.read_u8(ea);
|
||||
ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single-byte element load.** Architecturally `lvebx` loads exactly **one** byte from `EA` and places it in lane `EA mod 16` of the destination vector; the other 15 lanes are *undefined* (PowerISA permits implementations to leave them as garbage). Real hardware: lane `EA mod 16` gets the byte, others are unspecified.
|
||||
- **Xenia simplification — full-line read.** The xenia snapshot is shared with `lvehx` / `lvewx` and reads the **entire 16-byte aligned line** (`ea & ~0xF`, then 16 bytes), placing it in `VD`. This is stronger than the architectural guarantee — every lane is filled with whatever happened to be at the line — but matches the practical idiom of using these single-element loads to assemble a vector. Code that depends on undefined-lane behaviour will still produce well-defined output under xenia.
|
||||
- **Operand order subtle.** Unlike `lvx`, the architectural EA is **not** masked. The lane is `EA & 0xF`. Xenia's force-align mask (`& !0xF`) is a deliberate emulator simplification.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `lvebx VD, 0, RB` reads the byte at `RB` (and, in xenia, the surrounding aligned line).
|
||||
- **No update form.** No `lvebux` exists. Pointer-bumping requires a separate `addi`.
|
||||
- **No VMX128 sibling.** There is no `lvebx128` — the single-byte load family was kept Altivec-only in the Xbox 360 VMX128 extension, since 16-byte aligned loads (`lvx128`) plus `vperm`/`vsel` are usually faster.
|
||||
- **Common idiom.** Pair with `vperm` or `vsplt*` to broadcast the loaded byte to all lanes, or with `vinsertb` / shifts to assemble a vector from non-adjacent memory locations.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvehx`](lvehx.md), [`lvewx`](lvewx.md) — half-word and word element loads.
|
||||
- [`lvx`](lvx.md), [`lvxl`](lvxl.md) — full 16-byte aligned vector loads.
|
||||
- [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) — load-left / load-right partial-vector ops for unaligned vector I/O.
|
||||
- [`stvebx`](stvebx.md) — symmetric single-byte store.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvebx` (Load Vector Element Byte Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvebx-load-vector-element-byte-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" § "Vector Load and Store" for lane-placement rules.
|
||||
138
migration/project-root/ppc-manual/memory/lvehx.md
Normal file
138
migration/project-root/ppc-manual/memory/lvehx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `lvehx` — Load Vector Element Half Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00004e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvehx` | `lvehx` | — | Load Vector Element Half Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvehx [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvehx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00004e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `39`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvehx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvehx: read | Source GPR. |
|
||||
| `VD` | lvehx: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvehx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvehx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvehx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:81`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L81)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:763`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L763)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1884-1897`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1884-L1897)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvehx => {
|
||||
// Load a halfword from (EA & ~1) into vD at halfword slot
|
||||
// (EA & 0xF) >> 1. Other halfword lanes preserved (see lvebx).
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea_unaligned = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let ea = ea_unaligned & !0x1u32;
|
||||
let slot = ((ea_unaligned & 0xF) >> 1) as usize;
|
||||
let mut bytes = ctx.vr[instr.rd()].as_bytes();
|
||||
let h = mem.read_u16(ea);
|
||||
bytes[slot * 2] = (h >> 8) as u8;
|
||||
bytes[slot * 2 + 1] = (h & 0xFF) as u8;
|
||||
ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single half-word element load.** Architecturally `lvehx` loads exactly **two** bytes from `EA` (which must be 2-byte aligned) and places them in the half-word lane `(EA mod 16) >> 1` of the destination vector; the other 7 half-word lanes are *undefined*.
|
||||
- **EA must be half-aligned.** The low bit of `EA` is masked by hardware to align to 2 — an odd `EA` rounds down. Xenia's shared snapshot rounds further, masking to 16-byte alignment.
|
||||
- **Xenia simplification — full-line read.** The xenia snapshot is shared with `lvebx` / `lvewx`: `ea & ~0xF` then a full 16-byte read into `VD`. Architectural undefined lanes are filled in deterministically, which is stronger than hardware guarantees but practically convenient.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `lvehx VD, 0, RB` reads at `RB` (and, in xenia, the surrounding aligned line).
|
||||
- **No update form.** No `lvehux` exists.
|
||||
- **No VMX128 sibling.** No `lvehx128` — Xbox 360 code prefers `lvx128` plus `vperm`.
|
||||
- **Big-endian half within the lane.** The byte at the lower address is the most-significant byte of the half-word lane.
|
||||
- **Common idiom.** Pair with `vsplth` to broadcast or with `vperm` to assemble a vector from sparse memory.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvebx`](lvebx.md), [`lvewx`](lvewx.md) — byte and word element loads.
|
||||
- [`lvx`](lvx.md), [`lvxl`](lvxl.md) — full 16-byte aligned vector loads.
|
||||
- [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) — load-left / load-right partial-vector ops.
|
||||
- [`stvehx`](stvehx.md) — symmetric single-half store.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvehx` (Load Vector Element Half Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvehx-load-vector-element-half-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" § "Vector Load and Store" for lane-placement rules.
|
||||
185
migration/project-root/ppc-manual/memory/lvewx.md
Normal file
185
migration/project-root/ppc-manual/memory/lvewx.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# `lvewx` — Load Vector Element Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00008e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvewx` | `lvewx` | — | Load Vector Element Word Indexed |
|
||||
| `lvewx128` | `lvewx128` | — | Load Vector Element Word Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvewx [VD], [RA0], [RB]
|
||||
lvewx128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvewx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00008e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `71`
|
||||
- **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 |
|
||||
|
||||
### `lvewx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000083`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `131`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvewx: read; lvewx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvewx: read; lvewx128: read | Source GPR. |
|
||||
| `VD` | lvewx: write; lvewx128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvewx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvewx128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvewx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvewx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:96`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L96)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:770`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L770)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1898-1913`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1898-L1913)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvewx => {
|
||||
// Load a word from (EA & ~3) into vD at word slot
|
||||
// (EA & 0xF) >> 2. Other word lanes preserved (see lvebx).
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea_unaligned = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let ea = ea_unaligned & !0x3u32;
|
||||
let slot = ((ea_unaligned & 0xF) >> 2) as usize;
|
||||
let mut bytes = ctx.vr[instr.rd()].as_bytes();
|
||||
let w = mem.read_u32(ea);
|
||||
bytes[slot * 4] = (w >> 24) as u8;
|
||||
bytes[slot * 4 + 1] = (w >> 16) as u8;
|
||||
bytes[slot * 4 + 2] = (w >> 8) as u8;
|
||||
bytes[slot * 4 + 3] = (w & 0xFF) as u8;
|
||||
ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvewx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvewx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:99`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L99)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:414`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L414)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3168-3174`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3168-L3174)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvewx128 => {
|
||||
let ea = ea_indexed(ctx, instr) & !0xF;
|
||||
let mut bytes = [0u8; 16];
|
||||
for i in 0..16 { bytes[i] = mem.read_u8(ea + i as u32); }
|
||||
ctx.vr[instr.vd128()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single word element load.** Architecturally `lvewx` loads exactly **four** bytes from `EA` (which must be 4-byte aligned) and places them in the word lane `(EA mod 16) >> 2` of the destination vector; the other 3 word lanes are *undefined*.
|
||||
- **EA must be word-aligned.** The low two bits of `EA` are masked by hardware. Xenia's shared snapshot rounds further to 16-byte alignment for both `lvewx` and `lvewx128`.
|
||||
- **Xenia simplification — full-line read.** Both `lvewx` and `lvewx128` snapshots load the full aligned 16 bytes from `ea & ~0xF` into the destination vector. Architectural undefined lanes are filled deterministically.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero.
|
||||
- **No update form.** No `lvewux` exists.
|
||||
- **VMX128 sibling.** `lvewx128` shares semantics; the only difference is the operand encoding. VMX128 uses a 7-bit register index split across `VD128l ‖ VD128h` so it can address `v0..v127` instead of the 32-register Altivec space.
|
||||
- **Big-endian word within the lane.** The byte at the lower address is the most-significant byte of the word lane.
|
||||
- **Common idiom.** Pair with `vspltw` to broadcast the loaded word to all four lanes, or with `vperm` to gather words from sparse memory into one vector.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvebx`](lvebx.md), [`lvehx`](lvehx.md) — byte and half element loads.
|
||||
- [`lvx`](lvx.md), [`lvxl`](lvxl.md) — full 16-byte aligned vector loads.
|
||||
- [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) — load-left / load-right partial-vector ops.
|
||||
- [`stvewx`](stvewx.md) — symmetric single-word store.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvewx` (Load Vector Element Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvewx-load-vector-element-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" § "Vector Load and Store" for lane-placement rules; Microsoft XDK for `lvewx128`.
|
||||
172
migration/project-root/ppc-manual/memory/lvlx.md
Normal file
172
migration/project-root/ppc-manual/memory/lvlx.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# `lvlx` — Load Vector Left Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00040e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvlx` | `lvlx` | — | Load Vector Left Indexed |
|
||||
| `lvlx128` | `lvlx128` | — | Load Vector Left Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvlx [VD], [RA0], [RB]
|
||||
lvlx128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvlx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00040e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `519`
|
||||
- **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 |
|
||||
|
||||
### `lvlx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000403`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1027`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvlx: read; lvlx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvlx: read; lvlx128: read | Source GPR. |
|
||||
| `VD` | lvlx: write; lvlx128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvlx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvlx128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvlx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvlx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:216`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L216)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:815`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L815)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3083-3087`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3083-L3087)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvlx | PpcOpcode::lvlxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.rd()] = crate::vmx::load_vector_left(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvlx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvlx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:219`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L219)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:420`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L420)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3088-3092`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3088-L3092)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvlx128 | PpcOpcode::lvlxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.vd128()] = crate::vmx::load_vector_left(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Load-left half of an unaligned vector.** `lvlx` reads `(16 - (EA mod 16))` bytes starting at the **exact** `EA` and places them in the **left** (high-address-byte → low-lane) of the destination vector; the remaining lanes on the right are zero-filled. Combine with `lvrx` at `EA + 15` to assemble a full unaligned vector across an alignment boundary.
|
||||
- **Companion idiom.** `lvlx VD, RA, RB ; lvrx Vtemp, RA, RB ; vor VD, VD, Vtemp` produces the unaligned 16 bytes at `EA` regardless of alignment. This was the canonical unaligned-vector-read recipe before `lvsl`/`vperm` shuffles became the more common idiom.
|
||||
- **No alignment masking.** Unlike `lvx`, the EA is **not** rounded down. `EA mod 16` controls how the data is shifted into the destination.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** `lvlx` and `lvrx` are not in the standard Altivec specification — they are part of Microsoft's VMX128 / Cell BE-style extension, defined in PowerPC Cell and later VMX. The Xbox 360 Xenon supports them (decoder + xenia entry confirm).
|
||||
- **Implementation in xenia.** The shared snapshot calls `vmx::load_vector_left(mem, ea)`, which performs the unaligned partial-byte read and zero-fills the right side.
|
||||
- **VMX128 sibling (`lvlx128`).** Same semantics; different operand encoding (7-bit register field, addressing `v0..v127`).
|
||||
- **`lvlxl` is the LRU-hint variant.** Same data behaviour, hint ignored under emulation.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvrx`](lvrx.md), [`lvrx128`](lvrx.md) — load-right partner; combine to read unaligned 16 bytes.
|
||||
- [`lvlxl`](lvlxl.md), [`lvlxl128`](lvlxl.md) — LRU-hint variants.
|
||||
- [`lvx`](lvx.md), [`lvx128`](lvx.md) — aligned load (the EA-masking sibling).
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — symmetric unaligned stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvlx` (Load Vector Left Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvlx-load-vector-left-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 details.
|
||||
171
migration/project-root/ppc-manual/memory/lvlxl.md
Normal file
171
migration/project-root/ppc-manual/memory/lvlxl.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# `lvlxl` — Load Vector Left Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00060e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvlxl` | `lvlxl` | — | Load Vector Left Indexed LRU |
|
||||
| `lvlxl128` | `lvlxl128` | — | Load Vector Left Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvlxl [VD], [RA0], [RB]
|
||||
lvlxl128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvlxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00060e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `775`
|
||||
- **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 |
|
||||
|
||||
### `lvlxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000603`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1539`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvlxl: read; lvlxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvlxl: read; lvlxl128: read | Source GPR. |
|
||||
| `VD` | lvlxl: write; lvlxl128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvlxl`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvlxl128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvlxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvlxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:222`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L222)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:838`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L838)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3083-3087`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3083-L3087)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvlx | PpcOpcode::lvlxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.rd()] = crate::vmx::load_vector_left(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvlxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvlxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:225`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L225)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:44`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L44)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:424`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L424)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3088-3092`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3088-L3092)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvlx128 | PpcOpcode::lvlxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.vd128()] = crate::vmx::load_vector_left(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`lvlx`](lvlx.md), with LRU cache hint.** Reads `(16 - (EA mod 16))` bytes starting at `EA` into the left side of `VD`; right side zero-filled. The `l` suffix tells the cache the line is least-recently-used — likely streaming, evict early under pressure.
|
||||
- **Hint ignored under emulation.** Xenia's snapshot is shared with `lvlx` (`PpcOpcode::lvlx | PpcOpcode::lvlxl => …`). Functional behaviour is identical to `lvlx`.
|
||||
- **No alignment masking.** Like `lvlx`, the exact `EA` controls how data shifts into the vector.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of the VMX128 / Cell BE extended set, not in baseline Altivec.
|
||||
- **Used in single-pass streaming reads.** Decoder loops that consume each vector once benefit from the LRU hint on real hardware; xenia gains nothing from it.
|
||||
- **VMX128 sibling (`lvlxl128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvlx`](lvlx.md), [`lvlx128`](lvlx.md) — non-hint load-left variants.
|
||||
- [`lvrx`](lvrx.md), [`lvrxl`](lvrxl.md) — load-right partner.
|
||||
- [`stvlxl`](stvlxl.md), [`stvrxl`](stvrxl.md) — symmetric stores.
|
||||
- [`lvx`](lvx.md), [`lvxl`](lvxl.md) — aligned vector load family.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvlxl` (Load Vector Left Indexed Last)](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 cache-hint deltas.
|
||||
173
migration/project-root/ppc-manual/memory/lvrx.md
Normal file
173
migration/project-root/ppc-manual/memory/lvrx.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# `lvrx` — Load Vector Right Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00044e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvrx` | `lvrx` | — | Load Vector Right Indexed |
|
||||
| `lvrx128` | `lvrx128` | — | Load Vector Right Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvrx [VD], [RA0], [RB]
|
||||
lvrx128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00044e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `551`
|
||||
- **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 |
|
||||
|
||||
### `lvrx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000443`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1091`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvrx: read; lvrx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvrx: read; lvrx128: read | Source GPR. |
|
||||
| `VD` | lvrx: write; lvrx128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvrx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvrx128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:241`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L241)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:45`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L45)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:822`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L822)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3093-3097`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3093-L3097)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvrx | PpcOpcode::lvrxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.rd()] = crate::vmx::load_vector_right(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvrx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvrx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:244`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L244)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:45`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L45)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:421`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L421)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3098-3102`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3098-L3102)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvrx128 | PpcOpcode::lvrxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.vd128()] = crate::vmx::load_vector_right(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Load-right half of an unaligned vector.** `lvrx` reads `(EA mod 16)` bytes at the addresses *just below* `EA & ~0xF` (i.e., the bytes from the previous aligned line that fall on the right side of the unaligned vector) and places them in the **right** (low-address-byte → high-lane) of the destination; the left lanes are zero-filled.
|
||||
- **Standard pair-mate of [`lvlx`](lvlx.md).** The recipe `lvlx VD, RA, RB ; lvrx Vtmp, RA, (RB+16) ; vor VD, VD, Vtmp` (or some alignment-aware variant) reconstructs the unaligned 16 bytes spanning the boundary at `EA`.
|
||||
- **Right vs. left semantics.** "Right" refers to lower-numbered (high-significance) lanes after rotation, not in any byte-address sense — see PowerISA Cell BE addenda for the exact bit-position formulas.
|
||||
- **No alignment masking.** Like `lvlx`, the exact `EA` is used; the value `EA mod 16` controls how data is rotated.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Implementation in xenia.** The shared snapshot calls `vmx::load_vector_right(mem, ea)`, returning a zero-filled left side and the requested right-bytes payload.
|
||||
- **Microsoft Xbox 360 specific.** Part of VMX128 / Cell BE, not in baseline Altivec.
|
||||
- **VMX128 sibling (`lvrx128`).** Identical semantics; alternative operand encoding.
|
||||
- **`lvrxl` is the LRU-hint variant.** Same data; cache hint ignored under emulation.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvlx`](lvlx.md), [`lvlx128`](lvlx.md) — load-left partner.
|
||||
- [`lvrxl`](lvrxl.md), [`lvrxl128`](lvrxl.md) — LRU-hint variants.
|
||||
- [`lvx`](lvx.md), [`lvx128`](lvx.md) — aligned vector load.
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — symmetric unaligned stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvrx` (Load Vector Right Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvrx-load-vector-right-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 unaligned-vector idioms.
|
||||
171
migration/project-root/ppc-manual/memory/lvrxl.md
Normal file
171
migration/project-root/ppc-manual/memory/lvrxl.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# `lvrxl` — Load Vector Right Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00064e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvrxl` | `lvrxl` | — | Load Vector Right Indexed LRU |
|
||||
| `lvrxl128` | `lvrxl128` | — | Load Vector Right Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvrxl [VD], [RA0], [RB]
|
||||
lvrxl128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvrxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00064e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `807`
|
||||
- **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 |
|
||||
|
||||
### `lvrxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000643`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1603`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvrxl: read; lvrxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvrxl: read; lvrxl128: read | Source GPR. |
|
||||
| `VD` | lvrxl: write; lvrxl128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvrxl`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvrxl128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvrxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvrxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:247`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L247)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:45`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L45)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:842`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L842)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3093-3097`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3093-L3097)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvrx | PpcOpcode::lvrxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.rd()] = crate::vmx::load_vector_right(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvrxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvrxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:250`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L250)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:45`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L45)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:425`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L425)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3098-3102`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3098-L3102)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvrx128 | PpcOpcode::lvrxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
ctx.vr[instr.vd128()] = crate::vmx::load_vector_right(mem, ea);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`lvrx`](lvrx.md), with LRU cache hint.** Reads `(EA mod 16)` bytes from the previous aligned line into the right half of `VD`; left half zero-filled. The `l` suffix tells the cache the line is least-recently-used.
|
||||
- **Hint ignored under emulation.** Xenia's snapshot is shared with `lvrx` (`PpcOpcode::lvrx | PpcOpcode::lvrxl => …`).
|
||||
- **No alignment masking.** The exact `EA` controls how data shifts.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of the VMX128 / Cell BE extended set.
|
||||
- **Streaming-read use case.** Pair with [`lvlxl`](lvlxl.md) when iterating across a buffer that will not be revisited; the LRU hint frees cache capacity for the next line.
|
||||
- **VMX128 sibling (`lvrxl128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvrx`](lvrx.md), [`lvrx128`](lvrx.md) — non-hint variants.
|
||||
- [`lvlxl`](lvlxl.md), [`lvlxl128`](lvlxl.md) — load-left LRU partner.
|
||||
- [`lvxl`](lvxl.md), [`lvxl128`](lvxl.md) — aligned LRU vector load.
|
||||
- [`stvrxl`](stvrxl.md), [`stvlxl`](stvlxl.md) — symmetric LRU stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvrxl` (Load Vector Right Indexed Last)](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 cache-hint behaviour.
|
||||
164
migration/project-root/ppc-manual/memory/lvx.md
Normal file
164
migration/project-root/ppc-manual/memory/lvx.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# `lvx` — Load Vector Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0000ce`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvx` | `lvx` | — | Load Vector Indexed |
|
||||
| `lvx128` | `lvx128` | — | Load Vector Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvx [VD], [RA0], [RB]
|
||||
lvx128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000ce`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `103`
|
||||
- **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 |
|
||||
|
||||
### `lvx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x100000c3`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `195`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvx: read; lvx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvx: read; lvx128: read | Source GPR. |
|
||||
| `VD` | lvx: write; lvx128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvx128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- ((RA|0) + (RB)) & ~0xF ; align to 16
|
||||
VD <- byteswap(MEM(EA, 16))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* lvx VD, RA, RB — 16-byte aligned load of a vector register */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
uint32_t ea = (uint32_t)((base + r[insn.RB]) & ~(uint64_t)0xF);
|
||||
v[insn.VD] = mem_read_vec128_be(ea);
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`lvx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:139`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L139)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:47`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L47)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:775`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L775)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1833-1840`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1833-L1840)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32; // aligned
|
||||
let mut bytes = [0u8; 16];
|
||||
for i in 0..16 { bytes[i] = mem.read_u8(ea + i as u32); }
|
||||
ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:142`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L142)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:47`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L47)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:415`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L415)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1841-1848`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1841-L1848)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvx128 => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
let mut bytes = [0u8; 16];
|
||||
for i in 0..16 { bytes[i] = mem.read_u8(ea + i as u32); }
|
||||
ctx.vr[instr.vd128()] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Alignment is forced, not checked.** The low four bits of the effective address are **cleared** before the load — passing an unaligned `EA` silently reads from `EA & ~0xF` rather than trapping. This differs from scalar loads (no alignment enforcement) and from `lvewx` etc. (which architecturally use the exact `EA` for lane placement).
|
||||
- **Big-endian lane layout.** The byte at the aligned base goes into vector lane 0 (most-significant byte); the byte at base+15 lands in lane 15. On little-endian hosts the 16-byte block is byte-swapped at the memory boundary so the PowerPC-visible layout is preserved.
|
||||
- **`RA0` semantics.** When `RA = 0`, the base is the literal zero. Combined with the alignment mask this lets `lvx VD, 0, RB` load from `RB & ~0xF`.
|
||||
- **No update form.** Unlike scalar loads, VMX loads have no `u` variant that post-writes the base. Use [`lvxl`](lvxl.md) for the cache-hint variant ("last" — the line is not expected to be reused soon).
|
||||
- **VMX128 sibling (`lvx128`).** Identical semantics; the only difference is the operand encoding. VMX128 uses a 7-bit register index split across three non-contiguous bit fields (`VD128l ‖ VD128h`), addressing `v0..v127`.
|
||||
- **Atomic 16 bytes.** The read is a single conceptual load; observers see either all 16 old bytes or all 16 new bytes (to the extent the surrounding cache coherency model allows).
|
||||
- **Cache-line behaviour.** A 16-byte aligned load fits within one Xenon 128-byte cache line; cold-line cost is one fill.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvx`](stvx.md), [`stvx128`](stvx.md) — the store counterparts.
|
||||
- [`lvxl`](lvxl.md), [`lvxl128`](lvxl.md) — cache-hint "last-use" load variants.
|
||||
- [`lvebx`](lvebx.md), [`lvehx`](lvehx.md), [`lvewx`](lvewx.md) — single-element loads at the exact (sub-aligned) address.
|
||||
- [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) — load-left / load-right for unaligned vector I/O (combine to read across alignment).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvx` (Load Vector Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvx-load-vector-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" for full vector-load semantics; `lvx128` is documented in the Xbox 360 XDK.
|
||||
182
migration/project-root/ppc-manual/memory/lvxl.md
Normal file
182
migration/project-root/ppc-manual/memory/lvxl.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# `lvxl` — Load Vector Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0002ce`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lvxl` | `lvxl` | — | Load Vector Indexed LRU |
|
||||
| `lvxl128` | `lvxl128` | — | Load Vector Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lvslx [VD], [RA0], [RB]
|
||||
lvxl128 [VD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lvxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0002ce`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `359`
|
||||
- **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 |
|
||||
|
||||
### `lvxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x100002c3`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `707`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lvxl: read; lvxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lvxl: read; lvxl128: read | Source GPR. |
|
||||
| `VD` | lvxl: write; lvxl128: write | Destination vector register. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lvxl`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lvxl128`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `VD`
|
||||
- **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
|
||||
|
||||
**`lvxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:145`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L145)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:47`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L47)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:802`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L802)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1960-1969`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1960-L1969)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvxl | PpcOpcode::lvxl128 => {
|
||||
// Same as lvx but with cache hint (ignored)
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
let mut bytes = [0u8; 16];
|
||||
for i in 0..16 { bytes[i] = mem.read_u8(ea + i as u32); }
|
||||
let vd = if matches!(instr.opcode, PpcOpcode::lvxl128) { instr.vd128() } else { instr.rd() };
|
||||
ctx.vr[vd] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lvxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lvxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:148`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L148)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:47`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L47)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:418`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L418)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1960-1969`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1960-L1969)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lvxl | PpcOpcode::lvxl128 => {
|
||||
// Same as lvx but with cache hint (ignored)
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
let mut bytes = [0u8; 16];
|
||||
for i in 0..16 { bytes[i] = mem.read_u8(ea + i as u32); }
|
||||
let vd = if matches!(instr.opcode, PpcOpcode::lvxl128) { instr.vd128() } else { instr.rd() };
|
||||
ctx.vr[vd] = xenia_types::Vec128::from_bytes(bytes);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`lvx`](lvx.md), but with cache hint.** Loads 16 bytes from `EA & ~0xF` into `VD`. The `l` suffix signals to the cache hardware that the line is **least-recently-used** — a hint that the line will not be reused soon, allowing the cache to evict it preferentially under pressure. Useful in streaming reads (e.g. once-through vertex transforms, decode passes).
|
||||
- **Hint ignored under emulation.** Xenia's snapshot comment is explicit: "Same as lvx but with cache hint (ignored)". The functional behaviour is identical to `lvx` — only real hardware acts on the hint.
|
||||
- **Alignment is forced, not checked.** Like `lvx`, the low four bits of `EA` are masked. Unaligned `EA` silently rounds down to the 16-byte boundary.
|
||||
- **Big-endian lane layout.** Byte at the aligned base goes into lane 0; byte at base+15 into lane 15.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **No update form.** `lvxl` has no `u`-suffix variant.
|
||||
- **VMX128 sibling (`lvxl128`).** Identical semantics; the only difference is the operand encoding using the split-field 7-bit register index addressing `v0..v127`. Xenia's snapshot dispatches on the opcode to decide which decode helper to use.
|
||||
- **Note: assembler typo.** The Syntax block above shows `lvslx` for the non-128 variant — that is a transcription artefact of the source XML. The real mnemonic is `lvxl`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvx`](lvx.md), [`lvx128`](lvx.md) — same load without the LRU hint.
|
||||
- [`stvxl`](stvxl.md), [`stvxl128`](stvxl.md) — symmetric "store last" variants.
|
||||
- [`stvx`](stvx.md) — non-hint store.
|
||||
- [`dcbt`](dcbt.md), [`dcbtst`](dcbtst.md) — explicit prefetch hints (the hint family).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lvxl` (Load Vector Indexed LRU)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvxl-load-vector-indexed-last-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" for canonical hint semantics.
|
||||
209
migration/project-root/ppc-manual/memory/lwa.md
Normal file
209
migration/project-root/ppc-manual/memory/lwa.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# `lwa` — Load Word Algebraic
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [DS](../forms/DS.md) · **Opcode:** `0xe8000002`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lwa` | `lwa` | — | Load Word Algebraic |
|
||||
| `lwaux` | `lwaux` | — | Load Word Algebraic with Update Indexed |
|
||||
| `lwax` | `lwax` | — | Load Word Algebraic Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lwa [RD], [ds]([RA0])
|
||||
lwaux [RD], [RA], [RB]
|
||||
lwax [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lwa` — form `DS`
|
||||
|
||||
- **Opcode word:** `0xe8000002`
|
||||
- **Primary opcode (bits 0–5):** `58`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0) |
|
||||
| 16–29 | `DS` | 14-bit signed word-scaled displacement |
|
||||
| 30–31 | `XO` | extended opcode |
|
||||
|
||||
### `lwaux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0002ea`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `373`
|
||||
- **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 |
|
||||
|
||||
### `lwax` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0002aa`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `341`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lwa: read; lwax: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `ds` | lwa: read | 14-bit signed word-aligned displacement (`DS << 2`). |
|
||||
| `RD` | lwa: write; lwaux: write; lwax: write | Destination GPR. |
|
||||
| `RA` | lwaux: read; lwaux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lwaux: read; lwax: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lwa`
|
||||
|
||||
- **Reads (always):** `RA0`, `ds`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lwaux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lwax`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(ds || 0b00)
|
||||
RT <- SEXT32_to_64(MEM(EA, 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
|
||||
|
||||
**`lwa`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwa"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:244`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L244)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:382`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L382)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1108-1113`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1108-L1113)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwa => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.ds() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lwaux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwaux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:265`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L265)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:804`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L804)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1120-1125`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1120-L1125)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwaux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lwax`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwax"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:276`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L276)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:800`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L800)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1114-1119`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1114-L1119)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwax => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Sign-extending word load (32→64).** Reads 4 bytes big-endian, treats them as a signed int32, sign-extends to 64 bits. The xenia snapshot does the cast chain `u32 -> i32 -> i64 -> u64` to materialise the canonical sign-extended bit pattern.
|
||||
- **No `lwau` (D-form-update) in PowerISA.** Only `lwa` (DS-form), `lwax` (X-form), and `lwaux` (X-form-update) exist. The D-form-update slot is occupied by something else in the encoding space — to update with a 16-bit immediate you must use a separate `addi` plus `lwa`.
|
||||
- **DS-form displacement.** Like [`ld`](ld.md), `lwa` uses a 14-bit signed displacement scaled by 4 (`EXTS(ds || 0b00)`). The two encoding bits 30–31 distinguish `lwa` (XO=10) from `ld` (XO=00) and `ldu` (XO=01).
|
||||
- **`RA0` semantics.** `RA = 0` in `lwa` and `lwax` selects literal zero. `lwaux` invokes `RA = 0` and `RA = RT` as invalid forms; xenia performs the load before writing back `RA`, so an `RA = RT` collision destroys the loaded value.
|
||||
- **Alignment.** Xenon tolerates unaligned 4-byte loads. PowerISA permits but does not require an alignment exception; some implementations may raise one for cache-inhibited storage.
|
||||
- **Use `lwa` rather than `lwz` + `extsw`.** When the source type is `int32_t`, `lwa` is one fused instruction.
|
||||
- **Common in 64-bit code.** Sign-extending 32-bit fields out of structures (e.g. signed file offsets) into 64-bit GPRs uses this family.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwz`](lwz.md), [`lwzu`](lwz.md), [`lwzx`](lwz.md), [`lwzux`](lwz.md) — zero-extending counterparts.
|
||||
- [`ld`](ld.md), [`ldu`](ld.md), [`ldx`](ld.md), [`ldux`](ld.md) — 64-bit doubleword loads.
|
||||
- [`lha`](lha.md), [`lhax`](lha.md) — 16-bit sign-extending loads.
|
||||
- [`lwbrx`](lwbrx.md) — byte-reversed word load (zero-extending only).
|
||||
- [`stw`](stw.md) — corresponding store (no separate "store sign-extended" — narrow stores discard the high bits).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lwa` (Load Word Algebraic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwa-load-word-algebraic-instruction)
|
||||
- [AIX 7.3 — `lwax` / `lwaux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwax-load-word-algebraic-indexed-instruction)
|
||||
139
migration/project-root/ppc-manual/memory/lwarx.md
Normal file
139
migration/project-root/ppc-manual/memory/lwarx.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `lwarx` — Load Word and Reserve Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000028`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lwarx` | `lwarx` | — | Load Word and Reserve Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lwarx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lwarx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000028`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `20`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lwarx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lwarx: read | Source GPR. |
|
||||
| `RD` | lwarx: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lwarx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **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
|
||||
|
||||
**`lwarx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwarx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:795`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L795)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:754`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L754)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1207-1222`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1207-L1222)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwarx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let val = mem.read_u32(ea);
|
||||
ctx.gpr[instr.rd()] = val as u64;
|
||||
ctx.reserved_line = ea & !RESERVATION_MASK;
|
||||
ctx.reserved_val = val as u64;
|
||||
ctx.has_reservation = true;
|
||||
ctx.reservation_width = 4; // PPCBUG-151: word reservation
|
||||
if let Some(t) = &ctx.reservation_table {
|
||||
if t.is_enabled() {
|
||||
ctx.reserved_generation = t.reserve(ea, ctx.hw_id);
|
||||
}
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Reservation set on the addressed word.** Loads `MEM(EA, 4)` zero-extended to 64 bits and atomically establishes a *reservation* on `EA`. A subsequent [`stwcx`](stwcx.md) at the same address completes only if the reservation is still valid. Together they form the canonical 32-bit load-linked / store-conditional pair for lock-free updates and futexes.
|
||||
- **One reservation per thread.** Xenia's snapshot writes `reserved_addr`, `reserved_val`, and `has_reservation` in the per-context state. Hardware behaves the same: each hardware thread has at most one reservation. A second `lwarx` (or `ldarx`) discards the previous reservation.
|
||||
- **Granule.** Architecturally one naturally-aligned word; on Xenon the practical reservation granule is one **cache line** (128 bytes) — any store to that line by another agent invalidates the reservation. Xenia simplifies to per-address tracking, which can let real-hardware-failing pairs succeed under emulation.
|
||||
- **Alignment requirement.** `EA` must be 4-byte aligned. An unaligned `lwarx` raises an alignment exception on hardware; xenia does not check.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero — `lwarx RT, 0, RB` reads at exact `RB`.
|
||||
- **Reservation-loss events.** Any exception, context switch, or store by another thread to the reserved line clears the reservation. Application code treats `stwcx.` failure (CR0[EQ]=0) as a normal retry condition.
|
||||
- **Pair atomically.** Code must be `lwarx ... do work ... stwcx.` with no intervening loads/stores that could reorder. Optionally fence with [`lwsync`](sync.md) inside the loop.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stwcx`](stwcx.md) — store-conditional word (the matching half of the pair).
|
||||
- [`ldarx`](ldarx.md) / [`stdcx`](stdcx.md) — 64-bit reservation pair.
|
||||
- [`lwz`](lwz.md), [`lwzx`](lwz.md) — non-reserving word loads.
|
||||
- [`sync`](sync.md), [`lwsync`](sync.md), [`isync`](isync.md) — barriers commonly placed around reservation pairs.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lwarx` (Load Word and Reserve Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwarx-load-word-reserve-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Atomic Update Primitives" for the reservation model and granule rules.
|
||||
130
migration/project-root/ppc-manual/memory/lwbrx.md
Normal file
130
migration/project-root/ppc-manual/memory/lwbrx.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# `lwbrx` — Load Word Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00042c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lwbrx` | `lwbrx` | — | Load Word Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lwbrx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lwbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00042c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `534`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lwbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | lwbrx: read | Source GPR. |
|
||||
| `RD` | lwbrx: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lwbrx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **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
|
||||
|
||||
**`lwbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:641`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L641)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:818`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L818)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1799-1805`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1799-L1805)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwbrx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let val = mem.read_u32(ea);
|
||||
ctx.gpr[instr.rd()] = val.swap_bytes() as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Reads little-endian word.** Loads 4 bytes and reverses byte order. With Xenon's big-endian world view, the architectural effect is "load a little-endian word as if it were big-endian" — the standard parser instruction for PNG/ZIP/RIFF/TGA chunk fields, network protocol fields, and PC-side-formatted data.
|
||||
- **Implementation detail.** The xenia snapshot calls `mem.read_u32(ea).swap_bytes()`. `read_u32` already returns the host-native value of the big-endian word at `EA`; `swap_bytes` then flips it.
|
||||
- **X-form only — no update form.** Only the indexed form exists. `EA = (RA|0) + RB`. Pointer-bumping requires a separate `addi`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `lwbrx RT, 0, RB` reads at exact `RB`.
|
||||
- **Zero-extension to 64 bits.** Result occupies the full 64-bit GPR; high 32 bits zero. There is no sign-extending byte-reverse load — combine with `extsw` if needed.
|
||||
- **Alignment.** Hardware tolerates unaligned 4-byte reads. Cache-inhibited storage may raise alignment exceptions on real Xenon.
|
||||
- **Pair with [`stwbrx`](stwbrx.md).** Symmetric byte-reverse store.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lhbrx`](lhbrx.md), [`ldbrx`](ldbrx.md) — narrower / wider byte-reverse loads.
|
||||
- [`stwbrx`](stwbrx.md) — store-word byte-reverse counterpart.
|
||||
- [`lwz`](lwz.md), [`lwzx`](lwz.md) — non-reversing zero-extending word loads.
|
||||
- [`lwa`](lwa.md), [`lwax`](lwa.md) — non-reversing sign-extending word loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lwbrx` (Load Word Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwbrx-load-word-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
267
migration/project-root/ppc-manual/memory/lwz.md
Normal file
267
migration/project-root/ppc-manual/memory/lwz.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# `lwz` — Load Word and Zero
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x80000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `lwz` | `lwz` | — | Load Word and Zero |
|
||||
| `lwzu` | `lwzu` | — | Load Word and Zero with Update |
|
||||
| `lwzux` | `lwzux` | — | Load Word and Zero with Update Indexed |
|
||||
| `lwzx` | `lwzx` | — | Load Word and Zero Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
lwz [RD], [d]([RA0])
|
||||
lwzu [RD], [d]([RA])
|
||||
lwzux [RD], [RA], [RB]
|
||||
lwzx [RD], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `lwz` — form `D`
|
||||
|
||||
- **Opcode word:** `0x80000000`
|
||||
- **Primary opcode (bits 0–5):** `32`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lwzu` — form `D`
|
||||
|
||||
- **Opcode word:** `0x84000000`
|
||||
- **Primary opcode (bits 0–5):** `33`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `lwzux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00006e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `55`
|
||||
- **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 |
|
||||
|
||||
### `lwzx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00002e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `23`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | lwz: read; lwzx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | lwz: read; lwzu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RD` | lwz: write; lwzu: write; lwzux: write; lwzx: write | Destination GPR. |
|
||||
| `RA` | lwzu: read; lwzu: write; lwzux: read; lwzux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | lwzux: read; lwzx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `lwz`
|
||||
|
||||
- **Reads (always):** `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lwzu`
|
||||
|
||||
- **Reads (always):** `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lwzux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `lwzx`
|
||||
|
||||
- **Reads (always):** `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- ZEXT32_to_64(MEM(EA, 4))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* lwz RT, d(RA) */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
uint32_t ea = (uint32_t)(base + (int64_t)(int16_t)insn.D);
|
||||
r[insn.RT] = (uint64_t)mem_read_u32_be(ea); /* zero-extend */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`lwz`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwz"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:289`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L289)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:355`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L355)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1000-1005`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1000-L1005)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwz => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lwzu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwzu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:310`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L310)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:356`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L356)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1006-1011`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1006-L1011)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwzu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lwzux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwzux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:323`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L323)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:766`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L766)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1018-1023`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1018-L1023)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwzux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`lwzx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="lwzx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:334`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L334)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:49`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L49)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:756`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L756)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1012-1017`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1012-L1017)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::lwzx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
ctx.gpr[instr.rd()] = mem.read_u32(ea) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
; lwz — D-form plain
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- 0x0000_0000 || MEM(EA, 4) ; zero-extend 32→64
|
||||
|
||||
; lwzu — D-form with update (base-register post-write)
|
||||
EA <- (RA) + EXTS(d) ; RA ≠ 0 required
|
||||
RT <- 0x0000_0000 || MEM(EA, 4)
|
||||
RA <- EA
|
||||
|
||||
; lwzx — X-form indexed
|
||||
EA <- (RA|0) + (RB)
|
||||
RT <- 0x0000_0000 || MEM(EA, 4)
|
||||
|
||||
; lwzux — X-form indexed with update
|
||||
EA <- (RA) + (RB) ; RA ≠ 0 required
|
||||
RT <- 0x0000_0000 || MEM(EA, 4)
|
||||
RA <- EA
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Big-endian memory.** The Xenon reads memory big-endian. Translating to little-endian hosts requires a byte-swap on the 32-bit read (or calling a `mem_read_u32_be` helper as in the C example). Matching byte-order helpers in xenia: `mem.read_u32(...)` already returns a host-native `u32` of the big-endian word.
|
||||
- **Zero-extension to 64 bits.** The result occupies the full 64-bit GPR; the high 32 bits are zero. This is semantically distinct from [`lwa`](lwa.md) / [`lwax`](lwax.md) / [`lwaux`](lwaux.md), which sign-extend. Most Xbox 360 code uses `lwz` for unsigned word loads and for pointer loads (addresses are 32-bit and fit in the low half).
|
||||
- **`RA0` (non-update forms).** In `lwz` and `lwzx`, when the encoded `RA = 0` the base is the literal zero, **not** `r0`. This enables absolute-address loads `lwz RT, 0x8000(0)` and is heavily used to read from statically-linked data near the TOC base.
|
||||
- **Update forms require `RA ≠ 0`.** `lwzu` / `lwzux` invoke "RA = 0" as an invalid form; AIX docs say the result is undefined and assemblers will refuse to assemble `lwzu RT, d(0)`. Further, `RA = RT` is also invalid (the "effective address" write and the "loaded value" write would race). Xenia implements update forms without these checks; rely on incoming code being well-formed.
|
||||
- **No alignment requirement.** Xenon executes unaligned word loads without a fault (unlike some POWER cores). `MEM(EA, 4)` reads four bytes starting at `EA`, whatever alignment.
|
||||
- **No ordering guarantee.** These are ordinary cached loads; use [`sync`](sync.md) / [`isync`](isync.md) / [`lwsync`](sync.md) for explicit ordering, or [`lwarx`](lwarx.md) for load-reserve semantics.
|
||||
- **Indexed variant operand order.** `lwzx RT, RA, RB` — `RA` is the base (with `RA0` semantics), `RB` is the offset. The variant without `RA0` is `lwzux`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwa`](lwa.md), [`lwax`](lwax.md), [`lwaux`](lwaux.md) — load word, sign-extend to 64.
|
||||
- [`lwbrx`](lwbrx.md) — load word byte-reversed (little-endian word).
|
||||
- [`lwarx`](lwarx.md) — load word and reserve (pair with [`stwcx`](stwcx.md)).
|
||||
- [`ld`](ld.md), [`ldu`](ldu.md), [`ldx`](ldx.md), [`ldux`](ldux.md) — 64-bit loads.
|
||||
- [`lhz`](lhz.md), [`lbz`](lbz.md) — half-word / byte zero-extending loads (same family structure).
|
||||
- [`stw`](stw.md) family — the corresponding stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `lwz` (Load Word and Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwz-load-word-zero-instruction)
|
||||
- [AIX 7.3 — `lwzu` / `lwzx` / `lwzux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lwzu-load-word-zero-update-instruction)
|
||||
260
migration/project-root/ppc-manual/memory/stb.md
Normal file
260
migration/project-root/ppc-manual/memory/stb.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# `stb` — Store Byte
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x98000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stb` | `stb` | — | Store Byte |
|
||||
| `stbu` | `stbu` | — | Store Byte with Update |
|
||||
| `stbux` | `stbux` | — | Store Byte with Update Indexed |
|
||||
| `stbx` | `stbx` | — | Store Byte Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stb [RS], [d]([RA0])
|
||||
stbu [RS], [d]([RA])
|
||||
stbux [RS], [RA], [RB]
|
||||
stbx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stb` — form `D`
|
||||
|
||||
- **Opcode word:** `0x98000000`
|
||||
- **Primary opcode (bits 0–5):** `38`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stbu` — form `D`
|
||||
|
||||
- **Opcode word:** `0x9c000000`
|
||||
- **Primary opcode (bits 0–5):** `39`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stbux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0001ee`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `247`
|
||||
- **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 |
|
||||
|
||||
### `stbx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0001ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `215`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stb: read; stbu: read; stbux: read; stbx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stb: read; stbx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | stb: read; stbu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RA` | stbu: read; stbu: write; stbux: read; stbux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | stbux: read; stbx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stb`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stbu`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stbux`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stbx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
MEM(EA, 1) <- (RS)[56:63]
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`stb`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stb"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:404`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L404)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:67`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L67)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:361`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L361)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1327-1335`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1327-L1335)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stb => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u8(ea, ctx.gpr[instr.rs()] as u8);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stbu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stbu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:423`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L423)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:67`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L67)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:362`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L362)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1336-1344`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1336-L1344)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stbu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u8(ea, ctx.gpr[instr.rs()] as u8);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stbux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stbux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:433`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L433)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:67`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L67)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:793`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L793)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1354-1362`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1354-L1362)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stbux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u8(ea, ctx.gpr[instr.rs()] as u8);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stbx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stbx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:443`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L443)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:67`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L67)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:790`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L790)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1345-1353`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1345-L1353)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stbx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u8(ea, ctx.gpr[instr.rs()] as u8);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single-byte write.** Writes the low 8 bits of `RS` (`(RS)[56:63]` in IBM bit-numbering, equivalently `RS & 0xFF`) at `EA`. The xenia snapshot does `mem.write_u8(ea, ctx.gpr[instr.rs()] as u8)`, which casts the GPR's low byte directly.
|
||||
- **No endian concerns.** A single byte has no endianness — the byte at `EA` is the byte you wrote.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `stb` and `stbx` selects literal zero as base — useful for absolute writes. Update forms `stbu` / `stbux` invoke `RA = 0` as an invalid form (no `RA = RT` collision since the source is `RS`, not `RT`).
|
||||
- **Update-form post-write.** `stbu` / `stbux` write the computed `EA` back to `RA` after the store. The order is store-then-update; if `RA = RS` the store is unaffected (the store reads `RS` first), but the new `RA` value reflects `EA`, not the original `RS`.
|
||||
- **No alignment requirement.** Byte stores are intrinsically aligned. Xenon never raises alignment exceptions for byte writes.
|
||||
- **Common in string and packed-bool code.** Compilers emit `stb` for `char *` writes, packed boolean array updates, and small enum stores.
|
||||
- **Cache effects.** A `stb` to a cold cache line triggers a cache-line read-allocate (load the whole line, modify one byte, mark dirty). When writing many bytes sequentially, prefer one [`stw`](stw.md) or [`stvx`](stvx.md), or pre-clear the line with [`dcbz128`](dcbz.md).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sth`](sth.md), [`stw`](stw.md), [`std`](std.md) — wider stores (half / word / doubleword).
|
||||
- [`lbz`](lbz.md) — corresponding load (no `lba` exists).
|
||||
- [`stmw`](stmw.md), [`stswi`](stswi.md), [`stswx`](stswx.md) — multi-word / string stores for bulk transfer.
|
||||
- [`stwbrx`](stwbrx.md), [`sthbrx`](sthbrx.md) — byte-reversed wider stores (no byte-equivalent needed).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stb` (Store Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stb-store-byte-instruction)
|
||||
- [AIX 7.3 — `stbu` (Store Byte with Update)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stbu-store-byte-update-instruction)
|
||||
263
migration/project-root/ppc-manual/memory/std.md
Normal file
263
migration/project-root/ppc-manual/memory/std.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# `std` — Store Doubleword
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [DS](../forms/DS.md) · **Opcode:** `0xf8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `std` | `std` | — | Store Doubleword |
|
||||
| `stdu` | `stdu` | — | Store Doubleword with Update |
|
||||
| `stdux` | `stdux` | — | Store Doubleword with Update Indexed |
|
||||
| `stdx` | `stdx` | — | Store Doubleword Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
std [RS], [ds]([RA0])
|
||||
stdu [RS], [ds]([RA])
|
||||
stdux [RS], [RA], [RB]
|
||||
stdx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `std` — form `DS`
|
||||
|
||||
- **Opcode word:** `0xf8000000`
|
||||
- **Primary opcode (bits 0–5):** `62`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0) |
|
||||
| 16–29 | `DS` | 14-bit signed word-scaled displacement |
|
||||
| 30–31 | `XO` | extended opcode |
|
||||
|
||||
### `stdu` — form `DS`
|
||||
|
||||
- **Opcode word:** `0xf8000001`
|
||||
- **Primary opcode (bits 0–5):** `62`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0) |
|
||||
| 16–29 | `DS` | 14-bit signed word-scaled displacement |
|
||||
| 30–31 | `XO` | extended opcode |
|
||||
|
||||
### `stdux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00016a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `181`
|
||||
- **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 |
|
||||
|
||||
### `stdx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00012a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `149`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | std: read; stdu: read; stdux: read; stdx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | std: read; stdu: read; stdu: write; stdux: read; stdux: write | Source GPR (`r0`–`r31`). |
|
||||
| `ds` | std: read; stdu: read | 14-bit signed word-aligned displacement (`DS << 2`). |
|
||||
| `RB` | stdux: read; stdx: read | Source GPR. |
|
||||
| `RA0` | stdx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `std`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `ds`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stdu`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `ds`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stdux`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stdx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(ds || 0b00)
|
||||
MEM(EA, 8) <- (RS)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`std`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="std"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:575`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L575)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:399`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L399)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1399-1407`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1399-L1407)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::std => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.ds() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stdu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stdu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:594`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L594)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:400`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L400)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1417-1425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1417-L1425)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stdu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.ds() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()]);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stdux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stdux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:604`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L604)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:786`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L786)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1426-1434`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1426-L1434)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stdux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()]);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:614`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L614)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:781`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L781)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1408-1416`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1408-L1416)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stdx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **DS-form, not D-form.** Like [`ld`](ld.md), `std` uses a 14-bit signed displacement scaled by 4 (`EXTS(ds || 0b00)`). Bits 30–31 are the extended opcode used to distinguish `std` (XO=0) from `stdu` (XO=1). Assemblers verify the byte displacement is a multiple of 4.
|
||||
- **Big-endian write.** The 64-bit value of `RS` is written most-significant-byte-first: `RS[0:7]` to `EA`, `RS[56:63]` to `EA+7`. Xenia's `mem.write_u64` performs the host-side byte swap if needed.
|
||||
- **`RA0` for `std` and `stdx`.** When `RA = 0`, base is the literal zero. Update forms `stdu` / `stdux` invoke `RA = 0` as an invalid form (no `RA = RS` collision possible — `RS` and `RA` are independent encoding fields, and even if equal the store reads `RS` first).
|
||||
- **Update-form post-write.** `stdu` / `stdux` write `EA` to `RA` after the store. Order is store-then-update.
|
||||
- **Alignment.** Xenon tolerates unaligned doubleword stores. PowerISA permits implementations to raise alignment exceptions; portable code keeps doublewords 8-byte aligned. Cache-inhibited storage may force alignment.
|
||||
- **Cache-line behaviour.** A doubleword store fits inside one Xenon cache line (128 B), so it's a single line write. A doubleword store that **straddles** a line boundary triggers two line accesses — keep doublewords 8-byte aligned to avoid the cost.
|
||||
- **64-bit pointer / counter stores.** Xbox 360 user code is 32-bit, but kernel structures, TOC entries, and 64-bit counters are stored with `std`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stw`](stw.md), [`sth`](sth.md), [`stb`](stb.md) — narrower integer stores.
|
||||
- [`stdbrx`](stdbrx.md) — byte-reversed doubleword store.
|
||||
- [`stdcx`](stdcx.md) — store-conditional (the doubleword reservation pair end).
|
||||
- [`ld`](ld.md), [`ldu`](ld.md), [`ldx`](ld.md), [`ldux`](ld.md) — corresponding loads.
|
||||
- [`stfd`](stfd.md) — FP doubleword store (same width, different register file).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `std` (Store Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-std-store-doubleword-instruction)
|
||||
- [AIX 7.3 — `stdu` / `stdx` / `stdux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stdu-store-doubleword-update-instruction)
|
||||
130
migration/project-root/ppc-manual/memory/stdbrx.md
Normal file
130
migration/project-root/ppc-manual/memory/stdbrx.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# `stdbrx` — Store Doubleword Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000528`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stdbrx` | `stdbrx` | — | Store Doubleword Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stdbrx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stdbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000528`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `660`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stdbrx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stdbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stdbrx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stdbrx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `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
|
||||
|
||||
**`stdbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stdbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:691`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L691)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:829`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L829)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4632-4639`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4632-L4639)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stdbrx => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()].swap_bytes());
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Writes little-endian doubleword.** Reverses the 8 bytes of `RS` and stores them at `EA`. Compared to a regular `std`, the byte at `EA` becomes `RS[56:63]` (least-significant), and the byte at `EA+7` becomes `RS[0:7]` (most-significant). The xenia snapshot calls `mem.write_u64(ea, ctx.gpr[instr.rs()].swap_bytes())`.
|
||||
- **Used to emit little-endian payloads.** Symmetric counterpart of [`ldbrx`](ldbrx.md). Common when writing PC-side file formats, network packets, or PE/COFF headers from PowerPC code.
|
||||
- **X-form only — no update form, no DS-form.** Only the indexed form exists. `EA = (RA|0) + RB`. Pointer-bumping requires a separate `addi`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero. `stdbrx RS, 0, RB` writes at exact `RB`.
|
||||
- **Alignment.** Hardware tolerates unaligned 8-byte writes. Cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **No CR / FPSCR effects.** Pure data movement.
|
||||
- **Cache-line straddling cost.** As with `std`, writes that cross a 128-byte line boundary touch two cache lines; keep doublewords 8-byte aligned for best performance.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`ldbrx`](ldbrx.md) — load doubleword byte-reverse (the matching load).
|
||||
- [`stwbrx`](stwbrx.md), [`sthbrx`](sthbrx.md) — narrower byte-reverse stores.
|
||||
- [`std`](std.md), [`stdx`](std.md) — non-reversing doubleword stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stdbrx` (Store Doubleword Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stdbrx-store-double-word-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
176
migration/project-root/ppc-manual/memory/stdcx.md
Normal file
176
migration/project-root/ppc-manual/memory/stdcx.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# `stdcx` — Store Doubleword Conditional Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0001ad`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stdcx` | `stdcx` | — | Store Doubleword Conditional Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stdcx. [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stdcx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0001ad`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `214`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stdcx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stdcx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stdcx: read | Source GPR. |
|
||||
| `CR` | stdcx: write | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stdcx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `stdcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).
|
||||
|
||||
## 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
|
||||
|
||||
**`stdcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stdcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:827`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L827)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:69`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L69)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:789`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L789)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4576-4626`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4576-L4626)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stdcx => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
let line = ea & !RESERVATION_MASK;
|
||||
let table_route = ctx
|
||||
.reservation_table
|
||||
.as_ref()
|
||||
.filter(|t| t.is_enabled())
|
||||
.cloned();
|
||||
// PPCBUG-151: stdcx. requires a doubleword (ldarx) reservation;
|
||||
// a word (lwarx) reservation must not commit here.
|
||||
let width_ok = ctx.reservation_width == 8;
|
||||
let success = if let Some(t) = &table_route {
|
||||
ctx.has_reservation
|
||||
&& width_ok
|
||||
&& ctx.reserved_line == line
|
||||
&& t.try_commit(ea, ctx.reserved_generation, ctx.hw_id)
|
||||
} else {
|
||||
// Legacy per-ctx path (M2 default / lockstep).
|
||||
// PPCBUG-108: same sentinel as stwcx. — fires on non-primary
|
||||
// HW slots if the table is disabled under --parallel.
|
||||
debug_assert!(
|
||||
ctx.hw_id == 0,
|
||||
"PPCBUG-108: legacy per-ctx stdcx. on non-primary HW slot \
|
||||
(hw_id={}) — ReservationTable must be enabled under --parallel",
|
||||
ctx.hw_id
|
||||
);
|
||||
ctx.has_reservation && width_ok && ctx.reserved_line == line
|
||||
};
|
||||
if success {
|
||||
mem.write_u64(ea, ctx.gpr[instr.rs()]);
|
||||
ctx.cr[0] = crate::context::CrField {
|
||||
lt: false,
|
||||
gt: false,
|
||||
eq: true,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
} else {
|
||||
ctx.cr[0] = crate::context::CrField {
|
||||
lt: false,
|
||||
gt: false,
|
||||
eq: false,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
if let Some(t) = &table_route {
|
||||
t.release(ea, ctx.reserved_generation, ctx.hw_id);
|
||||
}
|
||||
}
|
||||
ctx.has_reservation = false;
|
||||
ctx.reservation_width = 0; // PPCBUG-151: always clear on exit
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Always sets `Rc=1` (the trailing dot).** The mnemonic is `stdcx.` — there is no non-Rc variant. CR0 is updated unconditionally to communicate success/failure. `EQ=1` means the conditional store succeeded; `EQ=0` means it failed (the prior reservation was cleared and no memory was written).
|
||||
- **Reservation check.** Xenia's snapshot tests `has_reservation && reserved_addr == ea`. On match it performs `mem.write_u64`, sets `EQ=1`. On mismatch it leaves memory untouched and sets `EQ=0`. In both cases the reservation is cleared (`has_reservation = false`), so a retry must be preceded by a fresh [`ldarx`](ldarx.md).
|
||||
- **Hardware granule.** PowerISA defines reservation by aligned doubleword; Xenon implementations widen this to one 128-byte cache line. A store by another agent anywhere in the line clears the reservation. Xenia's per-address check is more permissive than hardware.
|
||||
- **Alignment requirement.** `EA` must be 8-byte aligned. Unaligned `stdcx.` raises an alignment exception on real hardware.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero — `stdcx. RS, 0, RB` writes at exact `RB`.
|
||||
- **CR0[SO] reflects XER[SO].** Like all CR-updating ops, CR0[SO] is copied from `XER[SO]` rather than computed from this instruction.
|
||||
- **Spurious failures permitted.** Hardware may report failure even when no actual conflict occurred (e.g. on context switch). Application code treats failure as a normal retry condition.
|
||||
- **Pair atomically with [`ldarx`](ldarx.md).** Don't interleave loads/stores between the pair; an [`lwsync`](sync.md) inside the loop body is common.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`ldarx`](ldarx.md) — load-and-reserve doubleword (the matching load).
|
||||
- [`stwcx`](stwcx.md) / [`lwarx`](lwarx.md) — 32-bit reservation pair.
|
||||
- [`std`](std.md), [`stdx`](std.md) — non-conditional doubleword stores.
|
||||
- [`sync`](sync.md), [`lwsync`](sync.md), [`isync`](isync.md) — barriers used around reservation pairs.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stdcx.` (Store Doubleword Conditional Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stdcx-store-double-word-conditional-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Atomic Update Primitives" for canonical reservation semantics and granule rules.
|
||||
260
migration/project-root/ppc-manual/memory/stfd.md
Normal file
260
migration/project-root/ppc-manual/memory/stfd.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# `stfd` — Store Floating-Point Double
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xd8000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stfd` | `stfd` | — | Store Floating-Point Double |
|
||||
| `stfdu` | `stfdu` | — | Store Floating-Point Double with Update |
|
||||
| `stfdux` | `stfdux` | — | Store Floating-Point Double with Update Indexed |
|
||||
| `stfdx` | `stfdx` | — | Store Floating-Point Double Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stfd [FS], [d]([RA0])
|
||||
stfdu [FS], [d]([RA])
|
||||
stfdux [FS], [RA], [RB]
|
||||
stfdx [FS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stfd` — form `D`
|
||||
|
||||
- **Opcode word:** `0xd8000000`
|
||||
- **Primary opcode (bits 0–5):** `54`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stfdu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xdc000000`
|
||||
- **Primary opcode (bits 0–5):** `55`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stfdux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0005ee`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `759`
|
||||
- **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 |
|
||||
|
||||
### `stfdx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0005ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `727`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `FS` | stfd: read; stfdu: read; stfdux: read; stfdx: read | Source floating-point register. |
|
||||
| `RA0` | stfd: read; stfdx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | stfd: read; stfdu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RA` | stfdu: read; stfdu: write; stfdux: read; stfdux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | stfdux: read; stfdx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stfd`
|
||||
|
||||
- **Reads (always):** `FS`, `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfdu`
|
||||
|
||||
- **Reads (always):** `FS`, `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfdux`
|
||||
|
||||
- **Reads (always):** `FS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfdx`
|
||||
|
||||
- **Reads (always):** `FS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
MEM(EA, 8) <- (FRS)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`stfd`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfd"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1014`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1014)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:377`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L377)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1473-1481`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1473-L1481)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfd => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f64(ea, ctx.fpr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfdu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfdu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1026`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1026)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:378`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L378)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1482-1490`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1482-L1490)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfdu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f64(ea, ctx.fpr[instr.rs()]);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfdux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfdux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1036`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1036)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:837`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L837)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1500-1508`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1500-L1508)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfdux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f64(ea, ctx.fpr[instr.rs()]);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1046`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1046)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:836`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L836)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1491-1499`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1491-L1499)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfdx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f64(ea, ctx.fpr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Bit-exact double store.** Writes the 64-bit IEEE binary64 contents of `FRS` directly to memory; no rounding, no format conversion. The xenia snapshot calls `mem.write_f64(ea, ctx.fpr[instr.rs()])`, which preserves the exact bit pattern (including signalling NaNs).
|
||||
- **No FPSCR side effects.** Like [`lfd`](lfd.md), `stfd` cannot raise IEEE exceptions: there is no rounding step. Contrast [`stfs`](stfs.md), where double→single rounding **can** raise inexact / overflow / underflow.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `stfd` and `stfdx` selects literal zero. Update forms `stfdu` / `stfdux` invoke `RA = 0` as an invalid form.
|
||||
- **Update-form post-write.** `stfdu` / `stfdux` write the computed `EA` back to `RA` after the store. No `FRS` / `RA` collision possible — `RS` is an FPR, `RA` is a GPR.
|
||||
- **Big-endian write.** Byte at `EA` is the FPR's most-significant byte (sign + part of exponent), byte at `EA+7` is the least-significant mantissa byte. Xenia's `mem.write_f64` performs host-side byte-swap.
|
||||
- **Alignment.** Xenon tolerates unaligned 8-byte FP stores. PowerISA permits implementations to raise alignment exceptions on cache-inhibited storage.
|
||||
- **MSR[FP] required.** Disabled FP unit raises Floating-Point Unavailable.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lfd`](lfd.md), [`lfdu`](lfd.md), [`lfdx`](lfd.md), [`lfdux`](lfd.md) — corresponding loads.
|
||||
- [`stfs`](stfs.md) — single-precision store with format conversion (can raise FPSCR).
|
||||
- [`stfiwx`](stfiwx.md) — store low 32 bits of FPR as integer word.
|
||||
- [`std`](std.md) — integer doubleword store (same width, GPR source).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stfd` (Store Floating-Point Double)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stfd-store-floating-point-double-instruction)
|
||||
- [AIX 7.3 — `stfdu` / `stfdx` / `stfdux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stfdu-store-floating-point-double-update-instruction)
|
||||
133
migration/project-root/ppc-manual/memory/stfiwx.md
Normal file
133
migration/project-root/ppc-manual/memory/stfiwx.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# `stfiwx` — Store Floating-Point as Integer Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0007ae`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stfiwx` | `stfiwx` | — | Store Floating-Point as Integer Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stfiwx [FS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stfiwx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0007ae`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `983`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `FS` | stfiwx: read | Source floating-point register. |
|
||||
| `RA0` | stfiwx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stfiwx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stfiwx`
|
||||
|
||||
- **Reads (always):** `FS`, `RA0`, `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
|
||||
|
||||
**`stfiwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfiwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1058`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1058)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:851`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L851)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1509-1518`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1509-L1518)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfiwx => {
|
||||
// Store FP as integer word: stores low 32 bits of FPR as-is
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, ctx.fpr[instr.rs()].to_bits() as u32);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Stores low 32 bits of FPR as raw bytes.** Writes `FRS[32:63]` (the low half of the 64-bit FPR bit pattern) verbatim — no IEEE rounding, no float→int conversion. Used in conjunction with `fctiw` / `fctiwz` (convert float to integer word, leaving the 32-bit integer in the low half of an FPR) to materialise an integer in memory without going through a GPR.
|
||||
- **The asymmetric oddity of the FP load/store family.** There is no matching "load FP as integer word" — a 32-bit integer is brought in via `lwz` to a GPR, then to FPR via the memory-round-trip pattern (`stw` then `lfd`). `stfiwx` only exists in the store direction.
|
||||
- **X-form only — no D-form, no update form.** The instruction has only the indexed form. Compilers usually pair it with `addi` if a constant offset is needed.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `stfiwx FS, 0, RB` writes at exact `RB`.
|
||||
- **No FPSCR effects.** Pure data movement — does not look at the value, does not round.
|
||||
- **Big-endian word write.** The 32 bits are written most-significant-byte first into bytes `EA..EA+3`. The xenia snapshot extracts via `to_bits() as u32`, then `mem.write_u32` applies host-side byte-swap.
|
||||
- **Alignment.** Xenon tolerates unaligned 4-byte writes; cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **MSR[FP] required.** Disabled FP unit raises Floating-Point Unavailable.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stfd`](stfd.md), [`stfs`](stfs.md) — regular FP stores.
|
||||
- [`lfd`](lfd.md), [`lfs`](lfs.md) — FP loads (no `lfiwx` analog).
|
||||
- [`stw`](stw.md), [`stwx`](stw.md) — integer word stores from a GPR (the GPR-side equivalent).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stfiwx` (Store Floating-Point as Integer Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stfiwx-store-floating-point-as-integer-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` § "Floating-Point Load and Store" for the float-to-int memory pattern.
|
||||
261
migration/project-root/ppc-manual/memory/stfs.md
Normal file
261
migration/project-root/ppc-manual/memory/stfs.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# `stfs` — Store Floating-Point Single
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xd0000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stfs` | `stfs` | — | Store Floating-Point Single |
|
||||
| `stfsu` | `stfsu` | — | Store Floating-Point Single with Update |
|
||||
| `stfsux` | `stfsux` | — | Store Floating-Point Single with Update Indexed |
|
||||
| `stfsx` | `stfsx` | — | Store Floating-Point Single Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stfs [FS], [d]([RA0])
|
||||
stfsu [FS], [d]([RA])
|
||||
stfsux [FS], [RA], [RB]
|
||||
stfsx [FS], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stfs` — form `D`
|
||||
|
||||
- **Opcode word:** `0xd0000000`
|
||||
- **Primary opcode (bits 0–5):** `52`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stfsu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xd4000000`
|
||||
- **Primary opcode (bits 0–5):** `53`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stfsux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00056e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `695`
|
||||
- **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 |
|
||||
|
||||
### `stfsx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00052e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `663`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `FS` | stfs: read; stfsu: read; stfsux: read; stfsx: read | Source floating-point register. |
|
||||
| `RA0` | stfs: read; stfsx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | stfs: read; stfsu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RA` | stfsu: read; stfsu: write; stfsux: read; stfsux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | stfsux: read; stfsx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stfs`
|
||||
|
||||
- **Reads (always):** `FS`, `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfsu`
|
||||
|
||||
- **Reads (always):** `FS`, `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfsux`
|
||||
|
||||
- **Reads (always):** `FS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stfsx`
|
||||
|
||||
- **Reads (always):** `FS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
MEM(EA, 4) <- SingleFromDouble(FRS)
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`stfs`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfs"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1071`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1071)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:375`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L375)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1437-1445`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1437-L1445)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfs => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f32(ea, ctx.fpr[instr.rs()] as f32);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfsu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfsu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1084`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1084)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:376`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L376)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1446-1454`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1446-L1454)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfsu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f32(ea, ctx.fpr[instr.rs()] as f32);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfsux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfsux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1095`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1095)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:834`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L834)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1464-1472`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1464-L1472)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfsux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f32(ea, ctx.fpr[instr.rs()] as f32);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stfsx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stfsx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:1106`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L1106)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:71`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L71)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:832`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L832)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1455-1463`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1455-L1463)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stfsx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_f32(ea, ctx.fpr[instr.rs()] as f32);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Double → single rounding.** `FRS` always holds an IEEE binary64; `stfs` rounds to binary32 using the current `FPSCR[RN]` rounding mode before writing 4 bytes. The xenia snapshot does `ctx.fpr[instr.rs()] as f32`, which Rust defines as round-to-nearest-even; this differs from PPC if `RN` is configured otherwise. Real hardware honours `RN`.
|
||||
- **FPSCR side effects.** Unlike [`lfs`](lfs.md) / [`lfd`](lfd.md) / [`stfd`](stfd.md), `stfs` **can** raise `FPSCR[XX]` (inexact), `OX` (overflow), `UX` (underflow), and `VXSNAN` (signalling NaN) per IEEE-754 narrowing rules. These take effect even though the write itself succeeds (architecturally — xenia's `as f32` cast does not surface these flags).
|
||||
- **Out-of-range doubles.** Values larger than binary32's max (~3.4e38) round to ±∞; values smaller than min normal flush to ±0 or denormal per `FPSCR[NI]`. NaNs are quieted (the signalling bit drops).
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `stfs` and `stfsx` selects literal zero. Update forms `stfsu` / `stfsux` invoke `RA = 0` as an invalid form.
|
||||
- **Update-form post-write.** `stfsu` / `stfsux` write `EA` back to `RA` after the store.
|
||||
- **Big-endian write.** 4 bytes most-significant-byte first.
|
||||
- **Alignment.** Xenon tolerates unaligned 4-byte FP stores; cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **MSR[FP] required.** Disabled FP unit raises Floating-Point Unavailable.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lfs`](lfs.md), [`lfsu`](lfs.md), [`lfsx`](lfs.md), [`lfsux`](lfs.md) — corresponding loads (single→double widening, can't raise exceptions).
|
||||
- [`stfd`](stfd.md) — double-precision store (no rounding, no FPSCR effects).
|
||||
- [`stfiwx`](stfiwx.md) — store-FP-as-integer-word.
|
||||
- [`stw`](stw.md) — integer word store (same width, GPR source).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stfs` (Store Floating-Point Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stfs-store-floating-point-single-instruction)
|
||||
- [AIX 7.3 — `stfsu` / `stfsx` / `stfsux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stfsu-store-floating-point-single-update-instruction)
|
||||
260
migration/project-root/ppc-manual/memory/sth.md
Normal file
260
migration/project-root/ppc-manual/memory/sth.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# `sth` — Store Half Word
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xb0000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sth` | `sth` | — | Store Half Word |
|
||||
| `sthu` | `sthu` | — | Store Half Word with Update |
|
||||
| `sthux` | `sthux` | — | Store Half Word with Update Indexed |
|
||||
| `sthx` | `sthx` | — | Store Half Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sth [RS], [d]([RA0])
|
||||
sthu [RS], [d]([RA])
|
||||
sthux [RS], [RA], [RB]
|
||||
sthx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sth` — form `D`
|
||||
|
||||
- **Opcode word:** `0xb0000000`
|
||||
- **Primary opcode (bits 0–5):** `44`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `sthu` — form `D`
|
||||
|
||||
- **Opcode word:** `0xb4000000`
|
||||
- **Primary opcode (bits 0–5):** `45`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `sthux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00036e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `439`
|
||||
- **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 |
|
||||
|
||||
### `sthx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00032e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `407`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | sth: read; sthu: read; sthux: read; sthx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | sth: read; sthx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | sth: read; sthu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RA` | sthu: read; sthu: write; sthux: read; sthux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | sthux: read; sthx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sth`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `sthu`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `sthux`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `sthx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
MEM(EA, 2) <- (RS)[48:63]
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
**`sth`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sth"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:455`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L455)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:73`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L73)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:367`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L367)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1363-1371`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1363-L1371)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sth => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u16(ea, ctx.gpr[instr.rs()] as u16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`sthu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sthu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:475`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L475)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:73`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L73)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:368`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L368)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1372-1380`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1372-L1380)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sthu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u16(ea, ctx.gpr[instr.rs()] as u16);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`sthux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sthux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:485`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L485)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:73`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L73)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:808`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L808)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1390-1398`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1390-L1398)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sthux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u16(ea, ctx.gpr[instr.rs()] as u16);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`sthx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sthx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:495`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L495)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:73`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L73)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:806`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L806)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1381-1389`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1381-L1389)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sthx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u16(ea, ctx.gpr[instr.rs()] as u16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Stores low 16 bits of `RS`.** Writes `(RS)[48:63]` — the low half-word — at `EA`. The xenia snapshot does `mem.write_u16(ea, ctx.gpr[instr.rs()] as u16)`. The high 48 bits of `RS` are ignored: storing a 64-bit value through `sth` silently truncates.
|
||||
- **Big-endian write.** Byte at `EA` is the high byte of the half (`RS[48:55]`), byte at `EA+1` is the low byte (`RS[56:63]`). On little-endian hosts the byte-swap happens at the memory boundary.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `sth` and `sthx` selects literal zero. Update forms `sthu` / `sthux` invoke `RA = 0` as an invalid form.
|
||||
- **Update-form post-write.** `sthu` / `sthux` write the computed `EA` back to `RA` after the store.
|
||||
- **No alignment requirement.** Xenon tolerates unaligned half-word stores; the two bytes are written at `EA` and `EA+1` regardless of alignment.
|
||||
- **Common in audio / Unicode code.** Standard store for 16-bit PCM samples and UTF-16 code units. Compilers emit `sth` for `short *` writes.
|
||||
- **Cache effects.** A `sth` to a cold line triggers a read-allocate; for bulk half-word writes to a fresh line, prefer pre-clearing with [`dcbz128`](dcbz.md).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stb`](stb.md), [`stw`](stw.md), [`std`](std.md) — narrower / wider stores.
|
||||
- [`sthbrx`](sthbrx.md) — byte-reversed half-word store (little-endian half).
|
||||
- [`lhz`](lhz.md), [`lha`](lha.md) — corresponding loads (zero / sign extension).
|
||||
- [`stmw`](stmw.md), [`stswi`](stswi.md), [`stswx`](stswx.md) — bulk stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sth` (Store Half)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sth-store-half-instruction)
|
||||
- [AIX 7.3 — `sthu` / `sthx` / `sthux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sthu-store-half-update-instruction)
|
||||
131
migration/project-root/ppc-manual/memory/sthbrx.md
Normal file
131
migration/project-root/ppc-manual/memory/sthbrx.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# `sthbrx` — Store Half Word Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00072c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sthbrx` | `sthbrx` | — | Store Half Word Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sthbrx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sthbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00072c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `918`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | sthbrx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | sthbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | sthbrx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sthbrx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `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
|
||||
|
||||
**`sthbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sthbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:667`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L667)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:73`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L73)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:846`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L846)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1822-1830`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1822-L1830)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sthbrx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u16(ea, (ctx.gpr[instr.rs()] as u16).swap_bytes());
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Writes little-endian half.** Takes the low 16 bits of `RS`, swaps the two bytes, writes them at `EA`. After execution, byte at `EA` is `RS[56:63]` (low byte) and byte at `EA+1` is `RS[48:55]` (high byte). The xenia snapshot does `(ctx.gpr[instr.rs()] as u16).swap_bytes()`.
|
||||
- **Used to emit little-endian half-words.** Symmetric counterpart of [`lhbrx`](lhbrx.md). Common in PNG / ZIP / RIFF chunk emit paths.
|
||||
- **High bits of `RS` ignored.** Storing a 64-bit value through `sthbrx` truncates and reverses only the low half-word; the high 48 bits are not consulted.
|
||||
- **X-form only — no D-form, no update form.** Only the indexed form exists. `EA = (RA|0) + RB`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `sthbrx RS, 0, RB` writes at exact `RB`.
|
||||
- **Alignment.** Hardware tolerates unaligned half-word writes; cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **No CR / FPSCR effects.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lhbrx`](lhbrx.md) — load-half byte-reverse (matching load).
|
||||
- [`stwbrx`](stwbrx.md), [`stdbrx`](stdbrx.md) — wider byte-reverse stores.
|
||||
- [`sth`](sth.md), [`sthx`](sth.md) — non-reversing half stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sthbrx` (Store Half Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sthbrx-store-half-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
143
migration/project-root/ppc-manual/memory/stmw.md
Normal file
143
migration/project-root/ppc-manual/memory/stmw.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# `stmw` — Store Multiple Word
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0xbc000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stmw` | `stmw` | — | Store Multiple Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stmw` — form `D`
|
||||
|
||||
- **Opcode word:** `0xbc000000`
|
||||
- **Primary opcode (bits 0–5):** `47`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stmw`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`stmw`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stmw"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:527`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L527)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:75`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L75)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:370`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L370)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1735-1759`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1735-L1759)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stmw => {
|
||||
let mut ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
ea = ea.wrapping_add(instr.d() as i64 as u64);
|
||||
// PPCBUG-160: stmw can span two cache lines when (32-rs)*4 > one line.
|
||||
// Iterate over every touched line so any reservation on a later line
|
||||
// is also invalidated (same guarantee as single-word stores).
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let start_ea = ea as u32;
|
||||
let last_ea = start_ea.wrapping_add((32 - instr.rs() as u32) * 4).wrapping_sub(1);
|
||||
let line_size = RESERVATION_MASK + 1;
|
||||
let mut line = start_ea & !RESERVATION_MASK;
|
||||
loop {
|
||||
t.invalidate_for_write(line);
|
||||
if line >= (last_ea & !RESERVATION_MASK) { break; }
|
||||
line = line.wrapping_add(line_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
for r in instr.rs()..32 {
|
||||
mem.write_u32(ea as u32, ctx.gpr[r] as u32);
|
||||
ea = ea.wrapping_add(4);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Bulk register save.** Stores `(32 - RS)` consecutive 32-bit words taken from `r[RS]`, `r[RS+1]`, …, `r31` to memory starting at `EA`. The symmetric counterpart of [`lmw`](lmw.md). Used by AIX/PowerPC ABI prologues to save non-volatile GPRs in one instruction.
|
||||
- **Each store is the low 32 bits of the GPR.** Xenia's snapshot writes `ctx.gpr[r] as u32` — only the low half of the 64-bit GPR. The high 32 bits are discarded; `stmw` cannot save 64-bit values (use a sequence of [`std`](std.md) instead).
|
||||
- **Big-endian write.** Word from `r[RS]` lands at `EA`, word from `r[RS+1]` at `EA+4`, etc. Each word is itself written most-significant-byte first.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is the literal zero. Useful for absolute-address restoration.
|
||||
- **Alignment.** PowerISA requires word-aligned `EA`; an unaligned `stmw` may raise an alignment exception on hardware. Xenia tolerates it.
|
||||
- **Performance trap.** Modern PowerPC implementations microcode `stmw` — typically slower than the same number of `stw` instructions. Compilers prefer the unrolled form.
|
||||
- **Cache-line behaviour.** When the run of words crosses several 128-byte cache lines, each cold line triggers a read-allocate. Pre-clearing with [`dcbz128`](dcbz.md) helps for fresh frames.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lmw`](lmw.md) — symmetric "load multiple words" (the matching epilogue partner).
|
||||
- [`stw`](stw.md), [`stwx`](stw.md) — single-word stores; the modern preferred form.
|
||||
- [`stswi`](stswi.md), [`stswx`](stswx.md) — store string (byte-granular bulk transfer).
|
||||
- [`std`](std.md) — for 64-bit values (no "store multiple doubleword" exists).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stmw` (Store Multiple Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stmw-store-multiple-word-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store Multiple".
|
||||
145
migration/project-root/ppc-manual/memory/stswi.md
Normal file
145
migration/project-root/ppc-manual/memory/stswi.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# `stswi` — Store String Word Immediate
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0005aa`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stswi` | `stswi` | — | Store String Word Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stswi` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0005aa`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `725`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stswi`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`stswi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stswi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:737`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L737)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:75`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L75)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:835`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L835)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1540-1564`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1540-L1564)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stswi => {
|
||||
let mut ea = if instr.ra() == 0 { 0u32 } else { ctx.gpr[instr.ra()] as u32 };
|
||||
let nb = if instr.nb() == 0 { 32 } else { instr.nb() };
|
||||
let mut rs = instr.rs();
|
||||
let mut bytes_left = nb;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(nb - 1) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
while bytes_left > 0 {
|
||||
let val = ctx.gpr[rs] as u32;
|
||||
for byte_idx in 0..4 {
|
||||
if bytes_left == 0 { break; }
|
||||
mem.write_u8(ea, (val >> (24 - byte_idx * 8)) as u8);
|
||||
ea = ea.wrapping_add(1);
|
||||
bytes_left -= 1;
|
||||
}
|
||||
rs = (rs + 1) % 32;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Byte-granular bulk store.** Symmetric counterpart of [`lswi`](lswi.md). Reads the low 32 bits of `RS`, `RS+1`, …, takes the top byte of each (then the next, etc.) and writes successive bytes at `EA`. The byte count `NB` is in the `RB` field of the encoding (1..31), with `NB = 0` meaning "32 bytes".
|
||||
- **Register wraparound at r31 → r0.** Xenia's snapshot increments `rs = (rs + 1) % 32`. After r31 the source becomes r0, then r1, etc. Rare in practice; AIX flags overlapping register / address ranges as invalid.
|
||||
- **Big-endian byte ordering inside each register.** Writes the most-significant byte first: `mem.write_u8(ea, (val >> 24) as u8)`, then bits 16–23, etc. Matches the byte order produced by [`lswi`](lswi.md), so a `lswi`/`stswi` pair round-trips a buffer.
|
||||
- **Last partial register.** When `NB` is not a multiple of 4, the final source register has its trailing low bytes ignored — only the leading bytes that fit in the byte budget are written.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero. `stswi` is not an update form; `RA` is not modified.
|
||||
- **Alignment.** Architecture allows arbitrary alignment; cache-inhibited storage may raise alignment exceptions on hardware.
|
||||
- **Vanishingly rare in compiled code.** Compilers don't emit `stswi`. Hand-written `memcpy` cores may.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lswi`](lswi.md) — symmetric load.
|
||||
- [`stswx`](stswx.md) — register-supplied byte-count variant.
|
||||
- [`stmw`](stmw.md) — word-granular bulk store.
|
||||
- [`stw`](stw.md), [`stb`](stb.md) — scalar stores compilers actually emit.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stswi` (Store String Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stswi-store-string-word-immediate-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store String".
|
||||
148
migration/project-root/ppc-manual/memory/stswx.md
Normal file
148
migration/project-root/ppc-manual/memory/stswx.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# `stswx` — Store String Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00052a`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stswx` | `stswx` | — | Store String Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
(no disassembly template)
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stswx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00052a`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `661`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stswx`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **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
|
||||
|
||||
**`stswx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stswx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:742`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L742)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:75`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L75)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:830`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L830)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4663-4689`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4663-L4689)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stswx => {
|
||||
let mut ea = ea_indexed(ctx, instr);
|
||||
let nb = ctx.xer() & 0x7F;
|
||||
let mut rs = instr.rs();
|
||||
let mut bytes_left = nb;
|
||||
if nb > 0 {
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(nb - 1) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
}
|
||||
while bytes_left > 0 {
|
||||
let val = ctx.gpr[rs] as u32;
|
||||
for byte_idx in 0..4 {
|
||||
if bytes_left == 0 { break; }
|
||||
mem.write_u8(ea, (val >> (24 - byte_idx * 8)) as u8);
|
||||
ea = ea.wrapping_add(1);
|
||||
bytes_left -= 1;
|
||||
}
|
||||
rs = (rs + 1) % 32;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Byte count from `XER[25..31]`.** Unlike `stswi`, the byte count `NB` (0..127) is read from `XER[25..31]`. The xenia snapshot does `let nb = (ctx.xer() & 0x7F) as u32;`. `NB = 0` means literally zero bytes — the instruction becomes a no-op.
|
||||
- **Register packing identical to `stswi`.** Bytes are pulled from successive GPRs, four bytes per register, big-endian within each register, with wraparound `r31 → r0`. The final partial register's unused trailing bytes are not written.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero. The instruction has no update form — `RA` is not modified.
|
||||
- **Invalid forms.** AIX flags as invalid the cases where the byte-stream wraps through `RA` or `RB` while reading the source registers; xenia performs writes regardless.
|
||||
- **Big-endian byte ordering inside each register.** Writes most-significant byte of each source GPR's low word first.
|
||||
- **Used for non-multiple-of-4 copies.** Together with `lswx`, gives a way to store a runtime-determined byte count without per-byte loops. Compilers don't emit it.
|
||||
- **Alignment.** Architecture allows arbitrary alignment; cache-inhibited storage may raise alignment exceptions on hardware.
|
||||
- **No CR / FPSCR effects.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lswx`](lswx.md) — symmetric load.
|
||||
- [`stswi`](stswi.md) — sibling with byte count encoded as `RB` field (immediate-style).
|
||||
- [`stmw`](stmw.md) — word-granular bulk store (no byte tail handling).
|
||||
- [`stw`](stw.md), [`stb`](stb.md) — scalar stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stswx` (Store String Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stswx-store-string-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Load and Store String" for invalid-form rules and `XER` interaction.
|
||||
136
migration/project-root/ppc-manual/memory/stvebx.md
Normal file
136
migration/project-root/ppc-manual/memory/stvebx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `stvebx` — Store Vector Element Byte Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00010e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvebx` | `stvebx` | — | Store Vector Element Byte Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvebx [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvebx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00010e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `135`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvebx: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvebx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvebx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvebx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvebx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvebx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:152`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L152)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:778`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L778)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1914-1926`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1914-L1926)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvebx => {
|
||||
// Store vS[EA & 0xF] (1 byte) to memory at EA.
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
// PPCBUG-512: stvebx was missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let slot = (ea & 0xF) as usize;
|
||||
let bytes = ctx.vr[instr.rs()].as_bytes();
|
||||
mem.write_u8(ea, bytes[slot]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single-byte element store.** Architecturally `stvebx` writes exactly **one** byte from lane `EA mod 16` of `VS` to address `EA`. Other lanes are unaffected, and other memory bytes are unaffected.
|
||||
- **Xenia simplification — full 16-byte write.** The xenia snapshot is shared with `stvehx` / `stvewx` and writes the **entire 16-byte aligned line** (`ea & ~0xF`, then 16 bytes from the vector). This is stronger than the architectural single-byte store — it overwrites 15 adjacent bytes with whatever the source vector holds. Code that depends on architectural per-byte granularity (e.g. interleaved writes from multiple threads / DMA agents into the same line) may behave differently than on hardware.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **No update form, no VMX128 sibling.** No `stvebux`; no `stvebx128` — single-byte stores were kept Altivec-only in the Xbox 360 extension.
|
||||
- **Big-endian within the line.** Lane 0 of `VS` corresponds to the byte at the aligned base address.
|
||||
- **Common idiom.** Pair with `vsplt*` to broadcast a value, then `stvebx` to write one byte. Less efficient than `stb` from a GPR; rare in compiled code.
|
||||
- **Hardware fault model.** A protected or unmapped page raises a DSI exception just as for any store.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvehx`](stvehx.md), [`stvewx`](stvewx.md) — single half / single word element stores.
|
||||
- [`stvx`](stvx.md), [`stvxl`](stvxl.md) — full 16-byte aligned vector stores.
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — store-left / store-right unaligned vector ops.
|
||||
- [`lvebx`](lvebx.md) — symmetric single-byte load.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvebx` (Store Vector Element Byte Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvebx-store-vector-element-byte-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" § "Vector Load and Store" for canonical per-byte semantics.
|
||||
138
migration/project-root/ppc-manual/memory/stvehx.md
Normal file
138
migration/project-root/ppc-manual/memory/stvehx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `stvehx` — Store Vector Element Half Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00014e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvehx` | `stvehx` | — | Store Vector Element Half Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvehx [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvehx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00014e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `167`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvehx: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvehx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvehx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvehx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvehx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvehx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:160`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L160)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:784`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L784)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1927-1941`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1927-L1941)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvehx => {
|
||||
// Store vS[slot] (1 halfword) at EA & ~1. slot = (EA & 0xF) >> 1.
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea_unaligned = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let ea = ea_unaligned & !0x1u32;
|
||||
// PPCBUG-512: stvehx was missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let slot = ((ea_unaligned & 0xF) >> 1) as usize;
|
||||
let bytes = ctx.vr[instr.rs()].as_bytes();
|
||||
let h = ((bytes[slot * 2] as u16) << 8) | (bytes[slot * 2 + 1] as u16);
|
||||
mem.write_u16(ea, h);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single half-word element store.** Architecturally `stvehx` writes exactly **two** bytes from half-word lane `(EA mod 16) >> 1` of `VS` to address `EA & ~1` (low bit forced to half-aligned). Other lanes are unaffected, and bytes outside the 2-byte window are unaffected.
|
||||
- **Xenia simplification — full 16-byte write.** The xenia snapshot is shared with `stvebx` / `stvewx`: writes 16 bytes of the source vector at `ea & ~0xF`. This is stronger than the architectural 2-byte store — it overwrites 14 adjacent bytes that hardware would have left alone.
|
||||
- **EA forced half-aligned.** Hardware drops the low bit; xenia's shared snapshot drops the low four bits.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **No update form, no VMX128 sibling.** No `stvehux`; no `stvehx128`.
|
||||
- **Big-endian half within the lane.** The byte at the lower address is the most-significant byte of the half-word lane.
|
||||
- **Common idiom.** Pair with `vsplth` to broadcast then store one half; rare in compiled code (compilers prefer `sth`).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvebx`](stvebx.md), [`stvewx`](stvewx.md) — single byte / word element stores.
|
||||
- [`stvx`](stvx.md), [`stvxl`](stvxl.md) — full 16-byte aligned vector stores.
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — store-left / store-right unaligned ops.
|
||||
- [`lvehx`](lvehx.md) — symmetric single-half load.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvehx` (Store Vector Element Half Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvehx-store-vector-element-half-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility" § "Vector Load and Store".
|
||||
198
migration/project-root/ppc-manual/memory/stvewx.md
Normal file
198
migration/project-root/ppc-manual/memory/stvewx.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# `stvewx` — Store Vector Element Word Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00018e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvewx` | `stvewx` | — | Store Vector Element Word Indexed |
|
||||
| `stvewx128` | `stvewx128` | — | Store Vector Element Word Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvewx [VS], [RA0], [RB]
|
||||
stvewx128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvewx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00018e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `199`
|
||||
- **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 |
|
||||
|
||||
### `stvewx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000183`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `387`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvewx: read; stvewx128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvewx: read; stvewx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvewx: read; stvewx128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvewx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvewx128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvewx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvewx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:180`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L180)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:788`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L788)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1942-1959`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1942-L1959)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvewx => {
|
||||
// Store vS[slot] (1 word) at EA & ~3. slot = (EA & 0xF) >> 2.
|
||||
let base = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea_unaligned = base.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let ea = ea_unaligned & !0x3u32;
|
||||
// PPCBUG-512: stvewx was missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let slot = ((ea_unaligned & 0xF) >> 2) as usize;
|
||||
let bytes = ctx.vr[instr.rs()].as_bytes();
|
||||
let w = ((bytes[slot * 4] as u32) << 24)
|
||||
| ((bytes[slot * 4 + 1] as u32) << 16)
|
||||
| ((bytes[slot * 4 + 2] as u32) << 8)
|
||||
| (bytes[slot * 4 + 3] as u32);
|
||||
mem.write_u32(ea, w);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvewx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvewx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:183`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L183)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:416`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L416)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3175-3192`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3175-L3192)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvewx128 => {
|
||||
// Mirror of stvewx: word-align EA, extract one 32-bit lane, write 4 bytes only.
|
||||
// Previous code used & !0xF (16-byte) and wrote all 16 bytes, corrupting 12
|
||||
// adjacent bytes on every execution (PPCBUG-510).
|
||||
let ea_unaligned = ea_indexed(ctx, instr);
|
||||
let ea = ea_unaligned & !0x3u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let slot = ((ea_unaligned & 0xF) >> 2) as usize;
|
||||
let bytes = ctx.vr[instr.vs128()].as_bytes();
|
||||
let w = ((bytes[slot * 4] as u32) << 24)
|
||||
| ((bytes[slot * 4 + 1] as u32) << 16)
|
||||
| ((bytes[slot * 4 + 2] as u32) << 8)
|
||||
| (bytes[slot * 4 + 3] as u32);
|
||||
mem.write_u32(ea, w);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single word element store.** Architecturally `stvewx` writes exactly **four** bytes from word lane `(EA mod 16) >> 2` of `VS` to address `EA & ~3` (low two bits forced to word-aligned). Other lanes are unaffected, and bytes outside the 4-byte window are unaffected.
|
||||
- **Xenia simplification — full 16-byte write.** Both `stvewx` and `stvewx128` snapshots write the full 16 bytes of the source vector at `ea & ~0xF`. This overwrites 12 bytes that hardware would have left alone.
|
||||
- **EA forced word-aligned.** Hardware drops the low two bits; xenia's snapshots drop the low four.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **No update form.** No `stvewux`.
|
||||
- **VMX128 sibling (`stvewx128`).** Identical semantics; alternative operand encoding addressing `v0..v127` via the split-field 7-bit register index.
|
||||
- **Big-endian word within the lane.** The byte at the lower address is the most-significant byte.
|
||||
- **Common idiom.** Pair with `vspltw` to broadcast a 32-bit FP/integer value, then `stvewx` to commit one lane. Less common than `stw` from a GPR.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvebx`](stvebx.md), [`stvehx`](stvehx.md) — single byte / half element stores.
|
||||
- [`stvx`](stvx.md), [`stvx128`](stvx.md), [`stvxl`](stvxl.md) — full 16-byte aligned vector stores.
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — store-left / store-right unaligned ops.
|
||||
- [`lvewx`](lvewx.md), [`lvewx128`](lvewx.md) — symmetric single-word loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvewx` (Store Vector Element Word Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvewx-store-vector-element-word-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for `stvewx128`.
|
||||
193
migration/project-root/ppc-manual/memory/stvlx.md
Normal file
193
migration/project-root/ppc-manual/memory/stvlx.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# `stvlx` — Store Vector Left Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00050e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvlx` | `stvlx` | — | Store Vector Left Indexed |
|
||||
| `stvlx128` | `stvlx128` | — | Store Vector Left Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvlx [VS], [RA0], [RB]
|
||||
stvlx128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvlx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00050e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `647`
|
||||
- **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 |
|
||||
|
||||
### `stvlx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000503`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1283`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvlx: read; stvlx128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvlx: read; stvlx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvlx: read; stvlx128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvlx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvlx128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvlx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvlx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:265`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L265)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:828`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L828)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3103-3119`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3103-L3119)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvlx | PpcOpcode::stvlxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-513: stvlx/stvlxl were missing invalidate_for_write.
|
||||
// store_vector_left writes [ea, (ea & !0xF)+15]; in the worst case (ea & 0xF == 0)
|
||||
// that is exactly 16 bytes all within the same 16-byte block, so ea+15 lands in the
|
||||
// same 128-byte cache line. Two-call form is kept for defensive correctness.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_left(mem, ea, ctx.vr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvlx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvlx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:268`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L268)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:422`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L422)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3120-3133`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3120-L3133)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvlx128 | PpcOpcode::stvlxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-513: stvlx128/stvlxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_left(mem, ea, ctx.vr[instr.vs128()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Store-left half of an unaligned vector.** `stvlx` writes `(16 - (EA mod 16))` bytes from the **left** (low-lane) half of `VS` to addresses starting at the **exact** `EA`. The right half of `VS` is not stored. Combine with `stvrx` at `EA + 16` to commit a full unaligned vector across an alignment boundary.
|
||||
- **Companion idiom.** `stvlx VS, RA, RB ; stvrx VS, RA, RB+16` writes the 16 bytes of `VS` to address `EA` regardless of alignment. The two halves are byte-disjoint, so the order between them doesn't affect correctness.
|
||||
- **No alignment masking.** Unlike `stvx`, the `EA` is **not** rounded down. `EA mod 16` controls how the source vector splits.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of the VMX128 / Cell BE extended set, not in baseline Altivec.
|
||||
- **Implementation in xenia.** The shared snapshot calls `vmx::store_vector_left(mem, ea, vs)`, performing the unaligned partial-byte write.
|
||||
- **VMX128 sibling (`stvlx128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
- **`stvlxl` is the LRU-hint variant.** Same data behaviour, hint ignored under emulation.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvrx`](stvrx.md), [`stvrx128`](stvrx.md) — store-right partner.
|
||||
- [`stvlxl`](stvlxl.md), [`stvlxl128`](stvlxl.md) — LRU-hint variants.
|
||||
- [`stvx`](stvx.md), [`stvx128`](stvx.md) — aligned store (the EA-masking sibling).
|
||||
- [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) — symmetric unaligned loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvlx` (Store Vector Left Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvlx-store-vector-left-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 unaligned stores.
|
||||
192
migration/project-root/ppc-manual/memory/stvlxl.md
Normal file
192
migration/project-root/ppc-manual/memory/stvlxl.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# `stvlxl` — Store Vector Left Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00070e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvlxl` | `stvlxl` | — | Store Vector Left Indexed LRU |
|
||||
| `stvlxl128` | `stvlxl128` | — | Store Vector Left Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvlxl [VS], [RA0], [RB]
|
||||
stvlxl128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvlxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00070e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `903`
|
||||
- **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 |
|
||||
|
||||
### `stvlxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000703`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1795`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvlxl: read; stvlxl128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvlxl: read; stvlxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvlxl: read; stvlxl128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvlxl`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvlxl128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvlxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvlxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:271`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L271)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:845`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L845)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3103-3119`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3103-L3119)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvlx | PpcOpcode::stvlxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-513: stvlx/stvlxl were missing invalidate_for_write.
|
||||
// store_vector_left writes [ea, (ea & !0xF)+15]; in the worst case (ea & 0xF == 0)
|
||||
// that is exactly 16 bytes all within the same 16-byte block, so ea+15 lands in the
|
||||
// same 128-byte cache line. Two-call form is kept for defensive correctness.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_left(mem, ea, ctx.vr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvlxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvlxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:274`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L274)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:77`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L77)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:426`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L426)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3120-3133`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3120-L3133)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvlx128 | PpcOpcode::stvlxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-513: stvlx128/stvlxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_left(mem, ea, ctx.vr[instr.vs128()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`stvlx`](stvlx.md), with LRU cache hint.** Writes `(16 - (EA mod 16))` bytes from the left half of `VS` starting at `EA`; right half not stored. The `l` suffix marks the touched line as least-recently-used.
|
||||
- **Hint ignored under emulation.** Xenia's snapshot is shared with `stvlx` (`PpcOpcode::stvlx | PpcOpcode::stvlxl => …`).
|
||||
- **No alignment masking.** The exact `EA` controls how data is split.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of VMX128 / Cell BE.
|
||||
- **Streaming write use case.** Pair with [`stvrxl`](stvrxl.md) when the buffer is one-pass output that should not pollute the cache.
|
||||
- **VMX128 sibling (`stvlxl128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvlx`](stvlx.md), [`stvlx128`](stvlx.md) — non-hint variants.
|
||||
- [`stvrxl`](stvrxl.md), [`stvrxl128`](stvrxl.md) — store-right LRU partner.
|
||||
- [`stvxl`](stvxl.md), [`stvxl128`](stvxl.md) — aligned LRU vector store.
|
||||
- [`lvlxl`](lvlxl.md), [`lvrxl`](lvrxl.md) — symmetric LRU loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvlxl` (Store Vector Left Indexed Last)](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for cache-hint behaviour.
|
||||
193
migration/project-root/ppc-manual/memory/stvrx.md
Normal file
193
migration/project-root/ppc-manual/memory/stvrx.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# `stvrx` — Store Vector Right Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00054e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvrx` | `stvrx` | — | Store Vector Right Indexed |
|
||||
| `stvrx128` | `stvrx128` | — | Store Vector Right Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvrx [VS], [RA0], [RB]
|
||||
stvrx128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00054e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `679`
|
||||
- **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 |
|
||||
|
||||
### `stvrx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000543`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1347`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvrx: read; stvrx128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvrx: read; stvrx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvrx: read; stvrx128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvrx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvrx128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:290`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L290)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:78`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L78)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:833`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L833)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3134-3150`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3134-L3150)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvrx | PpcOpcode::stvrxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-514: stvrx/stvrxl were missing invalidate_for_write.
|
||||
// store_vector_right writes [ea & !0xF, ea-1] (up to 15 bytes, all within a single
|
||||
// 16-byte-aligned block). Two-call form is kept for defensive correctness.
|
||||
// stvrx at shift==0 is a no-op; the guard fires unconditionally (cheap).
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_right(mem, ea, ctx.vr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvrx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvrx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:293`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L293)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:78`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L78)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:423`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L423)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3151-3164`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3151-L3164)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvrx128 | PpcOpcode::stvrxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-514: stvrx128/stvrxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_right(mem, ea, ctx.vr[instr.vs128()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Store-right half of an unaligned vector.** `stvrx` writes `(EA mod 16)` bytes from the **right** (high-lane) half of `VS` to the addresses *just below* `EA & ~0xF` (the bytes from the previous aligned line that fall on the right side of the unaligned vector). The left half of `VS` is not stored.
|
||||
- **Standard pair-mate of [`stvlx`](stvlx.md).** `stvlx VS, RA, RB ; stvrx VS, RA, RB+16` (or analogous addressing) commits the 16 bytes of `VS` to address `EA` regardless of alignment. The two halves are byte-disjoint, so order is irrelevant for correctness.
|
||||
- **No alignment masking.** Unlike `stvx`, the exact `EA` is used; `EA mod 16` controls how `VS` splits.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of the VMX128 / Cell BE extended set.
|
||||
- **Implementation in xenia.** The shared snapshot calls `vmx::store_vector_right(mem, ea, vs)`, performing the unaligned partial-byte write of the right side.
|
||||
- **VMX128 sibling (`stvrx128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
- **`stvrxl` is the LRU-hint variant.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvlx`](stvlx.md), [`stvlx128`](stvlx.md) — store-left partner.
|
||||
- [`stvrxl`](stvrxl.md), [`stvrxl128`](stvrxl.md) — LRU-hint variants.
|
||||
- [`stvx`](stvx.md), [`stvx128`](stvx.md) — aligned vector store.
|
||||
- [`lvrx`](lvrx.md), [`lvlx`](lvlx.md) — symmetric unaligned loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvrx` (Store Vector Right Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvrx-store-vector-right-indexed-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for VMX128 unaligned stores.
|
||||
192
migration/project-root/ppc-manual/memory/stvrxl.md
Normal file
192
migration/project-root/ppc-manual/memory/stvrxl.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# `stvrxl` — Store Vector Right Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00074e`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvrxl` | `stvrxl` | — | Store Vector Right Indexed LRU |
|
||||
| `stvrxl128` | `stvrxl128` | — | Store Vector Right Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvrxl [VS], [RA0], [RB]
|
||||
stvrxl128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvrxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00074e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `935`
|
||||
- **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 |
|
||||
|
||||
### `stvrxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x10000743`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `1859`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvrxl: read; stvrxl128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvrxl: read; stvrxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvrxl: read; stvrxl128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvrxl`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvrxl128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvrxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvrxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:296`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L296)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:78`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L78)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:848`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L848)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3134-3150`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3134-L3150)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvrx | PpcOpcode::stvrxl => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-514: stvrx/stvrxl were missing invalidate_for_write.
|
||||
// store_vector_right writes [ea & !0xF, ea-1] (up to 15 bytes, all within a single
|
||||
// 16-byte-aligned block). Two-call form is kept for defensive correctness.
|
||||
// stvrx at shift==0 is a no-op; the guard fires unconditionally (cheap).
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_right(mem, ea, ctx.vr[instr.rs()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvrxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvrxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:299`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L299)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:78`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L78)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:427`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L427)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3151-3164`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3151-L3164)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvrx128 | PpcOpcode::stvrxl128 => {
|
||||
let ea = ea_indexed(ctx, instr);
|
||||
// PPCBUG-514: stvrx128/stvrxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() {
|
||||
let first_line = ea & !RESERVATION_MASK;
|
||||
let last_line = ea.wrapping_add(15) & !RESERVATION_MASK;
|
||||
t.invalidate_for_write(first_line);
|
||||
if last_line != first_line { t.invalidate_for_write(last_line); }
|
||||
}
|
||||
}
|
||||
crate::vmx::store_vector_right(mem, ea, ctx.vr[instr.vs128()]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`stvrx`](stvrx.md), with LRU cache hint.** Writes `(EA mod 16)` bytes from the right half of `VS` to the addresses just below `EA & ~0xF`. The `l` suffix marks the touched line as least-recently-used.
|
||||
- **Hint ignored under emulation.** Xenia's snapshot is shared with `stvrx` (`PpcOpcode::stvrx | PpcOpcode::stvrxl => …`).
|
||||
- **No alignment masking.** Exact `EA` used.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **Microsoft Xbox 360 specific.** Part of VMX128 / Cell BE.
|
||||
- **Streaming write use case.** Pair with [`stvlxl`](stvlxl.md) for a one-pass unaligned vector store sequence that signals "do not retain" to the cache.
|
||||
- **VMX128 sibling (`stvrxl128`).** Identical semantics; alternative operand encoding addressing `v0..v127`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvrx`](stvrx.md), [`stvrx128`](stvrx.md) — non-hint variants.
|
||||
- [`stvlxl`](stvlxl.md), [`stvlxl128`](stvlxl.md) — store-left LRU partner.
|
||||
- [`stvxl`](stvxl.md), [`stvxl128`](stvxl.md) — aligned LRU vector store.
|
||||
- [`lvrxl`](lvrxl.md), [`lvlxl`](lvlxl.md) — symmetric LRU loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvrxl` (Store Vector Right Indexed Last)](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for cache-hint behaviour.
|
||||
177
migration/project-root/ppc-manual/memory/stvx.md
Normal file
177
migration/project-root/ppc-manual/memory/stvx.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# `stvx` — Store Vector Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0001ce`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvx` | `stvx` | — | Store Vector Indexed |
|
||||
| `stvx128` | `stvx128` | — | Store Vector Indexed 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvx [VS], [RA0], [RB]
|
||||
stvx128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0001ce`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `231`
|
||||
- **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 |
|
||||
|
||||
### `stvx128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x100001c3`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `451`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvx: read; stvx128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvx: read; stvx128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvx: read; stvx128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvx`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvx128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- ((RA|0) + (RB)) & ~0xF ; align to 16
|
||||
MEM(EA, 16) <- byteswap(VS)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* stvx VS, RA, RB — 16-byte aligned store of a vector register */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
uint32_t ea = (uint32_t)((base + r[insn.RB]) & ~(uint64_t)0xF);
|
||||
mem_write_vec128_be(ea, v[insn.VS]);
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`stvx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:193`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L193)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:79`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L79)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:791`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L791)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1849-1859`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1849-L1859)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
// PPCBUG-511: stvx was missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let bytes = ctx.vr[instr.rs()].as_bytes();
|
||||
for i in 0..16 { mem.write_u8(ea + i as u32, bytes[i]); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvx128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvx128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:196`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L196)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:79`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L79)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:417`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L417)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1860-1870`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1860-L1870)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvx128 => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
// PPCBUG-511: stvx128 was missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let bytes = ctx.vr[instr.vs128()].as_bytes();
|
||||
for i in 0..16 { mem.write_u8(ea + i as u32, bytes[i]); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
EA <- ((RA|0) + (RB)) & ~0xF ; force 16-byte alignment
|
||||
MEM(EA, 16) <- byte_order_adjusted(VS) ; lane 0 at EA, lane 15 at EA+15
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Alignment is forced, not checked.** The low four bits of the effective address are **cleared** before the store — alignment violations silently corrupt adjacent data rather than trap. This differs from scalar `stw` (no alignment enforcement) and from `stvewx` (which stores only one element and keeps the exact EA).
|
||||
- **Big-endian lane layout.** Vector lane 0 (the most-significant bytes of the 128-bit register) lives at the lowest address; lane 15 at `EA + 15`. On little-endian hosts the whole 16-byte block is byte-swapped at the memory boundary so the PowerPC-visible layout is preserved. Xenia's helper `mem_write_vec128_be` handles this.
|
||||
- **`RA0` semantics.** When `RA = 0` the base is the literal zero — just like scalar loads/stores. Combined with the alignment mask this lets `stvx VS, 0, RB` store to address `RB & ~0xF`.
|
||||
- **No update form.** Unlike scalar stores, VMX stores have no `u` variant that post-writes the base. Use [`stvxl`](stvxl.md) for the cache-hint variant (suggests "last" — the line is not expected to be reused soon).
|
||||
- **VMX128 sibling (`stvx128`).** Identical semantics; the only difference is the operand encoding. VMX128 uses a 7-bit register index split across three non-contiguous bit fields (`VS128l ‖ VS128h`) so it can address `v0..v127` instead of the 32-register Altivec space. All alignment, byte-order and `RA0` rules are the same.
|
||||
- **Read-before-write.** The 16-byte write occurs as one conceptual store; subsequent loads from the same address observe the complete new value. There's no split-transaction window visible to software.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lvx`](lvx.md), [`lvx128`](lvx.md) — the load counterparts.
|
||||
- [`stvxl`](stvxl.md), [`stvxl128`](stvxl.md) — cache-hint "last-use" variants.
|
||||
- [`stvebx`](stvebx.md) / [`stvehx`](stvehx.md) / [`stvewx`](stvewx.md) — store single element (byte / half / word) at the exact (unaligned) address.
|
||||
- [`stvlx`](stvlx.md) / [`stvrx`](stvrx.md) — store-left / store-right for unaligned vector I/O.
|
||||
- [`dcbz`](dcbz.md) — zero a cache line; often paired with `stvx` in block-fill idioms.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvx` (Store Vector Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvx-store-vector-indexed-instruction)
|
||||
- PowerISA Book II (Altivec / VMX). Xbox 360 VMX128 is Microsoft-documented in the XDK; xenia's `ppc-instructions.xml` captures the deltas.
|
||||
186
migration/project-root/ppc-manual/memory/stvxl.md
Normal file
186
migration/project-root/ppc-manual/memory/stvxl.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# `stvxl` — Store Vector Indexed LRU
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0003ce`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stvxl` | `stvxl` | — | Store Vector Indexed LRU |
|
||||
| `stvxl128` | `stvxl128` | — | Store Vector Indexed LRU 128 |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stvxl [VS], [RA0], [RB]
|
||||
stvxl128 [VS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stvxl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0003ce`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `487`
|
||||
- **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 |
|
||||
|
||||
### `stvxl128` — form `VX128_1`
|
||||
|
||||
- **Opcode word:** `0x100003c3`
|
||||
- **Primary opcode (bits 0–5):** `4`
|
||||
- **Extended opcode:** `963`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (4) |
|
||||
| 6–10 | `VD128l` | destination low 5 bits |
|
||||
| 11–15 | `RA` | address register |
|
||||
| 16–20 | `RB` | offset register |
|
||||
| 21–27 | `XO` | extended opcode |
|
||||
| 28–29 | `VD128h` | destination high 2 bits |
|
||||
| 30–31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `VS` | stvxl: read; stvxl128: read | Source vector register (alias for VD on stores). |
|
||||
| `RA0` | stvxl: read; stvxl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stvxl: read; stvxl128: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stvxl`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stvxl128`
|
||||
|
||||
- **Reads (always):** `VS`, `RA0`, `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
|
||||
|
||||
**`stvxl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvxl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:199`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L199)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:79`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L79)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:813`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L813)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1970-1981`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1970-L1981)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvxl | PpcOpcode::stvxl128 => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
// PPCBUG-511: stvxl/stvxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let vs = if matches!(instr.opcode, PpcOpcode::stvxl128) { instr.vs128() } else { instr.rs() };
|
||||
let bytes = ctx.vr[vs].as_bytes();
|
||||
for i in 0..16 { mem.write_u8(ea + i as u32, bytes[i]); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stvxl128`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stvxl128"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:202`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L202)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:79`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L79)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:419`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L419)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1970-1981`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1970-L1981)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stvxl | PpcOpcode::stvxl128 => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = (ea.wrapping_add(ctx.gpr[instr.rb()]) & !0xF) as u32;
|
||||
// PPCBUG-511: stvxl/stvxl128 were missing invalidate_for_write.
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
let vs = if matches!(instr.opcode, PpcOpcode::stvxl128) { instr.vs128() } else { instr.rs() };
|
||||
let bytes = ctx.vr[vs].as_bytes();
|
||||
for i in 0..16 { mem.write_u8(ea + i as u32, bytes[i]); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Same data effect as [`stvx`](stvx.md), with LRU cache hint.** Writes 16 bytes from `VS` at `EA & ~0xF`. The `l` suffix tells the cache the line is least-recently-used — useful for streaming output (e.g. one-pass writes to a render target the producer will not re-read).
|
||||
- **Hint ignored under emulation.** Xenia's snapshot is shared with the VMX128 variant; it implements only the data side. Hardware uses the hint to choose write-allocate vs. write-streaming behaviour.
|
||||
- **Alignment is forced, not checked.** Low four bits of `EA` are masked.
|
||||
- **Big-endian lane layout.** Lane 0 of `VS` lands at the aligned base; lane 15 at base+15.
|
||||
- **`RA0` semantics.** `RA = 0` selects literal zero.
|
||||
- **No update form.**
|
||||
- **VMX128 sibling (`stvxl128`).** Identical semantics; alternative operand encoding addressing `v0..v127` via the split-field 7-bit register index.
|
||||
- **Common in render-target writes.** Pair with [`dcbz128`](dcbz.md) to allocate-and-zero, then `stvxl` to commit each line of a streaming output buffer; the LRU hint frees cache for the next line.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stvx`](stvx.md), [`stvx128`](stvx.md) — non-hint variants.
|
||||
- [`lvxl`](lvxl.md), [`lvxl128`](lvxl.md) — symmetric LRU loads.
|
||||
- [`stvebx`](stvebx.md), [`stvehx`](stvehx.md), [`stvewx`](stvewx.md) — single-element stores.
|
||||
- [`stvlx`](stvlx.md), [`stvrx`](stvrx.md) — store-left / store-right unaligned ops.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stvxl` (Store Vector Indexed Last)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stvxl-store-vector-indexed-last-instruction)
|
||||
- `PowerISA v2.07B Book I` "Vector Facility"; Microsoft Xbox 360 XDK for `stvxl128`.
|
||||
257
migration/project-root/ppc-manual/memory/stw.md
Normal file
257
migration/project-root/ppc-manual/memory/stw.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# `stw` — Store Word
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x90000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stw` | `stw` | — | Store Word |
|
||||
| `stwu` | `stwu` | — | Store Word with Update |
|
||||
| `stwux` | `stwux` | — | Store Word with Update Indexed |
|
||||
| `stwx` | `stwx` | — | Store Word Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stw [RS], [d]([RA0])
|
||||
stwu [RS], [d]([RA])
|
||||
stwux [RS], [RA], [RB]
|
||||
stwx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stw` — form `D`
|
||||
|
||||
- **Opcode word:** `0x90000000`
|
||||
- **Primary opcode (bits 0–5):** `36`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stwu` — form `D`
|
||||
|
||||
- **Opcode word:** `0x94000000`
|
||||
- **Primary opcode (bits 0–5):** `37`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
### `stwux` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00016e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `183`
|
||||
- **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 |
|
||||
|
||||
### `stwx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00012e`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `151`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stw: read; stwu: read; stwux: read; stwx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stw: read; stwx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `d` | stw: read; stwu: read | 16-bit signed displacement (`d`) added to the base address register. |
|
||||
| `RA` | stwu: read; stwu: write; stwux: read; stwux: write | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | stwux: read; stwx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stw`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stwu`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `d`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stwux`
|
||||
|
||||
- **Reads (always):** `RS`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
### `stwx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
MEM(EA, 4) <- (RS)[32:63]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* stw RS, d(RA) */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
uint32_t ea = (uint32_t)(base + (int64_t)(int16_t)insn.D);
|
||||
mem_write_u32_be(ea, (uint32_t)r[insn.RS]);
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`stw`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stw"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:507`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L507)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:359`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L359)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1291-1299`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1291-L1299)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stw => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, ctx.gpr[instr.rs()] as u32);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stwu`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stwu"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:543`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L543)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:360`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L360)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1300-1308`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1300-L1308)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stwu => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(instr.d() as i64 as u64) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, ctx.gpr[instr.rs()] as u32);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stwux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stwux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:553`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L553)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:787`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L787)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1318-1326`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1318-L1326)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stwux => {
|
||||
let ea = ctx.gpr[instr.ra()].wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, ctx.gpr[instr.rs()] as u32);
|
||||
ctx.gpr[instr.ra()] = ea as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
**`stwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:563`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L563)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:783`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L783)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1309-1317`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1309-L1317)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stwx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, ctx.gpr[instr.rs()] as u32);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Stores low 32 bits of `RS`.** Writes `(RS)[32:63]` — the low word of the 64-bit GPR — at `EA`. The xenia snapshot does `mem.write_u32(ea, ctx.gpr[instr.rs()] as u32)`. The high 32 bits are silently truncated; use [`std`](std.md) to store all 64 bits.
|
||||
- **Big-endian write.** `RS[32:39]` (the most-significant byte of the low word) lands at `EA`; `RS[56:63]` at `EA+3`. On little-endian hosts the byte-swap happens at the memory boundary.
|
||||
- **`RA0` (non-update forms).** `RA = 0` in `stw` and `stwx` selects literal zero. Update forms `stwu` / `stwux` invoke `RA = 0` as an invalid form. **The classic frame-allocation idiom** `stwu r1, -framesize(r1)` exploits the update form: it writes the old SP at the new SP and updates `r1` in one instruction.
|
||||
- **Update-form post-write.** `stwu` / `stwux` write `EA` to `RA` after the store. Order is store-then-update, so the new `RA` value reflects the post-update address (typically the new stack-frame base).
|
||||
- **No alignment requirement.** Xenon tolerates unaligned word stores. PowerISA permits implementations to raise alignment exceptions on cache-inhibited storage.
|
||||
- **Cache-line behaviour.** A word store fits inside one Xenon cache line (128 B). Stores that **straddle** a line boundary touch two lines; keep words 4-byte aligned for best performance.
|
||||
- **Common as pointer / ABI store.** Standard store for any `int32_t`/`uint32_t`/pointer field (Xbox 360 user pointers are 32-bit) and the workhorse of stack-frame setup.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`stb`](stb.md), [`sth`](sth.md), [`std`](std.md) — narrower / wider integer stores.
|
||||
- [`stwbrx`](stwbrx.md) — byte-reversed word store.
|
||||
- [`stwcx`](stwcx.md) — store-conditional word (the reservation pair end).
|
||||
- [`lwz`](lwz.md), [`lwa`](lwa.md), [`lwarx`](lwarx.md) — corresponding loads.
|
||||
- [`stmw`](stmw.md), [`stswi`](stswi.md), [`stswx`](stswx.md) — bulk stores.
|
||||
- [`stfs`](stfs.md), [`stfiwx`](stfiwx.md) — FP-side equivalents.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stw` (Store Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stw-store-word-instruction)
|
||||
- [AIX 7.3 — `stwu` / `stwx` / `stwux`](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stwu-store-word-update-instruction)
|
||||
131
migration/project-root/ppc-manual/memory/stwbrx.md
Normal file
131
migration/project-root/ppc-manual/memory/stwbrx.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# `stwbrx` — Store Word Byte-Reverse Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00052c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stwbrx` | `stwbrx` | — | Store Word Byte-Reverse Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stwbrx [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stwbrx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00052c`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `662`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stwbrx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stwbrx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stwbrx: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stwbrx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `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
|
||||
|
||||
**`stwbrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stwbrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:679`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L679)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:831`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L831)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1813-1821`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1813-L1821)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stwbrx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
if let Some(t) = ctx.reservation_table.as_ref().filter(|t| t.is_enabled()) {
|
||||
if t.has_active_reservers() { t.invalidate_for_write(ea); }
|
||||
}
|
||||
mem.write_u32(ea, (ctx.gpr[instr.rs()] as u32).swap_bytes());
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Writes little-endian word.** Takes the low 32 bits of `RS`, reverses the four bytes, writes them at `EA`. Byte at `EA` is `RS[56:63]` (low byte); byte at `EA+3` is `RS[32:39]` (high byte). The xenia snapshot does `(ctx.gpr[instr.rs()] as u32).swap_bytes()`.
|
||||
- **Used to emit little-endian payloads.** Symmetric counterpart of [`lwbrx`](lwbrx.md). Common when writing PC-side file formats, network packets, GPU command buffers in little-endian layout, etc.
|
||||
- **High bits of `RS` ignored.** Stores only the low 32 bits; the upper half of the 64-bit GPR is not consulted.
|
||||
- **X-form only — no D-form, no update form.** Only the indexed form exists. `EA = (RA|0) + RB`.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero; `stwbrx RS, 0, RB` writes at exact `RB`.
|
||||
- **Alignment.** Hardware tolerates unaligned 4-byte writes; cache-inhibited storage may raise alignment exceptions on real hardware.
|
||||
- **No CR / FPSCR effects.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwbrx`](lwbrx.md) — load-word byte-reverse (matching load).
|
||||
- [`sthbrx`](sthbrx.md), [`stdbrx`](stdbrx.md) — narrower / wider byte-reverse stores.
|
||||
- [`stw`](stw.md), [`stwx`](stw.md) — non-reversing word stores.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stwbrx` (Store Word Byte-Reverse Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stwbrx-store-word-byte-reverse-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Byte-Reverse Storage Access".
|
||||
190
migration/project-root/ppc-manual/memory/stwcx.md
Normal file
190
migration/project-root/ppc-manual/memory/stwcx.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# `stwcx` — Store Word Conditional Indexed
|
||||
|
||||
> **Category:** [Memory](../categories/memory.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c00012d`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `stwcx` | `stwcx` | — | Store Word Conditional Indexed |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
stwcx. [RS], [RA0], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `stwcx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c00012d`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `150`
|
||||
- **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 |
|
||||
| --- | --- | --- |
|
||||
| `RS` | stwcx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA0` | stwcx: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `RB` | stwcx: read | Source GPR. |
|
||||
| `CR` | stwcx: write | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `stwcx`
|
||||
|
||||
- **Reads (always):** `RS`, `RA0`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `stwcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).
|
||||
|
||||
## 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
|
||||
|
||||
**`stwcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="stwcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:868`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L868)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:81`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L81)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:782`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L782)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1225-1288`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1225-L1288)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::stwcx => {
|
||||
let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let ea = ea.wrapping_add(ctx.gpr[instr.rb()]) as u32;
|
||||
let line = ea & !RESERVATION_MASK;
|
||||
let table_route = ctx
|
||||
.reservation_table
|
||||
.as_ref()
|
||||
.filter(|t| t.is_enabled())
|
||||
.cloned();
|
||||
// PPCBUG-151: stwcx. requires a word (lwarx) reservation;
|
||||
// a doubleword (ldarx) reservation must not commit here.
|
||||
let width_ok = ctx.reservation_width == 4;
|
||||
let success = if let Some(t) = &table_route {
|
||||
// Table-routed: success iff the slot still holds our
|
||||
// reservation AND the per-ctx flag agrees (the per-ctx
|
||||
// flag would be cleared by an intervening write or
|
||||
// context switch).
|
||||
ctx.has_reservation
|
||||
&& width_ok
|
||||
&& ctx.reserved_line == line
|
||||
&& t.try_commit(ea, ctx.reserved_generation, ctx.hw_id)
|
||||
} else {
|
||||
// Legacy per-ctx path (M2 default / lockstep).
|
||||
// PPCBUG-108: fires on non-primary HW slots under misconfig —
|
||||
// if the table is disabled while workers are active, slots
|
||||
// 1..N will trip this assert, surfacing the misconfiguration
|
||||
// early in debug builds. Note: hw_id==0 (primary slot) taking
|
||||
// this path while other slots run in parallel would NOT be
|
||||
// caught; that case requires the table to be enabled instead.
|
||||
debug_assert!(
|
||||
ctx.hw_id == 0,
|
||||
"PPCBUG-108: legacy per-ctx stwcx. on non-primary HW slot \
|
||||
(hw_id={}) — ReservationTable must be enabled under --parallel",
|
||||
ctx.hw_id
|
||||
);
|
||||
ctx.has_reservation && width_ok && ctx.reserved_line == line
|
||||
};
|
||||
if success {
|
||||
mem.write_u32(ea, ctx.gpr[instr.rs()] as u32);
|
||||
ctx.cr[0] = crate::context::CrField {
|
||||
lt: false,
|
||||
gt: false,
|
||||
eq: true,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
} else {
|
||||
ctx.cr[0] = crate::context::CrField {
|
||||
lt: false,
|
||||
gt: false,
|
||||
eq: false,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
// Failed stwcx: if we held the reservation in the table
|
||||
// (someone else displaced our gen), release it from the
|
||||
// counter so `has_active_reservers` returns to zero
|
||||
// when no real reserver exists.
|
||||
if let Some(t) = &table_route {
|
||||
t.release(ea, ctx.reserved_generation, ctx.hw_id);
|
||||
}
|
||||
}
|
||||
ctx.has_reservation = false;
|
||||
ctx.reservation_width = 0; // PPCBUG-151: always clear on exit
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Always sets `Rc=1` (the trailing dot).** The mnemonic is `stwcx.` — there is no non-Rc variant. CR0 is updated unconditionally to communicate success/failure. `EQ=1` means the conditional store succeeded; `EQ=0` means it failed (the prior reservation was lost; no memory write).
|
||||
- **Reservation check.** Xenia's snapshot tests `has_reservation && reserved_addr == ea`. On match it performs `mem.write_u32` (low 32 bits of `RS`, big-endian), sets `EQ=1`. On mismatch, no memory write and `EQ=0`. In both cases the reservation is cleared, so a retry must begin with a fresh [`lwarx`](lwarx.md).
|
||||
- **Hardware granule.** PowerISA defines reservation by aligned word; Xenon implementations widen this to one 128-byte cache line. A store by another agent anywhere in the line clears the reservation. Xenia's per-address check is more permissive than hardware.
|
||||
- **Alignment requirement.** `EA` must be 4-byte aligned. Unaligned `stwcx.` raises an alignment exception on real hardware; xenia does not check.
|
||||
- **`RA0` semantics.** When `RA = 0`, base is literal zero — `stwcx. RS, 0, RB` writes at exact `RB`.
|
||||
- **CR0[SO] reflects XER[SO].** Like all CR-updating ops, CR0[SO] is copied from `XER[SO]` rather than computed.
|
||||
- **Spurious failures permitted.** Hardware may report failure even when no actual conflict occurred (e.g. on context switch). Application code treats failure as a normal retry condition.
|
||||
- **Pair atomically with [`lwarx`](lwarx.md).** Don't interleave loads/stores between the pair; an [`lwsync`](sync.md) inside the loop body is common.
|
||||
- **Stores low 32 bits of `RS`.** The high 32 bits of the source GPR are ignored.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`lwarx`](lwarx.md) — load-and-reserve word (the matching load).
|
||||
- [`stdcx`](stdcx.md) / [`ldarx`](ldarx.md) — 64-bit reservation pair.
|
||||
- [`stw`](stw.md), [`stwx`](stw.md) — non-conditional word stores.
|
||||
- [`sync`](sync.md), [`lwsync`](sync.md), [`isync`](isync.md) — barriers used around reservation pairs.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `stwcx.` (Store Word Conditional Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-stwcx-store-word-conditional-indexed-instruction)
|
||||
- `PowerISA v2.07B Book II` § "Atomic Update Primitives" for canonical reservation semantics and granule rules.
|
||||
Reference in New Issue
Block a user