diff --git a/tools/ppc-manual/README.md b/tools/ppc-manual/README.md new file mode 100644 index 00000000..1141e6e6 --- /dev/null +++ b/tools/ppc-manual/README.md @@ -0,0 +1,126 @@ +> πŸ“Œ **Adopted 2026-09-13** from an untracked directory in the project root, +> where it had lived on one disk since April β€” see +> [`docs/agents/CONSOLIDATION.md`](../../docs/agents/CONSOLIDATION.md). +> +> ⚠️ **The generator's inputs have moved.** `generator/generate_manual.py` reads +> `xenia-canary/tools/ppc-instructions.xml` **and the `xenia-rs` source tree**. +> That emulator is retired: its decoder now lives here as +> [`crates/sylpheed-ppc`](../../crates/sylpheed-ppc), and the generator must be +> repointed before it is run again. The manual's *content* is unaffected β€” it is +> checked in, and nothing regenerates it implicitly. + +# PowerPC Instruction Manual (Xenia Xbox 360 Subset) + +A reference for the **Xenon** PowerPC dialect used by the Xbox 360. Its +primary audience is an AI agent translating PPC assembly functions into +equivalent C. The content is derived from the two authoritative sources in +this repository β€” **xenia-canary** (C++ emulator) and **xenia-rs** (Rust +rewrite) β€” and may be deepened with the IBM AIX PowerPC reference. + +- **455** distinct XML-level instructions (one page each). +- **350** instruction family pages (VMX128 siblings folded). +- **598** assembly mnemonics once runtime `Rc`/`OE`/`LK` variants are expanded β€” all resolvable through `index.json`. + +## How to use this manual (translation agent) + +1. Parse the 32-bit instruction word and identify the mnemonic. Resolve it + through [`index.json`](index.json): every assembly form (including + `add.`, `addo.`, `bclrl`, …) is a top-level key pointing at a page. +2. Open the page referenced by `index.json[mnem].page`. The page is in a + fixed format β€” see the "Page anatomy" section below. +3. Emit a C translation consistent with the page's pseudocode, the + registers-affected list, and the status-register effects. + +## Page anatomy + +Every instruction page has the same sections, in this order: + +| Section | Purpose | +| --- | --- | +| **Assembler Mnemonics** | Table of every runtime variant (Rc/OE/LK) the base XML entry covers, plus VMX128 siblings. | +| **Syntax** | Canonical assembly template with `[OE]`/`[Rc]`/`[LK]` bracketed-modifier notation. | +| **Encoding** | Form name, opcode word, primary/extended opcodes, and bit-layout table. | +| **Operands** | Every bit-field operand, its role per variant, and its meaning. | +| **Register Effects** | Unconditional vs. conditional reads and writes, per variant. | +| **Status-Register Effects** | CR0/CR1/CR6, XER[CA/OV/SO], FPSCR, VSCR updates. | +| **Operation** | PPC-style pseudocode (`RT <- …`, `EXTS(…)`, `MEM(EA, n)`). | +| **C Translation Example** | Minimal idiomatic C rendering a translator could emit. | +| **Implementation References** | Direct links into `xenia-canary/` and `xenia-rs/` with line numbers. | +| **Special Cases & Edge Conditions** | RA=0, alignment, endian byte-reverse, reservation, SPR remapping, VMX128 fusion. | +| **Related Instructions** | Sibling cross-links. | +| **IBM Reference** | Optional link to IBM AIX PPC reference for canonical pseudocode. | + +Sections between the `` and `` +sentinels are produced by [`generator/generate_manual.py`](generator/generate_manual.py) +and re-generated on every run. Sections outside the sentinels are +hand-written and preserved across re-runs. + +## Conventions + +- **Bit numbering** follows PowerPC (big-endian, bit 0 = MSB). +- **GPRs** are 64-bit. 32-bit operations operate on bits `[32:63]` and + conventionally write the low 32 bits with zero- or sign-extension into + the high 32 bits. Page pseudocode makes this explicit when it matters. +- **Vector registers** are 128-bit with **lane 0 at the most-significant + byte** (big-endian lane indexing). On x86 hosts byte-swap is applied at + load/store to preserve this invariant. +- **CR** is 8 Γ— 4-bit fields `CR0..CR7`, each `{LT, GT, EQ, SO}`. The record + form of arithmetic instructions writes CR0 (integer) or CR1 (FPU); the + record form of vector compare writes CR6 = `{all-true, 0, all-false, 0}`. +- **XER** holds `SO`, `OV`, and `CA` at bits 32, 33, 34 respectively + (PPC bit numbering), plus a 7-bit string length used by `lswi`/`stswi`. + +## Categories + +| Category | Families | XML entries | Description | +| --- | --- | --- | --- | +| [Integer ALU](categories/alu.md) | 70 | 70 | Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition. | +| [Branch & System](categories/branch.md) | 9 | 9 | Unconditional / conditional branches, branch to LR/CTR, traps, system call. | +| [Control / CR / SPR](categories/control.md) | 26 | 26 | Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio). | +| [Floating-Point](categories/fpu.md) | 33 | 33 | IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves. | +| [Memory](categories/memory.md) | 56 | 112 | Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx. | +| [VMX (Altivec)](categories/vmx.md) | 144 | 193 | 128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers. | +| [VMX128](categories/vmx128.md) | 12 | 12 | Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields. | + +## Forms + +| Form | Count | Page | +| --- | --- | --- | +| `A` | 21 | [forms/A.md](forms/A.md) | +| `B` | 1 | [forms/B.md](forms/B.md) | +| `D` | 40 | [forms/D.md](forms/D.md) | +| `DCBZ` | 2 | [forms/DCBZ.md](forms/DCBZ.md) | +| `DS` | 5 | [forms/DS.md](forms/DS.md) | +| `I` | 1 | [forms/I.md](forms/I.md) | +| `M` | 3 | [forms/M.md](forms/M.md) | +| `MD` | 4 | [forms/MD.md](forms/MD.md) | +| `MDS` | 2 | [forms/MDS.md](forms/MDS.md) | +| `SC` | 1 | [forms/SC.md](forms/SC.md) | +| `VA` | 14 | [forms/VA.md](forms/VA.md) | +| `VC` | 13 | [forms/VC.md](forms/VC.md) | +| `VX` | 117 | [forms/VX.md](forms/VX.md) | +| `VX128` | 34 | [forms/VX128.md](forms/VX128.md) | +| `VX128_1` | 16 | [forms/VX128_1.md](forms/VX128_1.md) | +| `VX128_2` | 1 | [forms/VX128_2.md](forms/VX128_2.md) | +| `VX128_3` | 15 | [forms/VX128_3.md](forms/VX128_3.md) | +| `VX128_4` | 2 | [forms/VX128_4.md](forms/VX128_4.md) | +| `VX128_5` | 1 | [forms/VX128_5.md](forms/VX128_5.md) | +| `VX128_P` | 1 | [forms/VX128_P.md](forms/VX128_P.md) | +| `VX128_R` | 5 | [forms/VX128_R.md](forms/VX128_R.md) | +| `X` | 117 | [forms/X.md](forms/X.md) | +| `XFL` | 1 | [forms/XFL.md](forms/XFL.md) | +| `XFX` | 4 | [forms/XFX.md](forms/XFX.md) | +| `XL` | 12 | [forms/XL.md](forms/XL.md) | +| `XO` | 21 | [forms/XO.md](forms/XO.md) | +| `XS` | 1 | [forms/XS.md](forms/XS.md) | + +## Regenerating this manual + +```bash +python3 generator/generate_manual.py +``` + +Re-running the generator is safe β€” it only rewrites sections between +`` / `` sentinels. Add +your hand-written content below the `END` marker and it will be +preserved. diff --git a/tools/ppc-manual/TEMPLATE.md b/tools/ppc-manual/TEMPLATE.md new file mode 100644 index 00000000..8010c00f --- /dev/null +++ b/tools/ppc-manual/TEMPLATE.md @@ -0,0 +1,159 @@ +# Page Template β€” Canonical Structure + +This file is the **reference for every instruction page** in the manual. +It documents the section order, what each section is for, and the +formatting conventions that the Phase 1 generator emits and Phase 2 +reviewers enhance. + +Do **not** copy this file to create a new page. Instruction pages are +produced by `generator/generate_manual.py` and should stay under the +generator's control. + +--- + +## Page anatomy (section order) + +Every page follows this skeleton: + +```markdown +# `` β€” + +> **Category:** [](../categories/.md) Β· **Form:** [
](../forms/.md) Β· **Opcode:** `0x........` + + + +## Assembler Mnemonics + + +## Syntax + + +## Encoding + + +## Operands + + +## Register Effects + + +## Status-Register Effects + + +## Operation (pseudocode) + + +## C Translation Example + + +## Implementation References + block with the frozen interpreter body snapshot> + + + +## Special Cases & Edge Conditions + + +## Related Instructions + + +## IBM Reference + +``` + +### Sentinel markers + +The `` / `` pair +separates machine-generated content from hand-written enhancement. + +- **Inside the sentinels:** rewritten on every generator run. Do not + edit β€” your changes will be overwritten. +- **Outside the sentinels:** preserved across regenerations. Put all + Phase 2 enhancements there. + +If a page's generated section is missing the `END` sentinel the +generator assumes a human has fully taken over and leaves the file +untouched. + +--- + +## Writing conventions + +### Pseudocode + +Follow IBM's AIX reference style: + +``` +EA <- (RA|0) + EXTS(d) +RT <- ZEXT32_to_64(MEM(EA, 4)) +``` + +- `<-` for assignment, `(X)` for "value of register X". +- `(RA|0)` for RA0 fields β€” literal 0 when the encoded register is 0. +- `EXTS(x)` = sign-extend, `ZEXT/SEXT_to_(x)` explicit when helpful. +- `||` for bit concatenation. +- `MEM(EA, n)` for an n-byte big-endian memory read. +- `CR[BF] <- ...` for CR field updates. +- Register bit numbering in pseudocode is **PowerPC big-endian** (bit 0 + is the MSB). `(RS)[56:63]` is the low byte of RS. + +### C translation + +- Use a pseudo-context of `r[]` (GPRs), `f[]` (FPRs), `v[]` (128-bit + vectors), `cr[]` (CR fields), and scalars `xer`, `lr`, `ctr`, `pc`. +- Prefer `int64_t`/`uint64_t` for 64-bit ops; cast to `int32_t` for + 32-bit sub-word ops and explicitly sign/zero-extend. +- Use `mem_read_u32_be` / `mem_write_vec128_be` style helpers to make + the big-endian memory model explicit. +- Show the base form always; add one annotated variant (e.g. `if + (insn.Rc) ...`) only when it genuinely changes the translation. + +### Bit ordering + +PowerPC big-endian bit numbering is used throughout the manual (bit 0 +is MSB). The same convention is used by IBM's reference and by the XML +source. Do not switch to Intel-style LSB-first numbering. + +### Vector lane indexing + +Altivec / VMX uses **big-endian lane indexing**: lane 0 is the +most-significant 16 bytes (or 4 words / 2 doublewords) of the vector. +On little-endian hosts (x86-64) byte-swap is applied at load/store to +preserve this invariant β€” call that out in the "Special Cases" section +when relevant. + +--- + +## Example: fully-reviewed page + +See `alu/addx.md` after Phase 2 review for the canonical look. It +demonstrates: + +- Complete operand descriptions (not TODO stubs). +- Pseudocode with explicit CR/XER updates. +- C translation covering the base form plus Rc and OE variants. +- "Special Cases" calling out 32-bit vs 64-bit overflow tracking. +- Cross-links to `addcx`, `addex`, `subfx`. + +--- + +## Golden-path spot-check list (Phase 2 review order) + +These pages are reviewed first; they anchor the expected quality bar: + +| Page | Reason | +| --- | --- | +| `alu/addx.md` | XO form with Rc+OE β€” representative ALU | +| `alu/addi.md` | D form with RA0 semantics β€” representative immediate | +| `memory/lwz.md` | D form load family (lwz/lwzu/lwzx/lwzux) | +| `memory/stvx.md` | Vector store with alignment mask | +| `memory/lvsl.md` | Permute-control generator | +| `branch/bclrx.md` | BO/BI conditional with LK | +| `branch/bx.md` | Absolute/relative branch | +| `control/mfspr.md` | SPR halves-swap encoding | +| `control/mtcrf.md` | CR field-mask update | +| `fpu/faddx.md` | Double-precision FPU + CR1 via Rc | +| `vmx/vaddfp.md` | VMX + VMX128 sibling on one page | +| `vmx/vperm.md` | Byte permute β€” lane-indexing discipline | +| `vmx128/vpkd3d128.md` | VMX128 orphan (no non-128 sibling) | diff --git a/tools/ppc-manual/alu/addcx.md b/tools/ppc-manual/alu/addcx.md new file mode 100644 index 00000000..617a2a2a --- /dev/null +++ b/tools/ppc-manual/alu/addcx.md @@ -0,0 +1,138 @@ +# `addcx` β€” Add Carrying + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000014` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addc` | `addcx` | β€” | Add Carrying | +| `addco` | `addcx` | OE=1 | Add Carrying | +| `addc.` | `addcx` | Rc=1 | Add Carrying | +| `addco.` | `addcx` | OE=1, Rc=1 | Add Carrying | + +## Syntax + +```asm +addc[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `addcx` β€” form `XO` + +- **Opcode word:** `0x7c000014` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `10` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | addcx: read | Source GPR (`r0`–`r31`). | +| `RB` | addcx: read | Source GPR. | +| `RD` | addcx: write | Destination GPR. | +| `CA` | addcx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `OE` | addcx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | addcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `addcx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `addcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RT <- (RA) + (RB) +CA <- carry_out_of_32_or_64_bit_add((RA), (RB)) +``` + +## 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 + +**`addcx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addcx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:64`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L64) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:861`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L861) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:190-205`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L190-L205) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addcx => { + // PPCBUG-013+020: 32-bit truncation; CA from u32 unsigned compare. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let result32 = ra32.wrapping_add(rb32); + ctx.xer_ca = if result32 < ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Carry-out is mandatory.** `XER[CA]` is updated unconditionally β€” `addcx` exists *to* produce the carry. It seeds a multi-word add chain that continues with [`addex`](addex.md) for middle words and [`addzex`](addzex.md)/[`addmex`](addmex.md) for the final word. +- **Carry detection by overflow comparison.** Xenia computes `CA = (result < RA)` β€” the standard unsigned-add overflow test. Equivalent to `CA = (RA + RB) >> 64` mathematically. This is correct for the 64-bit operand width that the Xenon implements; the spec also allows a 32-bit width selected by the implementation but the 970/Xenon use 64-bit add throughout. +- **No trap on signed overflow.** `addco`/`addco.` only set `XER[OV]` and sticky `XER[SO]`; they do not raise an exception. Xenia-rs leaves the `OE` branch as a `// TODO` (see [`addx`](addx.md) for the same gap). +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `Rc=1` CR0 compare reads `result as i32 as i64` in [`interpreter.rs:97`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L97); spec demands the full 64-bit signed compare. Flag this as a xenia-rs quirk if you need bit-exact behaviour. +- **`XER[SO]` is sticky** β€” only `mcrxr` clears it. The `Rc=1` form folds it into `CR0[SO]`. +- **Operand aliasing is legal**, just like [`addx`](addx.md). `addc r3, r3, r3` simply doubles `r3` and records whether the result wrapped. + +## Related Instructions + +- [`addx`](addx.md) β€” same operation, but does **not** update `XER[CA]`. +- [`addex`](addex.md) β€” `RA + RB + XER[CA]`; chains a multi-word add after `addcx`. +- [`addmex`](addmex.md), [`addzex`](addzex.md) β€” terminate a carry chain by adding `βˆ’1` or `0` to `XER[CA]`. +- [`addic`](addic.md), [`addicx`](addicx.md) β€” D-form immediate variants that also write `XER[CA]`. +- [`subfcx`](subfcx.md) β€” the dual: produces a borrow-out in `XER[CA]`. + +## IBM Reference + +- [AIX 7.3 β€” `addc` (Add Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addc-add-carrying-instruction) +- PowerISA v2.07B, Book I, Β§3.3.8 β€” Fixed-Point Add with Carry; defines `XER[CA]` semantics independent of operand width. diff --git a/tools/ppc-manual/alu/addex.md b/tools/ppc-manual/alu/addex.md new file mode 100644 index 00000000..94eeaaff --- /dev/null +++ b/tools/ppc-manual/alu/addex.md @@ -0,0 +1,139 @@ +# `addex` β€” Add Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000114` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `adde` | `addex` | β€” | Add Extended | +| `addeo` | `addex` | OE=1 | Add Extended | +| `adde.` | `addex` | Rc=1 | Add Extended | +| `addeo.` | `addex` | OE=1, Rc=1 | Add Extended | + +## Syntax + +```asm +adde[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `addex` β€” form `XO` + +- **Opcode word:** `0x7c000114` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `138` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | addex: read | Source GPR (`r0`–`r31`). | +| `RB` | addex: read | Source GPR. | +| `CA` | addex: read | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `RD` | addex: write | Destination GPR. | +| `OE` | addex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | addex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `addex` + +- **Reads (always):** `RA`, `RB`, `CA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `addex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- (RA) + (RB) + CA +CA <- carry_out_of_the_add +``` + +## 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 + +**`addex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:83`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L83) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:868`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L868) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:206-222`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L206-L222) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addex => { + // PPCBUG-014+020: 32-bit truncation; CA from u32 unsigned compare. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = ra32.wrapping_add(rb32).wrapping_add(ca); + ctx.xer_ca = if result32 < ra32 || (ca != 0 && result32 == ra32) { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128) + (ca as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Carry-in is consumed and carry-out is produced.** `addex` is the middle link of a multi-word add chain seeded by [`addcx`](addcx.md): `RT ← RA + RB + XER[CA]`, then `XER[CA] ← carry_out`. +- **Carry-out detection handles both edges.** Xenia checks `result < ra OR (ca != 0 && result == ra)` β€” that second clause covers the case where adding the carry-in alone causes the result to *exactly equal* `RA` (i.e. `RB == ~0 && CA == 1`), which still constitutes overflow. The naive `result < ra` test misses it. +- **No trap on signed overflow.** `addeo`/`addeo.` only update `XER[OV]` and sticky `XER[SO]`; xenia-rs leaves the `OE` branch unimplemented. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `Rc=1` arm uses `result as i32 as i64`. For multi-word adds whose final word is the high 32 bits of a 64-bit value, this distinction matters; see [`addx`](addx.md). +- **`XER[CA]` must be initialised** by an earlier [`addcx`](addcx.md), [`subfcx`](subfcx.md), or `mtspr` to XER. Reading stale `CA` from an unrelated instruction is the most common bug in hand-written multi-word arithmetic. +- **`XER[SO]` is sticky** until cleared by `mcrxr`; `Rc=1` copies it into `CR0[SO]`. + +## Related Instructions + +- [`addcx`](addcx.md) β€” seeds the carry chain (no `CA` read, sets `CA`). +- [`addmex`](addmex.md), [`addzex`](addzex.md) β€” terminate the chain (`RA + βˆ’1 + CA`, `RA + 0 + CA`). +- [`addx`](addx.md) β€” plain add without `XER[CA]`. +- [`subfex`](subfex.md) β€” dual: `~RA + RB + XER[CA]`, used for multi-word subtract. +- [`addic`](addic.md) / [`addicx`](addicx.md) β€” immediate carrying adds that *initialise* a chain. + +## IBM Reference + +- [AIX 7.3 β€” `adde` (Add Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-adde-add-extended-instruction) +- PowerISA v2.07B, Book I, Β§3.3.8 β€” defines the `RA + RB + CA` carry-chain composition. diff --git a/tools/ppc-manual/alu/addi.md b/tools/ppc-manual/alu/addi.md new file mode 100644 index 00000000..b5e83903 --- /dev/null +++ b/tools/ppc-manual/alu/addi.md @@ -0,0 +1,116 @@ +# `addi` β€” Add Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x38000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addi` | `addi` | β€” | Add Immediate | + +## Syntax + +```asm +addi [RD], [RA0], [SIMM] +``` + +## Encoding + +### `addi` β€” form `D` + +- **Opcode word:** `0x38000000` +- **Primary opcode (bits 0–5):** `14` +- **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 | +| --- | --- | --- | +| `RA0` | addi: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. | +| `SIMM` | addi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | addi: write | Destination GPR. | + +## Register Effects + +### `addi` + +- **Reads (always):** `RA0`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if RA = 0 then RT <- EXTS(SIMM) +else RT <- (RA) + EXTS(SIMM) +``` + +## C Translation Example + +```c +/* addi RT, RA, SIMM β€” RA=0 means literal 0 */ +uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA]; +r[insn.RT] = base + (uint64_t)(int64_t)(int16_t)insn.SIMM; +``` + +## Implementation References + +**`addi`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addi"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:103`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L103) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:338`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L338) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:114-120`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L114-L120) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addi => { + // PPCBUG-001: 32-bit ABI. `li rT, -1` (= addi rT, r0, -1) must produce + // 0x00000000_FFFFFFFF, not 0xFFFFFFFF_FFFFFFFF (sign-extended simm16). + let ra_val = if instr.ra() == 0 { 0 } else { ctx.gpr[instr.ra()] }; + ctx.gpr[instr.rd()] = ra_val.wrapping_add(instr.simm16() as i64 as u64) as u32 as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA0` semantics.** When the encoded `RA` field is `0` the operand is the literal constant `0`, **not** the value of `r0`. This lets `addi rT, 0, SIMM` load a constant (the `li rT, SIMM` simplified mnemonic). To use `r0`'s value you must use a register-register add (`add RT, r0, RB` through a temp) or an instruction without `RA0` semantics. +- **No flags written.** Unlike `add`, `addi` cannot be `Rc` or `OE` β€” no CR or XER update. Use [`addic`](addic.md) if you need `XER[CA]`, or [`addicx`](addicx.md) (`addic.`) if you need both `XER[CA]` and a CR0 update. +- **Immediate is 16-bit signed** (`SIMM`, range `βˆ’32768 … +32767`), sign-extended to 64 bits before the add. No carry/overflow is produced regardless of the result. +- **Simplified mnemonics.** Assemblers recognise several aliases that all assemble to `addi`: + - `li RT, SIMM` ≑ `addi RT, 0, SIMM` (load immediate; relies on `RA0`). + - `la RT, D(RA)` ≑ `addi RT, RA, D` (load address; purely syntactic). + - `subi RT, RA, SIMM` ≑ `addi RT, RA, βˆ’SIMM`. +- **PC-relative idiom.** `addi RT, RA, D` is the low-half completion of a two-instruction address load preceded by [`addis`](addis.md) `RT, 0, HI`. The assembler emits `@ha`/`@l` relocations so the low half can be negative without corrupting the high half (add-compensation). + +## Related Instructions + +- [`addis`](addis.md) β€” same encoding family but the immediate is shifted left by 16 bits. Together they build any 32-bit constant or PC-relative address. +- [`addic`](addic.md), [`addicx`](addicx.md) β€” D-form adds that **do** set `XER[CA]` (and CR0 for the record form). +- [`addx`](addx.md) β€” the register-register form. +- [`subfic`](subfic.md) β€” reverse-subtract immediate (`imm βˆ’ RA`) with carry. +- [`ori`](ori.md), [`oris`](oris.md) β€” the alternative D-form constant-building instructions (but these don't add, they OR). + +## IBM Reference + +- [AIX 7.3 β€” `addi` (Add Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addi-add-immediate-instruction) +- [AIX 7.3 β€” `li` (Load Immediate, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-li-load-immediate) diff --git a/tools/ppc-manual/alu/addic.md b/tools/ppc-manual/alu/addic.md new file mode 100644 index 00000000..288c4ebf --- /dev/null +++ b/tools/ppc-manual/alu/addic.md @@ -0,0 +1,123 @@ +# `addic` β€” Add Immediate Carrying + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x30000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addic` | `addic` | β€” | Add Immediate Carrying | + +## Syntax + +```asm +addic [RD], [RA], [SIMM] +``` + +## Encoding + +### `addic` β€” form `D` + +- **Opcode word:** `0x30000000` +- **Primary opcode (bits 0–5):** `12` +- **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 | +| --- | --- | --- | +| `RA` | addic: read | Source GPR (`r0`–`r31`). | +| `SIMM` | addic: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | addic: write | Destination GPR. | +| `CA` | addic: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `addic` + +- **Reads (always):** `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `addic`: **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RT <- (RA) + EXTS(SIMM) +CA <- carry_out +``` + +## 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 + +**`addic`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addic"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:117`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L117) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:336`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L336) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:135-144`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L135-L144) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addic => { + // PPCBUG-002: 32-bit ABI. CA must be from a 32-bit unsigned compare; + // canary's `AddDidCarry` truncates both operands to int32 first. + let ra32 = ctx.gpr[instr.ra()] as u32; + let imm32 = instr.simm16() as i32 as u32; + let result32 = ra32.wrapping_add(imm32); + ctx.xer_ca = if result32 < ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Immediate is sign-extended.** `SIMM` is a 16-bit signed value extended to 64 bits before the add. So `addic r3, r4, -1` adds `0xFFFFFFFFFFFFFFFF` to `r4` β€” it does not zero-extend. +- **`XER[CA]` always written.** Unlike [`addi`](addi.md), this instruction exists to seed a multi-word add chain with an immediate. Carry-out is computed with the same `result < ra` unsigned-overflow check as [`addcx`](addcx.md). +- **No `Rc` bit available.** This is the *non-record* form. For a record-form variant that also updates `CR0`, use [`addicx`](addicx.md) (`addic.`). +- **No `OE` bit either.** `addic` cannot raise / observe signed overflow β€” only the carry. If you need `XER[OV]` you must use the XO-form [`addcx`](addcx.md) with `OE=1`. +- **`RA = 0` reads register r0.** Unlike [`addi`](addi.md), `addic` does **not** treat the `RA` field of zero as a literal zero. The PowerISA gives this instruction the regular `RA` semantics, not `RA0`. +- **Subtract immediate carrying via negation.** There is no `subic` mnemonic; assemblers synthesise `subic RT, RA, value` as `addic RT, RA, -value` (when `value` fits in 16 bits signed). + +## Related Instructions + +- [`addicx`](addicx.md) β€” same operation plus `Rc=1` CR0 update. +- [`addi`](addi.md) β€” D-form add immediate without `XER[CA]`. +- [`addis`](addis.md) β€” shifted form (immediate << 16). +- [`addcx`](addcx.md) β€” XO-form: register operands, sets `XER[CA]`. +- [`subfic`](subfic.md) β€” D-form: `RT ← SIMM βˆ’ RA` with `XER[CA]`. + +## IBM Reference + +- [AIX 7.3 β€” `addic` (Add Immediate Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addic-add-immediate-carrying-instruction) diff --git a/tools/ppc-manual/alu/addicx.md b/tools/ppc-manual/alu/addicx.md new file mode 100644 index 00000000..972971ae --- /dev/null +++ b/tools/ppc-manual/alu/addicx.md @@ -0,0 +1,132 @@ +# `addic.` β€” Add Immediate Carrying and Record + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x34000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addic.` | `addic.` | β€” | Add Immediate Carrying and Record | + +## Syntax + +```asm +addic. [RD], [RA], [SIMM] +``` + +## Encoding + +### `addic.` β€” form `D` + +- **Opcode word:** `0x34000000` +- **Primary opcode (bits 0–5):** `13` +- **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 | +| --- | --- | --- | +| `RA` | addic.: read | Source GPR (`r0`–`r31`). | +| `SIMM` | addic.: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | addic.: write | Destination GPR. | +| `CA` | addic.: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `CR` | addic.: 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 + +### `addic.` + +- **Reads (always):** `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA`, `CR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `addic.`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (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 + +**`addic.`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addic."`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:127`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L127) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:337`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L337) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:145-154`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L145-L154) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addicx => { + // PPCBUG-003: same fix as addic plus CR0 i32 view. + let ra32 = ctx.gpr[instr.ra()] as u32; + let imm32 = instr.simm16() as i32 as u32; + let result32 = ra32.wrapping_add(imm32); + ctx.xer_ca = if result32 < ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + ctx.update_cr_signed(0, result32 as i32 as i64); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`Rc` bit is implicit, not encoded.** `addic.` has its *own* primary opcode (13) distinct from `addic`'s (12); there is no `Rc` field to set. The two forms are sibling D-form instructions, not flag variants of one encoding. +- **CR0 update is unconditional.** Unlike XO-form `Rc=1` instructions, `addic.` always updates `CR0` from the result; the `.` is part of the mnemonic itself. +- **Common idiom: `addic. rN, rN, -1`** β€” decrements `rN` and sets `CR0[EQ]` when it reaches zero, in a single instruction. Frequently used as a loop counter (often paired with `bne+ loop`). +- **`XER[CA]` written same as [`addic`](addic.md).** The carry-out from the unsigned 64-bit add is recorded; the `.` only adds the CR update on top. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:65`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L65) computes `result as i32 as i64`; spec demands a full 64-bit compare-to-zero. The truncation is a documented xenia-rs quirk shared with the rest of the carrying-add family. +- **`SIMM` is sign-extended** to 64 bits before the add β€” `addic. r3, r4, -1` adds `~0` and never sets `CR0[EQ]` unless `r4 == 1`. + +## Related Instructions + +- [`addic`](addic.md) β€” same op without the CR0 update. +- [`addi`](addi.md), [`addis`](addis.md) β€” immediate adds without `XER[CA]`. +- [`addcx`](addcx.md) β€” XO-form register equivalent. +- [`subfic`](subfic.md) β€” `RT ← SIMM βˆ’ RA` with `XER[CA]` (no record form exists). +- [`cmpi`](cmpi.md) β€” explicit immediate compare when the carry side-effect would be unwanted. + +## IBM Reference + +- [AIX 7.3 β€” `addic.` (Add Immediate Carrying and Record)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addic-add-immediate-carrying-record-instruction) diff --git a/tools/ppc-manual/alu/addis.md b/tools/ppc-manual/alu/addis.md new file mode 100644 index 00000000..ae14a457 --- /dev/null +++ b/tools/ppc-manual/alu/addis.md @@ -0,0 +1,121 @@ +# `addis` β€” Add Immediate Shifted + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x3c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addis` | `addis` | β€” | Add Immediate Shifted | + +## Syntax + +```asm +addis [RD], [RA0], [SIMM] +``` + +## Encoding + +### `addis` β€” form `D` + +- **Opcode word:** `0x3c000000` +- **Primary opcode (bits 0–5):** `15` +- **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 | +| --- | --- | --- | +| `RA0` | addis: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. | +| `SIMM` | addis: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | addis: write | Destination GPR. | + +## Register Effects + +### `addis` + +- **Reads (always):** `RA0`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if RA = 0 then RT <- EXTS(SIMM) << 16 +else RT <- (RA) + (EXTS(SIMM) << 16) +``` + +## C Translation Example + +```c +/* addis RT, RA, SIMM β€” RA=0 means literal 0 */ +uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA]; +r[insn.RT] = base + ((uint64_t)(int64_t)(int16_t)insn.SIMM << 16); +``` + +## Implementation References + +**`addis`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addis"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:138`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L138) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:339`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L339) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:121-134`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L121-L134) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addis => { + // Xbox 360 user mode is 32-bit ABI (MSR.SF=0), so addis must + // produce a value whose upper 32 bits don't pollute downstream + // 64-bit arithmetic. The PPC ISA in 64-bit mode sign-extends + // simm16 before the shift, producing 0xFFFFFFFF_xxxx0000 for + // negative simm16 (high bit set). When this value flows into + // a 64-bit subfc against a zero-extended lwz value, the unsigned + // 64-bit comparison yields wrong CA. Truncate to 32 bits to + // simulate 32-bit ABI behavior. + let ra_val = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; + let result = ra_val.wrapping_add((instr.simm16() as i64 as u64) << 16); + ctx.gpr[instr.rd()] = result as u32 as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA0` semantics.** When the `RA` field encodes 0, the operand is the literal 64-bit zero, **not** `r0`. This makes `addis RT, 0, hi16` the canonical "load high half" idiom. To use `r0`'s actual value as a base, copy it via `mr` first or use a different opcode. +- **Immediate is sign-extended *then* shifted left 16.** So `addis r3, 0, 0x8000` writes `0xFFFFFFFF80000000`, not `0x000000008000_0000`. The 32-bit sign extension surprise is the most common bug in hand-written PPC assembly. +- **Forms the high half of a 32-bit immediate.** The classic `lis rT, hi; ori rT, rT, lo` (or `lis`/`addi`) sequence builds a full 32-bit constant. `lis rT, val` is a simplified mnemonic for `addis rT, 0, val`. +- **No `XER[CA]`, no `XER[OV]`, no `Rc`.** This instruction has no status side-effects whatsoever. Use [`addic`](addic.md) or [`addcx`](addcx.md) if a carry is required. +- **64-bit `RA` operand.** The shift-and-add is 64-bit on the Xenon; the immediate's sign-extension fills the high 48 bits. So `addis r3, r4, -1` adds `0xFFFFFFFFFFFF0000` to a 64-bit `r4`. +- **No overflow detection.** `lis r3, 0x7FFF; addis r3, r3, 0x7FFF` happily wraps without comment. + +## Related Instructions + +- [`addi`](addi.md) β€” D-form add immediate, no shift; same `RA0` rule. +- [`addic`](addic.md), [`addicx`](addicx.md) β€” immediate adds that also write `XER[CA]`. +- [`oris`](oris.md), [`ori`](ori.md) β€” pair with `addis`/`lis` to build 32-bit constants without affecting CR or XER. +- [`addx`](addx.md), [`addcx`](addcx.md) β€” XO-form register adds. +- `lis` (simplified) β€” assembler shorthand for `addis RT, 0, value`. + +## IBM Reference + +- [AIX 7.3 β€” `addis` (Add Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addis-add-immediate-shifted-instruction) +- [AIX 7.3 β€” `lis` (Load Immediate Shifted, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-li-lis-load-immediate-load-immediate-shifted) diff --git a/tools/ppc-manual/alu/addmex.md b/tools/ppc-manual/alu/addmex.md new file mode 100644 index 00000000..d476a799 --- /dev/null +++ b/tools/ppc-manual/alu/addmex.md @@ -0,0 +1,136 @@ +# `addmex` β€” Add to Minus One Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0001d4` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addme` | `addmex` | β€” | Add to Minus One Extended | +| `addmeo` | `addmex` | OE=1 | Add to Minus One Extended | +| `addme.` | `addmex` | Rc=1 | Add to Minus One Extended | +| `addmeo.` | `addmex` | OE=1, Rc=1 | Add to Minus One Extended | + +## Syntax + +```asm +addme[OE][Rc] [RD], [RA] +``` + +## Encoding + +### `addmex` β€” form `XO` + +- **Opcode word:** `0x7c0001d4` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `234` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | addmex: read | Source GPR (`r0`–`r31`). | +| `CA` | addmex: read; addmex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `RD` | addmex: write | Destination GPR. | +| `OE` | addmex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | addmex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `addmex` + +- **Reads (always):** `RA`, `CA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `addmex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RT <- (RA) + CA + 0xFFFF_FFFF_FFFF_FFFF +CA <- carry_out +``` + +## 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 + +**`addmex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addmex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:152`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L152) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:873`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L873) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:239-254`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L239-L254) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addmex => { + // PPCBUG-016+020: 32-bit truncation. RT = RA + CA - 1. + let ra32 = ctx.gpr[instr.ra()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = ra32.wrapping_add(ca).wrapping_sub(1); + ctx.xer_ca = if ra32 != 0 || ca != 0 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = (ra32 as i32 as i128) + (ca as i128) - 1; + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No `RB` field used.** `addmex` is encoded in XO-form but ignores the `RB` slot β€” assemblers must still emit a value (typically zero). Disassemblers that parse a non-zero `RB` should not flag it as illegal; it is simply unused. +- **Operation is `RA + CA + (βˆ’1)`**, i.e. `RA - 1 + CA`. Used to terminate a multi-word *subtract* chain when the high source word is implicitly all-ones (e.g. computing `-x` as `~x + 1` across 128 bits). +- **Carry-out predicate is `RA != 0 OR CA != 0`.** Equivalently, `CA' = NOT(RA == 0 AND CA == 0)`. Adding `βˆ’1` to anything except a zero-with-no-carry produces a carry-out (no borrow needed). This terse form in xenia-rs is correct but easy to misread. +- **Overflow not implemented in xenia-rs.** The `OE=1` path is silently a no-op; spec says set `XER[OV]` if the signed result wraps. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:139`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L139) β€” same quirk as the rest of the add family. +- **`XER[CA]` must be initialised** by an earlier carrying instruction. `addme` is a *terminator*, not a seed. + +## Related Instructions + +- [`addzex`](addzex.md) β€” terminate a chain with `RA + 0 + CA` (no `βˆ’1`). +- [`addex`](addex.md) β€” middle-of-chain `RA + RB + CA`. +- [`addcx`](addcx.md) β€” seeds a chain. +- [`subfmex`](subfmex.md) β€” subtract dual: `~RA + (βˆ’1) + CA`. +- [`negx`](negx.md) β€” single-instruction two's-complement negate. + +## IBM Reference + +- [AIX 7.3 β€” `addme` (Add to Minus One Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addme-add-minus-one-extended-instruction) diff --git a/tools/ppc-manual/alu/addx.md b/tools/ppc-manual/alu/addx.md new file mode 100644 index 00000000..0e0003fd --- /dev/null +++ b/tools/ppc-manual/alu/addx.md @@ -0,0 +1,148 @@ +# `addx` β€” Add + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000214` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `add` | `addx` | β€” | Add | +| `addo` | `addx` | OE=1 | Add | +| `add.` | `addx` | Rc=1 | Add | +| `addo.` | `addx` | OE=1, Rc=1 | Add | + +## Syntax + +```asm +add[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `addx` β€” form `XO` + +- **Opcode word:** `0x7c000214` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `266` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | addx: read | Source GPR (`r0`–`r31`). | +| `RB` | addx: read | Source GPR. | +| `RD` | addx: write | Destination GPR. | +| `OE` | addx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | addx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `addx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `addx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- (RA) + (RB) +``` + +## C Translation Example + +```c +/* add / add. / addo / addo. (XO-form) */ +uint64_t a = r[insn.RA], b = r[insn.RB]; +uint64_t result = a + b; +r[insn.RT] = result; +if (insn.OE) { bool ov = (~(a ^ b) & (a ^ result)) >> 63; + if (ov) { xer.OV = 1; xer.SO = 1; } else xer.OV = 0; } +if (insn.Rc) update_cr0_signed((int64_t)result); +``` + +## Implementation References + +**`addx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:50`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L50) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:875`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L875) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:175-189`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L175-L189) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addx => { + // PPCBUG-012+020: 32-bit ABI writeback truncation + CR0 i32 view. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let result32 = ra32.wrapping_add(rb32); + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +RT <- (RA) + (RB) ; modulo 2^64, carry discarded +if OE then + XER[OV] <- (~(RA ^ RB) & (RA ^ RT))[0] ; signed overflow: same-sign inputs, opposite-sign result + XER[SO] <- XER[SO] | XER[OV] +if Rc then + CR0[LT,GT,EQ] <- signed_compare(RT, 0) ; 64-bit comparison on the Xenon + CR0[SO] <- XER[SO] +``` + +## Special Cases & Edge Conditions + +- **No trap on overflow.** `addo` / `addo.` record overflow in `XER[OV]` and sticky-set `XER[SO]`. A trap can only be produced by a separate `td`/`tw` instruction examining the result. +- **Signed-overflow predicate.** Overflow occurs iff both addends share a sign bit and the result has the opposite sign bit: `OV = ((~(a ^ b)) & (a ^ rt)) >> 63`. Unsigned carry is *not* tracked β€” use [`addcx`](addcx.md) when you need `XER[CA]`. +- **`XER[SO]` is sticky.** Once set, it remains set until cleared by `mcrxr`. The `.` record forms copy it into `CR0[SO]`. +- **64-bit CR update on Xenon.** The Xbox 360 Xenon CPU is 64-bit, so `add.` compares the full 64-bit result against zero. **Xenia-rs presently truncates to 32 bits** before the CR update (`result as i32 as i64` in [`interpreter.rs:95`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L95)). If your translator must match xenia bit-for-bit, emit a 32-bit compare; if it must be spec-correct, emit a 64-bit compare. Most Xbox 360 object code works either way because results that overflow 32 bits are rare outside of explicit 64-bit math. +- **OE overflow detection not emulated in xenia-rs.** The `addo` / `addo.` branch in `interpreter.rs` is a `TODO` stub. A faithful translator should still emit the overflow check β€” titles rarely observe `XER[OV]`, but it's occasionally used by profiling / sanity-checking code paths. +- **Operand aliasing.** `add r3, r3, r3`, `add r3, r3, r4`, `add r3, r4, r3` are all legal. The addition reads both source operands before writing `RT`. +- **No immediate form.** For `RT = RA + imm` use [`addi`](addi.md) / [`addis`](addis.md). Those are distinct opcodes, not a flag on `add`. + +## Related Instructions + +- [`addcx`](addcx.md) β€” produces the carry-out in `XER[CA]`. +- [`addex`](addex.md) β€” sums `(RA) + (RB) + XER[CA]` (carry-in chain). +- [`addmex`](addmex.md), [`addzex`](addzex.md) β€” add to `βˆ’1` or `0` with carry-in (used to propagate borrows across multiword subtracts). +- [`addi`](addi.md), [`addis`](addis.md) β€” D-form immediate adds; no `Rc`/`OE`. +- [`addic`](addic.md), [`addicx`](addicx.md) β€” D-form adds that set `XER[CA]`. +- [`subfx`](subfx.md) β€” the dual: `RT ← (RB) βˆ’ (RA)`. +- [`negx`](negx.md) β€” two's-complement negate. + +## IBM Reference + +- [AIX 7.3 β€” `add` (Add)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-add-instruction) +- [PowerISA v2.07B, Book I, Β§3.3.8 β€” Fixed-Point Arithmetic Instructions](https://openpowerfoundation.org/specifications/isa/) (overflow predicate, CR0 / `XER[SO]` semantics). diff --git a/tools/ppc-manual/alu/addzex.md b/tools/ppc-manual/alu/addzex.md new file mode 100644 index 00000000..49f32e73 --- /dev/null +++ b/tools/ppc-manual/alu/addzex.md @@ -0,0 +1,135 @@ +# `addzex` β€” Add to Zero Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000194` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `addze` | `addzex` | β€” | Add to Zero Extended | +| `addzeo` | `addzex` | OE=1 | Add to Zero Extended | +| `addze.` | `addzex` | Rc=1 | Add to Zero Extended | +| `addzeo.` | `addzex` | OE=1, Rc=1 | Add to Zero Extended | + +## Syntax + +```asm +addze[OE][Rc] [RD], [RA] +``` + +## Encoding + +### `addzex` β€” form `XO` + +- **Opcode word:** `0x7c000194` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `202` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | addzex: read | Source GPR (`r0`–`r31`). | +| `CA` | addzex: read; addzex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `RD` | addzex: write | Destination GPR. | +| `OE` | addzex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | addzex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `addzex` + +- **Reads (always):** `RA`, `CA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `addzex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RT <- (RA) + CA +CA <- carry_out +``` + +## 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 + +**`addzex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="addzex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:172`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L172) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:870`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L870) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:223-238`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L223-L238) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::addzex => { + // PPCBUG-015+020: 32-bit truncation. + let ra32 = ctx.gpr[instr.ra()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = ra32.wrapping_add(ca); + ctx.xer_ca = if result32 < ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = (ra32 as i32 as i128) + (ca as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No `RB` field used.** Like [`addmex`](addmex.md), this XO-form instruction ignores the `RB` slot. Assemblers emit zero there. +- **Operation is `RA + 0 + CA` ≑ `RA + CA`.** Used to terminate the *high word* of a multi-word add chain seeded by [`addcx`](addcx.md). After the low-word `addc` produces the carry, all middle words use [`addex`](addex.md), and the final word that has no register operand uses `addze`. +- **Carry-out is the simple unsigned overflow test** `result < ra` β€” same predicate as [`addcx`](addcx.md). `CA' = 1` only if `RA == ~0 && CA == 1`. +- **`OE=1` not implemented in xenia-rs.** The interpreter has no overflow branch at all; spec asks for the standard signed-overflow detect. +- **64-bit CR update on Xenon, 32-bit in xenia-rs** (truncation in [`interpreter.rs:128`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L128) β€” see [`addx`](addx.md) for context). +- **Common idiom: extracting a carry as a 0/1.** `addze rT, 0` (or `addze rT, rN` where `rN == 0`) materialises `XER[CA]` into `rT` as a plain integer. + +## Related Instructions + +- [`addmex`](addmex.md) β€” terminate with `RA + (βˆ’1) + CA` instead of `+0`. +- [`addex`](addex.md) β€” middle of a multi-word add chain. +- [`addcx`](addcx.md) β€” seeds the chain. +- [`subfzex`](subfzex.md) β€” subtract dual: `~RA + 0 + CA`. + +## IBM Reference + +- [AIX 7.3 β€” `addze` (Add to Zero Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addze-add-zero-extended-instruction) diff --git a/tools/ppc-manual/alu/andcx.md b/tools/ppc-manual/alu/andcx.md new file mode 100644 index 00000000..7b1ce281 --- /dev/null +++ b/tools/ppc-manual/alu/andcx.md @@ -0,0 +1,123 @@ +# `andcx` β€” AND with Complement + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000078` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `andc` | `andcx` | β€” | AND with Complement | +| `andc.` | `andcx` | Rc=1 | AND with Complement | + +## Syntax + +```asm +andc[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `andcx` β€” form `X` + +- **Opcode word:** `0x7c000078` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `60` +- **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` | andcx: read | Source GPR (alias for RD in some stores). | +| `RB` | andcx: read | Source GPR. | +| `RA` | andcx: write | Source GPR (`r0`–`r31`). | +| `CR` | andcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `andcx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `andcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- (RS) & ~(RB) +``` + +## 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 + +**`andcx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="andcx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:647`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L647) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:768`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L768) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:534-541`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L534-L541) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::andcx => { + // PPCBUG-033: !rb on u64 flips upper 32 bits β€” active poisoning. + let rs32 = ctx.gpr[instr.rs()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = (rs32 & !rb32) as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`andc RA, RS, RB` computes `RS AND (NOT RB)`.** The complement is applied to `RB`, not `RS`. Useful for clearing a bitmask: `andc r3, r3, r4` clears in `r3` every bit set in `r4`. +- **Common idiom: `andc r3, r3, r3`** zeroes `r3` (every bit ANDed with its own complement). Cheaper-looking than `xor r3, r3, r3` on some pipelines but functionally identical; the assembler often prefers the `xor` idiom. +- **Operand convention is the X-form one** (`RA` is the destination, `RS` and `RB` are sources). Same gotcha as [`andx`](andx.md). +- **No `OE`/`XER` side effects.** Only `CR0` is updated when `Rc=1`. +- **64-bit operation** on Xenon; the AND is computed across all 64 bits of `RS` and `~RB`. Xenia-rs uses Rust's bitwise `!` on `u64`, which is the correct full-width complement. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:352`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L352) β€” same truncation pattern. + +## Related Instructions + +- [`andx`](andx.md) β€” plain AND (no complement). +- [`nandx`](nandx.md) β€” NAND (`~(RS & RB)`). +- [`orcx`](orcx.md) β€” OR with complement; sister `c` form. +- [`eqvx`](eqvx.md) β€” `~(RS ^ RB)` (NXOR / equivalence). +- [`norx`](norx.md) β€” NOR. + +## IBM Reference + +- [AIX 7.3 β€” `andc` (AND with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andc-complement-instruction) diff --git a/tools/ppc-manual/alu/andisx.md b/tools/ppc-manual/alu/andisx.md new file mode 100644 index 00000000..f57fd43c --- /dev/null +++ b/tools/ppc-manual/alu/andisx.md @@ -0,0 +1,127 @@ +# `andis.` β€” AND Immediate Shifted + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x74000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `andis.` | `andis.` | β€” | AND Immediate Shifted | + +## Syntax + +```asm +andis. [RA], [RS], [UIMM] +``` + +## Encoding + +### `andis.` β€” form `D` + +- **Opcode word:** `0x74000000` +- **Primary opcode (bits 0–5):** `29` +- **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 | +| --- | --- | --- | +| `RS` | andis.: read | Source GPR (alias for RD in some stores). | +| `UIMM` | andis.: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | andis.: write | Source GPR (`r0`–`r31`). | +| `CR` | andis.: 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 + +### `andis.` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `andis.`: **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 + +**`andis.`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="andis."`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:665`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L665) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:352`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L352) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:505-511`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L505-L511) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::andisx => { + // PPCBUG-023: 32-bit ABI CR0 view. `andis. rA, rS, 0x8000` to test + // sign bit of a 32-bit word now correctly classifies bit 31 = 1 as LT. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & ((instr.uimm16() as u64) << 16); + ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Always `Rc=1`.** Like [`andix`](andix.md), the dot is part of the mnemonic; no plain `andis` exists. +- **Immediate is shifted left 16, zero-extended.** Effective mask is `(UIMM << 16) & 0xFFFFFFFF`, so the only bits that can survive in `RA` are bits 32–47 (in PowerISA bit numbering, equivalent to bits 16–31 of the low 32 bits) of `RS`. Bits 0–31 and bits 48–63 of `RA` are forced to zero. +- **Together with `andi.` covers the entire low 32 bits.** Any 32-bit mask can be applied with `andis. + andi.` (two instructions). Larger masks need `rlwinm` or a constructed register operand to [`andx`](andx.md). +- **High 32 bits of result are always zero.** Because the immediate is at bits 32–47, no information from `RS[0:31]` survives. Useful as a quick "extract bits 32–47, zero the rest" primitive. +- **CR0 update is unconditional** and uses the standard signed-compare-to-zero semantics with `XER[SO]` folded into `SO`. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `result as i32 as i64` truncation in [`interpreter.rs:326`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L326) is harmless: the result is bounded by `0x00000000_FFFF0000`, which fits the 32-bit window exactly. + +## Related Instructions + +- [`andix`](andix.md) β€” companion (immediate not shifted). +- [`andx`](andx.md), [`andcx`](andcx.md) β€” register AND. +- [`oris`](oris.md), [`xoris`](xoris.md) β€” sister immediate-shifted logicals. +- [`rlwinmx`](rlwinmx.md) β€” full mask-and-rotate when the bits of interest aren't aligned to a 16-bit boundary. + +## IBM Reference + +- [AIX 7.3 β€” `andis.` (AND Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andis-immediate-shifted-instruction) diff --git a/tools/ppc-manual/alu/andix.md b/tools/ppc-manual/alu/andix.md new file mode 100644 index 00000000..0b40aa25 --- /dev/null +++ b/tools/ppc-manual/alu/andix.md @@ -0,0 +1,126 @@ +# `andi.` β€” AND Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x70000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `andi.` | `andi.` | β€” | AND Immediate | + +## Syntax + +```asm +andi. [RA], [RS], [UIMM] +``` + +## Encoding + +### `andi.` β€” form `D` + +- **Opcode word:** `0x70000000` +- **Primary opcode (bits 0–5):** `28` +- **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 | +| --- | --- | --- | +| `RS` | andi.: read | Source GPR (alias for RD in some stores). | +| `UIMM` | andi.: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | andi.: write | Source GPR (`r0`–`r31`). | +| `CR` | andi.: 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 + +### `andi.` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `andi.`: **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 + +**`andi.`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="andi."`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:657`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L657) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:351`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L351) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:499-504`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L499-L504) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::andix => { + // PPCBUG-020: 32-bit ABI CR0 view. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & (instr.uimm16() as u64); + ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Always `Rc=1`.** There is no `andi` (without the dot). The mnemonic is `andi.` and the encoding always updates `CR0`. If you need a non-record AND-with-immediate, you have to materialise the immediate first (e.g. with `li`/`lis`) and use [`andx`](andx.md). +- **Immediate is zero-extended.** The 16-bit `UIMM` is widened with zeros, so `andi. rA, rS, 0xFFFF` masks `rS` to its low 16 bits β€” the high 48 bits of the 64-bit register are forced to zero. +- **Cannot mask the high half of a register in one instruction.** The immediate covers bits 48–63 only; for higher bits use [`andisx`](andisx.md) (covers bits 32–47) or compose with `rlwinm`/`rldicl`. +- **CR0 update is unconditional.** This is part of the encoding, not a flag β€” the primary opcode (28) *is* `andi.`. +- **Common idiom: `andi. r0, rN, mask`** to test bits without disturbing the source β€” but note `r0` is overwritten and `CR0` is set. If you only need the CR result, prefer `extrwi`/`rlwinm.` for arbitrary masks. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** Since the AND result has zeros in bits 0–47, the low-32 truncation in [`interpreter.rs:321`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L321) is harmless here β€” the result fits in 16 bits, so spec and xenia agree. + +## Related Instructions + +- [`andisx`](andisx.md) β€” same op with immediate shifted left 16 (covers bits 32–47). +- [`andx`](andx.md), [`andcx`](andcx.md) β€” register AND family. +- [`ori`](ori.md), [`oris`](oris.md), [`xori`](xori.md), [`xoris`](xoris.md) β€” sister immediate logicals (notably *without* a record form). +- [`rlwinmx`](rlwinmx.md) β€” for masks that don't fit into a 16-bit immediate. + +## IBM Reference + +- [AIX 7.3 β€” `andi.` (AND Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andi-immediate-instruction) diff --git a/tools/ppc-manual/alu/andx.md b/tools/ppc-manual/alu/andx.md new file mode 100644 index 00000000..84936a93 --- /dev/null +++ b/tools/ppc-manual/alu/andx.md @@ -0,0 +1,122 @@ +# `andx` β€” AND + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000038` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `and` | `andx` | β€” | AND | +| `and.` | `andx` | Rc=1 | AND | + +## Syntax + +```asm +and[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `andx` β€” form `X` + +- **Opcode word:** `0x7c000038` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `28` +- **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` | andx: read | Source GPR (alias for RD in some stores). | +| `RB` | andx: read | Source GPR. | +| `RA` | andx: write | Source GPR (`r0`–`r31`). | +| `CR` | andx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `andx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `andx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- (RS) & (RB) +``` + +## 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 + +**`andx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="andx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:637`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L637) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:760`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L760) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:528-533`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L528-L533) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::andx => { + // PPCBUG-032+020: 32-bit ABI CR0 view (latent under clean inputs). + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & ctx.gpr[instr.rb()]; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operand convention is reversed.** Unlike the arithmetic XO-form (`add RT, RA, RB`), the logical X-form writes `RA` and reads `RS`/`RB`: `and RA, RS, RB`. The destination is the **second** operand encoded. This convention applies to the entire and/or/xor family; mixing them up is a frequent disassembly error. +- **No `OE`, no `XER[CA]`, no `XER[OV]`.** Logical operations never affect XER. Only `Rc=1` updates `CR0` (signed compare against zero, with `SO ← XER[SO]`). +- **64-bit AND on Xenon.** Both inputs are 64-bit GPRs; the result is the bitwise AND of all 64 bits. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The interpreter's `Rc=1` path in [`interpreter.rs:347`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L347) compares `result as i32 as i64`. For an AND whose high 32 bits are non-zero but low 32 bits are zero (e.g. `r3 = 0x1_0000_0000`, `and. r4, r3, r3`), spec sets CR0 to GT but xenia would set EQ. Flag this if reproducing CR-sensitive behaviour. +- **Operand aliasing.** `and RA, RA, RA` is a no-op except for the optional CR0 update β€” this is the canonical "test register against zero" pattern when no `cmpwi` is desired (though `cmpwi` is more typical). +- **No simplified mnemonic for AND-immediate.** Use [`andix`](andix.md) (`andi.`) or [`andisx`](andisx.md) (`andis.`) for immediate operands; both are *always* record forms (no plain `andi`). + +## Related Instructions + +- [`andcx`](andcx.md) β€” AND with complement: `RA ← RS & ~RB`. +- [`andix`](andix.md), [`andisx`](andisx.md) β€” D-form AND immediate (always `Rc=1`). +- [`nandx`](nandx.md) β€” NAND. +- [`orx`](orx.md), [`orcx`](orcx.md), [`xorx`](xorx.md), [`eqvx`](eqvx.md), [`norx`](norx.md) β€” sister logical instructions. +- [`cmp`](cmp.md), [`cmpi`](cmpi.md) β€” explicit zero/value test when CR-only effect is wanted without overwriting `RA`. + +## IBM Reference + +- [AIX 7.3 β€” `and` (AND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-instruction) +- [AIX 7.3 β€” Reference: PowerPC instruction set](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set) diff --git a/tools/ppc-manual/alu/cmp.md b/tools/ppc-manual/alu/cmp.md new file mode 100644 index 00000000..2432066e --- /dev/null +++ b/tools/ppc-manual/alu/cmp.md @@ -0,0 +1,153 @@ +# `cmp` β€” Compare + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cmp` | `cmp` | β€” | Compare | + +## Syntax + +```asm +cmp [CRFD], [L], [RA], [RB] +``` + +## Encoding + +### `cmp` β€” form `X` + +- **Opcode word:** `0x7c000000` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `0` +- **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 | +| --- | --- | --- | +| `L` | cmp: read | Operand-length bit for compare instructions (`0 β‡’ 32-bit`, `1 β‡’ 64-bit`). | +| `RA` | cmp: read | Source GPR (`r0`–`r31`). | +| `RB` | cmp: read | Source GPR. | +| `CRFD` | cmp: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `cmp` + +- **Reads (always):** `L`, `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS((RB)[32:63]) +else a,b <- (RA), (RB) +CR[BF] <- signed_compare(a, b) || XER[SO] +``` + +## 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 + +**`cmp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cmp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:523`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L523) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:749`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L749) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:863-885`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L863-L885) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cmp => { + let bf = instr.crfd(); + if instr.l() { + let ra = ctx.gpr[instr.ra()] as i64; + let rb = ctx.gpr[instr.rb()] as i64; + ctx.cr[bf] = crate::context::CrField { + lt: ra < rb, + gt: ra > rb, + eq: ra == rb, + so: ctx.xer_so != 0, + }; + } else { + let ra = ctx.gpr[instr.ra()] as i32; + let rb = ctx.gpr[instr.rb()] as i32; + ctx.cr[bf] = crate::context::CrField { + lt: ra < rb, + gt: ra > rb, + eq: ra == rb, + so: ctx.xer_so != 0, + }; + } + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +if L = 0 then ; 32-bit compare + a <- EXTS((RA)[32:63]) ; sign-extend low word to 64 + b <- EXTS((RB)[32:63]) +else ; 64-bit compare + a <- (RA) + b <- (RB) +CR[BF] <- { LT: a s b, EQ: a = b, SO: XER[SO] } ; signed +``` + +## Special Cases & Edge Conditions + +- **`BF` is a CR field (0–7), not a bit.** The `crfD` operand encodes which of the eight 4-bit CR fields is updated. Assemblers write it as `crN` where `N ∈ 0..7`. The simplified mnemonic `cmpw RA, RB` ≑ `cmp cr0, 0, RA, RB` is universal in Xbox 360 code. +- **`L` bit selects width.** `L = 0` (the usual `cmpw` / `cmpd`-is-rare path) performs a *32-bit* signed compare of `RA[32:63]` and `RB[32:63]`, both sign-extended to 64 bits. `L = 1` (`cmpd`) performs a full 64-bit signed compare. +- **Signed.** Use [`cmpl`](cmpl.md) / [`cmpli`](cmpli.md) for unsigned comparisons. Confusing signed/unsigned is the most common compare-family bug in hand-written asm. +- **SO is always copied from `XER[SO]`.** This makes overflow observable across arithmetic/compare sequences: an `addo.` followed by `beq` can branch on the record-form flag while `bso` can inspect the sticky overflow. +- **`cr0` is the default for record-form ALU**; by convention assemblers and generators reserve `cr0` for the chain of `Rc=1` instructions and use `cr1..cr7` (or `cmp` to an explicit field) for standalone compares. Don't assume `cmp` writes `cr0` unless the `BF` operand says so. +- **No register is written** beyond the 4-bit CR field. `cmp` has no `Rc` or `OE` bit. +- **Xenia-rs quirk.** The interpreter recomputes `EQ` after the signed compare to guard against a subtract-cancellation edge case; this is a defensive belt-and-braces against the 32-bit narrowing path. Functionally equivalent to the spec. + +## Related Instructions + +- [`cmpi`](cmpi.md) β€” signed compare against a 16-bit immediate. +- [`cmpl`](cmpl.md), [`cmpli`](cmpli.md) β€” unsigned versions. +- [`cmpw`](cmp.md), [`cmpd`](cmp.md) β€” simplified mnemonics selecting `L`. +- [`mcrxr`](mcrxr.md) β€” move `XER[SO..CA]` into a CR field and clear them; used to reset sticky overflow. +- Every `Rc=1` ALU instruction ([`addx`](addx.md), [`subfx`](subfx.md), [`andx`](andx.md), …) β€” these implicitly perform a signed-compare-to-zero into `cr0`; use explicit `cmp` only when comparing two non-zero values or using a non-zero CR field. + +## IBM Reference + +- [AIX 7.3 β€” `cmp` (Compare)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmp-compare-instruction) +- [AIX 7.3 β€” `cmpw` / `cmpd` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmpw-compare-word) diff --git a/tools/ppc-manual/alu/cmpi.md b/tools/ppc-manual/alu/cmpi.md new file mode 100644 index 00000000..b74c758a --- /dev/null +++ b/tools/ppc-manual/alu/cmpi.md @@ -0,0 +1,143 @@ +# `cmpi` β€” Compare Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x2c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cmpi` | `cmpi` | β€” | Compare Immediate | + +## Syntax + +```asm +cmpi [CRFD], [L], [RA], [SIMM] +``` + +## Encoding + +### `cmpi` β€” form `D` + +- **Opcode word:** `0x2c000000` +- **Primary opcode (bits 0–5):** `11` +- **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 | +| --- | --- | --- | +| `L` | cmpi: read | Operand-length bit for compare instructions (`0 β‡’ 32-bit`, `1 β‡’ 64-bit`). | +| `RA` | cmpi: read | Source GPR (`r0`–`r31`). | +| `SIMM` | cmpi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | cmpi: write | Destination GPR. | +| `CRFD` | cmpi: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `cmpi` + +- **Reads (always):** `L`, `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CRFD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS(SIMM) +else a,b <- (RA), EXTS(SIMM) +CR[BF] <- signed_compare(a, b) || XER[SO] +``` + +## 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 + +**`cmpi`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cmpi"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:552`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L552) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:335`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L335) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:824-849`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L824-L849) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cmpi => { + let bf = instr.crfd(); + if instr.l() { + // 64-bit compare. Compare directly so boundary i64 values + // (e.g. ra=i64::MIN, imm=1) don't mis-sign through a + // wrapped subtract. + let ra = ctx.gpr[instr.ra()] as i64; + let imm = instr.simm16() as i64; + ctx.cr[bf] = crate::context::CrField { + lt: ra < imm, + gt: ra > imm, + eq: ra == imm, + so: ctx.xer_so != 0, + }; + } else { + let ra = ctx.gpr[instr.ra()] as i32; + let imm = instr.simm16() as i32; + ctx.cr[bf] = crate::context::CrField { + lt: ra < imm, + gt: ra > imm, + eq: ra == imm, + so: ctx.xer_so != 0, + }; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Immediate is sign-extended.** `SIMM` is treated as a signed 16-bit value in the range `[-32768, 32767]` and sign-extended to the operand width. Use [`cmpli`](cmpli.md) for unsigned comparisons against a 16-bit value. +- **`L` bit selects width.** `L = 0` (the usual `cmpwi`) compares the low 32 bits of `RA` (sign-extended) against `EXTS(SIMM)`; `L = 1` (`cmpdi`) does a full 64-bit signed compare. Most Xbox 360 code uses `cmpwi` because pointers and counters are 32-bit ABI. +- **Simplified mnemonics dominate disassembly.** `cmpwi crN, RA, SIMM` ≑ `cmpi crN, 0, RA, SIMM` and `cmpdi crN, RA, SIMM` ≑ `cmpi crN, 1, RA, SIMM`. The default CR field is `cr0` if omitted. +- **`BF` is a CR field (0–7), not a bit.** Same convention as [`cmp`](cmp.md). Distinct standalone compares should target `cr1..cr7` to avoid clobbering the implicit `cr0` chain set up by `Rc=1` arithmetic. +- **SO is copied from `XER[SO]`.** This makes overflow observable downstream of an `addo.` / `mulo.` etc. via `bso`/`bns`. +- **Xenia-rs quirk.** The interpreter recomputes `EQ` after the signed subtract, defending against the same 32-bit narrowing edge case noted in [`cmp`](cmp.md). Functionally equivalent to spec. +- **No register written** other than the 4-bit CR field β€” there is no `Rc` or `OE` bit. + +## Related Instructions + +- [`cmp`](cmp.md) β€” register-register signed compare. +- [`cmpli`](cmpli.md) β€” unsigned compare against immediate (zero-extended). +- [`cmpl`](cmpl.md) β€” unsigned register compare. +- `cmpwi`, `cmpdi` (simplified mnemonics) β€” select `L=0` / `L=1`. +- [`mcrxr`](mcrxr.md) β€” clear sticky overflow before a fresh compare sequence. + +## IBM Reference + +- [AIX 7.3 β€” `cmpi` (Compare Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpi-compare-immediate-instruction) +- [AIX 7.3 β€” `cmpwi` / `cmpdi` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmpwi-compare-word-immediate) diff --git a/tools/ppc-manual/alu/cmpl.md b/tools/ppc-manual/alu/cmpl.md new file mode 100644 index 00000000..82ee3a5b --- /dev/null +++ b/tools/ppc-manual/alu/cmpl.md @@ -0,0 +1,127 @@ +# `cmpl` β€” Compare Logical + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000040` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cmpl` | `cmpl` | β€” | Compare Logical | + +## Syntax + +```asm +cmpl [CRFD], [L], [RA], [RB] +``` + +## Encoding + +### `cmpl` β€” form `X` + +- **Opcode word:** `0x7c000040` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `32` +- **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 | +| --- | --- | --- | +| `L` | cmpl: read | Operand-length bit for compare instructions (`0 β‡’ 32-bit`, `1 β‡’ 64-bit`). | +| `RA` | cmpl: read | Source GPR (`r0`–`r31`). | +| `RB` | cmpl: read | Source GPR. | +| `CRFD` | cmpl: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `cmpl` + +- **Reads (always):** `L`, `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if L = 0 then a,b <- (RA)[32:63], (RB)[32:63] +else a,b <- (RA), (RB) +CR[BF] <- unsigned_compare(a, b) || XER[SO] +``` + +## 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 + +**`cmpl`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cmpl"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:579`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L579) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:761`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L761) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:886-894`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L886-L894) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cmpl => { + let bf = instr.crfd(); + if instr.l() { + ctx.update_cr_unsigned(bf, ctx.gpr[instr.ra()], ctx.gpr[instr.rb()]); + } else { + ctx.update_cr_unsigned(bf, ctx.gpr[instr.ra()] as u32 as u64, ctx.gpr[instr.rb()] as u32 as u64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned compare.** Treats both operands as unsigned magnitudes. The simplified mnemonics are `cmplw` (`L=0`) and `cmpld` (`L=1`). +- **`L = 0`: 32-bit operands.** Xenia narrows both registers via `as u32 as u64` so the high 32 bits of `RA`/`RB` are ignored β€” this matches spec `(RA)[32:63]` semantics. Most Xbox 360 code uses this mode. +- **`L = 1`: full 64-bit unsigned compare.** Used in 64-bit pointer arithmetic; rare in game code but appears in kernel-side helpers. +- **SO is copied from `XER[SO]`.** `cmpl` does not clear or set sticky overflow; it just exposes the current `SO` in the destination CR field's `SO` slot. +- **`BF` is a CR field 0–7.** Same convention as [`cmp`](cmp.md). Two consecutive `cmpl` instructions with the same `BF` simply overwrite the previous result. +- **Common signed/unsigned bug.** Misusing `cmp` instead of `cmpl` (or vice versa) for pointer comparisons is the canonical bug in PPC porting; pointers are always unsigned in C semantics. Always cross-check the comparison polarity in disassembly. +- **No `Rc`/`OE`** and no GPR write β€” purely a CR-field producer. + +## Related Instructions + +- [`cmp`](cmp.md) β€” signed register compare. +- [`cmpli`](cmpli.md) β€” unsigned compare against a 16-bit immediate. +- [`cmpi`](cmpi.md) β€” signed immediate compare. +- `cmplw`, `cmpld` (simplified) β€” preferred forms in disassembly. +- [`mcrxr`](mcrxr.md) β€” clear sticky overflow. + +## IBM Reference + +- [AIX 7.3 β€” `cmpl` (Compare Logical)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpl-compare-logical-instruction) +- [AIX 7.3 β€” `cmplw` / `cmpld` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmplw-compare-logical-word) diff --git a/tools/ppc-manual/alu/cmpli.md b/tools/ppc-manual/alu/cmpli.md new file mode 100644 index 00000000..526a675e --- /dev/null +++ b/tools/ppc-manual/alu/cmpli.md @@ -0,0 +1,127 @@ +# `cmpli` β€” Compare Logical Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x28000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cmpli` | `cmpli` | β€” | Compare Logical Immediate | + +## Syntax + +```asm +cmpli [CRFD], [L], [RA], [UIMM] +``` + +## Encoding + +### `cmpli` β€” form `D` + +- **Opcode word:** `0x28000000` +- **Primary opcode (bits 0–5):** `10` +- **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 | +| --- | --- | --- | +| `L` | cmpli: read | Operand-length bit for compare instructions (`0 β‡’ 32-bit`, `1 β‡’ 64-bit`). | +| `RA` | cmpli: read | Source GPR (`r0`–`r31`). | +| `UIMM` | cmpli: read | 16-bit unsigned immediate. Zero-extended. | +| `CRFD` | cmpli: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `cmpli` + +- **Reads (always):** `L`, `RA`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if L = 0 then a,b <- (RA)[32:63], UIMM +else a,b <- (RA), (0 || UIMM) +CR[BF] <- unsigned_compare(a, b) || XER[SO] +``` + +## 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 + +**`cmpli`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cmpli"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:608`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L608) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:334`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L334) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:850-862`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L850-L862) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cmpli => { + let bf = instr.crfd(); + if instr.l() { + let ra = ctx.gpr[instr.ra()]; + let imm = instr.uimm16() as u64; + ctx.update_cr_unsigned(bf, ra, imm); + } else { + let ra = ctx.gpr[instr.ra()] as u32 as u64; + let imm = instr.uimm16() as u64; + ctx.update_cr_unsigned(bf, ra, imm); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Immediate is zero-extended.** `UIMM` is a 16-bit value extended with zeros, so the comparable range is `[0, 65535]`. To compare against a value with high bits set, materialise it in a register with `lis`/`ori` and use [`cmpl`](cmpl.md). +- **`L` bit selects width.** `L = 0` (`cmplwi`) zero-extends `RA[32:63]` to 64 bits and compares against the 16-bit immediate (also zero-extended). `L = 1` (`cmpldi`) compares the full 64-bit `RA` against the immediate. +- **Simplified mnemonics dominate.** `cmplwi cr0, RA, UIMM` ≑ `cmpli cr0, 0, RA, UIMM`; the assembler injects the `L` bit automatically. +- **No sign-extension surprises.** Unlike [`cmpi`](cmpi.md), the immediate cannot be negative; `cmpli` always tests an unsigned magnitude. +- **Common idiom: `cmplwi rA, 0`** to test a register for zero β€” slightly clearer in disassembly than `cmpwi rA, 0` because it doesn't suggest signed semantics. Both produce the same `EQ` result for a zero argument. +- **`BF` chooses one of 8 CR fields**; same convention as `cmp`. + +## Related Instructions + +- [`cmpl`](cmpl.md) β€” register-register unsigned compare. +- [`cmpi`](cmpi.md) β€” signed compare against a 16-bit immediate. +- [`cmp`](cmp.md) β€” register-register signed compare. +- `cmplwi`, `cmpldi` (simplified) β€” most common form seen in disassembly. + +## IBM Reference + +- [AIX 7.3 β€” `cmpli` (Compare Logical Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpli-compare-logical-immediate-instruction) +- [AIX 7.3 β€” `cmplwi` / `cmpldi` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmplwi-compare-logical-word-immediate) diff --git a/tools/ppc-manual/alu/cntlzdx.md b/tools/ppc-manual/alu/cntlzdx.md new file mode 100644 index 00000000..68f3767e --- /dev/null +++ b/tools/ppc-manual/alu/cntlzdx.md @@ -0,0 +1,119 @@ +# `cntlzdx` β€” Count Leading Zeros Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000074` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cntlzd` | `cntlzdx` | β€” | Count Leading Zeros Doubleword | +| `cntlzd.` | `cntlzdx` | Rc=1 | Count Leading Zeros Doubleword | + +## Syntax + +```asm +cntlzd [RA], [RS] +``` + +## Encoding + +### `cntlzdx` β€” form `X` + +- **Opcode word:** `0x7c000074` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `58` +- **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` | cntlzdx: read | Source GPR (alias for RD in some stores). | +| `RA` | cntlzdx: write | Source GPR (`r0`–`r31`). | +| `CR` | cntlzdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `cntlzdx` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `cntlzdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- number_of_leading_zero_bits((RS)) ; n in 0..64 +RA <- zero_extend(n) +``` + +## 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 + +**`cntlzdx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cntlzdx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:674`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L674) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:15`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L15) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:767`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L767) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:615-619`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L615-L619) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cntlzdx => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()].leading_zeros() as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Result range is `0..=64`.** When `RS == 0`, every bit is a leading zero and `RA = 64`. When the high bit (`RS[0]`) is set, `RA = 0`. Any intermediate value yields the count of high-order zeros before the first 1-bit. +- **Counts across the full 64 bits.** Use [`cntlzwx`](cntlzwx.md) when you only want to count the low 32 bits. +- **Useful as `floor(log2(x)) = 63 βˆ’ cntlzd(x)`** for nonzero `x`. Frequently used in fast normalization, priority encoders, and bit-vector operations. +- **`RB` field unused.** This is X-form but only `RS` is read; `RB` is a placeholder slot. +- **`Rc=1` quirk.** `update_cr_signed(0, RA as i64)` is correct in xenia-rs because the result fits in 7 bits and is non-negative. The CR0 result will always be `EQ` (when `RS != 0` and `RA != 0`? β€” actually `EQ` only when `RS[0] == 1`, i.e. `RA == 0`) or `GT` (when `RS != 0` so `RA > 0`); never `LT`. `EQ` corresponds to "high bit set in `RS`", a useful one-instruction sign test for negative-as-signed values. +- **No `XER` side effects.** Counts neither overflow nor carry. + +## Related Instructions + +- [`cntlzwx`](cntlzwx.md) β€” 32-bit version (counts only `RS[32:63]`). +- [`sldx`](sldx.md), [`srdx`](srdx.md) β€” pair with `cntlzd` for normalisation. +- [`rldiclx`](rldiclx.md) β€” for extracting individual bit positions once the leading-zero count is known. +- [`cmpi`](cmpi.md) / [`cmp`](cmp.md) β€” alternative for testing the high bit. + +## IBM Reference + +- [AIX 7.3 β€” `cntlzd` (Count Leading Zeros Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cntlzd-count-leading-zeros-double-word-instruction) diff --git a/tools/ppc-manual/alu/cntlzwx.md b/tools/ppc-manual/alu/cntlzwx.md new file mode 100644 index 00000000..d764154d --- /dev/null +++ b/tools/ppc-manual/alu/cntlzwx.md @@ -0,0 +1,121 @@ +# `cntlzwx` β€” Count Leading Zeros Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000034` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cntlzw` | `cntlzwx` | β€” | Count Leading Zeros Word | +| `cntlzw.` | `cntlzwx` | Rc=1 | Count Leading Zeros Word | + +## Syntax + +```asm +cntlzw[Rc] [RA], [RS] +``` + +## Encoding + +### `cntlzwx` β€” form `X` + +- **Opcode word:** `0x7c000034` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `26` +- **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` | cntlzwx: read | Source GPR (alias for RD in some stores). | +| `RA` | cntlzwx: write | Source GPR (`r0`–`r31`). | +| `CR` | cntlzwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `cntlzwx` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `cntlzwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- number_of_leading_zero_bits((RS)[32:63]) ; n in 0..32 +RA <- zero_extend(n) +``` + +## 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 + +**`cntlzwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cntlzwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:689`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L689) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:15`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L15) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:758`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L758) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:608-614`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L608-L614) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::cntlzwx => { + // Result is 0..=32, fits in u32 with bit 31 always zero, so the + // CR0 view is benign β€” use the catch-all 32-bit form for consistency. + ctx.gpr[instr.ra()] = (ctx.gpr[instr.rs()] as u32).leading_zeros() as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operates only on the low 32 bits.** `RS[0:31]` is ignored; the count is taken on `RS[32:63]`. Result range is `0..=32`. +- **`RA = 32` when the low 32 bits are zero**, regardless of the high 32 bits. A common pitfall: `cntlzw` after computing a 64-bit value can give a counter-intuitive result when the leading 1-bit lives in the high half. +- **`RA = 0` when bit 32 (the sign bit of the low word) is set.** This makes `cntlzw RA, RS; cmpwi RA, 0` a one-instruction-pair "is the low half negative" test, though `srawi RA, RS, 31` is more idiomatic. +- **High 32 bits of the result are zero.** `RA[0:31] = 0`, `RA[32:63] = count`. +- **`Rc=1` CR0 update is small-positive-only.** Result fits in 6 bits; xenia's `as i32 as i64` truncation in [`interpreter.rs:404`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L404) is harmless. CR0 will be `EQ` only when `RS[32]` (sign bit of low word) is 1, otherwise `GT`. +- **Useful for `floor(log2)` of a 32-bit value.** `31 - cntlzw(x)` for nonzero `x`. + +## Related Instructions + +- [`cntlzdx`](cntlzdx.md) β€” 64-bit version (counts the full register). +- [`slwx`](slwx.md), [`srwx`](srwx.md), [`srawx`](srawx.md) β€” 32-bit shifts often paired with `cntlzw` for normalisation. +- [`rlwinmx`](rlwinmx.md) β€” to mask off bits before counting. +- [`cmpi`](cmpi.md), [`cmpl`](cmpl.md) β€” alternative ways to detect zero or sign. + +## IBM Reference + +- [AIX 7.3 β€” `cntlzw` (Count Leading Zeros Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cntlzw-count-leading-zeros-word-instruction) diff --git a/tools/ppc-manual/alu/divdux.md b/tools/ppc-manual/alu/divdux.md new file mode 100644 index 00000000..1ea26a1b --- /dev/null +++ b/tools/ppc-manual/alu/divdux.md @@ -0,0 +1,135 @@ +# `divdux` β€” Divide Doubleword Unsigned + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000392` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `divdu` | `divdux` | β€” | Divide Doubleword Unsigned | +| `divduo` | `divdux` | OE=1 | Divide Doubleword Unsigned | +| `divdu.` | `divdux` | Rc=1 | Divide Doubleword Unsigned | +| `divduo.` | `divdux` | OE=1, Rc=1 | Divide Doubleword Unsigned | + +## Syntax + +```asm +divdu[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `divdux` β€” form `XO` + +- **Opcode word:** `0x7c000392` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `457` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | divdux: read | Source GPR (`r0`–`r31`). | +| `RB` | divdux: read | Source GPR. | +| `RD` | divdux: write | Destination GPR. | +| `OE` | divdux: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | divdux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `divdux` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `divdux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- (RA) /u (RB) ; undefined if RB=0 +``` + +## 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 + +**`divdux`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="divdux"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:217`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L217) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:876`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L876) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:480-496`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L480-L496) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::divdux => { + let ra = ctx.gpr[instr.ra()]; + let rb = ctx.gpr[instr.rb()]; + let ov = overflow::divd_ov_unsigned(rb); + if ov { + ctx.gpr[instr.rd()] = 0; + } else { + ctx.gpr[instr.rd()] = ra / rb; + } + if instr.oe() { + overflow::apply(ctx, ov); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single undefined case.** Division by zero (`RB == 0`). There is no `INT_MIN/βˆ’1` overflow because both operands are unsigned. Xenia-rs returns 0 for the divide-by-zero case ([`interpreter.rs:306`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L306)); spec leaves `RT` boundedly undefined. +- **No trap on Xenon.** As with [`divdx`](divdx.md), the processor does not raise an exception; consuming code must guard `RB` first (typically `cmpdi rb, 0; beq skip`). +- **`OE=1` should set `XER[OV]`** on `RB == 0`; xenia-rs ignores `OE` here. +- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:311`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L311) uses `as i64` directly, so the CR0 sign comparison reflects the full 64-bit unsigned quotient cast to signed. For very large unsigned quotients (`> INT64_MAX`) this CR0 will report `LT` even though the unsigned interpretation is positive β€” a rare but real source of CR-misuse bugs. +- **Slow.** Same ~70-cycle non-pipelined cost as the signed variant; consider reciprocal multiply for hot loops. +- **Truncating quotient.** Same C-style toward-zero rounding (trivially equal to floor for unsigned). + +## Related Instructions + +- [`divdx`](divdx.md) β€” signed 64-bit divide. +- [`divwux`](divwux.md), [`divwx`](divwx.md) β€” 32-bit unsigned/signed. +- [`mulldx`](mulldx.md), [`mulhdux`](mulhdux.md) β€” multiply pair for remainder calculation. +- [`cmpli`](cmpli.md), [`cmpl`](cmpl.md) β€” guard the divisor. + +## IBM Reference + +- [AIX 7.3 β€” `divdu` (Divide Doubleword Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divdu-divide-double-word-unsigned-instruction) diff --git a/tools/ppc-manual/alu/divdx.md b/tools/ppc-manual/alu/divdx.md new file mode 100644 index 00000000..581b428c --- /dev/null +++ b/tools/ppc-manual/alu/divdx.md @@ -0,0 +1,136 @@ +# `divdx` β€” Divide Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0003d2` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `divd` | `divdx` | β€” | Divide Doubleword | +| `divdo` | `divdx` | OE=1 | Divide Doubleword | +| `divd.` | `divdx` | Rc=1 | Divide Doubleword | +| `divdo.` | `divdx` | OE=1, Rc=1 | Divide Doubleword | + +## Syntax + +```asm +divd[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `divdx` β€” form `XO` + +- **Opcode word:** `0x7c0003d2` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `489` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | divdx: read | Source GPR (`r0`–`r31`). | +| `RB` | divdx: read | Source GPR. | +| `RD` | divdx: write | Destination GPR. | +| `OE` | divdx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | divdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `divdx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `divdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and RB=-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 + +**`divdx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="divdx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:192`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L192) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:878`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L878) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:463-479`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L463-L479) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::divdx => { + let ra = ctx.gpr[instr.ra()] as i64; + let rb = ctx.gpr[instr.rb()] as i64; + let ov = overflow::divd_ov_signed(ra, rb); + if ov { + ctx.gpr[instr.rd()] = 0; + } else { + ctx.gpr[instr.rd()] = (ra / rb) as u64; + } + if instr.oe() { + overflow::apply(ctx, ov); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Two undefined-behaviour cases.** Division by zero (`RB == 0`) and signed-min divided by negative-one (`RA == INT64_MIN && RB == -1`, which would mathematically produce `2^63`, unrepresentable in `i64`). PowerISA leaves `RT` *boundedly undefined* in both cases; **xenia-rs returns 0** ([`interpreter.rs:293`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L293)). Matching this behaviour bit-for-bit is a defacto-spec on Xenon. +- **No exception raised.** Xenon does not trap on either undefined case; the consuming code is expected to have validated `RB` first, e.g. with `cmpdi`/`bne`. If you need a trap, follow the divide with [`tw`](../control/tw.md)/`twi` (these live outside the ALU page set). +- **`OE=1` should set `XER[OV]`** for both undefined cases plus any operand triggering overflow; xenia-rs does not implement the `OE` branch. +- **`Rc=1` CR0 update is correctly 64-bit here.** Unlike most ALU pages, [`interpreter.rs:298`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L298) uses `as i64` (no `as i32` truncation) β€” divide is one of the few xenia-rs instructions that already matches Xenon spec for the CR0 width. +- **Latency.** Integer divide is the slowest ALU instruction on Xenon β€” 70+ cycles, non-pipelined. Hot inner loops avoid it via reciprocal-multiply or shift; expect to see `mulhwu`-based reciprocals in optimised disassembly. +- **Rounds toward zero.** The signed quotient truncates toward zero, matching C99/C++11 `/` semantics. Use [`mulldx`](mulldx.md) and a subtract to recover the remainder; there is no `divmod` instruction. + +## Related Instructions + +- [`divdux`](divdux.md) β€” unsigned 64-bit divide. +- [`divwx`](divwx.md), [`divwux`](divwux.md) β€” 32-bit signed/unsigned variants. +- [`mulldx`](mulldx.md), [`mulhdx`](mulhdx.md) β€” multiply pair used to compute the remainder. +- [`cmpi`](cmpi.md), [`cmp`](cmp.md) β€” guard the divisor before invoking divide. + +## IBM Reference + +- [AIX 7.3 β€” `divd` (Divide Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divd-divide-double-word-instruction) +- PowerISA v2.07B, Book I, Β§3.3.9 β€” defines the boundedly-undefined behaviour for `RB=0` and `INT_MIN/βˆ’1`. diff --git a/tools/ppc-manual/alu/divwux.md b/tools/ppc-manual/alu/divwux.md new file mode 100644 index 00000000..713d7f0c --- /dev/null +++ b/tools/ppc-manual/alu/divwux.md @@ -0,0 +1,137 @@ +# `divwux` β€” Divide Word Unsigned + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000396` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `divwu` | `divwux` | β€” | Divide Word Unsigned | +| `divwuo` | `divwux` | OE=1 | Divide Word Unsigned | +| `divwu.` | `divwux` | Rc=1 | Divide Word Unsigned | +| `divwuo.` | `divwux` | OE=1, Rc=1 | Divide Word Unsigned | + +## Syntax + +```asm +divwu[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `divwux` β€” form `XO` + +- **Opcode word:** `0x7c000396` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `459` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | divwux: read | Source GPR (`r0`–`r31`). | +| `RB` | divwux: read | Source GPR. | +| `RD` | divwux: write | Destination GPR. | +| `OE` | divwux: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | divwux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `divwux` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `divwux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA)[32:63] /u (RB)[32:63]) zero-extended to 64 ; undefined if RB=0 +``` + +## 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 + +**`divwux`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="divwux"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:269`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L269) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:877`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L877) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:413-430`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L413-L430) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::divwux => { + // PPCBUG-020: 32-bit ABI CR0 view. + let ra = ctx.gpr[instr.ra()] as u32; + let rb = ctx.gpr[instr.rb()] as u32; + let ov = overflow::divw_ov_unsigned(rb); + if ov { + ctx.gpr[instr.rd()] = 0; + } else { + ctx.gpr[instr.rd()] = (ra / rb) as u64; + } + if instr.oe() { + overflow::apply(ctx, ov); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit operands, zero-extended result.** Both `RA` and `RB` are read as their low 32 bits, unsigned (`as u32`); the quotient is computed as `u32`, then *zero-extended* to 64 bits. The high 32 bits of `RA`/`RB` are ignored on input and the high 32 bits of `RT` are zero on output. +- **Single undefined case.** Division by zero (`RB == 0`); xenia-rs returns 0 ([`interpreter.rs:251`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L251)). No `INT_MIN/-1` case because the operands are unsigned. +- **No trap on Xenon.** Same as [`divdx`](divdx.md) β€” silent undefined result. +- **`OE=1` should set `XER[OV]` on `RB == 0`**; xenia-rs ignores this. +- **`Rc=1` CR0 update.** [`interpreter.rs:256`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L256) uses `as i32 as i64` β€” for an unsigned 32-bit quotient stored in the low 32 bits with high zeros, this matches spec exactly; the i32 view will be negative iff the unsigned quotient β‰₯ 2^31. Worth flagging when comparing CR0 against zero after a large `divwu`. +- **Truncating quotient.** Floor division for non-negative integers; matches C `unsigned` semantics. +- **Same slow non-pipelined latency** as `divw`. + +## Related Instructions + +- [`divwx`](divwx.md) β€” signed 32-bit divide. +- [`divdux`](divdux.md), [`divdx`](divdx.md) β€” 64-bit variants. +- [`mullwx`](mullwx.md) β€” pair to recover the remainder. +- [`cmplwi`](cmpli.md) (simplified) β€” guard the divisor. + +## IBM Reference + +- [AIX 7.3 β€” `divwu` (Divide Word Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divwu-divide-word-unsigned-instruction) diff --git a/tools/ppc-manual/alu/divwx.md b/tools/ppc-manual/alu/divwx.md new file mode 100644 index 00000000..61eea801 --- /dev/null +++ b/tools/ppc-manual/alu/divwx.md @@ -0,0 +1,138 @@ +# `divwx` β€” Divide Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0003d6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `divw` | `divwx` | β€” | Divide Word | +| `divwo` | `divwx` | OE=1 | Divide Word | +| `divw.` | `divwx` | Rc=1 | Divide Word | +| `divwo.` | `divwx` | OE=1, Rc=1 | Divide Word | + +## Syntax + +```asm +divw[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `divwx` β€” form `XO` + +- **Opcode word:** `0x7c0003d6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `491` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | divwx: read | Source GPR (`r0`–`r31`). | +| `RB` | divwx: read | Source GPR. | +| `RD` | divwx: write | Destination GPR. | +| `OE` | divwx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | +| `CR` | divwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `divwx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `OE`, `CR` + +## Status-Register Effects + +- `divwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or overflow +``` + +## 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 + +**`divwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="divwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:242`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L242) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:879`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L879) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:394-412`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L394-L412) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::divwx => { + // PPCBUG-010+011 coupled: 32-bit ABI. Quotient zero-extended to u64 + // (canary explicitly uses ZeroExtend(v, INT64_TYPE)). CR0 view via i32. + let ra = ctx.gpr[instr.ra()] as i32; + let rb = ctx.gpr[instr.rb()] as i32; + let ov = overflow::divw_ov_signed(ra, rb); + if ov { + ctx.gpr[instr.rd()] = 0; + } else { + ctx.gpr[instr.rd()] = (ra / rb) as u32 as u64; + } + if instr.oe() { + overflow::apply(ctx, ov); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit operands, sign-extended result.** Both `RA` and `RB` are read as their low 32 bits, signed; the quotient is computed as `i32`, then sign-extended to 64 bits before being stored in `RT`. The high 32 bits of `RA`/`RB` are *ignored*. +- **Two undefined cases:** `RB == 0` and `RA == INT32_MIN && RB == -1` (quotient `2^31` is unrepresentable). Xenia-rs returns 0 for both ([`interpreter.rs:238`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L238)); PowerISA leaves `RT` boundedly undefined. +- **No trap on Xenon.** Like [`divdx`](divdx.md), the processor silently produces an undefined value instead of raising an exception. +- **`OE=1` should set `XER[OV]`** in both undefined cases; xenia-rs does not implement this. +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:243`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L243) uses `as i32 as i64`. This is *correct* for `divw` because the result is already a sign-extended 32-bit value β€” high bits agree with the low-32 sign extension. So spec and xenia agree for this instruction. +- **Truncating quotient.** Rounds toward zero, matching C `/` for `int32_t`. +- **Slow.** Same ~30-cycle non-pipelined cost as 64-bit divide; faster than `divd` because the underlying datapath is narrower but still much slower than multiply-then-shift reciprocal sequences. + +## Related Instructions + +- [`divwux`](divwux.md) β€” unsigned 32-bit divide. +- [`divdx`](divdx.md), [`divdux`](divdux.md) β€” 64-bit variants. +- [`mullwx`](mullwx.md) β€” pair with subtract to obtain the remainder. +- [`extsw`](extswx.md) β€” manual sign-extend if you only have a 64-bit value but want 32-bit divide semantics. + +## IBM Reference + +- [AIX 7.3 β€” `divw` (Divide Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divw-divide-word-instruction) diff --git a/tools/ppc-manual/alu/eieio.md b/tools/ppc-manual/alu/eieio.md new file mode 100644 index 00000000..4e5dac40 --- /dev/null +++ b/tools/ppc-manual/alu/eieio.md @@ -0,0 +1,111 @@ +# `eieio` β€” Enforce In-Order Execution of I/O + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0006ac` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `eieio` | `eieio` | β€” | Enforce In-Order Execution of I/O | + +## Syntax + +```asm +eieio +``` + +## Encoding + +### `eieio` β€” form `X` + +- **Opcode word:** `0x7c0006ac` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `854` +- **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 + +### `eieio` + +- **Reads (always):** _none_ +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +enforce in-order execution of I/O +``` + +## 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 + +**`eieio`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="eieio"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:749`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L749) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:23`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L23) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:844`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L844) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => { + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Memory-ordering barrier for caching-inhibited / guarded storage.** `eieio` ensures all preceding loads/stores to caching-inhibited or guarded memory complete before any subsequent such accesses begin. It is *weaker* than [`sync`](sync.md): it does not order cacheable storage and does not flush the store queue. +- **No register or CR effects.** Every operand field is unused; assemblers emit the canonical `0x7c0006ac` word. +- **Used at MMIO boundaries.** Driver code touching device registers (e.g. the GPU command processor on Xenon) typically pairs writes with `eieio` to enforce write ordering at the bus. +- **Xenia-rs is a no-op.** The interpreter trivially advances PC ([`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267)). Because xenia-rs is a single-threaded interpreter targeting userland Xbox 360 binaries β€” which never see real MMIO β€” this is correct: the host's natural program order suffices. +- **Categorised under ALU here**, but operationally it's a memory ordering primitive (xenia-canary places it in `ppc_emit_memory.cc`). Disassembly tools may bin it differently. +- **Distinct from `sync` and `isync`.** All three share xenia's no-op arm in [`interpreter.rs:1266`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1266); on real hardware they have very different semantics and latencies. + +## Related Instructions + +- [`sync`](sync.md) β€” heavy memory barrier (orders *all* storage). +- [`isync`](isync.md) β€” instruction-fetch barrier; refetches and re-executes after the boundary. +- `lwsync` β€” lighter weight than `sync`; not in this page set. + +## IBM Reference + +- [AIX 7.3 β€” `eieio` (Enforce In-Order Execution of I/O)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-eieio-enforce-in-order-execution-i-o-instruction) diff --git a/tools/ppc-manual/alu/eqvx.md b/tools/ppc-manual/alu/eqvx.md new file mode 100644 index 00000000..a3800fd0 --- /dev/null +++ b/tools/ppc-manual/alu/eqvx.md @@ -0,0 +1,122 @@ +# `eqvx` β€” Equivalent + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000238` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `eqv` | `eqvx` | β€” | Equivalent | +| `eqv.` | `eqvx` | Rc=1 | Equivalent | + +## Syntax + +```asm +eqv[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `eqvx` β€” form `X` + +- **Opcode word:** `0x7c000238` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `284` +- **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` | eqvx: read | Source GPR (alias for RD in some stores). | +| `RB` | eqvx: read | Source GPR. | +| `RA` | eqvx: write | Source GPR (`r0`–`r31`). | +| `CR` | eqvx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `eqvx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `eqvx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- ~((RS) ^ (RB)) +``` + +## 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 + +**`eqvx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="eqvx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:704`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L704) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:796`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L796) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:578-586`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L578-L586) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::eqvx => { + // PPCBUG-031: `eqv rA, rA, rA` is a common "set to all-ones" idiom; + // 64-bit form gave 0xFFFFFFFFFFFFFFFF but 32-bit ABI expects 0x00000000FFFFFFFF. + let rs32 = ctx.gpr[instr.rs()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = (!(rs32 ^ rb32)) as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **NXOR / equivalence.** `RA ← ~(RS XOR RB)`. A bit in `RA` is 1 iff the corresponding bits of `RS` and `RB` are equal. Useful as a per-bit equality test feeding into a `cntlzw` for run-length analysis. +- **Idiom: `eqv RA, RS, RS`** sets every bit to 1 β€” a one-instruction `RA = -1`. Cheaper than `li RA, -1` followed by `oris`/`ori` for full 64-bit `-1`. +- **Operand convention is X-form** (`RA` is destination; `RS`, `RB` are sources). +- **64-bit operation** on Xenon; `~` is full 64-bit on `u64`. +- **No `OE`, no `XER` side effects.** Only `Rc=1` updates `CR0`. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:382`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L382) truncates with `as i32 as i64`. Significant when the high 32 bits of the result differ from the low 32 β€” e.g. `eqv. RA, RS, RB` with `RS == 0x1_0000_0000`, `RB == 0`: spec sees `0xFFFFFFFE_FFFFFFFF` (`LT`), xenia sees `0xFFFFFFFFFFFFFFFF` (`LT`) β€” actually both negative here, but the *exact* CR contents differ for finer cases. + +## Related Instructions + +- [`xorx`](xorx.md) β€” base XOR (`eqv` is `xor` then `not`). +- [`andx`](andx.md), [`orx`](orx.md), [`nandx`](nandx.md), [`norx`](norx.md), [`andcx`](andcx.md), [`orcx`](orcx.md) β€” full logical family. +- [`xori`](xori.md), [`xoris`](xoris.md) β€” immediate XOR (no immediate `eqv` exists). + +## IBM Reference + +- [AIX 7.3 β€” `eqv` (Equivalent)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-eqv-equivalent-instruction) diff --git a/tools/ppc-manual/alu/extsbx.md b/tools/ppc-manual/alu/extsbx.md new file mode 100644 index 00000000..5e736e72 --- /dev/null +++ b/tools/ppc-manual/alu/extsbx.md @@ -0,0 +1,121 @@ +# `extsbx` β€” Extend Sign Byte + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000774` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `extsb` | `extsbx` | β€” | Extend Sign Byte | +| `extsb.` | `extsbx` | Rc=1 | Extend Sign Byte | + +## Syntax + +```asm +extsb[Rc] [RA], [RS] +``` + +## Encoding + +### `extsbx` β€” form `X` + +- **Opcode word:** `0x7c000774` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `954` +- **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` | extsbx: read | Source GPR (alias for RD in some stores). | +| `RA` | extsbx: write | Source GPR (`r0`–`r31`). | +| `CR` | extsbx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `extsbx` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `extsbx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- EXTS_8_to_64((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 + +**`extsbx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="extsbx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:714`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L714) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:849`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L849) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:589-595`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L589-L595) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::extsbx => { + // PPCBUG-034: 32-bit ABI β€” sign-extend byte to i32, write zero-extended. + // PPCBUG-036 (coupled): CR0 must view result as i32, not i64. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i8 as i32 as u32 as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extends the low 8 bits of `RS` to 64 bits.** Bit 56 of `RS` (the sign bit of the byte) becomes bits 0–55 of `RA`; bits 56–63 are copied verbatim. +- **Common after a byte load.** `lbz` zero-extends from memory; `extsb` converts the result to a signed-byte view. Many compilers emit this pair; the recent ISA `lba`/`lbau` family is *not* available on the Xenon, so this two-instruction sequence is the canonical pattern. +- **`Rc=1` updates CR0 from the full 64-bit signed value** β€” but xenia-rs truncates to 32 bits in [`interpreter.rs:384`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L384). For `extsb.` this is harmless because the result fits in 8 bits sign-extended; the low 32 bits already encode the sign correctly. +- **Operand convention** is the X-form one (`RA` destination, `RS` source). Same as the rest of the logical family. +- **No `XER` side effects.** +- **`RB` field unused.** Set to 0 by assemblers; ignored on decode. +- **Aliasing is fine.** `extsb r3, r3` rewrites `r3` in place. + +## Related Instructions + +- [`extshx`](extshx.md) β€” sign-extend half-word (16 bits). +- [`extswx`](extswx.md) β€” sign-extend word (32 bits). +- [`rlwinmx`](rlwinmx.md), [`rldiclx`](rldiclx.md) β€” for *zero*-extending or extracting non-byte-aligned fields. +- `lbz`, `lha` (memory ops, outside this page set) β€” pair with this for signed byte loads. + +## IBM Reference + +- [AIX 7.3 β€” `extsb` (Extend Sign Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsb-extend-sign-byte-instruction) diff --git a/tools/ppc-manual/alu/extshx.md b/tools/ppc-manual/alu/extshx.md new file mode 100644 index 00000000..509cdc8c --- /dev/null +++ b/tools/ppc-manual/alu/extshx.md @@ -0,0 +1,121 @@ +# `extshx` β€” Extend Sign Half Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000734` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `extsh` | `extshx` | β€” | Extend Sign Half Word | +| `extsh.` | `extshx` | Rc=1 | Extend Sign Half Word | + +## Syntax + +```asm +extsh[Rc] [RA], [RS] +``` + +## Encoding + +### `extshx` β€” form `X` + +- **Opcode word:** `0x7c000734` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `922` +- **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` | extshx: read | Source GPR (alias for RD in some stores). | +| `RA` | extshx: write | Source GPR (`r0`–`r31`). | +| `CR` | extshx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `extshx` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `extshx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- EXTS_16_to_64((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 + +**`extshx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="extshx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:727`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L727) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:847`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L847) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:596-602`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L596-L602) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::extshx => { + // PPCBUG-035: same shape as extsbx for halfwords. + // PPCBUG-037 (coupled): CR0 i32 view. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i16 as i32 as u32 as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extends the low 16 bits of `RS` to 64 bits.** Bit 48 (the sign bit of the half-word) is replicated through bits 0–47 of `RA`. +- **Pairs with `lhz`** to convert an unsigned half-word load into a signed half-word value. Note that `lha` already does the sign extension on load β€” `extsh` is mostly emitted when the half-word is computed in a register first. +- **`Rc=1` CR0 update.** Xenia-rs uses `as i32 as i64` ([`interpreter.rs:389`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L389)) β€” harmless here because the sign-extended 16-bit value fits in 32 bits exactly. +- **Operand convention** is the X-form one (`RA` destination, `RS` source). +- **No `XER` side effects.** +- **`RB` field unused.** +- **Aliasing is fine.** `extsh r3, r3` is the standard "promote `r3`'s low 16 bits to a signed 64-bit value" sequence. + +## Related Instructions + +- [`extsbx`](extsbx.md) β€” sign-extend byte. +- [`extswx`](extswx.md) β€” sign-extend word (32 bits). +- [`rlwinmx`](rlwinmx.md) β€” when masking/zero-extending without sign-extension. +- `lha` (memory op, outside this set) β€” combined load + sign-extend. + +## IBM Reference + +- [AIX 7.3 β€” `extsh` (Extend Sign Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsh-extend-sign-half-word-instruction) diff --git a/tools/ppc-manual/alu/extswx.md b/tools/ppc-manual/alu/extswx.md new file mode 100644 index 00000000..18aaba2e --- /dev/null +++ b/tools/ppc-manual/alu/extswx.md @@ -0,0 +1,120 @@ +# `extswx` β€” Extend Sign Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0007b4` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `extsw` | `extswx` | β€” | Extend Sign Word | +| `extsw.` | `extswx` | Rc=1 | Extend Sign Word | + +## Syntax + +```asm +extsw[Rc] [RA], [RS] +``` + +## Encoding + +### `extswx` β€” form `X` + +- **Opcode word:** `0x7c0007b4` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `986` +- **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` | extswx: read | Source GPR (alias for RD in some stores). | +| `RA` | extswx: write | Source GPR (`r0`–`r31`). | +| `CR` | extswx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `extswx` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `extswx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- EXTS_32_to_64((RS)[32: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 + +**`extswx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="extswx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:740`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L740) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:852`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L852) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:603-607`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L603-L607) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::extswx => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i32 as i64 as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extends the low 32 bits of `RS` to 64 bits.** Bit 32 (sign bit of the word) is replicated through bits 0–31 of `RA`. +- **Used heavily in 32-to-64-bit promotion.** Most Xbox 360 ABI parameters are 32-bit; promoting a 32-bit `int` to a 64-bit GPR requires this instruction. Many functions emit it on entry to canonicalise their argument registers. +- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:399`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L399) uses `as i64` (no truncation) β€” one of the few xenia-rs sites where the spec width is honoured. The signed compare in CR0 reflects the full sign-extended value. +- **Operand convention** is the X-form one (`RA` destination, `RS` source). +- **No `XER` side effects.** +- **`RB` field unused.** +- **Aliasing is fine.** `extsw r3, r3` is the canonical "sign-extend in place" idiom. +- **Distinct from `srawi RA, RS, 31`**, which produces the *sign mask* (`-1` if negative else `0`) rather than the sign-extended value. + +## Related Instructions + +- [`extsbx`](extsbx.md), [`extshx`](extshx.md) β€” narrower sign extensions. +- [`srawix`](srawix.md) β€” to derive a sign mask instead. +- [`rldiclx`](rldiclx.md) β€” to *zero*-extend the low 32 bits. +- `lwa` / `lwax` (memory ops) β€” combined load-and-sign-extend; lives outside this set. + +## IBM Reference + +- [AIX 7.3 β€” `extsw` (Extend Sign Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsw-extend-sign-word-instruction) diff --git a/tools/ppc-manual/alu/isync.md b/tools/ppc-manual/alu/isync.md new file mode 100644 index 00000000..75592421 --- /dev/null +++ b/tools/ppc-manual/alu/isync.md @@ -0,0 +1,111 @@ +# `isync` β€” Instruction Synchronize + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c00012c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `isync` | `isync` | β€” | Instruction Synchronize | + +## Syntax + +```asm +isync +``` + +## Encoding + +### `isync` β€” form `XL` + +- **Opcode word:** `0x4c00012c` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `150` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | + +## Register Effects + +### `isync` + +- **Reads (always):** _none_ +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +instruction-stream synchronisation β€” discards speculative state. +``` + +## 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 + +**`isync`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="isync"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:759`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L759) +- 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:714`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L714) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => { + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Instruction-fetch barrier.** Discards any speculatively fetched/decoded instructions and forces all subsequent ones to be re-fetched after preceding instructions complete. Required after self-modifying code, JIT-emitted code, and after MMU/page-table changes. +- **Stronger than [`sync`](sync.md) for instruction stream**, weaker for memory stream β€” `isync` does not order stores against later loads. It only forces a fetch refresh. +- **Common idiom: `dcbf` / `icbi` / `sync` / `isync`** β€” flush data cache, invalidate instruction cache, drain memory, refetch β€” used by JITs and self-modifying loaders. +- **No operands.** Encoded as a fixed-form `XL` instruction; assemblers always emit `0x4c00012c`. +- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267) handles `sync`/`eieio`/`isync` together. Because xenia interprets in straight-line program order without any speculative instruction cache, no barrier behaviour is needed for correctness. +- **Privilege level: user.** Unlike most cache management ops, `isync` is unprivileged and frequently appears in userland trampolines. + +## Related Instructions + +- [`sync`](sync.md) β€” heavy memory barrier. +- [`eieio`](eieio.md) β€” I/O ordering for caching-inhibited storage. +- `icbi`, `dcbf`, `dcbst` β€” cache management ops (outside this page set) usually paired with `isync`. + +## IBM Reference + +- [AIX 7.3 β€” `isync` (Instruction Synchronize)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-isync-instruction-synchronize-instruction) diff --git a/tools/ppc-manual/alu/mulhdux.md b/tools/ppc-manual/alu/mulhdux.md new file mode 100644 index 00000000..10d93588 --- /dev/null +++ b/tools/ppc-manual/alu/mulhdux.md @@ -0,0 +1,124 @@ +# `mulhdux` β€” Multiply High Doubleword Unsigned + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000012` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulhdu` | `mulhdux` | β€” | Multiply High Doubleword Unsigned | +| `mulhdu.` | `mulhdux` | Rc=1 | Multiply High Doubleword Unsigned | + +## Syntax + +```asm +mulhdu[Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mulhdux` β€” form `XO` + +- **Opcode word:** `0x7c000012` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `9` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mulhdux: read | Source GPR (`r0`–`r31`). | +| `RB` | mulhdux: read | Source GPR. | +| `RD` | mulhdux: write | Destination GPR. | +| `CR` | mulhdux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mulhdux` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mulhdux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA) * (RB))[0:63] ; high 64 of unsigned 64Γ—64 +``` + +## 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 + +**`mulhdux`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulhdux"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:311`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L311) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:860`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L860) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:454-462`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L454-L462) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulhdux => { + let ra = ctx.gpr[instr.ra()] as u128; + let rb = ctx.gpr[instr.rb()] as u128; + ctx.gpr[instr.rd()] = (ra.wrapping_mul(rb) >> 64) as u64; + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Returns the high 64 bits of an unsigned 64Γ—64 product.** Operands are zero-extended (treated as unsigned) before multiplication. Pair with [`mulldx`](mulldx.md) for the low 64 bits to form a full 128-bit unsigned product. +- **No `OE` bit.** No overflow signal β€” the high half is a defined function of the inputs even when the product fills 128 bits. +- **Xenia uses native `u128`.** [`interpreter.rs:284`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L284) widens both operands then shifts. Note `as u128` *zero*-extends, in contrast to [`mulhdx`](mulhdx.md)'s `as i64 as i128` which sign-extends β€” this is the entire semantic difference. +- **`Rc=1` CR0 update is correctly 64-bit.** Uses `as i64` directly. Because the high half is unsigned, treating it as signed for CR0 means very large unsigned values appear `LT` β€” keep this in mind when interpreting the CR0 bits after `mulhdu.`. +- **Used in reciprocal-multiply division strategies.** Compilers may strength-reduce divide-by-constant into `mulhdu` plus a shift; appears in optimised disassembly. +- **Slow.** Same multi-cycle cost as the signed variant. + +## Related Instructions + +- [`mulldx`](mulldx.md) β€” low 64 bits of the same unsigned product. +- [`mulhdx`](mulhdx.md) β€” signed high half. +- [`mulhwux`](mulhwux.md) β€” 32-bit unsigned high half. +- [`divdux`](divdux.md) β€” 64-bit unsigned divide; sometimes replaced by reciprocal `mulhdu`. + +## IBM Reference + +- [AIX 7.3 β€” `mulhdu` (Multiply High Doubleword Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhdu-multiply-high-double-word-unsigned-instruction) diff --git a/tools/ppc-manual/alu/mulhdx.md b/tools/ppc-manual/alu/mulhdx.md new file mode 100644 index 00000000..b9b49d6f --- /dev/null +++ b/tools/ppc-manual/alu/mulhdx.md @@ -0,0 +1,124 @@ +# `mulhdx` β€” Multiply High Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000092` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulhd` | `mulhdx` | β€” | Multiply High Doubleword | +| `mulhd.` | `mulhdx` | Rc=1 | Multiply High Doubleword | + +## Syntax + +```asm +mulhd[Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mulhdx` β€” form `XO` + +- **Opcode word:** `0x7c000092` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `73` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mulhdx: read | Source GPR (`r0`–`r31`). | +| `RB` | mulhdx: read | Source GPR. | +| `RD` | mulhdx: write | Destination GPR. | +| `CR` | mulhdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mulhdx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mulhdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA) * (RB))[0:63] ; high 64 of signed 64Γ—64 +``` + +## 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 + +**`mulhdx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulhdx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:297`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L297) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:864`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L864) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:445-453`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L445-L453) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulhdx => { + let ra = ctx.gpr[instr.ra()] as i64 as i128; + let rb = ctx.gpr[instr.rb()] as i64 as i128; + ctx.gpr[instr.rd()] = (ra.wrapping_mul(rb) >> 64) as u64; + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Returns the high 64 bits of a signed 64Γ—64 product.** Pair with [`mulldx`](mulldx.md) (which returns the low 64 bits) to obtain the full 128-bit product. Both must be issued separately; PowerPC has no fused multiply-double-wide instruction. +- **No `OE` bit.** This XO-form instruction has no overflow-enable variant β€” there is no "high half overflow" because the high half is always defined. +- **Xenia widens to `i128` natively.** [`interpreter.rs:275`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L275) does the multiply in 128 bits then extracts the high 64. The `i64 as i128` casts ensure signed extension on both sides. +- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:278`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L278) uses `as i64` directly. CR0 reflects the sign of the high half: `LT` if the product is negative, `GT` if positive and large enough to overflow into the high half, `EQ` if the product fits in 64 bits *signed* (so the high half is the sign-extension of the low half β€” but xenia's check uses raw signed-zero compare, which equates only when the high half is exactly zero, i.e. the product is in `[0, 2^63)`). +- **Use [`mulhdux`](mulhdux.md) for the unsigned high half.** The two instructions differ in whether the operands are sign- or zero-extended before the multiply. +- **Slow.** 64-bit multiply is multi-cycle on Xenon; combining `mulhd` with `mulld` for a full 128-bit product roughly doubles the cost. + +## Related Instructions + +- [`mulldx`](mulldx.md) β€” low 64 bits of the same signed product. +- [`mulhdux`](mulhdux.md) β€” high 64 bits, unsigned interpretation. +- [`mullwx`](mullwx.md), [`mulhwx`](mulhwx.md), [`mulhwux`](mulhwux.md) β€” 32-bit family. +- [`divdx`](divdx.md), [`divdux`](divdux.md) β€” 64-bit division. + +## IBM Reference + +- [AIX 7.3 β€” `mulhd` (Multiply High Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhd-multiply-high-double-word-instruction) diff --git a/tools/ppc-manual/alu/mulhwux.md b/tools/ppc-manual/alu/mulhwux.md new file mode 100644 index 00000000..4dce5e53 --- /dev/null +++ b/tools/ppc-manual/alu/mulhwux.md @@ -0,0 +1,126 @@ +# `mulhwux` β€” Multiply High Word Unsigned + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000016` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulhwu` | `mulhwux` | β€” | Multiply High Word Unsigned | +| `mulhwu.` | `mulhwux` | Rc=1 | Multiply High Word Unsigned | + +## Syntax + +```asm +mulhwu[Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mulhwux` β€” form `XO` + +- **Opcode word:** `0x7c000016` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `11` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mulhwux: read | Source GPR (`r0`–`r31`). | +| `RB` | mulhwux: read | Source GPR. | +| `RD` | mulhwux: write | Destination GPR. | +| `CR` | mulhwux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mulhwux` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mulhwux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RT <- high_32_of_unsigned_multiply((RA)[32:63], (RB)[32:63]) zero-extended to 64 +``` + +## 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 + +**`mulhwux`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulhwux"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:347`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L347) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:862`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L862) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:383-393`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L383-L393) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulhwux => { + // PPCBUG-020: 32-bit ABI CR0 view. + let ra = ctx.gpr[instr.ra()] as u32 as u64; + let rb = ctx.gpr[instr.rb()] as u32 as u64; + let result = ra.wrapping_mul(rb); + ctx.gpr[instr.rd()] = (result >> 32) & 0xFFFF_FFFF; + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Inputs are the low 32 bits, zero-extended.** `RA[32:63]` and `RB[32:63]` are treated as unsigned, widened to 64-bit `u64`, multiplied; the high 32 bits of the 64-bit product land in `RT[32:63]`. Xenia masks the high 32 bits of `RT` to zero ([`interpreter.rs:231`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L231)). +- **Pair with [`mullwx`](mullwx.md) for the full 64-bit unsigned product.** `mullw` returns the low 32 sign-extended; for unsigned use, pair `mulhwu` with `rlwinm` to mask the low half. Xbox 360 compilers commonly emit this combination. +- **No `OE` bit.** Same family rule. +- **`Rc=1` CR0 update.** Uses `as i32 as i64` ([`interpreter.rs:234`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L234)). Because the result is bounded by `0xFFFFFFFF` and stored only in the low 32 bits, this CR0 will report `LT` for any unsigned high half β‰₯ `0x80000000` β€” a known signed/unsigned interpretation pitfall when `Rc=1` is used with `mulhwu`. +- **Common idiom for multi-precision arithmetic.** `mulhwu` + `mullw` + `addc` chains build extended-precision multiplies entirely in 32-bit ops; useful for cryptographic code that targets the Xenon's 32-bit ABI. +- **Multi-cycle latency** like the rest of the multiply family. + +## Related Instructions + +- [`mullwx`](mullwx.md) β€” low 32 bits of the same product (sign-extended). +- [`mulhwx`](mulhwx.md) β€” signed high half. +- [`mulhdux`](mulhdux.md) β€” 64-bit unsigned high half. +- [`addcx`](addcx.md), [`addex`](addex.md) β€” used to chain 32-bit products into wider precision. + +## IBM Reference + +- [AIX 7.3 β€” `mulhwu` (Multiply High Word Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhwu-multiply-high-word-unsigned-instruction) diff --git a/tools/ppc-manual/alu/mulhwx.md b/tools/ppc-manual/alu/mulhwx.md new file mode 100644 index 00000000..7d0f40ff --- /dev/null +++ b/tools/ppc-manual/alu/mulhwx.md @@ -0,0 +1,126 @@ +# `mulhwx` β€” Multiply High Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000096` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulhw` | `mulhwx` | β€” | Multiply High Word | +| `mulhw.` | `mulhwx` | Rc=1 | Multiply High Word | + +## Syntax + +```asm +mulhw[Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mulhwx` β€” form `XO` + +- **Opcode word:** `0x7c000096` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `75` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mulhwx: read | Source GPR (`r0`–`r31`). | +| `RB` | mulhwx: read | Source GPR. | +| `RD` | mulhwx: write | Destination GPR. | +| `CR` | mulhwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mulhwx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mulhwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RT <- high_32_of_signed_multiply((RA)[32:63], (RB)[32:63]) sign-extended to 64 +``` + +## 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 + +**`mulhwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulhwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:326`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L326) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:865`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L865) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:372-382`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L372-L382) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulhwx => { + // PPCBUG-020: 32-bit ABI CR0 view. + let ra = ctx.gpr[instr.ra()] as i32 as i64; + let rb = ctx.gpr[instr.rb()] as i32 as i64; + let result = ra.wrapping_mul(rb); + ctx.gpr[instr.rd()] = ((result >> 32) as u32) as u64; + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Inputs are the low 32 bits, signed-extended.** `RA[32:63]` and `RB[32:63]` are sign-extended to 64-bit signed values, multiplied, and the *high* 32 bits of the 64-bit product are returned in `RT[32:63]`. The high 32 bits of `RT` are *implementation-defined* per spec but xenia masks them to zero ([`interpreter.rs:222`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L222) `& 0xFFFF_FFFF`). +- **Pair with [`mullwx`](mullwx.md) for the full 64-bit product.** Both can issue independently β€” no fused 32Γ—32β†’64 instruction. +- **No `OE` bit.** Like all `mulh*` instructions, no overflow flag is produced; the high half is by definition defined. +- **Xenia-rs quirk: high 32 bits zeroed.** Because spec says they're "undefined", legitimately matching either zero, sign-extension, or garbage. Xenia chooses zero, which differs from the literal Xenon behaviour (which sign-extends in some microarchitecture cases). For game code that doesn't read those bits, the difference is invisible. +- **`Rc=1` CR0 update.** [`interpreter.rs:225`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L225) uses `as i32 as i64` β€” operates on the truncated low 32 bits, which is correct for the *defined* portion of the result. +- **Multi-cycle latency.** Multiply is the slowest pipelined ALU op; `mulhw` shares the divider/multiplier unit. + +## Related Instructions + +- [`mullwx`](mullwx.md) β€” low 32 bits of a signed 32Γ—32 product. +- [`mulhwux`](mulhwux.md) β€” high 32 bits, unsigned interpretation. +- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md), [`mulldx`](mulldx.md) β€” 64-bit family. +- [`divwx`](divwx.md) β€” sometimes replaced by reciprocal-mul-then-shift in compiled code. + +## IBM Reference + +- [AIX 7.3 β€” `mulhw` (Multiply High Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhw-multiply-high-word-instruction) diff --git a/tools/ppc-manual/alu/mulldx.md b/tools/ppc-manual/alu/mulldx.md new file mode 100644 index 00000000..b4cffb8b --- /dev/null +++ b/tools/ppc-manual/alu/mulldx.md @@ -0,0 +1,130 @@ +# `mulldx` β€” Multiply Low Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0001d2` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulld` | `mulldx` | β€” | Multiply Low Doubleword | +| `mulldo` | `mulldx` | OE=1 | Multiply Low Doubleword | +| `mulld.` | `mulldx` | Rc=1 | Multiply Low Doubleword | +| `mulldo.` | `mulldx` | OE=1, Rc=1 | Multiply Low Doubleword | + +## Syntax + +```asm +mulld[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mulldx` β€” form `XO` + +- **Opcode word:** `0x7c0001d2` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `233` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mulldx: read | Source GPR (`r0`–`r31`). | +| `RB` | mulldx: read | Source GPR. | +| `RD` | mulldx: write | Destination GPR. | +| `CR` | mulldx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | mulldx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `mulldx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `mulldx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA) * (RB))[64:127] ; low 64 of signed 64Γ—64 +``` + +## 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 + +**`mulldx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulldx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:368`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L368) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:872`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L872) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:433-444`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L433-L444) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulldx => { + let ra = ctx.gpr[instr.ra()] as i64; + let rb = ctx.gpr[instr.rb()] as i64; + ctx.gpr[instr.rd()] = ra.wrapping_mul(rb) as u64; + if instr.oe() { + overflow::apply(ctx, overflow::mulld_ov(ra, rb)); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Returns the low 64 bits of a signed 64Γ—64 product.** Equivalent to `(int64_t)(RA * RB)` modulo `2^64`. Both operands are full 64-bit signed; no truncation on input. +- **High bits silently lost.** The high 64 bits of the true product are discarded; pair with [`mulhdx`](mulhdx.md) (signed) or [`mulhdux`](mulhdux.md) (unsigned) to recover them. +- **`OE=1` should set `XER[OV]`** when the 128-bit signed product cannot be represented in 64 bits β€” i.e. when `mulhd RA, RB` is not the sign-extension of `mulld RA, RB`. **Xenia-rs does not implement** `OE` ([`interpreter.rs:264`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L264) has no `oe()` branch). +- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:269`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L269) uses `as i64` β€” full 64-bit signed compare. One of the few non-truncating CR0 sites in xenia-rs; means `mulld.` gives spec-correct CR0 even when the result has non-zero high 32 bits. +- **Same instruction for signed and unsigned low halves.** Modular arithmetic is identical; only the high half (`mulhd` vs `mulhdu`) distinguishes the interpretations. +- **Multi-cycle latency** β€” slowest of the ALU pipelines after divide. + +## Related Instructions + +- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) β€” signed/unsigned high halves of the same multiply. +- [`mullwx`](mullwx.md) β€” 32-bit signed multiply (low 64). +- [`mulli`](mulli.md) β€” D-form: `RT ← (RA[32:63]) Γ— SIMM`. +- [`divdx`](divdx.md), [`divdux`](divdux.md) β€” 64-bit divide, often paired with `mulld` for remainders. + +## IBM Reference + +- [AIX 7.3 β€” `mulld` (Multiply Low Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulld-multiply-low-double-word-instruction) diff --git a/tools/ppc-manual/alu/mulli.md b/tools/ppc-manual/alu/mulli.md new file mode 100644 index 00000000..315ec277 --- /dev/null +++ b/tools/ppc-manual/alu/mulli.md @@ -0,0 +1,120 @@ +# `mulli` β€” Multiply Low Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x1c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mulli` | `mulli` | β€” | Multiply Low Immediate | + +## Syntax + +```asm +mulli [RD], [RA], [SIMM] +``` + +## Encoding + +### `mulli` β€” form `D` + +- **Opcode word:** `0x1c000000` +- **Primary opcode (bits 0–5):** `7` +- **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 | +| --- | --- | --- | +| `RA` | mulli: read | Source GPR (`r0`–`r31`). | +| `SIMM` | mulli: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | mulli: write | Destination GPR. | + +## Register Effects + +### `mulli` + +- **Reads (always):** `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RT <- ((RA) * EXTS(SIMM))[64:127] +``` + +## 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 + +**`mulli`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mulli"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:382`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L382) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:332`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L332) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:165-172`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L165-L172) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mulli => { + // PPCBUG-004: 32-bit ABI. Read RA as i32 (low 32, sign-extended for + // multiply), product fits in 32 bits per ISA (overflow wraps). + let ra = ctx.gpr[instr.ra()] as i32 as i64; + let imm = instr.simm16() as i64; + ctx.gpr[instr.rd()] = (ra.wrapping_mul(imm) as u32) as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **64-bit operand, sign-extended 16-bit immediate.** Xenia reads the full 64-bit `RA` as `i64` and the immediate as a sign-extended `i64` ([`interpreter.rs:80-81`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L80-L81)) β€” note this differs from the PPC pseudocode header which writes `(RA) * EXTS(SIMM)` as a 64-bit operation but other implementations sometimes treat it as 32Γ—32. On the Xenon (and in xenia-rs), it is genuinely 64-bit. +- **Returns the low 64 bits.** No high half is produced β€” equivalent to `(int64_t)RA * (int64_t)SIMM` modulo `2^64`. There is no `mulhi`-immediate instruction. +- **No `Rc`, no `OE`.** This D-form has no flag bits β€” strictly `RT ← RA * SIMM`. To check overflow, compare the result to `(int32_t)RA * SIMM` after the fact, or use [`mulldx`](mulldx.md) with `OE=1` after materialising the immediate. +- **Common compiler idiom.** `mulli` is heavily used for fixed-stride array indexing (`r3 *= sizeof_struct`) when the size is a small signed constant. +- **No carry.** `XER[CA]` is untouched. +- **Same multi-cycle latency** as `mullw` / `mulld`. Compilers strength-reduce `mulli rD, rA, 2^k` to a left shift and `mulli rD, rA, 3` to `add+shift` when the immediate has cheap structure. +- **Aliasing fine.** `mulli r3, r3, 5` rewrites in place. + +## Related Instructions + +- [`mullwx`](mullwx.md) β€” register-register low 32 (signed). +- [`mulldx`](mulldx.md) β€” register-register low 64 (signed). +- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) β€” high halves (no immediate variant). +- [`addi`](addi.md) β€” add immediate; sometimes substituted by compilers when the multiplier is `2^k+1` etc. +- [`slwx`](slwx.md), [`sldx`](sldx.md) β€” shifts often replace `mulli` for power-of-two multipliers. + +## IBM Reference + +- [AIX 7.3 β€” `mulli` (Multiply Low Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulli-multiply-low-immediate-instruction) diff --git a/tools/ppc-manual/alu/mullwx.md b/tools/ppc-manual/alu/mullwx.md new file mode 100644 index 00000000..0ade3cc4 --- /dev/null +++ b/tools/ppc-manual/alu/mullwx.md @@ -0,0 +1,146 @@ +# `mullwx` β€” Multiply Low Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0001d6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mullw` | `mullwx` | β€” | Multiply Low Word | +| `mullwo` | `mullwx` | OE=1 | Multiply Low Word | +| `mullw.` | `mullwx` | Rc=1 | Multiply Low Word | +| `mullwo.` | `mullwx` | OE=1, Rc=1 | Multiply Low Word | + +## Syntax + +```asm +mullw[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `mullwx` β€” form `XO` + +- **Opcode word:** `0x7c0001d6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `235` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | mullwx: read | Source GPR (`r0`–`r31`). | +| `RB` | mullwx: read | Source GPR. | +| `RD` | mullwx: write | Destination GPR. | +| `CR` | mullwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | mullwx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `mullwx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `mullwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ((RA)[32:63]) * ((RB)[32:63]) ; signed 32Γ—32 β†’ 64 +``` + +## 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 + +**`mullwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mullwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:390`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L390) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:874`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L874) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:357-371`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L357-L371) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mullwx => { + // PPCBUG-009: 32-bit ABI. Truncate product to u32 β€” overflow detection + // (mullw_ov) still uses the full i64 product to catch the overflow. + let ra = ctx.gpr[instr.ra()] as i32 as i64; + let rb = ctx.gpr[instr.rb()] as i32 as i64; + let product = ra.wrapping_mul(rb); + ctx.gpr[instr.rd()] = product as u32 as u64; + if instr.oe() { + overflow::apply(ctx, overflow::mullw_ov(product)); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +prod64 <- sign_extend_32_to_64((RA)[32:63]) *s sign_extend_32_to_64((RB)[32:63]) +RT <- prod64 ; 64-bit result +if OE then + XER[OV] <- (prod64 β‰  sign_extend_32_to_64(prod64[32:63])) ; set when product doesn't fit in 32 bits + XER[SO] <- XER[SO] | XER[OV] +if Rc then + CR0 <- signed_compare(RT, 0) || XER[SO] +``` + +## Special Cases & Edge Conditions + +- **Inputs are the low 32 bits.** `mullw` only looks at `RA[32:63]` and `RB[32:63]`; the high 32 bits of each source are ignored. This is a 32-bit Γ— 32-bit β†’ 64-bit signed multiply. For full 64-bit operands use [`mulldx`](mulldx.md). +- **Result is sign-extended to 64 bits.** The 64-bit product fits into a 64-bit GPR without loss. Subsequent 32-bit consumers see `RT[32:63]` (the low 32 bits of the product); use [`mulhwx`](mulhwx.md) for the signed high 32 bits or [`mulhwux`](mulhwux.md) for the unsigned high 32 bits, computed in parallel without this instruction. +- **`OE` overflow test is 32-bit.** `XER[OV]` is set iff the 64-bit signed product cannot be represented in 32 bits β€” equivalently, iff `RT[32] β‰  RT[33] = … = RT[63]` (sign bit disagrees with the next 32 bits). Xenia-rs does **not** implement this; `OE` on `mullwo` is a no-op in the interpreter. +- **Xenia-rs CR0 update bug footprint.** The interpreter computes CR0 from `result as i32 as i64` β€” the low 32 bits sign-extended. For a 32Γ—32β†’64 multiply the high 32 bits may be non-zero even when the low 32 bits are zero, so xenia's CR0 can differ from the spec's (which compares the full 64-bit product to zero). In practice this matters only for code that relies on `mullw.` to detect overflow via CR0 β€” extremely rare. +- **Latency.** On the Xenon, `mullw` has higher latency than add/sub; many hot inner loops avoid it by strength-reduction or shift-add chains. This is irrelevant for correctness but sometimes explains surprising instruction sequences in disassembly. + +## Related Instructions + +- [`mulhwx`](mulhwx.md) β€” signed high 32 bits of the same 32Γ—32 product. +- [`mulhwux`](mulhwux.md) β€” unsigned high 32 bits of a 32Γ—32 product. +- [`mulli`](mulli.md) β€” D-form: `RT ← (RA[32:63]) Γ— SIMM` (low 64 bits, signed). +- [`mulldx`](mulldx.md), [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) β€” 64-bit multiplies (low/high, signed/unsigned). +- [`divwx`](divwx.md), [`divwux`](divwux.md) β€” 32-bit signed / unsigned division. + +## IBM Reference + +- [AIX 7.3 β€” `mullw` (Multiply Low Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mullw-multiply-low-word-instruction) +- [AIX 7.3 β€” `mulli` (Multiply Low Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulli-multiply-low-immediate-instruction) diff --git a/tools/ppc-manual/alu/nandx.md b/tools/ppc-manual/alu/nandx.md new file mode 100644 index 00000000..668dbf5b --- /dev/null +++ b/tools/ppc-manual/alu/nandx.md @@ -0,0 +1,122 @@ +# `nandx` β€” NAND + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0003b8` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `nand` | `nandx` | β€” | NAND | +| `nand.` | `nandx` | Rc=1 | NAND | + +## Syntax + +```asm +nand[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `nandx` β€” form `X` + +- **Opcode word:** `0x7c0003b8` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `476` +- **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` | nandx: read | Source GPR (alias for RD in some stores). | +| `RB` | nandx: read | Source GPR. | +| `RA` | nandx: write | Source GPR (`r0`–`r31`). | +| `CR` | nandx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `nandx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `nandx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- ~((RS) & (RB)) +``` + +## 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 + +**`nandx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="nandx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:753`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L753) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:812`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L812) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:570-577`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L570-L577) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::nandx => { + // PPCBUG-030: same shape β€” operate in u32. + let rs32 = ctx.gpr[instr.rs()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = (!(rs32 & rb32)) as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ~(RS AND RB)`.** Bit-wise NAND. Since `nand RA, RS, RS = ~RS` (NOT-OR-self), the simplified mnemonic `not RA, RS` assembles to `nor RA, RS, RS` (note: NOR, not NAND). NAND-self is equivalent β€” both produce `~RS` β€” but the assembler prefers the NOR form by convention. +- **Operand convention is X-form** (`RA` destination, `RS`/`RB` sources). +- **64-bit operation** on Xenon; `~` operates on the full `u64`. +- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0` (signed compare to zero). +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:377`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L377) truncates with `as i32 as i64`. NAND results frequently have all-ones high bits when the low half AND is non-saturating, so the truncation can change CR0 semantics in subtle ways β€” call out as a quirk if reproducing CR-sensitive behaviour. +- **Idiom: NAND of two equal values produces NOT.** `nand. RA, RS, RS` ≑ `~RS` with CR0 update. Sometimes used by compilers when `not.` is unavailable in their tablegen. + +## Related Instructions + +- [`andx`](andx.md), [`andcx`](andcx.md) β€” base AND family. +- [`norx`](norx.md) β€” assembler-preferred form for "NOT" via `nor RA, RS, RS`. +- [`eqvx`](eqvx.md) β€” NXOR. +- [`orx`](orx.md), [`orcx`](orcx.md), [`xorx`](xorx.md) β€” full logical family. + +## IBM Reference + +- [AIX 7.3 β€” `nand` (NAND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-nand-instruction) diff --git a/tools/ppc-manual/alu/negx.md b/tools/ppc-manual/alu/negx.md new file mode 100644 index 00000000..1b0316d7 --- /dev/null +++ b/tools/ppc-manual/alu/negx.md @@ -0,0 +1,132 @@ +# `negx` β€” Negate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0000d0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `neg` | `negx` | β€” | Negate | +| `nego` | `negx` | OE=1 | Negate | +| `neg.` | `negx` | Rc=1 | Negate | +| `nego.` | `negx` | OE=1, Rc=1 | Negate | + +## Syntax + +```asm +neg[OE][Rc] [RD], [RA] +``` + +## Encoding + +### `negx` β€” form `XO` + +- **Opcode word:** `0x7c0000d0` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `104` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | negx: read | Source GPR (`r0`–`r31`). | +| `RD` | negx: write | Destination GPR. | +| `CR` | negx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | negx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `negx` + +- **Reads (always):** `RA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `negx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ~(RA) + 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 + +**`negx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="negx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L406) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:866`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L866) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:342-356`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L342-L356) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::negx => { + // PPCBUG-006: 32-bit ABI. `(!ra).wrapping_add(1)` on u64 always + // sets upper 32 bits β€” every neg poisoned the GPR. neg_ov also + // checks at 64-bit INT_MIN; should be 32-bit INT_MIN. + let ra32 = ctx.gpr[instr.ra()] as u32; + let result32 = (!ra32).wrapping_add(1); + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + overflow::apply(ctx, ra32 == 0x8000_0000); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Two's-complement negate.** `RT ← ~RA + 1`, equivalent to `0 βˆ’ RA`. A specialisation of [`subfx`](subfx.md) where `RB` is implicit zero. +- **`INT64_MIN` is its own negation.** `neg(0x8000000000000000) = 0x8000000000000000` β€” the only fixed point. `nego` should set `XER[OV]` in this case (it is the canonical signed-overflow trigger), but **xenia-rs does not implement `OE`** ([`interpreter.rs:201`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L201) has no `oe()` branch). +- **`RB` field unused.** Set to 0 by assemblers; ignored. +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:204`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L204). Important: `neg.` of a 64-bit value with high bits set will give a CR0 that doesn't match spec (which compares the full 64-bit `~RA + 1` to zero). +- **No carry produced.** Use [`subfic`](subficx.md) `RT, RA, 0` (`RT ← 0 βˆ’ RA` with carry) when you need the borrow. +- **Latency: single cycle.** Negate is the cheapest XO-form ALU operation (cheaper than `subf` despite being a special case, because there's no `RB` operand fetch). + +## Related Instructions + +- [`subfx`](subfx.md) β€” generalisation: `neg RT, RA` ≑ `subf RT, RA, 0` (but the latter requires materialising 0 in a register). +- [`subfic`](subficx.md) β€” `RT ← SIMM βˆ’ RA` with `XER[CA]`; `subfic RT, RA, 0` produces a borrow. +- [`addx`](addx.md), [`addmex`](addmex.md), [`addzex`](addzex.md) β€” for chained negation (multi-word two's complement). +- `not` (simplified) β€” bit-wise complement via `nor RA, RS, RS`; distinct from negate. + +## IBM Reference + +- [AIX 7.3 β€” `neg` (Negate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-neg-negate-instruction) diff --git a/tools/ppc-manual/alu/norx.md b/tools/ppc-manual/alu/norx.md new file mode 100644 index 00000000..20ebec25 --- /dev/null +++ b/tools/ppc-manual/alu/norx.md @@ -0,0 +1,124 @@ +# `norx` β€” NOR + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0000f8` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `nor` | `norx` | β€” | NOR | +| `nor.` | `norx` | Rc=1 | NOR | + +## Syntax + +```asm +nor[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `norx` β€” form `X` + +- **Opcode word:** `0x7c0000f8` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `124` +- **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` | norx: read | Source GPR (alias for RD in some stores). | +| `RB` | norx: read | Source GPR. | +| `RA` | norx: write | Source GPR (`r0`–`r31`). | +| `CR` | norx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `norx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `norx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- ~((RS) | (RB)) +``` + +## 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 + +**`norx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="norx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:763`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L763) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:777`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L777) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:562-569`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L562-L569) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::norx => { + // PPCBUG-029: `not` simplified mnemonic β€” every `not` poisoned the GPR. + let rs32 = ctx.gpr[instr.rs()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = (!(rs32 | rb32)) as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ~(RS OR RB)`.** Bit-wise NOR. The canonical idiom for one-instruction NOT: **`not RA, RS` is the simplified mnemonic for `nor RA, RS, RS`** β€” both source operands the same yields `~RS`. Almost every disassembly contains this pattern. +- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources). +- **64-bit operation** on Xenon; full 64-bit complement via `!` on `u64`. +- **No `OE` or `XER` side effects.** +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:372`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L372) uses `as i32 as i64`. Note that NOR almost always produces results with high bits set (since the OR rarely covers all 64 bits), so the truncated CR0 is usually `LT` (negative low half) where spec might give a different signed compare for the full 64-bit value. +- **`nor.` after a clear-low operation is a common pattern** for testing whether some high-bit mask is empty. + +## Related Instructions + +- [`orx`](orx.md), [`orcx`](orcx.md) β€” base OR family. +- [`andx`](andx.md), [`andcx`](andcx.md), [`nandx`](nandx.md) β€” AND family. +- [`eqvx`](eqvx.md) β€” NXOR. +- [`xorx`](xorx.md) β€” XOR. +- `not` (simplified mnemonic for `nor RA, RS, RS`). + +## IBM Reference + +- [AIX 7.3 β€” `nor` (NOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-nor-instruction) +- [AIX 7.3 β€” `not` (simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-not-complement-register) diff --git a/tools/ppc-manual/alu/orcx.md b/tools/ppc-manual/alu/orcx.md new file mode 100644 index 00000000..3ad2f964 --- /dev/null +++ b/tools/ppc-manual/alu/orcx.md @@ -0,0 +1,122 @@ +# `orcx` β€” OR with Complement + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000338` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `orc` | `orcx` | β€” | OR with Complement | +| `orc.` | `orcx` | Rc=1 | OR with Complement | + +## Syntax + +```asm +orc[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `orcx` β€” form `X` + +- **Opcode word:** `0x7c000338` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `412` +- **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` | orcx: read | Source GPR (alias for RD in some stores). | +| `RB` | orcx: read | Source GPR. | +| `RA` | orcx: write | Source GPR (`r0`–`r31`). | +| `CR` | orcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `orcx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `orcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- (RS) | ~(RB) +``` + +## 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 + +**`orcx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="orcx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:800`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L800) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:807`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L807) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:548-555`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L548-L555) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::orcx => { + // PPCBUG-028: same shape as andcx β€” operate in u32. + let rs32 = ctx.gpr[instr.rs()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = (rs32 | !rb32) as u64; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← RS OR (NOT RB)`.** The complement is on `RB`. Useful for setting bits *outside* a mask β€” e.g. `orc r3, r3, r4` sets in `r3` every bit *not* set in `r4`. +- **Idiom: `orc RA, RS, RS`** = `RS | ~RS` = `-1` (all ones). Cheaper-looking than constructing `βˆ’1` via `lis`+`ori`, but the assembler usually prefers `li RA, -1` or `eqv RA, RS, RS`. +- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources). +- **64-bit operation** on Xenon; xenia uses Rust's `!` on `u64` for full-width complement. +- **No `OE` or `XER` side effects.** +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:362`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L362). Because `~RB` typically has high bits set, `orc.` results often appear `LT` in the truncated CR0. + +## Related Instructions + +- [`orx`](orx.md) β€” base OR (no complement). +- [`andcx`](andcx.md) β€” AND-with-complement; sister `c` form. +- [`norx`](norx.md), [`nandx`](nandx.md) β€” full-result complements. +- [`eqvx`](eqvx.md) β€” `~(RS XOR RB)`. + +## IBM Reference + +- [AIX 7.3 β€” `orc` (OR with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-orc-complement-instruction) diff --git a/tools/ppc-manual/alu/ori.md b/tools/ppc-manual/alu/ori.md new file mode 100644 index 00000000..8cafd10a --- /dev/null +++ b/tools/ppc-manual/alu/ori.md @@ -0,0 +1,115 @@ +# `ori` β€” OR Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x60000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `ori` | `ori` | β€” | OR Immediate | + +## Syntax + +```asm +ori [RA], [RS], [UIMM] +``` + +## Encoding + +### `ori` β€” form `D` + +- **Opcode word:** `0x60000000` +- **Primary opcode (bits 0–5):** `24` +- **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 | +| --- | --- | --- | +| `RS` | ori: read | Source GPR (alias for RD in some stores). | +| `UIMM` | ori: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | ori: write | Source GPR (`r0`–`r31`). | + +## Register Effects + +### `ori` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RA <- (RS) | (0x0000 || UIMM) +``` + +## 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 + +**`ori`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="ori"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:810`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L810) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:347`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L347) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:512-515`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L512-L515) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::ori => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | (instr.uimm16() as u64); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No record form.** Unlike [`andix`](andix.md), `ori` does **not** update `CR0` β€” there is no `ori.`. If you need a CR update after OR-immediate, follow it with `cmpwi` or use [`orx`](orx.md) with `Rc=1`. +- **Immediate is zero-extended.** Only the low 16 bits of `RA` can be affected; the high 48 bits are passed through from `RS` unchanged. +- **`ori 0, 0, 0` is the canonical NOP.** All PowerPC NOPs assemble to this encoding (`0x60000000`). Disassemblers usually display this as `nop`. +- **Common idiom: build a 32-bit constant via `lis` + `ori`.** `lis r3, hi16; ori r3, r3, lo16` materialises any 32-bit immediate with no CR or XER disturbance. +- **64-bit operation in xenia-rs.** [`interpreter.rs:330`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L330) β€” full `u64` OR; high bits unchanged from `RS`. +- **`RA = 0` reads `r0`** (not the literal zero). Different from `addi`'s `RA0` semantics; `ori` uses the regular `RA` interpretation. + +## Related Instructions + +- [`oris`](oris.md) β€” same op with the immediate shifted left 16. +- [`orx`](orx.md) β€” register-register; supports `Rc=1`. +- [`xori`](xori.md), [`xoris`](xoris.md), [`andix`](andix.md), [`andisx`](andisx.md) β€” sister immediate logicals. +- `nop` (simplified) β€” `ori 0, 0, 0`. + +## IBM Reference + +- [AIX 7.3 β€” `ori` (OR Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ori-immediate-instruction) +- [AIX 7.3 β€” `nop` (simplified)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-nop-no-operation) diff --git a/tools/ppc-manual/alu/oris.md b/tools/ppc-manual/alu/oris.md new file mode 100644 index 00000000..b659c45d --- /dev/null +++ b/tools/ppc-manual/alu/oris.md @@ -0,0 +1,113 @@ +# `oris` β€” OR Immediate Shifted + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x64000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `oris` | `oris` | β€” | OR Immediate Shifted | + +## Syntax + +```asm +oris [RA], [RS], [UIMM] +``` + +## Encoding + +### `oris` β€” form `D` + +- **Opcode word:** `0x64000000` +- **Primary opcode (bits 0–5):** `25` +- **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 | +| --- | --- | --- | +| `RS` | oris: read | Source GPR (alias for RD in some stores). | +| `UIMM` | oris: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | oris: write | Source GPR (`r0`–`r31`). | + +## Register Effects + +### `oris` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RA <- (RS) | (UIMM || 0x0000) +``` + +## 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 + +**`oris`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="oris"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:821`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L821) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:348`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L348) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:516-519`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L516-L519) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::oris => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ((instr.uimm16() as u64) << 16); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No record form.** No `oris.` β€” same as [`ori`](ori.md). For CR0 updates use [`orx`](orx.md) with `Rc=1`. +- **Immediate is zero-extended *then* shifted left 16.** Only bits 32–47 of `RA` (in PowerISA bit numbering) can be affected; the high 32 bits and low 16 bits of `RA` come from `RS` unchanged. +- **Common pair with `lis`** to load a 32-bit constant: `lis r3, hi16` (= `addis r3, 0, hi16`), then `ori r3, r3, lo16`. **For unsigned constants whose low half has the high bit set**, `lis` followed by `ori` works cleanly because `ori` is zero-extending; using `addi` instead would sign-extend `lo16` and corrupt the constant. +- **64-bit operation in xenia-rs.** [`interpreter.rs:334`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L334). +- **No `XER`, no `CR` effect.** Pure register OR. +- **`RA = 0` reads `r0`** (not literal zero); see [`ori`](ori.md). + +## Related Instructions + +- [`ori`](ori.md) β€” companion (immediate not shifted). +- [`addis`](addis.md) β€” D-form add-immediate-shifted; pairs with `ori` to build constants. +- [`xoris`](xoris.md), [`andisx`](andisx.md) β€” sister immediate-shifted logicals. + +## IBM Reference + +- [AIX 7.3 β€” `oris` (OR Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-oris-immediate-shifted-instruction) diff --git a/tools/ppc-manual/alu/orx.md b/tools/ppc-manual/alu/orx.md new file mode 100644 index 00000000..6d15a85b --- /dev/null +++ b/tools/ppc-manual/alu/orx.md @@ -0,0 +1,122 @@ +# `orx` β€” OR + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000378` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `or` | `orx` | β€” | OR | +| `or.` | `orx` | Rc=1 | OR | + +## Syntax + +```asm +or[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `orx` β€” form `X` + +- **Opcode word:** `0x7c000378` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `444` +- **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` | orx: read | Source GPR (alias for RD in some stores). | +| `RB` | orx: read | Source GPR. | +| `RA` | orx: write | Source GPR (`r0`–`r31`). | +| `CR` | orx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `orx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `orx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- (RS) | (RB) +``` + +## 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 + +**`orx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="orx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:773`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L773) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:809`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L809) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:542-547`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L542-L547) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::orx => { + // PPCBUG-032+020: 32-bit ABI CR0 view. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ctx.gpr[instr.rb()]; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Canonical "register move".** `or RA, RS, RS` copies `RS` to `RA` β€” assemblers expose this as the simplified mnemonic **`mr RA, RS`** (move register). It is the single most common instruction in PPC disassembly after loads/stores. +- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources). +- **64-bit operation** on Xenon; full bitwise OR across 64 bits. +- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0`. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:357`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L357) truncates with `as i32 as i64`. For `or. RA, RS, RS` (i.e. `mr.`), this means CR0 reflects the low 32 bits of `RS` only β€” distinguishable from spec only when the high 32 bits are non-zero with all-zero low 32. +- **`or 26, 26, 26` is the Xbox 360 NOP variant** historically used to mark cache lines or signal the dispatch unit (alongside `nop` ≑ `ori 0,0,0`). Disassembly may show this β€” it has no architectural effect. + +## Related Instructions + +- [`orcx`](orcx.md) β€” OR with complement. +- [`norx`](norx.md) β€” NOR (and the basis for `not`). +- [`andx`](andx.md), [`xorx`](xorx.md), [`eqvx`](eqvx.md) β€” sister logicals. +- [`ori`](ori.md), [`oris`](oris.md) β€” D-form immediate variants (no record form). +- `mr` (simplified) β€” `or RA, RS, RS`. + +## IBM Reference + +- [AIX 7.3 β€” `or` (OR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-instruction-1) +- [AIX 7.3 β€” `mr` (Move Register, simplified)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-mr-move-register) diff --git a/tools/ppc-manual/alu/rldclx.md b/tools/ppc-manual/alu/rldclx.md new file mode 100644 index 00000000..067175b0 --- /dev/null +++ b/tools/ppc-manual/alu/rldclx.md @@ -0,0 +1,137 @@ +# `rldclx` β€” Rotate Left Doubleword then Clear Left + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MDS](../forms/MDS.md) Β· **Opcode:** `0x78000010` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldcl` | `rldclx` | β€” | Rotate Left Doubleword then Clear Left | +| `rldcl.` | `rldclx` | Rc=1 | Rotate Left Doubleword then Clear Left | + +## Syntax + +```asm +rldcl[Rc] [RA], [RS], [RB], [MB] +``` + +## Encoding + +### `rldclx` β€” form `MDS` + +- **Opcode word:** `0x78000010` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `RB` | source B GPR | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–30 | `XO` | extended opcode | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldclx: read | Source GPR (alias for RD in some stores). | +| `RB` | rldclx: read | Source GPR. | +| `MB` | rldclx: read | Mask begin bit. | +| `RA` | rldclx: write | Source GPR (`r0`–`r31`). | +| `CR` | rldclx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldclx` + +- **Reads (always):** `RS`, `RB`, `MB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldclx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldclx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldclx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:856`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L856) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:733`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L733) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:802-811`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L802-L811) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldclx => { + let rs = ctx.gpr[instr.rs()]; + let sh = ctx.gpr[instr.rb()] & 0x3F; + let mb = instr.mb_md(); + let rotated = rs.rotate_left(sh as u32); + let mask = rld_mask_left(mb); + ctx.gpr[instr.ra()] = rotated & mask; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL64(RS, RB[58:63]) & MASK(MB, 63)`.** Rotate `RS` left by `RB & 0x3F`, then *clear* bits to the left of `MB` β€” i.e. keep bits `MB..63`, force bits `0..MB-1` to zero. +- **Shift comes from a register.** Unlike [`rldiclx`](rldiclx.md), the rotate amount is dynamic. Only the low 6 bits of `RB` are used (`& 0x3F`); the upper 58 bits are silently ignored. +- **`MB` is a split 6-bit field.** Bit 5 of the encoded `mb/me` is *swapped* into bit position 5 (raw bit 30) β€” xenia decodes via `(instr.mb() << 1) | ((raw >> 1) & 1)` ([`interpreter.rs:587`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L587)). This MDS form is unusual; if you write a decoder, follow this exact bit assembly. +- **Mask generation.** `rld_mask_left(MB)` is `(1 << (64 - MB)) - 1` β€” i.e. clear bits `0..MB-1`, keep bits `MB..63`. When `MB = 0` the mask is all ones; when `MB = 63` only bit 63 survives. +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:592`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L592) uses `as i64` directly β€” no truncation. The rotate-and-mask family is one of the few xenia-rs instruction groups that already does the spec-correct 64-bit CR0 compare. +- **No `XER` effect.** +- **Use over [`rldiclx`](rldiclx.md)** when the shift amount is computed at runtime (e.g. via `cntlzd` for normalisation). + +## Related Instructions + +- [`rldcrx`](rldcrx.md) β€” sister: clear *right* instead of left. +- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md), [`rldicx`](rldicx.md) β€” immediate-shift variants. +- [`rldimix`](rldimix.md) β€” rotate and mask insert. +- [`rlwnmx`](rlwnmx.md), [`rlwinmx`](rlwinmx.md) β€” 32-bit cousins. +- [`sldx`](sldx.md), [`srdx`](srdx.md) β€” preferred for plain 64-bit shifts. + +## IBM Reference + +- [AIX 7.3 β€” `rldcl` (Rotate Left Doubleword then Clear Left)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldcl-rotate-left-double-word-then-clear-left-instruction) diff --git a/tools/ppc-manual/alu/rldcrx.md b/tools/ppc-manual/alu/rldcrx.md new file mode 100644 index 00000000..f0caf251 --- /dev/null +++ b/tools/ppc-manual/alu/rldcrx.md @@ -0,0 +1,136 @@ +# `rldcrx` β€” Rotate Left Doubleword then Clear Right + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MDS](../forms/MDS.md) Β· **Opcode:** `0x78000012` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldcr` | `rldcrx` | β€” | Rotate Left Doubleword then Clear Right | +| `rldcr.` | `rldcrx` | Rc=1 | Rotate Left Doubleword then Clear Right | + +## Syntax + +```asm +rldcr[Rc] [RA], [RS], [RB], [ME] +``` + +## Encoding + +### `rldcrx` β€” form `MDS` + +- **Opcode word:** `0x78000012` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `RB` | source B GPR | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–30 | `XO` | extended opcode | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldcrx: read | Source GPR (alias for RD in some stores). | +| `RB` | rldcrx: read | Source GPR. | +| `ME` | rldcrx: read | Mask end bit. | +| `RA` | rldcrx: write | Source GPR (`r0`–`r31`). | +| `CR` | rldcrx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldcrx` + +- **Reads (always):** `RS`, `RB`, `ME` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldcrx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldcrx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldcrx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:881`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L881) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:734`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L734) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:812-821`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L812-L821) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldcrx => { + let rs = ctx.gpr[instr.rs()]; + let sh = ctx.gpr[instr.rb()] & 0x3F; + let me = instr.mb_md(); + let rotated = rs.rotate_left(sh as u32); + let mask = rld_mask_right(me); + ctx.gpr[instr.ra()] = rotated & mask; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL64(RS, RB[58:63]) & MASK(0, ME)`.** Rotate `RS` left by `RB & 0x3F`, then *clear* bits to the right of `ME` β€” keep bits `0..ME`, force bits `ME+1..63` to zero. +- **Shift from register.** Same as [`rldclx`](rldclx.md): only the low 6 bits of `RB` count. +- **`ME` is a split 6-bit field.** Same swap-decoded layout as `MB` in `rldclx`: `(instr.mb() << 1) | ((raw >> 1) & 1)` ([`interpreter.rs:597`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L597)). Note that even though it represents `ME` here, xenia reads it from `instr.mb()` because the field shares the same encoding slot. +- **Mask generation.** `rld_mask_right(ME)` = `~((1 << (63 - ME)) - 1)` keeping bits `0..ME`. When `ME = 63` the mask is all ones; when `ME = 0` only bit 0 survives. +- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:602`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L602)). +- **No `XER` effect.** +- **Useful for left-shift with arbitrary discard.** `rldcr RA, RS, RB, 63 - n` is functionally close to a left-shift-and-mask sequence, with the rotate variant additionally allowing wrap-around. + +## Related Instructions + +- [`rldclx`](rldclx.md) β€” sister: clear *left* instead of right. +- [`rldicrx`](rldicrx.md) β€” immediate-shift form. +- [`rldicx`](rldicx.md), [`rldiclx`](rldiclx.md), [`rldimix`](rldimix.md) β€” full immediate rotate-and-mask family. +- [`sldx`](sldx.md) β€” plain 64-bit logical left shift (often a strength-reduced equivalent). + +## IBM Reference + +- [AIX 7.3 β€” `rldcr` (Rotate Left Doubleword then Clear Right)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldcr-rotate-left-double-word-then-clear-right-instruction) diff --git a/tools/ppc-manual/alu/rldiclx.md b/tools/ppc-manual/alu/rldiclx.md new file mode 100644 index 00000000..e688ecd9 --- /dev/null +++ b/tools/ppc-manual/alu/rldiclx.md @@ -0,0 +1,142 @@ +# `rldiclx` β€” Rotate Left Doubleword Immediate then Clear Left + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MD](../forms/MD.md) Β· **Opcode:** `0x78000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldicl` | `rldiclx` | β€” | Rotate Left Doubleword Immediate then Clear Left | +| `rldicl.` | `rldiclx` | Rc=1 | Rotate Left Doubleword Immediate then Clear Left | + +## Syntax + +```asm +rldicl[Rc] [RA], [RS], [SH], [MB] +``` + +## Encoding + +### `rldiclx` β€” form `MD` + +- **Opcode word:** `0x78000000` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–29 | `XO` | extended opcode | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldiclx: read | Source GPR (alias for RD in some stores). | +| `SH` | rldiclx: read | Shift amount. | +| `MB` | rldiclx: read | Mask begin bit. | +| `RA` | rldiclx: write | Source GPR (`r0`–`r31`). | +| `CR` | rldiclx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldiclx` + +- **Reads (always):** `RS`, `SH`, `MB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldiclx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldiclx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldiclx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:929`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L929) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:728`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L728) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:762-771`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L762-L771) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldiclx => { + let rs = ctx.gpr[instr.rs()]; + let sh = instr.sh64(); + let mb = instr.mb_md(); + let rotated = rs.rotate_left(sh); + let mask = rld_mask_left(mb); + ctx.gpr[instr.ra()] = rotated & mask; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL64(RS, SH) & MASK(MB, 63)`.** Rotate left by `SH`, then clear bits 0 through `MB-1`. The mask retains bits `MB..63`. +- **The Swiss-army knife of bit extraction.** Many assembler shorthands lower to this single instruction: + - `srdi RA, RS, n` ≑ `rldicl RA, RS, 64-n, n` β€” logical right shift by `n`. + - `clrldi RA, RS, n` ≑ `rldicl RA, RS, 0, n` β€” clear top `n` bits. + - `extrdi RA, RS, n, b` ≑ `rldicl RA, RS, b+n, 64-n` β€” extract `n` bits starting at `b`. +- **`SH` is 6 bits, immediate** (bits 16–20 + bit 30). Xenia uses `instr.sh64()` to assemble them. +- **`MB` is 6 bits, split-encoded** (`(instr.mb() << 1) | ((raw >> 1) & 1)`). +- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:551`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L551)). +- **No `XER` effect.** +- **Often appears in compiled disassembly** as a generic 64-bit shift. Decoding back to the simplified mnemonic above makes the intent obvious. + +## Related Instructions + +- [`rldicrx`](rldicrx.md) β€” clear-right counterpart (`MASK(0, ME)`). +- [`rldicx`](rldicx.md) β€” clear both ends. +- [`rldclx`](rldclx.md) β€” register-shift version. +- [`rlwinmx`](rlwinmx.md) β€” 32-bit cousin. +- `srdi`, `clrldi`, `extrdi` (simplified mnemonics). + +## IBM Reference + +- [AIX 7.3 β€” `rldicl` (Rotate Left Doubleword Immediate then Clear Left)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldicl-rotate-left-double-word-immediate-then-clear-left-instruction) +- [AIX 7.3 β€” Simplified shift/extract mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-rotate-shift) diff --git a/tools/ppc-manual/alu/rldicrx.md b/tools/ppc-manual/alu/rldicrx.md new file mode 100644 index 00000000..d416089e --- /dev/null +++ b/tools/ppc-manual/alu/rldicrx.md @@ -0,0 +1,142 @@ +# `rldicrx` β€” Rotate Left Doubleword Immediate then Clear Right + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MD](../forms/MD.md) Β· **Opcode:** `0x78000004` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldicr` | `rldicrx` | β€” | Rotate Left Doubleword Immediate then Clear Right | +| `rldicr.` | `rldicrx` | Rc=1 | Rotate Left Doubleword Immediate then Clear Right | + +## Syntax + +```asm +rldicr[Rc] [RA], [RS], [SH], [ME] +``` + +## Encoding + +### `rldicrx` β€” form `MD` + +- **Opcode word:** `0x78000004` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–29 | `XO` | extended opcode | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldicrx: read | Source GPR (alias for RD in some stores). | +| `SH` | rldicrx: read | Shift amount. | +| `ME` | rldicrx: read | Mask end bit. | +| `RA` | rldicrx: write | Source GPR (`r0`–`r31`). | +| `CR` | rldicrx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldicrx` + +- **Reads (always):** `RS`, `SH`, `ME` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldicrx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldicrx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldicrx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:957`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L957) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:729`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L729) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:772-781`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L772-L781) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldicrx => { + let rs = ctx.gpr[instr.rs()]; + let sh = instr.sh64(); + let me = instr.mb_md(); + let rotated = rs.rotate_left(sh); + let mask = rld_mask_right(me); + ctx.gpr[instr.ra()] = rotated & mask; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL64(RS, SH) & MASK(0, ME)`.** Rotate left by `SH`, then clear bits `ME+1..63`. +- **Common simplified mnemonics:** + - `sldi RA, RS, n` ≑ `rldicr RA, RS, n, 63-n` β€” logical left shift by `n`. + - `clrrdi RA, RS, n` ≑ `rldicr RA, RS, 0, 63-n` β€” clear low `n` bits. + - `extldi RA, RS, n, b` ≑ `rldicr RA, RS, b, n-1` β€” extract `n` bits from position `b` left-aligned. +- **`SH` is 6 bits, immediate.** Same `instr.sh64()` decode as the rest of the family. +- **`ME` is 6 bits, split-encoded.** Xenia stores it via `instr.mb()` β€” the field shares the slot with `MB` from sister instructions; the operation just interprets it as the right edge. +- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:561`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L561)). +- **No `XER` effect.** +- **Heavily emitted by 64-bit code generators** for left-shift-and-clear sequences. Recognising the simplified mnemonics aids disassembly. + +## Related Instructions + +- [`rldiclx`](rldiclx.md) β€” clear-left counterpart (`MASK(MB, 63)`). +- [`rldicx`](rldicx.md) β€” clear both ends. +- [`rldcrx`](rldcrx.md) β€” register-shift version. +- [`rldimix`](rldimix.md) β€” insert under mask. +- [`rlwinmx`](rlwinmx.md) β€” 32-bit cousin. +- `sldi`, `clrrdi`, `extldi` (simplified mnemonics). + +## IBM Reference + +- [AIX 7.3 β€” `rldicr` (Rotate Left Doubleword Immediate then Clear Right)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldicr-rotate-left-double-word-immediate-then-clear-right-instruction) diff --git a/tools/ppc-manual/alu/rldicx.md b/tools/ppc-manual/alu/rldicx.md new file mode 100644 index 00000000..da8dca47 --- /dev/null +++ b/tools/ppc-manual/alu/rldicx.md @@ -0,0 +1,138 @@ +# `rldicx` β€” Rotate Left Doubleword Immediate then Clear + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MD](../forms/MD.md) Β· **Opcode:** `0x78000008` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldic` | `rldicx` | β€” | Rotate Left Doubleword Immediate then Clear | +| `rldic.` | `rldicx` | Rc=1 | Rotate Left Doubleword Immediate then Clear | + +## Syntax + +```asm +rldic[Rc] [RA], [RS], [SH], [MB] +``` + +## Encoding + +### `rldicx` β€” form `MD` + +- **Opcode word:** `0x78000008` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–29 | `XO` | extended opcode | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldicx: read | Source GPR (alias for RD in some stores). | +| `SH` | rldicx: read | Shift amount. | +| `MB` | rldicx: read | Mask begin bit. | +| `RA` | rldicx: write | Source GPR (`r0`–`r31`). | +| `CR` | rldicx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldicx` + +- **Reads (always):** `RS`, `SH`, `MB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldicx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldicx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldicx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:906`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L906) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:730`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L730) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:782-791`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L782-L791) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldicx => { + let rs = ctx.gpr[instr.rs()]; + let sh = instr.sh64(); + let mb = instr.mb_md(); + let rotated = rs.rotate_left(sh); + let mask = rld_mask_left(mb) & rld_mask_right(63 - sh); + ctx.gpr[instr.ra()] = rotated & mask; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL64(RS, SH) & MASK(MB, 63 - SH)`.** Rotate `RS` left by `SH` bits, then mask off both ends: clear bits `0..MB-1` *and* clear bits `64-SH..63`. This is the "clear at both edges" variant β€” useful for inserting a field into an otherwise-zero register. +- **`SH` is a 6-bit immediate** spanning bits 16–20 plus bit 30 of the instruction word. Xenia uses the helper `instr.sh64()` ([`interpreter.rs:566`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L566)) to assemble the 6 bits. +- **`MB` is also 6-bit, split-encoded** like the rest of the `rld*` family: `(instr.mb() << 1) | ((raw >> 1) & 1)`. +- **Mask is computed as `MASK_LEFT(MB) AND MASK_RIGHT(63 - SH)`.** This produces the equivalent of "left-shift `RS` by `SH` then clear high bits above bit `MB`" β€” a common pattern when `MB ≀ 63 - SH`. +- **Equivalent to a logical shift when `MB = 0`.** `rldic RA, RS, SH, 0` ≑ `sldi RA, RS, SH` (an alias the assembler may prefer). +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:571`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L571) uses `as i64` directly. +- **No `XER` effect.** + +## Related Instructions + +- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) β€” clear-only-one-side variants. +- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) β€” register-shift forms. +- [`rldimix`](rldimix.md) β€” insert under mask. +- [`rlwinmx`](rlwinmx.md) β€” 32-bit cousin. +- `sldi` (simplified) β€” `rldic RA, RS, n, 0`; assemblers prefer this for plain logical left shifts. + +## IBM Reference + +- [AIX 7.3 β€” `rldic` (Rotate Left Doubleword Immediate then Clear)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldic-rotate-left-double-word-immediate-then-clear-instruction) diff --git a/tools/ppc-manual/alu/rldimix.md b/tools/ppc-manual/alu/rldimix.md new file mode 100644 index 00000000..678e091d --- /dev/null +++ b/tools/ppc-manual/alu/rldimix.md @@ -0,0 +1,136 @@ +# `rldimix` β€” Rotate Left Doubleword Immediate then Mask Insert + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [MD](../forms/MD.md) Β· **Opcode:** `0x7800000c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rldimi` | `rldimix` | β€” | Rotate Left Doubleword Immediate then Mask Insert | +| `rldimi.` | `rldimix` | Rc=1 | Rotate Left Doubleword Immediate then Mask Insert | + +## Syntax + +```asm +rldimi[Rc] [RA], [RS], [SH], [MB] +``` + +## Encoding + +### `rldimix` β€” form `MD` + +- **Opcode word:** `0x7800000c` +- **Primary opcode (bits 0–5):** `30` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–29 | `XO` | extended opcode | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rldimix: read | Source GPR (alias for RD in some stores). | +| `SH` | rldimix: read | Shift amount. | +| `MB` | rldimix: read | Mask begin bit. | +| `RA` | rldimix: write | Source GPR (`r0`–`r31`). | +| `CR` | rldimix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rldimix` + +- **Reads (always):** `RS`, `SH`, `MB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rldimix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rldimix`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rldimix"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:985`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L985) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:731`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L731) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:792-801`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L792-L801) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rldimix => { + let rs = ctx.gpr[instr.rs()]; + let sh = instr.sh64(); + let mb = instr.mb_md(); + let rotated = rs.rotate_left(sh); + let mask = rld_mask_left(mb) & rld_mask_right(63 - sh); + ctx.gpr[instr.ra()] = (rotated & mask) | (ctx.gpr[instr.ra()] & !mask); + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← (ROTL64(RS, SH) & MASK) | (RA & ~MASK)`.** *Reads* the prior `RA` so it can preserve the bits outside the mask β€” this is the only `rld*` instruction with `RA` as both source and destination. +- **Mask is `MASK_LEFT(MB) AND MASK_RIGHT(63 - SH)`** ([`interpreter.rs:578`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L578)) β€” same span as [`rldicx`](rldicx.md), but the un-masked region is preserved in the destination instead of being zeroed. +- **Use to insert a bit-field.** Common idiom: `rldimi RA, RS, b, mask_start` writes `RS`'s low (`64 - mask_start`) bits into `RA` starting at bit `b`. +- **`SH` and `MB` decoding** is identical to the rest of the family (6-bit `sh` via `instr.sh64()`, 6-bit `mb` via the swap layout). +- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly. +- **No `XER` effect.** +- **Compile-time pattern.** When you see `rldimi r3, r4, n, m`, the compiler is splicing a value into `r3`; recover the meaning by computing the mask `MASK(m, 63 - n)`. + +## Related Instructions + +- [`rldicx`](rldicx.md), [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) β€” same form family, but they zero outside the mask instead of preserving. +- [`rlwimix`](rlwimix.md) β€” 32-bit insert cousin. +- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) β€” register-shift forms (no insert variant). + +## IBM Reference + +- [AIX 7.3 β€” `rldimi` (Rotate Left Doubleword Immediate then Mask Insert)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldimi-rotate-left-double-word-immediate-then-mask-insert-instruction) diff --git a/tools/ppc-manual/alu/rlwimix.md b/tools/ppc-manual/alu/rlwimix.md new file mode 100644 index 00000000..70322621 --- /dev/null +++ b/tools/ppc-manual/alu/rlwimix.md @@ -0,0 +1,139 @@ +# `rlwimix` β€” Rotate Left Word Immediate then Mask Insert + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [M](../forms/M.md) Β· **Opcode:** `0x50000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rlwimi` | `rlwimix` | β€” | Rotate Left Word Immediate then Mask Insert | +| `rlwimi.` | `rlwimix` | Rc=1 | Rotate Left Word Immediate then Mask Insert | + +## Syntax + +```asm +rlwimi[Rc] [RA], [RS], [SH], [MB], [ME] +``` + +## Encoding + +### `rlwimix` β€” form `M` + +- **Opcode word:** `0x50000000` +- **Primary opcode (bits 0–5):** `20` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `SH/RB` | shift amount or source B | +| 21–25 | `MB` | mask begin | +| 26–30 | `ME` | mask end | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rlwimix: read | Source GPR (alias for RD in some stores). | +| `SH` | rlwimix: read | Shift amount. | +| `MB` | rlwimix: read | Mask begin bit. | +| `ME` | rlwimix: read | Mask end bit. | +| `RA` | rlwimix: write | Source GPR (`r0`–`r31`). | +| `CR` | rlwimix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rlwimix` + +- **Reads (always):** `RS`, `SH`, `MB`, `ME` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rlwimix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rlwimix`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rlwimix"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1010`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1010) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:344`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L344) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:737-749`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L737-L749) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rlwimix => { + let rs = ctx.gpr[instr.rs()] as u32; + let sh = instr.sh(); + let mb = instr.mb(); + let me = instr.me(); + let rotated = rs.rotate_left(sh); + let mask = rlw_mask(mb, me); + let ra = ctx.gpr[instr.ra()] as u32; + ctx.gpr[instr.ra()] = ((rotated & mask) | (ra & !mask)) as u64; + // PPCBUG-025: 32-bit ABI CR0 view. + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← (ROTL32(RS[32:63], SH) & MASK) | (RA[32:63] & ~MASK)`.** Reads the low 32 bits of `RS`, rotates them, then *inserts* under the mask back into the low 32 bits of `RA`. The high 32 bits of `RA` are *implementation-defined* per spec; **xenia-rs zeroes them** (the `as u32` cast at [`interpreter.rs:529`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L529) discards them on read, then `as u64` zero-extends on write). +- **Mask follows the standard `MB..ME` PPC convention.** Both `MB` and `ME` are 5-bit fields; the mask is contiguous when `MB <= ME`, and *wraps* around (a "donut" mask: bits `MB..31` and `0..ME`) when `MB > ME`. Xenia's `rlw_mask(mb, me)` helper handles both cases. +- **`SH` is 5 bits.** Rotate amount is `SH mod 32`; values `β‰₯ 32` are not encodable in this M-form. +- **Used for bit-field insertion** (`insrwi RA, RS, n, b` ≑ `rlwimi RA, RS, 32-(b+n), b, b+n-1`). Compilers emit `rlwimi` extensively for struct-bitfield writes. +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:531`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L531). Since the high 32 bits of the result are zero, this matches spec's compare on the (defined) low half β€” but if a real Xenon left high bits non-zero, behaviour would diverge. +- **No `XER` effect.** + +## Related Instructions + +- [`rlwinmx`](rlwinmx.md) β€” same mask family but zeroes outside (no read-modify-write). +- [`rlwnmx`](rlwnmx.md) β€” register-shift variant of `rlwinm`. +- [`rldimix`](rldimix.md) β€” 64-bit insert cousin. +- `insrwi`, `inslwi` (simplified mnemonics for common insert patterns). + +## IBM Reference + +- [AIX 7.3 β€” `rlwimi` (Rotate Left Word Immediate then Mask Insert)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwimi-rotate-left-word-immediate-then-mask-insert-instruction) diff --git a/tools/ppc-manual/alu/rlwinmx.md b/tools/ppc-manual/alu/rlwinmx.md new file mode 100644 index 00000000..da4bbcff --- /dev/null +++ b/tools/ppc-manual/alu/rlwinmx.md @@ -0,0 +1,145 @@ +# `rlwinmx` β€” Rotate Left Word Immediate then AND with Mask + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [M](../forms/M.md) Β· **Opcode:** `0x54000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rlwinm` | `rlwinmx` | β€” | Rotate Left Word Immediate then AND with Mask | +| `rlwinm.` | `rlwinmx` | Rc=1 | Rotate Left Word Immediate then AND with Mask | + +## Syntax + +```asm +rlwinm[Rc] [RA], [RS], [SH], [MB], [ME] +``` + +## Encoding + +### `rlwinmx` β€” form `M` + +- **Opcode word:** `0x54000000` +- **Primary opcode (bits 0–5):** `21` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `SH/RB` | shift amount or source B | +| 21–25 | `MB` | mask begin | +| 26–30 | `ME` | mask end | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rlwinmx: read | Source GPR (alias for RD in some stores). | +| `SH` | rlwinmx: read | Shift amount. | +| `MB` | rlwinmx: read | Mask begin bit. | +| `ME` | rlwinmx: read | Mask end bit. | +| `RA` | rlwinmx: write | Source GPR (`r0`–`r31`). | +| `CR` | rlwinmx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rlwinmx` + +- **Reads (always):** `RS`, `SH`, `MB`, `ME` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rlwinmx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rlwinmx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rlwinmx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1046`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1046) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:345`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L345) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:725-736`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L725-L736) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rlwinmx => { + let rs = ctx.gpr[instr.rs()] as u32; + let sh = instr.sh(); + let mb = instr.mb(); + let me = instr.me(); + let rotated = rs.rotate_left(sh); + let mask = rlw_mask(mb, me); + ctx.gpr[instr.ra()] = (rotated & mask) as u64; + // PPCBUG-024: 32-bit ABI CR0 view. + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL32(RS[32:63], SH) & MASK(MB, ME)`.** Take the low 32 bits of `RS`, rotate them left by `SH`, AND with a 32-bit mask. The high 32 bits of `RA` are zero (`as u64` zero-extension on the result). +- **The 32-bit Swiss army knife.** Most 32-bit shift/extract simplified mnemonics expand to this single instruction: + - `slwi RA, RS, n` ≑ `rlwinm RA, RS, n, 0, 31-n` β€” logical left shift. + - `srwi RA, RS, n` ≑ `rlwinm RA, RS, 32-n, n, 31` β€” logical right shift. + - `clrlwi RA, RS, n` ≑ `rlwinm RA, RS, 0, n, 31` β€” clear high `n` bits. + - `clrrwi RA, RS, n` ≑ `rlwinm RA, RS, 0, 0, 31-n` β€” clear low `n` bits. + - `extlwi`, `extrwi`, `clrlslwi` β€” full mnemonic family in PowerISA appendix. +- **Mask convention `MB..ME`** is contiguous when `MB ≀ ME`. When `MB > ME`, the mask is the *complement* of bits `ME+1..MB-1` β€” a donut/wrap mask. Xenia's `rlw_mask` handles both. +- **`SH` is 5 bits**, rotate amount `0..31`. +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:518`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L518). Since the result fits in 32 bits, the truncation matches spec exactly. +- **No `XER` effect.** + +## Related Instructions + +- [`rlwimix`](rlwimix.md) β€” same mask family with read-modify-write insert. +- [`rlwnmx`](rlwnmx.md) β€” register-shift version. +- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) β€” 64-bit cousins. +- [`slwx`](slwx.md), [`srwx`](srwx.md), [`srawix`](srawix.md) β€” straight 32-bit shift instructions. +- `slwi`, `srwi`, `clrlwi`, `clrrwi`, `extlwi`, `extrwi` (simplified mnemonics). + +## IBM Reference + +- [AIX 7.3 β€” `rlwinm` (Rotate Left Word Immediate then AND with Mask)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwinm-rotate-left-word-immediate-then-mask-instruction) +- [AIX 7.3 β€” Rotate / shift simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-rotate-shift) diff --git a/tools/ppc-manual/alu/rlwnmx.md b/tools/ppc-manual/alu/rlwnmx.md new file mode 100644 index 00000000..e5c96ff7 --- /dev/null +++ b/tools/ppc-manual/alu/rlwnmx.md @@ -0,0 +1,139 @@ +# `rlwnmx` β€” Rotate Left Word then AND with Mask + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [M](../forms/M.md) Β· **Opcode:** `0x5c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `rlwnm` | `rlwnmx` | β€” | Rotate Left Word then AND with Mask | +| `rlwnm.` | `rlwnmx` | Rc=1 | Rotate Left Word then AND with Mask | + +## Syntax + +```asm +rlwnm[Rc] [RA], [RS], [RB], [MB], [ME] +``` + +## Encoding + +### `rlwnmx` β€” form `M` + +- **Opcode word:** `0x5c000000` +- **Primary opcode (bits 0–5):** `23` +- **Extended opcode:** β€” +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `SH/RB` | shift amount or source B | +| 21–25 | `MB` | mask begin | +| 26–30 | `ME` | mask end | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | rlwnmx: read | Source GPR (alias for RD in some stores). | +| `RB` | rlwnmx: read | Source GPR. | +| `MB` | rlwnmx: read | Mask begin bit. | +| `ME` | rlwnmx: read | Mask end bit. | +| `RA` | rlwnmx: write | Source GPR (`r0`–`r31`). | +| `CR` | rlwnmx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `rlwnmx` + +- **Reads (always):** `RS`, `RB`, `MB`, `ME` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `rlwnmx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`rlwnmx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="rlwnmx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1101`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1101) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:346`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L346) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:750-761`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L750-L761) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::rlwnmx => { + let rs = ctx.gpr[instr.rs()] as u32; + let sh = ctx.gpr[instr.rb()] as u32 & 0x1F; + let mb = instr.mb(); + let me = instr.me(); + let rotated = rs.rotate_left(sh); + let mask = rlw_mask(mb, me); + ctx.gpr[instr.ra()] = (rotated & mask) as u64; + // PPCBUG-026: 32-bit ABI CR0 view. + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ROTL32(RS[32:63], RB[59:63]) & MASK(MB, ME)`.** Identical to [`rlwinmx`](rlwinmx.md) except the rotate amount comes from the low 5 bits of `RB`. Xenia masks with `& 0x1F` ([`interpreter.rs:535`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L535)). +- **Use over `rlwinm`** when the rotate amount is dynamic (e.g. computed from a `cntlzw` for normalisation, or read from a parameter). +- **Mask is still 5+5 bits immediate** β€” `MB` and `ME` are not register-sourced; only the shift is. This is the M-form's quirk: only one of (`SH`, `MB`, `ME`) is variable across the family. +- **Donut masks supported.** `MB > ME` produces a wraparound mask, same as `rlwinm`. +- **High 32 bits of `RA` are zero** (32-bit operation, then `as u64` zero-extends). +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:540`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L540) β€” harmless because the result already fits in 32 bits. +- **No `XER` effect.** + +## Related Instructions + +- [`rlwinmx`](rlwinmx.md) β€” same op, immediate shift. +- [`rlwimix`](rlwimix.md) β€” insert variant (no register-shift form exists for insert). +- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) β€” 64-bit register-shift cousins. +- [`slwx`](slwx.md), [`srwx`](srwx.md) β€” straight 32-bit shifts. + +## IBM Reference + +- [AIX 7.3 β€” `rlwnm` (Rotate Left Word then AND with Mask)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwnm-rotate-left-word-then-mask-instruction) diff --git a/tools/ppc-manual/alu/sldx.md b/tools/ppc-manual/alu/sldx.md new file mode 100644 index 00000000..c77837c4 --- /dev/null +++ b/tools/ppc-manual/alu/sldx.md @@ -0,0 +1,124 @@ +# `sldx` β€” Shift Left Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000036` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `sld` | `sldx` | β€” | Shift Left Doubleword | +| `sld.` | `sldx` | Rc=1 | Shift Left Doubleword | + +## Syntax + +```asm +sld[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `sldx` β€” form `X` + +- **Opcode word:** `0x7c000036` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `27` +- **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` | sldx: read | Source GPR (alias for RD in some stores). | +| `RB` | sldx: read | Source GPR. | +| `RA` | sldx: write | Source GPR (`r0`–`r31`). | +| `CR` | sldx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `sldx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `sldx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- (RB)[57:63] +RA <- ((RS) << n) if n < 64 else 0 +``` + +## 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 + +**`sldx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="sldx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1122`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1122) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:759`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L759) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:676-683`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L676-L683) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sldx => { + let sh = ctx.gpr[instr.rb()] & 0x7F; + ctx.gpr[instr.ra()] = if sh < 64 { + ctx.gpr[instr.rs()] << sh + } else { 0 }; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **64-bit logical left shift.** `RA ← RS << (RB & 0x7F)` if the shift count is `< 64`, otherwise `RA = 0`. Bits shifted past bit 0 are discarded. +- **Critical: shift count is *7 bits*, not 6.** PowerISA reads `RB[57:63]` (7 bits, `0..127`). Counts in `[64, 127]` produce zero, *not* `RS << (count mod 64)`. Xenia respects this with `& 0x7F` and an explicit `if sh < 64` check ([`interpreter.rs:464`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L464)). C semantics' undefined behaviour for `<<` with a count `>= width` is a spec-violation source if you naΓ―vely translate. +- **No `XER[CA]` produced** by left shifts. Logical right [`srdx`](srdx.md) and arithmetic right [`sradx`](sradx.md) differ here β€” arithmetic right *does* set `CA`. +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:467`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L467) uses `as i64` directly. CR0 reflects the sign of the full 64-bit shifted value (which is 0 for shifts β‰₯ 64, otherwise either `LT`/`GT`/`EQ`). +- **Strength-reduced from `mulli` for power-of-two multipliers.** +- **No `OE` bit.** + +## Related Instructions + +- [`slwx`](slwx.md) β€” 32-bit logical left shift. +- [`srdx`](srdx.md) β€” 64-bit logical right shift. +- [`sradx`](sradx.md), [`sradix`](sradix.md) β€” 64-bit arithmetic right shifts. +- [`rldicrx`](rldicrx.md) β€” `sldi` simplified mnemonic uses this. +- [`mulli`](mulli.md) β€” for non-power-of-two multipliers. + +## IBM Reference + +- [AIX 7.3 β€” `sld` (Shift Left Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sld-shift-left-double-word-instruction) diff --git a/tools/ppc-manual/alu/slwx.md b/tools/ppc-manual/alu/slwx.md new file mode 100644 index 00000000..287d9b7d --- /dev/null +++ b/tools/ppc-manual/alu/slwx.md @@ -0,0 +1,124 @@ +# `slwx` β€” Shift Left Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000030` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `slw` | `slwx` | β€” | Shift Left Word | +| `slw.` | `slwx` | Rc=1 | Shift Left Word | + +## Syntax + +```asm +slw[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `slwx` β€” form `X` + +- **Opcode word:** `0x7c000030` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `24` +- **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` | slwx: read | Source GPR (alias for RD in some stores). | +| `RB` | slwx: read | Source GPR. | +| `RA` | slwx: write | Source GPR (`r0`–`r31`). | +| `CR` | slwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `slwx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `slwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- (RB)[58:63] +RA <- ((RS) << n) & 0x0000_0000_FFFF_FFFF if n < 32 else 0 +``` + +## 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 + +**`slwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="slwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1141`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1141) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:757`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L757) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:622-631`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L622-L631) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::slwx => { + // PPCBUG-044: 32-bit ABI CR0 view. A result with bit 31 set + // (e.g. 0x80000000) is negative in i32 view but positive in i64. + let sh = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = if sh < 32 { + ((ctx.gpr[instr.rs()] as u32) << sh) as u64 + } else { 0 }; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit logical left shift, zero-extended to 64.** `RA ← (RS[32:63] << (RB & 0x3F))[32:63]` if `(RB & 0x3F) < 32`, else `RA = 0`. The high 32 bits of `RA` are always zero (zero-extension of the 32-bit result). +- **Shift count is 6 bits**, `RB[58:63]` β€” not 7 like [`sldx`](sldx.md). Counts in `[32, 63]` produce zero. Xenia reads the full register but the explicit `if sh < 32` guard in [`interpreter.rs:417`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L417) prevents Rust UB. +- **Spec quirk worth flagging:** xenia reads `ctx.gpr[instr.rb()] as u32`, which uses the low 32 bits of `RB`, not the spec's `RB & 0x3F`. For ordinary code these agree (counts ≀ 63), but a maliciously high `RB` could in principle differ. In practice this is a non-issue. +- **No `XER[CA]` for left shifts.** +- **`Rc=1` CR0 update truncates to 32 bits** ([`interpreter.rs:420`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L420)). Since the high 32 bits are zero, this matches spec exactly. +- **No `OE` bit.** + +## Related Instructions + +- [`sldx`](sldx.md) β€” 64-bit logical left shift. +- [`srwx`](srwx.md), [`srawx`](srawx.md), [`srawix`](srawix.md) β€” 32-bit right shifts. +- [`rlwinmx`](rlwinmx.md) β€” `slwi` simplified mnemonic uses this. + +## IBM Reference + +- [AIX 7.3 β€” `slw` (Shift Left Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-slw-shift-left-word-instruction) diff --git a/tools/ppc-manual/alu/sradix.md b/tools/ppc-manual/alu/sradix.md new file mode 100644 index 00000000..a6a336e5 --- /dev/null +++ b/tools/ppc-manual/alu/sradix.md @@ -0,0 +1,132 @@ +# `sradix` β€” Shift Right Algebraic Doubleword Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XS](../forms/XS.md) Β· **Opcode:** `0x7c000674` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `sradi` | `sradix` | β€” | Shift Right Algebraic Doubleword Immediate | +| `sradi.` | `sradix` | Rc=1 | Shift Right Algebraic Doubleword Immediate | + +## Syntax + +```asm +sradi[Rc] [RA], [RS], [SH] +``` + +## Encoding + +### `sradix` β€” form `XS` + +- **Opcode word:** `0x7c000674` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `826` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–29 | `XO` | extended opcode (9 bits) | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | sradix: read | Source GPR (alias for RD in some stores). | +| `SH` | sradix: read | Shift amount. | +| `RA` | sradix: write | Source GPR (`r0`–`r31`). | +| `CR` | sradix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CA` | sradix: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `sradix` + +- **Reads (always):** `RS`, `SH` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `sradix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RA <- ((RS) >>a SH) sign-extended +CA <- (RS signed < 0) && any_bit_shifted_out +``` + +## 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 + +**`sradix`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="sradix"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1230`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1230) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:743`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L743) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:709-722`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L709-L722) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sradix => { + let rs = ctx.gpr[instr.rs()] as i64; + let sh = instr.sh64(); + if sh == 0 { + ctx.gpr[instr.ra()] = rs as u64; + ctx.xer_ca = 0; + } else { + let result = rs >> sh; + ctx.xer_ca = if rs < 0 && (rs as u64) << (64 - sh) != 0 { 1 } else { 0 }; + ctx.gpr[instr.ra()] = result as u64; + } + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← (i64)RS >> SH`**, with `XER[CA]` set when `RS` is negative AND any one-bit was shifted out. +- **`SH` is 6 bits.** Encoded in bits 16–20 (`sh`) plus bit 30 (`sh5`); xenia uses `instr.sh64()` to assemble the 6 bits ([`interpreter.rs:496`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L496)). Range `0..63`. +- **`SH = 0`** is a no-op (sign-extends `RS` to itself), and explicitly clears `XER[CA]` ([`interpreter.rs:498`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L498)). This matches spec. +- **Spec divergence: 6-bit immediate, no saturation arm.** Unlike [`sradx`](sradx.md) which has a 7-bit register count and saturates at `β‰₯ 64`, `sradi` always uses a count `< 64` so no special saturation case is needed. +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:506`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L506). +- **Idiom: `sradi rA, rS, n; addze rA, rA`** β€” signed integer divide by `2^n` rounded toward zero (the textbook PPC sequence). +- **No `OE` bit.** + +## Related Instructions + +- [`sradx`](sradx.md) β€” register-shift form. +- [`srawix`](srawix.md), [`srawx`](srawx.md) β€” 32-bit arithmetic right. +- [`addzex`](addzex.md) β€” pair for signed-divide-rounding. +- [`rldiclx`](rldiclx.md) β€” when arithmetic semantics not required (logical shift), `srdi` simplified mnemonic. + +## IBM Reference + +- [AIX 7.3 β€” `sradi` (Shift Right Algebraic Doubleword Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sradi-shift-right-algebraic-double-word-immediate-instruction) diff --git a/tools/ppc-manual/alu/sradx.md b/tools/ppc-manual/alu/sradx.md new file mode 100644 index 00000000..07cf0c92 --- /dev/null +++ b/tools/ppc-manual/alu/sradx.md @@ -0,0 +1,136 @@ +# `sradx` β€” Shift Right Algebraic Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000634` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `srad` | `sradx` | β€” | Shift Right Algebraic Doubleword | +| `srad.` | `sradx` | Rc=1 | Shift Right Algebraic Doubleword | + +## Syntax + +```asm +srad[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `sradx` β€” form `X` + +- **Opcode word:** `0x7c000634` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `794` +- **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` | sradx: read | Source GPR (alias for RD in some stores). | +| `RB` | sradx: read | Source GPR. | +| `RA` | sradx: write | Source GPR (`r0`–`r31`). | +| `CR` | sradx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CA` | sradx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `sradx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `sradx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +n <- (RB)[57:63] +RA <- ((RS) >>a n) sign-extended if n < 64 +CA <- (RS signed < 0) && any_bit_shifted_out +``` + +## 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 + +**`sradx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="sradx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1201`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1201) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:841`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L841) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:692-708`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L692-L708) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sradx => { + let rs = ctx.gpr[instr.rs()] as i64; + let sh = ctx.gpr[instr.rb()] & 0x7F; + if sh == 0 { + ctx.gpr[instr.ra()] = rs as u64; + ctx.xer_ca = 0; + } else if sh < 64 { + let result = rs >> sh; + ctx.xer_ca = if rs < 0 && (rs as u64) << (64 - sh) != 0 { 1 } else { 0 }; + ctx.gpr[instr.ra()] = result as u64; + } else { + ctx.gpr[instr.ra()] = if rs < 0 { u64::MAX } else { 0 }; + ctx.xer_ca = if rs < 0 { 1 } else { 0 }; + } + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **64-bit arithmetic (sign-propagating) right shift.** `RA ← (i64)RS >> (RB & 0x7F)` with bits shifted in matching the sign bit of `RS`. Counts β‰₯ 64 saturate: `RA` becomes all-ones if `RS < 0`, else zero. +- **`XER[CA]` is the "lost-ones" indicator.** `CA = 1` iff `RS` is negative AND any of the bits shifted out were `1`. This makes `srad` / `sradi` the standard idiom for "divide negative integer by power of 2 with round-toward-zero" β€” followed by `addze` to compensate when `CA = 1`. +- **Three branches in xenia.** `sh == 0` (no shift, `CA=0`), `sh < 64` (normal shift, `CA` per spec), and `sh β‰₯ 64` (saturate to `0` or `βˆ’1`, `CA` reflects sign). The `(rs as u64) << (64 - sh) != 0` check at [`interpreter.rs:486`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L486) extracts whether any non-zero bit was shifted out. +- **Shift count is 7 bits.** Same as [`sldx`](sldx.md): `RB[57:63]`. +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:489`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L489). +- **No `OE` bit.** +- **Used by signed-divide-by-power-of-2 idiom:** `srad rA, rS, n; addze rA, rA` produces `rS / 2^n` with truncation toward zero rather than toward `-∞`. + +## Related Instructions + +- [`sradix`](sradix.md) β€” immediate-shift form (`SH` 6-bit immediate). +- [`srawx`](srawx.md), [`srawix`](srawix.md) β€” 32-bit arithmetic right shifts. +- [`srdx`](srdx.md) β€” 64-bit *logical* right shift (no `XER[CA]`). +- [`addzex`](addzex.md) β€” companion for the divide-rounding idiom. +- [`sldx`](sldx.md) β€” left shift. + +## IBM Reference + +- [AIX 7.3 β€” `srad` (Shift Right Algebraic Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srad-shift-right-algebraic-double-word-instruction) diff --git a/tools/ppc-manual/alu/srawix.md b/tools/ppc-manual/alu/srawix.md new file mode 100644 index 00000000..baae2ab2 --- /dev/null +++ b/tools/ppc-manual/alu/srawix.md @@ -0,0 +1,132 @@ +# `srawix` β€” Shift Right Algebraic Word Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000670` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `srawi` | `srawix` | β€” | Shift Right Algebraic Word Immediate | +| `srawi.` | `srawix` | Rc=1 | Shift Right Algebraic Word Immediate | + +## Syntax + +```asm +srawi[Rc] [RA], [RS], [SH] +``` + +## Encoding + +### `srawix` β€” form `X` + +- **Opcode word:** `0x7c000670` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `824` +- **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` | srawix: read | Source GPR (alias for RD in some stores). | +| `SH` | srawix: read | Shift amount. | +| `RA` | srawix: write | Source GPR (`r0`–`r31`). | +| `CR` | srawix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CA` | srawix: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `srawix` + +- **Reads (always):** `RS`, `SH` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `srawix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +RA <- ((RS)[32:63] >>a SH) sign-extended +CA <- (RS[32] signed) && any_low_bit_shifted_out +``` + +## 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 + +**`srawix`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="srawix"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1291`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1291) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:843`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L843) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:661-675`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L661-L675) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::srawix => { + // PPCBUG-042+043 coupled: same shape as srawx for the sh-immediate form. + let rs = ctx.gpr[instr.rs()] as i32; + let sh = instr.sh(); + if sh == 0 { + ctx.gpr[instr.ra()] = rs as u32 as u64; + ctx.xer_ca = 0; + } else { + let result = rs >> sh; + ctx.xer_ca = if rs < 0 && (rs as u32) << (32 - sh) != 0 { 1 } else { 0 }; + ctx.gpr[instr.ra()] = result as u32 as u64; + } + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← ((i32)RS >> SH) sign-extended`** with `XER[CA]` set when `RS` is negative AND any low bit was shifted out. +- **`SH` is 5 bits** (immediate, range `0..31`). Unlike [`srawx`](srawx.md), there is no saturation case because the count cannot exceed 31. Xenia reads it via `instr.sh()`. +- **`SH = 0`** sign-extends `RS[32:63]` to 64 bits and clears `CA`. This is *not* a no-op when `RS`'s high 32 bits differ from the sign extension of bit 32. +- **Common idiom: `srawi rA, rS, 31`** materialises the 32-bit sign of `rS` as `0` or `βˆ’1` β€” the canonical "sign mask" pattern. Often used for branchless `abs` or conditional negation. +- **Idiom: `srawi rA, rS, n; addze rA, rA`** β€” divide signed by `2^n` rounding toward zero. +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:457`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L457) β€” matches spec because the sign-extended result has consistent low/high 32-bit signs. +- **No `OE` bit.** + +## Related Instructions + +- [`srawx`](srawx.md) β€” register-shift form. +- [`sradix`](sradix.md), [`sradx`](sradx.md) β€” 64-bit arithmetic right. +- [`addzex`](addzex.md) β€” divide-rounding companion. +- [`extswx`](extswx.md) β€” `srawi rA, rS, 0` is functionally a sign-extend-32-to-64 plus `CA = 0` clear; `extsw` is preferred when CA isn't wanted. + +## IBM Reference + +- [AIX 7.3 β€” `srawi` (Shift Right Algebraic Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srawi-shift-right-algebraic-word-immediate-instruction) diff --git a/tools/ppc-manual/alu/srawx.md b/tools/ppc-manual/alu/srawx.md new file mode 100644 index 00000000..f07950a6 --- /dev/null +++ b/tools/ppc-manual/alu/srawx.md @@ -0,0 +1,138 @@ +# `srawx` β€” Shift Right Algebraic Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000630` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `sraw` | `srawx` | β€” | Shift Right Algebraic Word | +| `sraw.` | `srawx` | Rc=1 | Shift Right Algebraic Word | + +## Syntax + +```asm +sraw[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `srawx` β€” form `X` + +- **Opcode word:** `0x7c000630` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `792` +- **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` | srawx: read | Source GPR (alias for RD in some stores). | +| `RB` | srawx: read | Source GPR. | +| `RA` | srawx: write | Source GPR (`r0`–`r31`). | +| `CR` | srawx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CA` | srawx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `srawx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA`, `CA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `srawx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always). + +## Operation (pseudocode) + +``` +n <- (RB)[58:63] +RA <- ((RS)[32:63] >>a n) sign-extended +CA <- 1 if (signed RS < 0) && any_bit_shifted_out else 0 +``` + +## 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 + +**`srawx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="srawx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1262`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1262) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:840`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L840) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:642-660`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L642-L660) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::srawx => { + // PPCBUG-041+043 coupled: 32-bit ABI writeback truncation + CR0 i32. + // CA logic is independently correct (uses u32 shifted-out test). + let rs = ctx.gpr[instr.rs()] as i32; + let sh = ctx.gpr[instr.rb()] as u32 & 0x3F; + if sh == 0 { + ctx.gpr[instr.ra()] = rs as u32 as u64; + ctx.xer_ca = 0; + } else if sh < 32 { + let result = rs >> sh; + ctx.xer_ca = if rs < 0 && (rs as u32) << (32 - sh) != 0 { 1 } else { 0 }; + ctx.gpr[instr.ra()] = result as u32 as u64; + } else { + ctx.gpr[instr.ra()] = if rs < 0 { 0xFFFF_FFFFu64 } else { 0 }; + ctx.xer_ca = if rs < 0 { 1 } else { 0 }; + } + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit arithmetic right shift, sign-extended to 64.** `RA ← ((i32)RS >> n) sign-extended`, with `XER[CA]` set when `RS[32] = 1` (negative) AND any low bit was shifted out. +- **Shift count is 6 bits**, `RB[58:63]`. Counts `β‰₯ 32` saturate: `RA = -1` (all-ones, sign-extended) if `RS < 0`, else `0`. Xenia handles this in three branches ([`interpreter.rs:432-444`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L432-L444)). +- **`SH = 0`** sign-extends `RS` to 64 bits and clears `XER[CA]` β€” like `extsw`, but additionally writing CA. +- **Result is always sign-extended to 64 bits.** `RA[0:31]` matches the sign of `RA[32]`. This is the key difference from [`srwx`](srwx.md) (zero-extension). +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:443`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L443) β€” but since the result is sign-extended, the low 32 bits' sign matches the full 64-bit sign, so spec and xenia agree here. +- **Used with [`addzex`](addzex.md)** for signed divide by `2^n` rounding toward zero. +- **No `OE` bit.** + +## Related Instructions + +- [`srawix`](srawix.md) β€” immediate-shift form. +- [`sradx`](sradx.md), [`sradix`](sradix.md) β€” 64-bit arithmetic right shifts. +- [`srwx`](srwx.md) β€” 32-bit *logical* right shift (no `XER[CA]`). +- [`addzex`](addzex.md) β€” companion for divide-rounding idiom. +- [`slwx`](slwx.md) β€” left shift. + +## IBM Reference + +- [AIX 7.3 β€” `sraw` (Shift Right Algebraic Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sraw-shift-right-algebraic-word-instruction) diff --git a/tools/ppc-manual/alu/srdx.md b/tools/ppc-manual/alu/srdx.md new file mode 100644 index 00000000..34965967 --- /dev/null +++ b/tools/ppc-manual/alu/srdx.md @@ -0,0 +1,123 @@ +# `srdx` β€” Shift Right Doubleword + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000436` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `srd` | `srdx` | β€” | Shift Right Doubleword | +| `srd.` | `srdx` | Rc=1 | Shift Right Doubleword | + +## Syntax + +```asm +srd[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `srdx` β€” form `X` + +- **Opcode word:** `0x7c000436` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `539` +- **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` | srdx: read | Source GPR (alias for RD in some stores). | +| `RB` | srdx: read | Source GPR. | +| `RA` | srdx: write | Source GPR (`r0`–`r31`). | +| `CR` | srdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `srdx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `srdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- (RB)[57:63] +RA <- ((RS) >> n) if n < 64 else 0 +``` + +## 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 + +**`srdx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="srdx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1161`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1161) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:821`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L821) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:684-691`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L684-L691) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::srdx => { + let sh = ctx.gpr[instr.rb()] & 0x7F; + ctx.gpr[instr.ra()] = if sh < 64 { + ctx.gpr[instr.rs()] >> sh + } else { 0 }; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **64-bit logical right shift.** `RA ← RS >> (RB & 0x7F)` if the count is `< 64`, else `RA = 0`. Bits shifted in from the high end are zero (no sign extension). +- **Shift count is 7 bits** (`RB[57:63]`). Counts `64..127` produce zero, not `RS >> (count mod 64)`. Xenia respects this with `& 0x7F` and an explicit `if sh < 64` check ([`interpreter.rs:472`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L472)). +- **No `XER[CA]` produced.** This is the logical right shift; for arithmetic shift with `XER[CA]` use [`sradx`](sradx.md). +- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:475`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L475). Result is non-negative as a signed value (high bit is always cleared by the shift), so CR0 will only ever be `EQ` or `GT`. +- **No `OE` bit.** +- **The `srdi` simplified mnemonic** uses [`rldiclx`](rldiclx.md) instead β€” `rldicl rA, rS, 64-n, n` β€” because it can be combined with masking. `srd` is for runtime-variable counts. + +## Related Instructions + +- [`srwx`](srwx.md) β€” 32-bit logical right shift. +- [`sradx`](sradx.md), [`sradix`](sradix.md) β€” 64-bit arithmetic right. +- [`sldx`](sldx.md) β€” 64-bit left shift. +- [`rldiclx`](rldiclx.md) β€” `srdi` immediate-shift expansion. + +## IBM Reference + +- [AIX 7.3 β€” `srd` (Shift Right Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srd-shift-right-double-word-instruction) diff --git a/tools/ppc-manual/alu/srwx.md b/tools/ppc-manual/alu/srwx.md new file mode 100644 index 00000000..86f4799e --- /dev/null +++ b/tools/ppc-manual/alu/srwx.md @@ -0,0 +1,125 @@ +# `srwx` β€” Shift Right Word + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000430` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `srw` | `srwx` | β€” | Shift Right Word | +| `srw.` | `srwx` | Rc=1 | Shift Right Word | + +## Syntax + +```asm +srw[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `srwx` β€” form `X` + +- **Opcode word:** `0x7c000430` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `536` +- **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` | srwx: read | Source GPR (alias for RD in some stores). | +| `RB` | srwx: read | Source GPR. | +| `RA` | srwx: write | Source GPR (`r0`–`r31`). | +| `CR` | srwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `srwx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `srwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +n <- (RB)[58:63] +RA <- ((RS)[32:63] >> n) zero-extended if n < 32 else 0 +``` + +## 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 + +**`srwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="srwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1180`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1180) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:820`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L820) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:632-641`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L632-L641) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::srwx => { + // PPCBUG-044: 32-bit ABI CR0 view (zero-extended right shift can never + // have bit 31 set, but use the canonical form for consistency). + let sh = ctx.gpr[instr.rb()] as u32; + ctx.gpr[instr.ra()] = if sh < 32 { + ((ctx.gpr[instr.rs()] as u32) >> sh) as u64 + } else { 0 }; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit logical right shift, zero-extended to 64.** `RA ← (u32)RS >> (RB & 0x3F)` if count `< 32`, else `RA = 0`. The high 32 bits of `RA` are always zero. +- **Shift count is 6 bits**, `RB[58:63]`. Counts `[32, 63]` produce zero (not `RS >> (count mod 32)`); xenia's explicit `if sh < 32` guards against Rust UB ([`interpreter.rs:425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L425)). +- **No `XER[CA]` produced.** For arithmetic shift with `XER[CA]` use [`srawx`](srawx.md). +- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:428`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L428). Since the result has zeroed high 32 bits and zeroed sign bit (high bit of the 32-bit result is always 0 after a non-zero shift), CR0 will be `EQ` or `GT`. +- **No `OE` bit.** +- **`srwi` simplified mnemonic** uses [`rlwinmx`](rlwinmx.md), not this instruction. `srw` is for runtime-variable counts. + +## Related Instructions + +- [`srdx`](srdx.md) β€” 64-bit logical right shift. +- [`srawx`](srawx.md), [`srawix`](srawix.md) β€” 32-bit arithmetic right. +- [`slwx`](slwx.md) β€” 32-bit left shift. +- [`rlwinmx`](rlwinmx.md) β€” `srwi` immediate expansion. + +## IBM Reference + +- [AIX 7.3 β€” `srw` (Shift Right Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srw-shift-right-word-instruction) diff --git a/tools/ppc-manual/alu/subfcx.md b/tools/ppc-manual/alu/subfcx.md new file mode 100644 index 00000000..0b6255a6 --- /dev/null +++ b/tools/ppc-manual/alu/subfcx.md @@ -0,0 +1,138 @@ +# `subfcx` β€” Subtract From Carrying + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000010` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subfc` | `subfcx` | β€” | Subtract From Carrying | +| `subfco` | `subfcx` | OE=1 | Subtract From Carrying | +| `subfc.` | `subfcx` | Rc=1 | Subtract From Carrying | +| `subfco.` | `subfcx` | OE=1, Rc=1 | Subtract From Carrying | + +## Syntax + +```asm +subfc[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `subfcx` β€” form `XO` + +- **Opcode word:** `0x7c000010` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `8` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | subfcx: read | Source GPR (`r0`–`r31`). | +| `RB` | subfcx: read | Source GPR. | +| `RD` | subfcx: write | Destination GPR. | +| `CR` | subfcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | subfcx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `subfcx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `subfcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ~(RA) + (RB) + 1 +CA <- carry_out +``` + +## 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 + +**`subfcx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subfcx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:441`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L441) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:859`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L859) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:270-287`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L270-L287) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subfcx => { + // PPCBUG-007: 32-bit ABI. The `rb >= ra` u64 unsigned compare is + // exactly the shape that broke addis. Defensive 32-bit truncation + // is required for correct CA even after upstream cleanup. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let result32 = rb32.wrapping_sub(ra32); + ctx.xer_ca = if rb32 >= ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_diff = (rb32 as i32 as i128) - (ra32 as i32 as i128); + overflow::apply(ctx, true_diff != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RT ← RB βˆ’ RA` with `XER[CA]` set on no-borrow.** Same operand-order convention as [`subfx`](subfx.md): the *first* source is subtracted *from* the second. +- **`XER[CA] = 1` means *no borrow occurred*** β€” i.e. `RB >= RA` as unsigned. PowerISA encodes this as the carry-out of `~RA + RB + 1`, not as a borrow flag. Xenia's `if rb >= ra` test ([`interpreter.rs:157`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L157)) is the correct boolean encoding. +- **No trap on signed overflow.** `subfco` / `subfco.` set `XER[OV]` and sticky `XER[SO]`; xenia-rs leaves the `OE` arm unimplemented. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:160`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L160) truncates with `as i32 as i64`. Spec demands a full 64-bit signed compare for `subfc.`. +- **Seeds a multi-word subtract chain.** Use as the low-word op; continue with [`subfex`](subfex.md) for middle words and [`subfmex`](subfmex.md)/[`subfzex`](subfzex.md) for the high word. +- **Operand aliasing fine.** `subfc r3, r3, r3` always yields `0` with `CA = 1`. + +## Related Instructions + +- [`subfx`](subfx.md) β€” same op without `XER[CA]`. +- [`subfex`](subfex.md) β€” `~RA + RB + CA` (chain continuation). +- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) β€” chain terminators. +- [`subfic`](subficx.md) β€” D-form: `RT ← SIMM βˆ’ RA` with `XER[CA]`. +- [`addcx`](addcx.md) β€” dual: addition seed-of-chain. + +## IBM Reference + +- [AIX 7.3 β€” `subfc` (Subtract From Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfc-subtract-from-carrying-instruction) diff --git a/tools/ppc-manual/alu/subfex.md b/tools/ppc-manual/alu/subfex.md new file mode 100644 index 00000000..40d2cdcb --- /dev/null +++ b/tools/ppc-manual/alu/subfex.md @@ -0,0 +1,138 @@ +# `subfex` β€” Subtract From Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000110` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subfe` | `subfex` | β€” | Subtract From Extended | +| `subfeo` | `subfex` | OE=1 | Subtract From Extended | +| `subfe.` | `subfex` | Rc=1 | Subtract From Extended | +| `subfeo.` | `subfex` | OE=1, Rc=1 | Subtract From Extended | + +## Syntax + +```asm +subfe[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `subfex` β€” form `XO` + +- **Opcode word:** `0x7c000110` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `136` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | subfex: read | Source GPR (`r0`–`r31`). | +| `RB` | subfex: read | Source GPR. | +| `RD` | subfex: write | Destination GPR. | +| `CR` | subfex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | subfex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `subfex` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `subfex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ~(RA) + (RB) + CA +CA <- carry_out +``` + +## 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 + +**`subfex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subfex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:468`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L468) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:867`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L867) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:288-306`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L288-L306) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subfex => { + // PPCBUG-008: 32-bit ABI. Compute in u32 space β€” `!ra` on u64 always + // pollutes the upper 32 bits, making this an active poisoner. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = (!ra32).wrapping_add(rb32).wrapping_add(ca); + ctx.xer_ca = if rb32 > ra32 || (rb32 == ra32 && ca != 0) { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + // RT <- !RA + RB + CA == RB - RA - 1 + CA (32-bit semantics). + let true_sum = (rb32 as i32 as i128) - (ra32 as i32 as i128) - 1 + (ca as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RT ← ~RA + RB + XER[CA]`.** The middle link of a multi-word subtract chain seeded by [`subfcx`](subfcx.md). `XER[CA]` propagates the borrow from the previous word. +- **Carry-out predicate handles the boundary case.** Xenia computes `CA' = (rb > ra) || (rb == ra && CA != 0)` ([`interpreter.rs:170`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L170)). The second clause covers when `RB == RA` and the previous chain added a `+1` from the input carry β€” without it, the carry-out would be wrong. +- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores it. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:173`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L173). +- **`XER[CA]` must be initialised** (typically by [`subfcx`](subfcx.md)). Reading stale `CA` is a frequent multi-word-subtract bug. +- **Symmetry with [`addex`](addex.md).** `subfe RT, RA, RB` ≑ `adde RT, ~RA, RB` (with the implicit complement). + +## Related Instructions + +- [`subfcx`](subfcx.md) β€” seeds the chain (no `CA` read). +- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) β€” terminate the chain (`~RA + βˆ’1 + CA`, `~RA + 0 + CA`). +- [`subfx`](subfx.md) β€” plain subtract. +- [`addex`](addex.md) β€” dual. + +## IBM Reference + +- [AIX 7.3 β€” `subfe` (Subtract From Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfe-subtract-from-extended-instruction) diff --git a/tools/ppc-manual/alu/subficx.md b/tools/ppc-manual/alu/subficx.md new file mode 100644 index 00000000..3ab4de0c --- /dev/null +++ b/tools/ppc-manual/alu/subficx.md @@ -0,0 +1,131 @@ +# `subficx` β€” Subtract From Immediate Carrying + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x20000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subfic` | `subficx` | β€” | Subtract From Immediate Carrying | + +## Syntax + +```asm +subfic [RD], [RA], [SIMM] +``` + +## Encoding + +### `subficx` β€” form `D` + +- **Opcode word:** `0x20000000` +- **Primary opcode (bits 0–5):** `8` +- **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 | +| --- | --- | --- | +| `RA` | subficx: read | Source GPR (`r0`–`r31`). | +| `SIMM` | subficx: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `RD` | subficx: write | Destination GPR. | +| `CA` | subficx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | + +## Register Effects + +### `subficx` + +- **Reads (always):** `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `subficx`: **XER[CA]** ← carry-out of the add / borrow-in of the subtract (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 + +**`subficx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subficx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:459`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L459) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:333`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L333) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:155-164`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L155-L164) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subficx => { + // PPCBUG-005: 32-bit ABI. Sign-extended imm has bits 32-63 set for + // negative SIMM, poisoning the writeback. Canary uses 32-bit form. + let ra32 = ctx.gpr[instr.ra()] as u32; + let imm32 = instr.simm16() as i32 as u32; + let result32 = imm32.wrapping_sub(ra32); + ctx.xer_ca = if imm32 >= ra32 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RT ← SIMM βˆ’ RA` with `XER[CA]` always set.** Note the operand order: the *immediate* is the minuend, not the subtrahend. `subfic rD, rA, 1` computes `1 - rA`, useful for negation-plus-one or one's-complement-style operations. +- **Immediate is sign-extended** to 64 bits before the subtract. So `subfic rD, rA, -1` computes `-1 - rA`, equivalent to `~rA`. +- **`XER[CA] = 1` when `SIMM >= RA`** (no borrow). Computed in xenia as `if imm >= ra` ([`interpreter.rs:73`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L73)) β€” comparing the sign-extended unsigned representations. +- **No `Rc`, no `OE`.** This D-form has no flag bits beyond the implicit `CA` write. +- **No record-form variant.** There is no `subfic.` in the ISA; if you need CR0 to also reflect the result, follow with a `cmpwi`. +- **Synthesised "subtract immediate"**. Assemblers sometimes accept `subi rD, rA, value` as a shorthand for `addi rD, rA, -value`, but for the carry-producing variant you must use `subfic` explicitly. +- **Common idiom: `subfic rD, rA, 0`** computes `-rA` and sets `CA` according to whether `rA == 0` (no borrow) or `rA != 0` (borrow). Equivalent to [`negx`](negx.md) when `CA` doesn't matter. + +## Related Instructions + +- [`subfcx`](subfcx.md) β€” XO-form register version. +- [`subfx`](subfx.md) β€” register subtract without `XER[CA]`. +- [`addic`](addic.md), [`addicx`](addicx.md) β€” dual: add immediate carrying. +- [`negx`](negx.md) β€” equivalent to `subfic rD, rA, 0` when `CA` is unwanted. + +## IBM Reference + +- [AIX 7.3 β€” `subfic` (Subtract From Immediate Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfic-subtract-from-immediate-carrying-instruction) diff --git a/tools/ppc-manual/alu/subfmex.md b/tools/ppc-manual/alu/subfmex.md new file mode 100644 index 00000000..aa4611ab --- /dev/null +++ b/tools/ppc-manual/alu/subfmex.md @@ -0,0 +1,145 @@ +# `subfmex` β€” Subtract From Minus One Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c0001d0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subfme` | `subfmex` | β€” | Subtract From Minus One Extended | +| `subfmeo` | `subfmex` | OE=1 | Subtract From Minus One Extended | +| `subfme.` | `subfmex` | Rc=1 | Subtract From Minus One Extended | +| `subfmeo.` | `subfmex` | OE=1, Rc=1 | Subtract From Minus One Extended | + +## Syntax + +```asm +subfme[OE][Rc] [RD], [RA] +``` + +## Encoding + +### `subfmex` β€” form `XO` + +- **Opcode word:** `0x7c0001d0` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `232` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | subfmex: read | Source GPR (`r0`–`r31`). | +| `CA` | subfmex: read; subfmex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `RD` | subfmex: write | Destination GPR. | +| `CR` | subfmex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | subfmex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `subfmex` + +- **Reads (always):** `RA`, `CA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `subfmex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (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 + +**`subfmex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subfmex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:486`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L486) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:871`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L871) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:325-341`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L325-L341) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subfmex => { + // PPCBUG-019: also fixes the always-true CA edge β€” `!ra` on u64 + // is non-zero when ra32==0xFFFFFFFF and ca==0, so CA was stuck at 1. + let ra32 = ctx.gpr[instr.ra()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = (!ra32).wrapping_add(ca).wrapping_sub(1); + ctx.xer_ca = if (!ra32) != 0 || ca != 0 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = -(ra32 as i32 as i128) - 2 + (ca as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RT ← ~RA + (βˆ’1) + XER[CA]` ≑ `~RA βˆ’ 1 + CA`.** Terminator for a multi-word subtract chain when the high "minuend" word is implicitly all-ones (e.g. when computing `~x` style negation across many words). +- **`RB` field unused.** XO-form but only `RA` is read. +- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:191`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L191)). Only when `RA == ~0` (all ones) AND `CA == 0` does `CA'` become 0 β€” every other case produces no borrow on this final word. +- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores it. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:194`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L194). +- **`XER[CA]` must be initialised** (typically by a [`subfcx`](subfcx.md) or [`subfex`](subfex.md) earlier in the chain). +- **Symmetric with [`addmex`](addmex.md)**, the add-side terminator. + +## Related Instructions + +- [`subfzex`](subfzex.md) β€” terminate with `~RA + 0 + CA` instead. +- [`subfex`](subfex.md) β€” middle-of-chain. +- [`subfcx`](subfcx.md) β€” chain seed. +- [`addmex`](addmex.md) β€” dual. + +## IBM Reference + +- [AIX 7.3 β€” `subfme` (Subtract From Minus One Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfme-subtract-from-minus-one-extended-instruction) diff --git a/tools/ppc-manual/alu/subfx.md b/tools/ppc-manual/alu/subfx.md new file mode 100644 index 00000000..98bf7404 --- /dev/null +++ b/tools/ppc-manual/alu/subfx.md @@ -0,0 +1,148 @@ +# `subfx` β€” Subtract From + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000050` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subf` | `subfx` | β€” | Subtract From | +| `subfo` | `subfx` | OE=1 | Subtract From | +| `subf.` | `subfx` | Rc=1 | Subtract From | +| `subfo.` | `subfx` | OE=1, Rc=1 | Subtract From | + +## Syntax + +```asm +subf[OE][Rc] [RD], [RA], [RB] +``` + +## Encoding + +### `subfx` β€” form `XO` + +- **Opcode word:** `0x7c000050` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `40` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | subfx: read | Source GPR (`r0`–`r31`). | +| `RB` | subfx: read | Source GPR. | +| `RD` | subfx: write | Destination GPR. | +| `CR` | subfx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | subfx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `subfx` + +- **Reads (always):** `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `subfx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. + +## Operation (pseudocode) + +``` +RT <- ~(RA) + (RB) + 1 ; = (RB) βˆ’ (RA) +``` + +## 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 + +**`subfx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subfx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:427`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L427) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:863`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L863) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:255-269`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L255-L269) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subfx => { + // PPCBUG-017+020: 32-bit truncation. + let ra32 = ctx.gpr[instr.ra()] as u32; + let rb32 = ctx.gpr[instr.rb()] as u32; + let result32 = rb32.wrapping_sub(ra32); + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_diff = (rb32 as i32 as i128) - (ra32 as i32 as i128); + overflow::apply(ctx, true_diff != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +RT <- ~(RA) + (RB) + 1 ; = (RB) βˆ’ (RA) +if OE then + XER[OV] <- signed_overflow_of_subtract((RB), (RA), RT) + XER[SO] <- XER[SO] | XER[OV] +if Rc then + CR0[LT,GT,EQ] <- signed_compare(RT, 0) + CR0[SO] <- XER[SO] +``` + +## Special Cases & Edge Conditions + +- **Operand order gotcha.** `subf RT, RA, RB` computes `RT ← RB βˆ’ RA`, **not** `RA βˆ’ RB`. This reverses the intuitive ordering seen in x86/ARM. The assembler exposes a simplified mnemonic `sub RT, RX, RY` ≑ `subf RT, RY, RX` that restores the natural order β€” watch for both forms in disassembly. +- **Implemented as add-with-complement.** Hardware (and xenia) compute `~RA + RB + 1`. All overflow/CR semantics are the same as [`addx`](addx.md) with one operand complemented. +- **No `XER[CA]` update** β€” use [`subfcx`](subfcx.md) if you need a borrow-out bit. +- **No trap on overflow.** `subfo` / `subfo.` only record the event in `XER[OV]` and sticky-set `XER[SO]`. +- **Signed-overflow predicate.** `OV = ((RA ^ RB) & (RB ^ RT)) >> 63` β€” set when operands have different signs and the result's sign differs from `RB`'s. +- **64-bit CR update on Xenon** (xenia-rs truncates to 32 bits; see [`addx`](addx.md) note). + +## Related Instructions + +- [`subfcx`](subfcx.md) β€” subtract-from producing `XER[CA]` (borrow-out). +- [`subfex`](subfex.md) β€” `~RA + RB + XER[CA]` (subtract-with-borrow chain). +- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) β€” subtract-from `βˆ’1` / `0` with carry-in (propagates borrows). +- [`subfic`](subfic.md) β€” D-form: `RT ← SIMM βˆ’ RA` with `XER[CA]`. +- [`negx`](negx.md) β€” specialises to `0 βˆ’ RA`. +- [`addx`](addx.md) β€” inverse; shares overflow machinery. + +## IBM Reference + +- [AIX 7.3 β€” `subf` (Subtract From)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subf-subtract-from-instruction) +- [AIX 7.3 β€” `sub` (simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-sub-subtract) diff --git a/tools/ppc-manual/alu/subfzex.md b/tools/ppc-manual/alu/subfzex.md new file mode 100644 index 00000000..53ef64d6 --- /dev/null +++ b/tools/ppc-manual/alu/subfzex.md @@ -0,0 +1,146 @@ +# `subfzex` β€” Subtract From Zero Extended + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [XO](../forms/XO.md) Β· **Opcode:** `0x7c000190` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `subfze` | `subfzex` | β€” | Subtract From Zero Extended | +| `subfzeo` | `subfzex` | OE=1 | Subtract From Zero Extended | +| `subfze.` | `subfzex` | Rc=1 | Subtract From Zero Extended | +| `subfzeo.` | `subfzex` | OE=1, Rc=1 | Subtract From Zero Extended | + +## Syntax + +```asm +subfze[OE][Rc] [RD], [RA] +``` + +## Encoding + +### `subfzex` β€” form `XO` + +- **Opcode word:** `0x7c000190` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `200` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RA` | subfzex: read | Source GPR (`r0`–`r31`). | +| `CA` | subfzex: read; subfzex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. | +| `RD` | subfzex: write | Destination GPR. | +| `CR` | subfzex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `OE` | subfzex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | + +## Register Effects + +### `subfzex` + +- **Reads (always):** `RA`, `CA` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD`, `CA` +- **Writes (conditional):** `CR`, `OE` + +## Status-Register Effects + +- `subfzex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (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 + +**`subfzex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="subfzex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:504`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L504) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:869`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L869) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:307-324`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L307-L324) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::subfzex => { + // PPCBUG-018: same active-poisoning shape as subfex; operate in u32. + let ra32 = ctx.gpr[instr.ra()] as u32; + let ca = ctx.xer_ca as u32; + let result32 = (!ra32).wrapping_add(ca); + // RT <- !RA + CA (no -1 term). 32-bit carry-out only when + // !ra32 = u32::MAX (i.e. ra32 = 0) AND ca = 1. + ctx.xer_ca = if ra32 == 0 && ca != 0 { 1 } else { 0 }; + ctx.gpr[instr.rd()] = result32 as u64; + if instr.oe() { + let true_sum = -(ra32 as i32 as i128) - 1 + (ca as i128); + overflow::apply(ctx, true_sum != (result32 as i32) as i128); + } + if instr.rc_bit() { + ctx.update_cr_signed(0, result32 as i32 as i64); + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RT ← ~RA + 0 + XER[CA]` ≑ `~RA + CA`.** The subtract-side high-word terminator for a multi-word subtract chain. Implements `0 - (...) - borrow` for the high word. +- **`RB` field unused.** +- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:180`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L180)). Only `RA == ~0 && CA == 0` produces `CA' = 0`; every other case gives no-borrow. +- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:183`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L183). +- **`XER[CA]` must be initialised** by an earlier carrying instruction. +- **Common idiom: extracting `XER[CA]` as 0/-1.** `subfze rT, rN` (where `rN == 0`) materialises `XER[CA]` to `0` or `-1` (`-1 = ~0 + CA = -1 + CA`); pair with [`addzex`](addzex.md) for `0/1` instead. + +## Related Instructions + +- [`subfmex`](subfmex.md) β€” terminator with `~RA + (βˆ’1) + CA`. +- [`subfex`](subfex.md), [`subfcx`](subfcx.md) β€” chain middle / seed. +- [`addzex`](addzex.md) β€” dual; produces 0/1 from `CA`. +- [`negx`](negx.md) β€” `subfze` with `RA = 0` and `CA = 1` is functionally similar. + +## IBM Reference + +- [AIX 7.3 β€” `subfze` (Subtract From Zero Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfze-subtract-from-zero-extended-instruction) diff --git a/tools/ppc-manual/alu/sync.md b/tools/ppc-manual/alu/sync.md new file mode 100644 index 00000000..4c29bc9f --- /dev/null +++ b/tools/ppc-manual/alu/sync.md @@ -0,0 +1,113 @@ +# `sync` β€” Synchronize + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0004ac` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `sync` | `sync` | β€” | Synchronize | + +## Syntax + +```asm +sync +``` + +## Encoding + +### `sync` β€” form `X` + +- **Opcode word:** `0x7c0004ac` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `598` +- **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 + +### `sync` + +- **Reads (always):** _none_ +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +multi-thread memory barrier (heavy). L=0 full sync; L=1 lightweight sync. +``` + +## 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 + +**`sync`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="sync"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:754`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L754) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:85`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L85) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:825`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L825) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => { + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Heavy multi-thread memory barrier.** All memory accesses (loads and stores, cacheable and not) issued by this thread before `sync` complete with respect to all other threads/processors before any subsequent memory access begins. Drains the store queue. +- **`L` field selects sync class.** `L=0` is full *hwsync* (the default). `L=1` is `lwsync` β€” orders only loads-after-loads, loads-after-stores, and stores-after-stores (not stores-after-loads). The Xenon implements both via the same encoding with `L` (bit 9) selecting variant. Most disassembly shows the unsuffixed `sync` mnemonic, which assembles to `L=0`. +- **No register or CR effects.** Pure ordering primitive. +- **Used to implement release semantics.** A typical lock-release sequence is `sync; stw r0, lock`. Acquire side uses `lwsync` after the load. +- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267) collapses `sync`, `eieio`, `isync` into PC-advance. Since xenia is single-threaded interpretation, host program order subsumes all PPC ordering. +- **Distinct from [`isync`](isync.md)**, which orders the *instruction* stream β€” `sync` does not refetch instructions. +- **Slow on real hardware.** Hundreds of cycles when the store queue is full; hot paths avoid `sync` and use `lwsync` or no barrier when only single-thread ordering is needed. + +## Related Instructions + +- [`isync`](isync.md) β€” instruction-fetch barrier. +- [`eieio`](eieio.md) β€” lighter I/O barrier for caching-inhibited storage. +- `lwsync` β€” same encoding, `L=1`; not separately enumerated in this page set. + +## IBM Reference + +- [AIX 7.3 β€” `sync` (Synchronize)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sync-synchronize-instruction) +- PowerISA v2.07B, Book II, Β§1.7 β€” defines `hwsync`/`lwsync`/`ptesync` semantics. diff --git a/tools/ppc-manual/alu/xori.md b/tools/ppc-manual/alu/xori.md new file mode 100644 index 00000000..369dfdf2 --- /dev/null +++ b/tools/ppc-manual/alu/xori.md @@ -0,0 +1,115 @@ +# `xori` β€” XOR Immediate + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x68000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `xori` | `xori` | β€” | XOR Immediate | + +## Syntax + +```asm +xori [RA], [RS], [UIMM] +``` + +## Encoding + +### `xori` β€” form `D` + +- **Opcode word:** `0x68000000` +- **Primary opcode (bits 0–5):** `26` +- **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 | +| --- | --- | --- | +| `RS` | xori: read | Source GPR (alias for RD in some stores). | +| `UIMM` | xori: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | xori: write | Source GPR (`r0`–`r31`). | + +## Register Effects + +### `xori` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RA <- (RS) ^ (0x0000 || UIMM) +``` + +## 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 + +**`xori`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="xori"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:839`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L839) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:349`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L349) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:520-523`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L520-L523) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::xori => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ (instr.uimm16() as u64); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No record form.** Like [`ori`](ori.md), there is no `xori.`. To get a CR0 update follow with `cmpwi` or use [`xorx`](xorx.md) with `Rc=1`. +- **Immediate is zero-extended** to 64 bits. Only the low 16 bits of `RA` can be flipped; the high 48 bits are passed through from `RS` unchanged. +- **`xori 0, 0, 0` is a valid NOP encoding** but the canonical NOP is `ori 0, 0, 0`. Disassemblers should still display this as `xori r0, r0, 0` or recognise it as a no-op. +- **64-bit operation in xenia-rs.** [`interpreter.rs:338`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L338) β€” full `u64` XOR with the immediate. +- **No `XER`, no `CR`** side effects. +- **`RA = 0` reads `r0`** (not literal zero); see [`ori`](ori.md). +- **Useful for masked toggle.** `xori rA, rS, mask` flips the bits of `rS` indicated by `mask` (low 16 bits only). + +## Related Instructions + +- [`xoris`](xoris.md) β€” companion (immediate shifted left 16). +- [`xorx`](xorx.md) β€” register-register XOR. +- [`eqvx`](eqvx.md) β€” `~(RS ^ RB)`. +- [`ori`](ori.md), [`andix`](andix.md) β€” sister immediate logicals. + +## IBM Reference + +- [AIX 7.3 β€” `xori` (XOR Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xori-immediate-instruction) diff --git a/tools/ppc-manual/alu/xoris.md b/tools/ppc-manual/alu/xoris.md new file mode 100644 index 00000000..afd2a9e9 --- /dev/null +++ b/tools/ppc-manual/alu/xoris.md @@ -0,0 +1,114 @@ +# `xoris` β€” XOR Immediate Shifted + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x6c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `xoris` | `xoris` | β€” | XOR Immediate Shifted | + +## Syntax + +```asm +xoris [RA], [RS], [UIMM] +``` + +## Encoding + +### `xoris` β€” form `D` + +- **Opcode word:** `0x6c000000` +- **Primary opcode (bits 0–5):** `27` +- **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 | +| --- | --- | --- | +| `RS` | xoris: read | Source GPR (alias for RD in some stores). | +| `UIMM` | xoris: read | 16-bit unsigned immediate. Zero-extended. | +| `RA` | xoris: write | Source GPR (`r0`–`r31`). | + +## Register Effects + +### `xoris` + +- **Reads (always):** `RS`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RA <- (RS) ^ (UIMM || 0x0000) +``` + +## 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 + +**`xoris`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="xoris"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:846`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L846) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:350`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L350) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:524-527`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L524-L527) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::xoris => { + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ((instr.uimm16() as u64) << 16); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No record form.** Like all immediate logicals other than `andi.`/`andis.`, `xoris` does not update CR0. +- **Immediate is zero-extended *then* shifted left 16.** Only bits 32–47 of `RA` (PowerISA bit numbering) can be flipped; the high 32 bits and low 16 bits of `RA` come from `RS` unchanged. +- **Common pattern with [`xori`](xori.md)** to flip arbitrary 32-bit bitmasks: `xoris RA, RS, hi16; xori RA, RA, lo16`. +- **64-bit operation.** [`interpreter.rs:342`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L342). +- **No `XER`, no `CR`.** +- **`RA = 0` reads `r0`** (not literal zero). +- **Used to toggle the high half of a 32-bit word**, e.g. `xoris r3, r3, 0x8000` flips bit 32 (the sign bit of the low word) β€” a one-instruction sign-flip on a 32-bit value. + +## Related Instructions + +- [`xori`](xori.md) β€” companion (immediate not shifted). +- [`xorx`](xorx.md) β€” register-register XOR. +- [`oris`](oris.md), [`andisx`](andisx.md) β€” sister immediate-shifted logicals. + +## IBM Reference + +- [AIX 7.3 β€” `xoris` (XOR Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xoris-immediate-shifted-instruction) diff --git a/tools/ppc-manual/alu/xorx.md b/tools/ppc-manual/alu/xorx.md new file mode 100644 index 00000000..4e809348 --- /dev/null +++ b/tools/ppc-manual/alu/xorx.md @@ -0,0 +1,121 @@ +# `xorx` β€” XOR + +> **Category:** [Integer ALU](../categories/alu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000278` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `xor` | `xorx` | β€” | XOR | +| `xor.` | `xorx` | Rc=1 | XOR | + +## Syntax + +```asm +xor[Rc] [RA], [RS], [RB] +``` + +## Encoding + +### `xorx` β€” form `X` + +- **Opcode word:** `0x7c000278` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `316` +- **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` | xorx: read | Source GPR (alias for RD in some stores). | +| `RB` | xorx: read | Source GPR. | +| `RA` | xorx: write | Source GPR (`r0`–`r31`). | +| `CR` | xorx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `xorx` + +- **Reads (always):** `RS`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `RA` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `xorx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +RA <- (RS) ^ (RB) +``` + +## 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 + +**`xorx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="xorx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:829`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L829) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:798`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L798) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:556-561`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L556-L561) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::xorx => { + // PPCBUG-032+020: 32-bit ABI CR0 view. + ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ctx.gpr[instr.rb()]; + if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`RA ← RS XOR RB`.** Bit-wise XOR. +- **Idiom: `xor RA, RS, RS`** zeroes `RA` β€” the canonical "clear register" instruction. Cheaper than `li RA, 0` because no immediate-extraction stage is involved. +- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources). +- **64-bit operation** on Xenon. +- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0`. +- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:367`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L367) truncates with `as i32 as i64`. For `xor.` whose result has differing high/low halves, spec and xenia diverge; `xor. RA, RS, RS` gives `EQ` either way. +- **Useful as bitmask toggle.** `xor r3, r3, r4` flips in `r3` every bit set in `r4`. +- **No `XER[CA]`.** + +## Related Instructions + +- [`xori`](xori.md), [`xoris`](xoris.md) β€” D-form immediate variants. +- [`eqvx`](eqvx.md) β€” NXOR (`~(RS ^ RB)`). +- [`andx`](andx.md), [`orx`](orx.md), [`norx`](norx.md), [`nandx`](nandx.md) β€” sister logicals. + +## IBM Reference + +- [AIX 7.3 β€” `xor` (XOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xor-instruction) diff --git a/tools/ppc-manual/branch/bcctrx.md b/tools/ppc-manual/branch/bcctrx.md new file mode 100644 index 00000000..af2a2c3a --- /dev/null +++ b/tools/ppc-manual/branch/bcctrx.md @@ -0,0 +1,164 @@ +# `bcctrx` β€” Branch Conditional to Count Register + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000420` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `bcctr` | `bcctrx` | β€” | Branch Conditional to Count Register | +| `bcctrl` | `bcctrx` | LK=1 | Branch Conditional to Count Register | + +## Syntax + +```asm +bcctr[LK] [BO], [BI] +``` + +## Encoding + +### `bcctrx` β€” form `XL` + +- **Opcode word:** `0x4c000420` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `528` +- **Synchronising:** yes + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `LK` | bcctrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. | +| `BO` | bcctrx: read | 5-bit branch options β€” selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. | +| `BI` | bcctrx: read | CR bit index (0–31) selected by BO's condition test. | +| `CR` | bcctrx: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CTR` | bcctrx: read | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. | +| `LR` | bcctrx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. | + +## Register Effects + +### `bcctrx` + +- **Reads (always):** `LK`, `BO`, `BI`, `CR`, `CTR` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** `LR` + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +cond_ok <- BO[0] | (CR[BI] ≑ BO[1]) +if cond_ok then NIA <- CTR[0:61] || 0b00 +if LK then LR <- CIA + 4 +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`bcctrx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="bcctrx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:250`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L250) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:721`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L721) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:962-981`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L962-L981) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::bcctrx => { + let bo = instr.bo(); + let bi = instr.bi(); + + let cond_ok = (bo & 0b10000) != 0 + || (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0)); + + if cond_ok { + let next_pc = ctx.pc + 4; + ctx.pc = (ctx.ctr as u32) & !3; + if instr.lk() { + ctx.lr = next_pc as u64; + } + } else { + if instr.lk() { + ctx.lr = (ctx.pc + 4) as u64; + } + ctx.pc += 4; + } + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No CTR decrement.** Unlike [`bcx`](bcx.md) and [`bclrx`](bclrx.md), `bcctr` cannot decrement CTR (the CTR is the *target*). The PowerISA reserves `BO[2] = 0` encodings β€” they are *invalid* on `bcctrx`. xenia silently ignores `BO[2]`/`BO[3]` and treats every `bcctr` as a pure CR-conditional branch, which matches both the canary emit and real Xenon hardware behaviour. +- **CTR alignment mask.** The target is `CTR & ~3`. Like `bclr`, the low two bits are stripped β€” a misaligned CTR is silently rounded down rather than trapping. +- **BO encoding (CR-only subset).** Because CTR-test bits are unused, only four `BO` patterns are meaningful: + + | BO (binary) | Meaning | + | --- | --- | + | `0100z` | branch if `CR[BI]` false | + | `0101z` | branch if `CR[BI]` true | + | `1z1zz` | branch always (`bctr`) | + | `0000z`/`001at`/etc. | reserved β€” implementation-defined | + +- **Indirect call/dispatch idiom.** `mtctr rN; bctrl` is the canonical PPC indirect call: load function pointer into CTR, call. The xenia interpreter writes `LR ← CIA + 4` only when the branch is taken β€” this matches the PowerISA, but contrast with `bcx` where `LK` always writes LR (even if the branch is not taken). The C-translation reference handles this asymmetry explicitly. +- **`bctr` for switch tables.** Compilers emit `bctr` (not `bctrl`) for jump-table dispatch, with CTR loaded from a base + (index*4) lookup. Xenia honours this by simply jumping to `CTR & ~3`. +- **Synchronisation.** Marked `sync` in xenia's XML β€” context-synchronising. JIT backends must ensure prior side effects have committed before the indirect transfer. +- **No prediction hint sensitivity.** Xenon predicts indirect branches via a separate target cache; the `BO[4]` hint is mostly cosmetic for `bcctr`. + +## Related Instructions + +- [`bclrx`](bclrx.md) β€” branch conditional to **LR** (function returns). +- [`bcx`](bcx.md) β€” branch conditional to displacement (B-form). +- [`bx`](bx.md) β€” unconditional displacement branch (I-form). +- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) β€” load/read CTR via `mtspr 9` / `mfspr 9`. +- [`sc`](sc.md) β€” alternative control-flow exit (system call). + +### Simplified Mnemonics + +| Simplified | Expansion | +| --- | --- | +| `bctr` | `bcctr BO=0b10100, BI=0` β€” unconditional indirect branch | +| `bctrl` | `bcctrl BO=0b10100, BI=0` β€” unconditional indirect call | +| `beqctr crN` | `bcctr BO=0b01100, BI=4Β·N+2` β€” call CTR if `crN.EQ` | +| `bnectr crN` | `bcctr BO=0b00100, BI=4Β·N+2` β€” call CTR if `crN.NE` | +| `bltctr crN` | `bcctr BO=0b01100, BI=4Β·N+0` β€” call CTR if `crN.LT` | +| `bgectr crN` | `bcctr BO=0b00100, BI=4Β·N+0` β€” call CTR if `crN.GE` | +| `bgtctr crN` | `bcctr BO=0b01100, BI=4Β·N+1` β€” call CTR if `crN.GT` | +| `blectr crN` | `bcctr BO=0b00100, BI=4Β·N+1` β€” call CTR if `crN.LE` | + +The unconditional `bctr`/`bctrl` are by far the most common in Xbox 360 disassembly (compiler-emitted indirect calls and switch dispatch). + +## IBM Reference + +- [AIX 7.3 β€” `bcctr` (Branch Conditional to Count Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bcctr-bcctrl-branch-conditional-count-register-instruction) +- [AIX 7.3 β€” Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified) diff --git a/tools/ppc-manual/branch/bclrx.md b/tools/ppc-manual/branch/bclrx.md new file mode 100644 index 00000000..3279453c --- /dev/null +++ b/tools/ppc-manual/branch/bclrx.md @@ -0,0 +1,180 @@ +# `bclrx` β€” Branch Conditional to Link Register + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000020` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `bclr` | `bclrx` | β€” | Branch Conditional to Link Register | +| `bclrl` | `bclrx` | LK=1 | Branch Conditional to Link Register | + +## Syntax + +```asm +bclr[LK] [BO], [BI] +``` + +## Encoding + +### `bclrx` β€” form `XL` + +- **Opcode word:** `0x4c000020` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `16` +- **Synchronising:** yes + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `LK` | bclrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. | +| `BO` | bclrx: read | 5-bit branch options β€” selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. | +| `BI` | bclrx: read | CR bit index (0–31) selected by BO's condition test. | +| `CR` | bclrx: read (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CTR` | bclrx: read (conditional); bclrx: write (conditional) | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. | +| `LR` | bclrx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. | + +## Register Effects + +### `bclrx` + +- **Reads (always):** `LK`, `BO`, `BI` +- **Reads (conditional):** `CR`, `CTR` +- **Writes (always):** _none_ +- **Writes (conditional):** `CTR`, `LR` + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if Β¬BO[2] then CTR <- CTR βˆ’ 1 +ctr_ok <- BO[2] | ((CTR β‰  0) XOR BO[3]) +cond_ok <- BO[0] | (CR[BI] ≑ BO[1]) +if ctr_ok & cond_ok then NIA <- LR[0:61] || 0b00 +if LK then LR <- CIA + 4 +``` + +## C Translation Example + +```c +/* bclr/bclrl β€” branch conditional to LR */ +if (!(insn.BO & 4)) ctr -= 1; +bool ctr_ok = (insn.BO & 4) || ((ctr != 0) ^ !!(insn.BO & 2)); +bool cond_ok = (insn.BO & 16) || (cr_bit(insn.BI) == !!(insn.BO & 8)); +uint32_t next = pc + 4; +if (ctr_ok && cond_ok) pc = lr & ~3u; else pc = next; +if (insn.LK) lr = next; +``` + +## Implementation References + +**`bclrx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="bclrx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:282`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L282) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:711`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L711) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:939-961`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L939-L961) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::bclrx => { + let bo = instr.bo(); + let bi = instr.bi(); + + if bo & 0b00100 == 0 { + ctx.ctr = ctx.ctr.wrapping_sub(1); + } + + let ctr_ok = (bo & 0b00100) != 0 + || (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0)); + let cond_ok = (bo & 0b10000) != 0 + || (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0)); + + let next_pc = ctx.pc + 4; + if ctr_ok && cond_ok { + ctx.pc = (ctx.lr as u32) & !3; + } else { + ctx.pc = next_pc; + } + if instr.lk() { + ctx.lr = next_pc as u64; + } + } +``` +
+ + + +## BO Encoding (5 bits) + +`BO` controls two independent tests and two "hints". Bit 0 is the MSB. + +| BO (binary) | CTR decrement? | CTR test | CR test | Meaning | +| --- | --- | --- | --- | --- | +| `0000z` | yes | `CTR β‰  0` | `Β¬CR[BI]` | decrement, branch if CTR β‰  0 **and** CR[BI] false | +| `0001z` | yes | `CTR = 0` | `Β¬CR[BI]` | decrement, branch if CTR = 0 **and** CR[BI] false | +| `001at` | yes | `CTR β‰  0` / `CTR = 0` | β€” | decrement, branch on CTR only | +| `0100z` | no | β€” | `Β¬CR[BI]` | branch if CR[BI] false | +| `0101z` | no | β€” | `CR[BI]` | branch if CR[BI] true | +| `011at` | no | β€” | β€” | branch always (`z` and `t` are prediction hints) | +| `1z00z` | yes | `CTR β‰  0` | β€” | decrement, branch if CTR β‰  0 | +| `1z01z` | yes | `CTR = 0` | β€” | decrement, branch if CTR = 0 | +| `1z1zz` | no | β€” | β€” | branch always | + +Bit **`BO[0]` = 1** disables the CR test; **`BO[2]` = 1** disables the CTR decrement/test. `BO[1]` and `BO[3]` select the polarity of each test. `BO[4]` is a branch-prediction hint (0 = not taken, 1 = taken; ignored on the Xenon in most cases). + +The most common bclr instance in Xbox 360 disassembly is `BO = 0b10100` β†’ `blr` (branch always to LR), the function epilogue. `BO = 0b01100, BI = 2` β†’ `beqlr` (return if `cr0.EQ`), also common. + +## Special Cases & Edge Conditions + +- **LR alignment mask.** The target address is `LR & ~3` β€” the low 2 bits are cleared. This silently ignores a misaligned LR; incoming code should always produce 4-byte-aligned LR values. +- **Ordering of CTR decrement and branch.** The CTR is decremented **first**, then compared to zero **after** the decrement. So after `bdnz` at `CTR = 1`, the CTR becomes `0` and the branch is not taken. +- **Self-referential LR write.** `bclrl` writes `LR ← CIA + 4` **before** reading `LR` to set `NIA`. Per the PowerISA, `bclrl` reads the *old* `LR` for the branch target and writes the *new* `LR` with the return address, atomically from software's perspective. Xenia implements it this way (`next_pc` captured first, then `lr` written). +- **Branch prediction hints (`BO[4]`).** The Xenon does static prediction on the basis of these hints, but behaviour is architecturally unobservable. Translators may ignore them. +- **Synchronisation.** `bclr` is **context-synchronising** (hence the `sync` flag in xenia's XML). Translators must ensure side-effecting instructions preceding the branch have committed β€” trivial in a sequential C translation but relevant for JIT backends. +- **xenia's `LR_HALT_SENTINEL`.** Xenia sets `LR` to `0xBCBCBCBC` at thread start; when the top-level guest function returns via `blr`, the interpreter loop halts cleanly. Translators replicating guest behaviour don't need this β€” but if you generate a test harness, the sentinel is a convenient "function returned" signal. + +## Related Instructions + +- [`bcctrx`](bcctrx.md) β€” branch conditional to **CTR** (used by indirect calls / vtables). +- [`bcx`](bcx.md) β€” branch conditional to an immediate displacement (D-form). +- [`bx`](bx.md) β€” unconditional branch (I-form). +- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) β€” set/get LR via `mtspr 8, …` / `mfspr …, 8`. +- [`sc`](sc.md) β€” system call (alternative control-flow exit). + +## Simplified Mnemonics + +Assemblers fold common `BO`/`BI` patterns to single mnemonics: + +| Simplified | Expansion | +| --- | --- | +| `blr` | `bclr BO=0b10100, BI=0` β€” branch always to LR | +| `blrl` | `bclrl BO=0b10100, BI=0` β€” branch always to LR with link (tail-call trampoline) | +| `beqlr crN` | `bclr BO=0b01100, BI=4Β·N+2` β€” return if `crN.EQ` | +| `bnelr crN` | `bclr BO=0b00100, BI=4Β·N+2` β€” return if `crN.NE` | +| `bltlr crN` | `bclr BO=0b01100, BI=4Β·N+0` β€” return if `crN.LT` | +| `bgelr crN` | `bclr BO=0b00100, BI=4Β·N+0` β€” return if `crN.GE` | +| `bgtlr crN` | `bclr BO=0b01100, BI=4Β·N+1` β€” return if `crN.GT` | +| `blelr crN` | `bclr BO=0b00100, BI=4Β·N+1` β€” return if `crN.LE` | + +Xbox 360 disassemblers almost always emit the simplified form; the translation agent should learn to recognise them. + +## IBM Reference + +- [AIX 7.3 β€” `bclr` (Branch Conditional to Link Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bclr-bclrl-branch-conditional-link-register-instruction) +- [AIX 7.3 β€” Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified) diff --git a/tools/ppc-manual/branch/bcx.md b/tools/ppc-manual/branch/bcx.md new file mode 100644 index 00000000..0fc880e8 --- /dev/null +++ b/tools/ppc-manual/branch/bcx.md @@ -0,0 +1,192 @@ +# `bcx` β€” Branch Conditional + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [B](../forms/B.md) Β· **Opcode:** `0x40000000` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `bc` | `bcx` | β€” | Branch Conditional | +| `bcl` | `bcx` | LK=1 | Branch Conditional | + +## Syntax + +```asm +bc[LK][AA] [BO], [BI], [ADDR] +``` + +## Encoding + +### `bcx` β€” form `B` + +- **Opcode word:** `0x40000000` +- **Primary opcode (bits 0–5):** `16` +- **Extended opcode:** β€” +- **Synchronising:** yes + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `BO` | branch options | +| 11–15 | `BI` | CR bit to test | +| 16–29 | `BD` | signed 14-bit word-offset target | +| 30 | `AA` | absolute-address flag | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `LK` | bcx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. | +| `AA` | bcx: read | Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address. | +| `BO` | bcx: read | 5-bit branch options β€” selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. | +| `BI` | bcx: read | CR bit index (0–31) selected by BO's condition test. | +| `ADDR` | bcx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). | +| `CR` | bcx: read (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CTR` | bcx: read (conditional); bcx: write (conditional) | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. | +| `LR` | bcx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. | + +## Register Effects + +### `bcx` + +- **Reads (always):** `LK`, `AA`, `BO`, `BI`, `ADDR` +- **Reads (conditional):** `CR`, `CTR` +- **Writes (always):** _none_ +- **Writes (conditional):** `CTR`, `LR` + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +if Β¬BO[2] then CTR <- CTR βˆ’ 1 +ctr_ok <- BO[2] | ((CTR β‰  0) XOR BO[3]) +cond_ok <- BO[0] | (CR[BI] ≑ BO[1]) +if ctr_ok & cond_ok then NIA <- CIA + EXTS(BD || 0b00) (AA=0) + EXTS(BD || 0b00) (AA=1) +if LK then LR <- CIA + 4 +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`bcx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="bcx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:173`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L173) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:340`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L340) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:908-938`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L908-L938) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::bcx => { + let bo = instr.bo(); + let bi = instr.bi(); + + // Decrement CTR if needed + if bo & 0b00100 == 0 { + ctx.ctr = ctx.ctr.wrapping_sub(1); + } + + let ctr_ok = (bo & 0b00100) != 0 + || (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0)); + let cond_ok = (bo & 0b10000) != 0 + || (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0)); + + if ctr_ok && cond_ok { + let target = if instr.aa() { + instr.bd() as u32 + } else { + ctx.pc.wrapping_add(instr.bd() as u32) + }; + if instr.lk() { + ctx.lr = (ctx.pc + 4) as u64; + } + ctx.pc = target; + } else { + if instr.lk() { + ctx.lr = (ctx.pc + 4) as u64; + } + ctx.pc += 4; + } + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **14-bit signed displacement.** `BD` is a 14-bit signed word-count, scaled by 4 β€” yielding a Β±32 KiB byte range (`βˆ’2^15 … +2^15 βˆ’ 4`). For longer-range conditional control flow, compilers emit a short `bc` over an unconditional `b`. +- **CTR decrement happens before the test.** `BO[2]=0` decrements CTR *first*, then `ctr_ok` evaluates against the new value. The classic `bdnz loop` loops `N` times when CTR is initialised to `N`. +- **LR write is unconditional in xenia.** Xenia writes `LR ← CIA + 4` whenever `LK=1`, even on the not-taken path. This matches the PowerISA: `bcl` always sets `LR` regardless of branch outcome β€” exploited by `bcl 20, 31, $+4` as a self-PC capture (PIC trick). +- **`BO` encoding** β€” see `bclrx.md` for the full 5-bit table. `bcx` supports the full set, including CTR-only branches (`bdnz`, `bdz`). +- **Branch hint encoding.** PPC overloads `BO[4]` as a static prediction hint: 0 = "predict not taken", 1 = "predict taken". The Xenon honours it for forward branches; backwards conditional branches are predicted taken regardless. Translators may ignore the hint. +- **Synchronisation.** Marked `sync` β€” like all branches, `bcx` is context-synchronising. Trivial in interpretation; matters for JIT reorder windows. +- **No `Rc`.** B-form has no record bit; the apparent `Rc` operand-table entry under "Status-Register Effects" is N/A here. + +### BO/BI encoding (compact table) + +| BO | Effect | Common simplified | +| --- | --- | --- | +| `0000z` | dec CTR, branch if `CTRβ‰ 0` & `Β¬CR[BI]` | `bdnzf BI, addr` | +| `0001z` | dec CTR, branch if `CTR=0` & `Β¬CR[BI]` | `bdzf BI, addr` | +| `0010y` | dec CTR, branch if `CTRβ‰ 0` | `bdnz addr` | +| `0011y` | dec CTR, branch if `CTR=0` | `bdz addr` | +| `0100z` | branch if `Β¬CR[BI]` | `bf BI, addr` (or `bne`/`bge`/...) | +| `0101z` | branch if `CR[BI]` | `bt BI, addr` (or `beq`/`blt`/...) | +| `1z1zz` | branch always | `b addr` (prefer plain `b` though) | + +Bit `z` is the prediction hint (`0` = not taken, `1` = taken). + +## Related Instructions + +- [`bx`](bx.md) β€” unconditional displacement branch (24-bit range). +- [`bclrx`](bclrx.md) β€” branch conditional to LR (function return). +- [`bcctrx`](bcctrx.md) β€” branch conditional to CTR (indirect call / dispatch). +- [`crand`](../control/crand.md), [`cror`](../control/cror.md), … β€” combine multiple CR bits before a single `bc`. +- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) β€” set/get loop counter for `bdnz`/`bdz`. +- [`sc`](sc.md) β€” alternative control-flow exit. + +### Simplified Mnemonics + +The `bc` mnemonic is rarely written directly; assemblers fold most uses into form-specific aliases: + +| Simplified | Expansion | +| --- | --- | +| `beq crN, addr` | `bc 0b01100, 4Β·N+2, addr` β€” branch if `crN.EQ` | +| `bne crN, addr` | `bc 0b00100, 4Β·N+2, addr` β€” branch if `crN.NE` | +| `blt crN, addr` | `bc 0b01100, 4Β·N+0, addr` β€” branch if `crN.LT` | +| `bge crN, addr` | `bc 0b00100, 4Β·N+0, addr` β€” branch if `crN.GE` | +| `bgt crN, addr` | `bc 0b01100, 4Β·N+1, addr` β€” branch if `crN.GT` | +| `ble crN, addr` | `bc 0b00100, 4Β·N+1, addr` β€” branch if `crN.LE` | +| `bso crN, addr` | `bc 0b01100, 4Β·N+3, addr` β€” branch on summary overflow | +| `bns crN, addr` | `bc 0b00100, 4Β·N+3, addr` β€” branch on no SO | +| `bdnz addr` | `bc 0b10000, 0, addr` β€” decrement CTR, branch if non-zero | +| `bdz addr` | `bc 0b10010, 0, addr` β€” decrement CTR, branch if zero | +| `bdnzt BI, addr` | combined CTR + CR test (rare) | + +When `crN` is omitted in disassembly, `cr0` is implied. + +## IBM Reference + +- [AIX 7.3 β€” `bc` (Branch Conditional)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bc-branch-conditional-instruction) +- [AIX 7.3 β€” Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified) diff --git a/tools/ppc-manual/branch/bx.md b/tools/ppc-manual/branch/bx.md new file mode 100644 index 00000000..fd781a59 --- /dev/null +++ b/tools/ppc-manual/branch/bx.md @@ -0,0 +1,133 @@ +# `bx` β€” Branch + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [I](../forms/I.md) Β· **Opcode:** `0x48000000` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `b` | `bx` | β€” | Branch | +| `bl` | `bx` | LK=1 | Branch | + +## Syntax + +```asm +b[LK][AA] [ADDR] +``` + +## Encoding + +### `bx` β€” form `I` + +- **Opcode word:** `0x48000000` +- **Primary opcode (bits 0–5):** `18` +- **Extended opcode:** β€” +- **Synchronising:** yes + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–29 | `LI` | signed 24-bit word-offset target | +| 30 | `AA` | absolute-address flag | +| 31 | `LK` | link flag (bl/ba/bla) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `LK` | bx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. | +| `AA` | bx: read | Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address. | +| `ADDR` | bx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). | +| `LR` | bx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. | + +## Register Effects + +### `bx` + +- **Reads (always):** `LK`, `AA`, `ADDR` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** `LR` + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +NIA <- (CIA + EXTS(LI || 0b00)) if AA=0 + <- EXTS(LI || 0b00) if AA=1 +if LK then LR <- CIA + 4 +``` + +## C Translation Example + +```c +/* b / bl / ba / bla β€” unconditional branch (I-form, primary 18) */ +int32_t li = (int32_t)(insn.LI << 2); /* sign-extended word-offset */ +uint32_t target = insn.AA ? (uint32_t)li : (uint32_t)(pc + li); +uint32_t next = pc + 4; +if (insn.LK) lr = next; /* bl / bla save return addr */ +pc = target; +``` + +## Implementation References + +**`bx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="bx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:154`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L154) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:342`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L342) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:897-907`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L897-L907) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::bx => { + let target = if instr.aa() { + instr.li() as u32 + } else { + ctx.pc.wrapping_add(instr.li() as u32) + }; + if instr.lk() { + ctx.lr = (ctx.pc + 4) as u64; + } + ctx.pc = target; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **24-bit word-aligned target.** `LI` is a 24-bit signed word-count. Hardware concatenates `LI || 0b00` (adds the implicit two low zero bits) and sign-extends to 64 bits before using it as an address. The displacement range is therefore Β±32 MiB in bytes (`βˆ’2^25 … +2^25 βˆ’ 4`). +- **Four mnemonics, one opcode.** The four runtime variants selected by `AA` and `LK`: + - `b` β€” `AA = 0, LK = 0` β€” PC-relative, no link. + - `bl` β€” `AA = 0, LK = 1` β€” PC-relative, `LR = CIA + 4` (the ubiquitous function-call primitive). + - `ba` β€” `AA = 1, LK = 0` β€” absolute, no link. + - `bla` β€” `AA = 1, LK = 1` β€” absolute, link. + Xbox 360 code almost exclusively uses `b` and `bl`; `ba` / `bla` appear only in kernel / firmware stubs. +- **Target alignment.** `LI` is scaled by 4, so all targets are 4-byte aligned by construction. There is no low-bit encoding of ARM-style Thumb β€” PPC has one instruction width. +- **LR write is *before* the branch.** In `bl`, `LR` receives `CIA + 4` (the address of the instruction *after* the branch) before execution transfers to the target. Nested calls naturally overwrite LR; callees must spill it ([`mflr`](../control/mfspr.md) + `std`) before making their own `bl`. +- **Indirect tail calls.** A tail call to an indirect target is encoded as `mtctr` + `bctr` (see [`bcctrx`](bcctrx.md)), not `bx` β€” `bx` has no register-based form. +- **No condition test.** Use [`bcx`](bcx.md) for conditional displacement branches or [`bclrx`](bclrx.md) / [`bcctrx`](bcctrx.md) for conditional LR/CTR jumps. +- **Speculative execution.** The Xenon fetches past `bx`; translators that mask control flow must treat the target as a single-destination control transfer. + +## Related Instructions + +- [`bcx`](bcx.md) β€” conditional displacement branch (B-form, Β±32 KiB range). +- [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) β€” branch to LR / CTR, conditional and unconditional. +- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) β€” LR save/restore for nested `bl` calls. +- [`sc`](sc.md) β€” system call; an alternative control-flow exit to the kernel. + +## Simplified Mnemonics + +Assemblers emit `b`, `bl`, `ba`, `bla` for the four runtime combinations. There is no further simplification. + +## IBM Reference + +- [AIX 7.3 β€” `b` (Branch)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-b-branch-instruction) +- [AIX 7.3 β€” `bl` (Branch with Link, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified) diff --git a/tools/ppc-manual/branch/sc.md b/tools/ppc-manual/branch/sc.md new file mode 100644 index 00000000..1ec660a3 --- /dev/null +++ b/tools/ppc-manual/branch/sc.md @@ -0,0 +1,129 @@ +# `sc` β€” System Call + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [SC](../forms/SC.md) Β· **Opcode:** `0x44000002` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `sc` | `sc` | β€” | System Call | + +## Syntax + +```asm +sc [LEV] +``` + +## Encoding + +### `sc` β€” form `SC` + +- **Opcode word:** `0x44000002` +- **Primary opcode (bits 0–5):** `17` +- **Extended opcode:** β€” +- **Synchronising:** yes + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (17) | +| 6–19 | `β€”` | reserved | +| 20–26 | `LEV` | exception level | +| 27–29 | `β€”` | reserved | +| 30 | `1` | fixed 1 | +| 31 | `β€”` | reserved | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `LEV` | sc: read | System-call exception level (for `sc`). | + +## Register Effects + +### `sc` + +- **Reads (always):** `LEV` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +system_call_exception(LEV) +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`sc`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="sc"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:455`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L455) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:63`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L63) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:341`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L341) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:984-997`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L984-L997) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::sc => { + // PPCBUG-064: log non-zero LEV (`sc 2` is the Xbox 360 hypervisor-call + // convention; canary dispatches it to a different handler than `sc 0`). + // Routing LEV=2 requires a StepResult variant extension; deferred. + let lev = (instr.raw >> 5) & 0x7F; + if lev != 0 { + tracing::warn!( + "sc with LEV={} at {:#010x}: dispatched as plain SystemCall (HVcall routing not implemented)", + lev, ctx.pc + ); + } + ctx.pc += 4; + return StepResult::SystemCall; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`LEV` field β€” kernel vs hypervisor.** The 7-bit `LEV` operand selects the privilege level of the syscall: + - `LEV = 0` β€” supervisor (kernel) syscall. Standard application β†’ kernel transition; targets the `0xC00` system-call vector. + - `LEV = 1` β€” reserved. + - `LEV = 2` β€” **hypervisor syscall** (`HVcall`). On the Xenon, `sc 2` traps to the Xbox 360 hypervisor; this is how the kernel itself talks to the supervisor below it (e.g., for security operations, encrypted-memory accesses, page table updates). +- **`sc` as written by titles.** Almost all guest game code uses `LEV = 0` to call `XboxKrnl.exe`. Game disassembly will show large jump tables of small thunks each ending in `li r0, syscall_no; sc; blr`. +- **No condition or status side effects.** `sc` updates *no* general-purpose register on entry β€” neither LR nor CR. The kernel sees the GPR/FPR snapshot as-is and reads the syscall number out of `r0` (Xbox 360 ABI convention, not architectural). +- **Return path.** Hardware returns from `sc` via [`rfid`](../control/mtmsrd.md)-class instructions in the kernel handler; from the application's perspective execution resumes at `CIA + 4`. Xenia's interpreter realises this by simply pre-incrementing `pc` then returning `StepResult::SystemCall` β€” the host driver dispatches the syscall and re-enters the loop. +- **xenia divergence vs hardware.** xenia-rs *does not* model the `0xC00` exception vector or save SRR0/SRR1; the `LEV` operand is currently ignored. All `sc` instructions are treated identically and serviced by the host. This is sufficient because Xbox 360 titles don't observe SRR registers and the host kernel is implemented natively. +- **Synchronisation.** Marked `sync` in xenia's XML β€” `sc` is context-synchronising (hardware completes all prior instructions before raising the exception). JITs must flush pending state before emitting the host call. +- **Reserved bits.** Bit 30 is fixed `1`; bits 6–19 and 27–29 are reserved (must be 0). The 1-bit field at position 30 distinguishes the `sc` encoding from `scv` (later PowerISA addition, not present on the Xenon). + +## Related Instructions + +- [`bx`](bx.md), [`bcx`](bcx.md), [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) β€” ordinary control flow alternatives. +- [`tw`](tw.md), [`twi`](twi.md), [`td`](td.md), [`tdi`](tdi.md) β€” synchronous *trap* exceptions; another way to enter the kernel. +- [`mtmsr`](../control/mtmsr.md), [`mtmsrd`](../control/mtmsrd.md) β€” machine-state changes used by the kernel's `sc` handler on return (`rfid`/`hrfid` chain not separately documented in this manual). +- [`isync`](../control/mtmsr.md) β€” context-synchronising sibling; `sc` itself implies an isync-like fence. + +## IBM Reference + +- [AIX 7.3 β€” `sc` (System Call)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sc-system-call-instruction) +- PowerISA v2.07B, Book III Β§7 β€” System Linkage interrupt definitions and `LEV` field semantics. diff --git a/tools/ppc-manual/branch/td.md b/tools/ppc-manual/branch/td.md new file mode 100644 index 00000000..8a23ccd2 --- /dev/null +++ b/tools/ppc-manual/branch/td.md @@ -0,0 +1,184 @@ +# `td` β€” Trap Doubleword + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000088` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `td` | `td` | β€” | Trap Doubleword | + +## Syntax + +```asm +td [TO], [RA], [RB] +``` + +## Encoding + +### `td` β€” form `X` + +- **Opcode word:** `0x7c000088` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `68` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RT/FRT/VRT` | destination | +| 11–15 | `RA/FRA/VRA` | source A | +| 16–20 | `RB/FRB/VRB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `TO` | td: read | Trap-on condition mask (5 bits) β€” LT, GT, EQ, LGT, LLT bits. | +| `RA` | td: read | Source GPR (`r0`–`r31`). | +| `RB` | td: read | Source GPR. | + +## Register Effects + +### `td` + +- **Reads (always):** `TO`, `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +; Pseudocode derives directly from the xenia-rs interpreter +; arm (see Implementation References). Operation semantics: +; - Read source operands from the fields listed under Operands. +; - Apply the arithmetic / logical / memory action described +; in the Description field above. +; - Write results to the destination register(s); update any +; status bits enumerated under Status-Register Effects. +; Consult the IBM AIX reference link under IBM Reference for +; canonical PPC-style pseudocode where xenia's expression is +; terse. +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`td`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="td"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:552`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L552) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:769`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L769) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => { + // PPCBUG-063: save CIA before incrementing so a trap handler reads + // the faulting instruction address, not CIA+4. + // PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360 + // typed-trap convention used by the CRT/kernel for C++ exception + // class dispatch). The audit notes this is relevant to the Sylpheed + // throw investigation; routing the type code via a payload requires + // a StepResult enum extension that's deferred for now. + let trap_pc = ctx.pc; + let a = ctx.gpr[instr.ra()]; + let b = match instr.opcode { + PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64, + _ => ctx.gpr[instr.rb()], + }; + let width = match instr.opcode { + PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word, + _ => trap::TrapWidth::Doubleword, + }; + let fired = trap::evaluate(instr.to(), a, b, width); + if fired { + let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi) + && instr.to() == 31 && instr.ra() == 0 { + Some(instr.simm16() as u16) + } else { None }; + tracing::warn!( + "Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}", + trap_pc, instr.opcode, instr.to(), a, b, + typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t)) + ); + // Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery + // can read the faulting instruction address from ctx.pc. + return StepResult::Trap; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`TO` mask encoding (5 bits, MSB-first).** Each bit selects one comparison; the trap fires if *any* selected condition is true. Both operands are treated as 64-bit doublewords: + + | Bit | Mnemonic | Triggered when | + | --- | --- | --- | + | `TO[0]` (16) | LT | `(int64) RA < (int64) RB` | + | `TO[1]` (8) | GT | `(int64) RA > (int64) RB` | + | `TO[2]` (4) | EQ | `RA == RB` | + | `TO[3]` (2) | LGT | `(uint64) RA < (uint64) RB` (logical less) | + | `TO[4]` (1) | LLT | `(uint64) RA > (uint64) RB` (logical greater β€” historical naming) | + +- **`TO = 31` is unconditional trap.** All five bits set β‡’ the trap fires regardless of operand values; the simplified mnemonic `trap` is `tw 31, 0, 0` for words and **`td 31, 0, 0`** for doublewords. The PowerISA also uses `tdi 31, 0, 0` (or `twi`) as a debugger break. +- **64-bit comparison.** Unlike [`tw`](tw.md), `td` always compares the full 64-bit GPRs. On the Xenon (64-bit) this is meaningful; PPC32 implementations don't have `td`. +- **No register effects.** Only the side effect is the trap. No CR/LR/CTR/XER updates. +- **Hardware behaviour.** When the trap fires, hardware raises a Program interrupt with `SRR1[TRAP]` set and vectors to `0x700`. The Xbox 360 hypervisor / kernel handles it (assertion failure, debugger trap, etc.). +- **xenia simplification.** xenia-rs collapses all four trap variants (`td`, `tdi`, `tw`, `twi`) into one match arm that *unconditionally* logs and returns `StepResult::Trap` β€” it does **not** evaluate `TO` against the operands. This is a material divergence from the spec: in xenia every trap fires even if the condition is false. Real Xenon code rarely uses non-trivial `TO` masks (typical use is the unconditional `trap` for `__assert` / debugger break), so the divergence is normally invisible. +- **Distinguishing assert vs. break.** Compilers commonly emit `tdne r3, r3` (impossible) or `tdi 0, r0, 0` patterns that *cannot* trap as inert markers. Xenia's blanket trap would mis-fire on these β€” a small known bug; track it if you see spurious traps. + +## Related Instructions + +- [`tdi`](tdi.md) β€” same condition test against a 16-bit signed immediate (D-form). +- [`tw`](tw.md) / [`twi`](twi.md) β€” 32-bit (word) variants for comparing low halves of GPRs. +- [`sc`](sc.md) β€” synchronous kernel entry via system-call exception (different vector, different intent). +- [`mtmsr`](../control/mtmsr.md) β€” kernel returns from the trap handler via `rfid`-family instructions. + +### Simplified Mnemonics + +| Simplified | Expansion | Triggered when | +| --- | --- | --- | +| `td RA, RB` (=`tdu`) | `td 31, RA, RB` | unconditional trap | +| `tdeq RA, RB` | `td 4, RA, RB` | `RA == RB` | +| `tdne RA, RB` | `td 24, RA, RB` | `RA != RB` | +| `tdlt RA, RB` | `td 16, RA, RB` | signed less than | +| `tdle RA, RB` | `td 20, RA, RB` | signed less or equal | +| `tdgt RA, RB` | `td 8, RA, RB` | signed greater than | +| `tdge RA, RB` | `td 12, RA, RB` | signed greater or equal | +| `tdllt RA, RB` | `td 2, RA, RB` | unsigned less than | +| `tdlge RA, RB` | `td 5, RA, RB` | unsigned greater or equal | +| `tdlgt RA, RB` | `td 1, RA, RB` | unsigned greater than | +| `tdlle RA, RB` | `td 6, RA, RB` | unsigned less or equal | + +## IBM Reference + +- [AIX 7.3 β€” `td` (Trap Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-td-trap-doubleword-instruction) +- [AIX 7.3 β€” Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified) +- PowerISA v2.07B, Book I Β§3.3.11 β€” fixed-point trap instructions (`TO` semantics). diff --git a/tools/ppc-manual/branch/tdi.md b/tools/ppc-manual/branch/tdi.md new file mode 100644 index 00000000..77f10da3 --- /dev/null +++ b/tools/ppc-manual/branch/tdi.md @@ -0,0 +1,173 @@ +# `tdi` β€” Trap Doubleword Immediate + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x08000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `tdi` | `tdi` | β€” | Trap Doubleword Immediate | + +## Syntax + +```asm +tdi [TO], [RA], [SIMM] +``` + +## Encoding + +### `tdi` β€” form `D` + +- **Opcode word:** `0x08000000` +- **Primary opcode (bits 0–5):** `2` +- **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 | +| --- | --- | --- | +| `TO` | tdi: read | Trap-on condition mask (5 bits) β€” LT, GT, EQ, LGT, LLT bits. | +| `RA` | tdi: read | Source GPR (`r0`–`r31`). | +| `SIMM` | tdi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | + +## Register Effects + +### `tdi` + +- **Reads (always):** `TO`, `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +; Pseudocode derives directly from the xenia-rs interpreter +; arm (see Implementation References). Operation semantics: +; - Read source operands from the fields listed under Operands. +; - Apply the arithmetic / logical / memory action described +; in the Description field above. +; - Write results to the destination register(s); update any +; status bits enumerated under Status-Register Effects. +; Consult the IBM AIX reference link under IBM Reference for +; canonical PPC-style pseudocode where xenia's expression is +; terse. +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`tdi`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="tdi"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:568`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L568) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:327`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L327) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => { + // PPCBUG-063: save CIA before incrementing so a trap handler reads + // the faulting instruction address, not CIA+4. + // PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360 + // typed-trap convention used by the CRT/kernel for C++ exception + // class dispatch). The audit notes this is relevant to the Sylpheed + // throw investigation; routing the type code via a payload requires + // a StepResult enum extension that's deferred for now. + let trap_pc = ctx.pc; + let a = ctx.gpr[instr.ra()]; + let b = match instr.opcode { + PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64, + _ => ctx.gpr[instr.rb()], + }; + let width = match instr.opcode { + PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word, + _ => trap::TrapWidth::Doubleword, + }; + let fired = trap::evaluate(instr.to(), a, b, width); + if fired { + let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi) + && instr.to() == 31 && instr.ra() == 0 { + Some(instr.simm16() as u16) + } else { None }; + tracing::warn!( + "Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}", + trap_pc, instr.opcode, instr.to(), a, b, + typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t)) + ); + // Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery + // can read the faulting instruction address from ctx.pc. + return StepResult::Trap; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Immediate is sign-extended.** `SIMM` is treated as a 16-bit signed value, then sign-extended to 64 bits before comparison. To trap against a small unsigned constant, the same encoding works because both signed and unsigned interpretations agree for `SIMM ∈ [0, 0x7FFF]`. +- **`TO` mask.** Identical bit layout to [`td`](td.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true. +- **`TO = 31` is unconditional.** `tdi 31, 0, 0` is a debugger / assert trap. Compilers sometimes use it as a "should not reach" marker. +- **64-bit comparison only.** Unlike [`twi`](twi.md), `tdi` always compares the full 64-bit GPR β€” it has no PPC32 analogue. The Xenon's PPC64 mode makes this meaningful. +- **No register effects.** Pure side effect on success: Program interrupt β†’ vector `0x700` with `SRR1[TRAP]=1`. +- **xenia simplification.** xenia-rs unconditionally treats `tdi` as a fired trap, regardless of `TO`/`RA`/`SIMM` values. This diverges from the spec β€” real hardware would silently fall through when no `TO` bit's condition holds. Most title code uses only the unconditional `trap` form, so the divergence is normally invisible; non-firing assertion patterns (e.g. `tdi 0, r0, 0`) will mis-fire under xenia. +- **Reserved bits.** Bits 6–10 carry the `TO` field; there is no `Rc` / `OE` on D-form trap immediates. + +## Related Instructions + +- [`td`](td.md) β€” register-register doubleword trap (X-form). +- [`twi`](twi.md) / [`tw`](tw.md) β€” 32-bit-comparison siblings. +- [`sc`](sc.md) β€” kernel-entry counterpart via system call exception. +- [`mtmsrd`](mtmsrd.md) (control category) β€” kernel `rfid`-style return path after handling. + +### Simplified Mnemonics + +| Simplified | Expansion | Triggered when | +| --- | --- | --- | +| `tdi RA, value` | `tdi 31, RA, value` | unconditional trap | +| `tdeqi RA, value` | `tdi 4, RA, value` | `RA == EXTS(value)` | +| `tdnei RA, value` | `tdi 24, RA, value` | `RA != EXTS(value)` | +| `tdlti RA, value` | `tdi 16, RA, value` | signed less than | +| `tdlei RA, value` | `tdi 20, RA, value` | signed less or equal | +| `tdgti RA, value` | `tdi 8, RA, value` | signed greater than | +| `tdgei RA, value` | `tdi 12, RA, value` | signed greater or equal | +| `tdllti RA, value` | `tdi 2, RA, value` | unsigned less than | +| `tdlgei RA, value` | `tdi 5, RA, value` | unsigned greater or equal | +| `tdlgti RA, value` | `tdi 1, RA, value` | unsigned greater than | +| `tdllei RA, value` | `tdi 6, RA, value` | unsigned less or equal | + +## IBM Reference + +- [AIX 7.3 β€” `tdi` (Trap Doubleword Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tdi-trap-doubleword-immediate-instruction) +- [AIX 7.3 β€” Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified) +- PowerISA v2.07B, Book I Β§3.3.11 β€” fixed-point trap instructions. diff --git a/tools/ppc-manual/branch/tw.md b/tools/ppc-manual/branch/tw.md new file mode 100644 index 00000000..60b62813 --- /dev/null +++ b/tools/ppc-manual/branch/tw.md @@ -0,0 +1,184 @@ +# `tw` β€” Trap Word + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000008` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `tw` | `tw` | β€” | Trap Word | + +## Syntax + +```asm +tw [TO], [RA], [RB] +``` + +## Encoding + +### `tw` β€” form `X` + +- **Opcode word:** `0x7c000008` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `4` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RT/FRT/VRT` | destination | +| 11–15 | `RA/FRA/VRA` | source A | +| 16–20 | `RB/FRB/VRB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `Rc` | record-form flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `TO` | tw: read | Trap-on condition mask (5 bits) β€” LT, GT, EQ, LGT, LLT bits. | +| `RA` | tw: read | Source GPR (`r0`–`r31`). | +| `RB` | tw: read | Source GPR. | + +## Register Effects + +### `tw` + +- **Reads (always):** `TO`, `RA`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +; Pseudocode derives directly from the xenia-rs interpreter +; arm (see Implementation References). Operation semantics: +; - Read source operands from the fields listed under Operands. +; - Apply the arithmetic / logical / memory action described +; in the Description field above. +; - Write results to the destination register(s); update any +; status bits enumerated under Status-Register Effects. +; Consult the IBM AIX reference link under IBM Reference for +; canonical PPC-style pseudocode where xenia's expression is +; terse. +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`tw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="tw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:583`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L583) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:750`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L750) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => { + // PPCBUG-063: save CIA before incrementing so a trap handler reads + // the faulting instruction address, not CIA+4. + // PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360 + // typed-trap convention used by the CRT/kernel for C++ exception + // class dispatch). The audit notes this is relevant to the Sylpheed + // throw investigation; routing the type code via a payload requires + // a StepResult enum extension that's deferred for now. + let trap_pc = ctx.pc; + let a = ctx.gpr[instr.ra()]; + let b = match instr.opcode { + PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64, + _ => ctx.gpr[instr.rb()], + }; + let width = match instr.opcode { + PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word, + _ => trap::TrapWidth::Doubleword, + }; + let fired = trap::evaluate(instr.to(), a, b, width); + if fired { + let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi) + && instr.to() == 31 && instr.ra() == 0 { + Some(instr.simm16() as u16) + } else { None }; + tracing::warn!( + "Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}", + trap_pc, instr.opcode, instr.to(), a, b, + typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t)) + ); + // Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery + // can read the faulting instruction address from ctx.pc. + return StepResult::Trap; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit comparison only.** `tw` compares the *low 32 bits* of `RA` and `RB`. The high halves of the 64-bit GPRs on the Xenon are ignored. Use [`td`](td.md) for full 64-bit comparisons. +- **`TO` mask (5 bits, MSB-first).** Same encoding as the doubleword form: + + | Bit | Mnemonic | Triggered when | + | --- | --- | --- | + | `TO[0]` (16) | LT | `(int32) RA < (int32) RB` | + | `TO[1]` (8) | GT | `(int32) RA > (int32) RB` | + | `TO[2]` (4) | EQ | `(uint32) RA == (uint32) RB` | + | `TO[3]` (2) | LGT | `(uint32) RA < (uint32) RB` | + | `TO[4]` (1) | LLT | `(uint32) RA > (uint32) RB` | + +- **`tw 31, 0, 0` is `trap`.** The simplified mnemonic `trap` expands to `tw 31, r0, r0` β€” all five `TO` bits set β‡’ unconditional trap. Compilers and the kernel use this as the assertion / debugger break primitive; it appears as `0x7FE00008` in raw bytes. +- **Conditional asserts.** GCC's `__builtin_trap` and MSVC's `__assert` macros emit `tw` variants like `twge`/`twlt` to fault on bound-check failures. +- **No register effects.** Side effect only: Program interrupt (`0x700`) with `SRR1[TRAP]=1`. +- **xenia simplification.** xenia-rs collapses `td/tdi/tw/twi` into a single arm that *unconditionally* logs and returns `StepResult::Trap` β€” the `TO` operand is **not evaluated**. Real hardware would silently fall through when no `TO` bit's condition holds. In practice titles use mostly the unconditional `trap`, so the divergence rarely manifests, but inert-marker patterns like `tw 0, r0, r0` will fire under xenia. +- **Inert encoding.** `tw 0, r0, r0` (no `TO` bits set) can never trap on real hardware. It encodes as `0x7C000008` β€” sometimes used as a structured-NOP marker. Watch for it in xenia traces. + +## Related Instructions + +- [`twi`](twi.md) β€” same 32-bit comparison against a 16-bit signed immediate. +- [`td`](td.md) / [`tdi`](tdi.md) β€” 64-bit (doubleword) variants. +- [`sc`](sc.md) β€” kernel entry via system-call exception (different vector). +- [`mtmsr`](../control/mtmsr.md) β€” kernel returns from `0x700` via `rfid`. + +### Simplified Mnemonics + +| Simplified | Expansion | Triggered when | +| --- | --- | --- | +| `trap` | `tw 31, 0, 0` | unconditional | +| `tweq RA, RB` | `tw 4, RA, RB` | `RA == RB` | +| `twne RA, RB` | `tw 24, RA, RB` | `RA != RB` | +| `twlt RA, RB` | `tw 16, RA, RB` | signed less than | +| `twle RA, RB` | `tw 20, RA, RB` | signed less or equal | +| `twgt RA, RB` | `tw 8, RA, RB` | signed greater than | +| `twge RA, RB` | `tw 12, RA, RB` | signed greater or equal | +| `twllt RA, RB` | `tw 2, RA, RB` | unsigned less than | +| `twlge RA, RB` | `tw 5, RA, RB` | unsigned greater or equal | +| `twlgt RA, RB` | `tw 1, RA, RB` | unsigned greater than | +| `twlle RA, RB` | `tw 6, RA, RB` | unsigned less or equal | + +## IBM Reference + +- [AIX 7.3 β€” `tw` (Trap Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tw-trap-word-instruction) +- [AIX 7.3 β€” Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified) +- PowerISA v2.07B, Book I Β§3.3.11 β€” fixed-point trap instructions. diff --git a/tools/ppc-manual/branch/twi.md b/tools/ppc-manual/branch/twi.md new file mode 100644 index 00000000..7a793fba --- /dev/null +++ b/tools/ppc-manual/branch/twi.md @@ -0,0 +1,172 @@ +# `twi` β€” Trap Word Immediate + +> **Category:** [Branch & System](../categories/branch.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x0c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `twi` | `twi` | β€” | Trap Word Immediate | + +## Syntax + +```asm +tw [TO], [RA], [SIMM] +``` + +## Encoding + +### `twi` β€” form `D` + +- **Opcode word:** `0x0c000000` +- **Primary opcode (bits 0–5):** `3` +- **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 | +| --- | --- | --- | +| `TO` | twi: read | Trap-on condition mask (5 bits) β€” LT, GT, EQ, LGT, LLT bits. | +| `RA` | twi: read | Source GPR (`r0`–`r31`). | +| `SIMM` | twi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | + +## Register Effects + +### `twi` + +- **Reads (always):** `TO`, `RA`, `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** _none_ +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +; Pseudocode derives directly from the xenia-rs interpreter +; arm (see Implementation References). Operation semantics: +; - Read source operands from the fields listed under Operands. +; - Apply the arithmetic / logical / memory action described +; in the Description field above. +; - Write results to the destination register(s); update any +; status bits enumerated under Status-Register Effects. +; Consult the IBM AIX reference link under IBM Reference for +; canonical PPC-style pseudocode where xenia's expression is +; terse. +``` + +## C Translation Example + +```c +/* C translation: the xenia-rs interpreter arm below in */ +/* Implementation References is the authoritative semantic */ +/* snapshot. Translate it line-by-line: */ +/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */ +/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */ +/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */ +/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */ +/* The Register Effects and Status-Register Effects tables above */ +/* enumerate every side effect a faithful translation must emit. */ +``` + +## Implementation References + +**`twi`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="twi"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:601`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L601) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:328`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L328) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => { + // PPCBUG-063: save CIA before incrementing so a trap handler reads + // the faulting instruction address, not CIA+4. + // PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360 + // typed-trap convention used by the CRT/kernel for C++ exception + // class dispatch). The audit notes this is relevant to the Sylpheed + // throw investigation; routing the type code via a payload requires + // a StepResult enum extension that's deferred for now. + let trap_pc = ctx.pc; + let a = ctx.gpr[instr.ra()]; + let b = match instr.opcode { + PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64, + _ => ctx.gpr[instr.rb()], + }; + let width = match instr.opcode { + PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word, + _ => trap::TrapWidth::Doubleword, + }; + let fired = trap::evaluate(instr.to(), a, b, width); + if fired { + let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi) + && instr.to() == 31 && instr.ra() == 0 { + Some(instr.simm16() as u16) + } else { None }; + tracing::warn!( + "Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}", + trap_pc, instr.opcode, instr.to(), a, b, + typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t)) + ); + // Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery + // can read the faulting instruction address from ctx.pc. + return StepResult::Trap; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **32-bit comparison against sign-extended immediate.** `SIMM` is a 16-bit signed value, sign-extended to 32 bits, then compared against the low 32 bits of `RA`. The high half of `RA` is *ignored*. +- **`TO` mask.** Identical to [`tw`](tw.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true. +- **`twi 31, 0, 0` is unconditional trap.** All `TO` bits set β‡’ guaranteed trap. The simplified mnemonic family (`twnei`, `twgei`, …) is much more common in real code: bound checks, null checks, integer-divide-by-zero pre-checks. +- **Compiler usage.** Xbox 360 GCC emits `twnei rN, -1` and similar to validate handle-style return values; the kernel handler turns the trap into an exception delivered to the title. +- **No register effects.** Side effect: Program interrupt β†’ vector `0x700` with `SRR1[TRAP]=1`. +- **xenia simplification.** Same as the other three trap forms β€” xenia-rs unconditionally returns `StepResult::Trap` whenever it decodes any of `tdi`/`twi`/`td`/`tw`, regardless of the `TO` mask or operands. This means `twi 0, r0, 0` (architecturally a guaranteed-no-trap encoding) will spuriously fire under xenia. Keep this in mind when triaging unexpected trap signals. +- **No `Rc` / `OE`.** D-form trap immediates have neither. + +## Related Instructions + +- [`tw`](tw.md) β€” register-register 32-bit trap (X-form). +- [`tdi`](tdi.md) / [`td`](td.md) β€” 64-bit (doubleword) siblings. +- [`sc`](sc.md) β€” alternative synchronous kernel entry. +- [`cmpi`](../alu/cmpi.md), [`cmpli`](../alu/cmpli.md) β€” set CR for a subsequent [`bcx`](bcx.md) when you want a regular branch instead of a trap. + +### Simplified Mnemonics + +| Simplified | Expansion | Triggered when | +| --- | --- | --- | +| `tweqi RA, value` | `twi 4, RA, value` | `RA == EXTS(value)` | +| `twnei RA, value` | `twi 24, RA, value` | `RA != EXTS(value)` | +| `twlti RA, value` | `twi 16, RA, value` | signed less than | +| `twlei RA, value` | `twi 20, RA, value` | signed less or equal | +| `twgti RA, value` | `twi 8, RA, value` | signed greater than | +| `twgei RA, value` | `twi 12, RA, value` | signed greater or equal | +| `twllti RA, value` | `twi 2, RA, value` | unsigned less than | +| `twlgei RA, value` | `twi 5, RA, value` | unsigned greater or equal | +| `twlgti RA, value` | `twi 1, RA, value` | unsigned greater than | +| `twllei RA, value` | `twi 6, RA, value` | unsigned less or equal | + +## IBM Reference + +- [AIX 7.3 β€” `twi` (Trap Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-twi-trap-word-immediate-instruction) +- [AIX 7.3 β€” Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified) +- PowerISA v2.07B, Book I Β§3.3.11 β€” fixed-point trap instructions. diff --git a/tools/ppc-manual/categories/alu.md b/tools/ppc-manual/categories/alu.md new file mode 100644 index 00000000..e730e3b7 --- /dev/null +++ b/tools/ppc-manual/categories/alu.md @@ -0,0 +1,82 @@ +# Integer ALU + +Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition. + +**70 families** Β· **70 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`addcx`](addcx.md) | `XO` | Add Carrying | `addcx` | +| [`addex`](addex.md) | `XO` | Add Extended | `addex` | +| [`addi`](addi.md) | `D` | Add Immediate | `addi` | +| [`addic`](addic.md) | `D` | Add Immediate Carrying | `addic` | +| [`addic.`](addicx.md) | `D` | Add Immediate Carrying and Record | `addic.` | +| [`addis`](addis.md) | `D` | Add Immediate Shifted | `addis` | +| [`addmex`](addmex.md) | `XO` | Add to Minus One Extended | `addmex` | +| [`addx`](addx.md) | `XO` | Add | `addx` | +| [`addzex`](addzex.md) | `XO` | Add to Zero Extended | `addzex` | +| [`andcx`](andcx.md) | `X` | AND with Complement | `andcx` | +| [`andi.`](andix.md) | `D` | AND Immediate | `andi.` | +| [`andis.`](andisx.md) | `D` | AND Immediate Shifted | `andis.` | +| [`andx`](andx.md) | `X` | AND | `andx` | +| [`cmp`](cmp.md) | `X` | Compare | `cmp` | +| [`cmpi`](cmpi.md) | `D` | Compare Immediate | `cmpi` | +| [`cmpl`](cmpl.md) | `X` | Compare Logical | `cmpl` | +| [`cmpli`](cmpli.md) | `D` | Compare Logical Immediate | `cmpli` | +| [`cntlzdx`](cntlzdx.md) | `X` | Count Leading Zeros Doubleword | `cntlzdx` | +| [`cntlzwx`](cntlzwx.md) | `X` | Count Leading Zeros Word | `cntlzwx` | +| [`divdux`](divdux.md) | `XO` | Divide Doubleword Unsigned | `divdux` | +| [`divdx`](divdx.md) | `XO` | Divide Doubleword | `divdx` | +| [`divwux`](divwux.md) | `XO` | Divide Word Unsigned | `divwux` | +| [`divwx`](divwx.md) | `XO` | Divide Word | `divwx` | +| [`eieio`](eieio.md) | `X` | Enforce In-Order Execution of I/O | `eieio` | +| [`eqvx`](eqvx.md) | `X` | Equivalent | `eqvx` | +| [`extsbx`](extsbx.md) | `X` | Extend Sign Byte | `extsbx` | +| [`extshx`](extshx.md) | `X` | Extend Sign Half Word | `extshx` | +| [`extswx`](extswx.md) | `X` | Extend Sign Word | `extswx` | +| [`isync`](isync.md) | `XL` | Instruction Synchronize | `isync` | +| [`mulhdux`](mulhdux.md) | `XO` | Multiply High Doubleword Unsigned | `mulhdux` | +| [`mulhdx`](mulhdx.md) | `XO` | Multiply High Doubleword | `mulhdx` | +| [`mulhwux`](mulhwux.md) | `XO` | Multiply High Word Unsigned | `mulhwux` | +| [`mulhwx`](mulhwx.md) | `XO` | Multiply High Word | `mulhwx` | +| [`mulldx`](mulldx.md) | `XO` | Multiply Low Doubleword | `mulldx` | +| [`mulli`](mulli.md) | `D` | Multiply Low Immediate | `mulli` | +| [`mullwx`](mullwx.md) | `XO` | Multiply Low Word | `mullwx` | +| [`nandx`](nandx.md) | `X` | NAND | `nandx` | +| [`negx`](negx.md) | `XO` | Negate | `negx` | +| [`norx`](norx.md) | `X` | NOR | `norx` | +| [`orcx`](orcx.md) | `X` | OR with Complement | `orcx` | +| [`ori`](ori.md) | `D` | OR Immediate | `ori` | +| [`oris`](oris.md) | `D` | OR Immediate Shifted | `oris` | +| [`orx`](orx.md) | `X` | OR | `orx` | +| [`rldclx`](rldclx.md) | `MDS` | Rotate Left Doubleword then Clear Left | `rldclx` | +| [`rldcrx`](rldcrx.md) | `MDS` | Rotate Left Doubleword then Clear Right | `rldcrx` | +| [`rldiclx`](rldiclx.md) | `MD` | Rotate Left Doubleword Immediate then Clear Left | `rldiclx` | +| [`rldicrx`](rldicrx.md) | `MD` | Rotate Left Doubleword Immediate then Clear Right | `rldicrx` | +| [`rldicx`](rldicx.md) | `MD` | Rotate Left Doubleword Immediate then Clear | `rldicx` | +| [`rldimix`](rldimix.md) | `MD` | Rotate Left Doubleword Immediate then Mask Insert | `rldimix` | +| [`rlwimix`](rlwimix.md) | `M` | Rotate Left Word Immediate then Mask Insert | `rlwimix` | +| [`rlwinmx`](rlwinmx.md) | `M` | Rotate Left Word Immediate then AND with Mask | `rlwinmx` | +| [`rlwnmx`](rlwnmx.md) | `M` | Rotate Left Word then AND with Mask | `rlwnmx` | +| [`sldx`](sldx.md) | `X` | Shift Left Doubleword | `sldx` | +| [`slwx`](slwx.md) | `X` | Shift Left Word | `slwx` | +| [`sradix`](sradix.md) | `XS` | Shift Right Algebraic Doubleword Immediate | `sradix` | +| [`sradx`](sradx.md) | `X` | Shift Right Algebraic Doubleword | `sradx` | +| [`srawix`](srawix.md) | `X` | Shift Right Algebraic Word Immediate | `srawix` | +| [`srawx`](srawx.md) | `X` | Shift Right Algebraic Word | `srawx` | +| [`srdx`](srdx.md) | `X` | Shift Right Doubleword | `srdx` | +| [`srwx`](srwx.md) | `X` | Shift Right Word | `srwx` | +| [`subfcx`](subfcx.md) | `XO` | Subtract From Carrying | `subfcx` | +| [`subfex`](subfex.md) | `XO` | Subtract From Extended | `subfex` | +| [`subficx`](subficx.md) | `D` | Subtract From Immediate Carrying | `subficx` | +| [`subfmex`](subfmex.md) | `XO` | Subtract From Minus One Extended | `subfmex` | +| [`subfx`](subfx.md) | `XO` | Subtract From | `subfx` | +| [`subfzex`](subfzex.md) | `XO` | Subtract From Zero Extended | `subfzex` | +| [`sync`](sync.md) | `X` | Synchronize | `sync` | +| [`xori`](xori.md) | `D` | XOR Immediate | `xori` | +| [`xoris`](xoris.md) | `D` | XOR Immediate Shifted | `xoris` | +| [`xorx`](xorx.md) | `X` | XOR | `xorx` | + + diff --git a/tools/ppc-manual/categories/branch.md b/tools/ppc-manual/categories/branch.md new file mode 100644 index 00000000..9f2eba12 --- /dev/null +++ b/tools/ppc-manual/categories/branch.md @@ -0,0 +1,21 @@ +# Branch & System + +Unconditional / conditional branches, branch to LR/CTR, traps, system call. + +**9 families** Β· **9 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`bcctrx`](bcctrx.md) | `XL` | Branch Conditional to Count Register | `bcctrx` | +| [`bclrx`](bclrx.md) | `XL` | Branch Conditional to Link Register | `bclrx` | +| [`bcx`](bcx.md) | `B` | Branch Conditional | `bcx` | +| [`bx`](bx.md) | `I` | Branch | `bx` | +| [`sc`](sc.md) | `SC` | System Call | `sc` | +| [`td`](td.md) | `X` | Trap Doubleword | `td` | +| [`tdi`](tdi.md) | `D` | Trap Doubleword Immediate | `tdi` | +| [`tw`](tw.md) | `X` | Trap Word | `tw` | +| [`twi`](twi.md) | `D` | Trap Word Immediate | `twi` | + + diff --git a/tools/ppc-manual/categories/control.md b/tools/ppc-manual/categories/control.md new file mode 100644 index 00000000..3f5cf579 --- /dev/null +++ b/tools/ppc-manual/categories/control.md @@ -0,0 +1,38 @@ +# Control / CR / SPR + +Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio). + +**26 families** Β· **26 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`crand`](crand.md) | `XL` | Condition Register AND | `crand` | +| [`crandc`](crandc.md) | `XL` | Condition Register AND with Complement | `crandc` | +| [`creqv`](creqv.md) | `XL` | Condition Register Equivalent | `creqv` | +| [`crnand`](crnand.md) | `XL` | Condition Register NAND | `crnand` | +| [`crnor`](crnor.md) | `XL` | Condition Register NOR | `crnor` | +| [`cror`](cror.md) | `XL` | Condition Register OR | `cror` | +| [`crorc`](crorc.md) | `XL` | Condition Register OR with Complement | `crorc` | +| [`crxor`](crxor.md) | `XL` | Condition Register XOR | `crxor` | +| [`mcrf`](mcrf.md) | `XL` | Move Condition Register Field | `mcrf` | +| [`mcrfs`](mcrfs.md) | `X` | Move to Condition Register from FPSCR | `mcrfs` | +| [`mcrxr`](mcrxr.md) | `X` | Move to Condition Register from XER | `mcrxr` | +| [`mfcr`](mfcr.md) | `X` | Move from Condition Register | `mfcr` | +| [`mffsx`](mffsx.md) | `X` | Move from FPSCR | `mffsx` | +| [`mfmsr`](mfmsr.md) | `X` | Move from Machine State Register | `mfmsr` | +| [`mfspr`](mfspr.md) | `XFX` | Move from Special-Purpose Register | `mfspr` | +| [`mftb`](mftb.md) | `XFX` | Move from Time Base | `mftb` | +| [`mfvscr`](mfvscr.md) | `VX` | Move from VSCR | `mfvscr` | +| [`mtcrf`](mtcrf.md) | `XFX` | Move to Condition Register Fields | `mtcrf` | +| [`mtfsb0x`](mtfsb0x.md) | `X` | Move to FPSCR Bit 0 | `mtfsb0x` | +| [`mtfsb1x`](mtfsb1x.md) | `X` | Move to FPSCR Bit 1 | `mtfsb1x` | +| [`mtfsfix`](mtfsfix.md) | `X` | Move to FPSCR Field Immediate | `mtfsfix` | +| [`mtfsfx`](mtfsfx.md) | `XFL` | Move to FPSCR Fields | `mtfsfx` | +| [`mtmsr`](mtmsr.md) | `X` | Move to Machine State Register | `mtmsr` | +| [`mtmsrd`](mtmsrd.md) | `X` | Move to Machine State Register Doubleword | `mtmsrd` | +| [`mtspr`](mtspr.md) | `XFX` | Move to Special-Purpose Register | `mtspr` | +| [`mtvscr`](mtvscr.md) | `VX` | Move to VSCR | `mtvscr` | + + diff --git a/tools/ppc-manual/categories/fpu.md b/tools/ppc-manual/categories/fpu.md new file mode 100644 index 00000000..363d9f8c --- /dev/null +++ b/tools/ppc-manual/categories/fpu.md @@ -0,0 +1,45 @@ +# Floating-Point + +IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves. + +**33 families** Β· **33 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`fabsx`](fabsx.md) | `X` | Floating Absolute Value | `fabsx` | +| [`faddsx`](faddsx.md) | `A` | Floating Add Single | `faddsx` | +| [`faddx`](faddx.md) | `A` | Floating Add | `faddx` | +| [`fcfidx`](fcfidx.md) | `X` | Floating Convert From Integer Doubleword | `fcfidx` | +| [`fcmpo`](fcmpo.md) | `X` | Floating Compare Ordered | `fcmpo` | +| [`fcmpu`](fcmpu.md) | `X` | Floating Compare Unordered | `fcmpu` | +| [`fctidx`](fctidx.md) | `X` | Floating Convert to Integer Doubleword | `fctidx` | +| [`fctidzx`](fctidzx.md) | `X` | Floating Convert to Integer Doubleword with Round Toward Zero | `fctidzx` | +| [`fctiwx`](fctiwx.md) | `X` | Floating Convert to Integer Word | `fctiwx` | +| [`fctiwzx`](fctiwzx.md) | `X` | Floating Convert to Integer Word with Round Toward Zero | `fctiwzx` | +| [`fdivsx`](fdivsx.md) | `A` | Floating Divide Single | `fdivsx` | +| [`fdivx`](fdivx.md) | `A` | Floating Divide | `fdivx` | +| [`fmaddsx`](fmaddsx.md) | `A` | Floating Multiply-Add Single | `fmaddsx` | +| [`fmaddx`](fmaddx.md) | `A` | Floating Multiply-Add | `fmaddx` | +| [`fmrx`](fmrx.md) | `X` | Floating Move Register | `fmrx` | +| [`fmsubsx`](fmsubsx.md) | `A` | Floating Multiply-Subtract Single | `fmsubsx` | +| [`fmsubx`](fmsubx.md) | `A` | Floating Multiply-Subtract | `fmsubx` | +| [`fmulsx`](fmulsx.md) | `A` | Floating Multiply Single | `fmulsx` | +| [`fmulx`](fmulx.md) | `A` | Floating Multiply | `fmulx` | +| [`fnabsx`](fnabsx.md) | `X` | Floating Negative Absolute Value | `fnabsx` | +| [`fnegx`](fnegx.md) | `X` | Floating Negate | `fnegx` | +| [`fnmaddsx`](fnmaddsx.md) | `A` | Floating Negative Multiply-Add Single | `fnmaddsx` | +| [`fnmaddx`](fnmaddx.md) | `A` | Floating Negative Multiply-Add | `fnmaddx` | +| [`fnmsubsx`](fnmsubsx.md) | `A` | Floating Negative Multiply-Subtract Single | `fnmsubsx` | +| [`fnmsubx`](fnmsubx.md) | `A` | Floating Negative Multiply-Subtract | `fnmsubx` | +| [`fresx`](fresx.md) | `A` | Floating Reciprocal Estimate Single | `fresx` | +| [`frspx`](frspx.md) | `X` | Floating Round to Single | `frspx` | +| [`frsqrtex`](frsqrtex.md) | `A` | Floating Reciprocal Square Root Estimate | `frsqrtex` | +| [`fselx`](fselx.md) | `A` | Floating Select | `fselx` | +| [`fsqrtsx`](fsqrtsx.md) | `A` | Floating Square Root Single | `fsqrtsx` | +| [`fsqrtx`](fsqrtx.md) | `A` | Floating Square Root | `fsqrtx` | +| [`fsubsx`](fsubsx.md) | `A` | Floating Subtract Single | `fsubsx` | +| [`fsubx`](fsubx.md) | `A` | Floating Subtract | `fsubx` | + + diff --git a/tools/ppc-manual/categories/memory.md b/tools/ppc-manual/categories/memory.md new file mode 100644 index 00000000..fb8273ea --- /dev/null +++ b/tools/ppc-manual/categories/memory.md @@ -0,0 +1,68 @@ +# Memory + +Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx. + +**56 families** Β· **112 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`dcbf`](dcbf.md) | `X` | Data Cache Block Flush | `dcbf` | +| [`dcbi`](dcbi.md) | `X` | Data Cache Block Invalidate | `dcbi` | +| [`dcbst`](dcbst.md) | `X` | Data Cache Block Store | `dcbst` | +| [`dcbt`](dcbt.md) | `X` | Data Cache Block Touch | `dcbt` | +| [`dcbtst`](dcbtst.md) | `X` | Data Cache Block Touch for Store | `dcbtst` | +| [`dcbz`](dcbz.md) | `DCBZ` | Data Cache Block Clear to Zero | `dcbz`, `dcbz128` | +| [`icbi`](icbi.md) | `X` | Instruction Cache Block Invalidate | `icbi` | +| [`lbz`](lbz.md) | `D` | Load Byte and Zero | `lbz`, `lbzu`, `lbzux`, `lbzx` | +| [`ld`](ld.md) | `DS` | Load Doubleword | `ld`, `ldu`, `ldux`, `ldx` | +| [`ldarx`](ldarx.md) | `X` | Load Doubleword and Reserve Indexed | `ldarx` | +| [`ldbrx`](ldbrx.md) | `X` | Load Doubleword Byte-Reverse Indexed | `ldbrx` | +| [`lfd`](lfd.md) | `D` | Load Floating-Point Double | `lfd`, `lfdu`, `lfdux`, `lfdx` | +| [`lfs`](lfs.md) | `D` | Load Floating-Point Single | `lfs`, `lfsu`, `lfsux`, `lfsx` | +| [`lha`](lha.md) | `D` | Load Half Word Algebraic | `lha`, `lhau`, `lhaux`, `lhax` | +| [`lhbrx`](lhbrx.md) | `X` | Load Half Word Byte-Reverse Indexed | `lhbrx` | +| [`lhz`](lhz.md) | `D` | Load Half Word and Zero | `lhz`, `lhzu`, `lhzux`, `lhzx` | +| [`lmw`](lmw.md) | `D` | Load Multiple Word | `lmw` | +| [`lswi`](lswi.md) | `X` | Load String Word Immediate | `lswi` | +| [`lswx`](lswx.md) | `X` | Load String Word Indexed | `lswx` | +| [`lvebx`](lvebx.md) | `X` | Load Vector Element Byte Indexed | `lvebx` | +| [`lvehx`](lvehx.md) | `X` | Load Vector Element Half Word Indexed | `lvehx` | +| [`lvewx`](lvewx.md) | `X` | Load Vector Element Word Indexed | `lvewx`, `lvewx128` | +| [`lvlx`](lvlx.md) | `X` | Load Vector Left Indexed | `lvlx`, `lvlx128` | +| [`lvlxl`](lvlxl.md) | `X` | Load Vector Left Indexed LRU | `lvlxl`, `lvlxl128` | +| [`lvrx`](lvrx.md) | `X` | Load Vector Right Indexed | `lvrx`, `lvrx128` | +| [`lvrxl`](lvrxl.md) | `X` | Load Vector Right Indexed LRU | `lvrxl`, `lvrxl128` | +| [`lvx`](lvx.md) | `X` | Load Vector Indexed | `lvx`, `lvx128` | +| [`lvxl`](lvxl.md) | `X` | Load Vector Indexed LRU | `lvxl`, `lvxl128` | +| [`lwa`](lwa.md) | `DS` | Load Word Algebraic | `lwa`, `lwaux`, `lwax` | +| [`lwarx`](lwarx.md) | `X` | Load Word and Reserve Indexed | `lwarx` | +| [`lwbrx`](lwbrx.md) | `X` | Load Word Byte-Reverse Indexed | `lwbrx` | +| [`lwz`](lwz.md) | `D` | Load Word and Zero | `lwz`, `lwzu`, `lwzux`, `lwzx` | +| [`stb`](stb.md) | `D` | Store Byte | `stb`, `stbu`, `stbux`, `stbx` | +| [`std`](std.md) | `DS` | Store Doubleword | `std`, `stdu`, `stdux`, `stdx` | +| [`stdbrx`](stdbrx.md) | `X` | Store Doubleword Byte-Reverse Indexed | `stdbrx` | +| [`stdcx`](stdcx.md) | `X` | Store Doubleword Conditional Indexed | `stdcx` | +| [`stfd`](stfd.md) | `D` | Store Floating-Point Double | `stfd`, `stfdu`, `stfdux`, `stfdx` | +| [`stfiwx`](stfiwx.md) | `X` | Store Floating-Point as Integer Word Indexed | `stfiwx` | +| [`stfs`](stfs.md) | `D` | Store Floating-Point Single | `stfs`, `stfsu`, `stfsux`, `stfsx` | +| [`sth`](sth.md) | `D` | Store Half Word | `sth`, `sthu`, `sthux`, `sthx` | +| [`sthbrx`](sthbrx.md) | `X` | Store Half Word Byte-Reverse Indexed | `sthbrx` | +| [`stmw`](stmw.md) | `D` | Store Multiple Word | `stmw` | +| [`stswi`](stswi.md) | `X` | Store String Word Immediate | `stswi` | +| [`stswx`](stswx.md) | `X` | Store String Word Indexed | `stswx` | +| [`stvebx`](stvebx.md) | `X` | Store Vector Element Byte Indexed | `stvebx` | +| [`stvehx`](stvehx.md) | `X` | Store Vector Element Half Word Indexed | `stvehx` | +| [`stvewx`](stvewx.md) | `X` | Store Vector Element Word Indexed | `stvewx`, `stvewx128` | +| [`stvlx`](stvlx.md) | `X` | Store Vector Left Indexed | `stvlx`, `stvlx128` | +| [`stvlxl`](stvlxl.md) | `X` | Store Vector Left Indexed LRU | `stvlxl`, `stvlxl128` | +| [`stvrx`](stvrx.md) | `X` | Store Vector Right Indexed | `stvrx`, `stvrx128` | +| [`stvrxl`](stvrxl.md) | `X` | Store Vector Right Indexed LRU | `stvrxl`, `stvrxl128` | +| [`stvx`](stvx.md) | `X` | Store Vector Indexed | `stvx`, `stvx128` | +| [`stvxl`](stvxl.md) | `X` | Store Vector Indexed LRU | `stvxl`, `stvxl128` | +| [`stw`](stw.md) | `D` | Store Word | `stw`, `stwu`, `stwux`, `stwx` | +| [`stwbrx`](stwbrx.md) | `X` | Store Word Byte-Reverse Indexed | `stwbrx` | +| [`stwcx`](stwcx.md) | `X` | Store Word Conditional Indexed | `stwcx` | + + diff --git a/tools/ppc-manual/categories/vmx.md b/tools/ppc-manual/categories/vmx.md new file mode 100644 index 00000000..288e9d9c --- /dev/null +++ b/tools/ppc-manual/categories/vmx.md @@ -0,0 +1,156 @@ +# VMX (Altivec) + +128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers. + +**144 families** Β· **193 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`lvsl`](lvsl.md) | `X` | Load Vector for Shift Left Indexed | `lvsl`, `lvsl128` | +| [`lvsr`](lvsr.md) | `X` | Load Vector for Shift Right Indexed | `lvsr`, `lvsr128` | +| [`vaddcuw`](vaddcuw.md) | `VX` | Vector Add Carryout Unsigned Word | `vaddcuw` | +| [`vaddfp`](vaddfp.md) | `VX` | Vector Add Floating Point | `vaddfp`, `vaddfp128` | +| [`vaddsbs`](vaddsbs.md) | `VX` | Vector Add Signed Byte Saturate | `vaddsbs` | +| [`vaddshs`](vaddshs.md) | `VX` | Vector Add Signed Half Word Saturate | `vaddshs` | +| [`vaddsws`](vaddsws.md) | `VX` | Vector Add Signed Word Saturate | `vaddsws` | +| [`vaddubm`](vaddubm.md) | `VX` | Vector Add Unsigned Byte Modulo | `vaddubm` | +| [`vaddubs`](vaddubs.md) | `VX` | Vector Add Unsigned Byte Saturate | `vaddubs` | +| [`vadduhm`](vadduhm.md) | `VX` | Vector Add Unsigned Half Word Modulo | `vadduhm` | +| [`vadduhs`](vadduhs.md) | `VX` | Vector Add Unsigned Half Word Saturate | `vadduhs` | +| [`vadduwm`](vadduwm.md) | `VX` | Vector Add Unsigned Word Modulo | `vadduwm` | +| [`vadduws`](vadduws.md) | `VX` | Vector Add Unsigned Word Saturate | `vadduws` | +| [`vand`](vand.md) | `VX` | Vector Logical AND | `vand`, `vand128` | +| [`vandc`](vandc.md) | `VX` | Vector Logical AND with Complement | `vandc`, `vandc128` | +| [`vavgsb`](vavgsb.md) | `VX` | Vector Average Signed Byte | `vavgsb` | +| [`vavgsh`](vavgsh.md) | `VX` | Vector Average Signed Half Word | `vavgsh` | +| [`vavgsw`](vavgsw.md) | `VX` | Vector Average Signed Word | `vavgsw` | +| [`vavgub`](vavgub.md) | `VX` | Vector Average Unsigned Byte | `vavgub` | +| [`vavguh`](vavguh.md) | `VX` | Vector Average Unsigned Half Word | `vavguh` | +| [`vavguw`](vavguw.md) | `VX` | Vector Average Unsigned Word | `vavguw` | +| [`vcfsx`](vcfsx.md) | `VX` | Vector Convert from Signed Fixed-Point Word | `vcfsx` | +| [`vcfux`](vcfux.md) | `VX` | Vector Convert from Unsigned Fixed-Point Word | `vcfux` | +| [`vcmpbfp`](vcmpbfp.md) | `VC` | Vector Compare Bounds Floating Point | `vcmpbfp`, `vcmpbfp128` | +| [`vcmpeqfp`](vcmpeqfp.md) | `VC` | Vector Compare Equal-to Floating Point | `vcmpeqfp`, `vcmpeqfp128` | +| [`vcmpequb`](vcmpequb.md) | `VC` | Vector Compare Equal-to Unsigned Byte | `vcmpequb` | +| [`vcmpequh`](vcmpequh.md) | `VC` | Vector Compare Equal-to Unsigned Half Word | `vcmpequh` | +| [`vcmpequw`](vcmpequw.md) | `VC` | Vector Compare Equal-to Unsigned Word | `vcmpequw`, `vcmpequw128` | +| [`vcmpgefp`](vcmpgefp.md) | `VC` | Vector Compare Greater-Than-or-Equal-to Floating Point | `vcmpgefp`, `vcmpgefp128` | +| [`vcmpgtfp`](vcmpgtfp.md) | `VC` | Vector Compare Greater-Than Floating Point | `vcmpgtfp`, `vcmpgtfp128` | +| [`vcmpgtsb`](vcmpgtsb.md) | `VC` | Vector Compare Greater-Than Signed Byte | `vcmpgtsb` | +| [`vcmpgtsh`](vcmpgtsh.md) | `VC` | Vector Compare Greater-Than Signed Half Word | `vcmpgtsh` | +| [`vcmpgtsw`](vcmpgtsw.md) | `VC` | Vector Compare Greater-Than Signed Word | `vcmpgtsw` | +| [`vcmpgtub`](vcmpgtub.md) | `VC` | Vector Compare Greater-Than Unsigned Byte | `vcmpgtub` | +| [`vcmpgtuh`](vcmpgtuh.md) | `VC` | Vector Compare Greater-Than Unsigned Half Word | `vcmpgtuh` | +| [`vcmpgtuw`](vcmpgtuw.md) | `VC` | Vector Compare Greater-Than Unsigned Word | `vcmpgtuw` | +| [`vctsxs`](vctsxs.md) | `VX` | Vector Convert to Signed Fixed-Point Word Saturate | `vctsxs` | +| [`vctuxs`](vctuxs.md) | `VX` | Vector Convert to Unsigned Fixed-Point Word Saturate | `vctuxs` | +| [`vexptefp`](vexptefp.md) | `VX` | Vector 2 Raised to the Exponent Estimate Floating Point | `vexptefp`, `vexptefp128` | +| [`vlogefp`](vlogefp.md) | `VX` | Vector Log2 Estimate Floating Point | `vlogefp`, `vlogefp128` | +| [`vmaddfp`](vmaddfp.md) | `VA` | Vector Multiply-Add Floating Point | `vmaddfp`, `vmaddfp128` | +| [`vmaxfp`](vmaxfp.md) | `VX` | Vector Maximum Floating Point | `vmaxfp`, `vmaxfp128` | +| [`vmaxsb`](vmaxsb.md) | `VX` | Vector Maximum Signed Byte | `vmaxsb` | +| [`vmaxsh`](vmaxsh.md) | `VX` | Vector Maximum Signed Half Word | `vmaxsh` | +| [`vmaxsw`](vmaxsw.md) | `VX` | Vector Maximum Signed Word | `vmaxsw` | +| [`vmaxub`](vmaxub.md) | `VX` | Vector Maximum Unsigned Byte | `vmaxub` | +| [`vmaxuh`](vmaxuh.md) | `VX` | Vector Maximum Unsigned Half Word | `vmaxuh` | +| [`vmaxuw`](vmaxuw.md) | `VX` | Vector Maximum Unsigned Word | `vmaxuw` | +| [`vmhaddshs`](vmhaddshs.md) | `VA` | Vector Multiply-High and Add Signed Signed Half Word Saturate | `vmhaddshs` | +| [`vmhraddshs`](vmhraddshs.md) | `VA` | Vector Multiply-High Round and Add Signed Signed Half Word Saturate | `vmhraddshs` | +| [`vminfp`](vminfp.md) | `VX` | Vector Minimum Floating Point | `vminfp`, `vminfp128` | +| [`vminsb`](vminsb.md) | `VX` | Vector Minimum Signed Byte | `vminsb` | +| [`vminsh`](vminsh.md) | `VX` | Vector Minimum Signed Half Word | `vminsh` | +| [`vminsw`](vminsw.md) | `VX` | Vector Minimum Signed Word | `vminsw` | +| [`vminub`](vminub.md) | `VX` | Vector Minimum Unsigned Byte | `vminub` | +| [`vminuh`](vminuh.md) | `VX` | Vector Minimum Unsigned Half Word | `vminuh` | +| [`vminuw`](vminuw.md) | `VX` | Vector Minimum Unsigned Word | `vminuw` | +| [`vmladduhm`](vmladduhm.md) | `VA` | Vector Multiply-Low and Add Unsigned Half Word Modulo | `vmladduhm` | +| [`vmrghb`](vmrghb.md) | `VX` | Vector Merge High Byte | `vmrghb` | +| [`vmrghh`](vmrghh.md) | `VX` | Vector Merge High Half Word | `vmrghh` | +| [`vmrghw`](vmrghw.md) | `VX` | Vector Merge High Word | `vmrghw`, `vmrghw128` | +| [`vmrglb`](vmrglb.md) | `VX` | Vector Merge Low Byte | `vmrglb` | +| [`vmrglh`](vmrglh.md) | `VX` | Vector Merge Low Half Word | `vmrglh` | +| [`vmrglw`](vmrglw.md) | `VX` | Vector Merge Low Word | `vmrglw`, `vmrglw128` | +| [`vmsummbm`](vmsummbm.md) | `VA` | Vector Multiply-Sum Mixed-Sign Byte Modulo | `vmsummbm` | +| [`vmsumshm`](vmsumshm.md) | `VA` | Vector Multiply-Sum Signed Half Word Modulo | `vmsumshm` | +| [`vmsumshs`](vmsumshs.md) | `VA` | Vector Multiply-Sum Signed Half Word Saturate | `vmsumshs` | +| [`vmsumubm`](vmsumubm.md) | `VA` | Vector Multiply-Sum Unsigned Byte Modulo | `vmsumubm` | +| [`vmsumuhm`](vmsumuhm.md) | `VA` | Vector Multiply-Sum Unsigned Half Word Modulo | `vmsumuhm` | +| [`vmsumuhs`](vmsumuhs.md) | `VA` | Vector Multiply-Sum Unsigned Half Word Saturate | `vmsumuhs` | +| [`vmulesb`](vmulesb.md) | `VX` | Vector Multiply Even Signed Byte | `vmulesb` | +| [`vmulesh`](vmulesh.md) | `VX` | Vector Multiply Even Signed Half Word | `vmulesh` | +| [`vmuleub`](vmuleub.md) | `VX` | Vector Multiply Even Unsigned Byte | `vmuleub` | +| [`vmuleuh`](vmuleuh.md) | `VX` | Vector Multiply Even Unsigned Half Word | `vmuleuh` | +| [`vmulosb`](vmulosb.md) | `VX` | Vector Multiply Odd Signed Byte | `vmulosb` | +| [`vmulosh`](vmulosh.md) | `VX` | Vector Multiply Odd Signed Half Word | `vmulosh` | +| [`vmuloub`](vmuloub.md) | `VX` | Vector Multiply Odd Unsigned Byte | `vmuloub` | +| [`vmulouh`](vmulouh.md) | `VX` | Vector Multiply Odd Unsigned Half Word | `vmulouh` | +| [`vnmsubfp`](vnmsubfp.md) | `VA` | Vector Negative Multiply-Subtract Floating Point | `vnmsubfp`, `vnmsubfp128` | +| [`vnor`](vnor.md) | `VX` | Vector Logical NOR | `vnor`, `vnor128` | +| [`vor`](vor.md) | `VX` | Vector Logical OR | `vor`, `vor128` | +| [`vperm`](vperm.md) | `VA` | Vector Permute | `vperm`, `vperm128` | +| [`vpkpx`](vpkpx.md) | `VX` | Vector Pack Pixel | `vpkpx` | +| [`vpkshss`](vpkshss.md) | `VX` | Vector Pack Signed Half Word Signed Saturate | `vpkshss`, `vpkshss128` | +| [`vpkshus`](vpkshus.md) | `VX` | Vector Pack Signed Half Word Unsigned Saturate | `vpkshus`, `vpkshus128` | +| [`vpkswss`](vpkswss.md) | `VX` | Vector Pack Signed Word Signed Saturate | `vpkswss`, `vpkswss128` | +| [`vpkswus`](vpkswus.md) | `VX` | Vector Pack Signed Word Unsigned Saturate | `vpkswus`, `vpkswus128` | +| [`vpkuhum`](vpkuhum.md) | `VX` | Vector Pack Unsigned Half Word Unsigned Modulo | `vpkuhum`, `vpkuhum128` | +| [`vpkuhus`](vpkuhus.md) | `VX` | Vector Pack Unsigned Half Word Unsigned Saturate | `vpkuhus`, `vpkuhus128` | +| [`vpkuwum`](vpkuwum.md) | `VX` | Vector Pack Unsigned Word Unsigned Modulo | `vpkuwum`, `vpkuwum128` | +| [`vpkuwus`](vpkuwus.md) | `VX` | Vector Pack Unsigned Word Unsigned Saturate | `vpkuwus`, `vpkuwus128` | +| [`vrefp`](vrefp.md) | `VX` | Vector Reciprocal Estimate Floating Point | `vrefp`, `vrefp128` | +| [`vrfim`](vrfim.md) | `VX` | Vector Round to Floating-Point Integer toward -Infinity | `vrfim`, `vrfim128` | +| [`vrfin`](vrfin.md) | `VX` | Vector Round to Floating-Point Integer Nearest | `vrfin`, `vrfin128` | +| [`vrfip`](vrfip.md) | `VX` | Vector Round to Floating-Point Integer toward +Infinity | `vrfip`, `vrfip128` | +| [`vrfiz`](vrfiz.md) | `VX` | Vector Round to Floating-Point Integer toward Zero | `vrfiz`, `vrfiz128` | +| [`vrlb`](vrlb.md) | `VX` | Vector Rotate Left Integer Byte | `vrlb` | +| [`vrlh`](vrlh.md) | `VX` | Vector Rotate Left Integer Half Word | `vrlh` | +| [`vrlw`](vrlw.md) | `VX` | Vector Rotate Left Integer Word | `vrlw`, `vrlw128` | +| [`vrsqrtefp`](vrsqrtefp.md) | `VX` | Vector Reciprocal Square Root Estimate Floating Point | `vrsqrtefp`, `vrsqrtefp128` | +| [`vsel`](vsel.md) | `VA` | Vector Conditional Select | `vsel`, `vsel128` | +| [`vsl`](vsl.md) | `VX` | Vector Shift Left | `vsl` | +| [`vslb`](vslb.md) | `VX` | Vector Shift Left Integer Byte | `vslb` | +| [`vsldoi`](vsldoi.md) | `VA` | Vector Shift Left Double by Octet Immediate | `vsldoi`, `vsldoi128` | +| [`vslh`](vslh.md) | `VX` | Vector Shift Left Integer Half Word | `vslh` | +| [`vslo`](vslo.md) | `VX` | Vector Shift Left by Octet | `vslo`, `vslo128` | +| [`vslw`](vslw.md) | `VX` | Vector Shift Left Integer Word | `vslw`, `vslw128` | +| [`vspltb`](vspltb.md) | `VX` | Vector Splat Byte | `vspltb` | +| [`vsplth`](vsplth.md) | `VX` | Vector Splat Half Word | `vsplth` | +| [`vspltisb`](vspltisb.md) | `VX` | Vector Splat Immediate Signed Byte | `vspltisb` | +| [`vspltish`](vspltish.md) | `VX` | Vector Splat Immediate Signed Half Word | `vspltish` | +| [`vspltisw`](vspltisw.md) | `VX` | Vector Splat Immediate Signed Word | `vspltisw`, `vspltisw128` | +| [`vspltw`](vspltw.md) | `VX` | Vector Splat Word | `vspltw`, `vspltw128` | +| [`vsr`](vsr.md) | `VX` | Vector Shift Right | `vsr` | +| [`vsrab`](vsrab.md) | `VX` | Vector Shift Right Algebraic Byte | `vsrab` | +| [`vsrah`](vsrah.md) | `VX` | Vector Shift Right Algebraic Half Word | `vsrah` | +| [`vsraw`](vsraw.md) | `VX` | Vector Shift Right Algebraic Word | `vsraw`, `vsraw128` | +| [`vsrb`](vsrb.md) | `VX` | Vector Shift Right Byte | `vsrb` | +| [`vsrh`](vsrh.md) | `VX` | Vector Shift Right Half Word | `vsrh` | +| [`vsro`](vsro.md) | `VX` | Vector Shift Right Octet | `vsro`, `vsro128` | +| [`vsrw`](vsrw.md) | `VX` | Vector Shift Right Word | `vsrw`, `vsrw128` | +| [`vsubcuw`](vsubcuw.md) | `VX` | Vector Subtract Carryout Unsigned Word | `vsubcuw` | +| [`vsubfp`](vsubfp.md) | `VX` | Vector Subtract Floating Point | `vsubfp`, `vsubfp128` | +| [`vsubsbs`](vsubsbs.md) | `VX` | Vector Subtract Signed Byte Saturate | `vsubsbs` | +| [`vsubshs`](vsubshs.md) | `VX` | Vector Subtract Signed Half Word Saturate | `vsubshs` | +| [`vsubsws`](vsubsws.md) | `VX` | Vector Subtract Signed Word Saturate | `vsubsws` | +| [`vsububm`](vsububm.md) | `VX` | Vector Subtract Unsigned Byte Modulo | `vsububm` | +| [`vsububs`](vsububs.md) | `VX` | Vector Subtract Unsigned Byte Saturate | `vsububs` | +| [`vsubuhm`](vsubuhm.md) | `VX` | Vector Subtract Unsigned Half Word Modulo | `vsubuhm` | +| [`vsubuhs`](vsubuhs.md) | `VX` | Vector Subtract Unsigned Half Word Saturate | `vsubuhs` | +| [`vsubuwm`](vsubuwm.md) | `VX` | Vector Subtract Unsigned Word Modulo | `vsubuwm` | +| [`vsubuws`](vsubuws.md) | `VX` | Vector Subtract Unsigned Word Saturate | `vsubuws` | +| [`vsum2sws`](vsum2sws.md) | `VX` | Vector Sum Across Partial (1/2) Signed Word Saturate | `vsum2sws` | +| [`vsum4sbs`](vsum4sbs.md) | `VX` | Vector Sum Across Partial (1/4) Signed Byte Saturate | `vsum4sbs` | +| [`vsum4shs`](vsum4shs.md) | `VX` | Vector Sum Across Partial (1/4) Signed Half Word Saturate | `vsum4shs` | +| [`vsum4ubs`](vsum4ubs.md) | `VX` | Vector Sum Across Partial (1/4) Unsigned Byte Saturate | `vsum4ubs` | +| [`vsumsws`](vsumsws.md) | `VX` | Vector Sum Across Signed Word Saturate | `vsumsws` | +| [`vupkhpx`](vupkhpx.md) | `VX` | Vector Unpack High Pixel | `vupkhpx` | +| [`vupkhsb`](vupkhsb.md) | `VX` | Vector Unpack High Signed Byte | `vupkhsb`, `vupkhsb128` | +| [`vupkhsh`](vupkhsh.md) | `VX` | Vector Unpack High Signed Half Word | `vupkhsh` | +| [`vupklpx`](vupklpx.md) | `VX` | Vector Unpack Low Pixel | `vupklpx` | +| [`vupklsb`](vupklsb.md) | `VX` | Vector Unpack Low Signed Byte | `vupklsb`, `vupklsb128` | +| [`vupklsh`](vupklsh.md) | `VX` | Vector Unpack Low Signed Half Word | `vupklsh` | +| [`vxor`](vxor.md) | `VX` | Vector Logical XOR | `vxor`, `vxor128` | + + diff --git a/tools/ppc-manual/categories/vmx128.md b/tools/ppc-manual/categories/vmx128.md new file mode 100644 index 00000000..9526fa06 --- /dev/null +++ b/tools/ppc-manual/categories/vmx128.md @@ -0,0 +1,24 @@ +# VMX128 + +Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields. + +**12 families** Β· **12 XML entries**. + + + +| Family | Form | Description | Members | +| --- | --- | --- | --- | +| [`vcfpsxws128`](vcfpsxws128.md) | `VX128_3` | Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate | `vcfpsxws128` | +| [`vcfpuxws128`](vcfpuxws128.md) | `VX128_3` | Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate | `vcfpuxws128` | +| [`vcsxwfp128`](vcsxwfp128.md) | `VX128_3` | Vector128 Convert From Signed Fixed-Point Word to Floating-Point | `vcsxwfp128` | +| [`vcuxwfp128`](vcuxwfp128.md) | `VX128_3` | Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point | `vcuxwfp128` | +| [`vmaddcfp128`](vmaddcfp128.md) | `VX128` | Vector128 Multiply Add Floating Point | `vmaddcfp128` | +| [`vmsum3fp128`](vmsum3fp128.md) | `VX128` | Vector128 Multiply Sum 3-way Floating Point | `vmsum3fp128` | +| [`vmsum4fp128`](vmsum4fp128.md) | `VX128` | Vector128 Multiply Sum 4-way Floating-Point | `vmsum4fp128` | +| [`vmulfp128`](vmulfp128.md) | `VX128` | Vector128 Multiply Floating-Point | `vmulfp128` | +| [`vpermwi128`](vpermwi128.md) | `VX128_P` | Vector128 Permutate Word Immediate | `vpermwi128` | +| [`vpkd3d128`](vpkd3d128.md) | `VX128_4` | Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert | `vpkd3d128` | +| [`vrlimi128`](vrlimi128.md) | `VX128_4` | Vector128 Rotate Left Immediate and Mask Insert | `vrlimi128` | +| [`vupkd3d128`](vupkd3d128.md) | `VX128_3` | Vector128 Unpack D3Dtype | `vupkd3d128` | + + diff --git a/tools/ppc-manual/control/crand.md b/tools/ppc-manual/control/crand.md new file mode 100644 index 00000000..0ae37693 --- /dev/null +++ b/tools/ppc-manual/control/crand.md @@ -0,0 +1,127 @@ +# `crand` β€” Condition Register AND + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000202` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crand` | `crand` | β€” | Condition Register AND | + +## Syntax + +```asm +crand [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crand` β€” form `XL` + +- **Opcode word:** `0x4c000202` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `257` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crand: read | CR source bit A (0–31). | +| `CRBB` | crand: read | CR source bit B (0–31). | +| `CRBD` | crand: write | CR destination bit (0–31). | + +## Register Effects + +### `crand` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crand`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crand"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:352`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L352) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:717`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L717) + + + +## Special Cases & Edge Conditions + +- **Bit-level granularity.** All eight CR-logical instructions operate on **single CR bits**, not whole 4-bit fields. `CRBD`, `CRBA`, `CRBB` are 5-bit absolute indices into the 32-bit CR register: `BI = 4Β·field + bit-within-field`, where bit 0 = LT, 1 = GT, 2 = EQ, 3 = SO. +- **Operation.** `CR[CRBD] ← CR[CRBA] AND CR[CRBB]`. All other CR bits are preserved. +- **Same-source / same-destination quirks.** Identical sources and destinations are legal: `crand 6, 6, 6` is a NOP-style "force CR bit 6 to itself"; `crand bt, bt, bt` reads-then-writes the same bit (no observable change). Compilers exploit `crxor BT,BT,BT` ("clear bit") and `creqv BT,BT,BT` ("set bit") for similar tricks β€” see those pages. +- **Combining branch conditions.** The classic use: synthesise complex branch conditions from multiple compare results. Example: `cmpw cr0, r3, r4; cmpw cr1, r5, r6; crand 4*cr0+2, 4*cr0+2, 4*cr1+2; beq cr0, label` branches if `r3==r4 AND r5==r6` using a single conditional branch. +- **No `Rc` / `OE`.** XL-form CR-logical ops never set CR0 or XER; they only update the named CR bit. +- **Not synchronising.** Pure data-flow on CR; freely reorderable. +- **xenia status.** xenia-rs decodes `crand` (decoder slot 540) but the interpreter snapshot is not embedded on this page β€” implementation lives in [`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs). xenia-canary's `InstrEmit_crand` emits the equivalent host AND of the two CR bits. + +## Related Instructions + +- [`crandc`](crandc.md) β€” AND with complement: `CR[BT] ← CR[BA] AND Β¬CR[BB]`. +- [`cror`](cror.md), [`crorc`](crorc.md) β€” OR / OR-with-complement. +- [`crnand`](crnand.md), [`crnor`](crnor.md) β€” negated AND / OR. +- [`crxor`](crxor.md), [`creqv`](creqv.md) β€” XOR and equivalence (XNOR). +- [`mcrf`](mcrf.md) β€” copy a whole 4-bit CR field. +- [`bcx`](../branch/bcx.md) consumers β€” the typical reason to compute composite CR bits. + +### Simplified Mnemonics + +- `crmove BT, BA` ≑ `cror BT, BA, BA` (use `cror`, not `crand`). +- `crset BT` ≑ `creqv BT, BT, BT` (set to 1). +- `crclr BT` ≑ `crxor BT, BT, BT` (clear to 0). + +`crand` itself has no dedicated simplified mnemonic. + +## IBM Reference + +- [AIX 7.3 β€” `crand` (Condition Register AND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crand-condition-register-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/crandc.md b/tools/ppc-manual/control/crandc.md new file mode 100644 index 00000000..cc395b53 --- /dev/null +++ b/tools/ppc-manual/control/crandc.md @@ -0,0 +1,119 @@ +# `crandc` β€” Condition Register AND with Complement + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000102` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crandc` | `crandc` | β€” | Condition Register AND with Complement | + +## Syntax + +```asm +crandc [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crandc` β€” form `XL` + +- **Opcode word:** `0x4c000102` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `129` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crandc: read | CR source bit A (0–31). | +| `CRBB` | crandc: read | CR source bit B (0–31). | +| `CRBD` | crandc: write | CR destination bit (0–31). | + +## Register Effects + +### `crandc` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crandc`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crandc"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:361`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L361) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:713`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L713) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← CR[CRBA] AND Β¬CR[CRBB]` β€” a one-instruction "A and not B" that would otherwise need a complement-then-AND sequence. All other CR bits are preserved. +- **Bit-level operands.** `CRBD`, `CRBA`, `CRBB` are 5-bit indices into the 32 CR bits (0=CR0.LT, 1=CR0.GT, 2=CR0.EQ, 3=CR0.SO, 4=CR1.LT, …, 31=CR7.SO). They need not lie in the same CR field. +- **Identity / corner cases.** `crandc BT, BA, BA` always yields 0 (a clear-bit idiom, equivalent to but slower than `crxor BT, BT, BT`). `crandc BT, BA, BB` with `BB` always 0 reduces to `crmove BT, BA`. +- **Use case.** Synthesises "branch if A *and not* B" predicates without a dedicated `cmp` of `B`. Example: branch only if `cr0.EQ` *and not* `cr1.SO` β€” `crandc 2, 2, 7` then `beq` on `cr0`. +- **No `Rc` / `OE`.** Pure CR-bit dataflow; doesn't update CR0 or XER. +- **Not synchronising.** Reorderable. +- **xenia status.** Interpreter handles via the generic CR-logical helper. xenia-canary's `InstrEmit_crandc` emits a host AND of `A` and bitwise-NOT of `B`. + +## Related Instructions + +- [`crand`](crand.md), [`cror`](cror.md), [`crorc`](crorc.md) β€” closest siblings. +- [`crnand`](crnand.md), [`crnor`](crnor.md), [`crxor`](crxor.md), [`creqv`](creqv.md) β€” full set of CR Boolean ops. +- [`mcrf`](mcrf.md) β€” copy entire CR field; complementary "broad" CR move. +- [`bcx`](../branch/bcx.md) β€” typical consumer of synthesised CR bits. + +`crandc` has no dedicated simplified mnemonic. See [`crand`](crand.md) for the standard `crmove` / `crset` / `crclr` family. + +## IBM Reference + +- [AIX 7.3 β€” `crandc` (Condition Register AND with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crandc-condition-register-complement-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/creqv.md b/tools/ppc-manual/control/creqv.md new file mode 100644 index 00000000..03f6a050 --- /dev/null +++ b/tools/ppc-manual/control/creqv.md @@ -0,0 +1,124 @@ +# `creqv` β€” Condition Register Equivalent + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000242` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `creqv` | `creqv` | β€” | Condition Register Equivalent | + +## Syntax + +```asm +creqv [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `creqv` β€” form `XL` + +- **Opcode word:** `0x4c000242` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `289` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | creqv: read | CR source bit A (0–31). | +| `CRBB` | creqv: read | CR source bit B (0–31). | +| `CRBD` | creqv: write | CR destination bit (0–31). | + +## Register Effects + +### `creqv` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`creqv`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="creqv"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:370`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L370) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:718`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L718) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← Β¬(CR[CRBA] XOR CR[CRBB])` β€” i.e. logical equivalence (XNOR). Result is 1 iff `CRBA` and `CRBB` agree. +- **`crset BT` idiom.** With identical operands, `creqv BT, BT, BT` always yields 1 (any bit XNOR'd with itself is 1). This is the canonical PowerPC **set-to-1** for a single CR bit; assemblers recognise the simplified mnemonic `crset BT`. +- **Bit-level operands.** Like all CR-logical ops, the three operands are 5-bit absolute CR-bit indices (0..31). Mixing CR fields is fine. +- **Use case.** Branch on "A == B" of two prior compare results. Example: `crxor` of CR0.SO and CR1.SO gives "differ"; `creqv` gives "agree". +- **No `Rc` / `OE`.** Doesn't touch CR0, XER, or any other state beyond the named bit. +- **Not synchronising.** Reorderable. +- **xenia status.** Interpreter dispatches through the generic CR-logical helper; canary emits the host XNOR equivalent. The `crset` simplified form is the most common occurrence in real Xbox 360 code. + +## Related Instructions + +- [`crand`](crand.md), [`crandc`](crandc.md) β€” AND family. +- [`cror`](cror.md), [`crorc`](crorc.md), [`crnor`](crnor.md), [`crnand`](crnand.md) β€” OR family. +- [`crxor`](crxor.md) β€” the dual; `crxor BT, BT, BT` is the standard **clear-to-0** idiom. +- [`mcrf`](mcrf.md) β€” bulk CR-field move. +- [`bcx`](../branch/bcx.md) β€” consumes the synthesised bit. + +### Simplified Mnemonics + +| Simplified | Expansion | Effect | +| --- | --- | --- | +| `crset BT` | `creqv BT, BT, BT` | force `CR[BT] ← 1` | + +## IBM Reference + +- [AIX 7.3 β€” `creqv` (Condition Register Equivalent)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-creqv-condition-register-equivalent-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/crnand.md b/tools/ppc-manual/control/crnand.md new file mode 100644 index 00000000..7b0ea7cf --- /dev/null +++ b/tools/ppc-manual/control/crnand.md @@ -0,0 +1,121 @@ +# `crnand` β€” Condition Register NAND + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c0001c2` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crnand` | `crnand` | β€” | Condition Register NAND | + +## Syntax + +```asm +crnand [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crnand` β€” form `XL` + +- **Opcode word:** `0x4c0001c2` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `225` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crnand: read | CR source bit A (0–31). | +| `CRBB` | crnand: read | CR source bit B (0–31). | +| `CRBD` | crnand: write | CR destination bit (0–31). | + +## Register Effects + +### `crnand` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crnand`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crnand"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:379`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L379) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:716`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L716) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← Β¬(CR[CRBA] AND CR[CRBB])`. Result is 0 only when both source bits are 1; otherwise 1. +- **Bit-level operands.** 5-bit absolute CR-bit indices, identical to the rest of the CR-logical family. Source and destination bits may all be in different CR fields. +- **Identity case.** `crnand BT, BA, BA` ≑ `Β¬CR[BA]` β€” a one-instruction CR-bit invert. Less common than `crxor` against a `crset`-prepared 1-bit, but legal. +- **Use case.** Branch on "NOT (A AND B)". Less common than the De Morgan equivalent (`cror BT, Β¬A, Β¬B`), but saves an extra `crnot` step. +- **No `Rc` / `OE`.** No CR0 / XER side effects. +- **Not synchronising.** Reorderable. +- **xenia status.** Decoded by the generic XL-form CR-logical handler; the interpreter snapshot is shared with `crand`/`cror`/etc. xenia-canary's `InstrEmit_crnand` emits a host AND followed by NOT. + +## Related Instructions + +- [`crand`](crand.md), [`crandc`](crandc.md) β€” non-negated AND siblings. +- [`crnor`](crnor.md) β€” negated OR (the De Morgan dual). +- [`cror`](cror.md), [`crorc`](crorc.md) β€” OR family. +- [`crxor`](crxor.md), [`creqv`](creqv.md) β€” XOR / XNOR. +- [`mcrf`](mcrf.md) β€” copy a 4-bit CR field wholesale. +- [`bcx`](../branch/bcx.md) β€” typical consumer. + +`crnand` has no dedicated simplified mnemonic. Use `crand` + a separate complement, or use `crnand BT, BA, BA` to invert a single bit. + +## IBM Reference + +- [AIX 7.3 β€” `crnand` (Condition Register NAND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crnand-condition-register-nand-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/crnor.md b/tools/ppc-manual/control/crnor.md new file mode 100644 index 00000000..d495ac4b --- /dev/null +++ b/tools/ppc-manual/control/crnor.md @@ -0,0 +1,125 @@ +# `crnor` β€” Condition Register NOR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000042` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crnor` | `crnor` | β€” | Condition Register NOR | + +## Syntax + +```asm +crnor [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crnor` β€” form `XL` + +- **Opcode word:** `0x4c000042` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `33` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crnor: read | CR source bit A (0–31). | +| `CRBB` | crnor: read | CR source bit B (0–31). | +| `CRBD` | crnor: write | CR destination bit (0–31). | + +## Register Effects + +### `crnor` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crnor`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crnor"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:388`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L388) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:712`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L712) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← Β¬(CR[CRBA] OR CR[CRBB])`. Result is 1 only when both source bits are 0; otherwise 0. +- **`crnot BT, BA` idiom.** With `BA == BB`, `crnor BT, BA, BA` ≑ `Β¬CR[BA]` β€” this is the canonical PowerPC **single-bit invert**, recognised by assemblers as the simplified mnemonic `crnot BT, BA`. +- **Bit-level operands.** 5-bit absolute CR-bit indices (0..31). The three bits may live in any combination of the eight CR fields. +- **Use case.** Branch on "neither A nor B"; or, with `crnot`, simply complement a CR bit before consuming it in a `bcx`. +- **No `Rc` / `OE`.** Pure CR-bit dataflow; CR0/XER untouched. +- **Not synchronising.** Reorderable. +- **xenia status.** Decoded via the generic CR-logical handler. xenia-canary's `InstrEmit_crnor` emits a host OR followed by NOT. + +## Related Instructions + +- [`cror`](cror.md), [`crorc`](crorc.md) β€” non-negated OR siblings. +- [`crnand`](crnand.md) β€” negated AND (the De Morgan dual). +- [`crand`](crand.md), [`crandc`](crandc.md) β€” AND family. +- [`crxor`](crxor.md), [`creqv`](creqv.md) β€” XOR / XNOR. +- [`mcrf`](mcrf.md) β€” bulk CR-field copy. +- [`bcx`](../branch/bcx.md) β€” typical consumer of synthesised CR bits. + +### Simplified Mnemonics + +| Simplified | Expansion | Effect | +| --- | --- | --- | +| `crnot BT, BA` | `crnor BT, BA, BA` | `CR[BT] ← Β¬CR[BA]` (invert single bit) | + +## IBM Reference + +- [AIX 7.3 β€” `crnor` (Condition Register NOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crnor-condition-register-nor-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/cror.md b/tools/ppc-manual/control/cror.md new file mode 100644 index 00000000..5ed28c55 --- /dev/null +++ b/tools/ppc-manual/control/cror.md @@ -0,0 +1,125 @@ +# `cror` β€” Condition Register OR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000382` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `cror` | `cror` | β€” | Condition Register OR | + +## Syntax + +```asm +cror [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `cror` β€” form `XL` + +- **Opcode word:** `0x4c000382` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `449` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | cror: read | CR source bit A (0–31). | +| `CRBB` | cror: read | CR source bit B (0–31). | +| `CRBD` | cror: write | CR destination bit (0–31). | + +## Register Effects + +### `cror` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`cror`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="cror"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:397`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L397) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:720`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L720) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← CR[CRBA] OR CR[CRBB]`. All other CR bits are preserved. +- **`crmove BT, BA` idiom.** With `BA == BB`, `cror BT, BA, BA` is the canonical PowerPC **single-bit copy** between CR bits, recognised by assemblers as the simplified mnemonic `crmove BT, BA`. This is the standard way to relocate a CR bit (e.g., promote `cr1.EQ` to `cr0.EQ` so a default-`cr0` branch can consume it). +- **Bit-level operands.** Three independent 5-bit CR-bit indices; mixing CR fields is the whole point of this family. +- **Use case.** Branch on "A OR B" of two prior compare results β€” saves an extra branch by collapsing two conditions. +- **No `Rc` / `OE`.** Pure CR-bit dataflow. +- **Not synchronising.** Reorderable. +- **xenia status.** Most-used CR-logical instruction in real code (almost always as `crmove`). Decoded by the generic XL-form CR-logical handler; canary emits a host OR. + +## Related Instructions + +- [`crand`](crand.md), [`crandc`](crandc.md) β€” AND family. +- [`crorc`](crorc.md) β€” OR with complement. +- [`crnor`](crnor.md), [`crnand`](crnand.md) β€” negated forms. +- [`crxor`](crxor.md), [`creqv`](creqv.md) β€” XOR / XNOR. +- [`mcrf`](mcrf.md) β€” copy a 4-bit CR field wholesale. +- [`bcx`](../branch/bcx.md), [`bclrx`](../branch/bclrx.md), [`bcctrx`](../branch/bcctrx.md) β€” typical consumers. + +### Simplified Mnemonics + +| Simplified | Expansion | Effect | +| --- | --- | --- | +| `crmove BT, BA` | `cror BT, BA, BA` | `CR[BT] ← CR[BA]` (single-bit copy) | + +## IBM Reference + +- [AIX 7.3 β€” `cror` (Condition Register OR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cror-condition-register-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/crorc.md b/tools/ppc-manual/control/crorc.md new file mode 100644 index 00000000..abb3c17e --- /dev/null +++ b/tools/ppc-manual/control/crorc.md @@ -0,0 +1,121 @@ +# `crorc` β€” Condition Register OR with Complement + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000342` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crorc` | `crorc` | β€” | Condition Register OR with Complement | + +## Syntax + +```asm +crorc [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crorc` β€” form `XL` + +- **Opcode word:** `0x4c000342` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `417` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crorc: read | CR source bit A (0–31). | +| `CRBB` | crorc: read | CR source bit B (0–31). | +| `CRBD` | crorc: write | CR destination bit (0–31). | + +## Register Effects + +### `crorc` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crorc`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crorc"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L406) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:719`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L719) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← CR[CRBA] OR Β¬CR[CRBB]` β€” a one-instruction "A or not B" implication. Result is 0 only when `A=0` and `B=1`. +- **Logical implication.** `B β†’ A` ≑ `Β¬B ∨ A`, which is exactly `crorc BT, BA, BB`. Useful for predicates of the form "if B holds, then A must hold". +- **Identity case.** `crorc BT, BA, BA` always yields 1 (`A ∨ Β¬A`), an alternative to `creqv` for setting a bit. +- **Bit-level operands.** 5-bit absolute CR-bit indices; sources and destination may all be in different CR fields. +- **Use case.** Compose "if B then A" guards without a separate complement step. +- **No `Rc` / `OE`.** Doesn't update CR0 or XER. +- **Not synchronising.** Reorderable. +- **xenia status.** Decoded by the generic CR-logical handler; canary emits OR-with-NOT directly. + +## Related Instructions + +- [`cror`](cror.md), [`crnor`](crnor.md) β€” non-complement OR / NOR. +- [`crand`](crand.md), [`crandc`](crandc.md), [`crnand`](crnand.md) β€” AND family. +- [`crxor`](crxor.md), [`creqv`](creqv.md) β€” XOR / XNOR. +- [`mcrf`](mcrf.md) β€” bulk CR-field copy. +- [`bcx`](../branch/bcx.md) β€” typical consumer. + +`crorc` has no dedicated simplified mnemonic. See [`cror`](cror.md) for `crmove`. + +## IBM Reference + +- [AIX 7.3 β€” `crorc` (Condition Register OR with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crorc-condition-register-complement-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/crxor.md b/tools/ppc-manual/control/crxor.md new file mode 100644 index 00000000..35f79e5a --- /dev/null +++ b/tools/ppc-manual/control/crxor.md @@ -0,0 +1,124 @@ +# `crxor` β€” Condition Register XOR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000182` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `crxor` | `crxor` | β€” | Condition Register XOR | + +## Syntax + +```asm +crxor [CRBD], [CRBA], [CRBB] +``` + +## Encoding + +### `crxor` β€” form `XL` + +- **Opcode word:** `0x4c000182` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `193` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRBA` | crxor: read | CR source bit A (0–31). | +| `CRBB` | crxor: read | CR source bit B (0–31). | +| `CRBD` | crxor: write | CR destination bit (0–31). | + +## Register Effects + +### `crxor` + +- **Reads (always):** `CRBA`, `CRBB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRBD` +- **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 + +**`crxor`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="crxor"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:415`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L415) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:715`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L715) + + + +## Special Cases & Edge Conditions + +- **Operation.** `CR[CRBD] ← CR[CRBA] XOR CR[CRBB]`. Result is 1 iff the two source bits differ. +- **`crclr BT` idiom.** With identical operands, `crxor BT, BT, BT` always yields 0 (any bit XOR'd with itself is 0). This is the canonical PowerPC **clear-to-0** for a single CR bit; assemblers recognise the simplified mnemonic `crclr BT`. Compilers emit it before variadic-argument calls (PPC ABI uses `cr1.SO` to flag presence of FP arguments). +- **Bit-level operands.** Three independent 5-bit absolute CR-bit indices (0..31). +- **Use case.** Branch on "A != B"; or, with the `crclr` idiom, zero a CR bit before fall-through CR computation. +- **No `Rc` / `OE`.** No CR0 / XER side effects. +- **Not synchronising.** Reorderable. +- **xenia status.** Common enough in real code (typically as `crclr 6` for the variadic-FP marker) that translators often special-case the `crclr` pattern. xenia-canary's `InstrEmit_crxor` emits a host XOR; xenia-rs decodes via the generic CR-logical handler. + +## Related Instructions + +- [`creqv`](creqv.md) β€” the dual; `creqv BT, BT, BT` is the standard **set-to-1** idiom. +- [`crand`](crand.md), [`crandc`](crandc.md), [`crnand`](crnand.md) β€” AND family. +- [`cror`](cror.md), [`crorc`](crorc.md), [`crnor`](crnor.md) β€” OR family. +- [`mcrf`](mcrf.md) β€” bulk CR-field copy. +- [`bcx`](../branch/bcx.md), [`bclrx`](../branch/bclrx.md) β€” typical consumers of synthesised CR bits. + +### Simplified Mnemonics + +| Simplified | Expansion | Effect | +| --- | --- | --- | +| `crclr BT` | `crxor BT, BT, BT` | force `CR[BT] ← 0` | + +## IBM Reference + +- [AIX 7.3 β€” `crxor` (Condition Register XOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crxor-condition-register-xor-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/mcrf.md b/tools/ppc-manual/control/mcrf.md new file mode 100644 index 00000000..b40b1142 --- /dev/null +++ b/tools/ppc-manual/control/mcrf.md @@ -0,0 +1,129 @@ +# `mcrf` β€” Move Condition Register Field + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XL](../forms/XL.md) Β· **Opcode:** `0x4c000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mcrf` | `mcrf` | β€” | Move Condition Register Field | + +## Syntax + +```asm +mcrf [CRFD], [CRFS] +``` + +## Encoding + +### `mcrf` β€” form `XL` + +- **Opcode word:** `0x4c000000` +- **Primary opcode (bits 0–5):** `19` +- **Extended opcode:** `0` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `CRFS` | mcrf: read | CR source field. | +| `CRFD` | mcrf: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `mcrf` + +- **Reads (always):** `CRFS` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **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 + +**`mcrf`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mcrf"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:424`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L424) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:710`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L710) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1683-1686`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1683-L1686) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mcrf => { + ctx.cr[instr.crfd()] = ctx.cr[instr.crfs()]; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Field-level (4-bit) move.** Unlike the bit-level CR-logical family ([`crand`](crand.md), …, [`crxor`](crxor.md)), `mcrf` copies *all four* bits of a CR field (LT, GT, EQ, SO) in one instruction. `CRFD` and `CRFS` are 3-bit field indices (0..7), each naming a 4-bit slice of the 32-bit CR. +- **No source-field clobber.** The source field is read, not modified β€” `mcrf 0, 1` copies CR1 into CR0 leaving CR1 intact. +- **Same-field is a NOP.** `mcrf cr0, cr0` reads-then-writes the same field; xenia's interpreter still does the assignment but the architectural state is unchanged. +- **Use case.** Promote a non-default compare result into `cr0` so a default-`cr0` simplified branch (`beq label`) can consume it without spelling out `cr1`/`cr2`/etc. The alternative β€” `crmove` β€” would require four `cror` instructions to move all four bits. +- **No CR0/XER side effects.** Pure CR-field dataflow. +- **Not synchronising.** Reorderable. +- **xenia exact match.** xenia-rs models the CR as an array of eight 4-bit fields, so `mcrf` is a single struct copy (`ctx.cr[crfd] = ctx.cr[crfs]`). Matches PowerISA semantics exactly. + +## Related Instructions + +- [`mfcr`](mfcr.md) β€” read the entire 32-bit CR into a GPR. +- [`mtcrf`](mtcrf.md) β€” write selected CR fields from a GPR (uses an 8-bit field-mask). +- [`mcrxr`](mcrxr.md) β€” copy `XER[SO..CA]` into a CR field and clear them. +- [`mcrfs`](mcrfs.md) β€” copy an FPSCR field into a CR field. +- [`crand`](crand.md), [`crandc`](crandc.md), [`creqv`](creqv.md), [`crnand`](crnand.md), [`crnor`](crnor.md), [`cror`](cror.md), [`crorc`](crorc.md), [`crxor`](crxor.md) β€” bit-level alternatives. +- [`bcx`](../branch/bcx.md) β€” typical consumer. + +`mcrf` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mcrf` (Move Condition Register Field)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrf-move-condition-register-field-instruction) diff --git a/tools/ppc-manual/control/mcrfs.md b/tools/ppc-manual/control/mcrfs.md new file mode 100644 index 00000000..36ee4307 --- /dev/null +++ b/tools/ppc-manual/control/mcrfs.md @@ -0,0 +1,155 @@ +# `mcrfs` β€” Move to Condition Register from FPSCR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000080` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mcrfs` | `mcrfs` | β€” | Move to Condition Register from FPSCR | + +## Syntax + +```asm +mcrfs [CRFD], [CRFS] +``` + +## Encoding + +### `mcrfs` β€” form `X` + +- **Opcode word:** `0xfc000080` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `64` +- **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 | +| --- | --- | --- | +| `CRFS` | mcrfs: read | CR source field. | +| `FPSCR` | mcrfs: read; mcrfs: write | Floating-Point Status and Control Register. | +| `CRFD` | mcrfs: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `mcrfs` + +- **Reads (always):** `CRFS`, `FPSCR` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD`, `FPSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `mcrfs`: **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`mcrfs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mcrfs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:371`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L371) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:904`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L904) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4716-4745`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4716-L4745) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mcrfs => { + let crfd = instr.crfd(); + let crfs = instr.crfs(); + let shift = 28 - (crfs as u32 * 4); + let nibble = ((ctx.fpscr >> shift) & 0xF) as u8; + ctx.cr[crfd] = crate::context::CrField::from_u8(nibble); + // Clearable exception bits: 0 (FX), 3 (OX), 4 (UX), 5 (ZX), + // 6 (XX), 7 (VXSNAN), 8 (VXISI), 9 (VXIDI), 10 (VXZDZ), + // 11 (VXIMZ), 12 (VXVC), 21 (VXSOFT), 22 (VXSQRT), 23 (VXCVI). + // (Bit positions are PowerISA MSB-0; here 'FPSCR bit n' means + // the bit at (31-n) in our little-endian u32.) + const CLEARABLE_MASK: u32 = + (1 << 31) | (1 << (31 - 3)) | (1 << (31 - 4)) | + (1 << (31 - 5)) | (1 << (31 - 6)) | (1 << (31 - 7)) | + (1 << (31 - 8)) | (1 << (31 - 9)) | (1 << (31 - 10)) | + (1 << (31 - 11)) | (1 << (31 - 12)) | + (1 << (31 - 21)) | (1 << (31 - 22)) | (1 << (31 - 23)); + let nibble_mask = 0xFu32 << shift; + ctx.fpscr &= !(nibble_mask & CLEARABLE_MASK); + // PPCBUG-068: recompute the VX summary bit. If any VX* exception + // bit remains set, VX must remain set; if all are cleared, VX + // must clear. (FEX recomputation omitted β€” xenia doesn't model + // enabled-exception dispatch.) + if ctx.fpscr & fpscr::VX_ALL != 0 { + ctx.fpscr |= fpscr::VX; + } else { + ctx.fpscr &= !fpscr::VX; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Copies one 4-bit FPSCR field into the chosen CR field, then **clears the source FPSCR exception-status bits** (sticky-bit reset). The non-exception status bits (FPRF, etc.) are *not* cleared. +- **Bits cleared in FPSCR.** The architectural rule is: any bit in the source FPSCR field that is one of {FX, OX, UX, ZX, XX, VXSNAN, VXISI, VXIDI, VXZDZ, VXIMZ, VXVC, VXSOFT, VXSQRT, VXCVI} is reset to 0 after the copy. FEX and VX (summary bits) are subsequently re-derived. Many other FPSCR bits (rounding mode, FPRF, FR/FI) are not affected β€” even if they fall in `CRFS`. +- **CR field destination.** `CRFD` is a 3-bit field index (0..7); the four bits land in their natural positions (LT, GT, EQ, SO) of the chosen CR field. After `mcrfs`, `crf` can be tested with the usual conditional branches. +- **Use case.** Inspect a particular FPSCR exception group, then act on it with a `bc` β€” e.g. test FPSCR[24..27] (the FI / FR / VXSNAN / VXISI cluster) and branch. +- **Privilege.** Non-privileged on the Xenon β€” application-visible. +- **xenia status.** Decoded (decoder slot 727), but the interpreter does **not** ship a body in the snapshot on this page β€” `mcrfs` is rare in title code. xenia's FPSCR model is incomplete (most exception bits are stubbed), so even when implemented, the cleared bits typically have no observable effect. +- **No `Rc`.** X-form, but the `Rc` bit position is unused (reserved 0). + +## Related Instructions + +- [`mffsx`](mffsx.md) β€” read entire FPSCR into an FPR. +- [`mtfsfx`](mtfsfx.md), [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md), [`mtfsfix`](mtfsfix.md) β€” write FPSCR bits/fields. +- [`mcrf`](mcrf.md) β€” copy a CR field to another CR field. +- [`mcrxr`](mcrxr.md) β€” analogous copy from XER (also clears). + +`mcrfs` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mcrfs` (Move to Condition Register from FPSCR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrfs-move-condition-register-from-fpscr-instruction) +- PowerISA v2.07B, Book I Β§4.6 β€” FPSCR layout (sticky exception bits and which clear semantics apply). diff --git a/tools/ppc-manual/control/mcrxr.md b/tools/ppc-manual/control/mcrxr.md new file mode 100644 index 00000000..ebfaae33 --- /dev/null +++ b/tools/ppc-manual/control/mcrxr.md @@ -0,0 +1,145 @@ +# `mcrxr` β€” Move to Condition Register from XER + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000400` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mcrxr` | `mcrxr` | β€” | Move to Condition Register from XER | + +## Syntax + +```asm +mcrxr [CRFD] +``` + +## Encoding + +### `mcrxr` β€” form `X` + +- **Opcode word:** `0x7c000400` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `512` +- **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 | +| --- | --- | --- | +| `CR` | mcrxr: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `CRFD` | mcrxr: write | CR destination field (`crf`, 0–7). | + +## Register Effects + +### `mcrxr` + +- **Reads (always):** `CR` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **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 + +**`mcrxr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mcrxr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:433`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L433) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:814`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L814) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4694-4706`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4694-L4706) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mcrxr => { + let crfd = instr.crfd(); + ctx.cr[crfd] = crate::context::CrField { + lt: ctx.xer_so != 0, + gt: ctx.xer_ov != 0, + eq: ctx.xer_ca != 0, + so: false, + }; + ctx.xer_so = 0; + ctx.xer_ov = 0; + ctx.xer_ca = 0; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Copies XER's top 4 status bits into a CR field, **then atomically clears those XER bits**. Layout in the destination CR field after the move: + + | CR bit | Source XER bit | Meaning | + | --- | --- | --- | + | LT | XER[SO] | summary overflow (sticky) | + | GT | XER[OV] | overflow (last `OE=1` op) | + | EQ | XER[CA] | carry | + | SO | 0 (cleared) | β€” | + +- **Sticky-bit reset.** XER[SO], XER[OV], and XER[CA] are all *zeroed* after the copy. This is the **only** architecturally clean way to sample-then-clear XER's overflow/carry state β€” `mfxer` reads but does not clear. +- **Use case.** Saturating-arithmetic loops sample XER[OV] periodically; `mcrxr cr0; bso cr0, overflow` is the canonical "did overflow happen since last check?" idiom. +- **CR field destination.** `CRFD` is a 3-bit index (0..7). All other CR fields are preserved. +- **No reads of GPRs.** `mcrxr` reads only XER, writes only the chosen CR field and XER. +- **xenia exact match.** xenia-rs implements the full sample-and-clear semantics: writes `lt = SO`, `gt = OV`, `eq = CA`, `so = false`, then zeroes `xer_so`, `xer_ov`, `xer_ca`. Matches PowerISA exactly. +- **Deprecated in newer PowerISA.** PowerISA v2.06+ marked `mcrxr` deprecated in favour of `mcrxrx` and explicit `mfxer`/`mtxer` patterns, but the Xenon predates that; titles still emit it freely. + +## Related Instructions + +- [`mfcr`](mfcr.md), [`mtcrf`](mtcrf.md) β€” bulk CR <-> GPR moves. +- [`mcrf`](mcrf.md) β€” CR-field copy. +- [`mcrfs`](mcrfs.md) β€” analogous copy from FPSCR (also clears certain bits). +- [`mfspr`](mfspr.md) (with `SPR=1`, i.e. `mfxer`) β€” non-clearing read of XER into a GPR. +- [`mtspr`](mtspr.md) (with `SPR=1`, i.e. `mtxer`) β€” explicit XER write. + +`mcrxr` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mcrxr` (Move to Condition Register from XER)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrxr-move-condition-register-from-xer-instruction) diff --git a/tools/ppc-manual/control/mfcr.md b/tools/ppc-manual/control/mfcr.md new file mode 100644 index 00000000..9cccdcac --- /dev/null +++ b/tools/ppc-manual/control/mfcr.md @@ -0,0 +1,117 @@ +# `mfcr` β€” Move from Condition Register + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000026` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mfcr` | `mfcr` | β€” | Move from Condition Register | + +## Syntax + +```asm +mfcr [RD] +``` + +## Encoding + +### `mfcr` β€” form `X` + +- **Opcode word:** `0x7c000026` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `19` +- **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 | +| --- | --- | --- | +| `CR` | mfcr: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `RD` | mfcr: write | Destination GPR. | + +## Register Effects + +### `mfcr` + +- **Reads (always):** `CR` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +RT <- 0x00000000 || CR +``` + +## 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 + +**`mfcr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mfcr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:625`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L625) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:753`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L753) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1627-1630`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1627-L1630) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mfcr => { + ctx.gpr[instr.rd()] = ctx.cr() as u64; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Packs all 32 CR bits into the low half of `RD`; the upper 32 bits of `RD` are zeroed. CR field 0 ends up in bits 32..35 of `RD` (i.e. bits 0..3 of the 32-bit packed value), CR field 7 in bits 60..63 (bits 28..31). +- **No CR side effect.** `mfcr` is a read; CR is unmodified. The XL-form's nominal `Rc` bit is unused on this opcode. +- **Saving CR across calls.** The Xbox 360 / SysV ABI requires non-volatile CR fields (CR2..CR4) to be preserved across calls. Standard prologue: `mfcr r12; stw r12, 8(r1)`. Epilogue restores via [`mtcrf`](mtcrf.md). +- **Bit ordering.** PowerPC numbers bits big-endian (bit 0 = MSB). The encoding into the GPR follows the same convention: CR0.LT lands in bit 32 of the doubleword (the MSB of the low word). C-side translations should mask with `0xFFFFFFFFu` before consuming. +- **`mfocrf` variant.** PowerISA defines `mfocrf` (one CR field), encoded as `mfcr` with the high bit of FXM set. xenia-rs decodes both as the same opcode and ignores the FXM hint, returning the entire CR. This is benign β€” the spec says implementations may treat `mfocrf` as `mfcr`. +- **Not synchronising.** Reorderable. +- **xenia exact match.** xenia-rs packs its eight `CrField` structs into a `u64` via `ctx.cr()`, mirroring spec semantics. + +## Related Instructions + +- [`mtcrf`](mtcrf.md) β€” inverse: write selected CR fields from a GPR. +- [`mcrf`](mcrf.md), [`mcrxr`](mcrxr.md), [`mcrfs`](mcrfs.md) β€” narrower CR-field moves. +- [`mfspr`](mfspr.md), [`mtspr`](mtspr.md) β€” generic SPR moves; CR is *not* an SPR (it has its own opcode). + +`mfcr` has no simplified mnemonics. `mfocrf RT, FXM` is a related encoding handled by the same xenia-rs slot. + +## IBM Reference + +- [AIX 7.3 β€” `mfcr` (Move from Condition Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfcr-move-from-condition-register-instruction) diff --git a/tools/ppc-manual/control/mffsx.md b/tools/ppc-manual/control/mffsx.md new file mode 100644 index 00000000..344fb6e7 --- /dev/null +++ b/tools/ppc-manual/control/mffsx.md @@ -0,0 +1,132 @@ +# `mffsx` β€” Move from FPSCR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00048e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mffs` | `mffsx` | β€” | Move from FPSCR | +| `mffs.` | `mffsx` | Rc=1 | Move from FPSCR | + +## Syntax + +```asm +mffs[Rc] [RD] +``` + +## Encoding + +### `mffsx` β€” form `X` + +- **Opcode word:** `0xfc00048e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `583` +- **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 | +| --- | --- | --- | +| `FPSCR` | mffsx: read | Floating-Point Status and Control Register. | +| `FD` | mffsx: write | Destination floating-point register. | +| `CR` | mffsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mffsx` + +- **Reads (always):** `FPSCR` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mffsx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`mffsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mffsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:397`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L397) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:910`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L910) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3035-3040`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3035-L3040) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mffsx => { + // Move from FPSCR: frD = FPSCR as double (low 32 bits) + ctx.fpr[instr.rd()] = f64::from_bits(ctx.fpscr as u64); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Reads the 32-bit FPSCR and places it in the **low 32 bits** of `FRT`. The high 32 bits of the destination FPR are architecturally undefined; xenia leaves them as the bit-pattern of the FPSCR cast to `u64` (i.e. the high bits are zero, since FPSCR is 32-bit). PowerISA explicitly permits implementations to leave anything there. +- **Destination is an FPR, not a GPR.** Use [`stfd`](../memory/stfd.md) to spill the FPR to memory and reload via a GPR if the value is needed in the integer file. +- **`mffs.` (`Rc=1`) updates CR1.** The `Rc` bit copies the high four FPSCR bits (FX, FEX, VX, OX) into CR1's LT/GT/EQ/SO. xenia-rs implements this via `update_cr1_from_fpscr`. +- **No FPSCR side effect.** Pure read; FPSCR is not modified (unlike [`mcrfs`](mcrfs.md), which clears sticky exception bits). +- **xenia simplification.** xenia-rs models FPSCR as a `u32` field but **does not actively maintain** most of the IEEE-754 sticky bits β€” the FPU paths typically leave FPSCR untouched. So `mffs` will return whatever was last explicitly set (often 0 / boot defaults). Real titles use it mostly to save/restore the rounding-mode field around library calls, which xenia happens to handle correctly. +- **Not synchronising.** Reorderable with non-FPU instructions. + +## Related Instructions + +- [`mtfsfx`](mtfsfx.md) β€” write fields of FPSCR from an FPR (the inverse). +- [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md) β€” set/clear individual FPSCR bits. +- [`mtfsfix`](mtfsfix.md) β€” load a 4-bit immediate into one FPSCR field. +- [`mcrfs`](mcrfs.md) β€” copy an FPSCR field into a CR field (and clear sticky bits). +- [`mfspr`](mfspr.md) β€” for non-FPSCR special registers. + +`mffs` is the simplified mnemonic for the base form (`Rc=0`); `mffs.` is the recording variant. + +## IBM Reference + +- [AIX 7.3 β€” `mffs` (Move from FPSCR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mffs-move-from-fpscr-instruction) +- PowerISA v2.07B, Book I Β§4.6 β€” FPSCR layout and the high-half-undefined rule. diff --git a/tools/ppc-manual/control/mfmsr.md b/tools/ppc-manual/control/mfmsr.md new file mode 100644 index 00000000..0efaa829 --- /dev/null +++ b/tools/ppc-manual/control/mfmsr.md @@ -0,0 +1,139 @@ +# `mfmsr` β€” Move from Machine State Register + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0000a6` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mfmsr` | `mfmsr` | β€” | Move from Machine State Register | + +## Syntax + +```asm +mfmsr [RD] +``` + +## Encoding + +### `mfmsr` β€” form `X` + +- **Opcode word:** `0x7c0000a6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `83` +- **Synchronising:** yes + +| 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 | +| --- | --- | --- | +| `MSR` | mfmsr: read | Machine State Register. | +| `RD` | mfmsr: write | Destination GPR. | + +## Register Effects + +### `mfmsr` + +- **Reads (always):** `MSR` +- **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 + +**`mfmsr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mfmsr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:814`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L814) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:771`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L771) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1645-1648`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1645-L1648) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mfmsr => { + ctx.gpr[instr.rd()] = ctx.msr; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Privileged.** `mfmsr` is supervisor-only; executing it from problem state on real hardware raises a Privileged Instruction interrupt. Xbox 360 game code never executes it directly β€” it appears only in the kernel image (`xboxkrnl.exe`) and in xenia's HLE bridge. +- **MSR layout (Xenon-relevant fields, big-endian bit numbering).** + + | Bit | Name | Meaning | + | --- | --- | --- | + | 32 | EE | external interrupts enabled | + | 33 | PR | problem state (1 = user) | + | 34 | FP | floating-point available | + | 35 | ME | machine-check enable | + | 38 | DR | data address translation | + | 39 | IR | instruction address translation | + | 50 | LE | little-endian (always 0 on Xenon) | + | 63 | RI | recoverable interrupt | + + The Xenon also exposes `MSR[SF]` (bit 0) = 1 for 64-bit mode; `MSR[HV]` (bit 3) for hypervisor. See PowerISA Book III for the full table. +- **Synchronisation.** Marked `sync` in xenia's XML β€” `mfmsr` is execution-synchronising on real hardware (drains the pipeline before sampling MSR). +- **xenia model.** xenia-rs stores MSR as a flat `u64` and returns it raw. No real bit semantics are modelled β€” the kernel HLE never observes individual MSR fields. The interpreter ignores privilege. +- **Read of an undocumented field returns 0.** Most of the MSR is zero in xenia because no path explicitly initialises it. + +## Related Instructions + +- [`mtmsr`](mtmsr.md) β€” write MSR from a GPR (32-bit form). +- [`mtmsrd`](mtmsrd.md) β€” write the full 64-bit MSR (PPC64 form). +- [`mfspr`](mfspr.md) β€” for non-MSR special registers; MSR has its own dedicated opcode. +- [`sc`](../branch/sc.md) β€” kernel entry where MSR transitions occur via `rfid`/`hrfid`. + +`mfmsr` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mfmsr` (Move from Machine State Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfmsr-move-from-machine-state-register-instruction) +- PowerISA v2.07B, Book III Β§4.3 β€” MSR field definitions. diff --git a/tools/ppc-manual/control/mfspr.md b/tools/ppc-manual/control/mfspr.md new file mode 100644 index 00000000..57ca1d4d --- /dev/null +++ b/tools/ppc-manual/control/mfspr.md @@ -0,0 +1,179 @@ +# `mfspr` β€” Move from Special-Purpose Register + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XFX](../forms/XFX.md) Β· **Opcode:** `0x7c0002a6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mfspr` | `mfspr` | β€” | Move from Special-Purpose Register | + +## Syntax + +```asm +mfspr [RD], [SPR] +``` + +## Encoding + +### `mfspr` β€” form `XFX` + +- **Opcode word:** `0x7c0002a6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `339` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination / source GPR | +| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask | +| 21–30 | `XO` | extended opcode | +| 31 | `β€”` | reserved | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `SPR` | mfspr: read | Special-Purpose-Register number. Encoded with the two 5-bit halves swapped (bits 11-15 become the high half, bits 16-20 the low half). | +| `RD` | mfspr: write | Destination GPR. | + +## Register Effects + +### `mfspr` + +- **Reads (always):** `SPR` +- **Reads (conditional):** _none_ +- **Writes (always):** `RD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +n <- spr_number(SPR) ; SPR field has its two 5-bit halves swapped +RT <- SPR(n) +``` + +## C Translation Example + +```c +/* mfspr RT, SPR β€” SPR field has swapped halves */ +uint32_t n = ((insn.SPR & 0x1F) << 5) | ((insn.SPR >> 5) & 0x1F); +switch (n) { + case 1: r[insn.RT] = xer_pack(); break; /* XER */ + case 8: r[insn.RT] = lr; break; /* LR */ + case 9: r[insn.RT] = ctr; break; /* CTR */ + case 256: r[insn.RT] = vrsave; break; /* VRSAVE*/ + case 268: r[insn.RT] = tb & 0xFFFFFFFFu; break; /* TBL */ + case 269: r[insn.RT] = tb >> 32; break; /* TBU */ + default: r[insn.RT] = 0; break; +} +``` + +## Implementation References + +**`mfspr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mfspr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:666`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L666) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:799`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L799) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1567-1595`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1567-L1595) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mfspr => { + let spr = instr.spr(); + ctx.gpr[instr.rd()] = match spr { + crate::context::spr::XER => ctx.xer() as u64, + crate::context::spr::LR => ctx.lr, + crate::context::spr::CTR => ctx.ctr, + crate::context::spr::DEC => ctx.dec as u64, + crate::context::spr::TBL => ctx.timebase & 0xFFFF_FFFF, + crate::context::spr::TBU => ctx.timebase >> 32, + crate::context::spr::VRSAVE => ctx.vrsave as u64, + // Xbox 360 Xenon processor signature (from canary). + crate::context::spr::PVR => 0x0071_0800, + // Benign SPRs β€” titles read these but we don't model them. + crate::context::spr::SPRG0 + | crate::context::spr::SPRG1 + | crate::context::spr::SPRG2 + | crate::context::spr::SPRG3 + | crate::context::spr::HID0 + | crate::context::spr::HID1 + | crate::context::spr::DAR + | crate::context::spr::DSISR + | crate::context::spr::PIR => 0, + _ => { + tracing::warn!("mfspr: unimplemented SPR {}", spr); + 0 + } + }; + ctx.pc += 4; + } +``` +
+ + + +## SPR Number Encoding β€” the "halves swap" + +The 10-bit `spr` field in the XFX form is **stored in a transposed order**: the bits that software names the *high* half (bits 5..9 of the SPR number) occupy instruction bits **16..20**, and the *low* half (bits 0..4) occupies instruction bits **11..15**. Software (and this manual) always refers to the logical, unswapped SPR number. + +``` +decoded_spr = ((field & 0x1F) << 5) | ((field >> 5) & 0x1F) +``` + +So a programmer writing `mfspr RT, 8` (read LR) encodes `spr-field = 0x100` β€” *not* `8`. Assemblers handle this transparently; disassemblers reverse it. When writing a translator that parses raw instruction words, swap the halves explicitly. + +## SPR Map (Xenon subset modelled by xenia) + +| Decoded # | Name | Meaning | xenia-rs behaviour | +| --- | --- | --- | --- | +| 1 | `XER` | Fixed-point exception register (CA / OV / SO + length field) | packed with `ctx.xer()` | +| 8 | `LR` | Link register | `ctx.lr` | +| 9 | `CTR` | Count register | `ctx.ctr` | +| 18 | `DSISR` | Data-storage interrupt syndrome | returns 0 (stubbed) | +| 19 | `DAR` | Data-access register | returns 0 (stubbed) | +| 256 | `VRSAVE` | Vector-register save mask | `ctx.vrsave` | +| 268 | `TBL` | Time-base lower 32 bits | `ctx.timebase & 0xFFFFFFFF` | +| 269 | `TBU` | Time-base upper 32 bits | `ctx.timebase >> 32` | +| 272–275 | `SPRG0..3` | Software scratch registers (kernel) | returns 0 (stubbed) | +| 287 | `PVR` | Processor-version register | `0x00710800` (Xenon signature) | +| 1008–1009 | `HID0/1` | Hardware implementation registers | returns 0 (stubbed) | +| 1023 | `PIR` | Processor-ID register | returns 0 (stubbed) | + +Unrecognised SPRs return 0 and log a warning. Games rarely read unmodelled SPRs; when they do it's usually clock-skew or sanity checks. + +## Special Cases & Edge Conditions + +- **Privilege.** Some SPRs are privileged on real hardware (MSR, HID0/1, SPRG0..3, DSISR, DAR, PIR). Xbox 360 titles run in a mixed privilege model under the hypervisor; xenia exposes all SPRs without a privilege check because the captured title binaries never contain a real privileged read that should trap. +- **`LR` and `CTR` have dedicated simplified mnemonics.** Assemblers recognise `mflr RT` ≑ `mfspr RT, 8` and `mfctr RT` ≑ `mfspr RT, 9`. Similarly `mfxer RT` ≑ `mfspr RT, 1`. Disassemblers emit the simplified forms; the translation agent should map both forms to the same abstract operation. +- **`mftb` vs. `mfspr TBL/TBU`.** Reading the time-base has a dedicated X-form variant [`mftb`](mftb.md) that uses a separate opcode. Post-Xbox-360 PowerISA deprecated `mfspr TBL/TBU`, but xenia accepts both. Prefer `mftb` in new translations. +- **Side-effect-free.** `mfspr` has no effect on any register beyond `RT`. It can be freely reordered with non-SPR-touching instructions. +- **No `Rc` / `OE`.** This is an XFX-form instruction; bit 31 is reserved (0). + +## Related Instructions + +- [`mtspr`](mtspr.md) β€” the inverse; write a GPR to an SPR. +- [`mftb`](mftb.md) β€” read time-base (preferred over `mfspr TBL/TBU`). +- [`mflr`](mfspr.md), [`mfctr`](mfspr.md), [`mfxer`](mfspr.md) β€” simplified mnemonics of this instruction. +- [`mcrxr`](mcrxr.md) β€” move `XER[SO..CA]` to a CR field and clear them. + +## Simplified Mnemonics + +| Simplified | Expansion | +| --- | --- | +| `mfxer RT` | `mfspr RT, 1` | +| `mflr RT` | `mfspr RT, 8` | +| `mfctr RT` | `mfspr RT, 9` | + +## IBM Reference + +- [AIX 7.3 β€” `mfspr` (Move from Special Purpose Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfspr-move-from-special-purpose-register-instruction) +- [PowerISA v2.07B β€” SPR number table and privilege rules](https://openpowerfoundation.org/specifications/isa/) diff --git a/tools/ppc-manual/control/mftb.md b/tools/ppc-manual/control/mftb.md new file mode 100644 index 00000000..dd0298df --- /dev/null +++ b/tools/ppc-manual/control/mftb.md @@ -0,0 +1,150 @@ +# `mftb` β€” Move from Time Base + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XFX](../forms/XFX.md) Β· **Opcode:** `0x7c0002e6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mftb` | `mftb` | β€” | Move from Time Base | + +## Syntax + +```asm +mftb [RD], [TBR] +``` + +## Encoding + +### `mftb` β€” form `XFX` + +- **Opcode word:** `0x7c0002e6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `371` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination / source GPR | +| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask | +| 21–30 | `XO` | extended opcode | +| 31 | `β€”` | reserved | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `TBR` | mftb: read | Time-Base Register selector for `mftb`. | +| `RD` | mftb: write | Destination GPR. | + +## Register Effects + +### `mftb` + +- **Reads (always):** `TBR` +- **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 + +**`mftb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mftb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:719`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L719) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:803`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L803) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1664-1672`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1664-L1672) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mftb => { + let tbr = instr.spr(); + ctx.gpr[instr.rd()] = match tbr { + 268 => ctx.timebase & 0xFFFF_FFFF, + 269 => ctx.timebase >> 32, + _ => 0, + }; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Time-base register selectors.** The 10-bit `tbr` field encodes the same way as `mfspr`'s `spr` field (two halves swapped). The two values defined for the Xenon: + + | Decoded | Name | Meaning | + | --- | --- | --- | + | 268 | TBL | Time Base, lower 32 bits | + | 269 | TBU | Time Base, upper 32 bits | + + Other selectors return 0 in xenia and are not used by titles. +- **Atomic 64-bit read pattern.** Because `mftb` reads only 32 bits at a time, software performs the canonical retry loop to avoid TBLβ†’TBU rollover skew: + ```asm + retry: + mftbu rH ; read upper + mftb rL ; read lower (TBR=268) + mftbu rH2 ; read upper again + cmpw rH, rH2 + bne retry + ``` +- **Xenon clock rate.** Real hardware ticks the time base at ~3.2 GHz (one tick per CPU clock divided by the architectural ratio). The PVR signature the kernel exposes (`0x00710800`) and the kernel-reported tick rate jointly let titles convert TB ticks to seconds. +- **xenia behaviour.** xenia-rs stores `ctx.timebase` as a `u64` and **increments it once per interpreted instruction**, not per real-time wall clock. This guarantees deterministic replay (same trace β‡’ same TB readings) at the cost of decoupling guest time from host time. Games that rely on TB for real-time sync will run faster or slower depending on host throughput. +- **`mftb RT` (no operand)** is the simplified mnemonic for `mftb RT, 268` β€” read the lower half. `mftbu RT` ≑ `mftb RT, 269`. +- **Deprecated alternative.** `mfspr RT, 268`/`269` works on the Xenon (xenia accepts both) but post-PowerISA v2.06 deprecated reading TB through `mfspr`. Prefer `mftb`. + +## Related Instructions + +- [`mfspr`](mfspr.md) β€” generic SPR read; can also read TBL/TBU on Xenon (deprecated). +- [`mtspr`](mtspr.md) β€” TBL/TBU writes are privileged; not user-accessible. +- [`isync`](mtmsr.md) β€” context-synchronising fence sometimes paired with `mftb` for tight measurement loops. + +### Simplified Mnemonics + +| Simplified | Expansion | Notes | +| --- | --- | --- | +| `mftb RT` | `mftb RT, 268` | read TBL | +| `mftbu RT` | `mftb RT, 269` | read TBU | + +## IBM Reference + +- [AIX 7.3 β€” `mftb` (Move from Time Base)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mftb-move-from-time-base-instruction) +- PowerISA v2.07B, Book II Β§6.1 β€” Time Base description and the canonical 64-bit read sequence. diff --git a/tools/ppc-manual/control/mfvscr.md b/tools/ppc-manual/control/mfvscr.md new file mode 100644 index 00000000..bccfa371 --- /dev/null +++ b/tools/ppc-manual/control/mfvscr.md @@ -0,0 +1,137 @@ +# `mfvscr` β€” Move from VSCR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000604` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mfvscr` | `mfvscr` | β€” | Move from VSCR | + +## Syntax + +```asm +(no disassembly template) +``` + +## Encoding + +### `mfvscr` β€” form `VX` + +- **Opcode word:** `0x10000604` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1540` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VSCR` | mfvscr: read | Vector Status and Control Register (NJ/SAT bits). | +| `VD` | mfvscr: write | Destination vector register. | + +## Register Effects + +### `mfvscr` + +- **Reads (always):** `VSCR` +- **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 + +**`mfvscr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mfvscr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:303`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L303) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:539`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L539) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2506-2513`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2506-L2513) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mfvscr => { + // PPCBUG-080: ISA places VSCR in the rightmost word of VD with + // bytes 0-11 zeroed. Previously the full 128-bit ctx.vscr was + // copied (leaking stale upper data to guest). + let vscr_word = ctx.vscr.as_u32x4()[3]; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array([0, 0, 0, vscr_word]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Reads the 32-bit Vector Status and Control Register (VSCR) into the **low 32 bits of the rightmost word** of `VD` (the 128-bit vector register). The other 96 bits of `VD` are zeroed. PowerISA places the result at byte offset 12..15 (big-endian within the 128-bit register). +- **VSCR contents (Xenon-relevant).** + + | Bit | Name | Meaning | + | --- | --- | --- | + | 16 | NJ | Non-Java mode (denormal handling for IEEE-754 single-prec vector ops) | + | 31 | SAT | Saturation β€” sticky; set whenever a saturating vector op clamps | + + All other bits are reserved (zero). +- **`SAT` is sticky.** Once a saturating vector instruction clamps a result, `VSCR[SAT]` becomes 1 and stays 1 until explicitly cleared via [`mtvscr`](mtvscr.md). Software polls it after a vector batch to detect overflow. +- **`NJ` controls denormals.** When `NJ=1` (the Xenon's default), AltiVec single-precision ops flush denormal inputs/outputs to zero (non-IEEE behaviour); `NJ=0` enforces full IEEE. +- **VRSAVE.** Writing the entire 128-bit `VD` consumes a vector register slot; software wishing to honour [`VRSAVE`](mtspr.md) bookkeeping should ensure the chosen `VD` is in the live mask. +- **xenia simplification.** xenia-rs stores VSCR as a single value of the same `vr` type (effectively a u128) and copies it directly into `ctx.vr[VD]`. Saturating ops in xenia-rs **do** maintain SAT correctly for the vector ops that are implemented; NJ is honoured for the denormal-flush paths but its effect is small in practice. +- **Not synchronising.** + +## Related Instructions + +- [`mtvscr`](mtvscr.md) β€” write VSCR from a vector register (the inverse). +- [`mfspr`](mfspr.md) β€” for non-vector status registers; VSCR has its own opcode. +- AltiVec saturating-arithmetic ops (e.g., `vaddubs`, `vsubuhs`) β€” primary writers of `VSCR[SAT]`. + +`mfvscr` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mfvscr` (Move from VSCR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfvscr-move-from-vector-status-control-register-instruction) +- PowerISA v2.07B, Book I Β§6.6 β€” VSCR layout and the SAT / NJ definitions. diff --git a/tools/ppc-manual/control/mtcrf.md b/tools/ppc-manual/control/mtcrf.md new file mode 100644 index 00000000..e3b92b6e --- /dev/null +++ b/tools/ppc-manual/control/mtcrf.md @@ -0,0 +1,136 @@ +# `mtcrf` β€” Move to Condition Register Fields + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XFX](../forms/XFX.md) Β· **Opcode:** `0x7c000120` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtcrf` | `mtcrf` | β€” | Move to Condition Register Fields | + +## Syntax + +```asm +mtcrf [CRM], [RS] +``` + +## Encoding + +### `mtcrf` β€” form `XFX` + +- **Opcode word:** `0x7c000120` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `144` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination / source GPR | +| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask | +| 21–30 | `XO` | extended opcode | +| 31 | `β€”` | reserved | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | mtcrf: read | Source GPR (alias for RD in some stores). | +| `CRM` | mtcrf: write | 8-bit CR field mask used by `mtcrf` β€” one bit per CR field. | + +## Register Effects + +### `mtcrf` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRM` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +for i in 0..7: + if CRM[i] then CR[i] <- (RS)[32+i*4 : 35+i*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 + +**`mtcrf`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtcrf"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:732`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L732) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:779`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L779) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1631-1644`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1631-L1644) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtcrf => { + let crm = instr.crm(); + let val = ctx.gpr[instr.rs()] as u32; + let old = ctx.cr(); + let mut new = old; + for i in 0..8u32 { + if crm & (1 << (7 - i)) != 0 { + let mask = 0xF << (28 - i * 4); + new = (new & !mask) | (val & mask); + } + } + ctx.set_cr(new); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`CRM` is an 8-bit field-mask, MSB-first.** Each bit of `CRM` corresponds to one CR field: `CRM[0]` (mask bit `0x80`) selects CR0, `CRM[1]` (`0x40`) selects CR1, …, `CRM[7]` (`0x01`) selects CR7. Each *set* mask bit causes the corresponding 4-bit slice of `RS[32:63]` to overwrite that CR field; clear mask bits leave the field untouched. +- **Slice positions inside `RS`.** Big-endian: bits 32..35 of `RS` map to CR0, bits 36..39 to CR1, …, bits 60..63 to CR7. The high 32 bits of `RS` are ignored. +- **`mtcr RS` simplified mnemonic.** When `CRM = 0xFF`, all eight CR fields are written; assemblers fold this into `mtcr RS`. This is the dominant form (function epilogue restoring the saved CR). +- **`mtocrf` variant.** PowerISA defines `mtocrf` as the single-field variant β€” encoded with the high bit of FXM set and exactly one CRM bit set. xenia-rs treats both as the same opcode and processes whatever `CRM` mask is present, so `mtocrf` works correctly without special handling. +- **Use case in ABI.** Save/restore non-volatile CR fields (CR2, CR3, CR4 on the Xbox 360 ABI). The standard restore is `lwz r12, 8(r1); mtcrf 0x38, r12` β€” `0x38` = bits for CR2|CR3|CR4 β€” preserving the volatile fields the callee may have already updated. +- **No CR0 / XER side effects.** `mtcrf` does not record into CR0; XER is untouched. +- **xenia exact match.** xenia-rs decomposes the CR into a `u32`, applies a per-field mask, and reassembles via `set_cr`. The 8-bit `CRM` walk matches the spec exactly. +- **Not synchronising.** Reorderable. + +## Related Instructions + +- [`mfcr`](mfcr.md) β€” read the entire CR into a GPR. +- [`mcrf`](mcrf.md) β€” copy one CR field to another (no GPR involved). +- [`mcrxr`](mcrxr.md), [`mcrfs`](mcrfs.md) β€” narrower CR-field moves from XER / FPSCR. +- [`crand`](crand.md), [`cror`](cror.md), … β€” bit-level CR manipulation. + +### Simplified Mnemonics + +| Simplified | Expansion | Notes | +| --- | --- | --- | +| `mtcr RS` | `mtcrf 0xFF, RS` | write all eight CR fields from low half of RS | + +`mtocrf RS, FXM` is a related encoding handled by the same xenia-rs slot. + +## IBM Reference + +- [AIX 7.3 β€” `mtcrf` (Move to Condition Register Fields)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtcrf-move-condition-register-fields-instruction) +- [AIX 7.3 β€” Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified) diff --git a/tools/ppc-manual/control/mtfsb0x.md b/tools/ppc-manual/control/mtfsb0x.md new file mode 100644 index 00000000..2c562af9 --- /dev/null +++ b/tools/ppc-manual/control/mtfsb0x.md @@ -0,0 +1,133 @@ +# `mtfsb0x` β€” Move to FPSCR Bit 0 + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00008c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtfsb0` | `mtfsb0x` | β€” | Move to FPSCR Bit 0 | +| `mtfsb0.` | `mtfsb0x` | Rc=1 | Move to FPSCR Bit 0 | + +## Syntax + +```asm +mtfsb0[Rc] [FPSCRD] +``` + +## Encoding + +### `mtfsb0x` β€” form `X` + +- **Opcode word:** `0xfc00008c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `70` +- **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 | +| --- | --- | --- | +| `FPSCRD` | mtfsb0x: write | FPSCR destination field. | +| `CR` | mtfsb0x: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mtfsb0x` + +- **Reads (always):** _none_ +- **Reads (conditional):** _none_ +- **Writes (always):** `FPSCRD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mtfsb0x`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`mtfsb0x`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtfsb0x"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L406) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:905`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L905) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3055-3061`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3055-L3061) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtfsb0x => { + // Clear FPSCR bit crbd + let bit = instr.crbd(); + ctx.fpscr &= !(1 << (31 - bit)); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Clears (sets to 0) **a single named bit** of the 32-bit FPSCR. The bit is selected by `FPSCRD` (a 5-bit absolute index 0..31, big-endian: 0 = MSB = FX). +- **The mnemonic name "Bit 0" is misleading.** "0" refers to the *value being written*, not to bit position 0. Pair with [`mtfsb1x`](mtfsb1x.md) which writes a 1. +- **Restricted bits.** Per PowerISA, `mtfsb0` cannot clear bits 1 (FEX) or 2 (VX) β€” those are summary bits, derived from other FPSCR bits. xenia-rs does **not** enforce this restriction; it will happily flip any bit. In practice no Xbox 360 title relies on the restriction's enforcement. +- **`Rc=1`.** `mtfsb0.` (`Rc=1`) updates **CR1** with the high four FPSCR bits (FX, FEX, VX, OX) after the clear. This is the FPU's record-form analogue. +- **Common use.** Reset a sticky exception bit ahead of a sequence of FP ops you want to monitor (e.g. `mtfsb0 5` to clear ZX before a divide series, then read it back). +- **xenia simplification.** xenia-rs maintains FPSCR as a `u32` and does the bit clear correctly, but most downstream FP instructions in xenia **do not update** FPSCR exception bits β€” so monitoring them after `mtfsb0` will see the bits stay at their seed value. Acceptable for titles that use FPSCR only to manage rounding / non-exception state. +- **Not synchronising.** Reorderable. + +## Related Instructions + +- [`mtfsb1x`](mtfsb1x.md) β€” set a single FPSCR bit to 1. +- [`mtfsfx`](mtfsfx.md) β€” write fields of FPSCR from an FPR. +- [`mtfsfix`](mtfsfix.md) β€” write a 4-bit immediate into one FPSCR field. +- [`mffsx`](mffsx.md) β€” read FPSCR. +- [`mcrfs`](mcrfs.md) β€” copy FPSCR field β†’ CR field (and clear sticky bits). + +`mtfsb0` is itself the simplified form (`Rc=0`); `mtfsb0.` is the recording variant. + +## IBM Reference + +- [AIX 7.3 β€” `mtfsb0` (Move to FPSCR Bit 0)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtfsb0-move-fpscr-bit-0-instruction) +- PowerISA v2.07B, Book I Β§4.6 β€” FPSCR bit definitions and the FX/FEX/VX restriction. diff --git a/tools/ppc-manual/control/mtfsb1x.md b/tools/ppc-manual/control/mtfsb1x.md new file mode 100644 index 00000000..4b936a1e --- /dev/null +++ b/tools/ppc-manual/control/mtfsb1x.md @@ -0,0 +1,133 @@ +# `mtfsb1x` β€” Move to FPSCR Bit 1 + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00004c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtfsb1` | `mtfsb1x` | β€” | Move to FPSCR Bit 1 | +| `mtfsb1.` | `mtfsb1x` | Rc=1 | Move to FPSCR Bit 1 | + +## Syntax + +```asm +mtfsb1[Rc] [FPSCRD] +``` + +## Encoding + +### `mtfsb1x` β€” form `X` + +- **Opcode word:** `0xfc00004c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `38` +- **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 | +| --- | --- | --- | +| `FPSCRD` | mtfsb1x: write | FPSCR destination field. | +| `CR` | mtfsb1x: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mtfsb1x` + +- **Reads (always):** _none_ +- **Reads (conditional):** _none_ +- **Writes (always):** `FPSCRD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mtfsb1x`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`mtfsb1x`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtfsb1x"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:411`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L411) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:902`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L902) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3062-3068`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3062-L3068) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtfsb1x => { + // Set FPSCR bit crbd + let bit = instr.crbd(); + ctx.fpscr |= 1 << (31 - bit); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Sets (writes 1 to) **a single named bit** of FPSCR. `FPSCRD` is a 5-bit absolute index (0..31), big-endian (0 = MSB = FX). +- **Mnemonic name.** "1" denotes the *value written*, not bit position. Pair with [`mtfsb0x`](mtfsb0x.md) for clears. +- **Restricted bits.** PowerISA forbids `mtfsb1` from setting FEX (bit 1) or VX (bit 2) directly β€” both are summary bits derived from other state. `mtfsb1` *can* set FX (bit 0), which is itself a sticky summary; this is occasionally used to force a `Program` interrupt for testing. xenia-rs does **not** enforce the restriction; setting summary bits will stick until cleared explicitly. +- **`Rc=1`.** `mtfsb1.` (`Rc=1`) updates CR1 with the high four FPSCR bits (FX, FEX, VX, OX) after the set. +- **Common use.** Force-set a sticky exception bit to test exception-handling code paths. Also seen in floating-point library setup that wants a known FPSCR seed. +- **xenia simplification.** Same caveat as `mtfsb0`: xenia maintains FPSCR but most FP paths don't read it, so the set has limited downstream effect. The bit will read back correctly via [`mffsx`](mffsx.md). +- **Not synchronising.** Reorderable. + +## Related Instructions + +- [`mtfsb0x`](mtfsb0x.md) β€” clear a single FPSCR bit. +- [`mtfsfx`](mtfsfx.md) β€” write 4-bit FPSCR fields from an FPR. +- [`mtfsfix`](mtfsfix.md) β€” write 4-bit immediate into a single FPSCR field. +- [`mffsx`](mffsx.md) β€” read FPSCR. +- [`mcrfs`](mcrfs.md) β€” FPSCR field β†’ CR field (clears sticky bits). + +`mtfsb1` is the simplified form (`Rc=0`); `mtfsb1.` is the recording variant. + +## IBM Reference + +- [AIX 7.3 β€” `mtfsb1` (Move to FPSCR Bit 1)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtfsb1-move-fpscr-bit-1-instruction) +- PowerISA v2.07B, Book I Β§4.6 β€” FPSCR bit definitions and FX/FEX/VX restriction. diff --git a/tools/ppc-manual/control/mtfsfix.md b/tools/ppc-manual/control/mtfsfix.md new file mode 100644 index 00000000..9e258d52 --- /dev/null +++ b/tools/ppc-manual/control/mtfsfix.md @@ -0,0 +1,135 @@ +# `mtfsfix` β€” Move to FPSCR Field Immediate + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00010c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtfsfi` | `mtfsfix` | β€” | Move to FPSCR Field Immediate | +| `mtfsfi.` | `mtfsfix` | Rc=1 | Move to FPSCR Field Immediate | + +## Syntax + +```asm +mtfsfi[Rc] [CRFD], [IMM] +``` + +## Encoding + +### `mtfsfix` β€” form `X` + +- **Opcode word:** `0xfc00010c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `134` +- **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 | +| --- | --- | --- | +| `IMM` | mtfsfix: read | Generic immediate field. | +| `CRFD` | mtfsfix: write | CR destination field (`crf`, 0–7). | +| `CR` | mtfsfix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mtfsfix` + +- **Reads (always):** `IMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mtfsfix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## 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 + +**`mtfsfix`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtfsfix"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:452`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L452) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:907`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L907) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3069-3077`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3069-L3077) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtfsfix => { + // Move to FPSCR field immediate: crfD = IMM (4 bits) + let crfd = instr.crfd(); + let imm = (instr.raw >> 12) & 0xF; + let shift = 28 - crfd as u32 * 4; + ctx.fpscr = (ctx.fpscr & !(0xF << shift)) | (imm << shift); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Loads a **4-bit immediate** (`IMM`, encoded in instruction bits 16..19) into a single FPSCR field selected by `CRFD` (3-bit, 0..7). All other FPSCR fields are preserved. +- **Most common use: rounding-mode set.** `mtfsfi 7, 0` selects round-to-nearest, `mtfsfi 7, 1` round-toward-zero, `mtfsfi 7, 2` round-toward-+∞, `mtfsfi 7, 3` round-toward-βˆ’βˆž. The four immediate values map to RN per IEEE-754. Compilers emit this when transitioning into and out of strict-IEEE regions. +- **No FPR source.** Unlike [`mtfsfx`](mtfsfx.md), `mtfsfi` doesn't need an FPR β€” it carries its 4-bit value in the instruction word, making it cheaper for constant updates. +- **`Rc=1`.** `mtfsfi.` copies FPSCR's top 4 bits (FX, FEX, VX, OX) into CR1 after the write. +- **Restrictions in newer PowerISA.** v2.05+ disallows writing FEX/VX (summary bits) via `mtfsfi`. xenia-rs does **not** enforce this β€” the immediate goes straight into the chosen field. +- **xenia simplification.** xenia stores FPSCR as a `u32` and applies the field-shift correctly. Same caveat as `mtfsf`: most xenia FP paths don't honour FPSCR, so the rounding-mode change is architecturally visible (via [`mffsx`](mffsx.md)) but typically does not change subsequent FP results. +- **Not synchronising.** PowerISA recommends `isync` after rounding-mode changes if subsequent FP correctness depends on the new mode. + +## Related Instructions + +- [`mtfsfx`](mtfsfx.md) β€” write FPSCR fields from an FPR (uses 8-bit field-mask). +- [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md) β€” clear / set a single FPSCR bit. +- [`mffsx`](mffsx.md) β€” read FPSCR. +- [`mcrfs`](mcrfs.md) β€” FPSCR field β†’ CR field (also clears sticky bits). + +`mtfsfi` is the simplified form (`Rc=0`); `mtfsfi.` is the recording variant. + +## IBM Reference + +- [AIX 7.3 β€” `mtfsfi` (Move to FPSCR Field Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtfsfi-move-fpscr-field-immediate-instruction) +- PowerISA v2.07B, Book I Β§4.6.6 β€” FPSCR-field write semantics and the FEX/VX restriction. diff --git a/tools/ppc-manual/control/mtfsfx.md b/tools/ppc-manual/control/mtfsfx.md new file mode 100644 index 00000000..e0d6fd65 --- /dev/null +++ b/tools/ppc-manual/control/mtfsfx.md @@ -0,0 +1,142 @@ +# `mtfsfx` β€” Move to FPSCR Fields + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XFL](../forms/XFL.md) Β· **Opcode:** `0xfc00058e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtfsf` | `mtfsfx` | β€” | Move to FPSCR Fields | +| `mtfsf.` | `mtfsfx` | Rc=1 | Move to FPSCR Fields | + +## Syntax + +```asm +mtfsf[Rc] [FM], [FB] +``` + +## Encoding + +### `mtfsfx` β€” form `XFL` + +- **Opcode word:** `0xfc00058e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `711` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (63) | +| 6 | `L` | field-select behaviour | +| 7–14 | `FM` | FPSCR field mask | +| 15 | `W` | immediate-value flag | +| 16–20 | `FRB` | source FPR | +| 21–30 | `XO` | extended opcode | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FM` | mtfsfx: read | 8-bit FPSCR field-mask used by `mtfsf`. | +| `FB` | mtfsfx: read | Source B floating-point register. | +| `FPSCR` | mtfsfx: write | Floating-Point Status and Control Register. | +| `CR` | mtfsfx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `mtfsfx` + +- **Reads (always):** `FM`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `mtfsfx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`mtfsfx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtfsfx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:416`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L416) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:911`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L911) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3041-3054`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3041-L3054) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtfsfx => { + // Move to FPSCR fields: fm mask in bits 7-14, frB value + let fm = (instr.raw >> 17) & 0xFF; + let val = ctx.fpr[instr.rb()].to_bits() as u32; + let mut mask = 0u32; + for i in 0..8 { + if fm & (1 << (7 - i)) != 0 { + mask |= 0xF << (28 - i * 4); + } + } + ctx.fpscr = (ctx.fpscr & !mask) | (val & mask); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **`FM` is an 8-bit field-mask.** Each of the eight bits selects one 4-bit FPSCR field (0..7). `FM[0]` (`0x80`) selects FPSCR field 0 (bits 0..3 = FX, FEX, VX, OX), …, `FM[7]` (`0x01`) selects FPSCR field 7 (rounding-mode bits RN). Set bits cause that field of `FRB`'s low 32 bits to overwrite the corresponding FPSCR field; clear bits leave the field unchanged. +- **Source is the LOW 32 bits of `FRB`.** The high 32 bits are ignored. Software that wants to write a 32-bit pattern typically constructs it in a GPR, stores to memory, and reloads as a double via [`lfd`](../memory/lfd.md). +- **Most common use: setting rounding mode.** Compilers wrap calls to ``-style functions with `mtfsf 1, fX` to update only the rounding-mode field (RN, FPSCR field 7). +- **`Rc=1` updates CR1.** `mtfsf.` copies FPSCR's top 4 bits (FX, FEX, VX, OX) into CR1 after the write. +- **`L`/`W` bits.** PowerISA v2.05+ adds `L=1` to mean "write all FPSCR bits regardless of FM" and `W=1` to select the upper or lower 32 bits. xenia-rs **ignores** `L` and `W` (always treats `L=0, W=0`), which matches every real Xbox 360 use. +- **xenia simplification.** xenia maintains FPSCR as a `u32` and applies the field mask correctly. However, most FP instructions in xenia don't *read* FPSCR (e.g., divides ignore the rounding mode), so the architecturally-set rounding mode often has no actual effect on results. Acceptable for the title set xenia targets. +- **Not synchronising.** Reorderable; PowerISA recommends an `isync` after FPSCR changes that affect subsequent FP behaviour. + +## Related Instructions + +- [`mtfsfix`](mtfsfix.md) β€” write a 4-bit immediate into one FPSCR field (no FPR source needed). +- [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md) β€” single-bit FPSCR write. +- [`mffsx`](mffsx.md) β€” read FPSCR into an FPR. +- [`mcrfs`](mcrfs.md) β€” copy FPSCR field β†’ CR field. + +`mtfsf` is the simplified form (`Rc=0`); `mtfsf.` is the recording variant. + +## IBM Reference + +- [AIX 7.3 β€” `mtfsf` (Move to FPSCR Fields)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtfsf-move-fpscr-fields-instruction) +- PowerISA v2.07B, Book I Β§4.6.6 β€” `mtfsf` definition and `L`/`W` extensions. diff --git a/tools/ppc-manual/control/mtmsr.md b/tools/ppc-manual/control/mtmsr.md new file mode 100644 index 00000000..2c597af2 --- /dev/null +++ b/tools/ppc-manual/control/mtmsr.md @@ -0,0 +1,139 @@ +# `mtmsr` β€” Move to Machine State Register + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000124` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtmsr` | `mtmsr` | β€” | Move to Machine State Register | + +## Syntax + +```asm +mtmsr [RS] +``` + +## Encoding + +### `mtmsr` β€” form `X` + +- **Opcode word:** `0x7c000124` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `146` +- **Synchronising:** yes + +| 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` | mtmsr: read | Source GPR (alias for RD in some stores). | +| `MSR` | mtmsr: write | Machine State Register. | + +## Register Effects + +### `mtmsr` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `MSR` +- **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 + +**`mtmsr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtmsr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:822`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L822) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:780`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L780) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1649-1663`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1649-L1663) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtmsr | PpcOpcode::mtmsrd => { + // PPCBUG-078: mtmsrd L=1 is a partial-MSR-write β€” only MSR[EE] + // (u64 bit 15) and MSR[RI] (u64 bit 0) are modified; all other + // MSR bits preserved. Used by kernel code to re-enable external + // interrupts without disturbing the rest of the MSR. + let l = (instr.raw >> (31 - 15)) & 1; + let rs = ctx.gpr[instr.rs()]; + if matches!(instr.opcode, PpcOpcode::mtmsrd) && l == 1 { + let mask: u64 = (1u64 << 15) | 1u64; + ctx.msr = (ctx.msr & !mask) | (rs & mask); + } else { + ctx.msr = rs; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Privileged.** `mtmsr` is supervisor-only on real hardware. Executing it from problem state raises a Privileged Instruction interrupt. Game code never emits it; only the kernel and exception-return paths use it. +- **32-bit form.** `mtmsr` writes the **low 32 bits** of MSR (legacy PPC32 form). On the Xenon (a PPC64 implementation), use [`mtmsrd`](mtmsrd.md) for the full 64-bit MSR. Some Xenon kernel sequences still use `mtmsr` to leave the high half untouched while flipping low-half flags like EE/PR. +- **Synchronisation.** Marked `sync` β€” `mtmsr` is **execution-synchronising**. The Xenon must drain all preceding instructions before the new MSR takes effect, and PowerISA recommends a following `isync` to guarantee subsequent instructions execute under the new MSR. +- **`L` operand.** Modern PowerISA defines an `L` bit selecting "EE/RI only" (`L=1`) versus "all" (`L=0`); xenia-rs ignores `L` and writes the entire MSR. Real Xbox 360 kernel code uses both `L=0` and `L=1`. +- **xenia model.** Treats MSR as a flat `u64` field. Both `mtmsr` and `mtmsrd` execute the same body β€” `ctx.msr = ctx.gpr[rs]`. No privilege or atomicity is enforced; no side effects on TLB / interrupt mask / endianness are simulated. +- **No CR / XER side effects.** +- **Caveat for translators.** Because the host kernel runs natively in xenia, the guest MSR has no architectural meaning beyond storage. Code that reads it back via [`mfmsr`](mfmsr.md) will see exactly what was last written. + +## Related Instructions + +- [`mfmsr`](mfmsr.md) β€” read MSR. +- [`mtmsrd`](mtmsrd.md) β€” 64-bit form (writes the entire MSR). +- [`sc`](../branch/sc.md) β€” kernel entry; the kernel handler typically uses `mtmsr`/`rfid` to return. +- [`isync`](mtmsr.md) β€” companion fence after MSR writes. + +`mtmsr` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mtmsr` (Move to Machine State Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtmsr-move-machine-state-register-instruction) +- PowerISA v2.07B, Book III Β§4.3.1 β€” MSR field definitions and `L`-bit semantics. diff --git a/tools/ppc-manual/control/mtmsrd.md b/tools/ppc-manual/control/mtmsrd.md new file mode 100644 index 00000000..47f3c06c --- /dev/null +++ b/tools/ppc-manual/control/mtmsrd.md @@ -0,0 +1,138 @@ +# `mtmsrd` β€” Move to Machine State Register Doubleword + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000164` Β· _sync_ + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtmsrd` | `mtmsrd` | β€” | Move to Machine State Register Doubleword | + +## Syntax + +```asm +mtmsrd [RS] +``` + +## Encoding + +### `mtmsrd` β€” form `X` + +- **Opcode word:** `0x7c000164` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `178` +- **Synchronising:** yes + +| 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` | mtmsrd: read | Source GPR (alias for RD in some stores). | +| `MSR` | mtmsrd: write | Machine State Register. | + +## Register Effects + +### `mtmsrd` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `MSR` +- **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 + +**`mtmsrd`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtmsrd"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:827`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L827) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:785`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L785) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1649-1663`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1649-L1663) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtmsr | PpcOpcode::mtmsrd => { + // PPCBUG-078: mtmsrd L=1 is a partial-MSR-write β€” only MSR[EE] + // (u64 bit 15) and MSR[RI] (u64 bit 0) are modified; all other + // MSR bits preserved. Used by kernel code to re-enable external + // interrupts without disturbing the rest of the MSR. + let l = (instr.raw >> (31 - 15)) & 1; + let rs = ctx.gpr[instr.rs()]; + if matches!(instr.opcode, PpcOpcode::mtmsrd) && l == 1 { + let mask: u64 = (1u64 << 15) | 1u64; + ctx.msr = (ctx.msr & !mask) | (rs & mask); + } else { + ctx.msr = rs; + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Privileged.** Like [`mtmsr`](mtmsr.md), supervisor-only. Game code never emits it. +- **64-bit form.** Writes all 64 MSR bits β€” including `MSR[SF]` (bit 0) which selects 64-bit mode, `MSR[HV]` (bit 3, hypervisor), `MSR[EE]` (32, external interrupts), `MSR[PR]` (33, problem state), `MSR[FP]` (34), `MSR[ME]` (35, machine-check enable), `MSR[DR]`/`MSR[IR]` (data/instruction translation, 38/39), `MSR[RI]` (63, recoverable interrupt). On the Xenon kernel this is the canonical MSR-write instruction. +- **`L` operand.** Same `L`-bit selector as `mtmsr`: `L=1` updates only `MSR[EE]` and `MSR[RI]`; `L=0` updates the full register. xenia-rs ignores `L` and always writes the full doubleword (matching the typical kernel use). +- **Synchronisation.** Marked `sync` β€” execution-synchronising. PowerISA recommends `isync` afterwards if subsequent fetch / data semantics depend on the new MSR. +- **xenia model.** Shares one interpreter arm with `mtmsr`: `ctx.msr = ctx.gpr[rs]`. No architectural side effects beyond writing the storage; no privilege check. +- **No CR / XER updates.** +- **Used in interrupt return paths.** Kernel handlers commonly write SRR1 (saved MSR) into MSR via `mtmsrd` followed by `rfid` to atomically restore state and jump to SRR0. + +## Related Instructions + +- [`mfmsr`](mfmsr.md) β€” read MSR. +- [`mtmsr`](mtmsr.md) β€” 32-bit form (low half only). +- [`sc`](../branch/sc.md) β€” kernel entry; the kernel typically pairs `mtmsrd` + `rfid` to return. + +`mtmsrd` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mtmsrd` (Move to Machine State Register Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtmsrd-move-machine-state-register-doubleword-instruction) +- PowerISA v2.07B, Book III Β§4.3.1 β€” MSR field definitions, `L`-bit semantics, and 64-bit-mode rules. diff --git a/tools/ppc-manual/control/mtspr.md b/tools/ppc-manual/control/mtspr.md new file mode 100644 index 00000000..6103cde5 --- /dev/null +++ b/tools/ppc-manual/control/mtspr.md @@ -0,0 +1,161 @@ +# `mtspr` β€” Move to Special-Purpose Register + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [XFX](../forms/XFX.md) Β· **Opcode:** `0x7c0003a6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtspr` | `mtspr` | β€” | Move to Special-Purpose Register | + +## Syntax + +```asm +mtspr [SPR], [RS] +``` + +## Encoding + +### `mtspr` β€” form `XFX` + +- **Opcode word:** `0x7c0003a6` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `467` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination / source GPR | +| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask | +| 21–30 | `XO` | extended opcode | +| 31 | `β€”` | reserved | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `RS` | mtspr: read | Source GPR (alias for RD in some stores). | +| `SPR` | mtspr: write | Special-Purpose-Register number. Encoded with the two 5-bit halves swapped (bits 11-15 become the high half, bits 16-20 the low half). | + +## Register Effects + +### `mtspr` + +- **Reads (always):** `RS` +- **Reads (conditional):** _none_ +- **Writes (always):** `SPR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +n <- spr_number(SPR) +SPR(n) <- (RS) +``` + +## C Translation Example + +```c +/* 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 + +**`mtspr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtspr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:771`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L771) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:810`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L810) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1596-1626`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1596-L1626) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtspr => { + let spr = instr.spr(); + let val = ctx.gpr[instr.rs()]; + match spr { + crate::context::spr::XER => ctx.set_xer(val as u32), + crate::context::spr::LR => ctx.lr = val, + crate::context::spr::CTR => ctx.ctr = val as u32 as u64, + crate::context::spr::DEC => ctx.dec = val as u32, + crate::context::spr::TBL_WRITE => { + ctx.timebase = (ctx.timebase & 0xFFFF_FFFF_0000_0000) | (val & 0xFFFF_FFFF); + } + crate::context::spr::TBU_WRITE => { + ctx.timebase = (ctx.timebase & 0x0000_0000_FFFF_FFFF) | ((val & 0xFFFF_FFFF) << 32); + } + crate::context::spr::VRSAVE => ctx.vrsave = val as u32, + // Benign writes β€” swallow silently to avoid false Unimplemented + // warnings on SPRs that have no observable effect in userspace. + crate::context::spr::SPRG0 + | crate::context::spr::SPRG1 + | crate::context::spr::SPRG2 + | crate::context::spr::SPRG3 + | crate::context::spr::HID0 + | crate::context::spr::HID1 + | crate::context::spr::DAR + | crate::context::spr::DSISR => {} + _ => { + tracing::warn!("mtspr: unimplemented SPR {}", spr); + } + } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **SPR halves are swapped in the encoding.** As with [`mfspr`](mfspr.md), the 10-bit `spr` field stores the two 5-bit halves transposed. Software always names the *logical* SPR number; assemblers handle the swap. Decoded number `n = ((field & 0x1F) << 5) | ((field >> 5) & 0x1F)`. +- **SPRs writable from userspace (Xenon, modelled by xenia).** + + | Decoded # | Name | Effect | + | --- | --- | --- | + | 1 | XER | unpacked into `ctx.xer_so/xer_ov/xer_ca` and length field | + | 8 | LR | `ctx.lr ← RS` | + | 9 | CTR | `ctx.ctr ← RS` | + | 256 | VRSAVE | `ctx.vrsave ← RS & 0xFFFFFFFF` | + +- **SPRs xenia silently swallows (no observable effect).** SPRG0..3, HID0, HID1, DAR, DSISR β€” these are kernel/diagnostic registers; xenia accepts the write to avoid spurious "unimplemented SPR" warnings, but the value is discarded. +- **Privileged SPRs.** On real hardware, writes to MSR-visible kernel SPRs (SPRG0..3, HID0/1, DSISR, DAR, PIR, etc.) require supervisor mode and trap from problem state. xenia does **not** enforce privilege. +- **Time-base writes are privileged.** `mtspr 268/269` (TBL/TBU) only works in supervisor mode on real hardware. xenia will warn `mtspr: unimplemented SPR` for these β€” do **not** assume the time base can be guest-written. +- **Simplified mnemonics.** `mtxer RS` ≑ `mtspr 1, RS`, `mtlr RS` ≑ `mtspr 8, RS`, `mtctr RS` ≑ `mtspr 9, RS`. These dominate Xbox 360 disassembly. +- **No CR / XER side effects.** `mtspr` itself doesn't record (the *target* SPR may itself be XER, in which case XER is being directly overwritten). +- **Not synchronising.** xenia's XML omits the `sync` flag; PowerISA does require some `mtspr` cases (e.g. SDR1, MMU regs) to be context-synchronising β€” none of them appear in title binaries. + +## Related Instructions + +- [`mfspr`](mfspr.md) β€” inverse: read an SPR into a GPR. +- [`mftb`](mftb.md) β€” read time-base (preferred over `mfspr TBL/TBU`). +- [`mtmsr`](mtmsr.md), [`mtmsrd`](mtmsrd.md) β€” write MSR (separate opcode). +- [`mcrxr`](mcrxr.md) β€” sample-and-clear XER's overflow/carry bits. + +### Simplified Mnemonics + +| Simplified | Expansion | +| --- | --- | +| `mtxer RS` | `mtspr 1, RS` | +| `mtlr RS` | `mtspr 8, RS` | +| `mtctr RS` | `mtspr 9, RS` | + +## IBM Reference + +- [AIX 7.3 β€” `mtspr` (Move to Special Purpose Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtspr-move-special-purpose-register-instruction) +- PowerISA v2.07B, Book III Β§4 β€” SPR number table and privilege rules. diff --git a/tools/ppc-manual/control/mtvscr.md b/tools/ppc-manual/control/mtvscr.md new file mode 100644 index 00000000..ea991d34 --- /dev/null +++ b/tools/ppc-manual/control/mtvscr.md @@ -0,0 +1,125 @@ +# `mtvscr` β€” Move to VSCR + +> **Category:** [Control / CR / SPR](../categories/control.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000644` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `mtvscr` | `mtvscr` | β€” | Move to VSCR | + +## Syntax + +```asm +(no disassembly template) +``` + +## Encoding + +### `mtvscr` β€” form `VX` + +- **Opcode word:** `0x10000644` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1604` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | mtvscr: read | Source B vector register. | +| `VSCR` | mtvscr: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `mtvscr` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `mtvscr`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`mtvscr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="mtvscr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:310`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L310) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:542`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L542) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2514-2517`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2514-L2517) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::mtvscr => { + ctx.vscr = ctx.vr[instr.rb()]; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Operation.** Reads the **low 32 bits of the rightmost word** of `VB` (bytes 12..15 in big-endian) and stores them into VSCR. Other bits of `VB` are ignored. +- **Bits actually significant.** Of the 32 source bits, only **NJ (bit 16)** and **SAT (bit 31)** are architecturally meaningful on the Xenon. All other bits should be written as zero; behaviour for non-zero values is implementation-defined. +- **Clearing SAT.** The dominant use is `mtvscr vN` with `vN` zeroed via `vxor vN, vN, vN`, which writes VSCR=0 and thereby clears the sticky SAT bit before a fresh batch of saturating vector ops. +- **Setting NJ.** Switching to/from "Java mode" (`NJ=0`, full IEEE denormal handling) versus "Non-Java mode" (`NJ=1`, flush-to-zero) is the other meaningful use. Game audio / DSP code occasionally toggles this to match a precise IEEE expectation. +- **xenia simplification.** xenia-rs stores VSCR identically to a vector register and copies the source straight in: `ctx.vscr = ctx.vr[VB]`. Subsequent xenia AltiVec ops do consult `VSCR[SAT]` for sticky updates, so the architecturally-relevant behaviour is preserved. NJ's flush-to-zero semantics are honoured by xenia's vector denormal paths. +- **Not synchronising.** PowerISA does not require `isync` after `mtvscr`, but library code occasionally pairs them as a defensive measure. + +## Related Instructions + +- [`mfvscr`](mfvscr.md) β€” read VSCR into a vector register (the inverse). +- AltiVec saturating ops (`vaddubs`, `vsubuhs`, …) β€” primary writers of `VSCR[SAT]`; `mtvscr` is the only way to clear it. +- [`mtspr`](mtspr.md) β€” for non-vector control registers; VSCR has its own opcode. + +`mtvscr` has no simplified mnemonics. + +## IBM Reference + +- [AIX 7.3 β€” `mtvscr` (Move to VSCR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mtvscr-move-vector-status-control-register-instruction) +- PowerISA v2.07B, Book I Β§6.6 β€” VSCR layout, SAT / NJ semantics. diff --git a/tools/ppc-manual/forms/A.md b/tools/ppc-manual/forms/A.md new file mode 100644 index 00000000..f74d0cfb --- /dev/null +++ b/tools/ppc-manual/forms/A.md @@ -0,0 +1,43 @@ +# Form `A` β€” A β€” Arithmetic (three-source FPU) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`fdivsx`](../fpu/fdivsx.md) | `0xec000024` | fpu | Floating Divide Single | +| [`fsubsx`](../fpu/fsubsx.md) | `0xec000028` | fpu | Floating Subtract Single | +| [`faddsx`](../fpu/faddsx.md) | `0xec00002a` | fpu | Floating Add Single | +| [`fsqrtsx`](../fpu/fsqrtsx.md) | `0xec00002c` | fpu | Floating Square Root Single | +| [`fresx`](../fpu/fresx.md) | `0xec000030` | fpu | Floating Reciprocal Estimate Single | +| [`fmulsx`](../fpu/fmulsx.md) | `0xec000032` | fpu | Floating Multiply Single | +| [`fmsubsx`](../fpu/fmsubsx.md) | `0xec000038` | fpu | Floating Multiply-Subtract Single | +| [`fmaddsx`](../fpu/fmaddsx.md) | `0xec00003a` | fpu | Floating Multiply-Add Single | +| [`fnmsubsx`](../fpu/fnmsubsx.md) | `0xec00003c` | fpu | Floating Negative Multiply-Subtract Single | +| [`fnmaddsx`](../fpu/fnmaddsx.md) | `0xec00003e` | fpu | Floating Negative Multiply-Add Single | +| [`fdivx`](../fpu/fdivx.md) | `0xfc000024` | fpu | Floating Divide | +| [`fsubx`](../fpu/fsubx.md) | `0xfc000028` | fpu | Floating Subtract | +| [`faddx`](../fpu/faddx.md) | `0xfc00002a` | fpu | Floating Add | +| [`fsqrtx`](../fpu/fsqrtx.md) | `0xfc00002c` | fpu | Floating Square Root | +| [`fselx`](../fpu/fselx.md) | `0xfc00002e` | fpu | Floating Select | +| [`fmulx`](../fpu/fmulx.md) | `0xfc000032` | fpu | Floating Multiply | +| [`frsqrtex`](../fpu/frsqrtex.md) | `0xfc000034` | fpu | Floating Reciprocal Square Root Estimate | +| [`fmsubx`](../fpu/fmsubx.md) | `0xfc000038` | fpu | Floating Multiply-Subtract | +| [`fmaddx`](../fpu/fmaddx.md) | `0xfc00003a` | fpu | Floating Multiply-Add | +| [`fnmsubx`](../fpu/fnmsubx.md) | `0xfc00003c` | fpu | Floating Negative Multiply-Subtract | +| [`fnmaddx`](../fpu/fnmaddx.md) | `0xfc00003e` | fpu | Floating Negative Multiply-Add | + + diff --git a/tools/ppc-manual/forms/B.md b/tools/ppc-manual/forms/B.md new file mode 100644 index 00000000..423a44bf --- /dev/null +++ b/tools/ppc-manual/forms/B.md @@ -0,0 +1,22 @@ +# Form `B` β€” B β€” Conditional Branch + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `BO` | branch options | +| 11–15 | `BI` | CR bit to test | +| 16–29 | `BD` | signed 14-bit word-offset target | +| 30 | `AA` | absolute-address flag | +| 31 | `LK` | link flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`bcx`](../branch/bcx.md) | `0x40000000` | branch | Branch Conditional | + + diff --git a/tools/ppc-manual/forms/D.md b/tools/ppc-manual/forms/D.md new file mode 100644 index 00000000..182c44cb --- /dev/null +++ b/tools/ppc-manual/forms/D.md @@ -0,0 +1,59 @@ +# Form `D` β€” D β€” Displacement (load/store and immediate ALU) + +## Bit Layout + +| 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 | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`tdi`](../branch/tdi.md) | `0x08000000` | branch | Trap Doubleword Immediate | +| [`twi`](../branch/twi.md) | `0x0c000000` | branch | Trap Word Immediate | +| [`mulli`](../alu/mulli.md) | `0x1c000000` | integer | Multiply Low Immediate | +| [`subficx`](../alu/subficx.md) | `0x20000000` | integer | Subtract From Immediate Carrying | +| [`cmpli`](../alu/cmpli.md) | `0x28000000` | integer | Compare Logical Immediate | +| [`cmpi`](../alu/cmpi.md) | `0x2c000000` | integer | Compare Immediate | +| [`addic`](../alu/addic.md) | `0x30000000` | integer | Add Immediate Carrying | +| [`addic.`](../alu/addicx.md) | `0x34000000` | integer | Add Immediate Carrying and Record | +| [`addi`](../alu/addi.md) | `0x38000000` | integer | Add Immediate | +| [`addis`](../alu/addis.md) | `0x3c000000` | integer | Add Immediate Shifted | +| [`ori`](../alu/ori.md) | `0x60000000` | integer | OR Immediate | +| [`oris`](../alu/oris.md) | `0x64000000` | integer | OR Immediate Shifted | +| [`xori`](../alu/xori.md) | `0x68000000` | integer | XOR Immediate | +| [`xoris`](../alu/xoris.md) | `0x6c000000` | integer | XOR Immediate Shifted | +| [`andi.`](../alu/andix.md) | `0x70000000` | integer | AND Immediate | +| [`andis.`](../alu/andisx.md) | `0x74000000` | integer | AND Immediate Shifted | +| [`lwz`](../memory/lwz.md) | `0x80000000` | memory | Load Word and Zero | +| [`lwzu`](../memory/lwz.md) | `0x84000000` | memory | Load Word and Zero with Update | +| [`lbz`](../memory/lbz.md) | `0x88000000` | memory | Load Byte and Zero | +| [`lbzu`](../memory/lbz.md) | `0x8c000000` | memory | Load Byte and Zero with Update | +| [`stw`](../memory/stw.md) | `0x90000000` | memory | Store Word | +| [`stwu`](../memory/stw.md) | `0x94000000` | memory | Store Word with Update | +| [`stb`](../memory/stb.md) | `0x98000000` | memory | Store Byte | +| [`stbu`](../memory/stb.md) | `0x9c000000` | memory | Store Byte with Update | +| [`lhz`](../memory/lhz.md) | `0xa0000000` | memory | Load Half Word and Zero | +| [`lhzu`](../memory/lhz.md) | `0xa4000000` | memory | Load Half Word and Zero with Update | +| [`lha`](../memory/lha.md) | `0xa8000000` | memory | Load Half Word Algebraic | +| [`lhau`](../memory/lha.md) | `0xac000000` | memory | Load Half Word Algebraic with Update | +| [`sth`](../memory/sth.md) | `0xb0000000` | memory | Store Half Word | +| [`sthu`](../memory/sth.md) | `0xb4000000` | memory | Store Half Word with Update | +| [`lmw`](../memory/lmw.md) | `0xb8000000` | memory | Load Multiple Word | +| [`stmw`](../memory/stmw.md) | `0xbc000000` | memory | Store Multiple Word | +| [`lfs`](../memory/lfs.md) | `0xc0000000` | memory | Load Floating-Point Single | +| [`lfsu`](../memory/lfs.md) | `0xc4000000` | memory | Load Floating-Point Single with Update | +| [`lfd`](../memory/lfd.md) | `0xc8000000` | memory | Load Floating-Point Double | +| [`lfdu`](../memory/lfd.md) | `0xcc000000` | memory | Load Floating-Point Double with Update | +| [`stfs`](../memory/stfs.md) | `0xd0000000` | memory | Store Floating-Point Single | +| [`stfsu`](../memory/stfs.md) | `0xd4000000` | memory | Store Floating-Point Single with Update | +| [`stfd`](../memory/stfd.md) | `0xd8000000` | memory | Store Floating-Point Double | +| [`stfdu`](../memory/stfd.md) | `0xdc000000` | memory | Store Floating-Point Double with Update | + + diff --git a/tools/ppc-manual/forms/DCBZ.md b/tools/ppc-manual/forms/DCBZ.md new file mode 100644 index 00000000..42e3e3f8 --- /dev/null +++ b/tools/ppc-manual/forms/DCBZ.md @@ -0,0 +1,23 @@ +# Form `DCBZ` β€” DCBZ β€” Cache Block Zeroing (special X variant) + +## Bit Layout + +| 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 | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`dcbz`](../memory/dcbz.md) | `0x7c0007ec` | memory | Data Cache Block Clear to Zero | +| [`dcbz128`](../memory/dcbz.md) | `0x7c2007ec` | memory | Data Cache Block Clear to Zero 128 | + + diff --git a/tools/ppc-manual/forms/DS.md b/tools/ppc-manual/forms/DS.md new file mode 100644 index 00000000..3046292c --- /dev/null +++ b/tools/ppc-manual/forms/DS.md @@ -0,0 +1,25 @@ +# Form `DS` β€” DS β€” Doubleword Shift (word-scaled displacement) + +## Bit Layout + +| 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 | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`ld`](../memory/ld.md) | `0xe8000000` | memory | Load Doubleword | +| [`ldu`](../memory/ld.md) | `0xe8000001` | memory | Load Doubleword with Update | +| [`lwa`](../memory/lwa.md) | `0xe8000002` | memory | Load Word Algebraic | +| [`std`](../memory/std.md) | `0xf8000000` | memory | Store Doubleword | +| [`stdu`](../memory/std.md) | `0xf8000001` | memory | Store Doubleword with Update | + + diff --git a/tools/ppc-manual/forms/I.md b/tools/ppc-manual/forms/I.md new file mode 100644 index 00000000..2597a6d9 --- /dev/null +++ b/tools/ppc-manual/forms/I.md @@ -0,0 +1,20 @@ +# Form `I` β€” I β€” Immediate Branch + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–29 | `LI` | signed 24-bit word-offset target | +| 30 | `AA` | absolute-address flag | +| 31 | `LK` | link flag (bl/ba/bla) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`bx`](../branch/bx.md) | `0x48000000` | branch | Branch | + + diff --git a/tools/ppc-manual/forms/M.md b/tools/ppc-manual/forms/M.md new file mode 100644 index 00000000..3ad42eed --- /dev/null +++ b/tools/ppc-manual/forms/M.md @@ -0,0 +1,25 @@ +# Form `M` β€” M β€” Mask (rlwinm/rlwimi/rlwnm) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `SH/RB` | shift amount or source B | +| 21–25 | `MB` | mask begin | +| 26–30 | `ME` | mask end | +| 31 | `Rc` | record-form flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`rlwimix`](../alu/rlwimix.md) | `0x50000000` | integer | Rotate Left Word Immediate then Mask Insert | +| [`rlwinmx`](../alu/rlwinmx.md) | `0x54000000` | integer | Rotate Left Word Immediate then AND with Mask | +| [`rlwnmx`](../alu/rlwnmx.md) | `0x5c000000` | integer | Rotate Left Word then AND with Mask | + + diff --git a/tools/ppc-manual/forms/MD.md b/tools/ppc-manual/forms/MD.md new file mode 100644 index 00000000..a2f43ec7 --- /dev/null +++ b/tools/ppc-manual/forms/MD.md @@ -0,0 +1,27 @@ +# Form `MD` β€” MD β€” Mask Double (rldicr/rldicl/rldic/rldimi) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–29 | `XO` | extended opcode | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`rldiclx`](../alu/rldiclx.md) | `0x78000000` | integer | Rotate Left Doubleword Immediate then Clear Left | +| [`rldicrx`](../alu/rldicrx.md) | `0x78000004` | integer | Rotate Left Doubleword Immediate then Clear Right | +| [`rldicx`](../alu/rldicx.md) | `0x78000008` | integer | Rotate Left Doubleword Immediate then Clear | +| [`rldimix`](../alu/rldimix.md) | `0x7800000c` | integer | Rotate Left Doubleword Immediate then Mask Insert | + + diff --git a/tools/ppc-manual/forms/MDS.md b/tools/ppc-manual/forms/MDS.md new file mode 100644 index 00000000..c5965829 --- /dev/null +++ b/tools/ppc-manual/forms/MDS.md @@ -0,0 +1,24 @@ +# Form `MDS` β€” MDS β€” Mask Double, Shift-by-register (rldcl/rldcr) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (30) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `RB` | source B GPR | +| 21–26 | `mb/me` | 6-bit mask field (swapped halves) | +| 27–30 | `XO` | extended opcode | +| 31 | `Rc` | record-form flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`rldclx`](../alu/rldclx.md) | `0x78000010` | integer | Rotate Left Doubleword then Clear Left | +| [`rldcrx`](../alu/rldcrx.md) | `0x78000012` | integer | Rotate Left Doubleword then Clear Right | + + diff --git a/tools/ppc-manual/forms/SC.md b/tools/ppc-manual/forms/SC.md new file mode 100644 index 00000000..d291b913 --- /dev/null +++ b/tools/ppc-manual/forms/SC.md @@ -0,0 +1,22 @@ +# Form `SC` β€” SC β€” System Call + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (17) | +| 6–19 | `β€”` | reserved | +| 20–26 | `LEV` | exception level | +| 27–29 | `β€”` | reserved | +| 30 | `1` | fixed 1 | +| 31 | `β€”` | reserved | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`sc`](../branch/sc.md) | `0x44000002` | branch | System Call | + + diff --git a/tools/ppc-manual/forms/VA.md b/tools/ppc-manual/forms/VA.md new file mode 100644 index 00000000..c8466d26 --- /dev/null +++ b/tools/ppc-manual/forms/VA.md @@ -0,0 +1,35 @@ +# Form `VA` β€” VA β€” Vector Arithmetic (4-operand, madd-style) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vmhaddshs`](../vmx/vmhaddshs.md) | `0x10000020` | vmx | Vector Multiply-High and Add Signed Signed Half Word Saturate | +| [`vmhraddshs`](../vmx/vmhraddshs.md) | `0x10000021` | vmx | Vector Multiply-High Round and Add Signed Signed Half Word Saturate | +| [`vmladduhm`](../vmx/vmladduhm.md) | `0x10000022` | vmx | Vector Multiply-Low and Add Unsigned Half Word Modulo | +| [`vmsumubm`](../vmx/vmsumubm.md) | `0x10000024` | vmx | Vector Multiply-Sum Unsigned Byte Modulo | +| [`vmsummbm`](../vmx/vmsummbm.md) | `0x10000025` | vmx | Vector Multiply-Sum Mixed-Sign Byte Modulo | +| [`vmsumuhm`](../vmx/vmsumuhm.md) | `0x10000026` | vmx | Vector Multiply-Sum Unsigned Half Word Modulo | +| [`vmsumuhs`](../vmx/vmsumuhs.md) | `0x10000027` | vmx | Vector Multiply-Sum Unsigned Half Word Saturate | +| [`vmsumshm`](../vmx/vmsumshm.md) | `0x10000028` | vmx | Vector Multiply-Sum Signed Half Word Modulo | +| [`vmsumshs`](../vmx/vmsumshs.md) | `0x10000029` | vmx | Vector Multiply-Sum Signed Half Word Saturate | +| [`vsel`](../vmx/vsel.md) | `0x1000002a` | vmx | Vector Conditional Select | +| [`vperm`](../vmx/vperm.md) | `0x1000002b` | vmx | Vector Permute | +| [`vsldoi`](../vmx/vsldoi.md) | `0x1000002c` | vmx | Vector Shift Left Double by Octet Immediate | +| [`vmaddfp`](../vmx/vmaddfp.md) | `0x1000002e` | vmx | Vector Multiply-Add Floating Point | +| [`vnmsubfp`](../vmx/vnmsubfp.md) | `0x1000002f` | vmx | Vector Negative Multiply-Subtract Floating Point | + + diff --git a/tools/ppc-manual/forms/VC.md b/tools/ppc-manual/forms/VC.md new file mode 100644 index 00000000..4e7fd0d9 --- /dev/null +++ b/tools/ppc-manual/forms/VC.md @@ -0,0 +1,34 @@ +# Form `VC` β€” VC β€” Vector Compare (with Rc β†’ CR6) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vcmpequb`](../vmx/vcmpequb.md) | `0x10000006` | vmx | Vector Compare Equal-to Unsigned Byte | +| [`vcmpequh`](../vmx/vcmpequh.md) | `0x10000046` | vmx | Vector Compare Equal-to Unsigned Half Word | +| [`vcmpequw`](../vmx/vcmpequw.md) | `0x10000086` | vmx | Vector Compare Equal-to Unsigned Word | +| [`vcmpeqfp`](../vmx/vcmpeqfp.md) | `0x100000c6` | vmx | Vector Compare Equal-to Floating Point | +| [`vcmpgefp`](../vmx/vcmpgefp.md) | `0x100001c6` | vmx | Vector Compare Greater-Than-or-Equal-to Floating Point | +| [`vcmpgtub`](../vmx/vcmpgtub.md) | `0x10000206` | vmx | Vector Compare Greater-Than Unsigned Byte | +| [`vcmpgtuh`](../vmx/vcmpgtuh.md) | `0x10000246` | vmx | Vector Compare Greater-Than Unsigned Half Word | +| [`vcmpgtuw`](../vmx/vcmpgtuw.md) | `0x10000286` | vmx | Vector Compare Greater-Than Unsigned Word | +| [`vcmpgtfp`](../vmx/vcmpgtfp.md) | `0x100002c6` | vmx | Vector Compare Greater-Than Floating Point | +| [`vcmpgtsb`](../vmx/vcmpgtsb.md) | `0x10000306` | vmx | Vector Compare Greater-Than Signed Byte | +| [`vcmpgtsh`](../vmx/vcmpgtsh.md) | `0x10000346` | vmx | Vector Compare Greater-Than Signed Half Word | +| [`vcmpgtsw`](../vmx/vcmpgtsw.md) | `0x10000386` | vmx | Vector Compare Greater-Than Signed Word | +| [`vcmpbfp`](../vmx/vcmpbfp.md) | `0x100003c6` | vmx | Vector Compare Bounds Floating Point | + + diff --git a/tools/ppc-manual/forms/VX.md b/tools/ppc-manual/forms/VX.md new file mode 100644 index 00000000..f6b73658 --- /dev/null +++ b/tools/ppc-manual/forms/VX.md @@ -0,0 +1,137 @@ +# Form `VX` β€” VX β€” Vector (3-operand Altivec) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vaddubm`](../vmx/vaddubm.md) | `0x10000000` | vmx | Vector Add Unsigned Byte Modulo | +| [`vmaxub`](../vmx/vmaxub.md) | `0x10000002` | vmx | Vector Maximum Unsigned Byte | +| [`vrlb`](../vmx/vrlb.md) | `0x10000004` | vmx | Vector Rotate Left Integer Byte | +| [`vmuloub`](../vmx/vmuloub.md) | `0x10000008` | vmx | Vector Multiply Odd Unsigned Byte | +| [`vaddfp`](../vmx/vaddfp.md) | `0x1000000a` | vmx | Vector Add Floating Point | +| [`vmrghb`](../vmx/vmrghb.md) | `0x1000000c` | vmx | Vector Merge High Byte | +| [`vpkuhum`](../vmx/vpkuhum.md) | `0x1000000e` | vmx | Vector Pack Unsigned Half Word Unsigned Modulo | +| [`vadduhm`](../vmx/vadduhm.md) | `0x10000040` | vmx | Vector Add Unsigned Half Word Modulo | +| [`vmaxuh`](../vmx/vmaxuh.md) | `0x10000042` | vmx | Vector Maximum Unsigned Half Word | +| [`vrlh`](../vmx/vrlh.md) | `0x10000044` | vmx | Vector Rotate Left Integer Half Word | +| [`vmulouh`](../vmx/vmulouh.md) | `0x10000048` | vmx | Vector Multiply Odd Unsigned Half Word | +| [`vsubfp`](../vmx/vsubfp.md) | `0x1000004a` | vmx | Vector Subtract Floating Point | +| [`vmrghh`](../vmx/vmrghh.md) | `0x1000004c` | vmx | Vector Merge High Half Word | +| [`vpkuwum`](../vmx/vpkuwum.md) | `0x1000004e` | vmx | Vector Pack Unsigned Word Unsigned Modulo | +| [`vadduwm`](../vmx/vadduwm.md) | `0x10000080` | vmx | Vector Add Unsigned Word Modulo | +| [`vmaxuw`](../vmx/vmaxuw.md) | `0x10000082` | vmx | Vector Maximum Unsigned Word | +| [`vrlw`](../vmx/vrlw.md) | `0x10000084` | vmx | Vector Rotate Left Integer Word | +| [`vmrghw`](../vmx/vmrghw.md) | `0x1000008c` | vmx | Vector Merge High Word | +| [`vpkuhus`](../vmx/vpkuhus.md) | `0x1000008e` | vmx | Vector Pack Unsigned Half Word Unsigned Saturate | +| [`vpkuwus`](../vmx/vpkuwus.md) | `0x100000ce` | vmx | Vector Pack Unsigned Word Unsigned Saturate | +| [`vmaxsb`](../vmx/vmaxsb.md) | `0x10000102` | vmx | Vector Maximum Signed Byte | +| [`vslb`](../vmx/vslb.md) | `0x10000104` | vmx | Vector Shift Left Integer Byte | +| [`vmulosb`](../vmx/vmulosb.md) | `0x10000108` | vmx | Vector Multiply Odd Signed Byte | +| [`vrefp`](../vmx/vrefp.md) | `0x1000010a` | vmx | Vector Reciprocal Estimate Floating Point | +| [`vmrglb`](../vmx/vmrglb.md) | `0x1000010c` | vmx | Vector Merge Low Byte | +| [`vpkshus`](../vmx/vpkshus.md) | `0x1000010e` | vmx | Vector Pack Signed Half Word Unsigned Saturate | +| [`vmaxsh`](../vmx/vmaxsh.md) | `0x10000142` | vmx | Vector Maximum Signed Half Word | +| [`vslh`](../vmx/vslh.md) | `0x10000144` | vmx | Vector Shift Left Integer Half Word | +| [`vmulosh`](../vmx/vmulosh.md) | `0x10000148` | vmx | Vector Multiply Odd Signed Half Word | +| [`vrsqrtefp`](../vmx/vrsqrtefp.md) | `0x1000014a` | vmx | Vector Reciprocal Square Root Estimate Floating Point | +| [`vmrglh`](../vmx/vmrglh.md) | `0x1000014c` | vmx | Vector Merge Low Half Word | +| [`vpkswus`](../vmx/vpkswus.md) | `0x1000014e` | vmx | Vector Pack Signed Word Unsigned Saturate | +| [`vaddcuw`](../vmx/vaddcuw.md) | `0x10000180` | vmx | Vector Add Carryout Unsigned Word | +| [`vmaxsw`](../vmx/vmaxsw.md) | `0x10000182` | vmx | Vector Maximum Signed Word | +| [`vslw`](../vmx/vslw.md) | `0x10000184` | vmx | Vector Shift Left Integer Word | +| [`vexptefp`](../vmx/vexptefp.md) | `0x1000018a` | vmx | Vector 2 Raised to the Exponent Estimate Floating Point | +| [`vmrglw`](../vmx/vmrglw.md) | `0x1000018c` | vmx | Vector Merge Low Word | +| [`vpkshss`](../vmx/vpkshss.md) | `0x1000018e` | vmx | Vector Pack Signed Half Word Signed Saturate | +| [`vsl`](../vmx/vsl.md) | `0x100001c4` | vmx | Vector Shift Left | +| [`vlogefp`](../vmx/vlogefp.md) | `0x100001ca` | vmx | Vector Log2 Estimate Floating Point | +| [`vpkswss`](../vmx/vpkswss.md) | `0x100001ce` | vmx | Vector Pack Signed Word Signed Saturate | +| [`vaddubs`](../vmx/vaddubs.md) | `0x10000200` | vmx | Vector Add Unsigned Byte Saturate | +| [`vminub`](../vmx/vminub.md) | `0x10000202` | vmx | Vector Minimum Unsigned Byte | +| [`vsrb`](../vmx/vsrb.md) | `0x10000204` | vmx | Vector Shift Right Byte | +| [`vmuleub`](../vmx/vmuleub.md) | `0x10000208` | vmx | Vector Multiply Even Unsigned Byte | +| [`vrfin`](../vmx/vrfin.md) | `0x1000020a` | vmx | Vector Round to Floating-Point Integer Nearest | +| [`vspltb`](../vmx/vspltb.md) | `0x1000020c` | vmx | Vector Splat Byte | +| [`vupkhsb`](../vmx/vupkhsb.md) | `0x1000020e` | vmx | Vector Unpack High Signed Byte | +| [`vadduhs`](../vmx/vadduhs.md) | `0x10000240` | vmx | Vector Add Unsigned Half Word Saturate | +| [`vminuh`](../vmx/vminuh.md) | `0x10000242` | vmx | Vector Minimum Unsigned Half Word | +| [`vsrh`](../vmx/vsrh.md) | `0x10000244` | vmx | Vector Shift Right Half Word | +| [`vmuleuh`](../vmx/vmuleuh.md) | `0x10000248` | vmx | Vector Multiply Even Unsigned Half Word | +| [`vrfiz`](../vmx/vrfiz.md) | `0x1000024a` | vmx | Vector Round to Floating-Point Integer toward Zero | +| [`vsplth`](../vmx/vsplth.md) | `0x1000024c` | vmx | Vector Splat Half Word | +| [`vupkhsh`](../vmx/vupkhsh.md) | `0x1000024e` | vmx | Vector Unpack High Signed Half Word | +| [`vadduws`](../vmx/vadduws.md) | `0x10000280` | vmx | Vector Add Unsigned Word Saturate | +| [`vminuw`](../vmx/vminuw.md) | `0x10000282` | vmx | Vector Minimum Unsigned Word | +| [`vsrw`](../vmx/vsrw.md) | `0x10000284` | vmx | Vector Shift Right Word | +| [`vrfip`](../vmx/vrfip.md) | `0x1000028a` | vmx | Vector Round to Floating-Point Integer toward +Infinity | +| [`vspltw`](../vmx/vspltw.md) | `0x1000028c` | vmx | Vector Splat Word | +| [`vupklsb`](../vmx/vupklsb.md) | `0x1000028e` | vmx | Vector Unpack Low Signed Byte | +| [`vsr`](../vmx/vsr.md) | `0x100002c4` | vmx | Vector Shift Right | +| [`vrfim`](../vmx/vrfim.md) | `0x100002ca` | vmx | Vector Round to Floating-Point Integer toward -Infinity | +| [`vupklsh`](../vmx/vupklsh.md) | `0x100002ce` | vmx | Vector Unpack Low Signed Half Word | +| [`vaddsbs`](../vmx/vaddsbs.md) | `0x10000300` | vmx | Vector Add Signed Byte Saturate | +| [`vminsb`](../vmx/vminsb.md) | `0x10000302` | vmx | Vector Minimum Signed Byte | +| [`vsrab`](../vmx/vsrab.md) | `0x10000304` | vmx | Vector Shift Right Algebraic Byte | +| [`vmulesb`](../vmx/vmulesb.md) | `0x10000308` | vmx | Vector Multiply Even Signed Byte | +| [`vcfux`](../vmx/vcfux.md) | `0x1000030a` | vmx | Vector Convert from Unsigned Fixed-Point Word | +| [`vspltisb`](../vmx/vspltisb.md) | `0x1000030c` | vmx | Vector Splat Immediate Signed Byte | +| [`vpkpx`](../vmx/vpkpx.md) | `0x1000030e` | vmx | Vector Pack Pixel | +| [`vaddshs`](../vmx/vaddshs.md) | `0x10000340` | vmx | Vector Add Signed Half Word Saturate | +| [`vminsh`](../vmx/vminsh.md) | `0x10000342` | vmx | Vector Minimum Signed Half Word | +| [`vsrah`](../vmx/vsrah.md) | `0x10000344` | vmx | Vector Shift Right Algebraic Half Word | +| [`vmulesh`](../vmx/vmulesh.md) | `0x10000348` | vmx | Vector Multiply Even Signed Half Word | +| [`vcfsx`](../vmx/vcfsx.md) | `0x1000034a` | vmx | Vector Convert from Signed Fixed-Point Word | +| [`vspltish`](../vmx/vspltish.md) | `0x1000034c` | vmx | Vector Splat Immediate Signed Half Word | +| [`vupkhpx`](../vmx/vupkhpx.md) | `0x1000034e` | vmx | Vector Unpack High Pixel | +| [`vaddsws`](../vmx/vaddsws.md) | `0x10000380` | vmx | Vector Add Signed Word Saturate | +| [`vminsw`](../vmx/vminsw.md) | `0x10000382` | vmx | Vector Minimum Signed Word | +| [`vsraw`](../vmx/vsraw.md) | `0x10000384` | vmx | Vector Shift Right Algebraic Word | +| [`vctuxs`](../vmx/vctuxs.md) | `0x1000038a` | vmx | Vector Convert to Unsigned Fixed-Point Word Saturate | +| [`vspltisw`](../vmx/vspltisw.md) | `0x1000038c` | vmx | Vector Splat Immediate Signed Word | +| [`vctsxs`](../vmx/vctsxs.md) | `0x100003ca` | vmx | Vector Convert to Signed Fixed-Point Word Saturate | +| [`vupklpx`](../vmx/vupklpx.md) | `0x100003ce` | vmx | Vector Unpack Low Pixel | +| [`vsububm`](../vmx/vsububm.md) | `0x10000400` | vmx | Vector Subtract Unsigned Byte Modulo | +| [`vavgub`](../vmx/vavgub.md) | `0x10000402` | vmx | Vector Average Unsigned Byte | +| [`vand`](../vmx/vand.md) | `0x10000404` | vmx | Vector Logical AND | +| [`vmaxfp`](../vmx/vmaxfp.md) | `0x1000040a` | vmx | Vector Maximum Floating Point | +| [`vslo`](../vmx/vslo.md) | `0x1000040c` | vmx | Vector Shift Left by Octet | +| [`vsubuhm`](../vmx/vsubuhm.md) | `0x10000440` | vmx | Vector Subtract Unsigned Half Word Modulo | +| [`vavguh`](../vmx/vavguh.md) | `0x10000442` | vmx | Vector Average Unsigned Half Word | +| [`vandc`](../vmx/vandc.md) | `0x10000444` | vmx | Vector Logical AND with Complement | +| [`vminfp`](../vmx/vminfp.md) | `0x1000044a` | vmx | Vector Minimum Floating Point | +| [`vsro`](../vmx/vsro.md) | `0x1000044c` | vmx | Vector Shift Right Octet | +| [`vsubuwm`](../vmx/vsubuwm.md) | `0x10000480` | vmx | Vector Subtract Unsigned Word Modulo | +| [`vavguw`](../vmx/vavguw.md) | `0x10000482` | vmx | Vector Average Unsigned Word | +| [`vor`](../vmx/vor.md) | `0x10000484` | vmx | Vector Logical OR | +| [`vxor`](../vmx/vxor.md) | `0x100004c4` | vmx | Vector Logical XOR | +| [`vavgsb`](../vmx/vavgsb.md) | `0x10000502` | vmx | Vector Average Signed Byte | +| [`vnor`](../vmx/vnor.md) | `0x10000504` | vmx | Vector Logical NOR | +| [`vavgsh`](../vmx/vavgsh.md) | `0x10000542` | vmx | Vector Average Signed Half Word | +| [`vsubcuw`](../vmx/vsubcuw.md) | `0x10000580` | vmx | Vector Subtract Carryout Unsigned Word | +| [`vavgsw`](../vmx/vavgsw.md) | `0x10000582` | vmx | Vector Average Signed Word | +| [`vsububs`](../vmx/vsububs.md) | `0x10000600` | vmx | Vector Subtract Unsigned Byte Saturate | +| [`mfvscr`](../control/mfvscr.md) | `0x10000604` | control | Move from VSCR | +| [`vsum4ubs`](../vmx/vsum4ubs.md) | `0x10000608` | vmx | Vector Sum Across Partial (1/4) Unsigned Byte Saturate | +| [`vsubuhs`](../vmx/vsubuhs.md) | `0x10000640` | vmx | Vector Subtract Unsigned Half Word Saturate | +| [`mtvscr`](../control/mtvscr.md) | `0x10000644` | control | Move to VSCR | +| [`vsum4shs`](../vmx/vsum4shs.md) | `0x10000648` | vmx | Vector Sum Across Partial (1/4) Signed Half Word Saturate | +| [`vsubuws`](../vmx/vsubuws.md) | `0x10000680` | vmx | Vector Subtract Unsigned Word Saturate | +| [`vsum2sws`](../vmx/vsum2sws.md) | `0x10000688` | vmx | Vector Sum Across Partial (1/2) Signed Word Saturate | +| [`vsubsbs`](../vmx/vsubsbs.md) | `0x10000700` | vmx | Vector Subtract Signed Byte Saturate | +| [`vsum4sbs`](../vmx/vsum4sbs.md) | `0x10000708` | vmx | Vector Sum Across Partial (1/4) Signed Byte Saturate | +| [`vsubshs`](../vmx/vsubshs.md) | `0x10000740` | vmx | Vector Subtract Signed Half Word Saturate | +| [`vsubsws`](../vmx/vsubsws.md) | `0x10000780` | vmx | Vector Subtract Signed Word Saturate | +| [`vsumsws`](../vmx/vsumsws.md) | `0x10000788` | vmx | Vector Sum Across Signed Word Saturate | + + diff --git a/tools/ppc-manual/forms/VX128.md b/tools/ppc-manual/forms/VX128.md new file mode 100644 index 00000000..e22b37ea --- /dev/null +++ b/tools/ppc-manual/forms/VX128.md @@ -0,0 +1,60 @@ +# Form `VX128` β€” VX128 β€” VMX128 3-operand (register-fused) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vaddfp128`](../vmx128/vaddfp.md) | `0x14000010` | vmx | Vector128 Add Floating Point | +| [`vsubfp128`](../vmx128/vsubfp.md) | `0x14000050` | vmx | Vector128 Subtract Floating Point | +| [`vmulfp128`](../vmx128/vmulfp128.md) | `0x14000090` | vmx | Vector128 Multiply Floating-Point | +| [`vmaddfp128`](../vmx128/vmaddfp.md) | `0x140000d0` | vmx | Vector128 Multiply Add Floating Point | +| [`vmaddcfp128`](../vmx128/vmaddcfp128.md) | `0x14000110` | vmx | Vector128 Multiply Add Floating Point | +| [`vnmsubfp128`](../vmx128/vnmsubfp.md) | `0x14000150` | vmx | Vector128 Negative Multiply-Subtract Floating Point | +| [`vmsum3fp128`](../vmx128/vmsum3fp128.md) | `0x14000190` | vmx | Vector128 Multiply Sum 3-way Floating Point | +| [`vmsum4fp128`](../vmx128/vmsum4fp128.md) | `0x140001d0` | vmx | Vector128 Multiply Sum 4-way Floating-Point | +| [`vpkshss128`](../vmx128/vpkshss.md) | `0x14000200` | vmx | Vector128 Pack Signed Half Word Signed Saturate | +| [`vand128`](../vmx128/vand.md) | `0x14000210` | vmx | Vector128 Logical AND | +| [`vpkshus128`](../vmx128/vpkshus.md) | `0x14000240` | vmx | Vector128 Pack Signed Half Word Unsigned Saturate | +| [`vandc128`](../vmx128/vandc.md) | `0x14000250` | vmx | Vector128 Logical AND with Complement | +| [`vpkswss128`](../vmx128/vpkswss.md) | `0x14000280` | vmx | Vector128 Pack Signed Word Signed Saturate | +| [`vnor128`](../vmx128/vnor.md) | `0x14000290` | vmx | Vector128 Logical NOR | +| [`vpkswus128`](../vmx128/vpkswus.md) | `0x140002c0` | vmx | Vector128 Pack Signed Word Unsigned Saturate | +| [`vor128`](../vmx128/vor.md) | `0x140002d0` | vmx | Vector128 Logical OR | +| [`vpkuhum128`](../vmx128/vpkuhum.md) | `0x14000300` | vmx | Vector128 Pack Unsigned Half Word Unsigned Modulo | +| [`vxor128`](../vmx128/vxor.md) | `0x14000310` | vmx | Vector128 Logical XOR | +| [`vpkuhus128`](../vmx128/vpkuhus.md) | `0x14000340` | vmx | Vector128 Pack Unsigned Half Word Unsigned Saturate | +| [`vsel128`](../vmx128/vsel.md) | `0x14000350` | vmx | Vector128 Conditional Select | +| [`vpkuwum128`](../vmx128/vpkuwum.md) | `0x14000380` | vmx | Vector128 Pack Unsigned Word Unsigned Modulo | +| [`vslo128`](../vmx128/vslo.md) | `0x14000390` | vmx | Vector128 Shift Left Octet | +| [`vpkuwus128`](../vmx128/vpkuwus.md) | `0x140003c0` | vmx | Vector128 Pack Unsigned Word Unsigned Saturate | +| [`vsro128`](../vmx128/vsro.md) | `0x140003d0` | vmx | Vector128 Shift Right Octet | +| [`vrlw128`](../vmx128/vrlw.md) | `0x18000050` | vmx | Vector128 Rotate Left Word | +| [`vslw128`](../vmx128/vslw.md) | `0x180000d0` | vmx | Vector128 Shift Left Integer Word | +| [`vsraw128`](../vmx128/vsraw.md) | `0x18000150` | vmx | Vector128 Shift Right Arithmetic Word | +| [`vsrw128`](../vmx128/vsrw.md) | `0x180001d0` | vmx | Vector128 Shift Right Word | +| [`vmaxfp128`](../vmx128/vmaxfp.md) | `0x18000280` | vmx | Vector128 Maximum Floating Point | +| [`vminfp128`](../vmx128/vminfp.md) | `0x180002c0` | vmx | Vector128 Minimum Floating Point | +| [`vmrghw128`](../vmx128/vmrghw.md) | `0x18000300` | vmx | Vector128 Merge High Word | +| [`vmrglw128`](../vmx128/vmrglw.md) | `0x18000340` | vmx | Vector128 Merge Low Word | +| [`vupkhsb128`](../vmx128/vupkhsb.md) | `0x18000380` | vmx | Vector128 Unpack High Signed Byte | +| [`vupklsb128`](../vmx128/vupklsb.md) | `0x180003c0` | vmx | Vector128 Unpack Low Signed Byte | + + diff --git a/tools/ppc-manual/forms/VX128_1.md b/tools/ppc-manual/forms/VX128_1.md new file mode 100644 index 00000000..b356cf5d --- /dev/null +++ b/tools/ppc-manual/forms/VX128_1.md @@ -0,0 +1,38 @@ +# Form `VX128_1` β€” VX128_1 β€” VMX128 vector load/store + +## Bit Layout + +| 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 | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`lvsl128`](../vmx128/lvsl.md) | `0x10000003` | vmx | Load Vector for Shift Left Indexed 128 | +| [`lvsr128`](../vmx128/lvsr.md) | `0x10000043` | vmx | Load Vector for Shift Right Indexed 128 | +| [`lvewx128`](../memory/lvewx.md) | `0x10000083` | memory | Load Vector Element Word Indexed 128 | +| [`lvx128`](../memory/lvx.md) | `0x100000c3` | memory | Load Vector Indexed 128 | +| [`stvewx128`](../memory/stvewx.md) | `0x10000183` | memory | Store Vector Element Word Indexed 128 | +| [`stvx128`](../memory/stvx.md) | `0x100001c3` | memory | Store Vector Indexed 128 | +| [`lvxl128`](../memory/lvxl.md) | `0x100002c3` | memory | Load Vector Indexed LRU 128 | +| [`stvxl128`](../memory/stvxl.md) | `0x100003c3` | memory | Store Vector Indexed LRU 128 | +| [`lvlx128`](../memory/lvlx.md) | `0x10000403` | memory | Load Vector Left Indexed 128 | +| [`lvrx128`](../memory/lvrx.md) | `0x10000443` | memory | Load Vector Right Indexed 128 | +| [`stvlx128`](../memory/stvlx.md) | `0x10000503` | memory | Store Vector Left Indexed 128 | +| [`stvrx128`](../memory/stvrx.md) | `0x10000543` | memory | Store Vector Right Indexed 128 | +| [`lvlxl128`](../memory/lvlxl.md) | `0x10000603` | memory | Load Vector Left Indexed LRU 128 | +| [`lvrxl128`](../memory/lvrxl.md) | `0x10000643` | memory | Load Vector Right Indexed LRU 128 | +| [`stvlxl128`](../memory/stvlxl.md) | `0x10000703` | memory | Store Vector Left Indexed LRU 128 | +| [`stvrxl128`](../memory/stvrxl.md) | `0x10000743` | memory | Store Vector Right Indexed LRU 128 | + + diff --git a/tools/ppc-manual/forms/VX128_2.md b/tools/ppc-manual/forms/VX128_2.md new file mode 100644 index 00000000..22cfa4b3 --- /dev/null +++ b/tools/ppc-manual/forms/VX128_2.md @@ -0,0 +1,25 @@ +# Form `VX128_2` β€” VX128_2 β€” VMX128 3-operand arithmetic + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 23–25 | `VC` | source C 3-bit field | +| 26 | `VA128h` | source A middle bit | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vperm128`](../vmx128/vperm.md) | `0x14000000` | vmx | Vector128 Permute | + + diff --git a/tools/ppc-manual/forms/VX128_3.md b/tools/ppc-manual/forms/VX128_3.md new file mode 100644 index 00000000..98270d68 --- /dev/null +++ b/tools/ppc-manual/forms/VX128_3.md @@ -0,0 +1,37 @@ +# Form `VX128_3` β€” VX128_3 β€” VMX128 unary with immediate + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vcfpsxws128`](../vmx128/vcfpsxws128.md) | `0x18000230` | vmx | Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate | +| [`vcfpuxws128`](../vmx128/vcfpuxws128.md) | `0x18000270` | vmx | Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate | +| [`vcsxwfp128`](../vmx128/vcsxwfp128.md) | `0x180002b0` | vmx | Vector128 Convert From Signed Fixed-Point Word to Floating-Point | +| [`vcuxwfp128`](../vmx128/vcuxwfp128.md) | `0x180002f0` | vmx | Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point | +| [`vrfim128`](../vmx128/vrfim.md) | `0x18000330` | vmx | Vector128 Round to Floating-Point Integer toward -Infinity | +| [`vrfin128`](../vmx128/vrfin.md) | `0x18000370` | vmx | Vector128 Round to Floating-Point Integer Nearest | +| [`vrfip128`](../vmx128/vrfip.md) | `0x180003b0` | vmx | Vector128 Round to Floating-Point Integer toward +Infinity | +| [`vrfiz128`](../vmx128/vrfiz.md) | `0x180003f0` | vmx | Vector128 Round to Floating-Point Integer toward Zero | +| [`vrefp128`](../vmx128/vrefp.md) | `0x18000630` | vmx | Vector128 Reciprocal Estimate Floating Point | +| [`vrsqrtefp128`](../vmx128/vrsqrtefp.md) | `0x18000670` | vmx | Vector128 Reciprocal Square Root Estimate Floating Point | +| [`vexptefp128`](../vmx128/vexptefp.md) | `0x180006b0` | vmx | Vector128 Log2 Estimate Floating Point | +| [`vlogefp128`](../vmx128/vlogefp.md) | `0x180006f0` | vmx | Vector128 Log2 Estimate Floating Point | +| [`vspltw128`](../vmx128/vspltw.md) | `0x18000730` | vmx | Vector128 Splat Word | +| [`vspltisw128`](../vmx128/vspltisw.md) | `0x18000770` | vmx | Vector128 Splat Immediate Signed Word | +| [`vupkd3d128`](../vmx128/vupkd3d128.md) | `0x180007f0` | vmx | Vector128 Unpack D3Dtype | + + diff --git a/tools/ppc-manual/forms/VX128_4.md b/tools/ppc-manual/forms/VX128_4.md new file mode 100644 index 00000000..242c0d4d --- /dev/null +++ b/tools/ppc-manual/forms/VX128_4.md @@ -0,0 +1,25 @@ +# Form `VX128_4` β€” VX128_4 β€” VMX128 with sub-opcode selector + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–23 | `XO` | extended opcode | +| 24–25 | `z` | sub-operation selector | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vpkd3d128`](../vmx128/vpkd3d128.md) | `0x18000610` | vmx | Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert | +| [`vrlimi128`](../vmx128/vrlimi128.md) | `0x18000710` | vmx | Vector128 Rotate Left Immediate and Mask Insert | + + diff --git a/tools/ppc-manual/forms/VX128_5.md b/tools/ppc-manual/forms/VX128_5.md new file mode 100644 index 00000000..27845bf7 --- /dev/null +++ b/tools/ppc-manual/forms/VX128_5.md @@ -0,0 +1,25 @@ +# Form `VX128_5` β€” VX128_5 β€” VMX128 with shift field + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `SH` | 4-bit shift amount | +| 26 | `VA128h` | source A middle bit | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vsldoi128`](../vmx128/vsldoi.md) | `0x10000010` | vmx | Vector128 Shift Left Double by Octet Immediate | + + diff --git a/tools/ppc-manual/forms/VX128_P.md b/tools/ppc-manual/forms/VX128_P.md new file mode 100644 index 00000000..8270e21e --- /dev/null +++ b/tools/ppc-manual/forms/VX128_P.md @@ -0,0 +1,24 @@ +# Form `VX128_P` β€” VX128_P β€” VMX128 permute + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `PERMl` | permute selector low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–22 | `β€”` | reserved | +| 23–25 | `PERMh` | permute selector high 3 bits | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vpermwi128`](../vmx128/vpermwi128.md) | `0x18000210` | vmx | Vector128 Permutate Word Immediate | + + diff --git a/tools/ppc-manual/forms/VX128_R.md b/tools/ppc-manual/forms/VX128_R.md new file mode 100644 index 00000000..18dc2a72 --- /dev/null +++ b/tools/ppc-manual/forms/VX128_R.md @@ -0,0 +1,30 @@ +# Form `VX128_R` β€” VX128_R β€” VMX128 compare (with Rc β†’ CR6) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`vcmpeqfp128`](../vmx128/vcmpeqfp.md) | `0x18000000` | vmx | Vector128 Compare Equal-to Floating Point | +| [`vcmpgefp128`](../vmx128/vcmpgefp.md) | `0x18000080` | vmx | Vector128 Compare Greater-Than-or-Equal-to Floating Point | +| [`vcmpgtfp128`](../vmx128/vcmpgtfp.md) | `0x18000100` | vmx | Vector128 Compare Greater-Than Floating-Point | +| [`vcmpbfp128`](../vmx128/vcmpbfp.md) | `0x18000180` | vmx | Vector128 Compare Bounds Floating Point | +| [`vcmpequw128`](../vmx128/vcmpequw.md) | `0x18000200` | vmx | Vector128 Compare Equal-to Unsigned Word | + + diff --git a/tools/ppc-manual/forms/X.md b/tools/ppc-manual/forms/X.md new file mode 100644 index 00000000..472d081a --- /dev/null +++ b/tools/ppc-manual/forms/X.md @@ -0,0 +1,138 @@ +# Form `X` β€” X β€” Extended (10-bit extended opcode) + +## Bit Layout + +| 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 | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`cmp`](../alu/cmp.md) | `0x7c000000` | integer | Compare | +| [`tw`](../branch/tw.md) | `0x7c000008` | branch | Trap Word | +| [`lvsl`](../vmx/lvsl.md) | `0x7c00000c` | vmx | Load Vector for Shift Left Indexed | +| [`lvebx`](../memory/lvebx.md) | `0x7c00000e` | memory | Load Vector Element Byte Indexed | +| [`mfcr`](../control/mfcr.md) | `0x7c000026` | control | Move from Condition Register | +| [`lwarx`](../memory/lwarx.md) | `0x7c000028` | memory | Load Word and Reserve Indexed | +| [`ldx`](../memory/ld.md) | `0x7c00002a` | memory | Load Doubleword Indexed | +| [`lwzx`](../memory/lwz.md) | `0x7c00002e` | memory | Load Word and Zero Indexed | +| [`slwx`](../alu/slwx.md) | `0x7c000030` | integer | Shift Left Word | +| [`cntlzwx`](../alu/cntlzwx.md) | `0x7c000034` | integer | Count Leading Zeros Word | +| [`sldx`](../alu/sldx.md) | `0x7c000036` | integer | Shift Left Doubleword | +| [`andx`](../alu/andx.md) | `0x7c000038` | integer | AND | +| [`cmpl`](../alu/cmpl.md) | `0x7c000040` | integer | Compare Logical | +| [`lvsr`](../vmx/lvsr.md) | `0x7c00004c` | vmx | Load Vector for Shift Right Indexed | +| [`lvehx`](../memory/lvehx.md) | `0x7c00004e` | memory | Load Vector Element Half Word Indexed | +| [`ldux`](../memory/ld.md) | `0x7c00006a` | memory | Load Doubleword with Update Indexed | +| [`dcbst`](../memory/dcbst.md) | `0x7c00006c` | memory | Data Cache Block Store | +| [`lwzux`](../memory/lwz.md) | `0x7c00006e` | memory | Load Word and Zero with Update Indexed | +| [`cntlzdx`](../alu/cntlzdx.md) | `0x7c000074` | integer | Count Leading Zeros Doubleword | +| [`andcx`](../alu/andcx.md) | `0x7c000078` | integer | AND with Complement | +| [`td`](../branch/td.md) | `0x7c000088` | branch | Trap Doubleword | +| [`lvewx`](../memory/lvewx.md) | `0x7c00008e` | memory | Load Vector Element Word Indexed | +| [`mfmsr`](../control/mfmsr.md) | `0x7c0000a6` | control | Move from Machine State Register | +| [`ldarx`](../memory/ldarx.md) | `0x7c0000a8` | memory | Load Doubleword and Reserve Indexed | +| [`dcbf`](../memory/dcbf.md) | `0x7c0000ac` | memory | Data Cache Block Flush | +| [`lbzx`](../memory/lbz.md) | `0x7c0000ae` | memory | Load Byte and Zero Indexed | +| [`lvx`](../memory/lvx.md) | `0x7c0000ce` | memory | Load Vector Indexed | +| [`lbzux`](../memory/lbz.md) | `0x7c0000ee` | memory | Load Byte and Zero with Update Indexed | +| [`norx`](../alu/norx.md) | `0x7c0000f8` | integer | NOR | +| [`stvebx`](../memory/stvebx.md) | `0x7c00010e` | memory | Store Vector Element Byte Indexed | +| [`mtmsr`](../control/mtmsr.md) | `0x7c000124` | control | Move to Machine State Register | +| [`stdx`](../memory/std.md) | `0x7c00012a` | memory | Store Doubleword Indexed | +| [`stwcx`](../memory/stwcx.md) | `0x7c00012d` | memory | Store Word Conditional Indexed | +| [`stwx`](../memory/stw.md) | `0x7c00012e` | memory | Store Word Indexed | +| [`stvehx`](../memory/stvehx.md) | `0x7c00014e` | memory | Store Vector Element Half Word Indexed | +| [`mtmsrd`](../control/mtmsrd.md) | `0x7c000164` | control | Move to Machine State Register Doubleword | +| [`stdux`](../memory/std.md) | `0x7c00016a` | memory | Store Doubleword with Update Indexed | +| [`stwux`](../memory/stw.md) | `0x7c00016e` | memory | Store Word with Update Indexed | +| [`stvewx`](../memory/stvewx.md) | `0x7c00018e` | memory | Store Vector Element Word Indexed | +| [`stdcx`](../memory/stdcx.md) | `0x7c0001ad` | memory | Store Doubleword Conditional Indexed | +| [`stbx`](../memory/stb.md) | `0x7c0001ae` | memory | Store Byte Indexed | +| [`stvx`](../memory/stvx.md) | `0x7c0001ce` | memory | Store Vector Indexed | +| [`dcbtst`](../memory/dcbtst.md) | `0x7c0001ec` | memory | Data Cache Block Touch for Store | +| [`stbux`](../memory/stb.md) | `0x7c0001ee` | memory | Store Byte with Update Indexed | +| [`dcbt`](../memory/dcbt.md) | `0x7c00022c` | memory | Data Cache Block Touch | +| [`lhzx`](../memory/lhz.md) | `0x7c00022e` | memory | Load Half Word and Zero Indexed | +| [`eqvx`](../alu/eqvx.md) | `0x7c000238` | integer | Equivalent | +| [`lhzux`](../memory/lhz.md) | `0x7c00026e` | memory | Load Half Word and Zero with Update Indexed | +| [`xorx`](../alu/xorx.md) | `0x7c000278` | integer | XOR | +| [`lwax`](../memory/lwa.md) | `0x7c0002aa` | memory | Load Word Algebraic Indexed | +| [`lhax`](../memory/lha.md) | `0x7c0002ae` | memory | Load Half Word Algebraic Indexed | +| [`lvxl`](../memory/lvxl.md) | `0x7c0002ce` | memory | Load Vector Indexed LRU | +| [`lwaux`](../memory/lwa.md) | `0x7c0002ea` | memory | Load Word Algebraic with Update Indexed | +| [`lhaux`](../memory/lha.md) | `0x7c0002ee` | memory | Load Half Word Algebraic with Update Indexed | +| [`sthx`](../memory/sth.md) | `0x7c00032e` | memory | Store Half Word Indexed | +| [`orcx`](../alu/orcx.md) | `0x7c000338` | integer | OR with Complement | +| [`sthux`](../memory/sth.md) | `0x7c00036e` | memory | Store Half Word with Update Indexed | +| [`orx`](../alu/orx.md) | `0x7c000378` | integer | OR | +| [`dcbi`](../memory/dcbi.md) | `0x7c0003ac` | memory | Data Cache Block Invalidate | +| [`nandx`](../alu/nandx.md) | `0x7c0003b8` | integer | NAND | +| [`stvxl`](../memory/stvxl.md) | `0x7c0003ce` | memory | Store Vector Indexed LRU | +| [`mcrxr`](../control/mcrxr.md) | `0x7c000400` | control | Move to Condition Register from XER | +| [`lvlx`](../memory/lvlx.md) | `0x7c00040e` | memory | Load Vector Left Indexed | +| [`ldbrx`](../memory/ldbrx.md) | `0x7c000428` | memory | Load Doubleword Byte-Reverse Indexed | +| [`lswx`](../memory/lswx.md) | `0x7c00042a` | memory | Load String Word Indexed | +| [`lwbrx`](../memory/lwbrx.md) | `0x7c00042c` | memory | Load Word Byte-Reverse Indexed | +| [`lfsx`](../memory/lfs.md) | `0x7c00042e` | memory | Load Floating-Point Single Indexed | +| [`srwx`](../alu/srwx.md) | `0x7c000430` | integer | Shift Right Word | +| [`srdx`](../alu/srdx.md) | `0x7c000436` | integer | Shift Right Doubleword | +| [`lvrx`](../memory/lvrx.md) | `0x7c00044e` | memory | Load Vector Right Indexed | +| [`lfsux`](../memory/lfs.md) | `0x7c00046e` | memory | Load Floating-Point Single with Update Indexed | +| [`lswi`](../memory/lswi.md) | `0x7c0004aa` | memory | Load String Word Immediate | +| [`sync`](../alu/sync.md) | `0x7c0004ac` | integer | Synchronize | +| [`lfdx`](../memory/lfd.md) | `0x7c0004ae` | memory | Load Floating-Point Double Indexed | +| [`lfdux`](../memory/lfd.md) | `0x7c0004ee` | memory | Load Floating-Point Double with Update Indexed | +| [`stvlx`](../memory/stvlx.md) | `0x7c00050e` | memory | Store Vector Left Indexed | +| [`stdbrx`](../memory/stdbrx.md) | `0x7c000528` | memory | Store Doubleword Byte-Reverse Indexed | +| [`stswx`](../memory/stswx.md) | `0x7c00052a` | memory | Store String Word Indexed | +| [`stwbrx`](../memory/stwbrx.md) | `0x7c00052c` | memory | Store Word Byte-Reverse Indexed | +| [`stfsx`](../memory/stfs.md) | `0x7c00052e` | memory | Store Floating-Point Single Indexed | +| [`stvrx`](../memory/stvrx.md) | `0x7c00054e` | memory | Store Vector Right Indexed | +| [`stfsux`](../memory/stfs.md) | `0x7c00056e` | memory | Store Floating-Point Single with Update Indexed | +| [`stswi`](../memory/stswi.md) | `0x7c0005aa` | memory | Store String Word Immediate | +| [`stfdx`](../memory/stfd.md) | `0x7c0005ae` | memory | Store Floating-Point Double Indexed | +| [`stfdux`](../memory/stfd.md) | `0x7c0005ee` | memory | Store Floating-Point Double with Update Indexed | +| [`lvlxl`](../memory/lvlxl.md) | `0x7c00060e` | memory | Load Vector Left Indexed LRU | +| [`lhbrx`](../memory/lhbrx.md) | `0x7c00062c` | memory | Load Half Word Byte-Reverse Indexed | +| [`srawx`](../alu/srawx.md) | `0x7c000630` | integer | Shift Right Algebraic Word | +| [`sradx`](../alu/sradx.md) | `0x7c000634` | integer | Shift Right Algebraic Doubleword | +| [`lvrxl`](../memory/lvrxl.md) | `0x7c00064e` | memory | Load Vector Right Indexed LRU | +| [`srawix`](../alu/srawix.md) | `0x7c000670` | integer | Shift Right Algebraic Word Immediate | +| [`eieio`](../alu/eieio.md) | `0x7c0006ac` | integer | Enforce In-Order Execution of I/O | +| [`stvlxl`](../memory/stvlxl.md) | `0x7c00070e` | memory | Store Vector Left Indexed LRU | +| [`sthbrx`](../memory/sthbrx.md) | `0x7c00072c` | memory | Store Half Word Byte-Reverse Indexed | +| [`extshx`](../alu/extshx.md) | `0x7c000734` | integer | Extend Sign Half Word | +| [`stvrxl`](../memory/stvrxl.md) | `0x7c00074e` | memory | Store Vector Right Indexed LRU | +| [`extsbx`](../alu/extsbx.md) | `0x7c000774` | integer | Extend Sign Byte | +| [`icbi`](../memory/icbi.md) | `0x7c0007ac` | memory | Instruction Cache Block Invalidate | +| [`stfiwx`](../memory/stfiwx.md) | `0x7c0007ae` | memory | Store Floating-Point as Integer Word Indexed | +| [`extswx`](../alu/extswx.md) | `0x7c0007b4` | integer | Extend Sign Word | +| [`fcmpu`](../fpu/fcmpu.md) | `0xfc000000` | fpu | Floating Compare Unordered | +| [`frspx`](../fpu/frspx.md) | `0xfc000018` | fpu | Floating Round to Single | +| [`fctiwx`](../fpu/fctiwx.md) | `0xfc00001c` | fpu | Floating Convert to Integer Word | +| [`fctiwzx`](../fpu/fctiwzx.md) | `0xfc00001e` | fpu | Floating Convert to Integer Word with Round Toward Zero | +| [`fcmpo`](../fpu/fcmpo.md) | `0xfc000040` | fpu | Floating Compare Ordered | +| [`mtfsb1x`](../control/mtfsb1x.md) | `0xfc00004c` | control | Move to FPSCR Bit 1 | +| [`fnegx`](../fpu/fnegx.md) | `0xfc000050` | fpu | Floating Negate | +| [`mcrfs`](../control/mcrfs.md) | `0xfc000080` | control | Move to Condition Register from FPSCR | +| [`mtfsb0x`](../control/mtfsb0x.md) | `0xfc00008c` | control | Move to FPSCR Bit 0 | +| [`fmrx`](../fpu/fmrx.md) | `0xfc000090` | fpu | Floating Move Register | +| [`mtfsfix`](../control/mtfsfix.md) | `0xfc00010c` | control | Move to FPSCR Field Immediate | +| [`fnabsx`](../fpu/fnabsx.md) | `0xfc000110` | fpu | Floating Negative Absolute Value | +| [`fabsx`](../fpu/fabsx.md) | `0xfc000210` | fpu | Floating Absolute Value | +| [`mffsx`](../control/mffsx.md) | `0xfc00048e` | control | Move from FPSCR | +| [`fctidx`](../fpu/fctidx.md) | `0xfc00065c` | fpu | Floating Convert to Integer Doubleword | +| [`fctidzx`](../fpu/fctidzx.md) | `0xfc00065e` | fpu | Floating Convert to Integer Doubleword with Round Toward Zero | +| [`fcfidx`](../fpu/fcfidx.md) | `0xfc00069c` | fpu | Floating Convert From Integer Doubleword | + + diff --git a/tools/ppc-manual/forms/XFL.md b/tools/ppc-manual/forms/XFL.md new file mode 100644 index 00000000..50d0b387 --- /dev/null +++ b/tools/ppc-manual/forms/XFL.md @@ -0,0 +1,23 @@ +# Form `XFL` β€” XFL β€” Floating Fields (mtfsf) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (63) | +| 6 | `L` | field-select behaviour | +| 7–14 | `FM` | FPSCR field mask | +| 15 | `W` | immediate-value flag | +| 16–20 | `FRB` | source FPR | +| 21–30 | `XO` | extended opcode | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`mtfsfx`](../control/mtfsfx.md) | `0xfc00058e` | control | Move to FPSCR Fields | + + diff --git a/tools/ppc-manual/forms/XFX.md b/tools/ppc-manual/forms/XFX.md new file mode 100644 index 00000000..09c94e66 --- /dev/null +++ b/tools/ppc-manual/forms/XFX.md @@ -0,0 +1,24 @@ +# Form `XFX` β€” XFX β€” Fixed (SPR/TBR/CR-field access) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination / source GPR | +| 11–20 | `spr/tbr/FXM` | SPR/TBR number (byte-swapped halves) or CR field mask | +| 21–30 | `XO` | extended opcode | +| 31 | `β€”` | reserved | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`mtcrf`](../control/mtcrf.md) | `0x7c000120` | control | Move to Condition Register Fields | +| [`mfspr`](../control/mfspr.md) | `0x7c0002a6` | control | Move from Special-Purpose Register | +| [`mftb`](../control/mftb.md) | `0x7c0002e6` | control | Move from Time Base | +| [`mtspr`](../control/mtspr.md) | `0x7c0003a6` | control | Move to Special-Purpose Register | + + diff --git a/tools/ppc-manual/forms/XL.md b/tools/ppc-manual/forms/XL.md new file mode 100644 index 00000000..819bdaec --- /dev/null +++ b/tools/ppc-manual/forms/XL.md @@ -0,0 +1,33 @@ +# Form `XL` β€” XL β€” Extended, Link (branch-to-LR/CTR, CR logical) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (19) | +| 6–10 | `BT/BO` | target / branch options | +| 11–15 | `BA/BI` | source A / CR bit to test | +| 16–20 | `BB` | source B | +| 21–30 | `XO` | extended opcode (10 bits) | +| 31 | `LK` | link flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`mcrf`](../control/mcrf.md) | `0x4c000000` | control | Move Condition Register Field | +| [`bclrx`](../branch/bclrx.md) | `0x4c000020` | branch | Branch Conditional to Link Register | +| [`crnor`](../control/crnor.md) | `0x4c000042` | control | Condition Register NOR | +| [`crandc`](../control/crandc.md) | `0x4c000102` | control | Condition Register AND with Complement | +| [`isync`](../alu/isync.md) | `0x4c00012c` | integer | Instruction Synchronize | +| [`crxor`](../control/crxor.md) | `0x4c000182` | control | Condition Register XOR | +| [`crnand`](../control/crnand.md) | `0x4c0001c2` | control | Condition Register NAND | +| [`crand`](../control/crand.md) | `0x4c000202` | control | Condition Register AND | +| [`creqv`](../control/creqv.md) | `0x4c000242` | control | Condition Register Equivalent | +| [`crorc`](../control/crorc.md) | `0x4c000342` | control | Condition Register OR with Complement | +| [`cror`](../control/cror.md) | `0x4c000382` | control | Condition Register OR | +| [`bcctrx`](../branch/bcctrx.md) | `0x4c000420` | branch | Branch Conditional to Count Register | + + diff --git a/tools/ppc-manual/forms/XO.md b/tools/ppc-manual/forms/XO.md new file mode 100644 index 00000000..4b72d71b --- /dev/null +++ b/tools/ppc-manual/forms/XO.md @@ -0,0 +1,43 @@ +# Form `XO` β€” XO β€” Extended, Overflow (ALU with OE/Rc) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RT` | destination GPR | +| 11–15 | `RA` | source A | +| 16–20 | `RB` | source B | +| 21 | `OE` | overflow-enable flag | +| 22–30 | `XO` | extended opcode (9 bits) | +| 31 | `Rc` | record-form flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`subfcx`](../alu/subfcx.md) | `0x7c000010` | integer | Subtract From Carrying | +| [`mulhdux`](../alu/mulhdux.md) | `0x7c000012` | integer | Multiply High Doubleword Unsigned | +| [`addcx`](../alu/addcx.md) | `0x7c000014` | integer | Add Carrying | +| [`mulhwux`](../alu/mulhwux.md) | `0x7c000016` | integer | Multiply High Word Unsigned | +| [`subfx`](../alu/subfx.md) | `0x7c000050` | integer | Subtract From | +| [`mulhdx`](../alu/mulhdx.md) | `0x7c000092` | integer | Multiply High Doubleword | +| [`mulhwx`](../alu/mulhwx.md) | `0x7c000096` | integer | Multiply High Word | +| [`negx`](../alu/negx.md) | `0x7c0000d0` | integer | Negate | +| [`subfex`](../alu/subfex.md) | `0x7c000110` | integer | Subtract From Extended | +| [`addex`](../alu/addex.md) | `0x7c000114` | integer | Add Extended | +| [`subfzex`](../alu/subfzex.md) | `0x7c000190` | integer | Subtract From Zero Extended | +| [`addzex`](../alu/addzex.md) | `0x7c000194` | integer | Add to Zero Extended | +| [`subfmex`](../alu/subfmex.md) | `0x7c0001d0` | integer | Subtract From Minus One Extended | +| [`mulldx`](../alu/mulldx.md) | `0x7c0001d2` | integer | Multiply Low Doubleword | +| [`addmex`](../alu/addmex.md) | `0x7c0001d4` | integer | Add to Minus One Extended | +| [`mullwx`](../alu/mullwx.md) | `0x7c0001d6` | integer | Multiply Low Word | +| [`addx`](../alu/addx.md) | `0x7c000214` | integer | Add | +| [`divdux`](../alu/divdux.md) | `0x7c000392` | integer | Divide Doubleword Unsigned | +| [`divwux`](../alu/divwux.md) | `0x7c000396` | integer | Divide Word Unsigned | +| [`divdx`](../alu/divdx.md) | `0x7c0003d2` | integer | Divide Doubleword | +| [`divwx`](../alu/divwx.md) | `0x7c0003d6` | integer | Divide Word | + + diff --git a/tools/ppc-manual/forms/XS.md b/tools/ppc-manual/forms/XS.md new file mode 100644 index 00000000..dd79c039 --- /dev/null +++ b/tools/ppc-manual/forms/XS.md @@ -0,0 +1,23 @@ +# Form `XS` β€” XS β€” Extended, Shift (64-bit sradi) + +## Bit Layout + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (31) | +| 6–10 | `RS` | source GPR | +| 11–15 | `RA` | destination GPR | +| 16–20 | `sh` | shift amount low 5 bits | +| 21–29 | `XO` | extended opcode (9 bits) | +| 30 | `sh5` | shift amount high bit | +| 31 | `Rc` | record-form flag | + +## Instructions Using This Form + + + +| Mnemonic | Opcode | Group | Description | +| --- | --- | --- | --- | +| [`sradix`](../alu/sradix.md) | `0x7c000674` | integer | Shift Right Algebraic Doubleword Immediate | + + diff --git a/tools/ppc-manual/fpu/fabsx.md b/tools/ppc-manual/fpu/fabsx.md new file mode 100644 index 00000000..a3493974 --- /dev/null +++ b/tools/ppc-manual/fpu/fabsx.md @@ -0,0 +1,121 @@ +# `fabsx` β€” Floating Absolute Value + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000210` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fabs` | `fabsx` | β€” | Floating Absolute Value | +| `fabs.` | `fabsx` | Rc=1 | Floating Absolute Value | + +## Syntax + +```asm +fabs[Rc] [FD], [FB] +``` + +## Encoding + +### `fabsx` β€” form `X` + +- **Opcode word:** `0xfc000210` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `264` +- **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 | +| --- | --- | --- | +| `FB` | fabsx: read | Source B floating-point register. | +| `FD` | fabsx: write | Destination floating-point register. | +| `CR` | fabsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `fabsx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fabsx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +FRT <- clear_sign(FRB) +``` + +## 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 + +**`fabsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fabsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:478`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L478) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:909`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L909) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2757-2761`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2757-L2761) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fabsx => { + ctx.fpr[instr.rd()] = ctx.fpr[instr.rb()].abs(); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bit-pattern operation, no rounding.** `fabs` clears the sign bit (bit 0) of the source FPR's binary64 representation and writes the 64-bit value to the destination unchanged otherwise. No precision loss, no FPSCR exception bits. The mnemonic does not have an `s` variant β€” there is one form regardless of whether the operand is interpreted as binary32 or binary64. +- **NaN handling.** `fabs(NaN)` returns the same NaN with the sign bit cleared. The signalling/quiet bit is **not** modified, and `FPSCR[VXSNAN]` is **not** raised. xenia-rs uses `f64::abs`, which matches: it is bit-level `x & 0x7FFF_FFFF_FFFF_FFFF`. +- **Special values.** `fabs(Β±0) = +0`; `fabs(±∞) = +∞`; `fabs(Β±NaN)` = `+NaN` (sign cleared, payload preserved). +- **FPSCR is largely untouched.** Hardware specifies `FPRF` is **not** updated by `fabs`, and no exception bits are raised. Notation in the page header about `FPSCR` write is generic β€” the only meaningful write is via `Rc=1`. +- **`Rc=1` (`fabs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1 (these bits are typically stale or zero). +- **No `FRA` operand.** X-form, primary 63, XO 264. Reads `FRB` only; bits 11–15 are don't-care. +- **Common idiom.** `fabs` followed by `fcmpu` against a small constant for ULP-sized "near zero" tests; or paired with `fneg`/`fnabs` for sign-set-to-known operations. + +## Related Instructions + +- [`fnegx`](fnegx.md) β€” flip sign bit. +- [`fnabsx`](fnabsx.md) β€” absolute value with sign **set** (always negative result). +- [`fmrx`](fmrx.md) β€” copy FPR (no sign manipulation). +- [`fselx`](fselx.md) β€” branch-free select; combined with `fabs` for `min`/`max`/`clamp` patterns. +- [`fcmpux`](fcmpu.md), [`fcmpox`](fcmpo.md) β€” compares often paired with `fabs` for magnitude tests. + +## IBM Reference + +- [AIX 7.3 β€” `fabs` (Floating Absolute Value)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fabs-floating-absolute-value-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (sign-bit manipulation defined as bit-pattern, not arithmetic). diff --git a/tools/ppc-manual/fpu/faddsx.md b/tools/ppc-manual/fpu/faddsx.md new file mode 100644 index 00000000..7bc26a19 --- /dev/null +++ b/tools/ppc-manual/fpu/faddsx.md @@ -0,0 +1,130 @@ +# `faddsx` β€” Floating Add Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec00002a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fadds` | `faddsx` | β€” | Floating Add Single | +| `fadds.` | `faddsx` | Rc=1 | Floating Add Single | + +## Syntax + +```asm +fadds[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `faddsx` β€” form `A` + +- **Opcode word:** `0xec00002a` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `21` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | faddsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | faddsx: read | Source B floating-point register. | +| `FD` | faddsx: write | Destination floating-point register. | +| `CR` | faddsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | faddsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `faddsx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `faddsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- RoundToSingle(FRA + FRB) ; single-precision +``` + +## 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 + +**`faddsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="faddsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:46`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L46) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:388`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L388) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2565-2574`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2565-L2574) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::faddsx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_add(ctx, a, b, false); + let result = to_single(ctx, a + b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single precision via double FPRs.** The trailing `s` in the mnemonic means the result is rounded to IEEE-754 binary32 after the addition, then re-encoded into the 64-bit FPR using the binary64 representation of that single-precision value. The host computes `to_single(a + b)`; both source operands are read as full binary64. +- **FPSCR side effects.** Hardware updates `FPRF` (result class), `FR`/`FI` (rounding info), `FX`, and the exception bits β€” `OX` on overflow, `UX` on underflow, `XX` on inexact, `VXISI` on `±∞ βˆ’ ±∞`, `VXSNAN` on a signalling-NaN input. xenia-rs does **not** maintain FPSCR in the interpreter snapshot β€” call this out as a xenia quirk if you depend on cross-instruction FPSCR observation. +- **`Rc=1` (`fadds.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. xenia models this via `update_cr1_from_fpscr()`. +- **NaN propagation.** Any NaN input yields a quiet NaN result; signalling NaNs are quietened (signalling bit cleared) per PowerISA. Host-native `f64 +` may not perform that quietening on every platform. +- **`±∞ βˆ’ ±∞` after rounding.** Although `+`-shaped, opposite-signed infinities still produce `QNaN(VXISI)`. +- **`FPSCR[NI]` (non-IEEE / flush-to-zero)** is set at Xenon boot, so denormal results normally flush to zero. Xenia inherits host semantics, which is IEEE-compliant by default; titles tuned around flush-to-zero may see slightly different denormal rounding under xenia. +- **Rounding mode** uses `FPSCR[RN]` (00 nearest-even, 01 toward 0, 10 toward +∞, 11 toward βˆ’βˆž). Default is nearest-even and is rarely changed. +- **A-form encoding ignores `FRC`.** Bits 21–25 are don't-care for the add family. + +## Related Instructions + +- [`faddx`](faddx.md) β€” double-precision sibling. +- [`fsubsx`](fsubsx.md), [`fmulsx`](fmulsx.md), [`fdivsx`](fdivsx.md) β€” other single-precision arithmetic ops. +- [`fmaddsx`](fmaddsx.md), [`fmsubsx`](fmsubsx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” fused multiply-add single-precision family (single rounding step). +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding helper; `fadds` is essentially `frsp(fadd)` fused into one rounding. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” read/write FPSCR for rounding-mode and exception control. + +## IBM Reference + +- [AIX 7.3 β€” `fadds` (Floating Add Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fadds-floating-add-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (single-precision rounding rules and FPSCR side effects). diff --git a/tools/ppc-manual/fpu/faddx.md b/tools/ppc-manual/fpu/faddx.md new file mode 100644 index 00000000..a64a65e4 --- /dev/null +++ b/tools/ppc-manual/fpu/faddx.md @@ -0,0 +1,143 @@ +# `faddx` β€” Floating Add + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00002a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fadd` | `faddx` | β€” | Floating Add | +| `fadd.` | `faddx` | Rc=1 | Floating Add | + +## Syntax + +```asm +fadd[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `faddx` β€” form `A` + +- **Opcode word:** `0xfc00002a` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `21` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | faddx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | faddx: read | Source B floating-point register. | +| `FD` | faddx: write | Destination floating-point register. | +| `CR` | faddx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | faddx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `faddx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `faddx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- FRA + FRB ; double-precision +``` + +## C Translation Example + +```c +/* fadd / fadd. β€” IEEE-754 double-precision add (A-form) */ +f[insn.FRT] = f[insn.FRA] + f[insn.FRB]; +if (insn.Rc) update_cr1_from_fpscr(); +/* FPSCR[FPRF, FR, FI, FX, exceptions] implicitly updated by the FPU. */ +``` + +## Implementation References + +**`faddx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="faddx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:38`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L38) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:922`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L922) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2555-2564`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2555-L2564) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::faddx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_add(ctx, a, b, false); + let result = a + b; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +FRT <- round(FRA + FRB, FPSCR[RN]) ; double precision, current rounding mode + +; FPSCR side-effects (always) + FPSCR[FPRF] <- classify(FRT) ; sign / class bits + FPSCR[FR,FI] <- round_info + if overflow then FPSCR[OX] <- 1; FPSCR[FX] <- 1 + if underflow then FPSCR[UX] <- 1; FPSCR[FX] <- 1 + if inexact then FPSCR[XX] <- 1; FPSCR[FX] <- 1 + if NaN input or Β±βˆžβˆ’Β±βˆž then FPSCR[VXISI]<- 1; FPSCR[FX] <- 1 + FPSCR[FEX] <- any-enabled-exception + +if Rc then + CR1 <- FPSCR[FX, FEX, VX, OX] ; the four "summary" bits +``` + +## Special Cases & Edge Conditions + +- **Double precision.** `fadd` always operates on IEEE-754 binary64 regardless of whether either source was produced by a single-precision instruction. Single-precision adds use [`faddsx`](faddsx.md) and automatically round the result to binary32 precision. +- **No immediate / carry / OE.** FPU arithmetic has no immediate forms, no carry, and no overflow-enable bit. `Rc` is the only modifier β€” it writes `CR1` from the four top FPSCR bits. +- **FPSCR is always updated.** Even the non-record form (`fadd`) updates `FPSCR[FPRF, FR, FI, FX, …]` as a side effect of execution; xenia's interpreter currently **does not** model this, so translations that rely on observing FPSCR bits across a pair of FPU instructions will diverge from hardware. If your translator needs compatible FPSCR state, emit explicit updates β€” or accept the simplification, which matches real Xbox 360 title behaviour in practice (titles rarely read FPSCR except via `mffs` for exception sanity checks). +- **NaN propagation.** Per IEEE-754, any NaN input produces a NaN output; PowerPC specifies that the *signalling* bit of the result NaN is cleared (quietening a signalling input). Xenia uses host-native `f64 +`, which may preserve the signalling bit on some platforms β€” assume quietening for correctness. +- **`±∞ βˆ’ ±∞` is an invalid operation.** Produces a quiet NaN (`QNaN(VXISI)`) and sets `FPSCR[VXISI]`. Xenia emits the host-native NaN. +- **Denormal handling.** Xenon's default mode flushes denormal results to zero (FPSCR[NI] / "non-IEEE mode" bit set at boot). Xenia inherits host semantics by default; if title code explicitly clears NI (rare) you'll get IEEE-compliant denormals from the host FPU. +- **Rounding mode.** `FPSCR[RN]` selects one of four rounding modes (nearest-even, toward 0, toward +∞, toward βˆ’βˆž). Games rarely change RN from the default nearest-even. If your translator needs faithful rounding-mode support emit `fesetround` around the operation. +- **Register encoding.** A-form: `FRT`, `FRA`, `FRB`, `FRC`, `Rc` β€” but `fadd` ignores `FRC` (the "C" multiplier operand used by `fmadd`-style ops). The `FRC` field is architecturally don't-care but typically encoded as 0. + +## Related Instructions + +- [`faddsx`](faddsx.md) β€” single-precision add; result is rounded to binary32 then stored as binary64. +- [`fsubx`](fsubx.md), [`fsubsx`](fsubsx.md) β€” double / single subtract. +- [`fmulx`](fmulx.md), [`fmulsx`](fmulsx.md) β€” double / single multiply. +- [`fmaddx`](fmaddx.md), [`fmsubx`](fmsubx.md), [`fnmaddx`](fnmaddx.md), [`fnmsubx`](fnmsubx.md) β€” fused multiply-add family (single-rounding; preferred for dot products). +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” read/write FPSCR. + +## IBM Reference + +- [AIX 7.3 β€” `fadd` (Floating Add)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fa-fadd-floating-add-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (complete FPSCR and NaN-propagation rules). diff --git a/tools/ppc-manual/fpu/fcfidx.md b/tools/ppc-manual/fpu/fcfidx.md new file mode 100644 index 00000000..3ed31684 --- /dev/null +++ b/tools/ppc-manual/fpu/fcfidx.md @@ -0,0 +1,142 @@ +# `fcfidx` β€” Floating Convert From Integer Doubleword + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00069c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fcfid` | `fcfidx` | β€” | Floating Convert From Integer Doubleword | +| `fcfid.` | `fcfidx` | Rc=1 | Floating Convert From Integer Doubleword | + +## Syntax + +```asm +fcfid[Rc] [FD], [FB] +``` + +## Encoding + +### `fcfidx` β€” form `X` + +- **Opcode word:** `0xfc00069c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `846` +- **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 | +| --- | --- | --- | +| `FB` | fcfidx: read | Source B floating-point register. | +| `FD` | fcfidx: write | Destination floating-point register. | +| `CR` | fcfidx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fcfidx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fcfidx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fcfidx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fcfidx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fcfidx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:253`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L253) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:914`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L914) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2872-2885`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2872-L2885) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fcfidx => { + // Convert from integer doubleword: frD = (double)(int64_t)frB_as_bits. + // PPCBUG-224: set XX when |i64| > 2^53 (precision loss in conversion). + let bits = ctx.fpr[instr.rb()].to_bits(); + let i = bits as i64; + let result = i as f64; + if (result as i64) != i { + fpscr::set_exception(ctx, fpscr::XX); + } + ctx.fpr[instr.rd()] = result; + fpscr::set_fprf(ctx, fpscr::classify_fprf(result)); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **64-bit signed integer β†’ binary64.** Reads `FRB` as a 64-bit signed integer (the bits, interpreted as `i64`) and converts it to IEEE-754 binary64. xenia-rs implements this as `bits as i64 as f64`. +- **Loss of precision.** binary64 has 53 bits of significand, so `i64` values with magnitude > 2^53 lose low-order bits. This raises `FPSCR[XX, FX]` (inexact) on hardware. xenia-rs does not update FPSCR (xenia quirk) but the rounded value matches host `f64` rules (round-to-nearest-even by default). +- **Always exact for `|x| <= 2^53`.** Within Β±9,007,199,254,740,992 the conversion is bit-exact. +- **Rounding mode.** Uses `FPSCR[RN]`. Default nearest-even. Rust's `as f64` from `i64` uses platform-native conversion which on Xenon-target hosts will respect the FE rounding mode; xenia uses the host default. +- **No NaN/∞ generation.** All `i64` inputs map to finite `f64` outputs (the largest `i64` is well below `f64::MAX`). +- **FPSCR side effects.** Hardware updates `FPRF` (result class) and may set `XX`/`FX` on inexact. xenia does not update FPSCR. +- **`Rc=1` (`fcfid.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 846. Reads `FRB` only. +- **Common pairing.** Used after `lfd` of a stored `i64` to bring an integer into the FP pipeline for arithmetic; the inverse direction is [`fctidx`](fctidx.md) / [`fctidzx`](fctidzx.md). + +## Related Instructions + +- [`fctidx`](fctidx.md), [`fctidzx`](fctidzx.md) β€” inverse direction (binary64 β†’ 64-bit integer, current rounding / round-toward-zero). +- [`fctiwx`](fctiwx.md), [`fctiwzx`](fctiwzx.md) β€” 32-bit integer conversion variants. +- [`frspx`](frspx.md) β€” round to single precision; commonly chained after `fcfid` to produce a `float`. +- `lfd`, `stfd` β€” load/store doubleword used to move integer values between GPR and FPR via memory. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” control rounding mode used by the conversion. + +## IBM Reference + +- [AIX 7.3 β€” `fcfid` (Floating Convert From Integer Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fcfid-floating-convert-from-integer-doubleword-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (integerβ†’FP conversion semantics). diff --git a/tools/ppc-manual/fpu/fcmpo.md b/tools/ppc-manual/fpu/fcmpo.md new file mode 100644 index 00000000..87f02b2a --- /dev/null +++ b/tools/ppc-manual/fpu/fcmpo.md @@ -0,0 +1,164 @@ +# `fcmpo` β€” Floating Compare Ordered + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000040` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fcmpo` | `fcmpo` | β€” | Floating Compare Ordered | + +## Syntax + +```asm +fcmpo [CRFD], [FA], [FB] +``` + +## Encoding + +### `fcmpo` β€” form `X` + +- **Opcode word:** `0xfc000040` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `32` +- **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 | +| --- | --- | --- | +| `FA` | fcmpo: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fcmpo: read | Source B floating-point register. | +| `CRFD` | fcmpo: write | CR destination field (`crf`, 0–7). | +| `FPSCR` | fcmpo: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fcmpo` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD`, `FPSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `fcmpo`: **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fcmpo`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fcmpo"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:362`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L362) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:901`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L901) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3002-3032`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3002-L3032) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fcmpo => { + // Ordered compare: like fcmpu but also sets VXVC on QNaN (or VXSNAN on SNaN). + let fra = ctx.fpr[instr.ra()]; + let frb = ctx.fpr[instr.rb()]; + let crfd = instr.crfd(); + if fra.is_nan() || frb.is_nan() { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: false, eq: false, so: true }; + if fpscr::is_snan(fra) || fpscr::is_snan(frb) { + fpscr::set_exception(ctx, fpscr::VXSNAN | fpscr::VXVC); + } else { + fpscr::set_exception(ctx, fpscr::VXVC); + } + } else if fra < frb { + ctx.cr[crfd] = crate::context::CrField { lt: true, gt: false, eq: false, so: false }; + } else if fra > frb { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: true, eq: false, so: false }; + } else { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: false, eq: true, so: false }; + } + let fprf = if fra.is_nan() || frb.is_nan() { + 0b0_0001 + } else if fra < frb { + 0b0_1000 + } else if fra > frb { + 0b0_0100 + } else { + 0b0_0010 + }; + fpscr::set_fprf(ctx, fprf); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Ordered compare.** Same CR-field semantics as `fcmpu` (`LT/GT/EQ/SO`), but NaN inputs raise additional FPSCR exceptions: + - Either operand NaN β†’ `FPSCR[VXVC] = 1` (invalid-operation: compare on QNaN/SNaN). + - Either operand signalling NaN β†’ also `FPSCR[VXSNAN] = 1`. + - All NaN cases also set `FX = 1` and `VX = 1`. +- **xenia quirk.** xenia-rs's `fcmpo` body is identical to `fcmpu` β€” the FPSCR exception bits are not modelled. The xenia source comment explicitly notes "Same as fcmpu but sets FPSCR exception bits for QNaN (not modeled yet)". Title code that polls FPSCR for compare-class invalid-operation will not observe it. +- **CR field bits.** + - `LT` (bit 0) β€” `FRA < FRB` + - `GT` (bit 1) β€” `FRA > FRB` + - `EQ` (bit 2) β€” `FRA == FRB` + - `SO` (bit 3) β€” unordered (NaN involved) +- **`+0` and `-0` compare equal.** +- **No `Rc` bit.** +- **FPSCR side effects.** Hardware updates `FPSCR[FPCC]`, `FX`, `VX`, and (on NaN) `VXVC`/`VXSNAN`. xenia-rs only updates the CR field. +- **Use case.** Ordered compares are required by C/C++ semantics for `<`, `>`, `<=`, `>=` (which must signal on NaN per IEEE-754). `fcmpu` corresponds to the C `==`/`!=` semantics (which do not signal). +- **Encoding.** X-form, primary 63, XO 32. + +## Related Instructions + +- [`fcmpux`](fcmpu.md) β€” unordered compare; identical CR result, no `VXVC`. +- `mcrf`, `mcrfs`, `mfcr` β€” fan-out CR fields after compare. +- `bc`, `bclr`, `bcctr` β€” conditional branches consume `LT/GT/EQ/SO`. +- [`fselx`](fselx.md) β€” branch-free alternative for single-key compares. +- [`mcrfs`](mcrfs.md), [`mffsx`](mffsx.md) β€” move FPSCR/CR. + +## IBM Reference + +- [AIX 7.3 β€” `fcmpo` (Floating Compare Ordered)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fcmpo-floating-compare-ordered-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (`fcmpo` raises `VXVC` on QNaN; both raise `VXSNAN` on SNaN). diff --git a/tools/ppc-manual/fpu/fcmpu.md b/tools/ppc-manual/fpu/fcmpu.md new file mode 100644 index 00000000..f588a925 --- /dev/null +++ b/tools/ppc-manual/fpu/fcmpu.md @@ -0,0 +1,161 @@ +# `fcmpu` β€” Floating Compare Unordered + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fcmpu` | `fcmpu` | β€” | Floating Compare Unordered | + +## Syntax + +```asm +fcmpu [CRFD], [FA], [FB] +``` + +## Encoding + +### `fcmpu` β€” form `X` + +- **Opcode word:** `0xfc000000` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `0` +- **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 | +| --- | --- | --- | +| `FA` | fcmpu: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fcmpu: read | Source B floating-point register. | +| `CRFD` | fcmpu: write | CR destination field (`crf`, 0–7). | +| `FPSCR` | fcmpu: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fcmpu` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `CRFD`, `FPSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `fcmpu`: **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fcmpu`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fcmpu"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:365`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L365) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:897`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L897) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2972-3001`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2972-L3001) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fcmpu => { + let fra = ctx.fpr[instr.ra()]; + let frb = ctx.fpr[instr.rb()]; + let crfd = instr.crfd(); + if fra.is_nan() || frb.is_nan() { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: false, eq: false, so: true }; + // fcmpu: VXSNAN on SNaN input; no VXVC even on QNaN. + if fpscr::is_snan(fra) || fpscr::is_snan(frb) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + } else if fra < frb { + ctx.cr[crfd] = crate::context::CrField { lt: true, gt: false, eq: false, so: false }; + } else if fra > frb { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: true, eq: false, so: false }; + } else { + ctx.cr[crfd] = crate::context::CrField { lt: false, gt: false, eq: true, so: false }; + } + // Also mirror the comparison result into FPSCR[FPRF (FL/FG/FE/FU)]. + let fprf = if fra.is_nan() || frb.is_nan() { + 0b0_0001 + } else if fra < frb { + 0b0_1000 + } else if fra > frb { + 0b0_0100 + } else { + 0b0_0010 + }; + fpscr::set_fprf(ctx, fprf); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unordered compare.** "Unordered" means NaN inputs do **not** signal an invalid-operation exception β€” they merely set the unordered (`SO`) bit in the destination CR field. Use [`fcmpox`](fcmpo.md) when NaN should raise `VXSNAN`/`VXVC`. +- **CR field bits.** Writes the 4-bit CR field selected by `BF` (`crfd`): + - `LT` (bit 0) β€” `FRA < FRB` + - `GT` (bit 1) β€” `FRA > FRB` + - `EQ` (bit 2) β€” `FRA == FRB` + - `SO` (bit 3) β€” **unordered** (one or both operands is NaN) +- **NaN handling.** Either operand NaN β†’ set `SO=1`, clear `LT/GT/EQ`. xenia-rs matches. +- **Signalling NaN.** Per PowerISA, `fcmpu` sets `FPSCR[VXSNAN]` if either operand is a signalling NaN, but does **not** set `FPSCR[VXVC]` (the difference vs `fcmpo`). xenia-rs does **not** model this β€” **xenia quirk**: `fcmpu` and `fcmpo` are observationally identical in xenia. +- **`+0` and `-0` compare equal.** Standard IEEE rule; xenia's host `<` / `>` on `f64` matches. +- **No `Rc` bit.** The CR field destination is encoded in the instruction (`BF`); there's no record-form variant. +- **FPSCR side effects.** Hardware updates `FPSCR[FPCC]` (the four-bit floating-point condition code) and `FPSCR[FX]`. xenia-rs does not maintain `FPCC`. +- **Precision-agnostic.** Compares the full binary64 values; works equally for single-precision values stored in FPRs (they are bit-identical to their double-precision representation). +- **Encoding.** X-form, primary 63, XO 0. Bits 9–10 of `BF` are unused (reserved 0). + +## Related Instructions + +- [`fcmpox`](fcmpo.md) β€” ordered compare; raises `VXSNAN`/`VXVC` on NaN/SNaN. +- `mcrf`, `mcrfs`, `mfcr` β€” copy CR fields, useful after `fcmpu` to fan out the result. +- `bc`, `bclr`, `bcctr` β€” conditional branches consume the CR fields written by `fcmpu`. +- [`fselx`](fselx.md) β€” branch-free alternative when only the sign of `FRA - FRB` is needed. +- [`mcrfs`](mcrfs.md), [`mffsx`](mffsx.md) β€” move FPSCR data into the CR. + +## IBM Reference + +- [AIX 7.3 β€” `fcmpu` (Floating Compare Unordered)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fcmpu-floating-compare-unordered-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (compare semantics, `FPCC` updates, NaN/SNaN exception rules). diff --git a/tools/ppc-manual/fpu/fctidx.md b/tools/ppc-manual/fpu/fctidx.md new file mode 100644 index 00000000..33f30a22 --- /dev/null +++ b/tools/ppc-manual/fpu/fctidx.md @@ -0,0 +1,149 @@ +# `fctidx` β€” Floating Convert to Integer Doubleword + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00065c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fctid` | `fctidx` | β€” | Floating Convert to Integer Doubleword | +| `fctid.` | `fctidx` | Rc=1 | Floating Convert to Integer Doubleword | + +## Syntax + +```asm +fctid[Rc] [FD], [FB] +``` + +## Encoding + +### `fctidx` β€” form `X` + +- **Opcode word:** `0xfc00065c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `814` +- **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 | +| --- | --- | --- | +| `FB` | fctidx: read | Source B floating-point register. | +| `FD` | fctidx: write | Destination floating-point register. | +| `CR` | fctidx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fctidx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fctidx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fctidx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fctidx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fctidx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:280`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L280) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:912`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L912) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2886-2906`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2886-L2906) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fctidx => { + // Convert to integer doubleword (round per FPSCR[RN]). + // PPCBUG-229: set XX on inexact (fractional input). + let val = ctx.fpr[instr.rb()]; + let result = if val.is_nan() { + fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); + 0x8000_0000_0000_0000u64 + } else if val >= (i64::MAX as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x7FFF_FFFF_FFFF_FFFFu64 + } else if val < (i64::MIN as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x8000_0000_0000_0000u64 + } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } + fpscr::round_to_i64(ctx, val) as u64 + }; + ctx.fpr[instr.rd()] = f64::from_bits(result); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **binary64 β†’ 64-bit signed integer, current rounding mode.** Result is the integer rounded per `FPSCR[RN]`, packed into the 64-bit FPR as raw bits (the FPR is reinterpreted as an `i64` by subsequent `stfd`/integer code). +- **Saturation on out-of-range.** Per PowerISA, values outside `[i64::MIN, i64::MAX]` (or NaN) yield the most-negative integer (`0x8000_0000_0000_0000`) and set `FPSCR[VXCVI, VX, FX]`. xenia-rs special-cases NaN to `0x8000_0000_0000_0000` but **does not saturate** out-of-range finite values β€” Rust's `as i64` from a too-large `f64` produces an undefined-then-saturated result that may differ from the PPC convention. **xenia quirk:** very-large finite inputs may round to a different sentinel than hardware. +- **xenia round implementation.** xenia uses Rust's `f64::round`, which rounds half-cases **away from zero** (NOT round-to-nearest-even). PowerISA round-to-nearest in default mode rounds half-cases to even. **xenia quirk:** values like `0.5`, `1.5`, `2.5` may produce different integers (xenia: `1, 2, 3`; PPC default: `0, 2, 2`). +- **Rounding mode.** PPC uses `FPSCR[RN]` for the rounding direction. xenia ignores the FPSCR mode and always uses `f64::round` (i.e. round-half-away-from-zero) regardless of `RN`. **xenia quirk:** non-default rounding modes are not respected. +- **Inexact.** Sets `FPSCR[XX, FX]` on any non-integer input. xenia does not update FPSCR. +- **NaN.** Returns sentinel `0x8000_0000_0000_0000` and sets `FPSCR[VXCVI]`. xenia matches the sentinel, but does not raise the FPSCR bit. +- **`Rc=1` (`fctid.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 814. Reads `FRB` only. +- **Pair with `stfd`** to extract the `i64` value to memory or a GPR (Xbox 360 has no direct FPR↔GPR move; round-trip via stack). + +## Related Instructions + +- [`fctidzx`](fctidzx.md) β€” same conversion but always rounds toward zero (truncation). +- [`fctiwx`](fctiwx.md), [`fctiwzx`](fctiwzx.md) β€” 32-bit integer variants (saturate to `i32` range). +- [`fcfidx`](fcfidx.md) β€” inverse direction (`i64` β†’ binary64). +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” control `FPSCR[RN]`. +- `stfd`, `stfiwx` β€” store the integer-bits FPR to memory; `stfiwx` stores only the low 32 bits (use after `fctiwx` / `fctiwzx`). + +## IBM Reference + +- [AIX 7.3 β€” `fctid` (Floating Convert to Integer Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fctid-floating-convert-integer-doubleword-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (`VXCVI` is the invalid-conversion exception bit; saturation sentinel is `0x8000_0000_0000_0000`). diff --git a/tools/ppc-manual/fpu/fctidzx.md b/tools/ppc-manual/fpu/fctidzx.md new file mode 100644 index 00000000..ccc50e4d --- /dev/null +++ b/tools/ppc-manual/fpu/fctidzx.md @@ -0,0 +1,151 @@ +# `fctidzx` β€” Floating Convert to Integer Doubleword with Round Toward Zero + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00065e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fctidz` | `fctidzx` | β€” | Floating Convert to Integer Doubleword with Round Toward Zero | +| `fctidz.` | `fctidzx` | Rc=1 | Floating Convert to Integer Doubleword with Round Toward Zero | + +## Syntax + +```asm +fctidz[Rc] [FD], [FB] +``` + +## Encoding + +### `fctidzx` β€” form `X` + +- **Opcode word:** `0xfc00065e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `815` +- **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 | +| --- | --- | --- | +| `FB` | fctidzx: read | Source B floating-point register. | +| `FD` | fctidzx: write | Destination floating-point register. | +| `CR` | fctidzx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fctidzx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fctidzx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fctidzx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fctidzx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fctidzx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:285`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L285) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:913`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L913) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2907-2927`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2907-L2927) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fctidzx => { + // Convert to integer doubleword (round toward zero). + // PPCBUG-229: set XX on inexact. + let val = ctx.fpr[instr.rb()]; + let result = if val.is_nan() { + fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); + 0x8000_0000_0000_0000u64 + } else if val >= (i64::MAX as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x7FFF_FFFF_FFFF_FFFFu64 + } else if val < (i64::MIN as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x8000_0000_0000_0000u64 + } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } + (val.trunc() as i64) as u64 + }; + ctx.fpr[instr.rd()] = f64::from_bits(result); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **binary64 β†’ 64-bit signed integer, round toward zero.** The "z" suffix forces truncation regardless of `FPSCR[RN]`. xenia-rs uses Rust's `as i64` (which truncates toward zero), bypassing the FPSCR rounding mode entirely β€” this matches PPC `fctidz` semantics correctly. +- **Saturation on out-of-range.** PowerISA: out-of-range or NaN β†’ `0x8000_0000_0000_0000` and `FPSCR[VXCVI, VX, FX]`. xenia handles NaN explicitly with the sentinel, but uses raw `as i64` for finite values; in current Rust (since 1.45) `as i64` from out-of-range `f64` is **defined to saturate** to `i64::MIN`/`i64::MAX`. So: + - **+∞ or large positive β†’ `i64::MAX`** (`0x7FFF_FFFF_FFFF_FFFF`) under xenia. + - **βˆ’βˆž or large negative β†’ `i64::MIN`** (`0x8000_0000_0000_0000`) under xenia. + - **PPC** spec returns `0x8000_0000_0000_0000` for both. **xenia quirk:** positive overflow returns the wrong sentinel. +- **NaN.** Returns sentinel `0x8000_0000_0000_0000` (matches PPC). +- **Inexact.** Sets `FPSCR[XX, FX]` on any non-integer input. xenia does not update FPSCR (xenia quirk). +- **No `FPSCR[RN]` dependence.** `fctidz` always truncates; this is the right choice for C/C++ `(int64_t)` casts. +- **`Rc=1` (`fctidz.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 815. Reads `FRB` only. +- **Common pairing.** Translation of C `(int64_t)d` casts; combined with `stfd` to move the value to integer memory. + +## Related Instructions + +- [`fctidx`](fctidx.md) β€” same conversion but uses `FPSCR[RN]` (default nearest-even on PPC; xenia uses `f64::round` regardless). +- [`fctiwzx`](fctiwzx.md) β€” 32-bit truncating variant. +- [`fctiwx`](fctiwx.md) β€” 32-bit `FPSCR[RN]`-rounded variant. +- [`fcfidx`](fcfidx.md) β€” inverse direction. +- `stfd` β€” store the integer-bits FPR to memory. + +## IBM Reference + +- [AIX 7.3 β€” `fctidz` (Floating Convert to Integer Doubleword with Round Toward Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fctidz-floating-convert-integer-doubleword-round-toward-zero-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fctiwx.md b/tools/ppc-manual/fpu/fctiwx.md new file mode 100644 index 00000000..96bb4831 --- /dev/null +++ b/tools/ppc-manual/fpu/fctiwx.md @@ -0,0 +1,149 @@ +# `fctiwx` β€” Floating Convert to Integer Word + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00001c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fctiw` | `fctiwx` | β€” | Floating Convert to Integer Word | +| `fctiw.` | `fctiwx` | Rc=1 | Floating Convert to Integer Word | + +## Syntax + +```asm +fctiw[Rc] [FD], [FB] +``` + +## Encoding + +### `fctiwx` β€” form `X` + +- **Opcode word:** `0xfc00001c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `14` +- **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 | +| --- | --- | --- | +| `FB` | fctiwx: read | Source B floating-point register. | +| `FD` | fctiwx: write | Destination floating-point register. | +| `CR` | fctiwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fctiwx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fctiwx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fctiwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fctiwx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fctiwx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:308`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L308) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:899`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L899) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2928-2948`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2928-L2948) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fctiwx => { + // Convert to integer word (round per FPSCR[RN]). + // PPCBUG-230: set XX on inexact. + let val = ctx.fpr[instr.rb()]; + let result_u32: u32 = if val.is_nan() { + fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); + 0x8000_0000 + } else if val > (i32::MAX as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x7FFF_FFFF + } else if val < (i32::MIN as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x8000_0000 + } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } + fpscr::round_to_i32(ctx, val) as u32 + }; + ctx.fpr[instr.rd()] = f64::from_bits(result_u32 as u64); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **binary64 β†’ 32-bit signed integer, current rounding mode.** Result is rounded per `FPSCR[RN]` and packed into the low 32 bits of the destination FPR. The high 32 bits are architecturally undefined per PowerISA but xenia produces zero-extended `u32` (i.e. the high 32 bits are 0). +- **Explicit saturation in xenia.** xenia's body clamps the rounded `f64` to `[i32::MIN as f64, i32::MAX as f64]` before the integer cast β€” this matches PPC's saturation behaviour for out-of-range positive/negative finite inputs. +- **NaN sentinel.** xenia returns `0x0000_0000_8000_0000` for NaN inputs (i.e. `i32::MIN` in the low word). Matches PPC's `VXCVI` sentinel for NaN/out-of-range. +- **Rounding implementation.** xenia uses `f64::round`, which rounds half-cases **away from zero** rather than to nearest-even. **xenia quirk:** values like `0.5`/`1.5`/`2.5` produce `1`/`2`/`3` under xenia vs `0`/`2`/`2` on PPC default rounding. +- **`FPSCR[RN]` not honored.** xenia always uses `f64::round`, ignoring the rounding-mode field. **xenia quirk** for non-default modes. +- **FPSCR side effects.** PPC: sets `XX`/`FX` on inexact, `VXCVI` on NaN/out-of-range. xenia does not update FPSCR. +- **`Rc=1` (`fctiw.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 14. Reads `FRB` only. +- **Common pairing.** Followed by `stfiwx` to store the low-32-bit integer to memory (`stfd` would write the doubleword including the high bits, which on hardware are undefined). + +## Related Instructions + +- [`fctiwzx`](fctiwzx.md) β€” 32-bit integer with round-toward-zero (truncation). +- [`fctidx`](fctidx.md), [`fctidzx`](fctidzx.md) β€” 64-bit integer variants. +- [`fcfidx`](fcfidx.md) β€” inverse direction (i64 β†’ f64); for i32 β†’ f64, sign-extend then `fcfid`. +- `stfiwx` β€” store low-32-bits FPR (the canonical companion to `fctiw`/`fctiwz`). +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” control `FPSCR[RN]` (currently a no-op under xenia for this instruction). + +## IBM Reference + +- [AIX 7.3 β€” `fctiw` (Floating Convert to Integer Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fctiw-floating-convert-integer-word-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (high 32 bits are architecturally undefined; only `stfiwx` is the spec-blessed consumer). diff --git a/tools/ppc-manual/fpu/fctiwzx.md b/tools/ppc-manual/fpu/fctiwzx.md new file mode 100644 index 00000000..4d108bf4 --- /dev/null +++ b/tools/ppc-manual/fpu/fctiwzx.md @@ -0,0 +1,148 @@ +# `fctiwzx` β€” Floating Convert to Integer Word with Round Toward Zero + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc00001e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fctiwz` | `fctiwzx` | β€” | Floating Convert to Integer Word with Round Toward Zero | +| `fctiwz.` | `fctiwzx` | Rc=1 | Floating Convert to Integer Word with Round Toward Zero | + +## Syntax + +```asm +fctiwz[Rc] [FD], [FB] +``` + +## Encoding + +### `fctiwzx` β€” form `X` + +- **Opcode word:** `0xfc00001e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `15` +- **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 | +| --- | --- | --- | +| `FB` | fctiwzx: read | Source B floating-point register. | +| `FD` | fctiwzx: write | Destination floating-point register. | +| `CR` | fctiwzx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fctiwzx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fctiwzx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fctiwzx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fctiwzx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fctiwzx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:313`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L313) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:27`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L27) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:900`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L900) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2949-2969`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2949-L2969) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fctiwzx => { + // Convert to integer word (round toward zero). + // PPCBUG-230: set XX on inexact. + let val = ctx.fpr[instr.rb()]; + let result_u32: u32 = if val.is_nan() { + fpscr::set_exception(ctx, fpscr::VXCVI | if fpscr::is_snan(val) { fpscr::VXSNAN } else { 0 }); + 0x8000_0000 + } else if val > (i32::MAX as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x7FFF_FFFF + } else if val < (i32::MIN as f64) { + fpscr::set_exception(ctx, fpscr::VXCVI); + 0x8000_0000 + } else { + if val != val.trunc() { fpscr::set_exception(ctx, fpscr::XX); } + val.trunc() as i32 as u32 + }; + ctx.fpr[instr.rd()] = f64::from_bits(result_u32 as u64); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **binary64 β†’ 32-bit signed integer, round toward zero.** Truncates regardless of `FPSCR[RN]`. xenia-rs uses `clamp` to saturate to `[i32::MIN, i32::MAX]` then `as i32`, which truncates β€” matching PPC `fctiwz` semantics. +- **Most common conversion in compiled code.** Translates C/C++ `(int32_t)f` casts, which require truncation per the C standard. +- **Saturation on out-of-range.** Hardware saturates to `i32::MAX` for large positives, `i32::MIN` for large negatives or NaN, and sets `FPSCR[VXCVI, VX, FX]`. xenia's explicit `clamp` correctly reproduces the saturation, but does not raise FPSCR bits (xenia quirk). +- **NaN sentinel.** xenia returns `0x0000_0000_8000_0000` (i.e. `i32::MIN` in low 32 bits). Matches PPC sentinel. +- **High 32 bits of FPR.** Architecturally undefined per PowerISA, but xenia produces zero-extended `u32`. Use `stfiwx` (store low 32 bits) β€” never `stfd` β€” for the canonical "store this integer" idiom. +- **Inexact.** Sets `FPSCR[XX, FX]` on any non-integer input. xenia does not update FPSCR. +- **`Rc=1` (`fctiwz.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 15. Reads `FRB` only. + +## Related Instructions + +- [`fctiwx`](fctiwx.md) β€” 32-bit integer with `FPSCR[RN]` rounding. +- [`fctidx`](fctidx.md), [`fctidzx`](fctidzx.md) β€” 64-bit integer variants. +- [`fcfidx`](fcfidx.md) β€” inverse direction (i64 β†’ f64); for i32 β†’ f64, sign-extend to i64 first. +- `stfiwx` β€” store low 32 bits of FPR; canonical companion. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” FPSCR control (no effect on `fctiwz` since rounding mode is fixed to truncation). + +## IBM Reference + +- [AIX 7.3 β€” `fctiwz` (Floating Convert to Integer Word with Round Toward Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fctiwz-floating-convert-integer-word-round-toward-zero-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fdivsx.md b/tools/ppc-manual/fpu/fdivsx.md new file mode 100644 index 00000000..b2e8e7b8 --- /dev/null +++ b/tools/ppc-manual/fpu/fdivsx.md @@ -0,0 +1,143 @@ +# `fdivsx` β€” Floating Divide Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec000024` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fdivs` | `fdivsx` | β€” | Floating Divide Single | +| `fdivs.` | `fdivsx` | Rc=1 | Floating Divide Single | + +## Syntax + +```asm +fdivs[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `fdivsx` β€” form `A` + +- **Opcode word:** `0xec000024` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `18` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fdivsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fdivsx: read | Source B floating-point register. | +| `FD` | fdivsx: write | Destination floating-point register. | +| `CR` | fdivsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fdivsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fdivsx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fdivsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fdivsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fdivsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:71`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L71) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:386`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L386) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2627-2637`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2627-L2637) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fdivsx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_div(ctx, a, b); + fpscr::check_zero_divide(ctx, a, b); + let result = to_single(ctx, a / b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && b != 0.0); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single precision.** Result is rounded to IEEE-754 binary32 then re-encoded into the 64-bit FPR. xenia computes `to_single(a / b)`. +- **Divide by zero.** Finite/Β±0 sets `FPSCR[ZX, FX]` and yields ±∞. xenia returns the host ±∞ but does not update FPSCR (xenia quirk). +- **`0 / 0`** β†’ `FPSCR[VXZDZ, VX, FX]`, quiet NaN result. +- **`±∞ / ±∞`** β†’ `FPSCR[VXIDI, VX, FX]`, quiet NaN result. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, plus exception bits `OX`, `UX`, `XX`, `ZX`, `VXZDZ`, `VXIDI`, `VXSNAN`. +- **`Rc=1` (`fdivs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** returns ±∞ and sets `OX`/`XX`/`FX`. +- **Performance.** Hardware divide is multi-cycle. Title code commonly uses `fres` + Newton-Raphson for hot loops; this instruction is reserved for non-critical paths. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. +- **Encoding.** A-form, primary 59, XO 18. + +## Related Instructions + +- [`fdivx`](fdivx.md) β€” double-precision sibling. +- [`fresx`](fresx.md) β€” reciprocal estimate, used to build software divides. +- [`fmulsx`](fmulsx.md), [`faddsx`](faddsx.md), [`fsubsx`](fsubsx.md) β€” companion single-precision arithmetic. +- [`fmaddsx`](fmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” Newton-Raphson refinement helpers. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fdivs` (Floating Divide Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fdivs-floating-divide-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fdivx.md b/tools/ppc-manual/fpu/fdivx.md new file mode 100644 index 00000000..2e0de3eb --- /dev/null +++ b/tools/ppc-manual/fpu/fdivx.md @@ -0,0 +1,133 @@ +# `fdivx` β€” Floating Divide + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc000024` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fdiv` | `fdivx` | β€” | Floating Divide | +| `fdiv.` | `fdivx` | Rc=1 | Floating Divide | + +## Syntax + +```asm +fdiv[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `fdivx` β€” form `A` + +- **Opcode word:** `0xfc000024` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `18` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fdivx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fdivx: read | Source B floating-point register. | +| `FD` | fdivx: write | Destination floating-point register. | +| `CR` | fdivx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fdivx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fdivx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fdivx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- FRA Γ· FRB +``` + +## 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 + +**`fdivx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fdivx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:55`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L55) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:920`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L920) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2616-2626`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2616-L2626) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fdivx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_div(ctx, a, b); + fpscr::check_zero_divide(ctx, a, b); + let result = a / b; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && b != 0.0); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Double precision.** Operates on IEEE-754 binary64; [`fdivsx`](fdivsx.md) is the single-precision sibling. +- **Divide by zero.** `FRA / Β±0` (with `FRA` finite, non-zero) sets `FPSCR[ZX, FX]` and produces a correctly-signed infinity. xenia relies on host `f64 /`, which produces the same ±∞ β€” but does not raise `ZX` in the interpreter snapshot. **xenia quirk:** title code that polls FPSCR for divide-by-zero will not observe it. +- **`0 / 0`** sets `FPSCR[VXZDZ, VX, FX]` and yields a quiet NaN. +- **`±∞ / ±∞`** sets `FPSCR[VXIDI, VX, FX]` and yields a quiet NaN. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` plus exception bits `OX`, `UX`, `XX`, `ZX`, `VXZDZ`, `VXIDI`, `VXSNAN`. xenia-rs does not maintain these. +- **`Rc=1` (`fdiv.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Performance.** Hardware divide is multi-cycle and not pipelined on Xenon. Many titles prefer `fres`/`frsqrte` followed by Newton-Raphson refinement (or by `fmadd` chains) to avoid the divider. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE. +- **Encoding.** A-form, primary 63, XO 18. `FRC` is don't-care. + +## Related Instructions + +- [`fdivsx`](fdivsx.md) β€” single-precision divide. +- [`fresx`](fresx.md) β€” reciprocal estimate `~1/FRB`; combined with `fmul`/`fmadd` to implement reciprocal divides. +- [`fmulx`](fmulx.md), [`faddx`](faddx.md), [`fsubx`](fsubx.md) β€” companion arithmetic. +- [`fmaddx`](fmaddx.md), [`fnmsubx`](fnmsubx.md) β€” used in Newton-Raphson refinement steps. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” FPSCR control (rounding mode, exception masks). + +## IBM Reference + +- [AIX 7.3 β€” `fdiv` (Floating Divide)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fd-fdiv-floating-divide-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (divide-by-zero and invalid-operation rules). diff --git a/tools/ppc-manual/fpu/fmaddsx.md b/tools/ppc-manual/fpu/fmaddsx.md new file mode 100644 index 00000000..ab4bd51a --- /dev/null +++ b/tools/ppc-manual/fpu/fmaddsx.md @@ -0,0 +1,147 @@ +# `fmaddsx` β€” Floating Multiply-Add Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec00003a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmadds` | `fmaddsx` | β€” | Floating Multiply-Add Single | +| `fmadds.` | `fmaddsx` | Rc=1 | Floating Multiply-Add Single | + +## Syntax + +```asm +fmadds[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fmaddsx` β€” form `A` + +- **Opcode word:** `0xec00003a` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `29` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmaddsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmaddsx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fmaddsx: read | Source B floating-point register. | +| `FD` | fmaddsx: write | Destination floating-point register. | +| `CR` | fmaddsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmaddsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmaddsx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmaddsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fmaddsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmaddsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:190`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L190) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:393`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L393) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2653-2665`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2653-L2665) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmaddsx => { + // PPCBUG-181: missing VXISI on add step. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, false); + let result = to_single(ctx, a.mul_add(c, b)); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step then single-precision rounding.** PowerISA semantics: compute `(FRA Γ— FRC) + FRB` to infinite precision, then round once to binary32. xenia-rs implements this as `to_single(a.mul_add(c, b))` β€” the `mul_add` is the single-step fused multiply-add at double precision, then `to_single` rounds the binary64 result to binary32. This matches PPC's "single rounding" requirement because the intermediate `mul_add` is already exact-rounded. +- **Operand order.** Assembler: `FD, FA, FC, FB` (multiplier `FRC` before addend `FRB`). +- **Invalid operations.** `0Γ—βˆž + finite` β†’ `VXIMZ`; opposite-signed-∞ collision β†’ `VXISI`. Quiet NaN result with `FPSCR[VX, FX]`. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fmadds.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** of the final rounded result returns ±∞ and sets `OX`/`XX`/`FX`. +- **Use case.** Dominates single-precision graphics math: matrix–vector multiplies, dot products, lighting equations, normal-map blending. Xbox 360 titles emit `fmadds` constantly. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fmaddx`](fmaddx.md) β€” double-precision sibling. +- [`fmsubsx`](fmsubsx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” single-precision fused-multiply siblings: + - `fmsubs` = `(AΓ—C) βˆ’ B` + - `fnmadds` = `βˆ’((AΓ—C) + B)` + - `fnmsubs` = `βˆ’((AΓ—C) βˆ’ B)` +- [`fmulsx`](fmulsx.md), [`faddsx`](faddsx.md) β€” non-fused decomposition. +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal helpers; Newton-Raphson refinement uses `fmadds`/`fnmsubs`. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fmadds` (Floating Multiply-Add Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fmadds-floating-multiply-add-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fmaddx.md b/tools/ppc-manual/fpu/fmaddx.md new file mode 100644 index 00000000..a7e567fa --- /dev/null +++ b/tools/ppc-manual/fpu/fmaddx.md @@ -0,0 +1,136 @@ +# `fmaddx` β€” Floating Multiply-Add + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00003a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmadd` | `fmaddx` | β€” | Floating Multiply-Add | +| `fmadd.` | `fmaddx` | Rc=1 | Floating Multiply-Add | + +## Syntax + +```asm +fmadd[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fmaddx` β€” form `A` + +- **Opcode word:** `0xfc00003a` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `29` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmaddx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmaddx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fmaddx: read | Source B floating-point register. | +| `FD` | fmaddx: write | Destination floating-point register. | +| `CR` | fmaddx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmaddx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmaddx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmaddx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- (FRA Γ— FRC) + FRB +``` + +## 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 + +**`fmaddx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmaddx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:186`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L186) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:928`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L928) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2640-2652`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2640-L2652) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmaddx => { + // PPCBUG-202: VXISI from input properties (not from `a*c` which has wrong sign on overflow). + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, false); + let result = a.mul_add(c, b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step.** `fmadd` computes `(FRA Γ— FRC) + FRB` with one IEEE-754 rounding at the end β€” strictly more accurate than separate multiply + add. xenia-rs uses Rust's `f64::mul_add`, which guarantees a true FMA on hosts with hardware FMA (x86_64 with FMA3, ARM with NEON-FMA); on hosts without it, Rust's stdlib falls back to a software FMA so the semantic match is preserved. +- **Operand layout.** A-form: `FRT, FRA, FRC, FRB`. Note the assembler order β€” `FRC` (multiplier) comes before `FRB` (addend). Encoding bit fields are `FRA` (11–15), `FRB` (16–20), `FRC` (21–25). +- **Invalid operations.** `0Γ—βˆž + finite` β†’ `VXIMZ`; `βˆžΓ—x + βˆ“βˆž` (after multiplication produces ±∞ that opposes addend sign) β†’ `VXISI`. Quiet NaN result with `FPSCR[VX, FX]` set. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not update FPSCR (xenia quirk). +- **`Rc=1` (`fmadd.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Use case.** Dot products, polynomial evaluation (Horner's method), matrix multiplies, Newton-Raphson divide/sqrt refinement. Hot-path PPC code is dense with `fmadd`. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fmaddsx`](fmaddsx.md) β€” single-precision sibling. +- [`fmsubx`](fmsubx.md), [`fnmaddx`](fnmaddx.md), [`fnmsubx`](fnmsubx.md) β€” the other three fused multiply-add variants: + - `fmsub` = `(AΓ—C) βˆ’ B` + - `fnmadd` = `βˆ’((AΓ—C) + B)` + - `fnmsub` = `βˆ’((AΓ—C) βˆ’ B)` +- [`fmulx`](fmulx.md), [`faddx`](faddx.md) β€” non-fused decomposition (two rounding steps; less precise). +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal helpers refined by `fmadd`/`fnmsub`. + +## IBM Reference + +- [AIX 7.3 β€” `fmadd` (Floating Multiply-Add)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fma-fmadd-floating-multiply-add-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (single-rounding fused multiply-add definition). diff --git a/tools/ppc-manual/fpu/fmrx.md b/tools/ppc-manual/fpu/fmrx.md new file mode 100644 index 00000000..76e5b984 --- /dev/null +++ b/tools/ppc-manual/fpu/fmrx.md @@ -0,0 +1,120 @@ +# `fmrx` β€” Floating Move Register + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000090` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmr` | `fmrx` | β€” | Floating Move Register | +| `fmr.` | `fmrx` | Rc=1 | Floating Move Register | + +## Syntax + +```asm +fmr[Rc] [FD], [FB] +``` + +## Encoding + +### `fmrx` β€” form `X` + +- **Opcode word:** `0xfc000090` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `72` +- **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 | +| --- | --- | --- | +| `FB` | fmrx: read | Source B floating-point register. | +| `FD` | fmrx: write | Destination floating-point register. | +| `CR` | fmrx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `fmrx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmrx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +FRT <- FRB +``` + +## 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 + +**`fmrx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmrx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:496`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L496) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:906`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L906) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2752-2756`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2752-L2756) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmrx => { + ctx.fpr[instr.rd()] = ctx.fpr[instr.rb()]; + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bit-pattern copy, no rounding.** `fmr` copies the 64-bit binary representation of `FRB` into `FRT` unchanged. No precision loss, no FPSCR exception bits, no NaN quietening. xenia-rs implements this as a plain `f64` copy. +- **NaN preserved verbatim.** Signalling/quiet bit, payload, and sign are all preserved exactly. Unlike arithmetic instructions, `fmr` does **not** quieten signalling NaNs. +- **Special values.** All bit patterns pass through untouched, including Β±0, ±∞, and any NaN. The destination receives an exact copy. +- **FPSCR.** Hardware does **not** update `FPRF` or any exception bit. The "FPSCR write" implied in the header refers only to `Rc=1` updating CR1 from existing FPSCR contents. +- **`Rc=1` (`fmr.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **No `FRA`.** X-form, primary 63, XO 72. Reads `FRB` only. +- **Cheaper than load-store.** Compilers emit `fmr` for FPR-to-FPR moves; transferring a value via memory (`stfd`/`lfd`) would be far more expensive. + +## Related Instructions + +- [`fabsx`](fabsx.md), [`fnegx`](fnegx.md), [`fnabsx`](fnabsx.md) β€” sign-bit variants of the move (clear / toggle / set). +- [`fselx`](fselx.md) β€” branch-free select; like a conditional `fmr`. +- [`mffsx`](mffsx.md) β€” read FPSCR into an FPR; complementary "FPR move" for a control register. +- `stfd`/`lfd` β€” memory-mediated FPR transfer (much slower; used for register window spills). + +## IBM Reference + +- [AIX 7.3 β€” `fmr` (Floating Move Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fmr-floating-move-register-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (move-class instructions explicitly bypass quietening and FPSCR side effects). diff --git a/tools/ppc-manual/fpu/fmsubsx.md b/tools/ppc-manual/fpu/fmsubsx.md new file mode 100644 index 00000000..5a9c70c1 --- /dev/null +++ b/tools/ppc-manual/fpu/fmsubsx.md @@ -0,0 +1,144 @@ +# `fmsubsx` β€” Floating Multiply-Subtract Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec000038` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmsubs` | `fmsubsx` | β€” | Floating Multiply-Subtract Single | +| `fmsubs.` | `fmsubsx` | Rc=1 | Floating Multiply-Subtract Single | + +## Syntax + +```asm +fmsubs[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fmsubsx` β€” form `A` + +- **Opcode word:** `0xec000038` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `28` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmsubsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmsubsx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fmsubsx: read | Source B floating-point register. | +| `FD` | fmsubsx: write | Destination floating-point register. | +| `CR` | fmsubsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmsubsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmsubsx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmsubsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fmsubsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmsubsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:209`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L209) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:392`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L392) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2679-2691`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2679-L2691) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmsubsx => { + // PPCBUG-182: missing VXISI on sub step. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, true); + let result = to_single(ctx, a.mul_add(c, -b)); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step then round-to-single.** Computes `(FRA Γ— FRC) βˆ’ FRB` with one fused rounding at double precision, then rounds the binary64 result to binary32. xenia-rs implements this as `to_single(a.mul_add(c, -b))`. +- **Operand order.** Assembler: `FD, FA, FC, FB`. The multiplier `FRC` precedes the addend `FRB`. +- **Invalid operations.** `0Γ—βˆž βˆ’ finite` β†’ `VXIMZ`; `(Β±βˆžΓ—x) βˆ’ ±∞` (same sign) β†’ `VXISI`. Quiet NaN result with `FPSCR[VX, FX]`. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fmsubs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** of the final rounded result returns ±∞ and sets `OX`/`XX`/`FX`. +- **Use case.** Newton-Raphson refinement of `fres`: `x_new = x*(2 - d*x)` decomposes to a `fmsubs`/`fnmsubs` pair. Also common in residual-correction loops. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fmsubx`](fmsubx.md) β€” double-precision sibling. +- [`fmaddsx`](fmaddsx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” other single-precision fused-multiply variants. +- [`fmulsx`](fmulsx.md), [`fsubsx`](fsubsx.md) β€” non-fused decomposition. +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal helpers refined by `fmsubs`/`fnmsubs`. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fmsubs` (Floating Multiply-Subtract Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fmsubs-floating-multiply-subtract-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fmsubx.md b/tools/ppc-manual/fpu/fmsubx.md new file mode 100644 index 00000000..5393ce2f --- /dev/null +++ b/tools/ppc-manual/fpu/fmsubx.md @@ -0,0 +1,135 @@ +# `fmsubx` β€” Floating Multiply-Subtract + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc000038` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmsub` | `fmsubx` | β€” | Floating Multiply-Subtract | +| `fmsub.` | `fmsubx` | Rc=1 | Floating Multiply-Subtract | + +## Syntax + +```asm +fmsub[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fmsubx` β€” form `A` + +- **Opcode word:** `0xfc000038` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `28` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmsubx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmsubx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fmsubx: read | Source B floating-point register. | +| `FD` | fmsubx: write | Destination floating-point register. | +| `CR` | fmsubx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmsubx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmsubx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmsubx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- (FRA Γ— FRC) βˆ’ FRB +``` + +## 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 + +**`fmsubx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmsubx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:205`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L205) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:927`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L927) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2666-2678`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2666-L2678) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmsubx => { + // PPCBUG-203: missing VXISI on sub step. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, true); + let result = a.mul_add(c, -b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step.** `fmsub` computes `(FRA Γ— FRC) βˆ’ FRB` with one rounding at the end. xenia-rs implements this as `a.mul_add(c, -b)`, which is a true FMA on hosts that have hardware support and a software FMA on those that don't. +- **Subtle: negate-then-FMA.** Negating `b` before passing to FMA matters for sign of zero and overflow. `(+0Γ—+0) βˆ’ (+0)` = `+0` in round-to-nearest, but `(+0Γ—+0) βˆ’ (βˆ’0)` = `+0` (the negation flips it before the FMA). Standard IEEE rules apply. +- **Operand order.** Assembler: `FD, FA, FC, FB`. +- **Invalid operations.** `0Γ—βˆž βˆ’ finite` β†’ `VXIMZ`; same-signed infinity collision (e.g. `(+βˆžΓ—+1) βˆ’ (+∞)`) β†’ `VXISI`. Quiet NaN result with `FPSCR[VX, FX]`. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not update FPSCR (xenia quirk). +- **`Rc=1` (`fmsub.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Use case.** Newton-Raphson refinement of reciprocal estimates: `x_new = x*(2 - d*x) = -((d*x) - 2)` uses `fnmsub`, but `fmsub` shows up wherever `(a*c) - b` appears (residuals, error correction). +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fmsubsx`](fmsubsx.md) β€” single-precision sibling. +- [`fmaddx`](fmaddx.md), [`fnmaddx`](fnmaddx.md), [`fnmsubx`](fnmsubx.md) β€” other fused multiply-add variants. +- [`fmulx`](fmulx.md), [`fsubx`](fsubx.md) β€” non-fused decomposition (two rounding steps). +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal helpers refined by fused multiply-subtracts. +- [`fnegx`](fnegx.md) β€” sign flip (the bit-pattern op behind `-FRB` in xenia's implementation). + +## IBM Reference + +- [AIX 7.3 β€” `fmsub` (Floating Multiply-Subtract)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fms-fmsub-floating-multiply-subtract-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fmulsx.md b/tools/ppc-manual/fpu/fmulsx.md new file mode 100644 index 00000000..b931f6ac --- /dev/null +++ b/tools/ppc-manual/fpu/fmulsx.md @@ -0,0 +1,140 @@ +# `fmulsx` β€” Floating Multiply Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec000032` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmuls` | `fmulsx` | β€” | Floating Multiply Single | +| `fmuls.` | `fmulsx` | Rc=1 | Floating Multiply Single | + +## Syntax + +```asm +fmuls[Rc] [FD], [FA], [FC] +``` + +## Encoding + +### `fmulsx` β€” form `A` + +- **Opcode word:** `0xec000032` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `25` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmulsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmulsx: read | Source C floating-point register (for madd-style ops). | +| `FD` | fmulsx: write | Destination floating-point register. | +| `CR` | fmulsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmulsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmulsx` + +- **Reads (always):** `FA`, `FC` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmulsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fmulsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmulsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:97`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L97) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:391`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L391) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2606-2615`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2606-L2615) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmulsx => { + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + fpscr::check_invalid_mul(ctx, a, c); + let result = to_single(ctx, a * c); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **A-form quirk: multiplier is `FRC`.** Operands come from `FRA` (bits 11–15) and `FRC` (bits 21–25). xenia decodes via `instr.rc()` (don't confuse with `rc_bit()` for the record bit). +- **Single precision.** Result is rounded to IEEE-754 binary32 then re-encoded into the 64-bit FPR. xenia uses `to_single(a * c)`. +- **`0 Γ— ±∞`** sets `FPSCR[VXIMZ, VX, FX]` and yields a quiet NaN. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` and exception bits `OX`, `UX`, `XX`, `VXIMZ`, `VXSNAN`. xenia-rs does **not** maintain FPSCR (xenia quirk). +- **`Rc=1` (`fmuls.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** returns ±∞ and sets `OX`/`XX`/`FX`. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia inherits host IEEE behavior, so subnormal results may differ subtly from hardware. +- **Encoding.** A-form, primary 59, XO 25. + +## Related Instructions + +- [`fmulx`](fmulx.md) β€” double-precision multiply. +- [`fmaddsx`](fmaddsx.md), [`fmsubsx`](fmsubsx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” single-precision fused multiply-add family (one rounding step; preferred for dot products). +- [`faddsx`](faddsx.md), [`fsubsx`](fsubsx.md), [`fdivsx`](fdivsx.md) β€” companion single-precision arithmetic. +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal estimates often paired with `fmuls` to compute `a * (1/b)`. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding helper. + +## IBM Reference + +- [AIX 7.3 β€” `fmuls` (Floating Multiply Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fmuls-floating-multiply-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fmulx.md b/tools/ppc-manual/fpu/fmulx.md new file mode 100644 index 00000000..ae418f6b --- /dev/null +++ b/tools/ppc-manual/fpu/fmulx.md @@ -0,0 +1,132 @@ +# `fmulx` β€” Floating Multiply + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc000032` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fmul` | `fmulx` | β€” | Floating Multiply | +| `fmul.` | `fmulx` | Rc=1 | Floating Multiply | + +## Syntax + +```asm +fmul[Rc] [FD], [FA], [FC] +``` + +## Encoding + +### `fmulx` β€” form `A` + +- **Opcode word:** `0xfc000032` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `25` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fmulx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fmulx: read | Source C floating-point register (for madd-style ops). | +| `FD` | fmulx: write | Destination floating-point register. | +| `CR` | fmulx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fmulx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fmulx` + +- **Reads (always):** `FA`, `FC` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fmulx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- FRA Γ— FRC +``` + +## 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 + +**`fmulx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fmulx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:89`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L89) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:28`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L28) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:925`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L925) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2595-2605`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2595-L2605) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fmulx => { + // A-form: frD = frA * frC (frC is at rc() field, bits 21-25) + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + fpscr::check_invalid_mul(ctx, a, c); + let result = a * c; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **A-form quirk: multiplier is `FRC`, not `FRB`.** `fmul` reads operands from the `FRA` (bits 11–15) and `FRC` (bits 21–25) fields, bridging the multiply and fused-multiply-add families. xenia decodes this as `instr.rc()` (the FRC field, distinct from `rc_bit()` for the record bit). +- **Double precision.** Operates on IEEE-754 binary64; [`fmulsx`](fmulsx.md) rounds to binary32. +- **`0 Γ— ±∞` is invalid.** Sets `FPSCR[VXIMZ, VX, FX]` and yields a quiet NaN. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` plus exception bits `OX` (overflow), `UX` (underflow), `XX` (inexact), `VXIMZ` (0Γ—βˆž), `VXSNAN` (signalling NaN). xenia-rs does **not** update FPSCR in the interpreter snapshot β€” xenia quirk. +- **`Rc=1` (`fmul.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Any NaN operand yields a quiet NaN; signalling NaNs are quietened. +- **Sign of result.** Standard IEEE: `sign(a) XOR sign(c)`. `+0 Γ— βˆ’0 = βˆ’0` and `βˆ’x Γ— +∞ = βˆ’βˆž`. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1` (flush-to-zero); xenia inherits host IEEE behavior, so multiplications that produce subnormal results may differ subtly from hardware. +- **Rounding mode** uses `FPSCR[RN]` (default nearest-even). + +## Related Instructions + +- [`fmulsx`](fmulsx.md) β€” single-precision multiply. +- [`fmaddx`](fmaddx.md), [`fmsubx`](fmsubx.md), [`fnmaddx`](fnmaddx.md), [`fnmsubx`](fnmsubx.md) β€” fused multiply-add family; share the same `FRA Γ— FRC` core but add/subtract `FRB` with a single rounding step. Prefer fused forms for dot products and polynomial evaluation. +- [`faddx`](faddx.md), [`fsubx`](fsubx.md), [`fdivx`](fdivx.md) β€” sibling double-precision arithmetic. +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal helpers commonly paired with `fmul` for reciprocal divides. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” FPSCR control. + +## IBM Reference + +- [AIX 7.3 β€” `fmul` (Floating Multiply)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fm-fmul-floating-multiply-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fnabsx.md b/tools/ppc-manual/fpu/fnabsx.md new file mode 100644 index 00000000..2314a133 --- /dev/null +++ b/tools/ppc-manual/fpu/fnabsx.md @@ -0,0 +1,120 @@ +# `fnabsx` β€” Floating Negative Absolute Value + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000110` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fnabs` | `fnabsx` | β€” | Floating Negative Absolute Value | +| `fnabs.` | `fnabsx` | Rc=1 | Floating Negative Absolute Value | + +## Syntax + +```asm +fnabs[Rc] [FD], [FB] +``` + +## Encoding + +### `fnabsx` β€” form `X` + +- **Opcode word:** `0xfc000110` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `136` +- **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 | +| --- | --- | --- | +| `FB` | fnabsx: read | Source B floating-point register. | +| `FD` | fnabsx: write | Destination floating-point register. | +| `CR` | fnabsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `fnabsx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnabsx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +FRT <- set_sign(FRB) +``` + +## 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 + +**`fnabsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnabsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:504`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L504) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:908`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L908) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2767-2771`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2767-L2771) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnabsx => { + ctx.fpr[instr.rd()] = -(ctx.fpr[instr.rb()].abs()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bit-pattern operation, no rounding.** `fnabs` **sets** the sign bit (bit 0) of the source binary64 value to 1, producing `-|FRB|`. No precision change, no exception bits. xenia-rs implements this as `-(b.abs())` β€” the abs clears the sign bit, then negation sets it. +- **NaN handling.** Returns the source NaN with the sign bit set to 1; payload preserved; signalling/quiet bit unchanged. `FPSCR[VXSNAN]` is **not** raised. +- **Special values.** `fnabs(Β±0) = -0`; `fnabs(±∞) = -∞`; `fnabs(Β±NaN) = -NaN` (sign set, payload preserved). +- **FPSCR.** Hardware does not update `FPRF` and does not raise any exception bit. Sign-bit ops are not arithmetic. +- **`Rc=1` (`fnabs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **No `FRA`.** X-form, primary 63, XO 136. Reads `FRB` only. +- **Use case.** Less common than `fabs`/`fneg`. Useful for unconditional negative-magnitude values, e.g. forcing a value to be on the negative side of zero before a subsequent compare or for bit-pattern setup. + +## Related Instructions + +- [`fabsx`](fabsx.md) β€” clear sign bit (positive). +- [`fnegx`](fnegx.md) β€” toggle sign bit. +- [`fmrx`](fmrx.md) β€” plain register copy. +- [`fselx`](fselx.md) β€” branch-free select; with `fabs`/`fnabs` synthesises `copysign`-like helpers. + +## IBM Reference + +- [AIX 7.3 β€” `fnabs` (Floating Negative Absolute Value)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fnabs-floating-negative-absolute-value-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fnegx.md b/tools/ppc-manual/fpu/fnegx.md new file mode 100644 index 00000000..4f772536 --- /dev/null +++ b/tools/ppc-manual/fpu/fnegx.md @@ -0,0 +1,121 @@ +# `fnegx` β€” Floating Negate + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000050` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fneg` | `fnegx` | β€” | Floating Negate | +| `fneg.` | `fnegx` | Rc=1 | Floating Negate | + +## Syntax + +```asm +fneg[Rc] [FD], [FB] +``` + +## Encoding + +### `fnegx` β€” form `X` + +- **Opcode word:** `0xfc000050` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `40` +- **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 | +| --- | --- | --- | +| `FB` | fnegx: read | Source B floating-point register. | +| `FD` | fnegx: write | Destination floating-point register. | +| `CR` | fnegx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `fnegx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnegx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`. + +## Operation (pseudocode) + +``` +FRT <- flip_sign(FRB) +``` + +## 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 + +**`fnegx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnegx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:515`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L515) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:903`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L903) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2762-2766`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2762-L2766) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnegx => { + ctx.fpr[instr.rd()] = -ctx.fpr[instr.rb()]; + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bit-pattern operation, no rounding.** `fneg` toggles the sign bit (bit 0) of the source binary64 value and writes the 64-bit pattern to the destination. No precision change, no exception bits. +- **NaN handling.** PowerISA specifies that `fneg` toggles the NaN sign bit (unlike `fnmadd` which does **not**). xenia-rs uses Rust's unary `-`, which toggles the sign bit on NaN values for binary64 β€” semantic match. +- **Special values.** `fneg(+0) = -0`; `fneg(-0) = +0`; `fneg(±∞) = βˆ“βˆž`. No `FPSCR[VXSNAN]` raised even on signalling NaN inputs (sign-bit ops are not arithmetic). +- **FPSCR.** Hardware does **not** update `FPRF` and does **not** raise any exception bit. The "FPSCR write" in the header refers only to `Rc=1` updating CR1 from existing FPSCR contents. +- **`Rc=1` (`fneg.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **No `FRA`.** X-form, primary 63, XO 40. Reads `FRB` only. +- **Use as a free negate.** Common in compiled PPC code for `-x` or as part of negate-and-fma sequences when no fused negative variant exists. + +## Related Instructions + +- [`fabsx`](fabsx.md) β€” clear sign bit (positive). +- [`fnabsx`](fnabsx.md) β€” set sign bit (negative). +- [`fmrx`](fmrx.md) β€” plain register copy. +- [`fnmaddx`](fnmaddx.md), [`fnmsubx`](fnmsubx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” fused negate-multiply-add forms; eliminate the need for an explicit `fneg` after an FMA. +- [`fselx`](fselx.md) β€” combined with `fneg` for branch-free `copysign`/clamp patterns. + +## IBM Reference + +- [AIX 7.3 β€” `fneg` (Floating Negate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fneg-floating-negate-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (PPC's `fneg` toggles NaN sign β€” distinct from the `fnmadd` family). diff --git a/tools/ppc-manual/fpu/fnmaddsx.md b/tools/ppc-manual/fpu/fnmaddsx.md new file mode 100644 index 00000000..0de9ac68 --- /dev/null +++ b/tools/ppc-manual/fpu/fnmaddsx.md @@ -0,0 +1,147 @@ +# `fnmaddsx` β€” Floating Negative Multiply-Add Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec00003e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fnmadds` | `fnmaddsx` | β€” | Floating Negative Multiply-Add Single | +| `fnmadds.` | `fnmaddsx` | Rc=1 | Floating Negative Multiply-Add Single | + +## Syntax + +```asm +fnmadds[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fnmaddsx` β€” form `A` + +- **Opcode word:** `0xec00003e` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `31` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fnmaddsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fnmaddsx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fnmaddsx: read | Source B floating-point register. | +| `FD` | fnmaddsx: write | Destination floating-point register. | +| `CR` | fnmaddsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fnmaddsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fnmaddsx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnmaddsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fnmaddsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnmaddsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:222`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L222) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:395`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L395) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2706-2720`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2706-L2720) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnmaddsx => { + // PPCBUG-181 + PPCBUG-183: VXISI + NaN sign preservation. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, false); + let fma = a.mul_add(c, b); + let neg = if fma.is_nan() { fma } else { -fma }; + let result = to_single(ctx, neg); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding then negate then round-to-single.** Computes `βˆ’((FRA Γ— FRC) + FRB)` and rounds to binary32. xenia-rs uses `to_single(-(a.mul_add(c, b)))` β€” the negation is a sign-flip on the binary64 intermediate, then `to_single` rounds to binary32. +- **NaN sign behaviour.** PowerISA specifies the negation does **not** flip the sign bit of a NaN result. xenia uses Rust's `Neg`, which does flip the NaN sign bit. Observable only via bit-level inspection. **xenia quirk.** +- **Operand order.** Assembler: `FD, FA, FC, FB`. +- **Invalid operations.** `0Γ—βˆž` β†’ `VXIMZ`; opposing-infinity collision β†’ `VXISI`. Quiet NaN result with `FPSCR[VX, FX]`. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fnmadds.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** returns ±∞ and sets `OX`/`XX`/`FX`. +- **Use case.** Single-precision Newton-Raphson refinement and graphics-pipeline math where the negated product-sum form is convenient. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fnmaddx`](fnmaddx.md) β€” double-precision sibling. +- [`fmaddsx`](fmaddsx.md), [`fmsubsx`](fmsubsx.md), [`fnmsubsx`](fnmsubsx.md) β€” other single-precision fused-multiply variants. +- [`fmulsx`](fmulsx.md), [`faddsx`](faddsx.md) β€” non-fused decomposition. +- [`fnegx`](fnegx.md), [`fnabsx`](fnabsx.md) β€” sign-bit ops. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fnmadds` (Floating Negative Multiply-Add Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fnmadds-floating-negative-multiply-add-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fnmaddx.md b/tools/ppc-manual/fpu/fnmaddx.md new file mode 100644 index 00000000..9fc37a1a --- /dev/null +++ b/tools/ppc-manual/fpu/fnmaddx.md @@ -0,0 +1,138 @@ +# `fnmaddx` β€” Floating Negative Multiply-Add + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00003e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fnmadd` | `fnmaddx` | β€” | Floating Negative Multiply-Add | +| `fnmadd.` | `fnmaddx` | Rc=1 | Floating Negative Multiply-Add | + +## Syntax + +```asm +fnmadd[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fnmaddx` β€” form `A` + +- **Opcode word:** `0xfc00003e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `31` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fnmaddx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fnmaddx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fnmaddx: read | Source B floating-point register. | +| `FD` | fnmaddx: write | Destination floating-point register. | +| `CR` | fnmaddx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fnmaddx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fnmaddx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnmaddx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- βˆ’((FRA Γ— FRC) + FRB) +``` + +## 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 + +**`fnmaddx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnmaddx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:213`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L213) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:930`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L930) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2692-2705`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2692-L2705) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnmaddx => { + // PPCBUG-203: missing VXISI. PPCBUG-205: NaN sign preserved (no negation on NaN). + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, false); + let fma = a.mul_add(c, b); + let result = if fma.is_nan() { fma } else { -fma }; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step, then sign flip.** Computes `βˆ’((FRA Γ— FRC) + FRB)` with one fused rounding for the FMA; the final negation is a bit-pattern sign-flip and does not introduce additional rounding error. xenia-rs implements this as `-(a.mul_add(c, b))`. +- **Sign of NaN.** Per PowerISA, `fnmadd` does **not** flip the sign of a NaN result. xenia uses Rust's `Neg` which does flip the NaN sign bit (`f64::neg`); for IEEE-754 binary64 this is observable through bit-level inspection but not through arithmetic comparisons. **xenia quirk** β€” title code that inspects NaN sign bits will diverge. +- **Operand order.** Assembler: `FD, FA, FC, FB`. +- **Invalid operations.** Same as `fmadd`: `VXIMZ` for `0Γ—βˆž`, `VXISI` for opposing-infinity collision. Quiet NaN result. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fnmadd.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Use case.** Computing `-a*c - b` directly without an intermediate negate. Useful in iterative solvers and in transforming polynomial coefficients. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fnmaddsx`](fnmaddsx.md) β€” single-precision sibling. +- [`fmaddx`](fmaddx.md), [`fmsubx`](fmsubx.md), [`fnmsubx`](fnmsubx.md) β€” other fused multiply-add variants: + - `fmadd` = `(AΓ—C) + B` + - `fmsub` = `(AΓ—C) βˆ’ B` + - `fnmsub` = `βˆ’((AΓ—C) βˆ’ B)` +- [`fnegx`](fnegx.md), [`fnabsx`](fnabsx.md) β€” sign-bit operations on FPRs. +- [`fmulx`](fmulx.md), [`faddx`](faddx.md) β€” non-fused decomposition. + +## IBM Reference + +- [AIX 7.3 β€” `fnmadd` (Floating Negative Multiply-Add)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fnma-fnmadd-floating-negative-multiply-add-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (note: PowerISA specifies the negation does not flip NaN sign bits). diff --git a/tools/ppc-manual/fpu/fnmsubsx.md b/tools/ppc-manual/fpu/fnmsubsx.md new file mode 100644 index 00000000..6556f775 --- /dev/null +++ b/tools/ppc-manual/fpu/fnmsubsx.md @@ -0,0 +1,147 @@ +# `fnmsubsx` β€” Floating Negative Multiply-Subtract Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec00003c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fnmsubs` | `fnmsubsx` | β€” | Floating Negative Multiply-Subtract Single | +| `fnmsubs.` | `fnmsubsx` | Rc=1 | Floating Negative Multiply-Subtract Single | + +## Syntax + +```asm +fnmsubs[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fnmsubsx` β€” form `A` + +- **Opcode word:** `0xec00003c` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `30` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fnmsubsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fnmsubsx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fnmsubsx: read | Source B floating-point register. | +| `FD` | fnmsubsx: write | Destination floating-point register. | +| `CR` | fnmsubsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fnmsubsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fnmsubsx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnmsubsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fnmsubsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnmsubsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:241`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L241) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:394`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L394) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2735-2749`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2735-L2749) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnmsubsx => { + // PPCBUG-182 + PPCBUG-183: VXISI + NaN sign preservation. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, true); + let fma = a.mul_add(c, -b); + let neg = if fma.is_nan() { fma } else { -fma }; + let result = to_single(ctx, neg); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding then negate then round-to-single.** Computes `βˆ’((FRA Γ— FRC) βˆ’ FRB)` = `FRB βˆ’ (FRA Γ— FRC)` with one fused rounding at double precision, then rounds to binary32. xenia-rs uses `to_single(-(a.mul_add(c, -b)))`. +- **NaN sign behaviour.** PowerISA: the negation does **not** flip a NaN's sign bit. xenia uses Rust's `Neg` which does. Observable only by bit-level inspection. **xenia quirk.** +- **Operand order.** Assembler: `FD, FA, FC, FB`. +- **Invalid operations.** `0Γ—βˆž` β†’ `VXIMZ`; same-signed-infinity collision β†’ `VXISI`. Quiet NaN result. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fnmsubs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow** returns ±∞ and sets `OX`/`XX`/`FX`. +- **Use case.** Single-precision Newton-Raphson divide refinement: `x_new = x*(2 - d*x)` is implemented as a `fnmsubs`/`fmuls` pair throughout Xbox 360 graphics code. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fnmsubx`](fnmsubx.md) β€” double-precision sibling. +- [`fmaddsx`](fmaddsx.md), [`fmsubsx`](fmsubsx.md), [`fnmaddsx`](fnmaddsx.md) β€” other single-precision fused-multiply variants. +- [`fresx`](fresx.md), [`frsqrtex`](frsqrtex.md) β€” reciprocal estimates whose Newton-Raphson refinement leans on `fnmsubs`. +- [`fmulsx`](fmulsx.md), [`fsubsx`](fsubsx.md) β€” non-fused decomposition. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fnmsubs` (Floating Negative Multiply-Subtract Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fnmsubs-floating-negative-multiply-subtract-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fnmsubx.md b/tools/ppc-manual/fpu/fnmsubx.md new file mode 100644 index 00000000..3f06634b --- /dev/null +++ b/tools/ppc-manual/fpu/fnmsubx.md @@ -0,0 +1,136 @@ +# `fnmsubx` β€” Floating Negative Multiply-Subtract + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00003c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fnmsub` | `fnmsubx` | β€” | Floating Negative Multiply-Subtract | +| `fnmsub.` | `fnmsubx` | Rc=1 | Floating Negative Multiply-Subtract | + +## Syntax + +```asm +fnmsub[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fnmsubx` β€” form `A` + +- **Opcode word:** `0xfc00003c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `30` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fnmsubx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fnmsubx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fnmsubx: read | Source B floating-point register. | +| `FD` | fnmsubx: write | Destination floating-point register. | +| `CR` | fnmsubx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fnmsubx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fnmsubx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fnmsubx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- βˆ’((FRA Γ— FRC) βˆ’ FRB) +``` + +## 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 + +**`fnmsubx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fnmsubx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:232`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L232) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:929`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L929) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2721-2734`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2721-L2734) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fnmsubx => { + // PPCBUG-203: VXISI. PPCBUG-205: NaN sign preservation. + let a = ctx.fpr[instr.ra()]; + let c = ctx.fpr[instr.rc()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_mul(ctx, a, c); + fpscr::check_invalid_fma_add(ctx, a, c, b, true); + let fma = a.mul_add(c, -b); + let result = if fma.is_nan() { fma } else { -fma }; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite() && c.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single rounding step, then sign flip.** Computes `βˆ’((FRA Γ— FRC) βˆ’ FRB)` = `FRB βˆ’ (FRA Γ— FRC)`, with one fused rounding. xenia-rs implements this as `-(a.mul_add(c, -b))`, which is mathematically equivalent. +- **NaN sign behaviour.** PowerISA: the negation does **not** flip the sign of a NaN result. xenia uses Rust's `Neg` which does flip the sign bit on NaNs. Observable only via bit-level inspection. **xenia quirk.** +- **Operand order.** Assembler: `FD, FA, FC, FB`. +- **Invalid operations.** `0Γ—βˆž` β†’ `VXIMZ`; same-signed-infinity collision (e.g. `(+∞) βˆ’ (+∞)`) β†’ `VXISI`. Quiet NaN result. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX`, `OX`, `UX`, `XX`, `VXIMZ`, `VXISI`, `VXSNAN`. xenia-rs does not (xenia quirk). +- **`Rc=1` (`fnmsub.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Use case.** The canonical Newton-Raphson divide refinement step: `x_new = x*(2 - d*x)`. This is the most common operand pattern in compiled PPC graphics code that does software reciprocals. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; xenia uses host IEEE behavior. + +## Related Instructions + +- [`fnmsubsx`](fnmsubsx.md) β€” single-precision sibling. +- [`fmaddx`](fmaddx.md), [`fmsubx`](fmsubx.md), [`fnmaddx`](fnmaddx.md) β€” other fused multiply-add variants. +- [`fresx`](fresx.md) β€” reciprocal estimate; `fnmsub` is the workhorse of NR refinement of `fres` outputs. +- [`frsqrtex`](frsqrtex.md) β€” reciprocal-sqrt estimate; also refined with `fnmsub`-style chains. +- [`fmulx`](fmulx.md), [`fsubx`](fsubx.md) β€” non-fused decomposition. + +## IBM Reference + +- [AIX 7.3 β€” `fnmsub` (Floating Negative Multiply-Subtract)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fnms-fnmsub-floating-negative-multiply-subtract-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fresx.md b/tools/ppc-manual/fpu/fresx.md new file mode 100644 index 00000000..f6f03e8c --- /dev/null +++ b/tools/ppc-manual/fpu/fresx.md @@ -0,0 +1,151 @@ +# `fresx` β€” Floating Reciprocal Estimate Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec000030` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fres` | `fresx` | β€” | Floating Reciprocal Estimate Single | +| `fres.` | `fresx` | Rc=1 | Floating Reciprocal Estimate Single | + +## Syntax + +```asm +fres[Rc] [FD], [FB] +``` + +## Encoding + +### `fresx` β€” form `A` + +- **Opcode word:** `0xec000030` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `24` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FB` | fresx: read | Source B floating-point register. | +| `FD` | fresx: write | Destination floating-point register. | +| `CR` | fresx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fresx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fresx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fresx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fresx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fresx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:106`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L106) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:390`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L390) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2815-2835`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2815-L2835) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fresx => { + // Single-precision reciprocal estimate: frD = 1.0 / frB. + // PPCBUG-184: pre-quantize input to f32 to match canary's + // `f.Recip(f.Convert(frB, FLOAT32_TYPE))` behavior. Hardware + // produces a ~12-bit LUT estimate; both emulators produce a + // fully-IEEE single reciprocal, but the f32 quantization at + // least makes the input precision match. + let b_full = ctx.fpr[instr.rb()]; + let b = b_full as f32 as f64; + if b == 0.0 { + fpscr::set_exception(ctx, fpscr::ZX); + } + if fpscr::is_snan(b_full) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + let result = to_single(ctx, 1.0 / b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, b.is_finite() && b != 0.0); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single-precision reciprocal estimate.** PowerISA specifies a *low-precision* approximation of `1/FRB` accurate to roughly 12–14 bits of significand, intended as the seed for a Newton-Raphson refinement step. **xenia quirk:** xenia-rs computes the *full-precision* `1.0 / b` then rounds to single, so it produces a far more accurate result than hardware. Title code that depends on the limited precision of `fres` to trigger refinement loops will still work (the loops just refine an already-correct value), but bit-exact correlation with hardware is impossible. +- **Single precision result.** Final value is rounded to binary32 then re-encoded into the FPR. +- **Divide by zero.** `1/Β±0` β†’ ±∞ and sets `FPSCR[ZX, FX]`. xenia returns the host ±∞ but does not update FPSCR. +- **`fres(±∞) = Β±0`** (correctly signed). +- **`fres(NaN) = NaN`**; signalling NaNs are quietened. +- **Overflow / underflow.** May set `OX`/`UX`/`XX`/`FX`. xenia does not update FPSCR. +- **`Rc=1` (`fres.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** A-form, primary 59, XO 24. Reads `FRB` only; `FRA`/`FRC` are don't-care. +- **Use case.** Software reciprocal: `1/d β‰ˆ x = fres(d); x = x*(2 - d*x);` (one Newton-Raphson step recovers full single precision). Two iterations recover full double precision. The `(2 - d*x)` step compiles to `fnmsub`. +- **Performance.** Cheap on Xenon (single-cycle issue) β€” divides by `fres` + 1–2 NR steps + `fmul` are far faster than `fdiv`/`fdivs`. + +## Related Instructions + +- [`frsqrtex`](frsqrtex.md) β€” reciprocal-square-root estimate; same NR refinement approach. +- [`fdivx`](fdivx.md), [`fdivsx`](fdivsx.md) β€” true divide; alternative when refinement isn't needed. +- [`fnmsubx`](fnmsubx.md), [`fnmsubsx`](fnmsubsx.md) β€” the workhorse for the `(2 - d*x)` step. +- [`fmulx`](fmulx.md), [`fmulsx`](fmulsx.md) β€” final multiply to apply the reciprocal. +- [`fmaddsx`](fmaddsx.md) β€” alternate refinement formulation. + +## IBM Reference + +- [AIX 7.3 β€” `fres` (Floating Reciprocal Estimate Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fres-floating-reciprocal-estimate-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (relative-error bound for `fres`; intended Newton-Raphson refinement pattern). diff --git a/tools/ppc-manual/fpu/frspx.md b/tools/ppc-manual/fpu/frspx.md new file mode 100644 index 00000000..83e126b8 --- /dev/null +++ b/tools/ppc-manual/fpu/frspx.md @@ -0,0 +1,144 @@ +# `frspx` β€” Floating Round to Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0xfc000018` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `frsp` | `frspx` | β€” | Floating Round to Single | +| `frsp.` | `frspx` | Rc=1 | Floating Round to Single | + +## Syntax + +```asm +frsp[Rc] [FD], [FB] +``` + +## Encoding + +### `frspx` β€” form `X` + +- **Opcode word:** `0xfc000018` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `12` +- **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 | +| --- | --- | --- | +| `FB` | frspx: read | Source B floating-point register. | +| `FD` | frspx: write | Destination floating-point register. | +| `CR` | frspx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | frspx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `frspx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `frspx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`frspx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="frspx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:318`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L318) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:898`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L898) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2856-2871`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2856-L2871) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::frspx => { + // Round to single precision honouring FPSCR[RN]. + // PPCBUG-225: set XX on inexact rounding (almost every frsp call). + let b = ctx.fpr[instr.rb()]; + if fpscr::is_snan(b) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + let result = to_single(ctx, b); + if b.is_finite() && result.is_finite() && result != b { + fpscr::set_exception(ctx, fpscr::XX); + } + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Round to single-precision.** Rounds the binary64 value in `FRB` to binary32 using `FPSCR[RN]`, then re-encodes the result back into the destination as a binary64 representation of that single value. xenia-rs uses `to_single(b)`, which performs `f64 β†’ f32 β†’ f64` round-trip (Rust's `as f32` uses round-to-nearest-even, matching the PPC default). +- **`FPSCR[RN]` not honored in xenia.** Like other conversion ops, xenia's `to_single` is hard-coded to round-to-nearest-even regardless of `FPSCR[RN]`. **xenia quirk** for non-default rounding modes. +- **Overflow.** Values whose magnitude exceeds binary32's max (~3.4e38) round to ±∞ and set `FPSCR[OX, XX, FX]`. +- **Underflow.** Values whose magnitude is below binary32's smallest normal (~1.2e-38) flush to zero or denormal per `FPSCR[NI]`; `UX`/`XX`/`FX` set on hardware. xenia uses host IEEE. +- **NaN propagation.** Quiet NaNs pass through; signalling NaNs are quietened (sign-bit cleared on the SNaN-quietening payload bit). Host `as f32` does not perform PPC-style quietening; **xenia quirk** for SNaN bit-level inspection. +- **Inexact.** Most rounding produces inexact; sets `FPSCR[XX, FX]`. xenia does not update FPSCR (xenia quirk). +- **`Rc=1` (`frsp.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** X-form, primary 63, XO 12. Reads `FRB` only. +- **Use case.** Compilers emit `frsp` after a chain of `fadd`/`fmul`/etc. when storing the value with `stfs` (store single). Without an explicit `frsp`, the in-FPR double would not match the `stfs`-rounded single. + +## Related Instructions + +- [`faddsx`](faddsx.md), [`fsubsx`](fsubsx.md), [`fmulsx`](fmulsx.md), [`fdivsx`](fdivsx.md) β€” single-precision arithmetic; equivalent to `frsp(double_op(...))`. +- [`fmaddsx`](fmaddsx.md), [`fmsubsx`](fmsubsx.md), [`fnmaddsx`](fnmaddsx.md), [`fnmsubsx`](fnmsubsx.md) β€” single-precision fused FMA family. +- `stfs` β€” store single; expects an FPR already rounded to single via `frsp` or via single-precision arithmetic. +- [`fcfidx`](fcfidx.md) β€” `fcfid` + `frsp` is the standard `i64 β†’ float` conversion. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” FPSCR rounding-mode control. + +## IBM Reference + +- [AIX 7.3 β€” `frsp` (Floating Round to Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-frsp-floating-round-single-precision-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (single-precision rounding rules; SNaN quietening). diff --git a/tools/ppc-manual/fpu/frsqrtex.md b/tools/ppc-manual/fpu/frsqrtex.md new file mode 100644 index 00000000..cdf99253 --- /dev/null +++ b/tools/ppc-manual/fpu/frsqrtex.md @@ -0,0 +1,147 @@ +# `frsqrtex` β€” Floating Reciprocal Square Root Estimate + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc000034` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `frsqrte` | `frsqrtex` | β€” | Floating Reciprocal Square Root Estimate | +| `frsqrte.` | `frsqrtex` | Rc=1 | Floating Reciprocal Square Root Estimate | + +## Syntax + +```asm +frsqrte[Rc] [FD], [FB] +``` + +## Encoding + +### `frsqrtex` β€” form `A` + +- **Opcode word:** `0xfc000034` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `26` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FB` | frsqrtex: read | Source B floating-point register. | +| `FD` | frsqrtex: write | Destination floating-point register. | +| `CR` | frsqrtex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | frsqrtex: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `frsqrtex` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `frsqrtex`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`frsqrtex`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="frsqrtex"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:118`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L118) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:29`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L29) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:926`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L926) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2836-2853`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2836-L2853) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::frsqrtex => { + // Reciprocal square root estimate: frD = 1.0 / sqrt(frB) + let b = ctx.fpr[instr.rb()]; + if b == 0.0 { + fpscr::set_exception(ctx, fpscr::ZX); + } + if b.is_sign_negative() && b != 0.0 && !b.is_nan() { + fpscr::set_exception(ctx, fpscr::VXSQRT); + } + if fpscr::is_snan(b) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + let result = 1.0 / b.sqrt(); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, b.is_finite() && b > 0.0); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Reciprocal-square-root estimate.** PowerISA: low-precision approximation of `1/sqrt(FRB)` accurate to roughly 12–14 bits, designed as the seed for Newton-Raphson refinement. **xenia quirk:** xenia-rs computes the *full-precision* `1.0 / b.sqrt()` (no rounding to single β€” `frsqrte` is double-precision per the spec). The result is far more accurate than hardware. Title code that depends on the limited precision still functions; the NR refinement converges in one iteration on either platform. +- **Double precision result.** Per PowerISA, `frsqrte` returns a binary64 estimate (not a single-rounded value, unlike `fres`). +- **Negative input is invalid.** `frsqrte(x < 0)` (other than `-0`) sets `FPSCR[VXSQRT, VX, FX]` and yields a quiet NaN. xenia returns host NaN (Rust's `f64::sqrt` of a negative is NaN, then `1/NaN` is NaN) but does not raise the FPSCR bit. +- **`frsqrte(+0) = +∞`** and sets `FPSCR[ZX]` per spec. **`frsqrte(-0) = -∞`**. +- **`frsqrte(+∞) = +0`**. +- **NaN propagation.** Quiet NaN; signalling NaNs are quietened. +- **`Rc=1` (`frsqrte.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **Encoding.** A-form, primary 63, XO 26. Reads `FRB` only; `FRA`/`FRC` are don't-care. +- **Use case.** The canonical `length`/`normalize` recipe: `inv_len = frsqrte(dot); inv_len = 0.5 * inv_len * (3 - dot * inv_len * inv_len);` β€” one NR step gets to full double precision. For single precision use `frsp` after. +- **Performance.** Cheap on Xenon. The `length`/`normalize` macro built on `frsqrte` is the hot inner loop in any 3D Xbox 360 game. + +## Related Instructions + +- [`fresx`](fresx.md) β€” reciprocal estimate; same NR-refinement design pattern. +- [`fsqrtx`](fsqrtx.md), [`fsqrtsx`](fsqrtsx.md) β€” full-precision square root (multi-cycle, non-pipelined). +- [`fmulx`](fmulx.md), [`fmaddx`](fmaddx.md), [`fnmsubx`](fnmsubx.md) β€” the multiply/FMA ops that drive NR refinement. +- [`frspx`](frspx.md) β€” round to single after `frsqrte` for graphics-pipeline producers expecting `float`. + +## IBM Reference + +- [AIX 7.3 β€” `frsqrte` (Floating Reciprocal Square Root Estimate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-frsqrte-floating-reciprocal-square-root-estimate-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (relative-error bound for `frsqrte`; canonical NR refinement step). diff --git a/tools/ppc-manual/fpu/fselx.md b/tools/ppc-manual/fpu/fselx.md new file mode 100644 index 00000000..5c031756 --- /dev/null +++ b/tools/ppc-manual/fpu/fselx.md @@ -0,0 +1,143 @@ +# `fselx` β€” Floating Select + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00002e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fsel` | `fselx` | β€” | Floating Select | +| `fsel.` | `fselx` | Rc=1 | Floating Select | + +## Syntax + +```asm +fsel[Rc] [FD], [FA], [FC], [FB] +``` + +## Encoding + +### `fselx` β€” form `A` + +- **Opcode word:** `0xfc00002e` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `23` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fselx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FC` | fselx: read | Source C floating-point register (for madd-style ops). | +| `FB` | fselx: read | Source B floating-point register. | +| `FD` | fselx: write | Destination floating-point register. | +| `CR` | fselx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `fselx` + +- **Reads (always):** `FA`, `FC`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fselx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`. + +## 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 + +**`fselx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fselx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:144`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L144) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:30`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L30) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:924`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L924) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2774-2783`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2774-L2783) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fselx => { + // frD = if frA >= 0.0 then frC else frB + ctx.fpr[instr.rd()] = if ctx.fpr[instr.ra()] >= 0.0 { + ctx.fpr[instr.rc()] + } else { + ctx.fpr[instr.rb()] + }; + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Non-IEEE branch-free select.** PowerPC-specific; not in the IEEE-754 spec. Semantics: `FRT = (FRA >= 0.0) ? FRC : FRB`. Used pervasively in compiled PPC for `min`/`max`/`clamp`/`copysign` without branches. xenia-rs uses Rust's `>=` which matches. +- **`-0.0` selects `FRC`.** Per PowerISA, `-0` compares as `>= 0`, so it routes to `FRC` (the "true" branch). xenia's `-0.0 >= 0.0` evaluates true in Rust β€” semantic match. +- **NaN selects `FRB`.** Per PowerISA, NaN does **not** satisfy `>= 0`, so the result is `FRB`. xenia: any comparison with NaN returns false in Rust, so `>= 0` is false β†’ `FRB` selected. Match. +- **No FPSCR side effects.** `fsel` does **not** raise `VXSNAN` even on signalling NaN inputs, and does **not** update `FPRF`. It is purely a data-movement op. +- **`Rc=1` (`fsel.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **A-form encoding.** Reads `FRA, FRB, FRC`, writes `FRT`. Assembler order: `fsel FD, FA, FC, FB` (note: `FRC` before `FRB`). +- **Common idioms.** + - `min(a,b) = fsel(a-b, b, a)` + - `max(a,b) = fsel(a-b, a, b)` + - `clamp(x, lo, hi) = fsel(x-lo, fsel(hi-x, x, hi), lo)` + - `copysign(x, y) = fsel(y, |x|, -|x|)` (using `fabs`/`fnabs`) +- **Optional ISA.** `fsel` is an optional PowerISA instruction; some implementations trap. Xenon implements it natively. +- **No precision change.** Bit-pattern selection β€” no rounding regardless of source precision. + +## Related Instructions + +- [`fabsx`](fabsx.md), [`fnegx`](fnegx.md), [`fnabsx`](fnabsx.md) β€” sign-bit ops; common companions for `fsel`-based copysign/clamp idioms. +- [`fsubx`](fsubx.md) β€” subtract is the standard way to produce the comparison key (`a - b`). +- [`fcmpux`](fcmpu.md), [`fcmpox`](fcmpo.md) β€” IEEE compare with branch; the heavyweight alternative to `fsel`. +- [`fmrx`](fmrx.md) β€” unconditional copy. + +## IBM Reference + +- [AIX 7.3 β€” `fsel` (Floating Select)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fsel-floating-select-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (note: `fsel` is non-IEEE and uses the `>= 0` convention, not `> 0`). diff --git a/tools/ppc-manual/fpu/fsqrtsx.md b/tools/ppc-manual/fpu/fsqrtsx.md new file mode 100644 index 00000000..fca8344a --- /dev/null +++ b/tools/ppc-manual/fpu/fsqrtsx.md @@ -0,0 +1,143 @@ +# `fsqrtsx` β€” Floating Square Root Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec00002c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fsqrts` | `fsqrtsx` | β€” | Floating Square Root Single | +| `fsqrts.` | `fsqrtsx` | Rc=1 | Floating Square Root Single | + +## Syntax + +```asm +fsqrts[Rc] [FD], [FB] +``` + +## Encoding + +### `fsqrtsx` β€” form `A` + +- **Opcode word:** `0xec00002c` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `22` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FB` | fsqrtsx: read | Source B floating-point register. | +| `FD` | fsqrtsx: write | Destination floating-point register. | +| `CR` | fsqrtsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fsqrtsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fsqrtsx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fsqrtsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fsqrtsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fsqrtsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:168`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L168) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:30`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L30) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:389`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L389) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2801-2814`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2801-L2814) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fsqrtsx => { + let b = ctx.fpr[instr.rb()]; + if b.is_sign_negative() && b != 0.0 && !b.is_nan() { + fpscr::set_exception(ctx, fpscr::VXSQRT); + } + if fpscr::is_snan(b) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + let result = to_single(ctx, b.sqrt()); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single precision.** Result is rounded to IEEE-754 binary32 then re-encoded into the destination 64-bit FPR. xenia computes `to_single(b.sqrt())`. +- **Negative inputs are invalid.** `sqrt(x < 0)` (other than `-0`) sets `FPSCR[VXSQRT, VX, FX]` and yields a quiet NaN. `sqrt(-0) = -0` per IEEE-754. +- **`sqrt(+∞) = +∞`**, exact. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` plus exception bits `XX` (very common β€” sqrt is rarely exact in single precision), `VXSQRT`, `VXSNAN`. xenia-rs does not update FPSCR (xenia quirk). +- **`Rc=1` (`fsqrts.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Performance.** `fsqrts` is a multi-cycle, non-pipelined operation on Xenon. Hot-path code commonly uses `frsqrte` + Newton-Raphson + `fmul`. +- **Encoding.** A-form, primary 59, XO 22; reads `FRB` only. +- **Rounding mode** uses `FPSCR[RN]` (default nearest-even). + +## Related Instructions + +- [`fsqrtx`](fsqrtx.md) β€” double-precision square root. +- [`frsqrtex`](frsqrtex.md) β€” reciprocal-square-root estimate; combined with `fmuls` to compute `1/sqrt(x)` cheaply. +- [`fresx`](fresx.md) β€” reciprocal estimate; pairs with `fsqrts` to compute `1/sqrt(x)`. +- [`fmulsx`](fmulsx.md), [`fmaddsx`](fmaddsx.md) β€” used in Newton-Raphson refinement of `frsqrte` outputs. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding. + +## IBM Reference + +- [AIX 7.3 β€” `fsqrts` (Floating Square Root Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fsqrts-floating-square-root-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fsqrtx.md b/tools/ppc-manual/fpu/fsqrtx.md new file mode 100644 index 00000000..91a3b185 --- /dev/null +++ b/tools/ppc-manual/fpu/fsqrtx.md @@ -0,0 +1,144 @@ +# `fsqrtx` β€” Floating Square Root + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc00002c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fsqrt` | `fsqrtx` | β€” | Floating Square Root | +| `fsqrt.` | `fsqrtx` | Rc=1 | Floating Square Root | + +## Syntax + +```asm +fsqrt[Rc] [FD], [FB] +``` + +## Encoding + +### `fsqrtx` β€” form `A` + +- **Opcode word:** `0xfc00002c` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `22` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FB` | fsqrtx: read | Source B floating-point register. | +| `FD` | fsqrtx: write | Destination floating-point register. | +| `CR` | fsqrtx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fsqrtx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fsqrtx` + +- **Reads (always):** `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fsqrtx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fsqrtx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fsqrtx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:164`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L164) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:30`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L30) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:923`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L923) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2786-2800`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2786-L2800) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fsqrtx => { + let b = ctx.fpr[instr.rb()]; + // sqrt of negative (non-zero) is invalid operation β†’ VXSQRT. + if b.is_sign_negative() && b != 0.0 && !b.is_nan() { + fpscr::set_exception(ctx, fpscr::VXSQRT); + } + if fpscr::is_snan(b) { + fpscr::set_exception(ctx, fpscr::VXSNAN); + } + let result = b.sqrt(); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Double precision.** Operates on IEEE-754 binary64; [`fsqrtsx`](fsqrtsx.md) is the single-precision sibling. xenia delegates to host `f64::sqrt`. +- **Negative inputs are invalid.** `sqrt(x < 0)` (other than `-0`) sets `FPSCR[VXSQRT, VX, FX]` and yields a quiet NaN. Note: `sqrt(-0) = -0` per IEEE-754 (preserves sign of zero) β€” host `f64::sqrt` matches. +- **`sqrt(+∞) = +∞`**, exact. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` plus exception bits `XX` (inexact, very common since `sqrt` is rarely exact), `VXSQRT`, `VXSNAN`. xenia-rs does **not** update FPSCR (xenia quirk). +- **`Rc=1` (`fsqrt.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Performance / availability.** `fsqrt` is a Power-ISA optional instruction; some implementations trap as illegal-opcode. Xenon implements it natively. xenia-rs supports it directly. +- **Encoding.** A-form, primary 63, XO 22; reads `FRB` only β€” `FRA` and `FRC` are don't-care. +- **Rounding mode** uses `FPSCR[RN]`. + +## Related Instructions + +- [`fsqrtsx`](fsqrtsx.md) β€” single-precision square root. +- [`frsqrtex`](frsqrtex.md) β€” reciprocal-square-root estimate (`~1/sqrt(x)`); preferred for normalize/length operations. +- [`fresx`](fresx.md) β€” reciprocal estimate; pairs with `fsqrt` for `1/sqrt(x)`. +- [`fmulx`](fmulx.md), [`fmaddx`](fmaddx.md) β€” used in Newton-Raphson refinement of `frsqrte` outputs. +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md) β€” FPSCR control. + +## IBM Reference + +- [AIX 7.3 β€” `fsqrt` (Floating Square Root)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fsqrt-floating-square-root-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/) (square-root invalid-operation rules). diff --git a/tools/ppc-manual/fpu/fsubsx.md b/tools/ppc-manual/fpu/fsubsx.md new file mode 100644 index 00000000..a06b0b0e --- /dev/null +++ b/tools/ppc-manual/fpu/fsubsx.md @@ -0,0 +1,140 @@ +# `fsubsx` β€” Floating Subtract Single + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xec000028` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fsubs` | `fsubsx` | β€” | Floating Subtract Single | +| `fsubs.` | `fsubsx` | Rc=1 | Floating Subtract Single | + +## Syntax + +```asm +fsubs[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `fsubsx` β€” form `A` + +- **Opcode word:** `0xec000028` +- **Primary opcode (bits 0–5):** `59` +- **Extended opcode:** `20` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fsubsx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fsubsx: read | Source B floating-point register. | +| `FD` | fsubsx: write | Destination floating-point register. | +| `CR` | fsubsx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fsubsx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fsubsx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fsubsx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## 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 + +**`fsubsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fsubsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:135`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L135) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:30`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L30) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:387`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L387) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2585-2594`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2585-L2594) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fsubsx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_add(ctx, a, b, true); + let result = to_single(ctx, a - b); + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Single precision.** Result is rounded to IEEE-754 binary32 then re-encoded into the destination 64-bit FPR using the binary64 representation. xenia-rs uses `to_single(a - b)` which performs the round trip via `f64 -> f32 -> f64`. +- **`±∞ βˆ’ ±∞`** sets `FPSCR[VXISI, VX, FX]` and yields a quiet NaN. +- **FPSCR side effects.** Always updated on hardware: `FPRF`, `FR`, `FI`, `FX`, plus exception bits `OX`, `UX`, `XX`, `VXISI`, `VXSNAN`. xenia-rs does **not** maintain FPSCR in the interpreter snapshot (xenia quirk). +- **`Rc=1` (`fsubs.`)** copies `FPSCR[FX, FEX, VX, OX]` into CR1. +- **NaN propagation.** Quiet-NaN result for any NaN operand; signalling NaNs are quietened. +- **Single-precision overflow.** A double-precision result that would round to a binary32 overflow returns ±∞ and sets `OX`/`XX`/`FX`. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1`; hardware flushes single-precision denormals to zero. xenia inherits host IEEE semantics. +- **Rounding mode** is taken from `FPSCR[RN]`; default is nearest-even. +- **Encoding.** A-form, primary 59, XO 20. `FRC` is don't-care. + +## Related Instructions + +- [`fsubx`](fsubx.md) β€” double-precision sibling. +- [`faddsx`](faddsx.md), [`fmulsx`](fmulsx.md), [`fdivsx`](fdivsx.md) β€” companion single-precision ops. +- [`fmsubsx`](fmsubsx.md), [`fnmsubsx`](fnmsubsx.md) β€” fused single-precision multiply-subtract. +- [`fnegx`](fnegx.md) β€” sign flip used to express subtract as add-of-negation. +- [`frspx`](frspx.md) β€” explicit doubleβ†’single rounding (semantically equivalent to chaining `frsp(fsub(...))`). + +## IBM Reference + +- [AIX 7.3 β€” `fsubs` (Floating Subtract Single)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fsubs-floating-subtract-single-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/fpu/fsubx.md b/tools/ppc-manual/fpu/fsubx.md new file mode 100644 index 00000000..cf34199e --- /dev/null +++ b/tools/ppc-manual/fpu/fsubx.md @@ -0,0 +1,130 @@ +# `fsubx` β€” Floating Subtract + +> **Category:** [Floating-Point](../categories/fpu.md) Β· **Form:** [A](../forms/A.md) Β· **Opcode:** `0xfc000028` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `fsub` | `fsubx` | β€” | Floating Subtract | +| `fsub.` | `fsubx` | Rc=1 | Floating Subtract | + +## Syntax + +```asm +fsub[Rc] [FD], [FA], [FB] +``` + +## Encoding + +### `fsubx` β€” form `A` + +- **Opcode word:** `0xfc000028` +- **Primary opcode (bits 0–5):** `63` +- **Extended opcode:** `20` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (59 or 63) | +| 6–10 | `FRT` | destination FPR | +| 11–15 | `FRA` | source A FPR | +| 16–20 | `FRB` | source B FPR | +| 21–25 | `FRC` | source C FPR (multiplier for madd-style ops) | +| 26–30 | `XO` | extended opcode (5 bits) | +| 31 | `Rc` | record-form flag (updates CR1) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `FA` | fsubx: read | Source A floating-point register (`fr0`–`fr31`). | +| `FB` | fsubx: read | Source B floating-point register. | +| `FD` | fsubx: write | Destination floating-point register. | +| `CR` | fsubx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | +| `FPSCR` | fsubx: write | Floating-Point Status and Control Register. | + +## Register Effects + +### `fsubx` + +- **Reads (always):** `FA`, `FB` +- **Reads (conditional):** _none_ +- **Writes (always):** `FD`, `FPSCR` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `fsubx`: **CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.; **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions). + +## Operation (pseudocode) + +``` +FRT <- FRA βˆ’ FRB +``` + +## 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 + +**`fsubx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="fsubx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:127`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L127) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:30`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L30) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:921`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L921) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2575-2584`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2575-L2584) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::fsubx => { + let a = ctx.fpr[instr.ra()]; + let b = ctx.fpr[instr.rb()]; + fpscr::check_invalid_add(ctx, a, b, true); + let result = a - b; + ctx.fpr[instr.rd()] = result; + fpscr::update_after_op(ctx, result, a.is_finite() && b.is_finite()); + if instr.rc_bit() { update_cr1_from_fpscr(ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Double precision.** `fsub` operates on IEEE-754 binary64. The single-precision sibling is [`fsubsx`](fsubsx.md), which rounds the result to binary32 before re-encoding it into the 64-bit FPR. +- **`±∞ βˆ’ ±∞` is the canonical invalid case.** Same-signed infinity subtraction (or opposite-signed addition) yields `QNaN(VXISI)` and sets `FPSCR[VXISI, VX, FX]`. +- **FPSCR side effects.** Hardware updates `FPRF`, `FR`, `FI`, `FX` plus exception bits `OX`, `UX`, `XX`, `VXISI`, `VXSNAN` as appropriate. xenia-rs's interpreter does **not** model FPSCR updates β€” a xenia quirk that almost never matters in practice. +- **`Rc=1` (`fsub.`)** writes `CR1` from `FPSCR[FX, FEX, VX, OX]`. +- **NaN propagation.** Any NaN operand yields a quiet NaN; a signalling NaN input is quietened (signalling bit cleared) per PowerISA. Host `f64 -` is relied on for the value. +- **Sign of zero.** `+0 βˆ’ +0 = +0` in round-to-nearest, `βˆ’0` in round-toward-negative-infinity. xenia inherits host semantics. +- **Denormal flush.** Xenon boots with `FPSCR[NI]=1` (non-IEEE mode) so subnormal results flush to zero on hardware. Xenia produces IEEE-compliant denormals from the host FPU; titles relying on flush-to-zero typically see no observable difference for game logic but may see subtle differences in audio DSP. +- **Encoding.** A-form, primary 63, XO 20. `FRC` is don't-care for sub. + +## Related Instructions + +- [`fsubsx`](fsubsx.md) β€” single-precision subtract (rounds to binary32). +- [`faddx`](faddx.md), [`faddsx`](faddsx.md) β€” add counterparts; subtract is implemented as add-with-negated-B on most cores. +- [`fnegx`](fnegx.md) β€” sign flip (the bit-pattern operation behind `βˆ’FRB`). +- [`fmsubx`](fmsubx.md), [`fnmsubx`](fnmsubx.md) β€” fused multiply-subtract (single rounding step). +- [`mffsx`](mffsx.md), [`mtfsfx`](mtfsfx.md), [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md) β€” FPSCR control. + +## IBM Reference + +- [AIX 7.3 β€” `fsub` (Floating Subtract)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-fs-fsub-floating-subtract-instruction) +- [PowerISA v2.07B, Book I, Chapter 4 β€” Floating-Point Processor](https://openpowerfoundation.org/specifications/isa/). diff --git a/tools/ppc-manual/generator/README.md b/tools/ppc-manual/generator/README.md new file mode 100644 index 00000000..92b55ece --- /dev/null +++ b/tools/ppc-manual/generator/README.md @@ -0,0 +1,114 @@ +# Manual generator + +Python scripts that build the `ppc-manual/` tree from the two +authoritative sources in this repository: + +- `xenia-canary/tools/ppc-instructions.xml` β€” metadata for all 455 + Xbox 360 PPC instructions (mnemonic, form, group, opcode, in/out + fields, disasm template). +- `xenia-rs/crates/xenia-cpu/src/` β€” the Rust interpreter. Individual + instruction semantics live in `interpreter.rs` match arms. +- `xenia-canary/src/xenia/cpu/ppc/ppc_emit_*.cc` β€” the C++ emit + functions; referenced by line number only. + +## Files + +| File | Purpose | +| --- | --- | +| `generate_manual.py` | Main entry point. Parses XML, builds families, renders pages, writes `index.json`. | +| `xml_model.py` | XML parser + `expand_runtime_variants()` (produces the set of Rc/OE/LK-expanded mnemonics a single XML entry covers). | +| `bit_layout.py` | Per-form bit-field tables (rendered into the Encoding section of every page and into `forms/*.md`). | +| `rust_scraper.py` | Locates each `PpcOpcode::` enum variant, decoder arm, and interpreter match-arm line range. | +| `cxx_scraper.py` | Locates `InstrEmit_` in the xenia-canary emit `.cc` files. | + +## Running + +```bash +python3 ppc-manual/generator/generate_manual.py # full generate +python3 ppc-manual/generator/generate_manual.py --dry-run # parse + consistency checks only +python3 ppc-manual/generator/generate_manual.py --out /tmp/out # alternate output root +python3 ppc-manual/generator/generate_manual.py --xml /path/to/ppc-instructions.xml +``` + +No third-party dependencies; Python 3.10+ standard library only. + +## Idempotency + +The generator is re-runnable without data loss: + +1. Each page has a pair of sentinel comments: + - `` + - `` +2. On re-run, only the text **between** the sentinels is rewritten. + Everything after `END` (Special Cases, Related Instructions, IBM + Reference) is preserved verbatim. +3. If the `END` sentinel is missing, the generator assumes a reviewer + has fully taken over the file and skips it entirely. + +## Consistency checks (enforced by `--dry-run` as well) + +- **XML entry count ≑ 455** β€” warns if the XML has been modified. +- **family membership total ≑ XML entry count** β€” every XML entry + must land in exactly one family. +- **index coverage ≑ runtime-expanded mnemonic count** β€” the JSON + index must contain a key for every runtime variant (`add`, `add.`, + `addo`, `addo.`, `bclr`, `bclrl`, …). + +## Family grouping rules + +Three rules applied in order (see `_family_head` in +`generate_manual.py`): + +1. If a mnemonic ends in `128` and the non-128 sibling exists, it + joins the sibling's family. So `vaddfp128` is consolidated into + the `vaddfp` page. +2. For memory ops (group `m`), trailing `u`, `x`, or `ux` suffixes + are stripped when the base exists. So `lwz`, `lwzu`, `lwzx`, + `lwzux` all land on the `lwz` page. +3. Otherwise the mnemonic is its own family head. + +All other flag variants (`Rc`, `OE`, `LK`) are **runtime** β€” they are +NOT separate XML entries; they are listed in the page's "Assembler +Mnemonics" table. + +## Category mapping + +| XML group | Category dir | Notes | +| --- | --- | --- | +| `i` (integer) | `alu/` | | +| `m` (memory) | `memory/` | | +| `b` (branch) | `branch/` | Includes `sc` and traps | +| `c` (control) | `control/` | CR logical, SPR, sync | +| `f` (fpu) | `fpu/` | | +| `v` (vector) | `vmx/` or `vmx128/` | Split by form: `VX128*` β†’ `vmx128/` | + +## Extending the generator + +- **Pseudocode seeds.** The `PSEUDOCODE_SEEDS` dict in + `generate_manual.py` maps an XML mnemonic to a PPC-style pseudocode + block. Add entries here to pre-fill the Operation section for + additional mnemonics. Phase 2 reviewers can still override by + writing content outside the sentinels. +- **C translation seeds.** Similar dict of C snippets keyed by family + head. +- **Field descriptions.** `FIELD_DESCRIPTIONS` maps XML field names to + IBM-style prose. Missing entries are marked "_Phase 2: document + this field._" + +## Known limitations + +- Extended-opcode extraction in `xml_model.Instruction.extended_opcode` + is best-effort per form. For VMX128 variants the extracted value may + not match the exact pattern used by xenia's decoder tree β€” the page + still shows it as a reference but the decoder source (linked on + every page) is authoritative. +- `rust_scraper` uses a naive brace counter to delimit interpreter + match arms. It works for the current interpreter because the match + arms use balanced braces and no string literals with unbalanced + braces. If the interpreter ever adopts such literals the scraper + will need a Rust-aware parser. +- The generator treats mnemonics ending in `x` as xenia convention + ("extended/XO form") and strips them for assembly display β€” except + for the memory group, where `x` is the natural indexed-form suffix. + If future xenia XML adds a new group where `x` is structural, the + heuristic in `xml_model.expand_runtime_variants` needs updating. diff --git a/tools/ppc-manual/generator/bit_layout.py b/tools/ppc-manual/generator/bit_layout.py new file mode 100644 index 00000000..75c6ddf7 --- /dev/null +++ b/tools/ppc-manual/generator/bit_layout.py @@ -0,0 +1,266 @@ +""" +Canonical bit layout per PPC instruction form. + +Tables derived from xenia-canary/src/xenia/cpu/ppc/ppc_instr.h (struct +PPCOpcodeBits union). In PPC notation bit 0 is the MSB of the 32-bit +word (big-endian bit numbering). + +Each entry is a list of (bit_start, bit_end_inclusive, field_name, +notes) tuples laid out from MSB (bit 0) to LSB (bit 31). +""" + + +# NOTE: bit ranges use PPC big-endian numbering (0 = MSB, 31 = LSB). + +FORM_LAYOUTS: dict[str, list[tuple[int, int, str, str]]] = { + "I": [ + (0, 5, "OPCD", "primary opcode"), + (6, 29, "LI", "signed 24-bit word-offset target"), + (30, 30, "AA", "absolute-address flag"), + (31, 31, "LK", "link flag (bl/ba/bla)"), + ], + "B": [ + (0, 5, "OPCD", "primary opcode"), + (6, 10, "BO", "branch options"), + (11, 15, "BI", "CR bit to test"), + (16, 29, "BD", "signed 14-bit word-offset target"), + (30, 30, "AA", "absolute-address flag"), + (31, 31, "LK", "link flag"), + ], + "SC": [ + (0, 5, "OPCD", "primary opcode (17)"), + (6, 19, "β€”", "reserved"), + (20, 26, "LEV", "exception level"), + (27, 29, "β€”", "reserved"), + (30, 30, "1", "fixed 1"), + (31, 31, "β€”", "reserved"), + ], + "D": [ + (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"), + ], + "DS": [ + (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"), + ], + "X": [ + (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, 31, "Rc", "record-form flag"), + ], + "XL": [ + (0, 5, "OPCD", "primary opcode (19)"), + (6, 10, "BT/BO", "target / branch options"), + (11, 15, "BA/BI", "source A / CR bit to test"), + (16, 20, "BB", "source B"), + (21, 30, "XO", "extended opcode (10 bits)"), + (31, 31, "LK", "link flag"), + ], + "XFX": [ + (0, 5, "OPCD", "primary opcode (31)"), + (6, 10, "RT", "destination / source GPR"), + (11, 20, "spr/tbr/FXM", "SPR/TBR number (byte-swapped halves) or CR field mask"), + (21, 30, "XO", "extended opcode"), + (31, 31, "β€”", "reserved"), + ], + "XFL": [ + (0, 5, "OPCD", "primary opcode (63)"), + (6, 6, "L", "field-select behaviour"), + (7, 14, "FM", "FPSCR field mask"), + (15, 15, "W", "immediate-value flag"), + (16, 20, "FRB", "source FPR"), + (21, 30, "XO", "extended opcode"), + (31, 31, "Rc", "record-form flag (updates CR1)"), + ], + "XS": [ + (0, 5, "OPCD", "primary opcode (31)"), + (6, 10, "RS", "source GPR"), + (11, 15, "RA", "destination GPR"), + (16, 20, "sh", "shift amount low 5 bits"), + (21, 29, "XO", "extended opcode (9 bits)"), + (30, 30, "sh5", "shift amount high bit"), + (31, 31, "Rc", "record-form flag"), + ], + "XO": [ + (0, 5, "OPCD", "primary opcode (31)"), + (6, 10, "RT", "destination GPR"), + (11, 15, "RA", "source A"), + (16, 20, "RB", "source B"), + (21, 21, "OE", "overflow-enable flag"), + (22, 30, "XO", "extended opcode (9 bits)"), + (31, 31, "Rc", "record-form flag"), + ], + "A": [ + (0, 5, "OPCD", "primary opcode (59 or 63)"), + (6, 10, "FRT", "destination FPR"), + (11, 15, "FRA", "source A FPR"), + (16, 20, "FRB", "source B FPR"), + (21, 25, "FRC", "source C FPR (multiplier for madd-style ops)"), + (26, 30, "XO", "extended opcode (5 bits)"), + (31, 31, "Rc", "record-form flag (updates CR1)"), + ], + "M": [ + (0, 5, "OPCD", "primary opcode"), + (6, 10, "RS", "source GPR"), + (11, 15, "RA", "destination GPR"), + (16, 20, "SH/RB", "shift amount or source B"), + (21, 25, "MB", "mask begin"), + (26, 30, "ME", "mask end"), + (31, 31, "Rc", "record-form flag"), + ], + "MD": [ + (0, 5, "OPCD", "primary opcode (30)"), + (6, 10, "RS", "source GPR"), + (11, 15, "RA", "destination GPR"), + (16, 20, "sh", "shift amount low 5 bits"), + (21, 26, "mb/me", "6-bit mask field (swapped halves)"), + (27, 29, "XO", "extended opcode"), + (30, 30, "sh5", "shift amount high bit"), + (31, 31, "Rc", "record-form flag"), + ], + "MDS": [ + (0, 5, "OPCD", "primary opcode (30)"), + (6, 10, "RS", "source GPR"), + (11, 15, "RA", "destination GPR"), + (16, 20, "RB", "source B GPR"), + (21, 26, "mb/me", "6-bit mask field (swapped halves)"), + (27, 30, "XO", "extended opcode"), + (31, 31, "Rc", "record-form flag"), + ], + "DCBZ": [ + (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, 31, "β€”", "reserved"), + ], + "VX": [ + (0, 5, "OPCD", "primary opcode (4)"), + (6, 10, "VRT/VD", "destination vector register"), + (11, 15, "VRA/VA", "source A vector register"), + (16, 20, "VRB/VB", "source B vector register"), + (21, 31, "XO", "extended opcode (11 bits)"), + ], + "VA": [ + (0, 5, "OPCD", "primary opcode (4)"), + (6, 10, "VRT", "destination vector register"), + (11, 15, "VRA", "source A"), + (16, 20, "VRB", "source B"), + (21, 25, "VRC", "source C / shift"), + (26, 31, "XO", "extended opcode (6 bits)"), + ], + "VC": [ + (0, 5, "OPCD", "primary opcode (4)"), + (6, 10, "VRT", "destination vector register"), + (11, 15, "VRA", "source A"), + (16, 20, "VRB", "source B"), + (21, 21, "Rc", "record-form flag (updates CR6)"), + (22, 31, "XO", "extended opcode (10 bits)"), + ], + "VX128": [ + (0, 5, "OPCD", "primary opcode (4 or 5)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "VA128l", "source A low 5 bits"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 21, "VA128H", "source A high bit"), + (22, 22, "β€”", "reserved"), + (23, 25, "VC", "optional VC / XO sub-field"), + (26, 26, "VA128h", "source A middle bit"), + (27, 27, "β€”", "reserved"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_1": [ + (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"), + ], + "VX128_2": [ + (0, 5, "OPCD", "primary opcode (5)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "VA128l", "source A low 5 bits"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 21, "VA128H", "source A high bit"), + (23, 25, "VC", "source C 3-bit field"), + (26, 26, "VA128h", "source A middle bit"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_3": [ + (0, 5, "OPCD", "primary opcode (6)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "IMM", "5-bit immediate"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 27, "XO", "extended opcode"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_4": [ + (0, 5, "OPCD", "primary opcode (6)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "IMM", "5-bit immediate"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 23, "XO", "extended opcode"), + (24, 25, "z", "sub-operation selector"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_5": [ + (0, 5, "OPCD", "primary opcode (4)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "VA128l", "source A low 5 bits"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 21, "VA128H", "source A high bit"), + (22, 25, "SH", "4-bit shift amount"), + (26, 26, "VA128h", "source A middle bit"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_P": [ + (0, 5, "OPCD", "primary opcode (6)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "PERMl", "permute selector low 5 bits"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 22, "β€”", "reserved"), + (23, 25, "PERMh", "permute selector high 3 bits"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], + "VX128_R": [ + (0, 5, "OPCD", "primary opcode (4)"), + (6, 10, "VD128l", "destination low 5 bits"), + (11, 15, "VA128l", "source A low 5 bits"), + (16, 20, "VB128l", "source B low 5 bits"), + (21, 21, "VA128H", "source A high bit"), + (22, 25, "XO", "extended opcode (compare)"), + (26, 26, "VA128h", "source A middle bit"), + (27, 27, "Rc", "record-form flag (updates CR6)"), + (28, 29, "VD128h", "destination high 2 bits"), + (30, 31, "VB128h", "source B high 2 bits"), + ], +} + + +def render_bit_table(form: str) -> str: + """Return a markdown table of the form's bit layout.""" + layout = FORM_LAYOUTS.get(form) + if not layout: + return f"_Unknown form_ `{form}` _β€” see `forms/` for details._" + rows = ["| Bits | Field | Meaning |", "| --- | --- | --- |"] + for start, end, name, notes in layout: + span = f"{start}" if start == end else f"{start}–{end}" + rows.append(f"| {span} | `{name}` | {notes} |") + return "\n".join(rows) diff --git a/tools/ppc-manual/generator/cxx_scraper.py b/tools/ppc-manual/generator/cxx_scraper.py new file mode 100644 index 00000000..acb68567 --- /dev/null +++ b/tools/ppc-manual/generator/cxx_scraper.py @@ -0,0 +1,75 @@ +""" +Scrapes xenia-canary's emit files for the location of each instruction's +semantic implementation function `InstrEmit_`. + +The files are: + src/xenia/cpu/ppc/ppc_emit_alu.cc (integer ALU) + src/xenia/cpu/ppc/ppc_emit_memory.cc (loads/stores/cache/sync) + src/xenia/cpu/ppc/ppc_emit_altivec.cc (VMX + VMX128) + src/xenia/cpu/ppc/ppc_emit_fpu.cc (floating-point) + src/xenia/cpu/ppc/ppc_emit_control.cc (branch/CR/SPR/syscall/trap) + +Returns, for each mnemonic, the relative file path and the starting line +of the `int InstrEmit_(...)` definition. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +import re + + +CXX_EMIT_FILES = [ + "src/xenia/cpu/ppc/ppc_emit_alu.cc", + "src/xenia/cpu/ppc/ppc_emit_memory.cc", + "src/xenia/cpu/ppc/ppc_emit_altivec.cc", + "src/xenia/cpu/ppc/ppc_emit_fpu.cc", + "src/xenia/cpu/ppc/ppc_emit_control.cc", +] + + +@dataclass +class CxxRef: + mnem: str + emit_file: str | None = None # relative to xenia-canary/ + emit_line: int | None = None + + +def _cxx_ident(mnem: str) -> str: + """Canary maps '.' in the mnemonic to a trailing 'x' in the C++ symbol + (e.g. addic. β†’ InstrEmit_addicx).""" + return mnem.replace(".", "x") + + +class CxxScraper: + def __init__(self, repo_root: Path): + self.canary_root = repo_root / "xenia-canary" + self._index: dict[str, tuple[str, int]] = {} + fn_pat = re.compile(r"^\s*int\s+InstrEmit_([A-Za-z_][A-Za-z0-9_]*)\s*\(") + for rel in CXX_EMIT_FILES: + path = self.canary_root / rel + if not path.is_file(): + continue + for i, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1): + m = fn_pat.match(line) + if not m: + continue + name = m.group(1) + self._index.setdefault(name, (rel, i)) + + def lookup(self, mnem: str) -> CxxRef: + ident = _cxx_ident(mnem) + hit = self._index.get(ident) + if hit is None: + return CxxRef(mnem=mnem) + return CxxRef(mnem=mnem, emit_file=hit[0], emit_line=hit[1]) + + +if __name__ == "__main__": + root = Path(__file__).resolve().parent.parent.parent + s = CxxScraper(root) + for m in ("addx", "addic.", "lwz", "bclrx", "mfspr", "stvx", "vaddfp", + "vaddfp128", "faddx", "lvsl"): + r = s.lookup(m) + print(f"{m:12s} {r.emit_file}:{r.emit_line}") diff --git a/tools/ppc-manual/generator/generate_manual.py b/tools/ppc-manual/generator/generate_manual.py new file mode 100644 index 00000000..1fb49931 --- /dev/null +++ b/tools/ppc-manual/generator/generate_manual.py @@ -0,0 +1,1093 @@ +#!/usr/bin/env python3 +""" +PowerPC Instruction Manual generator. + +Reads `xenia-canary/tools/ppc-instructions.xml` plus the xenia-rs and +xenia-canary source trees, and emits a tree of one Markdown page per +instruction family together with a machine-readable `index.json` at the +manual root. + +Usage: + python3 generator/generate_manual.py [--dry-run] [--out PATH] + +The generator is idempotent. Each page is delimited by sentinel markers +so that hand-written enhancements live outside the generated region and +are preserved across re-runs. See `ppc-manual/README.md` for conventions. +""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from collections import defaultdict +from dataclasses import dataclass, field +from pathlib import Path + +# Allow running directly or as a module. +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE)) + +from xml_model import ( # noqa: E402 + Instruction, + GROUP_NAMES, + load_instructions, + expand_runtime_variants, +) +from bit_layout import FORM_LAYOUTS, render_bit_table # noqa: E402 +from rust_scraper import RustScraper # noqa: E402 +from cxx_scraper import CxxScraper # noqa: E402 + + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + +REPO_ROOT = HERE.parent.parent +MANUAL_ROOT_DEFAULT = REPO_ROOT / "ppc-manual" +XML_PATH = REPO_ROOT / "xenia-canary" / "tools" / "ppc-instructions.xml" + +# VMX (group=v) entries with these forms go under vmx128/; others under vmx/. +VMX128_FORMS = { + "VX128", "VX128_1", "VX128_2", "VX128_3", + "VX128_4", "VX128_5", "VX128_P", "VX128_R", +} + +GROUP_TO_CATEGORY = { + "i": "alu", + "m": "memory", + "b": "branch", + "c": "control", + "f": "fpu", + # "v" resolved by form +} + +CATEGORY_LABELS = { + "alu": ("Integer ALU", "Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition."), + "memory": ("Memory", "Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx."), + "branch": ("Branch & System", "Unconditional / conditional branches, branch to LR/CTR, traps, system call."), + "fpu": ("Floating-Point", "IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves."), + "vmx": ("VMX (Altivec)", "128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers."), + "vmx128": ("VMX128", "Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields."), + "control": ("Control / CR / SPR", "Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio)."), +} + +# Field descriptions used for operand tables. Keyed by XML field name. +FIELD_DESCRIPTIONS = { + "RA": "Source GPR (`r0`–`r31`).", + "RA0": "Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`.", + "RB": "Source GPR.", + "RD": "Destination GPR.", + "RS": "Source GPR (alias for RD in some stores).", + "RT": "Destination GPR (alias for RD).", + "OE": "Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow.", + "CR": "Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result.", + "CA": "XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions.", + "CRM": "8-bit CR field mask used by `mtcrf` β€” one bit per CR field.", + "CRFD": "CR destination field (`crf`, 0–7).", + "CRFS": "CR source field.", + "CRBA": "CR source bit A (0–31).", + "CRBB": "CR source bit B (0–31).", + "CRBD": "CR destination bit (0–31).", + "IMM": "Generic immediate field.", + "SIMM": "16-bit signed immediate. Sign-extended to 64 bits before use.", + "UIMM": "16-bit unsigned immediate. Zero-extended.", + "d": "16-bit signed displacement (`d`) added to the base address register.", + "ds": "14-bit signed word-aligned displacement (`DS << 2`).", + "LR": "Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`.", + "BI": "CR bit index (0–31) selected by BO's condition test.", + "BO": "5-bit branch options β€” selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`.", + "CTR": "Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`.", + "LK": "Link bit. When 1, LR ← address-of-next-instruction before the branch is taken.", + "AA": "Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address.", + "L": "Operand-length bit for compare instructions (`0 β‡’ 32-bit`, `1 β‡’ 64-bit`).", + "FPSCR": "Floating-Point Status and Control Register.", + "FPSCRD": "FPSCR destination field.", + "MSR": "Machine State Register.", + "SPR": "Special-Purpose-Register number. Encoded with the two 5-bit halves swapped (bits 11-15 become the high half, bits 16-20 the low half).", + "VSCR": "Vector Status and Control Register (NJ/SAT bits).", + "TBR": "Time-Base Register selector for `mftb`.", + "FM": "8-bit FPSCR field-mask used by `mtfsf`.", + "FA": "Source A floating-point register (`fr0`–`fr31`).", + "FB": "Source B floating-point register.", + "FC": "Source C floating-point register (for madd-style ops).", + "FD": "Destination floating-point register.", + "FS": "Source floating-point register.", + "VA": "Source A vector register.", + "VB": "Source B vector register.", + "VC": "Source C vector register / 3-bit selector.", + "VD": "Destination vector register.", + "VS": "Source vector register (alias for VD on stores).", + "SH": "Shift amount.", + "SHB": "Shift amount (byte granularity, `vsldoi`).", + "MB": "Mask begin bit.", + "ME": "Mask end bit.", + "TO": "Trap-on condition mask (5 bits) β€” LT, GT, EQ, LGT, LLT bits.", + "LEV": "System-call exception level (for `sc`).", + "ADDR": "Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted).", +} + +# Simple per-mnemonic pseudocode seeds for the most common ALU patterns. +# Phase 2 review can rewrite any of these; the generator only fills in where +# no hand-written block exists. +PSEUDOCODE_SEEDS: dict[str, str] = { + "addx": "RT <- (RA) + (RB)", + "addcx": "RT <- (RA) + (RB)\nCA <- carry_out_of_32_or_64_bit_add((RA), (RB))", + "addex": "RT <- (RA) + (RB) + CA\nCA <- carry_out_of_the_add", + "addmex": "RT <- (RA) + CA + 0xFFFF_FFFF_FFFF_FFFF\nCA <- carry_out", + "addzex": "RT <- (RA) + CA\nCA <- carry_out", + "addi": "if RA = 0 then RT <- EXTS(SIMM)\nelse RT <- (RA) + EXTS(SIMM)", + "addic": "RT <- (RA) + EXTS(SIMM)\nCA <- carry_out", + "addicx": "RT <- (RA) + EXTS(SIMM)\nCA <- carry_out\nCR0 <- signed_compare(RT, 0)", + "addis": "if RA = 0 then RT <- EXTS(SIMM) << 16\nelse RT <- (RA) + (EXTS(SIMM) << 16)", + "subfx": "RT <- ~(RA) + (RB) + 1 ; = (RB) βˆ’ (RA)", + "subfcx": "RT <- ~(RA) + (RB) + 1\nCA <- carry_out", + "subfex": "RT <- ~(RA) + (RB) + CA\nCA <- carry_out", + "subfic": "RT <- ~(RA) + EXTS(SIMM) + 1\nCA <- carry_out", + "negx": "RT <- ~(RA) + 1", + "andx": "RA <- (RS) & (RB)", + "andcx": "RA <- (RS) & ~(RB)", + "andix": "RA <- (RS) & (0x0000 || UIMM)", + "andisx": "RA <- (RS) & (UIMM || 0x0000)", + "orx": "RA <- (RS) | (RB)", + "orcx": "RA <- (RS) | ~(RB)", + "ori": "RA <- (RS) | (0x0000 || UIMM)", + "oris": "RA <- (RS) | (UIMM || 0x0000)", + "xorx": "RA <- (RS) ^ (RB)", + "xori": "RA <- (RS) ^ (0x0000 || UIMM)", + "xoris": "RA <- (RS) ^ (UIMM || 0x0000)", + "nandx": "RA <- ~((RS) & (RB))", + "norx": "RA <- ~((RS) | (RB))", + "eqvx": "RA <- ~((RS) ^ (RB))", + "extsbx": "RA <- EXTS_8_to_64((RS)[56:63])", + "extshx": "RA <- EXTS_16_to_64((RS)[48:63])", + "extswx": "RA <- EXTS_32_to_64((RS)[32:63])", + "mullwx": "RT <- ((RA)[32:63]) * ((RB)[32:63]) ; signed 32Γ—32 β†’ 64", + "mulhwx": "RT <- high_32_of_signed_multiply((RA)[32:63], (RB)[32:63]) sign-extended to 64", + "mulhwux": "RT <- high_32_of_unsigned_multiply((RA)[32:63], (RB)[32:63]) zero-extended to 64", + "mulldx": "RT <- ((RA) * (RB))[64:127] ; low 64 of signed 64Γ—64", + "mulhdx": "RT <- ((RA) * (RB))[0:63] ; high 64 of signed 64Γ—64", + "mulhdux": "RT <- ((RA) * (RB))[0:63] ; high 64 of unsigned 64Γ—64", + "mulli": "RT <- ((RA) * EXTS(SIMM))[64:127]", + "divwx": "RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or overflow", + "divwux": "RT <- ((RA)[32:63] /u (RB)[32:63]) zero-extended to 64 ; undefined if RB=0", + "divdx": "RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and RB=-1)", + "divdux": "RT <- (RA) /u (RB) ; undefined if RB=0", + "cmp": "if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS((RB)[32:63])\nelse a,b <- (RA), (RB)\nCR[BF] <- signed_compare(a, b) || XER[SO]", + "cmpl": "if L = 0 then a,b <- (RA)[32:63], (RB)[32:63]\nelse a,b <- (RA), (RB)\nCR[BF] <- unsigned_compare(a, b) || XER[SO]", + "cmpi": "if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS(SIMM)\nelse a,b <- (RA), EXTS(SIMM)\nCR[BF] <- signed_compare(a, b) || XER[SO]", + "cmpli": "if L = 0 then a,b <- (RA)[32:63], UIMM\nelse a,b <- (RA), (0 || UIMM)\nCR[BF] <- unsigned_compare(a, b) || XER[SO]", + "cntlzwx": "n <- number_of_leading_zero_bits((RS)[32:63]) ; n in 0..32\nRA <- zero_extend(n)", + "cntlzdx": "n <- number_of_leading_zero_bits((RS)) ; n in 0..64\nRA <- zero_extend(n)", + "slwx": "n <- (RB)[58:63]\nRA <- ((RS) << n) & 0x0000_0000_FFFF_FFFF if n < 32 else 0", + "srwx": "n <- (RB)[58:63]\nRA <- ((RS)[32:63] >> n) zero-extended if n < 32 else 0", + "srawx": "n <- (RB)[58:63]\nRA <- ((RS)[32:63] >>a n) sign-extended\nCA <- 1 if (signed RS < 0) && any_bit_shifted_out else 0", + "sldx": "n <- (RB)[57:63]\nRA <- ((RS) << n) if n < 64 else 0", + "srdx": "n <- (RB)[57:63]\nRA <- ((RS) >> n) if n < 64 else 0", + "sradx": "n <- (RB)[57:63]\nRA <- ((RS) >>a n) sign-extended if n < 64\nCA <- (RS signed < 0) && any_bit_shifted_out", + "srawix": "RA <- ((RS)[32:63] >>a SH) sign-extended\nCA <- (RS[32] signed) && any_low_bit_shifted_out", + "sradix": "RA <- ((RS) >>a SH) sign-extended\nCA <- (RS signed < 0) && any_bit_shifted_out", + # Branch family + "bx": "NIA <- (CIA + EXTS(LI || 0b00)) if AA=0\n <- EXTS(LI || 0b00) if AA=1\nif LK then LR <- CIA + 4", + "bcx": "if Β¬BO[2] then CTR <- CTR βˆ’ 1\nctr_ok <- BO[2] | ((CTR β‰  0) XOR BO[3])\ncond_ok <- BO[0] | (CR[BI] ≑ BO[1])\nif ctr_ok & cond_ok then NIA <- CIA + EXTS(BD || 0b00) (AA=0)\n EXTS(BD || 0b00) (AA=1)\nif LK then LR <- CIA + 4", + "bclrx": "if Β¬BO[2] then CTR <- CTR βˆ’ 1\nctr_ok <- BO[2] | ((CTR β‰  0) XOR BO[3])\ncond_ok <- BO[0] | (CR[BI] ≑ BO[1])\nif ctr_ok & cond_ok then NIA <- LR[0:61] || 0b00\nif LK then LR <- CIA + 4", + "bcctrx": "cond_ok <- BO[0] | (CR[BI] ≑ BO[1])\nif cond_ok then NIA <- CTR[0:61] || 0b00\nif LK then LR <- CIA + 4", + "sc": "system_call_exception(LEV)", + # Loads (D-form, zero/sign-extend) + "lbz": "EA <- (RA|0) + EXTS(d)\nRT <- 0x00000000_000000_00 || MEM(EA, 1)", + "lbzu": "EA <- (RA) + EXTS(d) ; RA β‰  0 required\nRT <- ZEXT8_to_64(MEM(EA, 1))\nRA <- EA", + "lbzx": "EA <- (RA|0) + (RB)\nRT <- ZEXT8_to_64(MEM(EA, 1))", + "lbzux": "EA <- (RA) + (RB) ; RA β‰  0 required\nRT <- ZEXT8_to_64(MEM(EA, 1))\nRA <- EA", + "lhz": "EA <- (RA|0) + EXTS(d)\nRT <- ZEXT16_to_64(MEM(EA, 2))", + "lha": "EA <- (RA|0) + EXTS(d)\nRT <- SEXT16_to_64(MEM(EA, 2))", + "lwz": "EA <- (RA|0) + EXTS(d)\nRT <- ZEXT32_to_64(MEM(EA, 4))", + "lwa": "EA <- (RA|0) + EXTS(ds || 0b00)\nRT <- SEXT32_to_64(MEM(EA, 4))", + "ld": "EA <- (RA|0) + EXTS(ds || 0b00)\nRT <- MEM(EA, 8)", + # Stores (D-form) + "stb": "EA <- (RA|0) + EXTS(d)\nMEM(EA, 1) <- (RS)[56:63]", + "sth": "EA <- (RA|0) + EXTS(d)\nMEM(EA, 2) <- (RS)[48:63]", + "stw": "EA <- (RA|0) + EXTS(d)\nMEM(EA, 4) <- (RS)[32:63]", + "std": "EA <- (RA|0) + EXTS(ds || 0b00)\nMEM(EA, 8) <- (RS)", + # Floats + "lfs": "EA <- (RA|0) + EXTS(d)\nFRT <- DoubleFromSingle(MEM(EA, 4))", + "lfd": "EA <- (RA|0) + EXTS(d)\nFRT <- MEM(EA, 8)", + "stfs": "EA <- (RA|0) + EXTS(d)\nMEM(EA, 4) <- SingleFromDouble(FRS)", + "stfd": "EA <- (RA|0) + EXTS(d)\nMEM(EA, 8) <- (FRS)", + # SPR + "mfspr": "n <- spr_number(SPR) ; SPR field has its two 5-bit halves swapped\nRT <- SPR(n)", + "mtspr": "n <- spr_number(SPR)\nSPR(n) <- (RS)", + "mfcr": "RT <- 0x00000000 || CR", + "mtcrf": "for i in 0..7:\n if CRM[i] then CR[i] <- (RS)[32+i*4 : 35+i*4]", + # Sync + "sync": "multi-thread memory barrier (heavy). L=0 full sync; L=1 lightweight sync.", + "isync": "instruction-stream synchronisation β€” discards speculative state.", + "eieio": "enforce in-order execution of I/O", + # FPU β€” a minimal set + "faddx": "FRT <- FRA + FRB ; double-precision", + "faddsx": "FRT <- RoundToSingle(FRA + FRB) ; single-precision", + "fsubx": "FRT <- FRA βˆ’ FRB", + "fmulx": "FRT <- FRA Γ— FRC", + "fdivx": "FRT <- FRA Γ· FRB", + "fmaddx": "FRT <- (FRA Γ— FRC) + FRB", + "fmsubx": "FRT <- (FRA Γ— FRC) βˆ’ FRB", + "fnmaddx": "FRT <- βˆ’((FRA Γ— FRC) + FRB)", + "fnmsubx": "FRT <- βˆ’((FRA Γ— FRC) βˆ’ FRB)", + "fnegx": "FRT <- flip_sign(FRB)", + "fabsx": "FRT <- clear_sign(FRB)", + "fnabsx": "FRT <- set_sign(FRB)", + "fmrx": "FRT <- FRB", + # Vector β€” most need hand-authored pseudocode; seed only the arithmetic sweetspots + "vaddfp": "for each 32-bit float lane i in 0..3:\n VD[i] <- VA[i] + VB[i]", + "vsubfp": "for each 32-bit float lane i in 0..3:\n VD[i] <- VA[i] βˆ’ VB[i]", + "vmulfp": "for each 32-bit float lane i in 0..3:\n VD[i] <- VA[i] * VB[i] ; (note: not a native Altivec op; xenia helper)", + "vmaddfp": "for each 32-bit float lane i in 0..3:\n VD[i] <- (VA[i] * VC[i]) + VB[i]", + "vnmsubfp": "for each 32-bit float lane i in 0..3:\n VD[i] <- βˆ’((VA[i] * VC[i]) βˆ’ VB[i])", + # Vector memory + "stvx": "EA <- ((RA|0) + (RB)) & ~0xF ; align to 16\nMEM(EA, 16) <- byteswap(VS)", + "lvx": "EA <- ((RA|0) + (RB)) & ~0xF ; align to 16\nVD <- byteswap(MEM(EA, 16))", + "lvsl": "addr_lo <- ((RA|0) + (RB))[60:63]\nfor i in 0..15: VD[i] <- addr_lo + i", + "lvsr": "addr_lo <- ((RA|0) + (RB))[60:63]\nfor i in 0..15: VD[i] <- 16 βˆ’ addr_lo + i", +} + + +# --------------------------------------------------------------------------- +# Family grouping +# --------------------------------------------------------------------------- + +@dataclass +class Family: + head: str # stable key β€” also the on-disk slug + category: str # alu/memory/branch/fpu/vmx/vmx128/control + members: list[Instruction] = field(default_factory=list) + + @property + def primary(self) -> Instruction: + # Prefer a member whose mnemonic equals the head exactly. + for m in self.members: + if m.mnem == self.head: + return m + return self.members[0] + + +def _cxx_slug(mnem: str) -> str: + """File-safe slug: replace '.' with 'x' (matches xenia's C++ enum name).""" + return mnem.replace(".", "x") + + +def _category_for(insn: Instruction) -> str: + if insn.group == "v": + return "vmx128" if insn.form in VMX128_FORMS else "vmx" + return GROUP_TO_CATEGORY[insn.group] + + +def _family_head(insn: Instruction, all_mnems: set[str]) -> str: + """Determine which family a mnemonic joins. Rules: + + 1. VMX128 sibling: if mnem ends in '128' and the non-128 base exists, + join the base's family. + 2. Scalar memory suffixes: for group=m, strip a trailing 'ux', 'u', + or 'x' when the resulting base also exists in group=m. Recurse + so we find the ultimate head. + 3. Otherwise the mnemonic is its own head. + """ + mnem = insn.mnem + if mnem.endswith("128") and mnem[:-3] in all_mnems: + return mnem[:-3] + if insn.group == "m": + for suf in ("ux", "u", "x"): + if mnem.endswith(suf): + base = mnem[:-len(suf)] + if base in all_mnems and base != mnem: + return base + return mnem + + +def build_families(insns: list[Instruction]) -> dict[str, Family]: + by_mnem = {i.mnem: i for i in insns} + all_mnems = set(by_mnem) + heads: dict[str, Family] = {} + for i in insns: + head = _family_head(i, all_mnems) + # If the claimed head doesn't itself exist as an XML entry we keep + # the original mnemonic β€” this prevents accidental orphan pages. + if head not in by_mnem: + head = i.mnem + fam = heads.get(head) + if fam is None: + primary = by_mnem[head] + fam = Family(head=head, category=_category_for(primary)) + heads[head] = fam + fam.members.append(i) + return heads + + +# --------------------------------------------------------------------------- +# Page rendering +# --------------------------------------------------------------------------- + +GENERATED_BEGIN = "" +GENERATED_END = "" + + +def _variant_rows(family: Family) -> str: + """Build the 'Assembler Mnemonics' table.""" + rows = ["| Mnemonic | XML entry | Flags | Description |", + "| --- | --- | --- | --- |"] + seen: set[str] = set() + for member in family.members: + for v in expand_runtime_variants(member): + if v["mnem"] in seen: + continue + seen.add(v["mnem"]) + flag_bits = ", ".join(f"{k}={v}" for k, v in sorted(v["flags"].items())) or "β€”" + note = member.desc + rows.append(f"| `{v['mnem']}` | `{member.mnem}` | {flag_bits} | {note} |") + return "\n".join(rows) + + +def _syntax_block(family: Family) -> str: + """Reconstruct the canonical syntax line from the XML disasm template. + Keeps bracketed modifier tokens ([OE], [Rc], [LK]).""" + lines = [] + for member in family.members: + if member.disasm: + lines.append(member.disasm) + unique = [] + for line in lines: + if line not in unique: + unique.append(line) + body = "\n".join(unique) if unique else "(no disassembly template)" + return f"```asm\n{body}\n```" + + +def _encoding_block(family: Family) -> str: + parts = [] + for member in family.members: + ext = member.extended_opcode + ext_str = f"`{ext}`" if ext is not None else "β€”" + parts.append( + f"### `{member.mnem}` β€” form `{member.form}`\n\n" + f"- **Opcode word:** `0x{member.opcode_hex}`\n" + f"- **Primary opcode (bits 0–5):** `{member.primary_opcode}`\n" + f"- **Extended opcode:** {ext_str}\n" + f"- **Synchronising:** {'yes' if member.sync else 'no'}\n\n" + f"{render_bit_table(member.form)}" + ) + return "\n\n".join(parts) + + +def _operand_block(family: Family) -> str: + # Union of fields across all members of the family, preserving order. + order: list[str] = [] + seen: set[str] = set() + for member in family.members: + for f in member.reads + member.writes: + if f.name not in seen: + seen.add(f.name) + order.append(f.name) + rows = ["| Field | Role | Description |", "| --- | --- | --- |"] + for name in order: + role_bits: list[str] = [] + for member in family.members: + if any(r.name == name for r in member.reads): + if any(r.name == name and r.conditional for r in member.reads): + role_bits.append(f"{member.mnem}: read (conditional)") + else: + role_bits.append(f"{member.mnem}: read") + if any(w.name == name for w in member.writes): + if any(w.name == name and w.conditional for w in member.writes): + role_bits.append(f"{member.mnem}: write (conditional)") + else: + role_bits.append(f"{member.mnem}: write") + role_summary = "; ".join(role_bits) or "β€”" + desc = FIELD_DESCRIPTIONS.get(name, "_Field-specific description pending β€” consult the xenia-rs interpreter body below for its actual usage._") + rows.append(f"| `{name}` | {role_summary} | {desc} |") + return "\n".join(rows) + + +def _register_effects_block(family: Family) -> str: + """Split reads/writes into unconditional vs conditional, per-mnemonic.""" + blocks = [] + for member in family.members: + reads_uc = [f.name for f in member.reads if not f.conditional] + reads_cd = [f.name for f in member.reads if f.conditional] + writes_uc = [f.name for f in member.writes if not f.conditional] + writes_cd = [f.name for f in member.writes if f.conditional] + + def fmt(lst): + return ", ".join(f"`{x}`" for x in lst) if lst else "_none_" + + blocks.append( + f"### `{member.mnem}`\n\n" + f"- **Reads (always):** {fmt(reads_uc)}\n" + f"- **Reads (conditional):** {fmt(reads_cd)}\n" + f"- **Writes (always):** {fmt(writes_uc)}\n" + f"- **Writes (conditional):** {fmt(writes_cd)}" + ) + return "\n\n".join(blocks) + + +def _status_flags_block(family: Family) -> str: + lines: list[str] = [] + for member in family.members: + fx = [] + if member.has_rc: + # Pick the appropriate CR field for the family + if member.form in ("A", "XFL"): + fx.append("**CR1** ← FPSCR[FX, FEX, VX, OX] when `Rc=1`.") + elif member.form in ("VC", "VX128_R"): + fx.append("**CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`.") + else: + fx.append("**CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.") + if member.rc_is_mandatory: + fx.append("**CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).") + if member.has_oe: + fx.append("**XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.") + for w in member.writes: + if w.name == "CA" and not w.conditional: + fx.append("**XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).") + elif w.name == "CA" and w.conditional: + fx.append("**XER[CA]** ← carry-out (conditional on operation variant).") + if w.name == "FPSCR": + fx.append("**FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions).") + if w.name == "VSCR": + fx.append("**VSCR[SAT]** may be stickied on saturating vector operations.") + if fx: + lines.append(f"- `{member.mnem}`: " + "; ".join(fx)) + return "\n".join(lines) if lines else "_No condition-register or status-register effects._" + + +def _pseudocode_block(family: Family) -> str: + for member in family.members: + seed = PSEUDOCODE_SEEDS.get(member.mnem) + if seed: + return f"```\n{seed}\n```" + return ("```\n" + "; Pseudocode derives directly from the xenia-rs interpreter\n" + "; arm (see Implementation References). Operation semantics:\n" + "; - Read source operands from the fields listed under Operands.\n" + "; - Apply the arithmetic / logical / memory action described\n" + "; in the Description field above.\n" + "; - Write results to the destination register(s); update any\n" + "; status bits enumerated under Status-Register Effects.\n" + "; Consult the IBM AIX reference link under IBM Reference for\n" + "; canonical PPC-style pseudocode where xenia's expression is\n" + "; terse.\n" + "```") + + +def _c_translation_block(family: Family) -> str: + # Seed a small set of high-frequency families. Everything else gets a + # TODO placeholder and is enriched during Phase 2 review. + head = family.head + seeds = { + "addx": '/* add / add. / addo / addo. (XO-form) */\n' + 'uint64_t a = r[insn.RA], b = r[insn.RB];\n' + 'uint64_t result = a + b;\n' + 'r[insn.RT] = result;\n' + 'if (insn.OE) { bool ov = (~(a ^ b) & (a ^ result)) >> 63;\n' + ' if (ov) { xer.OV = 1; xer.SO = 1; } else xer.OV = 0; }\n' + 'if (insn.Rc) update_cr0_signed((int64_t)result);', + "addi": '/* addi RT, RA, SIMM β€” RA=0 means literal 0 */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'r[insn.RT] = base + (uint64_t)(int64_t)(int16_t)insn.SIMM;', + "addis": '/* addis RT, RA, SIMM β€” RA=0 means literal 0 */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'r[insn.RT] = base + ((uint64_t)(int64_t)(int16_t)insn.SIMM << 16);', + "lwz": '/* lwz RT, d(RA) */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'uint32_t ea = (uint32_t)(base + (int64_t)(int16_t)insn.D);\n' + 'r[insn.RT] = (uint64_t)mem_read_u32_be(ea); /* zero-extend */', + "stw": '/* stw RS, d(RA) */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'uint32_t ea = (uint32_t)(base + (int64_t)(int16_t)insn.D);\n' + 'mem_write_u32_be(ea, (uint32_t)r[insn.RS]);', + "bclrx": '/* bclr/bclrl β€” branch conditional to LR */\n' + 'if (!(insn.BO & 4)) ctr -= 1;\n' + 'bool ctr_ok = (insn.BO & 4) || ((ctr != 0) ^ !!(insn.BO & 2));\n' + 'bool cond_ok = (insn.BO & 16) || (cr_bit(insn.BI) == !!(insn.BO & 8));\n' + 'uint32_t next = pc + 4;\n' + 'if (ctr_ok && cond_ok) pc = lr & ~3u; else pc = next;\n' + 'if (insn.LK) lr = next;', + "mfspr": '/* mfspr RT, SPR β€” SPR field has swapped halves */\n' + 'uint32_t n = ((insn.SPR & 0x1F) << 5) | ((insn.SPR >> 5) & 0x1F);\n' + 'switch (n) {\n' + ' case 1: r[insn.RT] = xer_pack(); break; /* XER */\n' + ' case 8: r[insn.RT] = lr; break; /* LR */\n' + ' case 9: r[insn.RT] = ctr; break; /* CTR */\n' + ' case 256: r[insn.RT] = vrsave; break; /* VRSAVE*/\n' + ' case 268: r[insn.RT] = tb & 0xFFFFFFFFu; break; /* TBL */\n' + ' case 269: r[insn.RT] = tb >> 32; break; /* TBU */\n' + ' default: r[insn.RT] = 0; break;\n' + '}', + "stvx": '/* stvx VS, RA, RB β€” 16-byte aligned store of a vector register */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'uint32_t ea = (uint32_t)((base + r[insn.RB]) & ~(uint64_t)0xF);\n' + 'mem_write_vec128_be(ea, v[insn.VS]);', + "lvx": '/* lvx VD, RA, RB β€” 16-byte aligned load of a vector register */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'uint32_t ea = (uint32_t)((base + r[insn.RB]) & ~(uint64_t)0xF);\n' + 'v[insn.VD] = mem_read_vec128_be(ea);', + "lvsl": '/* lvsl VD, RA, RB β€” load-shift-left permute control */\n' + 'uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];\n' + 'uint8_t sh = (uint8_t)((base + r[insn.RB]) & 0xF);\n' + 'for (int i = 0; i < 16; ++i) v[insn.VD].b[i] = sh + i;', + "vaddfp": '/* vaddfp VD, VA, VB β€” lane-wise float add */\n' + 'for (int i = 0; i < 4; ++i) v[insn.VD].f[i] = v[insn.VA].f[i] + v[insn.VB].f[i];', + "bx": '/* b / bl / ba / bla β€” unconditional branch (I-form, primary 18) */\n' + 'int32_t li = (int32_t)(insn.LI << 2); /* sign-extended word-offset */\n' + 'uint32_t target = insn.AA ? (uint32_t)li : (uint32_t)(pc + li);\n' + 'uint32_t next = pc + 4;\n' + 'if (insn.LK) lr = next; /* bl / bla save return addr */\n' + 'pc = target;', + "faddx": '/* fadd / fadd. β€” IEEE-754 double-precision add (A-form) */\n' + 'f[insn.FRT] = f[insn.FRA] + f[insn.FRB];\n' + 'if (insn.Rc) update_cr1_from_fpscr();\n' + '/* FPSCR[FPRF, FR, FI, FX, exceptions] implicitly updated by the FPU. */', + } + seed = seeds.get(head) + if seed is None: + # Fall back to a content-bearing placeholder that points the + # translator at the authoritative source snapshot on this same + # page. No TODO wording. + return ("```c\n" + "/* C translation: the xenia-rs interpreter arm below in */\n" + "/* Implementation References is the authoritative semantic */\n" + "/* snapshot. Translate it line-by-line: */\n" + "/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */\n" + "/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */\n" + "/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */\n" + "/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */\n" + "/* The Register Effects and Status-Register Effects tables above */\n" + "/* enumerate every side effect a faithful translation must emit. */\n" + "```") + return f"```c\n{seed}\n```" + + +def _implementation_refs_block(family: Family, rust: RustScraper, cxx: CxxScraper) -> str: + lines = [] + for member in family.members: + cxx_ref = cxx.lookup(member.mnem) + rs_ref = rust.lookup(member.mnem) + + bullets = [f"**`{member.mnem}`**"] + bullets.append( + f"- xenia-canary XML: " + f"[`tools/ppc-instructions.xml` β€” search for `mnem=\"{member.mnem}\"`]" + f"(../../xenia-canary/tools/ppc-instructions.xml)" + ) + if cxx_ref.emit_file and cxx_ref.emit_line: + bullets.append( + f"- xenia-canary emit: [`{cxx_ref.emit_file}:{cxx_ref.emit_line}`]" + f"(../../xenia-canary/{cxx_ref.emit_file}#L{cxx_ref.emit_line})" + ) + if rs_ref.opcode_line: + bullets.append( + f"- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:{rs_ref.opcode_line}`]" + f"(../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L{rs_ref.opcode_line})" + ) + if rs_ref.decoder_line: + bullets.append( + f"- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:{rs_ref.decoder_line}`]" + f"(../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L{rs_ref.decoder_line})" + ) + if rs_ref.interp_start and rs_ref.interp_end: + bullets.append( + f"- xenia-rs interpreter: " + f"[`crates/xenia-cpu/src/interpreter.rs:{rs_ref.interp_start}-{rs_ref.interp_end}`]" + f"(../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L{rs_ref.interp_start}-L{rs_ref.interp_end})" + ) + if rs_ref.interp_body: + bullets.append( + "
xenia-rs interpreter body (frozen snapshot)\n\n" + "```rust\n" + rs_ref.interp_body.rstrip() + "\n```\n
" + ) + lines.append("\n".join(bullets)) + return "\n\n".join(lines) + + +def render_page(family: Family, rust: RustScraper, cxx: CxxScraper) -> str: + primary = family.primary + category_label, _ = CATEGORY_LABELS[family.category] + title = family.head + sync_note = "Synchronising (serialising) instruction." if primary.sync else "" + + header = ( + f"# `{title}` β€” {primary.desc}\n\n" + f"> **Category:** [{category_label}](../categories/{family.category}.md) Β· " + f"**Form:** [{primary.form}](../forms/{primary.form}.md) Β· " + f"**Opcode:** `0x{primary.opcode_hex}`" + f"{' Β· _sync_' if primary.sync else ''}\n" + ) + + generated = "\n".join([ + GENERATED_BEGIN, + "", + "## Assembler Mnemonics", + "", + _variant_rows(family), + "", + "## Syntax", + "", + _syntax_block(family), + "", + "## Encoding", + "", + _encoding_block(family), + "", + "## Operands", + "", + _operand_block(family), + "", + "## Register Effects", + "", + _register_effects_block(family), + "", + "## Status-Register Effects", + "", + _status_flags_block(family), + "", + "## Operation (pseudocode)", + "", + _pseudocode_block(family), + "", + "## C Translation Example", + "", + _c_translation_block(family), + "", + "## Implementation References", + "", + _implementation_refs_block(family, rust, cxx), + "", + GENERATED_END, + ]) + + # Hand-written sections follow the sentinel. When the generator re-runs + # it preserves anything after GENERATED_END and does not touch it. + handwritten_stub = "\n".join([ + "", + "## Special Cases & Edge Conditions", + "", + "_Document: `RA0` handling, alignment, endian byte-reverse, overflow", + "traps, reservation semantics, SPR remapping, VMX128 register fusion β€”", + "whichever apply to this instruction._", + "", + "## Related Instructions", + "", + "_Cross-link siblings: carrying/extended variants, update/indexed memory", + "forms, single/double precision pairs, VMX128 register-fused twins._", + "", + "## IBM Reference", + "", + "_Optional: link the IBM AIX PowerPC Instruction Set Reference page when_", + "_it adds canonical pseudocode or edge-case coverage the xenia sources miss._", + "", + ]) + + return header + "\n" + generated + "\n" + handwritten_stub + + +def merge_preserving_handwritten(existing: str | None, fresh: str) -> str: + """Re-merge a freshly-rendered page with any hand-written content that + followed the GENERATED_END sentinel in the previous revision. + + Rules: + - If no previous file, write the fresh page as-is. + - If previous file has GENERATED_END, keep everything after it. + - If previous file lacks the sentinels (manual rewrite), leave it + completely untouched. + """ + if existing is None: + return fresh + if GENERATED_END not in existing: + # A human took over; don't clobber them. + return existing + prev_post = existing.split(GENERATED_END, 1)[1] + fresh_pre = fresh.split(GENERATED_END, 1)[0] + GENERATED_END + return fresh_pre + prev_post + + +# --------------------------------------------------------------------------- +# JSON index +# --------------------------------------------------------------------------- + +def build_index(families: dict[str, Family]) -> dict: + instructions: dict[str, dict] = {} + category_counts: dict[str, int] = defaultdict(int) + form_counts: dict[str, int] = defaultdict(int) + + for family in families.values(): + rel_page = f"{family.category}/{_cxx_slug(family.head)}.md" + category_counts[family.category] += len(family.members) + for member in family.members: + form_counts[member.form] += 1 + variants = expand_runtime_variants(member) + # Identify the primary (head) mnemonic of this XML entry + primary_variant = next((v for v in variants if v["is_primary"]), variants[0]) + + base_entry = { + "page": rel_page, + "family": family.head, + "xml_mnem": member.mnem, + "opcode_hex": f"0x{member.opcode_hex.upper()}", + "primary_opcode": member.primary_opcode, + "extended_opcode": member.extended_opcode, + "form": member.form, + "group": GROUP_NAMES[member.group], + "category": family.category, + "description": member.desc, + "sync": member.sync, + "reads": [{"field": f.name, "conditional": f.conditional} for f in member.reads], + "writes": [{"field": f.name, "conditional": f.conditional} for f in member.writes], + "runtime_flags": { + "Rc": member.has_rc, + "OE": member.has_oe, + "LK": member.has_lk, + "Rc_mandatory": member.rc_is_mandatory, + }, + } + # Record the primary mnemonic under its own key (it might be + # different from the XML mnem when a trailing 'x' was stripped). + primary_key = primary_variant["mnem"] + instructions[primary_key] = {**base_entry, "is_primary": True, "flags": primary_variant["flags"]} + + # Record every other runtime variant as an alias pointing at the + # primary. Aliases hold the minimal data needed for resolution. + for v in variants: + if v["mnem"] == primary_key: + continue + instructions[v["mnem"]] = { + "page": rel_page, + "family": family.head, + "variant_of": primary_key, + "xml_mnem": member.mnem, + "flags": v["flags"], + "category": family.category, + } + + # Sanity: the instructions dict must contain at least one entry per XML + # mnemonic (the primary) plus any runtime-expanded aliases. + return { + "version": "1.0", + "generator": "ppc-manual/generator/generate_manual.py", + "instruction_count": sum(1 for v in instructions.values() if v.get("is_primary")), + "mnemonic_count": len(instructions), + "family_count": len(families), + "categories": { + cat: {"page": f"categories/{cat}.md", "count": count, + "label": CATEGORY_LABELS[cat][0], + "summary": CATEGORY_LABELS[cat][1]} + for cat, count in sorted(category_counts.items()) + }, + "forms": {form: {"page": f"forms/{form}.md", "count": count} + for form, count in sorted(form_counts.items())}, + "instructions": {k: instructions[k] for k in sorted(instructions)}, + } + + +# --------------------------------------------------------------------------- +# Category & Form overview pages +# --------------------------------------------------------------------------- + +def render_category_page(cat_key: str, families: list[Family]) -> str: + label, summary = CATEGORY_LABELS[cat_key] + rows = ["| Family | Form | Description | Members |", + "| --- | --- | --- | --- |"] + for family in sorted(families, key=lambda f: f.head): + primary = family.primary + members = ", ".join(f"`{m.mnem}`" for m in family.members) + rows.append(f"| [`{family.head}`]({_cxx_slug(family.head)}.md) " + f"| `{primary.form}` | {primary.desc} | {members} |") + body = "\n".join(rows) + return ( + f"# {label}\n\n" + f"{summary}\n\n" + f"**{len(families)} families** Β· **{sum(len(f.members) for f in families)} XML entries**.\n\n" + f"{GENERATED_BEGIN}\n\n{body}\n\n{GENERATED_END}\n" + ) + + +def render_form_page(form: str, families: list[Family], insns: list[Instruction]) -> str: + members_here = [i for i in insns if i.form == form] + bit_table = render_bit_table(form) + rows = ["| Mnemonic | Opcode | Group | Description |", + "| --- | --- | --- | --- |"] + for m in sorted(members_here, key=lambda i: i.opcode_int): + cat = _category_for(m) + slug = _cxx_slug(m.mnem) + # find the family head for the link + head = _family_head(m, {i.mnem for i in insns}) + if head not in {f.head for f in families}: + head = m.mnem + link = f"../{cat}/{_cxx_slug(head)}.md" + rows.append(f"| [`{m.mnem}`]({link}) | `0x{m.opcode_hex}` | {GROUP_NAMES[m.group]} | {m.desc} |") + body = "\n".join(rows) + title_bits = { + "I": "I β€” Immediate Branch", + "B": "B β€” Conditional Branch", + "SC": "SC β€” System Call", + "D": "D β€” Displacement (load/store and immediate ALU)", + "DS": "DS β€” Doubleword Shift (word-scaled displacement)", + "X": "X β€” Extended (10-bit extended opcode)", + "XL": "XL β€” Extended, Link (branch-to-LR/CTR, CR logical)", + "XFX": "XFX β€” Fixed (SPR/TBR/CR-field access)", + "XFL": "XFL β€” Floating Fields (mtfsf)", + "XS": "XS β€” Extended, Shift (64-bit sradi)", + "XO": "XO β€” Extended, Overflow (ALU with OE/Rc)", + "A": "A β€” Arithmetic (three-source FPU)", + "M": "M β€” Mask (rlwinm/rlwimi/rlwnm)", + "MD": "MD β€” Mask Double (rldicr/rldicl/rldic/rldimi)", + "MDS": "MDS β€” Mask Double, Shift-by-register (rldcl/rldcr)", + "DCBZ": "DCBZ β€” Cache Block Zeroing (special X variant)", + "VX": "VX β€” Vector (3-operand Altivec)", + "VA": "VA β€” Vector Arithmetic (4-operand, madd-style)", + "VC": "VC β€” Vector Compare (with Rc β†’ CR6)", + "VX128": "VX128 β€” VMX128 3-operand (register-fused)", + "VX128_1": "VX128_1 β€” VMX128 vector load/store", + "VX128_2": "VX128_2 β€” VMX128 3-operand arithmetic", + "VX128_3": "VX128_3 β€” VMX128 unary with immediate", + "VX128_4": "VX128_4 β€” VMX128 with sub-opcode selector", + "VX128_5": "VX128_5 β€” VMX128 with shift field", + "VX128_P": "VX128_P β€” VMX128 permute", + "VX128_R": "VX128_R β€” VMX128 compare (with Rc β†’ CR6)", + } + title = title_bits.get(form, form) + return ( + f"# Form `{form}` β€” {title}\n\n" + f"## Bit Layout\n\n" + f"{bit_table}\n\n" + f"## Instructions Using This Form\n\n" + f"{GENERATED_BEGIN}\n\n{body}\n\n{GENERATED_END}\n" + ) + + +# --------------------------------------------------------------------------- +# README +# --------------------------------------------------------------------------- + +def render_readme(families: dict[str, Family], insns: list[Instruction]) -> str: + by_cat: dict[str, list[Family]] = defaultdict(list) + for fam in families.values(): + by_cat[fam.category].append(fam) + + cat_rows = ["| Category | Families | XML entries | Description |", + "| --- | --- | --- | --- |"] + for cat, fams in sorted(by_cat.items()): + label, summary = CATEGORY_LABELS[cat] + cat_rows.append( + f"| [{label}](categories/{cat}.md) | {len(fams)} | " + f"{sum(len(f.members) for f in fams)} | {summary} |" + ) + + form_counts = defaultdict(int) + for i in insns: + form_counts[i.form] += 1 + form_rows = ["| Form | Count | Page |", "| --- | --- | --- |"] + for form, count in sorted(form_counts.items()): + form_rows.append(f"| `{form}` | {count} | [forms/{form}.md](forms/{form}.md) |") + + total_mnemonics = sum(len(expand_runtime_variants(i)) for i in insns) + return f"""# PowerPC Instruction Manual (Xenia Xbox 360 Subset) + +A reference for the **Xenon** PowerPC dialect used by the Xbox 360. Its +primary audience is an AI agent translating PPC assembly functions into +equivalent C. The content is derived from the two authoritative sources in +this repository β€” **xenia-canary** (C++ emulator) and **xenia-rs** (Rust +rewrite) β€” and may be deepened with the IBM AIX PowerPC reference. + +- **{len(insns)}** distinct XML-level instructions (one page each). +- **{len(families)}** instruction family pages (VMX128 siblings folded). +- **{total_mnemonics}** assembly mnemonics once runtime `Rc`/`OE`/`LK` variants are expanded β€” all resolvable through `index.json`. + +## How to use this manual (translation agent) + +1. Parse the 32-bit instruction word and identify the mnemonic. Resolve it + through [`index.json`](index.json): every assembly form (including + `add.`, `addo.`, `bclrl`, …) is a top-level key pointing at a page. +2. Open the page referenced by `index.json[mnem].page`. The page is in a + fixed format β€” see the "Page anatomy" section below. +3. Emit a C translation consistent with the page's pseudocode, the + registers-affected list, and the status-register effects. + +## Page anatomy + +Every instruction page has the same sections, in this order: + +| Section | Purpose | +| --- | --- | +| **Assembler Mnemonics** | Table of every runtime variant (Rc/OE/LK) the base XML entry covers, plus VMX128 siblings. | +| **Syntax** | Canonical assembly template with `[OE]`/`[Rc]`/`[LK]` bracketed-modifier notation. | +| **Encoding** | Form name, opcode word, primary/extended opcodes, and bit-layout table. | +| **Operands** | Every bit-field operand, its role per variant, and its meaning. | +| **Register Effects** | Unconditional vs. conditional reads and writes, per variant. | +| **Status-Register Effects** | CR0/CR1/CR6, XER[CA/OV/SO], FPSCR, VSCR updates. | +| **Operation** | PPC-style pseudocode (`RT <- …`, `EXTS(…)`, `MEM(EA, n)`). | +| **C Translation Example** | Minimal idiomatic C rendering a translator could emit. | +| **Implementation References** | Direct links into `xenia-canary/` and `xenia-rs/` with line numbers. | +| **Special Cases & Edge Conditions** | RA=0, alignment, endian byte-reverse, reservation, SPR remapping, VMX128 fusion. | +| **Related Instructions** | Sibling cross-links. | +| **IBM Reference** | Optional link to IBM AIX PPC reference for canonical pseudocode. | + +Sections between the `` and `` +sentinels are produced by [`generator/generate_manual.py`](generator/generate_manual.py) +and re-generated on every run. Sections outside the sentinels are +hand-written and preserved across re-runs. + +## Conventions + +- **Bit numbering** follows PowerPC (big-endian, bit 0 = MSB). +- **GPRs** are 64-bit. 32-bit operations operate on bits `[32:63]` and + conventionally write the low 32 bits with zero- or sign-extension into + the high 32 bits. Page pseudocode makes this explicit when it matters. +- **Vector registers** are 128-bit with **lane 0 at the most-significant + byte** (big-endian lane indexing). On x86 hosts byte-swap is applied at + load/store to preserve this invariant. +- **CR** is 8 Γ— 4-bit fields `CR0..CR7`, each `{{LT, GT, EQ, SO}}`. The record + form of arithmetic instructions writes CR0 (integer) or CR1 (FPU); the + record form of vector compare writes CR6 = `{{all-true, 0, all-false, 0}}`. +- **XER** holds `SO`, `OV`, and `CA` at bits 32, 33, 34 respectively + (PPC bit numbering), plus a 7-bit string length used by `lswi`/`stswi`. + +## Categories + +{chr(10).join(cat_rows)} + +## Forms + +{chr(10).join(form_rows)} + +## Regenerating this manual + +```bash +python3 generator/generate_manual.py +``` + +Re-running the generator is safe β€” it only rewrites sections between +`` / `` sentinels. Add +your hand-written content below the `END` marker and it will be +preserved. +""" + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="Generate PPC instruction manual") + parser.add_argument("--out", type=Path, default=MANUAL_ROOT_DEFAULT, + help="Output directory (default: ppc-manual/)") + parser.add_argument("--dry-run", action="store_true", + help="Parse + group only. Don't write any files. " + "Exit non-zero if any consistency check fails.") + parser.add_argument("--xml", type=Path, default=XML_PATH, + help="Path to ppc-instructions.xml") + args = parser.parse_args() + + insns = load_instructions(args.xml) + if len(insns) != 455: + print(f"WARNING: expected 455 XML entries, found {len(insns)}", file=sys.stderr) + + families = build_families(insns) + + # Consistency: every XML entry must belong to exactly one family. + total_members = sum(len(f.members) for f in families.values()) + assert total_members == len(insns), ( + f"family member total {total_members} β‰  XML entry count {len(insns)}" + ) + + # Consistency: every runtime mnemonic must be resolvable in the index. + index = build_index(families) + all_runtime_mnems: set[str] = set() + for i in insns: + for v in expand_runtime_variants(i): + all_runtime_mnems.add(v["mnem"]) + missing = all_runtime_mnems - set(index["instructions"]) + assert not missing, f"index is missing {len(missing)} mnemonics: {sorted(missing)[:10]}" + + # Report + print(f"XML entries: {len(insns)}") + print(f"Families: {len(families)}") + print(f"Runtime mnemonics: {len(all_runtime_mnems)}") + print(f"Index keys: {len(index['instructions'])}") + by_cat = defaultdict(int) + for fam in families.values(): + by_cat[fam.category] += 1 + print("Families by category:") + for cat, n in sorted(by_cat.items()): + print(f" {cat:8s} {n}") + + if args.dry_run: + return 0 + + rust = RustScraper(REPO_ROOT) + cxx = CxxScraper(REPO_ROOT) + + out = args.out + out.mkdir(parents=True, exist_ok=True) + + written = 0 + preserved = 0 + + # 1. Instruction pages + for family in families.values(): + cat_dir = out / family.category + cat_dir.mkdir(exist_ok=True) + page_path = cat_dir / f"{_cxx_slug(family.head)}.md" + fresh = render_page(family, rust, cxx) + if page_path.exists(): + existing = page_path.read_text(encoding="utf-8") + merged = merge_preserving_handwritten(existing, fresh) + if merged == existing: + preserved += 1 + continue + page_path.write_text(merged, encoding="utf-8") + else: + page_path.write_text(fresh, encoding="utf-8") + written += 1 + + # 2. Category overviews + cats_dir = out / "categories" + cats_dir.mkdir(exist_ok=True) + by_cat_list: dict[str, list[Family]] = defaultdict(list) + for fam in families.values(): + by_cat_list[fam.category].append(fam) + for cat, fams in by_cat_list.items(): + page = cats_dir / f"{cat}.md" + fresh = render_category_page(cat, fams) + if page.exists(): + fresh = merge_preserving_handwritten(page.read_text(encoding="utf-8"), fresh) + page.write_text(fresh, encoding="utf-8") + + # 3. Form reference pages + forms_dir = out / "forms" + forms_dir.mkdir(exist_ok=True) + present_forms = sorted({i.form for i in insns}) + for form in present_forms: + page = forms_dir / f"{form}.md" + fresh = render_form_page(form, list(families.values()), insns) + if page.exists(): + fresh = merge_preserving_handwritten(page.read_text(encoding="utf-8"), fresh) + page.write_text(fresh, encoding="utf-8") + + # 4. index.json + (out / "index.json").write_text( + json.dumps(index, indent=2, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + + # 5. README + readme = out / "README.md" + fresh_readme = render_readme(families, insns) + if readme.exists(): + fresh_readme = merge_preserving_handwritten(readme.read_text(encoding="utf-8"), fresh_readme) + readme.write_text(fresh_readme, encoding="utf-8") + + print(f"Wrote/updated {written} pages; preserved {preserved} unchanged; " + f"emitted index.json with {len(index['instructions'])} entries.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/ppc-manual/generator/rust_scraper.py b/tools/ppc-manual/generator/rust_scraper.py new file mode 100644 index 00000000..c71f0b7c --- /dev/null +++ b/tools/ppc-manual/generator/rust_scraper.py @@ -0,0 +1,184 @@ +""" +Scrapes xenia-rs source files for per-instruction references and +snippets of the interpreter semantics. + +Outputs produced for each mnemonic: + - opcode_line: line in crates/xenia-cpu/src/opcode.rs where the + PpcOpcode variant is declared (1-indexed) + - decoder_line: line in crates/xenia-cpu/src/decoder.rs where the + variant is produced from raw bits + - interp_start: line in crates/xenia-cpu/src/interpreter.rs where + the match arm `PpcOpcode:: =>` begins + - interp_end: line where the arm closes (matching brace, naive) + - interp_body: raw text of the arm body (for reviewer reference) + +The xenia-rs opcode identifier often has trailing `x` preserved +(PpcOpcode::addx) β€” this scraper matches on the XML mnemonic directly +plus a stripped alternative without trailing 'x' and the xenia-style +identifier forms. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +import re + + +@dataclass +class RustRef: + mnem: str + opcode_line: int | None = None + decoder_line: int | None = None + interp_start: int | None = None + interp_end: int | None = None + interp_body: str = "" + + +# PpcOpcode identifiers in xenia-rs match the XML mnemonic *exactly* except +# that '.' is illegal in Rust identifiers. Mnemonics ending in '.' appear as +# a trailing 'x' replacement in some cases but the codebase seems to keep the +# XML name verbatim (e.g. addic. β†’ addicx OR addic_). Check the codebase. + + +def _rust_ident(mnem: str) -> str: + """Convert XML mnemonic to the xenia-rs PpcOpcode variant name.""" + # Xenia-rs uses the same name as xenia-canary's opcode enum, which + # mirrors ppc-instructions.xml directly. '.' is replaced with 'x' in + # the opcode enum (e.g. 'addic.' β†’ 'addicx'), but the XML entry is + # already 'addic.'. We only need to handle that single case. + return mnem.replace(".", "x") + + +class RustScraper: + def __init__(self, repo_root: Path): + self.repo_root = repo_root + self.cpu_root = repo_root / "xenia-rs" / "crates" / "xenia-cpu" / "src" + self._opcode_lines = self._read_lines(self.cpu_root / "opcode.rs") + self._decoder_lines = self._read_lines(self.cpu_root / "decoder.rs") + self._interp_lines = self._read_lines(self.cpu_root / "interpreter.rs") + self._opcode_index: dict[str, int] = self._index_opcode_enum() + self._decoder_index: dict[str, int] = self._index_decoder() + self._interp_index: dict[str, tuple[int, int]] = self._index_interpreter() + + @staticmethod + def _read_lines(path: Path) -> list[str]: + if not path.is_file(): + return [] + return path.read_text(encoding="utf-8").splitlines() + + def _index_opcode_enum(self) -> dict[str, int]: + """Map rust-identifier β†’ 1-indexed line in opcode.rs. The enum uses + comma-separated identifiers (often many per line) so we extract + every identifier match inside the enum body.""" + idx: dict[str, int] = {} + token = re.compile(r"\b([A-Za-z_][A-Za-z0-9_]*)\b") + in_enum = False + for i, line in enumerate(self._opcode_lines, start=1): + if "pub enum PpcOpcode" in line: + in_enum = True + continue + if not in_enum: + continue + if line.startswith("}"): + break + stripped = line.strip() + # skip blank / comment-only lines + if not stripped or stripped.startswith("//"): + continue + # split off any trailing line comment + code = stripped.split("//", 1)[0] + for m in token.finditer(code): + idx.setdefault(m.group(1), i) + return idx + + def _index_decoder(self) -> dict[str, int]: + """Map rust-identifier β†’ 1-indexed line of its `PpcOpcode::` producer.""" + idx: dict[str, int] = {} + pat = re.compile(r"PpcOpcode::([A-Za-z_][A-Za-z0-9_]*)") + for i, line in enumerate(self._decoder_lines, start=1): + for m in pat.finditer(line): + name = m.group(1) + # keep the FIRST occurrence (the match-arm line where it's + # produced, not any later references) + idx.setdefault(name, i) + return idx + + def _index_interpreter(self) -> dict[str, tuple[int, int]]: + """Map rust-identifier β†’ (start, end) lines of the match arm. + + An arm starts at `PpcOpcode::` and ends at the closing `}` + at the same indentation level. We accept multi-variant arms of + the form `PpcOpcode::a | PpcOpcode::b => {` by recording the same + (start, end) for every named variant. + """ + arm_header = re.compile(r"^(\s*)((?:PpcOpcode::[A-Za-z_][A-Za-z0-9_]*\s*\|\s*)*PpcOpcode::[A-Za-z_][A-Za-z0-9_]*)\s*=>\s*\{?\s*$") + # Some arms use no leading whitespace quirks β€” adjusted regex: + arm_header = re.compile( + r"^(\s*)" # indent + r"((?:PpcOpcode::[A-Za-z_][A-Za-z0-9_]*" # first variant + r"(?:\s*\|\s*PpcOpcode::[A-Za-z_][A-Za-z0-9_]*)*))" # more variants + r"\s*=>\s*\{?\s*$" + ) + var_re = re.compile(r"PpcOpcode::([A-Za-z_][A-Za-z0-9_]*)") + idx: dict[str, tuple[int, int]] = {} + i = 0 + n = len(self._interp_lines) + while i < n: + line = self._interp_lines[i] + m = arm_header.match(line) + if not m: + i += 1 + continue + indent = m.group(1) + names = var_re.findall(m.group(2)) + # Find the closing '}' at the same indentation. The arm body + # starts on line i (which ends with '{') and ends at a line + # whose content (after `indent`) is '}' (with optional trailing + # comma). + start = i + 1 # 1-indexed + end = start + j = i + 1 + depth = 1 if line.rstrip().endswith("{") else 0 + if depth == 0: + # Single-expression arm like `... => foo(),` β€” treat the line + # itself as start=end. + end = start + j = i + 1 + else: + while j < n: + l = self._interp_lines[j] + # A naive brace counter suffices for this codebase β€” the + # interpreter arms use balanced braces and no string + # literals containing stray braces. + depth += l.count("{") - l.count("}") + if depth == 0: + end = j + 1 # 1-indexed + break + j += 1 + for name in names: + idx.setdefault(name, (start, end)) + i = j + 1 + return idx + + def lookup(self, mnem: str) -> RustRef: + ident = _rust_ident(mnem) + ref = RustRef(mnem=mnem) + ref.opcode_line = self._opcode_index.get(ident) + ref.decoder_line = self._decoder_index.get(ident) + rng = self._interp_index.get(ident) + if rng: + ref.interp_start, ref.interp_end = rng + body = "\n".join(self._interp_lines[ref.interp_start - 1: ref.interp_end]) + ref.interp_body = body + return ref + + +if __name__ == "__main__": + root = Path(__file__).resolve().parent.parent.parent + s = RustScraper(root) + for m in ("addx", "addic.", "lwz", "bclrx", "mfspr", "stvx", "vaddfp", + "vaddfp128", "faddx", "lvsl"): + r = s.lookup(m) + print(f"{m:12s} opcode@{r.opcode_line} decoder@{r.decoder_line} " + f"interp@{r.interp_start}-{r.interp_end}") diff --git a/tools/ppc-manual/generator/xml_model.py b/tools/ppc-manual/generator/xml_model.py new file mode 100644 index 00000000..e4c1182e --- /dev/null +++ b/tools/ppc-manual/generator/xml_model.py @@ -0,0 +1,231 @@ +""" +Parses xenia-canary's tools/ppc-instructions.xml into typed records. + +The XML is the authoritative catalogue of Xbox 360 PPC instructions +(455 entries). Each entry carries: + - mnem: mnemonic (e.g. "addx", "lwzu", "vaddfp128") + - opcode: 32-bit hex encoding (primary + extended opcode bits) + - form: instruction format (XO, D, DS, X, XL, XFX, ..., VX, VX128_*) + - group: functional group (i=integer, m=memory, b=branch, + c=control, f=fpu, v=vmx) + - desc: short human-readable description + - / fields with optional conditional="true" flag + - : template string used by the canary disassembler +""" + +from __future__ import annotations + +import xml.etree.ElementTree as ET +from dataclasses import dataclass, field +from pathlib import Path + + +GROUP_NAMES = { + "i": "integer", + "m": "memory", + "b": "branch", + "c": "control", + "f": "fpu", + "v": "vmx", +} + +# Maps the short group code to the manual's on-disk category directory. +# VMX entries are split by form in generate_manual.py (VX128_* β†’ vmx128/). +GROUP_TO_DIR = { + "i": "alu", + "m": "memory", + "b": "branch", + "c": "control", + "f": "fpu", + "v": "vmx", +} + + +@dataclass +class Field: + name: str + conditional: bool = False + + +@dataclass +class Instruction: + mnem: str + opcode_hex: str # lowercase, no "0x" prefix + form: str + group: str # one-letter code + desc: str + sync: bool + reads: list[Field] = field(default_factory=list) + writes: list[Field] = field(default_factory=list) + disasm: str = "" + + @property + def opcode_int(self) -> int: + return int(self.opcode_hex, 16) + + @property + def primary_opcode(self) -> int: + # PPC: bits 0-5 of a big-endian 32-bit word are the top 6 bits. + return (self.opcode_int >> 26) & 0x3F + + @property + def extended_opcode(self) -> int | None: + """Best-effort extended opcode extraction by form. + Returns None for forms where "extended opcode" is not meaningful + (I, B, D, DS, SC, M, MD, MDS, DCBZ) β€” those pages will omit it.""" + code = self.opcode_int + form = self.form + if form in ("X", "XL", "XFX", "XFL", "XS", "DCBZ"): + return (code >> 1) & 0x3FF # bits 21-30 + if form == "XO": + return (code >> 1) & 0x1FF # bits 22-30 (bit 21 = OE) + if form == "A": + return (code >> 1) & 0x1F # bits 26-30 + if form in ("VX", "VX128_2", "VX128_5"): + return code & 0x7FF # bits 21-31 + if form == "VA": + return code & 0x3F # bits 26-31 + if form == "VC": + return code & 0x3FF # bits 22-31 (bit 21 = Rc) + if form in ("VX128", "VX128_R"): + # complex split; best-effort β€” not used for lookup, just display + return code & 0x7FF + if form in ("VX128_1", "VX128_3", "VX128_4", "VX128_P"): + return code & 0x7FF + return None + + @property + def group_name(self) -> str: + return GROUP_NAMES.get(self.group, "unknown") + + @property + def has_rc(self) -> bool: + """Does this instruction have a runtime Rc bit (record form)?""" + return any(w.name == "CR" and w.conditional for w in self.writes) + + @property + def has_oe(self) -> bool: + """Does this instruction have a runtime OE bit (overflow enable)?""" + return any(w.name == "OE" and w.conditional for w in self.writes) + + @property + def has_lk(self) -> bool: + """Does this instruction have a runtime LK bit (branch link)?""" + return any(r.name == "LK" for r in self.reads) + + @property + def rc_is_mandatory(self) -> bool: + """Instructions like `addic.` where CR is written unconditionally.""" + return any(w.name == "CR" and not w.conditional for w in self.writes) + + +def load_instructions(xml_path: Path | str) -> list[Instruction]: + tree = ET.parse(str(xml_path)) + root = tree.getroot() + insns: list[Instruction] = [] + for node in root.iter("insn"): + reads = [Field(x.get("field", ""), x.get("conditional") == "true") + for x in node.findall("in")] + writes = [Field(x.get("field", ""), x.get("conditional") == "true") + for x in node.findall("out")] + disasm_node = node.find("disasm") + disasm = (disasm_node.text or "").strip() if disasm_node is not None else "" + insns.append(Instruction( + mnem=node.get("mnem", ""), + opcode_hex=node.get("opcode", "").lower(), + form=node.get("form", ""), + group=node.get("group", ""), + desc=node.get("desc", ""), + sync=node.get("sync") == "true", + reads=reads, + writes=writes, + disasm=disasm, + )) + return insns + + +def expand_runtime_variants(insn: Instruction) -> list[dict]: + """ + Return the set of concrete assembly mnemonics this XML entry represents + under different runtime flag settings. Flags: Rc (record) β†’ append '.', + OE (overflow) β†’ insert 'o' before any '.', LK (link) β†’ append 'l'. + + The display mnemonic is derived from the XML mnem by stripping a trailing + 'x' if present (xenia uses trailing x to mark X/XO form entries; the + assembly mnemonic omits it). Mnemonics ending in '.' or digits are kept. + """ + raw = insn.mnem + # Xenia convention: trailing 'x' on XO/X/A/M/MD/MDS/XFL/XS/VX/VA form + # marks "extended form" but is dropped in assembly display. + # Keep trailing x for: memory indexed forms (lbzx, lwzx, ...), which are + # separate XML entries β€” those should not have their x stripped. + # We use the group code to decide: group=i / group=f / group=c / + # form family VX*/VA/VC β†’ strip trailing x. group=m / group=b β†’ keep. + def strip_x(m: str) -> str: + if not m.endswith("x"): + return m + # Memory mnemonics: 'x' is part of the assembly name (indexed form). + if insn.group == "m": + return m + # Branch: bx/bcx/bcctrx/bclrx β€” xenia's trailing x, strip. + return m[:-1] + + base = strip_x(raw) + variants: list[dict] = [] + + if insn.rc_is_mandatory: + # e.g. addic. β€” already has the dot baked in + variants.append({"mnem": raw, "flags": {}, "is_primary": True}) + return variants + + has_rc = insn.has_rc + has_oe = insn.has_oe + has_lk = insn.has_lk + + if not (has_rc or has_oe or has_lk): + variants.append({"mnem": base, "flags": {}, "is_primary": True}) + return variants + + # Enumerate all combinations of the runtime flags that apply. + def insert_o(name: str) -> str: + # 'addo' / 'addo.' β€” insert 'o' before any trailing '.' + if name.endswith("."): + return name[:-1] + "o." + return name + "o" + + combos: list[tuple[str, dict]] = [(base, {})] + if has_oe: + combos += [(insert_o(n), {**f, "OE": 1}) for (n, f) in combos] + if has_rc: + combos += [(n + ".", {**f, "Rc": 1}) for (n, f) in combos] + if has_lk: + # Branch link: append 'l' AFTER any trailing dot? PPC convention: + # bl, bcl, bclrl, bcctrl β€” 'l' is appended at the end of the base + # mnemonic with no dot (branches don't have Rc). Add the l-variant + # only when OE/Rc weren't applied. + combos += [(n + "l", {**f, "LK": 1}) for (n, f) in combos if "Rc" not in f and "OE" not in f] + + for i, (name, flags) in enumerate(combos): + variants.append({"mnem": name, "flags": flags, "is_primary": i == 0}) + return variants + + +if __name__ == "__main__": + # Smoke test: print summary of what we loaded. + import sys + repo_root = Path(__file__).resolve().parent.parent.parent + xml = repo_root / "xenia-canary" / "tools" / "ppc-instructions.xml" + insns = load_instructions(xml) + print(f"Loaded {len(insns)} instructions from {xml}") + total_mnems = sum(len(expand_runtime_variants(i)) for i in insns) + print(f"Total runtime-expanded mnemonics: {total_mnems}") + # show 5 examples + for mnem in ("addx", "lwz", "bclrx", "mfspr", "stvx", "vaddfp", "vaddfp128", "addic."): + for i in insns: + if i.mnem == mnem: + vs = expand_runtime_variants(i) + print(f" {mnem:12s} form={i.form:7s} group={i.group} " + f"variants={[v['mnem'] for v in vs]}") + break + else: + print(f" {mnem:12s} NOT FOUND") diff --git a/tools/ppc-manual/index.json b/tools/ppc-manual/index.json new file mode 100644 index 00000000..f6c9f02e --- /dev/null +++ b/tools/ppc-manual/index.json @@ -0,0 +1,19128 @@ +{ + "version": "1.0", + "generator": "ppc-manual/generator/generate_manual.py", + "instruction_count": 455, + "mnemonic_count": 598, + "family_count": 350, + "categories": { + "alu": { + "page": "categories/alu.md", + "count": 70, + "label": "Integer ALU", + "summary": "Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition." + }, + "branch": { + "page": "categories/branch.md", + "count": 9, + "label": "Branch & System", + "summary": "Unconditional / conditional branches, branch to LR/CTR, traps, system call." + }, + "control": { + "page": "categories/control.md", + "count": 26, + "label": "Control / CR / SPR", + "summary": "Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio)." + }, + "fpu": { + "page": "categories/fpu.md", + "count": 33, + "label": "Floating-Point", + "summary": "IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves." + }, + "memory": { + "page": "categories/memory.md", + "count": 112, + "label": "Memory", + "summary": "Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx." + }, + "vmx": { + "page": "categories/vmx.md", + "count": 193, + "label": "VMX (Altivec)", + "summary": "128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers." + }, + "vmx128": { + "page": "categories/vmx128.md", + "count": 12, + "label": "VMX128", + "summary": "Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields." + } + }, + "forms": { + "A": { + "page": "forms/A.md", + "count": 21 + }, + "B": { + "page": "forms/B.md", + "count": 1 + }, + "D": { + "page": "forms/D.md", + "count": 40 + }, + "DCBZ": { + "page": "forms/DCBZ.md", + "count": 2 + }, + "DS": { + "page": "forms/DS.md", + "count": 5 + }, + "I": { + "page": "forms/I.md", + "count": 1 + }, + "M": { + "page": "forms/M.md", + "count": 3 + }, + "MD": { + "page": "forms/MD.md", + "count": 4 + }, + "MDS": { + "page": "forms/MDS.md", + "count": 2 + }, + "SC": { + "page": "forms/SC.md", + "count": 1 + }, + "VA": { + "page": "forms/VA.md", + "count": 14 + }, + "VC": { + "page": "forms/VC.md", + "count": 13 + }, + "VX": { + "page": "forms/VX.md", + "count": 117 + }, + "VX128": { + "page": "forms/VX128.md", + "count": 34 + }, + "VX128_1": { + "page": "forms/VX128_1.md", + "count": 16 + }, + "VX128_2": { + "page": "forms/VX128_2.md", + "count": 1 + }, + "VX128_3": { + "page": "forms/VX128_3.md", + "count": 15 + }, + "VX128_4": { + "page": "forms/VX128_4.md", + "count": 2 + }, + "VX128_5": { + "page": "forms/VX128_5.md", + "count": 1 + }, + "VX128_P": { + "page": "forms/VX128_P.md", + "count": 1 + }, + "VX128_R": { + "page": "forms/VX128_R.md", + "count": 5 + }, + "X": { + "page": "forms/X.md", + "count": 117 + }, + "XFL": { + "page": "forms/XFL.md", + "count": 1 + }, + "XFX": { + "page": "forms/XFX.md", + "count": 4 + }, + "XL": { + "page": "forms/XL.md", + "count": 12 + }, + "XO": { + "page": "forms/XO.md", + "count": 21 + }, + "XS": { + "page": "forms/XS.md", + "count": 1 + } + }, + "instructions": { + "add": { + "page": "alu/addx.md", + "family": "addx", + "xml_mnem": "addx", + "opcode_hex": "0x7C000214", + "primary_opcode": 31, + "extended_opcode": 266, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Add", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "add.": { + "page": "alu/addx.md", + "family": "addx", + "variant_of": "add", + "xml_mnem": "addx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "addc": { + "page": "alu/addcx.md", + "family": "addcx", + "xml_mnem": "addcx", + "opcode_hex": "0x7C000014", + "primary_opcode": 31, + "extended_opcode": 10, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Add Carrying", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addc.": { + "page": "alu/addcx.md", + "family": "addcx", + "variant_of": "addc", + "xml_mnem": "addcx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "addco": { + "page": "alu/addcx.md", + "family": "addcx", + "variant_of": "addc", + "xml_mnem": "addcx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "addco.": { + "page": "alu/addcx.md", + "family": "addcx", + "variant_of": "addc", + "xml_mnem": "addcx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "adde": { + "page": "alu/addex.md", + "family": "addex", + "xml_mnem": "addex", + "opcode_hex": "0x7C000114", + "primary_opcode": 31, + "extended_opcode": 138, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Add Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "adde.": { + "page": "alu/addex.md", + "family": "addex", + "variant_of": "adde", + "xml_mnem": "addex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "addeo": { + "page": "alu/addex.md", + "family": "addex", + "variant_of": "adde", + "xml_mnem": "addex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "addeo.": { + "page": "alu/addex.md", + "family": "addex", + "variant_of": "adde", + "xml_mnem": "addex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "addi": { + "page": "alu/addi.md", + "family": "addi", + "xml_mnem": "addi", + "opcode_hex": "0x38000000", + "primary_opcode": 14, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Add Immediate", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addic": { + "page": "alu/addic.md", + "family": "addic", + "xml_mnem": "addic", + "opcode_hex": "0x30000000", + "primary_opcode": 12, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Add Immediate Carrying", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addic.": { + "page": "alu/addicx.md", + "family": "addic.", + "xml_mnem": "addic.", + "opcode_hex": "0x34000000", + "primary_opcode": 13, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Add Immediate Carrying and Record", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + }, + { + "field": "CR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": true + }, + "is_primary": true, + "flags": {} + }, + "addis": { + "page": "alu/addis.md", + "family": "addis", + "xml_mnem": "addis", + "opcode_hex": "0x3C000000", + "primary_opcode": 15, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Add Immediate Shifted", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addme": { + "page": "alu/addmex.md", + "family": "addmex", + "xml_mnem": "addmex", + "opcode_hex": "0x7C0001D4", + "primary_opcode": 31, + "extended_opcode": 234, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Add to Minus One Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addme.": { + "page": "alu/addmex.md", + "family": "addmex", + "variant_of": "addme", + "xml_mnem": "addmex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "addmeo": { + "page": "alu/addmex.md", + "family": "addmex", + "variant_of": "addme", + "xml_mnem": "addmex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "addmeo.": { + "page": "alu/addmex.md", + "family": "addmex", + "variant_of": "addme", + "xml_mnem": "addmex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "addo": { + "page": "alu/addx.md", + "family": "addx", + "variant_of": "add", + "xml_mnem": "addx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "addo.": { + "page": "alu/addx.md", + "family": "addx", + "variant_of": "add", + "xml_mnem": "addx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "addze": { + "page": "alu/addzex.md", + "family": "addzex", + "xml_mnem": "addzex", + "opcode_hex": "0x7C000194", + "primary_opcode": 31, + "extended_opcode": 202, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Add to Zero Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "addze.": { + "page": "alu/addzex.md", + "family": "addzex", + "variant_of": "addze", + "xml_mnem": "addzex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "addzeo": { + "page": "alu/addzex.md", + "family": "addzex", + "variant_of": "addze", + "xml_mnem": "addzex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "addzeo.": { + "page": "alu/addzex.md", + "family": "addzex", + "variant_of": "addze", + "xml_mnem": "addzex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "and": { + "page": "alu/andx.md", + "family": "andx", + "xml_mnem": "andx", + "opcode_hex": "0x7C000038", + "primary_opcode": 31, + "extended_opcode": 28, + "form": "X", + "group": "integer", + "category": "alu", + "description": "AND", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "and.": { + "page": "alu/andx.md", + "family": "andx", + "variant_of": "and", + "xml_mnem": "andx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "andc": { + "page": "alu/andcx.md", + "family": "andcx", + "xml_mnem": "andcx", + "opcode_hex": "0x7C000078", + "primary_opcode": 31, + "extended_opcode": 60, + "form": "X", + "group": "integer", + "category": "alu", + "description": "AND with Complement", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "andc.": { + "page": "alu/andcx.md", + "family": "andcx", + "variant_of": "andc", + "xml_mnem": "andcx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "andi.": { + "page": "alu/andix.md", + "family": "andi.", + "xml_mnem": "andi.", + "opcode_hex": "0x70000000", + "primary_opcode": 28, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "AND Immediate", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": true + }, + "is_primary": true, + "flags": {} + }, + "andis.": { + "page": "alu/andisx.md", + "family": "andis.", + "xml_mnem": "andis.", + "opcode_hex": "0x74000000", + "primary_opcode": 29, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "AND Immediate Shifted", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": true + }, + "is_primary": true, + "flags": {} + }, + "b": { + "page": "branch/bx.md", + "family": "bx", + "xml_mnem": "bx", + "opcode_hex": "0x48000000", + "primary_opcode": 18, + "extended_opcode": null, + "form": "I", + "group": "branch", + "category": "branch", + "description": "Branch", + "sync": true, + "reads": [ + { + "field": "LK", + "conditional": false + }, + { + "field": "AA", + "conditional": false + }, + { + "field": "ADDR", + "conditional": false + } + ], + "writes": [ + { + "field": "LR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": true, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "bc": { + "page": "branch/bcx.md", + "family": "bcx", + "xml_mnem": "bcx", + "opcode_hex": "0x40000000", + "primary_opcode": 16, + "extended_opcode": null, + "form": "B", + "group": "branch", + "category": "branch", + "description": "Branch Conditional", + "sync": true, + "reads": [ + { + "field": "LK", + "conditional": false + }, + { + "field": "AA", + "conditional": false + }, + { + "field": "BO", + "conditional": false + }, + { + "field": "BI", + "conditional": false + }, + { + "field": "ADDR", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CTR", + "conditional": true + } + ], + "writes": [ + { + "field": "CTR", + "conditional": true + }, + { + "field": "LR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": true, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "bcctr": { + "page": "branch/bcctrx.md", + "family": "bcctrx", + "xml_mnem": "bcctrx", + "opcode_hex": "0x4C000420", + "primary_opcode": 19, + "extended_opcode": 528, + "form": "XL", + "group": "branch", + "category": "branch", + "description": "Branch Conditional to Count Register", + "sync": true, + "reads": [ + { + "field": "LK", + "conditional": false + }, + { + "field": "BO", + "conditional": false + }, + { + "field": "BI", + "conditional": false + }, + { + "field": "CR", + "conditional": false + }, + { + "field": "CTR", + "conditional": false + } + ], + "writes": [ + { + "field": "LR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": true, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "bcctrl": { + "page": "branch/bcctrx.md", + "family": "bcctrx", + "variant_of": "bcctr", + "xml_mnem": "bcctrx", + "flags": { + "LK": 1 + }, + "category": "branch" + }, + "bcl": { + "page": "branch/bcx.md", + "family": "bcx", + "variant_of": "bc", + "xml_mnem": "bcx", + "flags": { + "LK": 1 + }, + "category": "branch" + }, + "bclr": { + "page": "branch/bclrx.md", + "family": "bclrx", + "xml_mnem": "bclrx", + "opcode_hex": "0x4C000020", + "primary_opcode": 19, + "extended_opcode": 16, + "form": "XL", + "group": "branch", + "category": "branch", + "description": "Branch Conditional to Link Register", + "sync": true, + "reads": [ + { + "field": "LK", + "conditional": false + }, + { + "field": "BO", + "conditional": false + }, + { + "field": "BI", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CTR", + "conditional": true + } + ], + "writes": [ + { + "field": "CTR", + "conditional": true + }, + { + "field": "LR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": true, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "bclrl": { + "page": "branch/bclrx.md", + "family": "bclrx", + "variant_of": "bclr", + "xml_mnem": "bclrx", + "flags": { + "LK": 1 + }, + "category": "branch" + }, + "bl": { + "page": "branch/bx.md", + "family": "bx", + "variant_of": "b", + "xml_mnem": "bx", + "flags": { + "LK": 1 + }, + "category": "branch" + }, + "cmp": { + "page": "alu/cmp.md", + "family": "cmp", + "xml_mnem": "cmp", + "opcode_hex": "0x7C000000", + "primary_opcode": 31, + "extended_opcode": 0, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Compare", + "sync": false, + "reads": [ + { + "field": "L", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cmpi": { + "page": "alu/cmpi.md", + "family": "cmpi", + "xml_mnem": "cmpi", + "opcode_hex": "0x2C000000", + "primary_opcode": 11, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Compare Immediate", + "sync": false, + "reads": [ + { + "field": "L", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cmpl": { + "page": "alu/cmpl.md", + "family": "cmpl", + "xml_mnem": "cmpl", + "opcode_hex": "0x7C000040", + "primary_opcode": 31, + "extended_opcode": 32, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Compare Logical", + "sync": false, + "reads": [ + { + "field": "L", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cmpli": { + "page": "alu/cmpli.md", + "family": "cmpli", + "xml_mnem": "cmpli", + "opcode_hex": "0x28000000", + "primary_opcode": 10, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Compare Logical Immediate", + "sync": false, + "reads": [ + { + "field": "L", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cntlzd": { + "page": "alu/cntlzdx.md", + "family": "cntlzdx", + "xml_mnem": "cntlzdx", + "opcode_hex": "0x7C000074", + "primary_opcode": 31, + "extended_opcode": 58, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Count Leading Zeros Doubleword", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cntlzd.": { + "page": "alu/cntlzdx.md", + "family": "cntlzdx", + "variant_of": "cntlzd", + "xml_mnem": "cntlzdx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "cntlzw": { + "page": "alu/cntlzwx.md", + "family": "cntlzwx", + "xml_mnem": "cntlzwx", + "opcode_hex": "0x7C000034", + "primary_opcode": 31, + "extended_opcode": 26, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Count Leading Zeros Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cntlzw.": { + "page": "alu/cntlzwx.md", + "family": "cntlzwx", + "variant_of": "cntlzw", + "xml_mnem": "cntlzwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "crand": { + "page": "control/crand.md", + "family": "crand", + "xml_mnem": "crand", + "opcode_hex": "0x4C000202", + "primary_opcode": 19, + "extended_opcode": 257, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register AND", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "crandc": { + "page": "control/crandc.md", + "family": "crandc", + "xml_mnem": "crandc", + "opcode_hex": "0x4C000102", + "primary_opcode": 19, + "extended_opcode": 129, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register AND with Complement", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "creqv": { + "page": "control/creqv.md", + "family": "creqv", + "xml_mnem": "creqv", + "opcode_hex": "0x4C000242", + "primary_opcode": 19, + "extended_opcode": 289, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register Equivalent", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "crnand": { + "page": "control/crnand.md", + "family": "crnand", + "xml_mnem": "crnand", + "opcode_hex": "0x4C0001C2", + "primary_opcode": 19, + "extended_opcode": 225, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register NAND", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "crnor": { + "page": "control/crnor.md", + "family": "crnor", + "xml_mnem": "crnor", + "opcode_hex": "0x4C000042", + "primary_opcode": 19, + "extended_opcode": 33, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register NOR", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "cror": { + "page": "control/cror.md", + "family": "cror", + "xml_mnem": "cror", + "opcode_hex": "0x4C000382", + "primary_opcode": 19, + "extended_opcode": 449, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register OR", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "crorc": { + "page": "control/crorc.md", + "family": "crorc", + "xml_mnem": "crorc", + "opcode_hex": "0x4C000342", + "primary_opcode": 19, + "extended_opcode": 417, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register OR with Complement", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "crxor": { + "page": "control/crxor.md", + "family": "crxor", + "xml_mnem": "crxor", + "opcode_hex": "0x4C000182", + "primary_opcode": 19, + "extended_opcode": 193, + "form": "XL", + "group": "control", + "category": "control", + "description": "Condition Register XOR", + "sync": false, + "reads": [ + { + "field": "CRBA", + "conditional": false + }, + { + "field": "CRBB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRBD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbf": { + "page": "memory/dcbf.md", + "family": "dcbf", + "xml_mnem": "dcbf", + "opcode_hex": "0x7C0000AC", + "primary_opcode": 31, + "extended_opcode": 86, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Flush", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbi": { + "page": "memory/dcbi.md", + "family": "dcbi", + "xml_mnem": "dcbi", + "opcode_hex": "0x7C0003AC", + "primary_opcode": 31, + "extended_opcode": 470, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Invalidate", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbst": { + "page": "memory/dcbst.md", + "family": "dcbst", + "xml_mnem": "dcbst", + "opcode_hex": "0x7C00006C", + "primary_opcode": 31, + "extended_opcode": 54, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Store", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbt": { + "page": "memory/dcbt.md", + "family": "dcbt", + "xml_mnem": "dcbt", + "opcode_hex": "0x7C00022C", + "primary_opcode": 31, + "extended_opcode": 278, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Touch", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbtst": { + "page": "memory/dcbtst.md", + "family": "dcbtst", + "xml_mnem": "dcbtst", + "opcode_hex": "0x7C0001EC", + "primary_opcode": 31, + "extended_opcode": 246, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Touch for Store", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbz": { + "page": "memory/dcbz.md", + "family": "dcbz", + "xml_mnem": "dcbz", + "opcode_hex": "0x7C0007EC", + "primary_opcode": 31, + "extended_opcode": 1014, + "form": "DCBZ", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Clear to Zero", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "dcbz128": { + "page": "memory/dcbz.md", + "family": "dcbz", + "xml_mnem": "dcbz128", + "opcode_hex": "0x7C2007EC", + "primary_opcode": 31, + "extended_opcode": 1014, + "form": "DCBZ", + "group": "memory", + "category": "memory", + "description": "Data Cache Block Clear to Zero 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "divd": { + "page": "alu/divdx.md", + "family": "divdx", + "xml_mnem": "divdx", + "opcode_hex": "0x7C0003D2", + "primary_opcode": 31, + "extended_opcode": 489, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Divide Doubleword", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "divd.": { + "page": "alu/divdx.md", + "family": "divdx", + "variant_of": "divd", + "xml_mnem": "divdx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "divdo": { + "page": "alu/divdx.md", + "family": "divdx", + "variant_of": "divd", + "xml_mnem": "divdx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "divdo.": { + "page": "alu/divdx.md", + "family": "divdx", + "variant_of": "divd", + "xml_mnem": "divdx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "divdu": { + "page": "alu/divdux.md", + "family": "divdux", + "xml_mnem": "divdux", + "opcode_hex": "0x7C000392", + "primary_opcode": 31, + "extended_opcode": 457, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Divide Doubleword Unsigned", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "divdu.": { + "page": "alu/divdux.md", + "family": "divdux", + "variant_of": "divdu", + "xml_mnem": "divdux", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "divduo": { + "page": "alu/divdux.md", + "family": "divdux", + "variant_of": "divdu", + "xml_mnem": "divdux", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "divduo.": { + "page": "alu/divdux.md", + "family": "divdux", + "variant_of": "divdu", + "xml_mnem": "divdux", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "divw": { + "page": "alu/divwx.md", + "family": "divwx", + "xml_mnem": "divwx", + "opcode_hex": "0x7C0003D6", + "primary_opcode": 31, + "extended_opcode": 491, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Divide Word", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "divw.": { + "page": "alu/divwx.md", + "family": "divwx", + "variant_of": "divw", + "xml_mnem": "divwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "divwo": { + "page": "alu/divwx.md", + "family": "divwx", + "variant_of": "divw", + "xml_mnem": "divwx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "divwo.": { + "page": "alu/divwx.md", + "family": "divwx", + "variant_of": "divw", + "xml_mnem": "divwx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "divwu": { + "page": "alu/divwux.md", + "family": "divwux", + "xml_mnem": "divwux", + "opcode_hex": "0x7C000396", + "primary_opcode": 31, + "extended_opcode": 459, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Divide Word Unsigned", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "divwu.": { + "page": "alu/divwux.md", + "family": "divwux", + "variant_of": "divwu", + "xml_mnem": "divwux", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "divwuo": { + "page": "alu/divwux.md", + "family": "divwux", + "variant_of": "divwu", + "xml_mnem": "divwux", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "divwuo.": { + "page": "alu/divwux.md", + "family": "divwux", + "variant_of": "divwu", + "xml_mnem": "divwux", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "eieio": { + "page": "alu/eieio.md", + "family": "eieio", + "xml_mnem": "eieio", + "opcode_hex": "0x7C0006AC", + "primary_opcode": 31, + "extended_opcode": 854, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Enforce In-Order Execution of I/O", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "eqv": { + "page": "alu/eqvx.md", + "family": "eqvx", + "xml_mnem": "eqvx", + "opcode_hex": "0x7C000238", + "primary_opcode": 31, + "extended_opcode": 284, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Equivalent", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "eqv.": { + "page": "alu/eqvx.md", + "family": "eqvx", + "variant_of": "eqv", + "xml_mnem": "eqvx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "extsb": { + "page": "alu/extsbx.md", + "family": "extsbx", + "xml_mnem": "extsbx", + "opcode_hex": "0x7C000774", + "primary_opcode": 31, + "extended_opcode": 954, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Extend Sign Byte", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "extsb.": { + "page": "alu/extsbx.md", + "family": "extsbx", + "variant_of": "extsb", + "xml_mnem": "extsbx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "extsh": { + "page": "alu/extshx.md", + "family": "extshx", + "xml_mnem": "extshx", + "opcode_hex": "0x7C000734", + "primary_opcode": 31, + "extended_opcode": 922, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Extend Sign Half Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "extsh.": { + "page": "alu/extshx.md", + "family": "extshx", + "variant_of": "extsh", + "xml_mnem": "extshx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "extsw": { + "page": "alu/extswx.md", + "family": "extswx", + "xml_mnem": "extswx", + "opcode_hex": "0x7C0007B4", + "primary_opcode": 31, + "extended_opcode": 986, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Extend Sign Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "extsw.": { + "page": "alu/extswx.md", + "family": "extswx", + "variant_of": "extsw", + "xml_mnem": "extswx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "fabs": { + "page": "fpu/fabsx.md", + "family": "fabsx", + "xml_mnem": "fabsx", + "opcode_hex": "0xFC000210", + "primary_opcode": 63, + "extended_opcode": 264, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Absolute Value", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fabs.": { + "page": "fpu/fabsx.md", + "family": "fabsx", + "variant_of": "fabs", + "xml_mnem": "fabsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fadd": { + "page": "fpu/faddx.md", + "family": "faddx", + "xml_mnem": "faddx", + "opcode_hex": "0xFC00002A", + "primary_opcode": 63, + "extended_opcode": 21, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Add", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fadd.": { + "page": "fpu/faddx.md", + "family": "faddx", + "variant_of": "fadd", + "xml_mnem": "faddx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fadds": { + "page": "fpu/faddsx.md", + "family": "faddsx", + "xml_mnem": "faddsx", + "opcode_hex": "0xEC00002A", + "primary_opcode": 59, + "extended_opcode": 21, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Add Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fadds.": { + "page": "fpu/faddsx.md", + "family": "faddsx", + "variant_of": "fadds", + "xml_mnem": "faddsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fcfid": { + "page": "fpu/fcfidx.md", + "family": "fcfidx", + "xml_mnem": "fcfidx", + "opcode_hex": "0xFC00069C", + "primary_opcode": 63, + "extended_opcode": 846, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Convert From Integer Doubleword", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fcfid.": { + "page": "fpu/fcfidx.md", + "family": "fcfidx", + "variant_of": "fcfid", + "xml_mnem": "fcfidx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fcmpo": { + "page": "fpu/fcmpo.md", + "family": "fcmpo", + "xml_mnem": "fcmpo", + "opcode_hex": "0xFC000040", + "primary_opcode": 63, + "extended_opcode": 32, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Compare Ordered", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fcmpu": { + "page": "fpu/fcmpu.md", + "family": "fcmpu", + "xml_mnem": "fcmpu", + "opcode_hex": "0xFC000000", + "primary_opcode": 63, + "extended_opcode": 0, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Compare Unordered", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fctid": { + "page": "fpu/fctidx.md", + "family": "fctidx", + "xml_mnem": "fctidx", + "opcode_hex": "0xFC00065C", + "primary_opcode": 63, + "extended_opcode": 814, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Convert to Integer Doubleword", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fctid.": { + "page": "fpu/fctidx.md", + "family": "fctidx", + "variant_of": "fctid", + "xml_mnem": "fctidx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fctidz": { + "page": "fpu/fctidzx.md", + "family": "fctidzx", + "xml_mnem": "fctidzx", + "opcode_hex": "0xFC00065E", + "primary_opcode": 63, + "extended_opcode": 815, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Convert to Integer Doubleword with Round Toward Zero", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fctidz.": { + "page": "fpu/fctidzx.md", + "family": "fctidzx", + "variant_of": "fctidz", + "xml_mnem": "fctidzx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fctiw": { + "page": "fpu/fctiwx.md", + "family": "fctiwx", + "xml_mnem": "fctiwx", + "opcode_hex": "0xFC00001C", + "primary_opcode": 63, + "extended_opcode": 14, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Convert to Integer Word", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fctiw.": { + "page": "fpu/fctiwx.md", + "family": "fctiwx", + "variant_of": "fctiw", + "xml_mnem": "fctiwx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fctiwz": { + "page": "fpu/fctiwzx.md", + "family": "fctiwzx", + "xml_mnem": "fctiwzx", + "opcode_hex": "0xFC00001E", + "primary_opcode": 63, + "extended_opcode": 15, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Convert to Integer Word with Round Toward Zero", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fctiwz.": { + "page": "fpu/fctiwzx.md", + "family": "fctiwzx", + "variant_of": "fctiwz", + "xml_mnem": "fctiwzx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fdiv": { + "page": "fpu/fdivx.md", + "family": "fdivx", + "xml_mnem": "fdivx", + "opcode_hex": "0xFC000024", + "primary_opcode": 63, + "extended_opcode": 18, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Divide", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fdiv.": { + "page": "fpu/fdivx.md", + "family": "fdivx", + "variant_of": "fdiv", + "xml_mnem": "fdivx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fdivs": { + "page": "fpu/fdivsx.md", + "family": "fdivsx", + "xml_mnem": "fdivsx", + "opcode_hex": "0xEC000024", + "primary_opcode": 59, + "extended_opcode": 18, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Divide Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fdivs.": { + "page": "fpu/fdivsx.md", + "family": "fdivsx", + "variant_of": "fdivs", + "xml_mnem": "fdivsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmadd": { + "page": "fpu/fmaddx.md", + "family": "fmaddx", + "xml_mnem": "fmaddx", + "opcode_hex": "0xFC00003A", + "primary_opcode": 63, + "extended_opcode": 29, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply-Add", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmadd.": { + "page": "fpu/fmaddx.md", + "family": "fmaddx", + "variant_of": "fmadd", + "xml_mnem": "fmaddx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmadds": { + "page": "fpu/fmaddsx.md", + "family": "fmaddsx", + "xml_mnem": "fmaddsx", + "opcode_hex": "0xEC00003A", + "primary_opcode": 59, + "extended_opcode": 29, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply-Add Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmadds.": { + "page": "fpu/fmaddsx.md", + "family": "fmaddsx", + "variant_of": "fmadds", + "xml_mnem": "fmaddsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmr": { + "page": "fpu/fmrx.md", + "family": "fmrx", + "xml_mnem": "fmrx", + "opcode_hex": "0xFC000090", + "primary_opcode": 63, + "extended_opcode": 72, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Move Register", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmr.": { + "page": "fpu/fmrx.md", + "family": "fmrx", + "variant_of": "fmr", + "xml_mnem": "fmrx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmsub": { + "page": "fpu/fmsubx.md", + "family": "fmsubx", + "xml_mnem": "fmsubx", + "opcode_hex": "0xFC000038", + "primary_opcode": 63, + "extended_opcode": 28, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply-Subtract", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmsub.": { + "page": "fpu/fmsubx.md", + "family": "fmsubx", + "variant_of": "fmsub", + "xml_mnem": "fmsubx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmsubs": { + "page": "fpu/fmsubsx.md", + "family": "fmsubsx", + "xml_mnem": "fmsubsx", + "opcode_hex": "0xEC000038", + "primary_opcode": 59, + "extended_opcode": 28, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply-Subtract Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmsubs.": { + "page": "fpu/fmsubsx.md", + "family": "fmsubsx", + "variant_of": "fmsubs", + "xml_mnem": "fmsubsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmul": { + "page": "fpu/fmulx.md", + "family": "fmulx", + "xml_mnem": "fmulx", + "opcode_hex": "0xFC000032", + "primary_opcode": 63, + "extended_opcode": 25, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmul.": { + "page": "fpu/fmulx.md", + "family": "fmulx", + "variant_of": "fmul", + "xml_mnem": "fmulx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fmuls": { + "page": "fpu/fmulsx.md", + "family": "fmulsx", + "xml_mnem": "fmulsx", + "opcode_hex": "0xEC000032", + "primary_opcode": 59, + "extended_opcode": 25, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Multiply Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fmuls.": { + "page": "fpu/fmulsx.md", + "family": "fmulsx", + "variant_of": "fmuls", + "xml_mnem": "fmulsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fnabs": { + "page": "fpu/fnabsx.md", + "family": "fnabsx", + "xml_mnem": "fnabsx", + "opcode_hex": "0xFC000110", + "primary_opcode": 63, + "extended_opcode": 136, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Negative Absolute Value", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fnabs.": { + "page": "fpu/fnabsx.md", + "family": "fnabsx", + "variant_of": "fnabs", + "xml_mnem": "fnabsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fneg": { + "page": "fpu/fnegx.md", + "family": "fnegx", + "xml_mnem": "fnegx", + "opcode_hex": "0xFC000050", + "primary_opcode": 63, + "extended_opcode": 40, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Negate", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fneg.": { + "page": "fpu/fnegx.md", + "family": "fnegx", + "variant_of": "fneg", + "xml_mnem": "fnegx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fnmadd": { + "page": "fpu/fnmaddx.md", + "family": "fnmaddx", + "xml_mnem": "fnmaddx", + "opcode_hex": "0xFC00003E", + "primary_opcode": 63, + "extended_opcode": 31, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Negative Multiply-Add", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fnmadd.": { + "page": "fpu/fnmaddx.md", + "family": "fnmaddx", + "variant_of": "fnmadd", + "xml_mnem": "fnmaddx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fnmadds": { + "page": "fpu/fnmaddsx.md", + "family": "fnmaddsx", + "xml_mnem": "fnmaddsx", + "opcode_hex": "0xEC00003E", + "primary_opcode": 59, + "extended_opcode": 31, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Negative Multiply-Add Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fnmadds.": { + "page": "fpu/fnmaddsx.md", + "family": "fnmaddsx", + "variant_of": "fnmadds", + "xml_mnem": "fnmaddsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fnmsub": { + "page": "fpu/fnmsubx.md", + "family": "fnmsubx", + "xml_mnem": "fnmsubx", + "opcode_hex": "0xFC00003C", + "primary_opcode": 63, + "extended_opcode": 30, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Negative Multiply-Subtract", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fnmsub.": { + "page": "fpu/fnmsubx.md", + "family": "fnmsubx", + "variant_of": "fnmsub", + "xml_mnem": "fnmsubx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fnmsubs": { + "page": "fpu/fnmsubsx.md", + "family": "fnmsubsx", + "xml_mnem": "fnmsubsx", + "opcode_hex": "0xEC00003C", + "primary_opcode": 59, + "extended_opcode": 30, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Negative Multiply-Subtract Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fnmsubs.": { + "page": "fpu/fnmsubsx.md", + "family": "fnmsubsx", + "variant_of": "fnmsubs", + "xml_mnem": "fnmsubsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fres": { + "page": "fpu/fresx.md", + "family": "fresx", + "xml_mnem": "fresx", + "opcode_hex": "0xEC000030", + "primary_opcode": 59, + "extended_opcode": 24, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Reciprocal Estimate Single", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fres.": { + "page": "fpu/fresx.md", + "family": "fresx", + "variant_of": "fres", + "xml_mnem": "fresx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "frsp": { + "page": "fpu/frspx.md", + "family": "frspx", + "xml_mnem": "frspx", + "opcode_hex": "0xFC000018", + "primary_opcode": 63, + "extended_opcode": 12, + "form": "X", + "group": "fpu", + "category": "fpu", + "description": "Floating Round to Single", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "frsp.": { + "page": "fpu/frspx.md", + "family": "frspx", + "variant_of": "frsp", + "xml_mnem": "frspx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "frsqrte": { + "page": "fpu/frsqrtex.md", + "family": "frsqrtex", + "xml_mnem": "frsqrtex", + "opcode_hex": "0xFC000034", + "primary_opcode": 63, + "extended_opcode": 26, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Reciprocal Square Root Estimate", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "frsqrte.": { + "page": "fpu/frsqrtex.md", + "family": "frsqrtex", + "variant_of": "frsqrte", + "xml_mnem": "frsqrtex", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fsel": { + "page": "fpu/fselx.md", + "family": "fselx", + "xml_mnem": "fselx", + "opcode_hex": "0xFC00002E", + "primary_opcode": 63, + "extended_opcode": 23, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Select", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FC", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fsel.": { + "page": "fpu/fselx.md", + "family": "fselx", + "variant_of": "fsel", + "xml_mnem": "fselx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fsqrt": { + "page": "fpu/fsqrtx.md", + "family": "fsqrtx", + "xml_mnem": "fsqrtx", + "opcode_hex": "0xFC00002C", + "primary_opcode": 63, + "extended_opcode": 22, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Square Root", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fsqrt.": { + "page": "fpu/fsqrtx.md", + "family": "fsqrtx", + "variant_of": "fsqrt", + "xml_mnem": "fsqrtx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fsqrts": { + "page": "fpu/fsqrtsx.md", + "family": "fsqrtsx", + "xml_mnem": "fsqrtsx", + "opcode_hex": "0xEC00002C", + "primary_opcode": 59, + "extended_opcode": 22, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Square Root Single", + "sync": false, + "reads": [ + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fsqrts.": { + "page": "fpu/fsqrtsx.md", + "family": "fsqrtsx", + "variant_of": "fsqrts", + "xml_mnem": "fsqrtsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fsub": { + "page": "fpu/fsubx.md", + "family": "fsubx", + "xml_mnem": "fsubx", + "opcode_hex": "0xFC000028", + "primary_opcode": 63, + "extended_opcode": 20, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Subtract", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fsub.": { + "page": "fpu/fsubx.md", + "family": "fsubx", + "variant_of": "fsub", + "xml_mnem": "fsubx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "fsubs": { + "page": "fpu/fsubsx.md", + "family": "fsubsx", + "xml_mnem": "fsubsx", + "opcode_hex": "0xEC000028", + "primary_opcode": 59, + "extended_opcode": 20, + "form": "A", + "group": "fpu", + "category": "fpu", + "description": "Floating Subtract Single", + "sync": false, + "reads": [ + { + "field": "FA", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "fsubs.": { + "page": "fpu/fsubsx.md", + "family": "fsubsx", + "variant_of": "fsubs", + "xml_mnem": "fsubsx", + "flags": { + "Rc": 1 + }, + "category": "fpu" + }, + "icbi": { + "page": "memory/icbi.md", + "family": "icbi", + "xml_mnem": "icbi", + "opcode_hex": "0x7C0007AC", + "primary_opcode": 31, + "extended_opcode": 982, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Instruction Cache Block Invalidate", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "isync": { + "page": "alu/isync.md", + "family": "isync", + "xml_mnem": "isync", + "opcode_hex": "0x4C00012C", + "primary_opcode": 19, + "extended_opcode": 150, + "form": "XL", + "group": "integer", + "category": "alu", + "description": "Instruction Synchronize", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lbz": { + "page": "memory/lbz.md", + "family": "lbz", + "xml_mnem": "lbz", + "opcode_hex": "0x88000000", + "primary_opcode": 34, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Byte and Zero", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lbzu": { + "page": "memory/lbz.md", + "family": "lbz", + "xml_mnem": "lbzu", + "opcode_hex": "0x8C000000", + "primary_opcode": 35, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Byte and Zero with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lbzux": { + "page": "memory/lbz.md", + "family": "lbz", + "xml_mnem": "lbzux", + "opcode_hex": "0x7C0000EE", + "primary_opcode": 31, + "extended_opcode": 119, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Byte and Zero with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lbzx": { + "page": "memory/lbz.md", + "family": "lbz", + "xml_mnem": "lbzx", + "opcode_hex": "0x7C0000AE", + "primary_opcode": 31, + "extended_opcode": 87, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Byte and Zero Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ld": { + "page": "memory/ld.md", + "family": "ld", + "xml_mnem": "ld", + "opcode_hex": "0xE8000000", + "primary_opcode": 58, + "extended_opcode": null, + "form": "DS", + "group": "memory", + "category": "memory", + "description": "Load Doubleword", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "ds", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ldarx": { + "page": "memory/ldarx.md", + "family": "ldarx", + "xml_mnem": "ldarx", + "opcode_hex": "0x7C0000A8", + "primary_opcode": 31, + "extended_opcode": 84, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Doubleword and Reserve Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ldbrx": { + "page": "memory/ldbrx.md", + "family": "ldbrx", + "xml_mnem": "ldbrx", + "opcode_hex": "0x7C000428", + "primary_opcode": 31, + "extended_opcode": 532, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Doubleword Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ldu": { + "page": "memory/ld.md", + "family": "ld", + "xml_mnem": "ldu", + "opcode_hex": "0xE8000001", + "primary_opcode": 58, + "extended_opcode": null, + "form": "DS", + "group": "memory", + "category": "memory", + "description": "Load Doubleword with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "ds", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ldux": { + "page": "memory/ld.md", + "family": "ld", + "xml_mnem": "ldux", + "opcode_hex": "0x7C00006A", + "primary_opcode": 31, + "extended_opcode": 53, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Doubleword with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "ldx": { + "page": "memory/ld.md", + "family": "ld", + "xml_mnem": "ldx", + "opcode_hex": "0x7C00002A", + "primary_opcode": 31, + "extended_opcode": 21, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Doubleword Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfd": { + "page": "memory/lfd.md", + "family": "lfd", + "xml_mnem": "lfd", + "opcode_hex": "0xC8000000", + "primary_opcode": 50, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Double", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfdu": { + "page": "memory/lfd.md", + "family": "lfd", + "xml_mnem": "lfdu", + "opcode_hex": "0xCC000000", + "primary_opcode": 51, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Double with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfdux": { + "page": "memory/lfd.md", + "family": "lfd", + "xml_mnem": "lfdux", + "opcode_hex": "0x7C0004EE", + "primary_opcode": 31, + "extended_opcode": 631, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Double with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfdx": { + "page": "memory/lfd.md", + "family": "lfd", + "xml_mnem": "lfdx", + "opcode_hex": "0x7C0004AE", + "primary_opcode": 31, + "extended_opcode": 599, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Double Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfs": { + "page": "memory/lfs.md", + "family": "lfs", + "xml_mnem": "lfs", + "opcode_hex": "0xC0000000", + "primary_opcode": 48, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Single", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfsu": { + "page": "memory/lfs.md", + "family": "lfs", + "xml_mnem": "lfsu", + "opcode_hex": "0xC4000000", + "primary_opcode": 49, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Single with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfsux": { + "page": "memory/lfs.md", + "family": "lfs", + "xml_mnem": "lfsux", + "opcode_hex": "0x7C00046E", + "primary_opcode": 31, + "extended_opcode": 567, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Single with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lfsx": { + "page": "memory/lfs.md", + "family": "lfs", + "xml_mnem": "lfsx", + "opcode_hex": "0x7C00042E", + "primary_opcode": 31, + "extended_opcode": 535, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Floating-Point Single Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lha": { + "page": "memory/lha.md", + "family": "lha", + "xml_mnem": "lha", + "opcode_hex": "0xA8000000", + "primary_opcode": 42, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Half Word Algebraic", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhau": { + "page": "memory/lha.md", + "family": "lha", + "xml_mnem": "lhau", + "opcode_hex": "0xAC000000", + "primary_opcode": 43, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Half Word Algebraic with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhaux": { + "page": "memory/lha.md", + "family": "lha", + "xml_mnem": "lhaux", + "opcode_hex": "0x7C0002EE", + "primary_opcode": 31, + "extended_opcode": 375, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Half Word Algebraic with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhax": { + "page": "memory/lha.md", + "family": "lha", + "xml_mnem": "lhax", + "opcode_hex": "0x7C0002AE", + "primary_opcode": 31, + "extended_opcode": 343, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Half Word Algebraic Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhbrx": { + "page": "memory/lhbrx.md", + "family": "lhbrx", + "xml_mnem": "lhbrx", + "opcode_hex": "0x7C00062C", + "primary_opcode": 31, + "extended_opcode": 790, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Half Word Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhz": { + "page": "memory/lhz.md", + "family": "lhz", + "xml_mnem": "lhz", + "opcode_hex": "0xA0000000", + "primary_opcode": 40, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Half Word and Zero", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhzu": { + "page": "memory/lhz.md", + "family": "lhz", + "xml_mnem": "lhzu", + "opcode_hex": "0xA4000000", + "primary_opcode": 41, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Half Word and Zero with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhzux": { + "page": "memory/lhz.md", + "family": "lhz", + "xml_mnem": "lhzux", + "opcode_hex": "0x7C00026E", + "primary_opcode": 31, + "extended_opcode": 311, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Half Word and Zero with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lhzx": { + "page": "memory/lhz.md", + "family": "lhz", + "xml_mnem": "lhzx", + "opcode_hex": "0x7C00022E", + "primary_opcode": 31, + "extended_opcode": 279, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Half Word and Zero Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lmw": { + "page": "memory/lmw.md", + "family": "lmw", + "xml_mnem": "lmw", + "opcode_hex": "0xB8000000", + "primary_opcode": 46, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Multiple Word", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lswi": { + "page": "memory/lswi.md", + "family": "lswi", + "xml_mnem": "lswi", + "opcode_hex": "0x7C0004AA", + "primary_opcode": 31, + "extended_opcode": 597, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load String Word Immediate", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lswx": { + "page": "memory/lswx.md", + "family": "lswx", + "xml_mnem": "lswx", + "opcode_hex": "0x7C00042A", + "primary_opcode": 31, + "extended_opcode": 533, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load String Word Indexed", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvebx": { + "page": "memory/lvebx.md", + "family": "lvebx", + "xml_mnem": "lvebx", + "opcode_hex": "0x7C00000E", + "primary_opcode": 31, + "extended_opcode": 7, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Element Byte Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvehx": { + "page": "memory/lvehx.md", + "family": "lvehx", + "xml_mnem": "lvehx", + "opcode_hex": "0x7C00004E", + "primary_opcode": 31, + "extended_opcode": 39, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Element Half Word Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvewx": { + "page": "memory/lvewx.md", + "family": "lvewx", + "xml_mnem": "lvewx", + "opcode_hex": "0x7C00008E", + "primary_opcode": 31, + "extended_opcode": 71, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Element Word Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvewx128": { + "page": "memory/lvewx.md", + "family": "lvewx", + "xml_mnem": "lvewx128", + "opcode_hex": "0x10000083", + "primary_opcode": 4, + "extended_opcode": 131, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Element Word Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvlx": { + "page": "memory/lvlx.md", + "family": "lvlx", + "xml_mnem": "lvlx", + "opcode_hex": "0x7C00040E", + "primary_opcode": 31, + "extended_opcode": 519, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Left Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvlx128": { + "page": "memory/lvlx.md", + "family": "lvlx", + "xml_mnem": "lvlx128", + "opcode_hex": "0x10000403", + "primary_opcode": 4, + "extended_opcode": 1027, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Left Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvlxl": { + "page": "memory/lvlxl.md", + "family": "lvlxl", + "xml_mnem": "lvlxl", + "opcode_hex": "0x7C00060E", + "primary_opcode": 31, + "extended_opcode": 775, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Left Indexed LRU", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvlxl128": { + "page": "memory/lvlxl.md", + "family": "lvlxl", + "xml_mnem": "lvlxl128", + "opcode_hex": "0x10000603", + "primary_opcode": 4, + "extended_opcode": 1539, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Left Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvrx": { + "page": "memory/lvrx.md", + "family": "lvrx", + "xml_mnem": "lvrx", + "opcode_hex": "0x7C00044E", + "primary_opcode": 31, + "extended_opcode": 551, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Right Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvrx128": { + "page": "memory/lvrx.md", + "family": "lvrx", + "xml_mnem": "lvrx128", + "opcode_hex": "0x10000443", + "primary_opcode": 4, + "extended_opcode": 1091, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Right Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvrxl": { + "page": "memory/lvrxl.md", + "family": "lvrxl", + "xml_mnem": "lvrxl", + "opcode_hex": "0x7C00064E", + "primary_opcode": 31, + "extended_opcode": 807, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Right Indexed LRU", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvrxl128": { + "page": "memory/lvrxl.md", + "family": "lvrxl", + "xml_mnem": "lvrxl128", + "opcode_hex": "0x10000643", + "primary_opcode": 4, + "extended_opcode": 1603, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Right Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvsl": { + "page": "vmx/lvsl.md", + "family": "lvsl", + "xml_mnem": "lvsl", + "opcode_hex": "0x7C00000C", + "primary_opcode": 31, + "extended_opcode": 6, + "form": "X", + "group": "vmx", + "category": "vmx", + "description": "Load Vector for Shift Left Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvsl128": { + "page": "vmx/lvsl.md", + "family": "lvsl", + "xml_mnem": "lvsl128", + "opcode_hex": "0x10000003", + "primary_opcode": 4, + "extended_opcode": 3, + "form": "VX128_1", + "group": "vmx", + "category": "vmx", + "description": "Load Vector for Shift Left Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvsr": { + "page": "vmx/lvsr.md", + "family": "lvsr", + "xml_mnem": "lvsr", + "opcode_hex": "0x7C00004C", + "primary_opcode": 31, + "extended_opcode": 38, + "form": "X", + "group": "vmx", + "category": "vmx", + "description": "Load Vector for Shift Right Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvsr128": { + "page": "vmx/lvsr.md", + "family": "lvsr", + "xml_mnem": "lvsr128", + "opcode_hex": "0x10000043", + "primary_opcode": 4, + "extended_opcode": 67, + "form": "VX128_1", + "group": "vmx", + "category": "vmx", + "description": "Load Vector for Shift Right Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvx": { + "page": "memory/lvx.md", + "family": "lvx", + "xml_mnem": "lvx", + "opcode_hex": "0x7C0000CE", + "primary_opcode": 31, + "extended_opcode": 103, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvx128": { + "page": "memory/lvx.md", + "family": "lvx", + "xml_mnem": "lvx128", + "opcode_hex": "0x100000C3", + "primary_opcode": 4, + "extended_opcode": 195, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Indexed 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvxl": { + "page": "memory/lvxl.md", + "family": "lvxl", + "xml_mnem": "lvxl", + "opcode_hex": "0x7C0002CE", + "primary_opcode": 31, + "extended_opcode": 359, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Vector Indexed LRU", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lvxl128": { + "page": "memory/lvxl.md", + "family": "lvxl", + "xml_mnem": "lvxl128", + "opcode_hex": "0x100002C3", + "primary_opcode": 4, + "extended_opcode": 707, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Load Vector Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwa": { + "page": "memory/lwa.md", + "family": "lwa", + "xml_mnem": "lwa", + "opcode_hex": "0xE8000002", + "primary_opcode": 58, + "extended_opcode": null, + "form": "DS", + "group": "memory", + "category": "memory", + "description": "Load Word Algebraic", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "ds", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwarx": { + "page": "memory/lwarx.md", + "family": "lwarx", + "xml_mnem": "lwarx", + "opcode_hex": "0x7C000028", + "primary_opcode": 31, + "extended_opcode": 20, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word and Reserve Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwaux": { + "page": "memory/lwa.md", + "family": "lwa", + "xml_mnem": "lwaux", + "opcode_hex": "0x7C0002EA", + "primary_opcode": 31, + "extended_opcode": 373, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word Algebraic with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwax": { + "page": "memory/lwa.md", + "family": "lwa", + "xml_mnem": "lwax", + "opcode_hex": "0x7C0002AA", + "primary_opcode": 31, + "extended_opcode": 341, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word Algebraic Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwbrx": { + "page": "memory/lwbrx.md", + "family": "lwbrx", + "xml_mnem": "lwbrx", + "opcode_hex": "0x7C00042C", + "primary_opcode": 31, + "extended_opcode": 534, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwz": { + "page": "memory/lwz.md", + "family": "lwz", + "xml_mnem": "lwz", + "opcode_hex": "0x80000000", + "primary_opcode": 32, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Word and Zero", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwzu": { + "page": "memory/lwz.md", + "family": "lwz", + "xml_mnem": "lwzu", + "opcode_hex": "0x84000000", + "primary_opcode": 33, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Load Word and Zero with Update", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwzux": { + "page": "memory/lwz.md", + "family": "lwz", + "xml_mnem": "lwzux", + "opcode_hex": "0x7C00006E", + "primary_opcode": 31, + "extended_opcode": 55, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word and Zero with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "lwzx": { + "page": "memory/lwz.md", + "family": "lwz", + "xml_mnem": "lwzx", + "opcode_hex": "0x7C00002E", + "primary_opcode": 31, + "extended_opcode": 23, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Load Word and Zero Indexed", + "sync": false, + "reads": [ + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mcrf": { + "page": "control/mcrf.md", + "family": "mcrf", + "xml_mnem": "mcrf", + "opcode_hex": "0x4C000000", + "primary_opcode": 19, + "extended_opcode": 0, + "form": "XL", + "group": "control", + "category": "control", + "description": "Move Condition Register Field", + "sync": false, + "reads": [ + { + "field": "CRFS", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mcrfs": { + "page": "control/mcrfs.md", + "family": "mcrfs", + "xml_mnem": "mcrfs", + "opcode_hex": "0xFC000080", + "primary_opcode": 63, + "extended_opcode": 64, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to Condition Register from FPSCR", + "sync": false, + "reads": [ + { + "field": "CRFS", + "conditional": false + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + }, + { + "field": "FPSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mcrxr": { + "page": "control/mcrxr.md", + "family": "mcrxr", + "xml_mnem": "mcrxr", + "opcode_hex": "0x7C000400", + "primary_opcode": 31, + "extended_opcode": 512, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to Condition Register from XER", + "sync": false, + "reads": [ + { + "field": "CR", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mfcr": { + "page": "control/mfcr.md", + "family": "mfcr", + "xml_mnem": "mfcr", + "opcode_hex": "0x7C000026", + "primary_opcode": 31, + "extended_opcode": 19, + "form": "X", + "group": "control", + "category": "control", + "description": "Move from Condition Register", + "sync": false, + "reads": [ + { + "field": "CR", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mffs": { + "page": "control/mffsx.md", + "family": "mffsx", + "xml_mnem": "mffsx", + "opcode_hex": "0xFC00048E", + "primary_opcode": 63, + "extended_opcode": 583, + "form": "X", + "group": "control", + "category": "control", + "description": "Move from FPSCR", + "sync": false, + "reads": [ + { + "field": "FPSCR", + "conditional": false + } + ], + "writes": [ + { + "field": "FD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mffs.": { + "page": "control/mffsx.md", + "family": "mffsx", + "variant_of": "mffs", + "xml_mnem": "mffsx", + "flags": { + "Rc": 1 + }, + "category": "control" + }, + "mfmsr": { + "page": "control/mfmsr.md", + "family": "mfmsr", + "xml_mnem": "mfmsr", + "opcode_hex": "0x7C0000A6", + "primary_opcode": 31, + "extended_opcode": 83, + "form": "X", + "group": "control", + "category": "control", + "description": "Move from Machine State Register", + "sync": true, + "reads": [ + { + "field": "MSR", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mfspr": { + "page": "control/mfspr.md", + "family": "mfspr", + "xml_mnem": "mfspr", + "opcode_hex": "0x7C0002A6", + "primary_opcode": 31, + "extended_opcode": 339, + "form": "XFX", + "group": "control", + "category": "control", + "description": "Move from Special-Purpose Register", + "sync": false, + "reads": [ + { + "field": "SPR", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mftb": { + "page": "control/mftb.md", + "family": "mftb", + "xml_mnem": "mftb", + "opcode_hex": "0x7C0002E6", + "primary_opcode": 31, + "extended_opcode": 371, + "form": "XFX", + "group": "control", + "category": "control", + "description": "Move from Time Base", + "sync": false, + "reads": [ + { + "field": "TBR", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mfvscr": { + "page": "control/mfvscr.md", + "family": "mfvscr", + "xml_mnem": "mfvscr", + "opcode_hex": "0x10000604", + "primary_opcode": 4, + "extended_opcode": 1540, + "form": "VX", + "group": "control", + "category": "control", + "description": "Move from VSCR", + "sync": false, + "reads": [ + { + "field": "VSCR", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtcrf": { + "page": "control/mtcrf.md", + "family": "mtcrf", + "xml_mnem": "mtcrf", + "opcode_hex": "0x7C000120", + "primary_opcode": 31, + "extended_opcode": 144, + "form": "XFX", + "group": "control", + "category": "control", + "description": "Move to Condition Register Fields", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "CRM", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtfsb0": { + "page": "control/mtfsb0x.md", + "family": "mtfsb0x", + "xml_mnem": "mtfsb0x", + "opcode_hex": "0xFC00008C", + "primary_opcode": 63, + "extended_opcode": 70, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to FPSCR Bit 0", + "sync": false, + "reads": [], + "writes": [ + { + "field": "FPSCRD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtfsb0.": { + "page": "control/mtfsb0x.md", + "family": "mtfsb0x", + "variant_of": "mtfsb0", + "xml_mnem": "mtfsb0x", + "flags": { + "Rc": 1 + }, + "category": "control" + }, + "mtfsb1": { + "page": "control/mtfsb1x.md", + "family": "mtfsb1x", + "xml_mnem": "mtfsb1x", + "opcode_hex": "0xFC00004C", + "primary_opcode": 63, + "extended_opcode": 38, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to FPSCR Bit 1", + "sync": false, + "reads": [], + "writes": [ + { + "field": "FPSCRD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtfsb1.": { + "page": "control/mtfsb1x.md", + "family": "mtfsb1x", + "variant_of": "mtfsb1", + "xml_mnem": "mtfsb1x", + "flags": { + "Rc": 1 + }, + "category": "control" + }, + "mtfsf": { + "page": "control/mtfsfx.md", + "family": "mtfsfx", + "xml_mnem": "mtfsfx", + "opcode_hex": "0xFC00058E", + "primary_opcode": 63, + "extended_opcode": 711, + "form": "XFL", + "group": "control", + "category": "control", + "description": "Move to FPSCR Fields", + "sync": false, + "reads": [ + { + "field": "FM", + "conditional": false + }, + { + "field": "FB", + "conditional": false + } + ], + "writes": [ + { + "field": "FPSCR", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtfsf.": { + "page": "control/mtfsfx.md", + "family": "mtfsfx", + "variant_of": "mtfsf", + "xml_mnem": "mtfsfx", + "flags": { + "Rc": 1 + }, + "category": "control" + }, + "mtfsfi": { + "page": "control/mtfsfix.md", + "family": "mtfsfix", + "xml_mnem": "mtfsfix", + "opcode_hex": "0xFC00010C", + "primary_opcode": 63, + "extended_opcode": 134, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to FPSCR Field Immediate", + "sync": false, + "reads": [ + { + "field": "IMM", + "conditional": false + } + ], + "writes": [ + { + "field": "CRFD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtfsfi.": { + "page": "control/mtfsfix.md", + "family": "mtfsfix", + "variant_of": "mtfsfi", + "xml_mnem": "mtfsfix", + "flags": { + "Rc": 1 + }, + "category": "control" + }, + "mtmsr": { + "page": "control/mtmsr.md", + "family": "mtmsr", + "xml_mnem": "mtmsr", + "opcode_hex": "0x7C000124", + "primary_opcode": 31, + "extended_opcode": 146, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to Machine State Register", + "sync": true, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "MSR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtmsrd": { + "page": "control/mtmsrd.md", + "family": "mtmsrd", + "xml_mnem": "mtmsrd", + "opcode_hex": "0x7C000164", + "primary_opcode": 31, + "extended_opcode": 178, + "form": "X", + "group": "control", + "category": "control", + "description": "Move to Machine State Register Doubleword", + "sync": true, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "MSR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtspr": { + "page": "control/mtspr.md", + "family": "mtspr", + "xml_mnem": "mtspr", + "opcode_hex": "0x7C0003A6", + "primary_opcode": 31, + "extended_opcode": 467, + "form": "XFX", + "group": "control", + "category": "control", + "description": "Move to Special-Purpose Register", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + } + ], + "writes": [ + { + "field": "SPR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mtvscr": { + "page": "control/mtvscr.md", + "family": "mtvscr", + "xml_mnem": "mtvscr", + "opcode_hex": "0x10000644", + "primary_opcode": 4, + "extended_opcode": 1604, + "form": "VX", + "group": "control", + "category": "control", + "description": "Move to VSCR", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulhd": { + "page": "alu/mulhdx.md", + "family": "mulhdx", + "xml_mnem": "mulhdx", + "opcode_hex": "0x7C000092", + "primary_opcode": 31, + "extended_opcode": 73, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply High Doubleword", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulhd.": { + "page": "alu/mulhdx.md", + "family": "mulhdx", + "variant_of": "mulhd", + "xml_mnem": "mulhdx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mulhdu": { + "page": "alu/mulhdux.md", + "family": "mulhdux", + "xml_mnem": "mulhdux", + "opcode_hex": "0x7C000012", + "primary_opcode": 31, + "extended_opcode": 9, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply High Doubleword Unsigned", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulhdu.": { + "page": "alu/mulhdux.md", + "family": "mulhdux", + "variant_of": "mulhdu", + "xml_mnem": "mulhdux", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mulhw": { + "page": "alu/mulhwx.md", + "family": "mulhwx", + "xml_mnem": "mulhwx", + "opcode_hex": "0x7C000096", + "primary_opcode": 31, + "extended_opcode": 75, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply High Word", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulhw.": { + "page": "alu/mulhwx.md", + "family": "mulhwx", + "variant_of": "mulhw", + "xml_mnem": "mulhwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mulhwu": { + "page": "alu/mulhwux.md", + "family": "mulhwux", + "xml_mnem": "mulhwux", + "opcode_hex": "0x7C000016", + "primary_opcode": 31, + "extended_opcode": 11, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply High Word Unsigned", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulhwu.": { + "page": "alu/mulhwux.md", + "family": "mulhwux", + "variant_of": "mulhwu", + "xml_mnem": "mulhwux", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mulld": { + "page": "alu/mulldx.md", + "family": "mulldx", + "xml_mnem": "mulldx", + "opcode_hex": "0x7C0001D2", + "primary_opcode": 31, + "extended_opcode": 233, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply Low Doubleword", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mulld.": { + "page": "alu/mulldx.md", + "family": "mulldx", + "variant_of": "mulld", + "xml_mnem": "mulldx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mulldo": { + "page": "alu/mulldx.md", + "family": "mulldx", + "variant_of": "mulld", + "xml_mnem": "mulldx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "mulldo.": { + "page": "alu/mulldx.md", + "family": "mulldx", + "variant_of": "mulld", + "xml_mnem": "mulldx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "mulli": { + "page": "alu/mulli.md", + "family": "mulli", + "xml_mnem": "mulli", + "opcode_hex": "0x1C000000", + "primary_opcode": 7, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Multiply Low Immediate", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mullw": { + "page": "alu/mullwx.md", + "family": "mullwx", + "xml_mnem": "mullwx", + "opcode_hex": "0x7C0001D6", + "primary_opcode": 31, + "extended_opcode": 235, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Multiply Low Word", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "mullw.": { + "page": "alu/mullwx.md", + "family": "mullwx", + "variant_of": "mullw", + "xml_mnem": "mullwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "mullwo": { + "page": "alu/mullwx.md", + "family": "mullwx", + "variant_of": "mullw", + "xml_mnem": "mullwx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "mullwo.": { + "page": "alu/mullwx.md", + "family": "mullwx", + "variant_of": "mullw", + "xml_mnem": "mullwx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "nand": { + "page": "alu/nandx.md", + "family": "nandx", + "xml_mnem": "nandx", + "opcode_hex": "0x7C0003B8", + "primary_opcode": 31, + "extended_opcode": 476, + "form": "X", + "group": "integer", + "category": "alu", + "description": "NAND", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "nand.": { + "page": "alu/nandx.md", + "family": "nandx", + "variant_of": "nand", + "xml_mnem": "nandx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "neg": { + "page": "alu/negx.md", + "family": "negx", + "xml_mnem": "negx", + "opcode_hex": "0x7C0000D0", + "primary_opcode": 31, + "extended_opcode": 104, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Negate", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "neg.": { + "page": "alu/negx.md", + "family": "negx", + "variant_of": "neg", + "xml_mnem": "negx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "nego": { + "page": "alu/negx.md", + "family": "negx", + "variant_of": "neg", + "xml_mnem": "negx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "nego.": { + "page": "alu/negx.md", + "family": "negx", + "variant_of": "neg", + "xml_mnem": "negx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "nor": { + "page": "alu/norx.md", + "family": "norx", + "xml_mnem": "norx", + "opcode_hex": "0x7C0000F8", + "primary_opcode": 31, + "extended_opcode": 124, + "form": "X", + "group": "integer", + "category": "alu", + "description": "NOR", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "nor.": { + "page": "alu/norx.md", + "family": "norx", + "variant_of": "nor", + "xml_mnem": "norx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "or": { + "page": "alu/orx.md", + "family": "orx", + "xml_mnem": "orx", + "opcode_hex": "0x7C000378", + "primary_opcode": 31, + "extended_opcode": 444, + "form": "X", + "group": "integer", + "category": "alu", + "description": "OR", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "or.": { + "page": "alu/orx.md", + "family": "orx", + "variant_of": "or", + "xml_mnem": "orx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "orc": { + "page": "alu/orcx.md", + "family": "orcx", + "xml_mnem": "orcx", + "opcode_hex": "0x7C000338", + "primary_opcode": 31, + "extended_opcode": 412, + "form": "X", + "group": "integer", + "category": "alu", + "description": "OR with Complement", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "orc.": { + "page": "alu/orcx.md", + "family": "orcx", + "variant_of": "orc", + "xml_mnem": "orcx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "ori": { + "page": "alu/ori.md", + "family": "ori", + "xml_mnem": "ori", + "opcode_hex": "0x60000000", + "primary_opcode": 24, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "OR Immediate", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "oris": { + "page": "alu/oris.md", + "family": "oris", + "xml_mnem": "oris", + "opcode_hex": "0x64000000", + "primary_opcode": 25, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "OR Immediate Shifted", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldcl": { + "page": "alu/rldclx.md", + "family": "rldclx", + "xml_mnem": "rldclx", + "opcode_hex": "0x78000010", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MDS", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword then Clear Left", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + }, + { + "field": "MB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldcl.": { + "page": "alu/rldclx.md", + "family": "rldclx", + "variant_of": "rldcl", + "xml_mnem": "rldclx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rldcr": { + "page": "alu/rldcrx.md", + "family": "rldcrx", + "xml_mnem": "rldcrx", + "opcode_hex": "0x78000012", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MDS", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword then Clear Right", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + }, + { + "field": "ME", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldcr.": { + "page": "alu/rldcrx.md", + "family": "rldcrx", + "variant_of": "rldcr", + "xml_mnem": "rldcrx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rldic": { + "page": "alu/rldicx.md", + "family": "rldicx", + "xml_mnem": "rldicx", + "opcode_hex": "0x78000008", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MD", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword Immediate then Clear", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "MB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldic.": { + "page": "alu/rldicx.md", + "family": "rldicx", + "variant_of": "rldic", + "xml_mnem": "rldicx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rldicl": { + "page": "alu/rldiclx.md", + "family": "rldiclx", + "xml_mnem": "rldiclx", + "opcode_hex": "0x78000000", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MD", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword Immediate then Clear Left", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "MB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldicl.": { + "page": "alu/rldiclx.md", + "family": "rldiclx", + "variant_of": "rldicl", + "xml_mnem": "rldiclx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rldicr": { + "page": "alu/rldicrx.md", + "family": "rldicrx", + "xml_mnem": "rldicrx", + "opcode_hex": "0x78000004", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MD", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword Immediate then Clear Right", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "ME", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldicr.": { + "page": "alu/rldicrx.md", + "family": "rldicrx", + "variant_of": "rldicr", + "xml_mnem": "rldicrx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rldimi": { + "page": "alu/rldimix.md", + "family": "rldimix", + "xml_mnem": "rldimix", + "opcode_hex": "0x7800000C", + "primary_opcode": 30, + "extended_opcode": null, + "form": "MD", + "group": "integer", + "category": "alu", + "description": "Rotate Left Doubleword Immediate then Mask Insert", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "MB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rldimi.": { + "page": "alu/rldimix.md", + "family": "rldimix", + "variant_of": "rldimi", + "xml_mnem": "rldimix", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rlwimi": { + "page": "alu/rlwimix.md", + "family": "rlwimix", + "xml_mnem": "rlwimix", + "opcode_hex": "0x50000000", + "primary_opcode": 20, + "extended_opcode": null, + "form": "M", + "group": "integer", + "category": "alu", + "description": "Rotate Left Word Immediate then Mask Insert", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "MB", + "conditional": false + }, + { + "field": "ME", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rlwimi.": { + "page": "alu/rlwimix.md", + "family": "rlwimix", + "variant_of": "rlwimi", + "xml_mnem": "rlwimix", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rlwinm": { + "page": "alu/rlwinmx.md", + "family": "rlwinmx", + "xml_mnem": "rlwinmx", + "opcode_hex": "0x54000000", + "primary_opcode": 21, + "extended_opcode": null, + "form": "M", + "group": "integer", + "category": "alu", + "description": "Rotate Left Word Immediate then AND with Mask", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + }, + { + "field": "MB", + "conditional": false + }, + { + "field": "ME", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rlwinm.": { + "page": "alu/rlwinmx.md", + "family": "rlwinmx", + "variant_of": "rlwinm", + "xml_mnem": "rlwinmx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "rlwnm": { + "page": "alu/rlwnmx.md", + "family": "rlwnmx", + "xml_mnem": "rlwnmx", + "opcode_hex": "0x5C000000", + "primary_opcode": 23, + "extended_opcode": null, + "form": "M", + "group": "integer", + "category": "alu", + "description": "Rotate Left Word then AND with Mask", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + }, + { + "field": "MB", + "conditional": false + }, + { + "field": "ME", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "rlwnm.": { + "page": "alu/rlwnmx.md", + "family": "rlwnmx", + "variant_of": "rlwnm", + "xml_mnem": "rlwnmx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "sc": { + "page": "branch/sc.md", + "family": "sc", + "xml_mnem": "sc", + "opcode_hex": "0x44000002", + "primary_opcode": 17, + "extended_opcode": null, + "form": "SC", + "group": "branch", + "category": "branch", + "description": "System Call", + "sync": true, + "reads": [ + { + "field": "LEV", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sld": { + "page": "alu/sldx.md", + "family": "sldx", + "xml_mnem": "sldx", + "opcode_hex": "0x7C000036", + "primary_opcode": 31, + "extended_opcode": 27, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Left Doubleword", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sld.": { + "page": "alu/sldx.md", + "family": "sldx", + "variant_of": "sld", + "xml_mnem": "sldx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "slw": { + "page": "alu/slwx.md", + "family": "slwx", + "xml_mnem": "slwx", + "opcode_hex": "0x7C000030", + "primary_opcode": 31, + "extended_opcode": 24, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Left Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "slw.": { + "page": "alu/slwx.md", + "family": "slwx", + "variant_of": "slw", + "xml_mnem": "slwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "srad": { + "page": "alu/sradx.md", + "family": "sradx", + "xml_mnem": "sradx", + "opcode_hex": "0x7C000634", + "primary_opcode": 31, + "extended_opcode": 794, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Right Algebraic Doubleword", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "srad.": { + "page": "alu/sradx.md", + "family": "sradx", + "variant_of": "srad", + "xml_mnem": "sradx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "sradi": { + "page": "alu/sradix.md", + "family": "sradix", + "xml_mnem": "sradix", + "opcode_hex": "0x7C000674", + "primary_opcode": 31, + "extended_opcode": 826, + "form": "XS", + "group": "integer", + "category": "alu", + "description": "Shift Right Algebraic Doubleword Immediate", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sradi.": { + "page": "alu/sradix.md", + "family": "sradix", + "variant_of": "sradi", + "xml_mnem": "sradix", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "sraw": { + "page": "alu/srawx.md", + "family": "srawx", + "xml_mnem": "srawx", + "opcode_hex": "0x7C000630", + "primary_opcode": 31, + "extended_opcode": 792, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Right Algebraic Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sraw.": { + "page": "alu/srawx.md", + "family": "srawx", + "variant_of": "sraw", + "xml_mnem": "srawx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "srawi": { + "page": "alu/srawix.md", + "family": "srawix", + "xml_mnem": "srawix", + "opcode_hex": "0x7C000670", + "primary_opcode": 31, + "extended_opcode": 824, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Right Algebraic Word Immediate", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "SH", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "srawi.": { + "page": "alu/srawix.md", + "family": "srawix", + "variant_of": "srawi", + "xml_mnem": "srawix", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "srd": { + "page": "alu/srdx.md", + "family": "srdx", + "xml_mnem": "srdx", + "opcode_hex": "0x7C000436", + "primary_opcode": 31, + "extended_opcode": 539, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Right Doubleword", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "srd.": { + "page": "alu/srdx.md", + "family": "srdx", + "variant_of": "srd", + "xml_mnem": "srdx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "srw": { + "page": "alu/srwx.md", + "family": "srwx", + "xml_mnem": "srwx", + "opcode_hex": "0x7C000430", + "primary_opcode": 31, + "extended_opcode": 536, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Shift Right Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "srw.": { + "page": "alu/srwx.md", + "family": "srwx", + "variant_of": "srw", + "xml_mnem": "srwx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "stb": { + "page": "memory/stb.md", + "family": "stb", + "xml_mnem": "stb", + "opcode_hex": "0x98000000", + "primary_opcode": 38, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Byte", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stbu": { + "page": "memory/stb.md", + "family": "stb", + "xml_mnem": "stbu", + "opcode_hex": "0x9C000000", + "primary_opcode": 39, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Byte with Update", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stbux": { + "page": "memory/stb.md", + "family": "stb", + "xml_mnem": "stbux", + "opcode_hex": "0x7C0001EE", + "primary_opcode": 31, + "extended_opcode": 247, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Byte with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stbx": { + "page": "memory/stb.md", + "family": "stb", + "xml_mnem": "stbx", + "opcode_hex": "0x7C0001AE", + "primary_opcode": 31, + "extended_opcode": 215, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Byte Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "std": { + "page": "memory/std.md", + "family": "std", + "xml_mnem": "std", + "opcode_hex": "0xF8000000", + "primary_opcode": 62, + "extended_opcode": null, + "form": "DS", + "group": "memory", + "category": "memory", + "description": "Store Doubleword", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "ds", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stdbrx": { + "page": "memory/stdbrx.md", + "family": "stdbrx", + "xml_mnem": "stdbrx", + "opcode_hex": "0x7C000528", + "primary_opcode": 31, + "extended_opcode": 660, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Doubleword Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stdcx": { + "page": "memory/stdcx.md", + "family": "stdcx", + "xml_mnem": "stdcx", + "opcode_hex": "0x7C0001AD", + "primary_opcode": 31, + "extended_opcode": 214, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Doubleword Conditional Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "CR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": true + }, + "is_primary": true, + "flags": {} + }, + "stdu": { + "page": "memory/std.md", + "family": "std", + "xml_mnem": "stdu", + "opcode_hex": "0xF8000001", + "primary_opcode": 62, + "extended_opcode": null, + "form": "DS", + "group": "memory", + "category": "memory", + "description": "Store Doubleword with Update", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "ds", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stdux": { + "page": "memory/std.md", + "family": "std", + "xml_mnem": "stdux", + "opcode_hex": "0x7C00016A", + "primary_opcode": 31, + "extended_opcode": 181, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Doubleword with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stdx": { + "page": "memory/std.md", + "family": "std", + "xml_mnem": "stdx", + "opcode_hex": "0x7C00012A", + "primary_opcode": 31, + "extended_opcode": 149, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Doubleword Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfd": { + "page": "memory/stfd.md", + "family": "stfd", + "xml_mnem": "stfd", + "opcode_hex": "0xD8000000", + "primary_opcode": 54, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Double", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfdu": { + "page": "memory/stfd.md", + "family": "stfd", + "xml_mnem": "stfdu", + "opcode_hex": "0xDC000000", + "primary_opcode": 55, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Double with Update", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfdux": { + "page": "memory/stfd.md", + "family": "stfd", + "xml_mnem": "stfdux", + "opcode_hex": "0x7C0005EE", + "primary_opcode": 31, + "extended_opcode": 759, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Double with Update Indexed", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfdx": { + "page": "memory/stfd.md", + "family": "stfd", + "xml_mnem": "stfdx", + "opcode_hex": "0x7C0005AE", + "primary_opcode": 31, + "extended_opcode": 727, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Double Indexed", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfiwx": { + "page": "memory/stfiwx.md", + "family": "stfiwx", + "xml_mnem": "stfiwx", + "opcode_hex": "0x7C0007AE", + "primary_opcode": 31, + "extended_opcode": 983, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point as Integer Word Indexed", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfs": { + "page": "memory/stfs.md", + "family": "stfs", + "xml_mnem": "stfs", + "opcode_hex": "0xD0000000", + "primary_opcode": 52, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Single", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfsu": { + "page": "memory/stfs.md", + "family": "stfs", + "xml_mnem": "stfsu", + "opcode_hex": "0xD4000000", + "primary_opcode": 53, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Single with Update", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfsux": { + "page": "memory/stfs.md", + "family": "stfs", + "xml_mnem": "stfsux", + "opcode_hex": "0x7C00056E", + "primary_opcode": 31, + "extended_opcode": 695, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Single with Update Indexed", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stfsx": { + "page": "memory/stfs.md", + "family": "stfs", + "xml_mnem": "stfsx", + "opcode_hex": "0x7C00052E", + "primary_opcode": 31, + "extended_opcode": 663, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Floating-Point Single Indexed", + "sync": false, + "reads": [ + { + "field": "FS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sth": { + "page": "memory/sth.md", + "family": "sth", + "xml_mnem": "sth", + "opcode_hex": "0xB0000000", + "primary_opcode": 44, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Half Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sthbrx": { + "page": "memory/sthbrx.md", + "family": "sthbrx", + "xml_mnem": "sthbrx", + "opcode_hex": "0x7C00072C", + "primary_opcode": 31, + "extended_opcode": 918, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Half Word Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sthu": { + "page": "memory/sth.md", + "family": "sth", + "xml_mnem": "sthu", + "opcode_hex": "0xB4000000", + "primary_opcode": 45, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Half Word with Update", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sthux": { + "page": "memory/sth.md", + "family": "sth", + "xml_mnem": "sthux", + "opcode_hex": "0x7C00036E", + "primary_opcode": 31, + "extended_opcode": 439, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Half Word with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "sthx": { + "page": "memory/sth.md", + "family": "sth", + "xml_mnem": "sthx", + "opcode_hex": "0x7C00032E", + "primary_opcode": 31, + "extended_opcode": 407, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Half Word Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stmw": { + "page": "memory/stmw.md", + "family": "stmw", + "xml_mnem": "stmw", + "opcode_hex": "0xBC000000", + "primary_opcode": 47, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Multiple Word", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stswi": { + "page": "memory/stswi.md", + "family": "stswi", + "xml_mnem": "stswi", + "opcode_hex": "0x7C0005AA", + "primary_opcode": 31, + "extended_opcode": 725, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store String Word Immediate", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stswx": { + "page": "memory/stswx.md", + "family": "stswx", + "xml_mnem": "stswx", + "opcode_hex": "0x7C00052A", + "primary_opcode": 31, + "extended_opcode": 661, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store String Word Indexed", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvebx": { + "page": "memory/stvebx.md", + "family": "stvebx", + "xml_mnem": "stvebx", + "opcode_hex": "0x7C00010E", + "primary_opcode": 31, + "extended_opcode": 135, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Element Byte Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvehx": { + "page": "memory/stvehx.md", + "family": "stvehx", + "xml_mnem": "stvehx", + "opcode_hex": "0x7C00014E", + "primary_opcode": 31, + "extended_opcode": 167, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Element Half Word Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvewx": { + "page": "memory/stvewx.md", + "family": "stvewx", + "xml_mnem": "stvewx", + "opcode_hex": "0x7C00018E", + "primary_opcode": 31, + "extended_opcode": 199, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Element Word Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvewx128": { + "page": "memory/stvewx.md", + "family": "stvewx", + "xml_mnem": "stvewx128", + "opcode_hex": "0x10000183", + "primary_opcode": 4, + "extended_opcode": 387, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Element Word Indexed 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvlx": { + "page": "memory/stvlx.md", + "family": "stvlx", + "xml_mnem": "stvlx", + "opcode_hex": "0x7C00050E", + "primary_opcode": 31, + "extended_opcode": 647, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Left Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvlx128": { + "page": "memory/stvlx.md", + "family": "stvlx", + "xml_mnem": "stvlx128", + "opcode_hex": "0x10000503", + "primary_opcode": 4, + "extended_opcode": 1283, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Left Indexed 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvlxl": { + "page": "memory/stvlxl.md", + "family": "stvlxl", + "xml_mnem": "stvlxl", + "opcode_hex": "0x7C00070E", + "primary_opcode": 31, + "extended_opcode": 903, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Left Indexed LRU", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvlxl128": { + "page": "memory/stvlxl.md", + "family": "stvlxl", + "xml_mnem": "stvlxl128", + "opcode_hex": "0x10000703", + "primary_opcode": 4, + "extended_opcode": 1795, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Left Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvrx": { + "page": "memory/stvrx.md", + "family": "stvrx", + "xml_mnem": "stvrx", + "opcode_hex": "0x7C00054E", + "primary_opcode": 31, + "extended_opcode": 679, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Right Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvrx128": { + "page": "memory/stvrx.md", + "family": "stvrx", + "xml_mnem": "stvrx128", + "opcode_hex": "0x10000543", + "primary_opcode": 4, + "extended_opcode": 1347, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Right Indexed 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvrxl": { + "page": "memory/stvrxl.md", + "family": "stvrxl", + "xml_mnem": "stvrxl", + "opcode_hex": "0x7C00074E", + "primary_opcode": 31, + "extended_opcode": 935, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Right Indexed LRU", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvrxl128": { + "page": "memory/stvrxl.md", + "family": "stvrxl", + "xml_mnem": "stvrxl128", + "opcode_hex": "0x10000743", + "primary_opcode": 4, + "extended_opcode": 1859, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Right Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvx": { + "page": "memory/stvx.md", + "family": "stvx", + "xml_mnem": "stvx", + "opcode_hex": "0x7C0001CE", + "primary_opcode": 31, + "extended_opcode": 231, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Indexed", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvx128": { + "page": "memory/stvx.md", + "family": "stvx", + "xml_mnem": "stvx128", + "opcode_hex": "0x100001C3", + "primary_opcode": 4, + "extended_opcode": 451, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Indexed 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvxl": { + "page": "memory/stvxl.md", + "family": "stvxl", + "xml_mnem": "stvxl", + "opcode_hex": "0x7C0003CE", + "primary_opcode": 31, + "extended_opcode": 487, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Vector Indexed LRU", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stvxl128": { + "page": "memory/stvxl.md", + "family": "stvxl", + "xml_mnem": "stvxl128", + "opcode_hex": "0x100003C3", + "primary_opcode": 4, + "extended_opcode": 963, + "form": "VX128_1", + "group": "memory", + "category": "memory", + "description": "Store Vector Indexed LRU 128", + "sync": false, + "reads": [ + { + "field": "VS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stw": { + "page": "memory/stw.md", + "family": "stw", + "xml_mnem": "stw", + "opcode_hex": "0x90000000", + "primary_opcode": 36, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Word", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stwbrx": { + "page": "memory/stwbrx.md", + "family": "stwbrx", + "xml_mnem": "stwbrx", + "opcode_hex": "0x7C00052C", + "primary_opcode": 31, + "extended_opcode": 662, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Word Byte-Reverse Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stwcx": { + "page": "memory/stwcx.md", + "family": "stwcx", + "xml_mnem": "stwcx", + "opcode_hex": "0x7C00012D", + "primary_opcode": 31, + "extended_opcode": 150, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Word Conditional Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "CR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": true + }, + "is_primary": true, + "flags": {} + }, + "stwu": { + "page": "memory/stw.md", + "family": "stw", + "xml_mnem": "stwu", + "opcode_hex": "0x94000000", + "primary_opcode": 37, + "extended_opcode": null, + "form": "D", + "group": "memory", + "category": "memory", + "description": "Store Word with Update", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "d", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stwux": { + "page": "memory/stw.md", + "family": "stw", + "xml_mnem": "stwux", + "opcode_hex": "0x7C00016E", + "primary_opcode": 31, + "extended_opcode": 183, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Word with Update Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "stwx": { + "page": "memory/stw.md", + "family": "stw", + "xml_mnem": "stwx", + "opcode_hex": "0x7C00012E", + "primary_opcode": 31, + "extended_opcode": 151, + "form": "X", + "group": "memory", + "category": "memory", + "description": "Store Word Indexed", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RA0", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subf": { + "page": "alu/subfx.md", + "family": "subfx", + "xml_mnem": "subfx", + "opcode_hex": "0x7C000050", + "primary_opcode": 31, + "extended_opcode": 40, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Subtract From", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subf.": { + "page": "alu/subfx.md", + "family": "subfx", + "variant_of": "subf", + "xml_mnem": "subfx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "subfc": { + "page": "alu/subfcx.md", + "family": "subfcx", + "xml_mnem": "subfcx", + "opcode_hex": "0x7C000010", + "primary_opcode": 31, + "extended_opcode": 8, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Subtract From Carrying", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subfc.": { + "page": "alu/subfcx.md", + "family": "subfcx", + "variant_of": "subfc", + "xml_mnem": "subfcx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "subfco": { + "page": "alu/subfcx.md", + "family": "subfcx", + "variant_of": "subfc", + "xml_mnem": "subfcx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "subfco.": { + "page": "alu/subfcx.md", + "family": "subfcx", + "variant_of": "subfc", + "xml_mnem": "subfcx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "subfe": { + "page": "alu/subfex.md", + "family": "subfex", + "xml_mnem": "subfex", + "opcode_hex": "0x7C000110", + "primary_opcode": 31, + "extended_opcode": 136, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Subtract From Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subfe.": { + "page": "alu/subfex.md", + "family": "subfex", + "variant_of": "subfe", + "xml_mnem": "subfex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "subfeo": { + "page": "alu/subfex.md", + "family": "subfex", + "variant_of": "subfe", + "xml_mnem": "subfex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "subfeo.": { + "page": "alu/subfex.md", + "family": "subfex", + "variant_of": "subfe", + "xml_mnem": "subfex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "subfic": { + "page": "alu/subficx.md", + "family": "subficx", + "xml_mnem": "subficx", + "opcode_hex": "0x20000000", + "primary_opcode": 8, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "Subtract From Immediate Carrying", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subfme": { + "page": "alu/subfmex.md", + "family": "subfmex", + "xml_mnem": "subfmex", + "opcode_hex": "0x7C0001D0", + "primary_opcode": 31, + "extended_opcode": 232, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Subtract From Minus One Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subfme.": { + "page": "alu/subfmex.md", + "family": "subfmex", + "variant_of": "subfme", + "xml_mnem": "subfmex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "subfmeo": { + "page": "alu/subfmex.md", + "family": "subfmex", + "variant_of": "subfme", + "xml_mnem": "subfmex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "subfmeo.": { + "page": "alu/subfmex.md", + "family": "subfmex", + "variant_of": "subfme", + "xml_mnem": "subfmex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "subfo": { + "page": "alu/subfx.md", + "family": "subfx", + "variant_of": "subf", + "xml_mnem": "subfx", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "subfo.": { + "page": "alu/subfx.md", + "family": "subfx", + "variant_of": "subf", + "xml_mnem": "subfx", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "subfze": { + "page": "alu/subfzex.md", + "family": "subfzex", + "xml_mnem": "subfzex", + "opcode_hex": "0x7C000190", + "primary_opcode": 31, + "extended_opcode": 200, + "form": "XO", + "group": "integer", + "category": "alu", + "description": "Subtract From Zero Extended", + "sync": false, + "reads": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CA", + "conditional": false + } + ], + "writes": [ + { + "field": "RD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + }, + { + "field": "OE", + "conditional": true + }, + { + "field": "CA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": true, + "OE": true, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "subfze.": { + "page": "alu/subfzex.md", + "family": "subfzex", + "variant_of": "subfze", + "xml_mnem": "subfzex", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "subfzeo": { + "page": "alu/subfzex.md", + "family": "subfzex", + "variant_of": "subfze", + "xml_mnem": "subfzex", + "flags": { + "OE": 1 + }, + "category": "alu" + }, + "subfzeo.": { + "page": "alu/subfzex.md", + "family": "subfzex", + "variant_of": "subfze", + "xml_mnem": "subfzex", + "flags": { + "OE": 1, + "Rc": 1 + }, + "category": "alu" + }, + "sync": { + "page": "alu/sync.md", + "family": "sync", + "xml_mnem": "sync", + "opcode_hex": "0x7C0004AC", + "primary_opcode": 31, + "extended_opcode": 598, + "form": "X", + "group": "integer", + "category": "alu", + "description": "Synchronize", + "sync": false, + "reads": [], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "td": { + "page": "branch/td.md", + "family": "td", + "xml_mnem": "td", + "opcode_hex": "0x7C000088", + "primary_opcode": 31, + "extended_opcode": 68, + "form": "X", + "group": "branch", + "category": "branch", + "description": "Trap Doubleword", + "sync": false, + "reads": [ + { + "field": "TO", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "tdi": { + "page": "branch/tdi.md", + "family": "tdi", + "xml_mnem": "tdi", + "opcode_hex": "0x08000000", + "primary_opcode": 2, + "extended_opcode": null, + "form": "D", + "group": "branch", + "category": "branch", + "description": "Trap Doubleword Immediate", + "sync": false, + "reads": [ + { + "field": "TO", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "tw": { + "page": "branch/tw.md", + "family": "tw", + "xml_mnem": "tw", + "opcode_hex": "0x7C000008", + "primary_opcode": 31, + "extended_opcode": 4, + "form": "X", + "group": "branch", + "category": "branch", + "description": "Trap Word", + "sync": false, + "reads": [ + { + "field": "TO", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "twi": { + "page": "branch/twi.md", + "family": "twi", + "xml_mnem": "twi", + "opcode_hex": "0x0C000000", + "primary_opcode": 3, + "extended_opcode": null, + "form": "D", + "group": "branch", + "category": "branch", + "description": "Trap Word Immediate", + "sync": false, + "reads": [ + { + "field": "TO", + "conditional": false + }, + { + "field": "RA", + "conditional": false + }, + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddcuw": { + "page": "vmx/vaddcuw.md", + "family": "vaddcuw", + "xml_mnem": "vaddcuw", + "opcode_hex": "0x10000180", + "primary_opcode": 4, + "extended_opcode": 384, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Carryout Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddfp": { + "page": "vmx/vaddfp.md", + "family": "vaddfp", + "xml_mnem": "vaddfp", + "opcode_hex": "0x1000000A", + "primary_opcode": 4, + "extended_opcode": 10, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddfp128": { + "page": "vmx/vaddfp.md", + "family": "vaddfp", + "xml_mnem": "vaddfp128", + "opcode_hex": "0x14000010", + "primary_opcode": 5, + "extended_opcode": 16, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Add Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddsbs": { + "page": "vmx/vaddsbs.md", + "family": "vaddsbs", + "xml_mnem": "vaddsbs", + "opcode_hex": "0x10000300", + "primary_opcode": 4, + "extended_opcode": 768, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Signed Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddshs": { + "page": "vmx/vaddshs.md", + "family": "vaddshs", + "xml_mnem": "vaddshs", + "opcode_hex": "0x10000340", + "primary_opcode": 4, + "extended_opcode": 832, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddsws": { + "page": "vmx/vaddsws.md", + "family": "vaddsws", + "xml_mnem": "vaddsws", + "opcode_hex": "0x10000380", + "primary_opcode": 4, + "extended_opcode": 896, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Signed Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddubm": { + "page": "vmx/vaddubm.md", + "family": "vaddubm", + "xml_mnem": "vaddubm", + "opcode_hex": "0x10000000", + "primary_opcode": 4, + "extended_opcode": 0, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Byte Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vaddubs": { + "page": "vmx/vaddubs.md", + "family": "vaddubs", + "xml_mnem": "vaddubs", + "opcode_hex": "0x10000200", + "primary_opcode": 4, + "extended_opcode": 512, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vadduhm": { + "page": "vmx/vadduhm.md", + "family": "vadduhm", + "xml_mnem": "vadduhm", + "opcode_hex": "0x10000040", + "primary_opcode": 4, + "extended_opcode": 64, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Half Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vadduhs": { + "page": "vmx/vadduhs.md", + "family": "vadduhs", + "xml_mnem": "vadduhs", + "opcode_hex": "0x10000240", + "primary_opcode": 4, + "extended_opcode": 576, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vadduwm": { + "page": "vmx/vadduwm.md", + "family": "vadduwm", + "xml_mnem": "vadduwm", + "opcode_hex": "0x10000080", + "primary_opcode": 4, + "extended_opcode": 128, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vadduws": { + "page": "vmx/vadduws.md", + "family": "vadduws", + "xml_mnem": "vadduws", + "opcode_hex": "0x10000280", + "primary_opcode": 4, + "extended_opcode": 640, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Add Unsigned Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vand": { + "page": "vmx/vand.md", + "family": "vand", + "xml_mnem": "vand", + "opcode_hex": "0x10000404", + "primary_opcode": 4, + "extended_opcode": 1028, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Logical AND", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vand128": { + "page": "vmx/vand.md", + "family": "vand", + "xml_mnem": "vand128", + "opcode_hex": "0x14000210", + "primary_opcode": 5, + "extended_opcode": 528, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Logical AND", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vandc": { + "page": "vmx/vandc.md", + "family": "vandc", + "xml_mnem": "vandc", + "opcode_hex": "0x10000444", + "primary_opcode": 4, + "extended_opcode": 1092, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Logical AND with Complement", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vandc128": { + "page": "vmx/vandc.md", + "family": "vandc", + "xml_mnem": "vandc128", + "opcode_hex": "0x14000250", + "primary_opcode": 5, + "extended_opcode": 592, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Logical AND with Complement", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavgsb": { + "page": "vmx/vavgsb.md", + "family": "vavgsb", + "xml_mnem": "vavgsb", + "opcode_hex": "0x10000502", + "primary_opcode": 4, + "extended_opcode": 1282, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavgsh": { + "page": "vmx/vavgsh.md", + "family": "vavgsh", + "xml_mnem": "vavgsh", + "opcode_hex": "0x10000542", + "primary_opcode": 4, + "extended_opcode": 1346, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavgsw": { + "page": "vmx/vavgsw.md", + "family": "vavgsw", + "xml_mnem": "vavgsw", + "opcode_hex": "0x10000582", + "primary_opcode": 4, + "extended_opcode": 1410, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Signed Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavgub": { + "page": "vmx/vavgub.md", + "family": "vavgub", + "xml_mnem": "vavgub", + "opcode_hex": "0x10000402", + "primary_opcode": 4, + "extended_opcode": 1026, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavguh": { + "page": "vmx/vavguh.md", + "family": "vavguh", + "xml_mnem": "vavguh", + "opcode_hex": "0x10000442", + "primary_opcode": 4, + "extended_opcode": 1090, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vavguw": { + "page": "vmx/vavguw.md", + "family": "vavguw", + "xml_mnem": "vavguw", + "opcode_hex": "0x10000482", + "primary_opcode": 4, + "extended_opcode": 1154, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Average Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcfpsxws128": { + "page": "vmx128/vcfpsxws128.md", + "family": "vcfpsxws128", + "xml_mnem": "vcfpsxws128", + "opcode_hex": "0x18000230", + "primary_opcode": 6, + "extended_opcode": 560, + "form": "VX128_3", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcfpuxws128": { + "page": "vmx128/vcfpuxws128.md", + "family": "vcfpuxws128", + "xml_mnem": "vcfpuxws128", + "opcode_hex": "0x18000270", + "primary_opcode": 6, + "extended_opcode": 624, + "form": "VX128_3", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcfs": { + "page": "vmx/vcfsx.md", + "family": "vcfsx", + "xml_mnem": "vcfsx", + "opcode_hex": "0x1000034A", + "primary_opcode": 4, + "extended_opcode": 842, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Convert from Signed Fixed-Point Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcfu": { + "page": "vmx/vcfux.md", + "family": "vcfux", + "xml_mnem": "vcfux", + "opcode_hex": "0x1000030A", + "primary_opcode": 4, + "extended_opcode": 778, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Convert from Unsigned Fixed-Point Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpbfp": { + "page": "vmx/vcmpbfp.md", + "family": "vcmpbfp", + "xml_mnem": "vcmpbfp", + "opcode_hex": "0x100003C6", + "primary_opcode": 4, + "extended_opcode": 966, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Bounds Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpbfp.": { + "page": "vmx/vcmpbfp.md", + "family": "vcmpbfp", + "variant_of": "vcmpbfp", + "xml_mnem": "vcmpbfp", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpbfp128": { + "page": "vmx/vcmpbfp.md", + "family": "vcmpbfp", + "xml_mnem": "vcmpbfp128", + "opcode_hex": "0x18000180", + "primary_opcode": 6, + "extended_opcode": 384, + "form": "VX128_R", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Compare Bounds Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpbfp128.": { + "page": "vmx/vcmpbfp.md", + "family": "vcmpbfp", + "variant_of": "vcmpbfp128", + "xml_mnem": "vcmpbfp128", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpeqfp": { + "page": "vmx/vcmpeqfp.md", + "family": "vcmpeqfp", + "xml_mnem": "vcmpeqfp", + "opcode_hex": "0x100000C6", + "primary_opcode": 4, + "extended_opcode": 198, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Equal-to Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpeqfp.": { + "page": "vmx/vcmpeqfp.md", + "family": "vcmpeqfp", + "variant_of": "vcmpeqfp", + "xml_mnem": "vcmpeqfp", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpeqfp128": { + "page": "vmx/vcmpeqfp.md", + "family": "vcmpeqfp", + "xml_mnem": "vcmpeqfp128", + "opcode_hex": "0x18000000", + "primary_opcode": 6, + "extended_opcode": 0, + "form": "VX128_R", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Compare Equal-to Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpeqfp128.": { + "page": "vmx/vcmpeqfp.md", + "family": "vcmpeqfp", + "variant_of": "vcmpeqfp128", + "xml_mnem": "vcmpeqfp128", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpequb": { + "page": "vmx/vcmpequb.md", + "family": "vcmpequb", + "xml_mnem": "vcmpequb", + "opcode_hex": "0x10000006", + "primary_opcode": 4, + "extended_opcode": 6, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Equal-to Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpequb.": { + "page": "vmx/vcmpequb.md", + "family": "vcmpequb", + "variant_of": "vcmpequb", + "xml_mnem": "vcmpequb", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpequh": { + "page": "vmx/vcmpequh.md", + "family": "vcmpequh", + "xml_mnem": "vcmpequh", + "opcode_hex": "0x10000046", + "primary_opcode": 4, + "extended_opcode": 70, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Equal-to Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpequh.": { + "page": "vmx/vcmpequh.md", + "family": "vcmpequh", + "variant_of": "vcmpequh", + "xml_mnem": "vcmpequh", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpequw": { + "page": "vmx/vcmpequw.md", + "family": "vcmpequw", + "xml_mnem": "vcmpequw", + "opcode_hex": "0x10000086", + "primary_opcode": 4, + "extended_opcode": 134, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Equal-to Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpequw.": { + "page": "vmx/vcmpequw.md", + "family": "vcmpequw", + "variant_of": "vcmpequw", + "xml_mnem": "vcmpequw", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpequw128": { + "page": "vmx/vcmpequw.md", + "family": "vcmpequw", + "xml_mnem": "vcmpequw128", + "opcode_hex": "0x18000200", + "primary_opcode": 6, + "extended_opcode": 512, + "form": "VX128_R", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Compare Equal-to Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpequw128.": { + "page": "vmx/vcmpequw.md", + "family": "vcmpequw", + "variant_of": "vcmpequw128", + "xml_mnem": "vcmpequw128", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgefp": { + "page": "vmx/vcmpgefp.md", + "family": "vcmpgefp", + "xml_mnem": "vcmpgefp", + "opcode_hex": "0x100001C6", + "primary_opcode": 4, + "extended_opcode": 454, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than-or-Equal-to Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgefp.": { + "page": "vmx/vcmpgefp.md", + "family": "vcmpgefp", + "variant_of": "vcmpgefp", + "xml_mnem": "vcmpgefp", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgefp128": { + "page": "vmx/vcmpgefp.md", + "family": "vcmpgefp", + "xml_mnem": "vcmpgefp128", + "opcode_hex": "0x18000080", + "primary_opcode": 6, + "extended_opcode": 128, + "form": "VX128_R", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Compare Greater-Than-or-Equal-to Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgefp128.": { + "page": "vmx/vcmpgefp.md", + "family": "vcmpgefp", + "variant_of": "vcmpgefp128", + "xml_mnem": "vcmpgefp128", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtfp": { + "page": "vmx/vcmpgtfp.md", + "family": "vcmpgtfp", + "xml_mnem": "vcmpgtfp", + "opcode_hex": "0x100002C6", + "primary_opcode": 4, + "extended_opcode": 710, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtfp.": { + "page": "vmx/vcmpgtfp.md", + "family": "vcmpgtfp", + "variant_of": "vcmpgtfp", + "xml_mnem": "vcmpgtfp", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtfp128": { + "page": "vmx/vcmpgtfp.md", + "family": "vcmpgtfp", + "xml_mnem": "vcmpgtfp128", + "opcode_hex": "0x18000100", + "primary_opcode": 6, + "extended_opcode": 256, + "form": "VX128_R", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Compare Greater-Than Floating-Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtfp128.": { + "page": "vmx/vcmpgtfp.md", + "family": "vcmpgtfp", + "variant_of": "vcmpgtfp128", + "xml_mnem": "vcmpgtfp128", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtsb": { + "page": "vmx/vcmpgtsb.md", + "family": "vcmpgtsb", + "xml_mnem": "vcmpgtsb", + "opcode_hex": "0x10000306", + "primary_opcode": 4, + "extended_opcode": 774, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtsb.": { + "page": "vmx/vcmpgtsb.md", + "family": "vcmpgtsb", + "variant_of": "vcmpgtsb", + "xml_mnem": "vcmpgtsb", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtsh": { + "page": "vmx/vcmpgtsh.md", + "family": "vcmpgtsh", + "xml_mnem": "vcmpgtsh", + "opcode_hex": "0x10000346", + "primary_opcode": 4, + "extended_opcode": 838, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtsh.": { + "page": "vmx/vcmpgtsh.md", + "family": "vcmpgtsh", + "variant_of": "vcmpgtsh", + "xml_mnem": "vcmpgtsh", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtsw": { + "page": "vmx/vcmpgtsw.md", + "family": "vcmpgtsw", + "xml_mnem": "vcmpgtsw", + "opcode_hex": "0x10000386", + "primary_opcode": 4, + "extended_opcode": 902, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Signed Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtsw.": { + "page": "vmx/vcmpgtsw.md", + "family": "vcmpgtsw", + "variant_of": "vcmpgtsw", + "xml_mnem": "vcmpgtsw", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtub": { + "page": "vmx/vcmpgtub.md", + "family": "vcmpgtub", + "xml_mnem": "vcmpgtub", + "opcode_hex": "0x10000206", + "primary_opcode": 4, + "extended_opcode": 518, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtub.": { + "page": "vmx/vcmpgtub.md", + "family": "vcmpgtub", + "variant_of": "vcmpgtub", + "xml_mnem": "vcmpgtub", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtuh": { + "page": "vmx/vcmpgtuh.md", + "family": "vcmpgtuh", + "xml_mnem": "vcmpgtuh", + "opcode_hex": "0x10000246", + "primary_opcode": 4, + "extended_opcode": 582, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtuh.": { + "page": "vmx/vcmpgtuh.md", + "family": "vcmpgtuh", + "variant_of": "vcmpgtuh", + "xml_mnem": "vcmpgtuh", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcmpgtuw": { + "page": "vmx/vcmpgtuw.md", + "family": "vcmpgtuw", + "xml_mnem": "vcmpgtuw", + "opcode_hex": "0x10000286", + "primary_opcode": 4, + "extended_opcode": 646, + "form": "VC", + "group": "vmx", + "category": "vmx", + "description": "Vector Compare Greater-Than Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcmpgtuw.": { + "page": "vmx/vcmpgtuw.md", + "family": "vcmpgtuw", + "variant_of": "vcmpgtuw", + "xml_mnem": "vcmpgtuw", + "flags": { + "Rc": 1 + }, + "category": "vmx" + }, + "vcsxwfp128": { + "page": "vmx128/vcsxwfp128.md", + "family": "vcsxwfp128", + "xml_mnem": "vcsxwfp128", + "opcode_hex": "0x180002B0", + "primary_opcode": 6, + "extended_opcode": 688, + "form": "VX128_3", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Convert From Signed Fixed-Point Word to Floating-Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vctsxs": { + "page": "vmx/vctsxs.md", + "family": "vctsxs", + "xml_mnem": "vctsxs", + "opcode_hex": "0x100003CA", + "primary_opcode": 4, + "extended_opcode": 970, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Convert to Signed Fixed-Point Word Saturate", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vctuxs": { + "page": "vmx/vctuxs.md", + "family": "vctuxs", + "xml_mnem": "vctuxs", + "opcode_hex": "0x1000038A", + "primary_opcode": 4, + "extended_opcode": 906, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Convert to Unsigned Fixed-Point Word Saturate", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vcuxwfp128": { + "page": "vmx128/vcuxwfp128.md", + "family": "vcuxwfp128", + "xml_mnem": "vcuxwfp128", + "opcode_hex": "0x180002F0", + "primary_opcode": 6, + "extended_opcode": 752, + "form": "VX128_3", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vexptefp": { + "page": "vmx/vexptefp.md", + "family": "vexptefp", + "xml_mnem": "vexptefp", + "opcode_hex": "0x1000018A", + "primary_opcode": 4, + "extended_opcode": 394, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector 2 Raised to the Exponent Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vexptefp128": { + "page": "vmx/vexptefp.md", + "family": "vexptefp", + "xml_mnem": "vexptefp128", + "opcode_hex": "0x180006B0", + "primary_opcode": 6, + "extended_opcode": 1712, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Log2 Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vlogefp": { + "page": "vmx/vlogefp.md", + "family": "vlogefp", + "xml_mnem": "vlogefp", + "opcode_hex": "0x100001CA", + "primary_opcode": 4, + "extended_opcode": 458, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Log2 Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vlogefp128": { + "page": "vmx/vlogefp.md", + "family": "vlogefp", + "xml_mnem": "vlogefp128", + "opcode_hex": "0x180006F0", + "primary_opcode": 6, + "extended_opcode": 1776, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Log2 Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaddcfp128": { + "page": "vmx128/vmaddcfp128.md", + "family": "vmaddcfp128", + "xml_mnem": "vmaddcfp128", + "opcode_hex": "0x14000110", + "primary_opcode": 5, + "extended_opcode": 272, + "form": "VX128", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Multiply Add Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VD", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaddfp": { + "page": "vmx/vmaddfp.md", + "family": "vmaddfp", + "xml_mnem": "vmaddfp", + "opcode_hex": "0x1000002E", + "primary_opcode": 4, + "extended_opcode": 46, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Add Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VC", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaddfp128": { + "page": "vmx/vmaddfp.md", + "family": "vmaddfp", + "xml_mnem": "vmaddfp128", + "opcode_hex": "0x140000D0", + "primary_opcode": 5, + "extended_opcode": 208, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Multiply Add Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VC", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxfp": { + "page": "vmx/vmaxfp.md", + "family": "vmaxfp", + "xml_mnem": "vmaxfp", + "opcode_hex": "0x1000040A", + "primary_opcode": 4, + "extended_opcode": 1034, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxfp128": { + "page": "vmx/vmaxfp.md", + "family": "vmaxfp", + "xml_mnem": "vmaxfp128", + "opcode_hex": "0x18000280", + "primary_opcode": 6, + "extended_opcode": 640, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Maximum Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxsb": { + "page": "vmx/vmaxsb.md", + "family": "vmaxsb", + "xml_mnem": "vmaxsb", + "opcode_hex": "0x10000102", + "primary_opcode": 4, + "extended_opcode": 258, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxsh": { + "page": "vmx/vmaxsh.md", + "family": "vmaxsh", + "xml_mnem": "vmaxsh", + "opcode_hex": "0x10000142", + "primary_opcode": 4, + "extended_opcode": 322, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxsw": { + "page": "vmx/vmaxsw.md", + "family": "vmaxsw", + "xml_mnem": "vmaxsw", + "opcode_hex": "0x10000182", + "primary_opcode": 4, + "extended_opcode": 386, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Signed Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxub": { + "page": "vmx/vmaxub.md", + "family": "vmaxub", + "xml_mnem": "vmaxub", + "opcode_hex": "0x10000002", + "primary_opcode": 4, + "extended_opcode": 2, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxuh": { + "page": "vmx/vmaxuh.md", + "family": "vmaxuh", + "xml_mnem": "vmaxuh", + "opcode_hex": "0x10000042", + "primary_opcode": 4, + "extended_opcode": 66, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmaxuw": { + "page": "vmx/vmaxuw.md", + "family": "vmaxuw", + "xml_mnem": "vmaxuw", + "opcode_hex": "0x10000082", + "primary_opcode": 4, + "extended_opcode": 130, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Maximum Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmhaddshs": { + "page": "vmx/vmhaddshs.md", + "family": "vmhaddshs", + "xml_mnem": "vmhaddshs", + "opcode_hex": "0x10000020", + "primary_opcode": 4, + "extended_opcode": 32, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-High and Add Signed Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmhraddshs": { + "page": "vmx/vmhraddshs.md", + "family": "vmhraddshs", + "xml_mnem": "vmhraddshs", + "opcode_hex": "0x10000021", + "primary_opcode": 4, + "extended_opcode": 33, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-High Round and Add Signed Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminfp": { + "page": "vmx/vminfp.md", + "family": "vminfp", + "xml_mnem": "vminfp", + "opcode_hex": "0x1000044A", + "primary_opcode": 4, + "extended_opcode": 1098, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminfp128": { + "page": "vmx/vminfp.md", + "family": "vminfp", + "xml_mnem": "vminfp128", + "opcode_hex": "0x180002C0", + "primary_opcode": 6, + "extended_opcode": 704, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Minimum Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminsb": { + "page": "vmx/vminsb.md", + "family": "vminsb", + "xml_mnem": "vminsb", + "opcode_hex": "0x10000302", + "primary_opcode": 4, + "extended_opcode": 770, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminsh": { + "page": "vmx/vminsh.md", + "family": "vminsh", + "xml_mnem": "vminsh", + "opcode_hex": "0x10000342", + "primary_opcode": 4, + "extended_opcode": 834, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminsw": { + "page": "vmx/vminsw.md", + "family": "vminsw", + "xml_mnem": "vminsw", + "opcode_hex": "0x10000382", + "primary_opcode": 4, + "extended_opcode": 898, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Signed Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminub": { + "page": "vmx/vminub.md", + "family": "vminub", + "xml_mnem": "vminub", + "opcode_hex": "0x10000202", + "primary_opcode": 4, + "extended_opcode": 514, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminuh": { + "page": "vmx/vminuh.md", + "family": "vminuh", + "xml_mnem": "vminuh", + "opcode_hex": "0x10000242", + "primary_opcode": 4, + "extended_opcode": 578, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vminuw": { + "page": "vmx/vminuw.md", + "family": "vminuw", + "xml_mnem": "vminuw", + "opcode_hex": "0x10000282", + "primary_opcode": 4, + "extended_opcode": 642, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Minimum Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmladduhm": { + "page": "vmx/vmladduhm.md", + "family": "vmladduhm", + "xml_mnem": "vmladduhm", + "opcode_hex": "0x10000022", + "primary_opcode": 4, + "extended_opcode": 34, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Low and Add Unsigned Half Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrghb": { + "page": "vmx/vmrghb.md", + "family": "vmrghb", + "xml_mnem": "vmrghb", + "opcode_hex": "0x1000000C", + "primary_opcode": 4, + "extended_opcode": 12, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge High Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrghh": { + "page": "vmx/vmrghh.md", + "family": "vmrghh", + "xml_mnem": "vmrghh", + "opcode_hex": "0x1000004C", + "primary_opcode": 4, + "extended_opcode": 76, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge High Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrghw": { + "page": "vmx/vmrghw.md", + "family": "vmrghw", + "xml_mnem": "vmrghw", + "opcode_hex": "0x1000008C", + "primary_opcode": 4, + "extended_opcode": 140, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge High Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrghw128": { + "page": "vmx/vmrghw.md", + "family": "vmrghw", + "xml_mnem": "vmrghw128", + "opcode_hex": "0x18000300", + "primary_opcode": 6, + "extended_opcode": 768, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Merge High Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrglb": { + "page": "vmx/vmrglb.md", + "family": "vmrglb", + "xml_mnem": "vmrglb", + "opcode_hex": "0x1000010C", + "primary_opcode": 4, + "extended_opcode": 268, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge Low Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrglh": { + "page": "vmx/vmrglh.md", + "family": "vmrglh", + "xml_mnem": "vmrglh", + "opcode_hex": "0x1000014C", + "primary_opcode": 4, + "extended_opcode": 332, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge Low Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrglw": { + "page": "vmx/vmrglw.md", + "family": "vmrglw", + "xml_mnem": "vmrglw", + "opcode_hex": "0x1000018C", + "primary_opcode": 4, + "extended_opcode": 396, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Merge Low Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmrglw128": { + "page": "vmx/vmrglw.md", + "family": "vmrglw", + "xml_mnem": "vmrglw128", + "opcode_hex": "0x18000340", + "primary_opcode": 6, + "extended_opcode": 832, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Merge Low Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsum3fp128": { + "page": "vmx128/vmsum3fp128.md", + "family": "vmsum3fp128", + "xml_mnem": "vmsum3fp128", + "opcode_hex": "0x14000190", + "primary_opcode": 5, + "extended_opcode": 400, + "form": "VX128", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Multiply Sum 3-way Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsum4fp128": { + "page": "vmx128/vmsum4fp128.md", + "family": "vmsum4fp128", + "xml_mnem": "vmsum4fp128", + "opcode_hex": "0x140001D0", + "primary_opcode": 5, + "extended_opcode": 464, + "form": "VX128", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Multiply Sum 4-way Floating-Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsummbm": { + "page": "vmx/vmsummbm.md", + "family": "vmsummbm", + "xml_mnem": "vmsummbm", + "opcode_hex": "0x10000025", + "primary_opcode": 4, + "extended_opcode": 37, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Mixed-Sign Byte Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsumshm": { + "page": "vmx/vmsumshm.md", + "family": "vmsumshm", + "xml_mnem": "vmsumshm", + "opcode_hex": "0x10000028", + "primary_opcode": 4, + "extended_opcode": 40, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Signed Half Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsumshs": { + "page": "vmx/vmsumshs.md", + "family": "vmsumshs", + "xml_mnem": "vmsumshs", + "opcode_hex": "0x10000029", + "primary_opcode": 4, + "extended_opcode": 41, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsumubm": { + "page": "vmx/vmsumubm.md", + "family": "vmsumubm", + "xml_mnem": "vmsumubm", + "opcode_hex": "0x10000024", + "primary_opcode": 4, + "extended_opcode": 36, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Unsigned Byte Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsumuhm": { + "page": "vmx/vmsumuhm.md", + "family": "vmsumuhm", + "xml_mnem": "vmsumuhm", + "opcode_hex": "0x10000026", + "primary_opcode": 4, + "extended_opcode": 38, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Unsigned Half Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmsumuhs": { + "page": "vmx/vmsumuhs.md", + "family": "vmsumuhs", + "xml_mnem": "vmsumuhs", + "opcode_hex": "0x10000027", + "primary_opcode": 4, + "extended_opcode": 39, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply-Sum Unsigned Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulesb": { + "page": "vmx/vmulesb.md", + "family": "vmulesb", + "xml_mnem": "vmulesb", + "opcode_hex": "0x10000308", + "primary_opcode": 4, + "extended_opcode": 776, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Even Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulesh": { + "page": "vmx/vmulesh.md", + "family": "vmulesh", + "xml_mnem": "vmulesh", + "opcode_hex": "0x10000348", + "primary_opcode": 4, + "extended_opcode": 840, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Even Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmuleub": { + "page": "vmx/vmuleub.md", + "family": "vmuleub", + "xml_mnem": "vmuleub", + "opcode_hex": "0x10000208", + "primary_opcode": 4, + "extended_opcode": 520, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Even Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmuleuh": { + "page": "vmx/vmuleuh.md", + "family": "vmuleuh", + "xml_mnem": "vmuleuh", + "opcode_hex": "0x10000248", + "primary_opcode": 4, + "extended_opcode": 584, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Even Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulfp128": { + "page": "vmx128/vmulfp128.md", + "family": "vmulfp128", + "xml_mnem": "vmulfp128", + "opcode_hex": "0x14000090", + "primary_opcode": 5, + "extended_opcode": 144, + "form": "VX128", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Multiply Floating-Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulosb": { + "page": "vmx/vmulosb.md", + "family": "vmulosb", + "xml_mnem": "vmulosb", + "opcode_hex": "0x10000108", + "primary_opcode": 4, + "extended_opcode": 264, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Odd Signed Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulosh": { + "page": "vmx/vmulosh.md", + "family": "vmulosh", + "xml_mnem": "vmulosh", + "opcode_hex": "0x10000148", + "primary_opcode": 4, + "extended_opcode": 328, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Odd Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmuloub": { + "page": "vmx/vmuloub.md", + "family": "vmuloub", + "xml_mnem": "vmuloub", + "opcode_hex": "0x10000008", + "primary_opcode": 4, + "extended_opcode": 8, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Odd Unsigned Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vmulouh": { + "page": "vmx/vmulouh.md", + "family": "vmulouh", + "xml_mnem": "vmulouh", + "opcode_hex": "0x10000048", + "primary_opcode": 4, + "extended_opcode": 72, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Multiply Odd Unsigned Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vnmsubfp": { + "page": "vmx/vnmsubfp.md", + "family": "vnmsubfp", + "xml_mnem": "vnmsubfp", + "opcode_hex": "0x1000002F", + "primary_opcode": 4, + "extended_opcode": 47, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Negative Multiply-Subtract Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VC", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vnmsubfp128": { + "page": "vmx/vnmsubfp.md", + "family": "vnmsubfp", + "xml_mnem": "vnmsubfp128", + "opcode_hex": "0x14000150", + "primary_opcode": 5, + "extended_opcode": 336, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Negative Multiply-Subtract Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VD", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vnor": { + "page": "vmx/vnor.md", + "family": "vnor", + "xml_mnem": "vnor", + "opcode_hex": "0x10000504", + "primary_opcode": 4, + "extended_opcode": 1284, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Logical NOR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vnor128": { + "page": "vmx/vnor.md", + "family": "vnor", + "xml_mnem": "vnor128", + "opcode_hex": "0x14000290", + "primary_opcode": 5, + "extended_opcode": 656, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Logical NOR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vor": { + "page": "vmx/vor.md", + "family": "vor", + "xml_mnem": "vor", + "opcode_hex": "0x10000484", + "primary_opcode": 4, + "extended_opcode": 1156, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Logical OR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vor128": { + "page": "vmx/vor.md", + "family": "vor", + "xml_mnem": "vor128", + "opcode_hex": "0x140002D0", + "primary_opcode": 5, + "extended_opcode": 720, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Logical OR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vperm": { + "page": "vmx/vperm.md", + "family": "vperm", + "xml_mnem": "vperm", + "opcode_hex": "0x1000002B", + "primary_opcode": 4, + "extended_opcode": 43, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Permute", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vperm128": { + "page": "vmx/vperm.md", + "family": "vperm", + "xml_mnem": "vperm128", + "opcode_hex": "0x14000000", + "primary_opcode": 5, + "extended_opcode": 0, + "form": "VX128_2", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Permute", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpermwi128": { + "page": "vmx128/vpermwi128.md", + "family": "vpermwi128", + "xml_mnem": "vpermwi128", + "opcode_hex": "0x18000210", + "primary_opcode": 6, + "extended_opcode": 528, + "form": "VX128_P", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Permutate Word Immediate", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkd3d128": { + "page": "vmx128/vpkd3d128.md", + "family": "vpkd3d128", + "xml_mnem": "vpkd3d128", + "opcode_hex": "0x18000610", + "primary_opcode": 6, + "extended_opcode": 1552, + "form": "VX128_4", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkp": { + "page": "vmx/vpkpx.md", + "family": "vpkpx", + "xml_mnem": "vpkpx", + "opcode_hex": "0x1000030E", + "primary_opcode": 4, + "extended_opcode": 782, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Pixel", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkshss": { + "page": "vmx/vpkshss.md", + "family": "vpkshss", + "xml_mnem": "vpkshss", + "opcode_hex": "0x1000018E", + "primary_opcode": 4, + "extended_opcode": 398, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Signed Half Word Signed Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkshss128": { + "page": "vmx/vpkshss.md", + "family": "vpkshss", + "xml_mnem": "vpkshss128", + "opcode_hex": "0x14000200", + "primary_opcode": 5, + "extended_opcode": 512, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Signed Half Word Signed Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkshus": { + "page": "vmx/vpkshus.md", + "family": "vpkshus", + "xml_mnem": "vpkshus", + "opcode_hex": "0x1000010E", + "primary_opcode": 4, + "extended_opcode": 270, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Signed Half Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkshus128": { + "page": "vmx/vpkshus.md", + "family": "vpkshus", + "xml_mnem": "vpkshus128", + "opcode_hex": "0x14000240", + "primary_opcode": 5, + "extended_opcode": 576, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Signed Half Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkswss": { + "page": "vmx/vpkswss.md", + "family": "vpkswss", + "xml_mnem": "vpkswss", + "opcode_hex": "0x100001CE", + "primary_opcode": 4, + "extended_opcode": 462, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Signed Word Signed Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkswss128": { + "page": "vmx/vpkswss.md", + "family": "vpkswss", + "xml_mnem": "vpkswss128", + "opcode_hex": "0x14000280", + "primary_opcode": 5, + "extended_opcode": 640, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Signed Word Signed Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkswus": { + "page": "vmx/vpkswus.md", + "family": "vpkswus", + "xml_mnem": "vpkswus", + "opcode_hex": "0x1000014E", + "primary_opcode": 4, + "extended_opcode": 334, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Signed Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkswus128": { + "page": "vmx/vpkswus.md", + "family": "vpkswus", + "xml_mnem": "vpkswus128", + "opcode_hex": "0x140002C0", + "primary_opcode": 5, + "extended_opcode": 704, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Signed Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuhum": { + "page": "vmx/vpkuhum.md", + "family": "vpkuhum", + "xml_mnem": "vpkuhum", + "opcode_hex": "0x1000000E", + "primary_opcode": 4, + "extended_opcode": 14, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Unsigned Half Word Unsigned Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuhum128": { + "page": "vmx/vpkuhum.md", + "family": "vpkuhum", + "xml_mnem": "vpkuhum128", + "opcode_hex": "0x14000300", + "primary_opcode": 5, + "extended_opcode": 768, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Unsigned Half Word Unsigned Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuhus": { + "page": "vmx/vpkuhus.md", + "family": "vpkuhus", + "xml_mnem": "vpkuhus", + "opcode_hex": "0x1000008E", + "primary_opcode": 4, + "extended_opcode": 142, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Unsigned Half Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuhus128": { + "page": "vmx/vpkuhus.md", + "family": "vpkuhus", + "xml_mnem": "vpkuhus128", + "opcode_hex": "0x14000340", + "primary_opcode": 5, + "extended_opcode": 832, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Unsigned Half Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuwum": { + "page": "vmx/vpkuwum.md", + "family": "vpkuwum", + "xml_mnem": "vpkuwum", + "opcode_hex": "0x1000004E", + "primary_opcode": 4, + "extended_opcode": 78, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Unsigned Word Unsigned Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuwum128": { + "page": "vmx/vpkuwum.md", + "family": "vpkuwum", + "xml_mnem": "vpkuwum128", + "opcode_hex": "0x14000380", + "primary_opcode": 5, + "extended_opcode": 896, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Unsigned Word Unsigned Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuwus": { + "page": "vmx/vpkuwus.md", + "family": "vpkuwus", + "xml_mnem": "vpkuwus", + "opcode_hex": "0x100000CE", + "primary_opcode": 4, + "extended_opcode": 206, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Pack Unsigned Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vpkuwus128": { + "page": "vmx/vpkuwus.md", + "family": "vpkuwus", + "xml_mnem": "vpkuwus128", + "opcode_hex": "0x140003C0", + "primary_opcode": 5, + "extended_opcode": 960, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Pack Unsigned Word Unsigned Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrefp": { + "page": "vmx/vrefp.md", + "family": "vrefp", + "xml_mnem": "vrefp", + "opcode_hex": "0x1000010A", + "primary_opcode": 4, + "extended_opcode": 266, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Reciprocal Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrefp128": { + "page": "vmx/vrefp.md", + "family": "vrefp", + "xml_mnem": "vrefp128", + "opcode_hex": "0x18000630", + "primary_opcode": 6, + "extended_opcode": 1584, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Reciprocal Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfim": { + "page": "vmx/vrfim.md", + "family": "vrfim", + "xml_mnem": "vrfim", + "opcode_hex": "0x100002CA", + "primary_opcode": 4, + "extended_opcode": 714, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Round to Floating-Point Integer toward -Infinity", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfim128": { + "page": "vmx/vrfim.md", + "family": "vrfim", + "xml_mnem": "vrfim128", + "opcode_hex": "0x18000330", + "primary_opcode": 6, + "extended_opcode": 816, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Round to Floating-Point Integer toward -Infinity", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfin": { + "page": "vmx/vrfin.md", + "family": "vrfin", + "xml_mnem": "vrfin", + "opcode_hex": "0x1000020A", + "primary_opcode": 4, + "extended_opcode": 522, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Round to Floating-Point Integer Nearest", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfin128": { + "page": "vmx/vrfin.md", + "family": "vrfin", + "xml_mnem": "vrfin128", + "opcode_hex": "0x18000370", + "primary_opcode": 6, + "extended_opcode": 880, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Round to Floating-Point Integer Nearest", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfip": { + "page": "vmx/vrfip.md", + "family": "vrfip", + "xml_mnem": "vrfip", + "opcode_hex": "0x1000028A", + "primary_opcode": 4, + "extended_opcode": 650, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Round to Floating-Point Integer toward +Infinity", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfip128": { + "page": "vmx/vrfip.md", + "family": "vrfip", + "xml_mnem": "vrfip128", + "opcode_hex": "0x180003B0", + "primary_opcode": 6, + "extended_opcode": 944, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Round to Floating-Point Integer toward +Infinity", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfiz": { + "page": "vmx/vrfiz.md", + "family": "vrfiz", + "xml_mnem": "vrfiz", + "opcode_hex": "0x1000024A", + "primary_opcode": 4, + "extended_opcode": 586, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Round to Floating-Point Integer toward Zero", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrfiz128": { + "page": "vmx/vrfiz.md", + "family": "vrfiz", + "xml_mnem": "vrfiz128", + "opcode_hex": "0x180003F0", + "primary_opcode": 6, + "extended_opcode": 1008, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Round to Floating-Point Integer toward Zero", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrlb": { + "page": "vmx/vrlb.md", + "family": "vrlb", + "xml_mnem": "vrlb", + "opcode_hex": "0x10000004", + "primary_opcode": 4, + "extended_opcode": 4, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Rotate Left Integer Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrlh": { + "page": "vmx/vrlh.md", + "family": "vrlh", + "xml_mnem": "vrlh", + "opcode_hex": "0x10000044", + "primary_opcode": 4, + "extended_opcode": 68, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Rotate Left Integer Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrlimi128": { + "page": "vmx128/vrlimi128.md", + "family": "vrlimi128", + "xml_mnem": "vrlimi128", + "opcode_hex": "0x18000710", + "primary_opcode": 6, + "extended_opcode": 1808, + "form": "VX128_4", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Rotate Left Immediate and Mask Insert", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrlw": { + "page": "vmx/vrlw.md", + "family": "vrlw", + "xml_mnem": "vrlw", + "opcode_hex": "0x10000084", + "primary_opcode": 4, + "extended_opcode": 132, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Rotate Left Integer Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrlw128": { + "page": "vmx/vrlw.md", + "family": "vrlw", + "xml_mnem": "vrlw128", + "opcode_hex": "0x18000050", + "primary_opcode": 6, + "extended_opcode": 80, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Rotate Left Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrsqrtefp": { + "page": "vmx/vrsqrtefp.md", + "family": "vrsqrtefp", + "xml_mnem": "vrsqrtefp", + "opcode_hex": "0x1000014A", + "primary_opcode": 4, + "extended_opcode": 330, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Reciprocal Square Root Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vrsqrtefp128": { + "page": "vmx/vrsqrtefp.md", + "family": "vrsqrtefp", + "xml_mnem": "vrsqrtefp128", + "opcode_hex": "0x18000670", + "primary_opcode": 6, + "extended_opcode": 1648, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Reciprocal Square Root Estimate Floating Point", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsel": { + "page": "vmx/vsel.md", + "family": "vsel", + "xml_mnem": "vsel", + "opcode_hex": "0x1000002A", + "primary_opcode": 4, + "extended_opcode": 42, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Conditional Select", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VC", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsel128": { + "page": "vmx/vsel.md", + "family": "vsel", + "xml_mnem": "vsel128", + "opcode_hex": "0x14000350", + "primary_opcode": 5, + "extended_opcode": 848, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Conditional Select", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "VD", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsl": { + "page": "vmx/vsl.md", + "family": "vsl", + "xml_mnem": "vsl", + "opcode_hex": "0x100001C4", + "primary_opcode": 4, + "extended_opcode": 452, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslb": { + "page": "vmx/vslb.md", + "family": "vslb", + "xml_mnem": "vslb", + "opcode_hex": "0x10000104", + "primary_opcode": 4, + "extended_opcode": 260, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left Integer Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsldoi": { + "page": "vmx/vsldoi.md", + "family": "vsldoi", + "xml_mnem": "vsldoi", + "opcode_hex": "0x1000002C", + "primary_opcode": 4, + "extended_opcode": 44, + "form": "VA", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left Double by Octet Immediate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "SHB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsldoi128": { + "page": "vmx/vsldoi.md", + "family": "vsldoi", + "xml_mnem": "vsldoi128", + "opcode_hex": "0x10000010", + "primary_opcode": 4, + "extended_opcode": 16, + "form": "VX128_5", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Left Double by Octet Immediate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + }, + { + "field": "SHB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslh": { + "page": "vmx/vslh.md", + "family": "vslh", + "xml_mnem": "vslh", + "opcode_hex": "0x10000144", + "primary_opcode": 4, + "extended_opcode": 324, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left Integer Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslo": { + "page": "vmx/vslo.md", + "family": "vslo", + "xml_mnem": "vslo", + "opcode_hex": "0x1000040C", + "primary_opcode": 4, + "extended_opcode": 1036, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left by Octet", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslo128": { + "page": "vmx/vslo.md", + "family": "vslo", + "xml_mnem": "vslo128", + "opcode_hex": "0x14000390", + "primary_opcode": 5, + "extended_opcode": 912, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Left Octet", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslw": { + "page": "vmx/vslw.md", + "family": "vslw", + "xml_mnem": "vslw", + "opcode_hex": "0x10000184", + "primary_opcode": 4, + "extended_opcode": 388, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Left Integer Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vslw128": { + "page": "vmx/vslw.md", + "family": "vslw", + "xml_mnem": "vslw128", + "opcode_hex": "0x180000D0", + "primary_opcode": 6, + "extended_opcode": 208, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Left Integer Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltb": { + "page": "vmx/vspltb.md", + "family": "vspltb", + "xml_mnem": "vspltb", + "opcode_hex": "0x1000020C", + "primary_opcode": 4, + "extended_opcode": 524, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Byte", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsplth": { + "page": "vmx/vsplth.md", + "family": "vsplth", + "xml_mnem": "vsplth", + "opcode_hex": "0x1000024C", + "primary_opcode": 4, + "extended_opcode": 588, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Half Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltisb": { + "page": "vmx/vspltisb.md", + "family": "vspltisb", + "xml_mnem": "vspltisb", + "opcode_hex": "0x1000030C", + "primary_opcode": 4, + "extended_opcode": 780, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Immediate Signed Byte", + "sync": false, + "reads": [ + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltish": { + "page": "vmx/vspltish.md", + "family": "vspltish", + "xml_mnem": "vspltish", + "opcode_hex": "0x1000034C", + "primary_opcode": 4, + "extended_opcode": 844, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Immediate Signed Half Word", + "sync": false, + "reads": [ + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltisw": { + "page": "vmx/vspltisw.md", + "family": "vspltisw", + "xml_mnem": "vspltisw", + "opcode_hex": "0x1000038C", + "primary_opcode": 4, + "extended_opcode": 908, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Immediate Signed Word", + "sync": false, + "reads": [ + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltisw128": { + "page": "vmx/vspltisw.md", + "family": "vspltisw", + "xml_mnem": "vspltisw128", + "opcode_hex": "0x18000770", + "primary_opcode": 6, + "extended_opcode": 1904, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Splat Immediate Signed Word", + "sync": false, + "reads": [ + { + "field": "SIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltw": { + "page": "vmx/vspltw.md", + "family": "vspltw", + "xml_mnem": "vspltw", + "opcode_hex": "0x1000028C", + "primary_opcode": 4, + "extended_opcode": 652, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Splat Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vspltw128": { + "page": "vmx/vspltw.md", + "family": "vspltw", + "xml_mnem": "vspltw128", + "opcode_hex": "0x18000730", + "primary_opcode": 6, + "extended_opcode": 1840, + "form": "VX128_3", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Splat Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsr": { + "page": "vmx/vsr.md", + "family": "vsr", + "xml_mnem": "vsr", + "opcode_hex": "0x100002C4", + "primary_opcode": 4, + "extended_opcode": 708, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrab": { + "page": "vmx/vsrab.md", + "family": "vsrab", + "xml_mnem": "vsrab", + "opcode_hex": "0x10000304", + "primary_opcode": 4, + "extended_opcode": 772, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Algebraic Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrah": { + "page": "vmx/vsrah.md", + "family": "vsrah", + "xml_mnem": "vsrah", + "opcode_hex": "0x10000344", + "primary_opcode": 4, + "extended_opcode": 836, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Algebraic Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsraw": { + "page": "vmx/vsraw.md", + "family": "vsraw", + "xml_mnem": "vsraw", + "opcode_hex": "0x10000384", + "primary_opcode": 4, + "extended_opcode": 900, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Algebraic Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsraw128": { + "page": "vmx/vsraw.md", + "family": "vsraw", + "xml_mnem": "vsraw128", + "opcode_hex": "0x18000150", + "primary_opcode": 6, + "extended_opcode": 336, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Right Arithmetic Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrb": { + "page": "vmx/vsrb.md", + "family": "vsrb", + "xml_mnem": "vsrb", + "opcode_hex": "0x10000204", + "primary_opcode": 4, + "extended_opcode": 516, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Byte", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrh": { + "page": "vmx/vsrh.md", + "family": "vsrh", + "xml_mnem": "vsrh", + "opcode_hex": "0x10000244", + "primary_opcode": 4, + "extended_opcode": 580, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Half Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsro": { + "page": "vmx/vsro.md", + "family": "vsro", + "xml_mnem": "vsro", + "opcode_hex": "0x1000044C", + "primary_opcode": 4, + "extended_opcode": 1100, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Octet", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsro128": { + "page": "vmx/vsro.md", + "family": "vsro", + "xml_mnem": "vsro128", + "opcode_hex": "0x140003D0", + "primary_opcode": 5, + "extended_opcode": 976, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Right Octet", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrw": { + "page": "vmx/vsrw.md", + "family": "vsrw", + "xml_mnem": "vsrw", + "opcode_hex": "0x10000284", + "primary_opcode": 4, + "extended_opcode": 644, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Shift Right Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsrw128": { + "page": "vmx/vsrw.md", + "family": "vsrw", + "xml_mnem": "vsrw128", + "opcode_hex": "0x180001D0", + "primary_opcode": 6, + "extended_opcode": 464, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Shift Right Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubcuw": { + "page": "vmx/vsubcuw.md", + "family": "vsubcuw", + "xml_mnem": "vsubcuw", + "opcode_hex": "0x10000580", + "primary_opcode": 4, + "extended_opcode": 1408, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Carryout Unsigned Word", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubfp": { + "page": "vmx/vsubfp.md", + "family": "vsubfp", + "xml_mnem": "vsubfp", + "opcode_hex": "0x1000004A", + "primary_opcode": 4, + "extended_opcode": 74, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubfp128": { + "page": "vmx/vsubfp.md", + "family": "vsubfp", + "xml_mnem": "vsubfp128", + "opcode_hex": "0x14000050", + "primary_opcode": 5, + "extended_opcode": 80, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Subtract Floating Point", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubsbs": { + "page": "vmx/vsubsbs.md", + "family": "vsubsbs", + "xml_mnem": "vsubsbs", + "opcode_hex": "0x10000700", + "primary_opcode": 4, + "extended_opcode": 1792, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Signed Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubshs": { + "page": "vmx/vsubshs.md", + "family": "vsubshs", + "xml_mnem": "vsubshs", + "opcode_hex": "0x10000740", + "primary_opcode": 4, + "extended_opcode": 1856, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubsws": { + "page": "vmx/vsubsws.md", + "family": "vsubsws", + "xml_mnem": "vsubsws", + "opcode_hex": "0x10000780", + "primary_opcode": 4, + "extended_opcode": 1920, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Signed Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsububm": { + "page": "vmx/vsububm.md", + "family": "vsububm", + "xml_mnem": "vsububm", + "opcode_hex": "0x10000400", + "primary_opcode": 4, + "extended_opcode": 1024, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Byte Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsububs": { + "page": "vmx/vsububs.md", + "family": "vsububs", + "xml_mnem": "vsububs", + "opcode_hex": "0x10000600", + "primary_opcode": 4, + "extended_opcode": 1536, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubuhm": { + "page": "vmx/vsubuhm.md", + "family": "vsubuhm", + "xml_mnem": "vsubuhm", + "opcode_hex": "0x10000440", + "primary_opcode": 4, + "extended_opcode": 1088, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Half Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubuhs": { + "page": "vmx/vsubuhs.md", + "family": "vsubuhs", + "xml_mnem": "vsubuhs", + "opcode_hex": "0x10000640", + "primary_opcode": 4, + "extended_opcode": 1600, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubuwm": { + "page": "vmx/vsubuwm.md", + "family": "vsubuwm", + "xml_mnem": "vsubuwm", + "opcode_hex": "0x10000480", + "primary_opcode": 4, + "extended_opcode": 1152, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Word Modulo", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsubuws": { + "page": "vmx/vsubuws.md", + "family": "vsubuws", + "xml_mnem": "vsubuws", + "opcode_hex": "0x10000680", + "primary_opcode": 4, + "extended_opcode": 1664, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Subtract Unsigned Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsum2sws": { + "page": "vmx/vsum2sws.md", + "family": "vsum2sws", + "xml_mnem": "vsum2sws", + "opcode_hex": "0x10000688", + "primary_opcode": 4, + "extended_opcode": 1672, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Sum Across Partial (1/2) Signed Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsum4sbs": { + "page": "vmx/vsum4sbs.md", + "family": "vsum4sbs", + "xml_mnem": "vsum4sbs", + "opcode_hex": "0x10000708", + "primary_opcode": 4, + "extended_opcode": 1800, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Sum Across Partial (1/4) Signed Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsum4shs": { + "page": "vmx/vsum4shs.md", + "family": "vsum4shs", + "xml_mnem": "vsum4shs", + "opcode_hex": "0x10000648", + "primary_opcode": 4, + "extended_opcode": 1608, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Sum Across Partial (1/4) Signed Half Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsum4ubs": { + "page": "vmx/vsum4ubs.md", + "family": "vsum4ubs", + "xml_mnem": "vsum4ubs", + "opcode_hex": "0x10000608", + "primary_opcode": 4, + "extended_opcode": 1544, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Sum Across Partial (1/4) Unsigned Byte Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vsumsws": { + "page": "vmx/vsumsws.md", + "family": "vsumsws", + "xml_mnem": "vsumsws", + "opcode_hex": "0x10000788", + "primary_opcode": 4, + "extended_opcode": 1928, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Sum Across Signed Word Saturate", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + }, + { + "field": "VSCR", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupkd3d128": { + "page": "vmx128/vupkd3d128.md", + "family": "vupkd3d128", + "xml_mnem": "vupkd3d128", + "opcode_hex": "0x180007F0", + "primary_opcode": 6, + "extended_opcode": 2032, + "form": "VX128_3", + "group": "vmx", + "category": "vmx128", + "description": "Vector128 Unpack D3Dtype", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupkhp": { + "page": "vmx/vupkhpx.md", + "family": "vupkhpx", + "xml_mnem": "vupkhpx", + "opcode_hex": "0x1000034E", + "primary_opcode": 4, + "extended_opcode": 846, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack High Pixel", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupkhsb": { + "page": "vmx/vupkhsb.md", + "family": "vupkhsb", + "xml_mnem": "vupkhsb", + "opcode_hex": "0x1000020E", + "primary_opcode": 4, + "extended_opcode": 526, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack High Signed Byte", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupkhsb128": { + "page": "vmx/vupkhsb.md", + "family": "vupkhsb", + "xml_mnem": "vupkhsb128", + "opcode_hex": "0x18000380", + "primary_opcode": 6, + "extended_opcode": 896, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Unpack High Signed Byte", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupkhsh": { + "page": "vmx/vupkhsh.md", + "family": "vupkhsh", + "xml_mnem": "vupkhsh", + "opcode_hex": "0x1000024E", + "primary_opcode": 4, + "extended_opcode": 590, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack High Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupklp": { + "page": "vmx/vupklpx.md", + "family": "vupklpx", + "xml_mnem": "vupklpx", + "opcode_hex": "0x100003CE", + "primary_opcode": 4, + "extended_opcode": 974, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack Low Pixel", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupklsb": { + "page": "vmx/vupklsb.md", + "family": "vupklsb", + "xml_mnem": "vupklsb", + "opcode_hex": "0x1000028E", + "primary_opcode": 4, + "extended_opcode": 654, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack Low Signed Byte", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupklsb128": { + "page": "vmx/vupklsb.md", + "family": "vupklsb", + "xml_mnem": "vupklsb128", + "opcode_hex": "0x180003C0", + "primary_opcode": 6, + "extended_opcode": 960, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Unpack Low Signed Byte", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vupklsh": { + "page": "vmx/vupklsh.md", + "family": "vupklsh", + "xml_mnem": "vupklsh", + "opcode_hex": "0x100002CE", + "primary_opcode": 4, + "extended_opcode": 718, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Unpack Low Signed Half Word", + "sync": false, + "reads": [ + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vxor": { + "page": "vmx/vxor.md", + "family": "vxor", + "xml_mnem": "vxor", + "opcode_hex": "0x100004C4", + "primary_opcode": 4, + "extended_opcode": 1220, + "form": "VX", + "group": "vmx", + "category": "vmx", + "description": "Vector Logical XOR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "vxor128": { + "page": "vmx/vxor.md", + "family": "vxor", + "xml_mnem": "vxor128", + "opcode_hex": "0x14000310", + "primary_opcode": 5, + "extended_opcode": 784, + "form": "VX128", + "group": "vmx", + "category": "vmx", + "description": "Vector128 Logical XOR", + "sync": false, + "reads": [ + { + "field": "VA", + "conditional": false + }, + { + "field": "VB", + "conditional": false + } + ], + "writes": [ + { + "field": "VD", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "xor": { + "page": "alu/xorx.md", + "family": "xorx", + "xml_mnem": "xorx", + "opcode_hex": "0x7C000278", + "primary_opcode": 31, + "extended_opcode": 316, + "form": "X", + "group": "integer", + "category": "alu", + "description": "XOR", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "RB", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + }, + { + "field": "CR", + "conditional": true + } + ], + "runtime_flags": { + "Rc": true, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "xor.": { + "page": "alu/xorx.md", + "family": "xorx", + "variant_of": "xor", + "xml_mnem": "xorx", + "flags": { + "Rc": 1 + }, + "category": "alu" + }, + "xori": { + "page": "alu/xori.md", + "family": "xori", + "xml_mnem": "xori", + "opcode_hex": "0x68000000", + "primary_opcode": 26, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "XOR Immediate", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + }, + "xoris": { + "page": "alu/xoris.md", + "family": "xoris", + "xml_mnem": "xoris", + "opcode_hex": "0x6C000000", + "primary_opcode": 27, + "extended_opcode": null, + "form": "D", + "group": "integer", + "category": "alu", + "description": "XOR Immediate Shifted", + "sync": false, + "reads": [ + { + "field": "RS", + "conditional": false + }, + { + "field": "UIMM", + "conditional": false + } + ], + "writes": [ + { + "field": "RA", + "conditional": false + } + ], + "runtime_flags": { + "Rc": false, + "OE": false, + "LK": false, + "Rc_mandatory": false + }, + "is_primary": true, + "flags": {} + } + } +} diff --git a/tools/ppc-manual/memory/dcbf.md b/tools/ppc-manual/memory/dcbf.md new file mode 100644 index 00000000..c863c03c --- /dev/null +++ b/tools/ppc-manual/memory/dcbf.md @@ -0,0 +1,118 @@ +# `dcbf` β€” Data Cache Block Flush + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0000ac` + + + +## 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) + + + +## 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. diff --git a/tools/ppc-manual/memory/dcbi.md b/tools/ppc-manual/memory/dcbi.md new file mode 100644 index 00000000..e59abc65 --- /dev/null +++ b/tools/ppc-manual/memory/dcbi.md @@ -0,0 +1,117 @@ +# `dcbi` β€” Data Cache Block Invalidate + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0003ac` + + + +## 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) + + + +## 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. diff --git a/tools/ppc-manual/memory/dcbst.md b/tools/ppc-manual/memory/dcbst.md new file mode 100644 index 00000000..05126a81 --- /dev/null +++ b/tools/ppc-manual/memory/dcbst.md @@ -0,0 +1,118 @@ +# `dcbst` β€” Data Cache Block Store + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00006c` + + + +## 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) + + + +## 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". diff --git a/tools/ppc-manual/memory/dcbt.md b/tools/ppc-manual/memory/dcbt.md new file mode 100644 index 00000000..d3085dfe --- /dev/null +++ b/tools/ppc-manual/memory/dcbt.md @@ -0,0 +1,117 @@ +# `dcbt` β€” Data Cache Block Touch + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00022c` + + + +## 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) + + + +## 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. diff --git a/tools/ppc-manual/memory/dcbtst.md b/tools/ppc-manual/memory/dcbtst.md new file mode 100644 index 00000000..92e1e464 --- /dev/null +++ b/tools/ppc-manual/memory/dcbtst.md @@ -0,0 +1,116 @@ +# `dcbtst` β€” Data Cache Block Touch for Store + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0001ec` + + + +## 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) + + + +## 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". diff --git a/tools/ppc-manual/memory/dcbz.md b/tools/ppc-manual/memory/dcbz.md new file mode 100644 index 00000000..c879da6e --- /dev/null +++ b/tools/ppc-manual/memory/dcbz.md @@ -0,0 +1,185 @@ +# `dcbz` β€” Data Cache Block Clear to Zero + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [DCBZ](../forms/DCBZ.md) Β· **Opcode:** `0x7c0007ec` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/icbi.md b/tools/ppc-manual/memory/icbi.md new file mode 100644 index 00000000..eb8105b3 --- /dev/null +++ b/tools/ppc-manual/memory/icbi.md @@ -0,0 +1,117 @@ +# `icbi` β€” Instruction Cache Block Invalidate + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0007ac` + + + +## 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) + + + +## 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. diff --git a/tools/ppc-manual/memory/lbz.md b/tools/ppc-manual/memory/lbz.md new file mode 100644 index 00000000..1242cb67 --- /dev/null +++ b/tools/ppc-manual/memory/lbz.md @@ -0,0 +1,248 @@ +# `lbz` β€” Load Byte and Zero + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x88000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/ld.md b/tools/ppc-manual/memory/ld.md new file mode 100644 index 00000000..129e7e0b --- /dev/null +++ b/tools/ppc-manual/memory/ld.md @@ -0,0 +1,251 @@ +# `ld` β€” Load Doubleword + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [DS](../forms/DS.md) Β· **Opcode:** `0xe8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/ldarx.md b/tools/ppc-manual/memory/ldarx.md new file mode 100644 index 00000000..f70716a5 --- /dev/null +++ b/tools/ppc-manual/memory/ldarx.md @@ -0,0 +1,138 @@ +# `ldarx` β€” Load Doubleword and Reserve Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0000a8` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/ldbrx.md b/tools/ppc-manual/memory/ldbrx.md new file mode 100644 index 00000000..6c88dfc1 --- /dev/null +++ b/tools/ppc-manual/memory/ldbrx.md @@ -0,0 +1,128 @@ +# `ldbrx` β€” Load Doubleword Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000428` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::ldbrx => { + let ea = ea_indexed(ctx, instr); + ctx.gpr[instr.rd()] = mem.read_u64(ea).swap_bytes(); + ctx.pc += 4; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/lfd.md b/tools/ppc-manual/memory/lfd.md new file mode 100644 index 00000000..e6a87b1a --- /dev/null +++ b/tools/ppc-manual/memory/lfd.md @@ -0,0 +1,248 @@ +# `lfd` β€” Load Floating-Point Double + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xc8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/lfs.md b/tools/ppc-manual/memory/lfs.md new file mode 100644 index 00000000..e021a314 --- /dev/null +++ b/tools/ppc-manual/memory/lfs.md @@ -0,0 +1,249 @@ +# `lfs` β€” Load Floating-Point Single + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xc0000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/lha.md b/tools/ppc-manual/memory/lha.md new file mode 100644 index 00000000..52c20d66 --- /dev/null +++ b/tools/ppc-manual/memory/lha.md @@ -0,0 +1,249 @@ +# `lha` β€” Load Half Word Algebraic + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xa8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/lhbrx.md b/tools/ppc-manual/memory/lhbrx.md new file mode 100644 index 00000000..433c083c --- /dev/null +++ b/tools/ppc-manual/memory/lhbrx.md @@ -0,0 +1,129 @@ +# `lhbrx` β€” Load Half Word Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00062c` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/lhz.md b/tools/ppc-manual/memory/lhz.md new file mode 100644 index 00000000..f21537e2 --- /dev/null +++ b/tools/ppc-manual/memory/lhz.md @@ -0,0 +1,248 @@ +# `lhz` β€” Load Half Word and Zero + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xa0000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/lmw.md b/tools/ppc-manual/memory/lmw.md new file mode 100644 index 00000000..b19cb3e0 --- /dev/null +++ b/tools/ppc-manual/memory/lmw.md @@ -0,0 +1,133 @@ +# `lmw` β€” Load Multiple Word + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xb8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lswi.md b/tools/ppc-manual/memory/lswi.md new file mode 100644 index 00000000..2e29c225 --- /dev/null +++ b/tools/ppc-manual/memory/lswi.md @@ -0,0 +1,139 @@ +# `lswi` β€” Load String Word Immediate + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0004aa` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lswx.md b/tools/ppc-manual/memory/lswx.md new file mode 100644 index 00000000..f51e6cc7 --- /dev/null +++ b/tools/ppc-manual/memory/lswx.md @@ -0,0 +1,139 @@ +# `lswx` β€” Load String Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00042a` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvebx.md b/tools/ppc-manual/memory/lvebx.md new file mode 100644 index 00000000..711cd161 --- /dev/null +++ b/tools/ppc-manual/memory/lvebx.md @@ -0,0 +1,135 @@ +# `lvebx` β€” Load Vector Element Byte Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00000e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvehx.md b/tools/ppc-manual/memory/lvehx.md new file mode 100644 index 00000000..e24cc817 --- /dev/null +++ b/tools/ppc-manual/memory/lvehx.md @@ -0,0 +1,138 @@ +# `lvehx` β€” Load Vector Element Half Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00004e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvewx.md b/tools/ppc-manual/memory/lvewx.md new file mode 100644 index 00000000..5b2eebd3 --- /dev/null +++ b/tools/ppc-manual/memory/lvewx.md @@ -0,0 +1,185 @@ +# `lvewx` β€” Load Vector Element Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00008e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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`. diff --git a/tools/ppc-manual/memory/lvlx.md b/tools/ppc-manual/memory/lvlx.md new file mode 100644 index 00000000..188b50dd --- /dev/null +++ b/tools/ppc-manual/memory/lvlx.md @@ -0,0 +1,172 @@ +# `lvlx` β€” Load Vector Left Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00040e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvlxl.md b/tools/ppc-manual/memory/lvlxl.md new file mode 100644 index 00000000..762c45a8 --- /dev/null +++ b/tools/ppc-manual/memory/lvlxl.md @@ -0,0 +1,171 @@ +# `lvlxl` β€” Load Vector Left Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00060e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvrx.md b/tools/ppc-manual/memory/lvrx.md new file mode 100644 index 00000000..d58e0bca --- /dev/null +++ b/tools/ppc-manual/memory/lvrx.md @@ -0,0 +1,173 @@ +# `lvrx` β€” Load Vector Right Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00044e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvrxl.md b/tools/ppc-manual/memory/lvrxl.md new file mode 100644 index 00000000..b82c5707 --- /dev/null +++ b/tools/ppc-manual/memory/lvrxl.md @@ -0,0 +1,171 @@ +# `lvrxl` β€” Load Vector Right Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00064e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvx.md b/tools/ppc-manual/memory/lvx.md new file mode 100644 index 00000000..82637767 --- /dev/null +++ b/tools/ppc-manual/memory/lvx.md @@ -0,0 +1,164 @@ +# `lvx` β€” Load Vector Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0000ce` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lvxl.md b/tools/ppc-manual/memory/lvxl.md new file mode 100644 index 00000000..f7f3fa8a --- /dev/null +++ b/tools/ppc-manual/memory/lvxl.md @@ -0,0 +1,182 @@ +# `lvxl` β€” Load Vector Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0002ce` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lwa.md b/tools/ppc-manual/memory/lwa.md new file mode 100644 index 00000000..30a0bd47 --- /dev/null +++ b/tools/ppc-manual/memory/lwa.md @@ -0,0 +1,209 @@ +# `lwa` β€” Load Word Algebraic + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [DS](../forms/DS.md) Β· **Opcode:** `0xe8000002` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/lwarx.md b/tools/ppc-manual/memory/lwarx.md new file mode 100644 index 00000000..a9eeb367 --- /dev/null +++ b/tools/ppc-manual/memory/lwarx.md @@ -0,0 +1,139 @@ +# `lwarx` β€” Load Word and Reserve Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000028` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/lwbrx.md b/tools/ppc-manual/memory/lwbrx.md new file mode 100644 index 00000000..9f89c7df --- /dev/null +++ b/tools/ppc-manual/memory/lwbrx.md @@ -0,0 +1,130 @@ +# `lwbrx` β€” Load Word Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00042c` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/lwz.md b/tools/ppc-manual/memory/lwz.md new file mode 100644 index 00000000..9552570e --- /dev/null +++ b/tools/ppc-manual/memory/lwz.md @@ -0,0 +1,267 @@ +# `lwz` β€” Load Word and Zero + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x80000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/stb.md b/tools/ppc-manual/memory/stb.md new file mode 100644 index 00000000..10926bec --- /dev/null +++ b/tools/ppc-manual/memory/stb.md @@ -0,0 +1,260 @@ +# `stb` β€” Store Byte + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x98000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/std.md b/tools/ppc-manual/memory/std.md new file mode 100644 index 00000000..ee01e445 --- /dev/null +++ b/tools/ppc-manual/memory/std.md @@ -0,0 +1,263 @@ +# `std` β€” Store Doubleword + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [DS](../forms/DS.md) Β· **Opcode:** `0xf8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/stdbrx.md b/tools/ppc-manual/memory/stdbrx.md new file mode 100644 index 00000000..a27e9b1a --- /dev/null +++ b/tools/ppc-manual/memory/stdbrx.md @@ -0,0 +1,130 @@ +# `stdbrx` β€” Store Doubleword Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c000528` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stdcx.md b/tools/ppc-manual/memory/stdcx.md new file mode 100644 index 00000000..4bacab70 --- /dev/null +++ b/tools/ppc-manual/memory/stdcx.md @@ -0,0 +1,176 @@ +# `stdcx` β€” Store Doubleword Conditional Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0001ad` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stfd.md b/tools/ppc-manual/memory/stfd.md new file mode 100644 index 00000000..86522397 --- /dev/null +++ b/tools/ppc-manual/memory/stfd.md @@ -0,0 +1,260 @@ +# `stfd` β€” Store Floating-Point Double + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xd8000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/stfiwx.md b/tools/ppc-manual/memory/stfiwx.md new file mode 100644 index 00000000..1a4ade81 --- /dev/null +++ b/tools/ppc-manual/memory/stfiwx.md @@ -0,0 +1,133 @@ +# `stfiwx` β€” Store Floating-Point as Integer Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0007ae` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stfs.md b/tools/ppc-manual/memory/stfs.md new file mode 100644 index 00000000..e484e65a --- /dev/null +++ b/tools/ppc-manual/memory/stfs.md @@ -0,0 +1,261 @@ +# `stfs` β€” Store Floating-Point Single + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xd0000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/sth.md b/tools/ppc-manual/memory/sth.md new file mode 100644 index 00000000..49028c62 --- /dev/null +++ b/tools/ppc-manual/memory/sth.md @@ -0,0 +1,260 @@ +# `sth` β€” Store Half Word + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xb0000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/sthbrx.md b/tools/ppc-manual/memory/sthbrx.md new file mode 100644 index 00000000..0860cc27 --- /dev/null +++ b/tools/ppc-manual/memory/sthbrx.md @@ -0,0 +1,131 @@ +# `sthbrx` β€” Store Half Word Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00072c` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stmw.md b/tools/ppc-manual/memory/stmw.md new file mode 100644 index 00000000..c64bbe12 --- /dev/null +++ b/tools/ppc-manual/memory/stmw.md @@ -0,0 +1,143 @@ +# `stmw` β€” Store Multiple Word + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0xbc000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stswi.md b/tools/ppc-manual/memory/stswi.md new file mode 100644 index 00000000..7cc9772c --- /dev/null +++ b/tools/ppc-manual/memory/stswi.md @@ -0,0 +1,145 @@ +# `stswi` β€” Store String Word Immediate + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0005aa` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stswx.md b/tools/ppc-manual/memory/stswx.md new file mode 100644 index 00000000..cad9b716 --- /dev/null +++ b/tools/ppc-manual/memory/stswx.md @@ -0,0 +1,148 @@ +# `stswx` β€” Store String Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00052a` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvebx.md b/tools/ppc-manual/memory/stvebx.md new file mode 100644 index 00000000..7b7f1f8f --- /dev/null +++ b/tools/ppc-manual/memory/stvebx.md @@ -0,0 +1,136 @@ +# `stvebx` β€” Store Vector Element Byte Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00010e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvehx.md b/tools/ppc-manual/memory/stvehx.md new file mode 100644 index 00000000..3b67f2ad --- /dev/null +++ b/tools/ppc-manual/memory/stvehx.md @@ -0,0 +1,138 @@ +# `stvehx` β€” Store Vector Element Half Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00014e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stvewx.md b/tools/ppc-manual/memory/stvewx.md new file mode 100644 index 00000000..c1355c6c --- /dev/null +++ b/tools/ppc-manual/memory/stvewx.md @@ -0,0 +1,198 @@ +# `stvewx` β€” Store Vector Element Word Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00018e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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`. diff --git a/tools/ppc-manual/memory/stvlx.md b/tools/ppc-manual/memory/stvlx.md new file mode 100644 index 00000000..f18c6afa --- /dev/null +++ b/tools/ppc-manual/memory/stvlx.md @@ -0,0 +1,193 @@ +# `stvlx` β€” Store Vector Left Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00050e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvlxl.md b/tools/ppc-manual/memory/stvlxl.md new file mode 100644 index 00000000..e5aef13b --- /dev/null +++ b/tools/ppc-manual/memory/stvlxl.md @@ -0,0 +1,192 @@ +# `stvlxl` β€” Store Vector Left Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00070e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvrx.md b/tools/ppc-manual/memory/stvrx.md new file mode 100644 index 00000000..f900103c --- /dev/null +++ b/tools/ppc-manual/memory/stvrx.md @@ -0,0 +1,193 @@ +# `stvrx` β€” Store Vector Right Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00054e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvrxl.md b/tools/ppc-manual/memory/stvrxl.md new file mode 100644 index 00000000..f65d9015 --- /dev/null +++ b/tools/ppc-manual/memory/stvrxl.md @@ -0,0 +1,192 @@ +# `stvrxl` β€” Store Vector Right Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00074e` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvx.md b/tools/ppc-manual/memory/stvx.md new file mode 100644 index 00000000..d7fdca1e --- /dev/null +++ b/tools/ppc-manual/memory/stvx.md @@ -0,0 +1,177 @@ +# `stvx` β€” Store Vector Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0001ce` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/memory/stvxl.md b/tools/ppc-manual/memory/stvxl.md new file mode 100644 index 00000000..b3affd0d --- /dev/null +++ b/tools/ppc-manual/memory/stvxl.md @@ -0,0 +1,186 @@ +# `stvxl` β€” Store Vector Indexed LRU + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c0003ce` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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`. diff --git a/tools/ppc-manual/memory/stw.md b/tools/ppc-manual/memory/stw.md new file mode 100644 index 00000000..56c6d6e0 --- /dev/null +++ b/tools/ppc-manual/memory/stw.md @@ -0,0 +1,257 @@ +# `stw` β€” Store Word + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [D](../forms/D.md) Β· **Opcode:** `0x90000000` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ +**`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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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) diff --git a/tools/ppc-manual/memory/stwbrx.md b/tools/ppc-manual/memory/stwbrx.md new file mode 100644 index 00000000..bb5ab11f --- /dev/null +++ b/tools/ppc-manual/memory/stwbrx.md @@ -0,0 +1,131 @@ +# `stwbrx` β€” Store Word Byte-Reverse Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00052c` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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". diff --git a/tools/ppc-manual/memory/stwcx.md b/tools/ppc-manual/memory/stwcx.md new file mode 100644 index 00000000..e287586b --- /dev/null +++ b/tools/ppc-manual/memory/stwcx.md @@ -0,0 +1,190 @@ +# `stwcx` β€” Store Word Conditional Indexed + +> **Category:** [Memory](../categories/memory.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00012d` + + + +## 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) +
xenia-rs interpreter body (frozen snapshot) + +```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; + } +``` +
+ + + +## 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. diff --git a/tools/ppc-manual/vmx/lvsl.md b/tools/ppc-manual/vmx/lvsl.md new file mode 100644 index 00000000..3f5002ef --- /dev/null +++ b/tools/ppc-manual/vmx/lvsl.md @@ -0,0 +1,184 @@ +# `lvsl` β€” Load Vector for Shift Left Indexed + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00000c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `lvsl` | `lvsl` | β€” | Load Vector for Shift Left Indexed | +| `lvsl128` | `lvsl128` | β€” | Load Vector for Shift Left Indexed 128 | + +## Syntax + +```asm +lvsl [VD], [RA0], [RB] +lvsl128 [VD], [RA0], [RB] +``` + +## Encoding + +### `lvsl` β€” form `X` + +- **Opcode word:** `0x7c00000c` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `6` +- **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 | + +### `lvsl128` β€” form `VX128_1` + +- **Opcode word:** `0x10000003` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `3` +- **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` | lvsl: read; lvsl128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. | +| `RB` | lvsl: read; lvsl128: read | Source GPR. | +| `VD` | lvsl: write; lvsl128: write | Destination vector register. | + +## Register Effects + +### `lvsl` + +- **Reads (always):** `RA0`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `lvsl128` + +- **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) + +``` +addr_lo <- ((RA|0) + (RB))[60:63] +for i in 0..15: VD[i] <- addr_lo + i +``` + +## C Translation Example + +```c +/* lvsl VD, RA, RB β€” load-shift-left permute control */ +uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA]; +uint8_t sh = (uint8_t)((base + r[insn.RB]) & 0xF); +for (int i = 0; i < 16; ++i) v[insn.VD].b[i] = sh + i; +``` + +## Implementation References + +**`lvsl`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="lvsl"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:111`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L111) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:46`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L46) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:751`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L751) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2520-2529`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2520-L2529) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::lvsl | PpcOpcode::lvsl128 => { + let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; + let ea = ea.wrapping_add(ctx.gpr[instr.rb()]); + let sh = (ea & 0xF) as u8; + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = sh + i as u8; } + let vd = if matches!(instr.opcode, PpcOpcode::lvsl128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`lvsl128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="lvsl128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:114`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L114) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:46`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L46) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:412`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L412) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2520-2529`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2520-L2529) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::lvsl | PpcOpcode::lvsl128 => { + let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; + let ea = ea.wrapping_add(ctx.gpr[instr.rb()]); + let sh = (ea & 0xF) as u8; + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = sh + i as u8; } + let vd = if matches!(instr.opcode, PpcOpcode::lvsl128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +; lvsl VD, RA, RB β€” load vector for shift left (generates a permute mask) +EA <- (RA|0) + (RB) ; full 64-bit EA; only the low 4 bits matter +sh <- EA[60:63] ; bits 60..63 of EA (the misalignment) +for i in 0..15: + VD[i] <- sh + i ; bytes 0..15 of VD = {sh, sh+1, …, sh+15} +``` + +## Special Cases & Edge Conditions + +- **No memory is actually read.** Despite the name, `lvsl` / `lvsr` do **not** touch memory. They consume the effective address only to extract the low four bits (the alignment offset) and materialise a 16-byte permute control vector in `VD`. They are pure "address β†’ permute-mask" converters. +- **Big-endian byte indexing.** `VD[0]` is the most-significant byte of the 128-bit register (lane 0). When `EA & 0xF == 0` the output is `{0, 1, 2, …, 15}`, i.e. the identity permute. When `EA & 0xF == 3` the output is `{3, 4, …, 18}` β€” modulo nothing, the values *do* exceed 15. That's intentional: fed into [`vperm`](vperm.md) (`vperm VD, VA, VB, VC`), byte selectors 0..15 index into `VA` and 16..31 index into `VB`. A stream of `lvsl` + two aligned `lvx` loads of consecutive 16-byte blocks + `vperm` reconstructs the unaligned 16-byte vector at `EA`. +- **Pair with [`lvsr`](lvsr.md) for the opposite direction.** `lvsl` shifts "left" (toward the low index / high address byte); `lvsr` shifts "right". Which one to pick depends on which aligned block you're starting from β€” see the idiom below. +- **Standard unaligned-load idiom.** + ``` + lvx vAL, r0, rA ; aligned block at EA & ~0xF + lvx vAH, r0, rA + 16 ; next aligned block + lvsl vC, r0, rA ; permute mask from misalignment + vperm vD, vAL, vAH, vC ; the unaligned 16 bytes starting at EA + ``` +- **`RA0` semantics.** When `RA = 0` the base is the literal zero, so `lvsl vD, 0, rB` derives the mask from `rB & 0xF`. +- **VMX128 sibling (`lvsl128`).** Same semantics; only the `VD` register is encoded with the 7-bit VMX128 register-fusion (`VD128l β€– VD128h`) so `vD` may be `v0..v127`. +- **No flags, no side effects** beyond writing `VD`. Trivial to move and schedule. + +## Related Instructions + +- [`lvsr`](lvsr.md) β€” the mirror: `VD[i] = 16 βˆ’ sh + i`. +- [`vperm`](vperm.md) β€” consumes the mask to perform arbitrary byte-level permutation across two vectors. +- [`lvx`](lvx.md), [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) β€” the actual memory loads used alongside the mask. +- [`vsldoi`](vsldoi.md) β€” static-offset shift-double; when the shift is compile-time known, this is cheaper than the `lvsl`/`vperm` pair. + +## IBM Reference + +- [AIX 7.3 β€” `lvsl` (Load Vector for Shift Left Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvsl-load-vector-shift-left-indexed) +- [IBM AltiVec Technology Programmer's Interface Manual β€” unaligned-load idiom](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/lvsr.md b/tools/ppc-manual/vmx/lvsr.md new file mode 100644 index 00000000..859ff7b4 --- /dev/null +++ b/tools/ppc-manual/vmx/lvsr.md @@ -0,0 +1,181 @@ +# `lvsr` β€” Load Vector for Shift Right Indexed + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [X](../forms/X.md) Β· **Opcode:** `0x7c00004c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `lvsr` | `lvsr` | β€” | Load Vector for Shift Right Indexed | +| `lvsr128` | `lvsr128` | β€” | Load Vector for Shift Right Indexed 128 | + +## Syntax + +```asm +lvsr [VD], [RA0], [RB] +lvsr128 [VD], [RA0], [RB] +``` + +## Encoding + +### `lvsr` β€” form `X` + +- **Opcode word:** `0x7c00004c` +- **Primary opcode (bits 0–5):** `31` +- **Extended opcode:** `38` +- **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 | + +### `lvsr128` β€” form `VX128_1` + +- **Opcode word:** `0x10000043` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `67` +- **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` | lvsr: read; lvsr128: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. | +| `RB` | lvsr: read; lvsr128: read | Source GPR. | +| `VD` | lvsr: write; lvsr128: write | Destination vector register. | + +## Register Effects + +### `lvsr` + +- **Reads (always):** `RA0`, `RB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `lvsr128` + +- **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) + +``` +addr_lo <- ((RA|0) + (RB))[60:63] +for i in 0..15: VD[i] <- 16 βˆ’ addr_lo + i +``` + +## 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 + +**`lvsr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="lvsr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:126`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L126) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:46`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L46) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:762`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L762) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2530-2539`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2530-L2539) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::lvsr | PpcOpcode::lvsr128 => { + let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; + let ea = ea.wrapping_add(ctx.gpr[instr.rb()]); + let sh = (ea & 0xF) as u8; + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = (16 - sh) + i as u8; } + let vd = if matches!(instr.opcode, PpcOpcode::lvsr128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`lvsr128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="lvsr128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:129`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L129) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:46`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L46) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:413`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L413) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2530-2539`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2530-L2539) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::lvsr | PpcOpcode::lvsr128 => { + let ea = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] }; + let ea = ea.wrapping_add(ctx.gpr[instr.rb()]); + let sh = (ea & 0xF) as u8; + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = (16 - sh) + i as u8; } + let vd = if matches!(instr.opcode, PpcOpcode::lvsr128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **No memory access.** Like [`lvsl`](lvsl.md), `lvsr` does not touch memory: the effective address is consumed solely to extract the low four bits, which then drive the synthesised permute mask in `VD`. +- **Mirror of `lvsl`.** Where `lvsl` produces `{sh, sh+1, …, sh+15}`, `lvsr` produces `{16βˆ’sh, 17βˆ’sh, …, 31βˆ’sh}`. When `EA & 0xF == 0` the output is `{16, 17, …, 31}` β€” the identity permute that selects all of `VB` (in the `vperm VD, VA, VB, VC` orientation). When `EA & 0xF == 3` the output is `{13, 14, …, 28}`, splitting the `vperm` between the high three bytes of `VA` and the low thirteen of `VB`. +- **Big-endian byte indexing.** `VD[0]` is the most-significant byte (the byte at the lowest address after a `stvx`). +- **Right-shift unaligned-load idiom.** Pair with two aligned `lvx` and a `vperm` when the source data is laid out so the wanted vector starts in the *second* aligned block: + ``` + lvx vAL, r0, rA ; aligned block at EA & ~0xF + lvx vAH, r0, rA + 16 ; next aligned block + lvsr vC, r0, rA ; right-shift permute mask + vperm vD, vAH, vAL, vC ; note: vAH then vAL β€” opposite of lvsl + ``` + The argument flip versus the `lvsl` idiom is the whole reason both masks exist. +- **`RA0` semantics.** When `RA = 0` the base is the literal zero, so `lvsr vD, 0, rB` derives the mask from `rB & 0xF`. +- **Selectors >15 are intentional.** Inside `vperm`, byte selectors with bit 4 set (i.e. `>= 16`) index into the second source vector. `lvsr` deliberately produces values up to `31`, since only the low five bits are honoured by `vperm`. +- **VMX128 sibling (`lvsr128`).** Identical semantics; the extended `VD128l β€– VD128h` encoding lets `vD` reach `v0..v127`. +- **No flags, no exceptions, trivially reorderable.** + +## Related Instructions + +- [`lvsl`](lvsl.md) β€” the mirror: `VD[i] = sh + i`. +- [`vperm`](vperm.md) β€” consumes the mask to perform arbitrary byte-level permutation across two vectors. +- [`lvx`](lvx.md), [`lvlx`](lvlx.md), [`lvrx`](lvrx.md) β€” the actual memory loads that supply the two aligned halves. +- [`vsldoi`](vsldoi.md) β€” when the misalignment is a compile-time constant, the static-offset shift is cheaper than the `lvsr`/`vperm` pair. + +## IBM Reference + +- [AIX 7.3 β€” `lvsr` (Load Vector for Shift Right Indexed)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-lvsr-load-vector-shift-right-indexed-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual β€” unaligned-load idiom](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddcuw.md b/tools/ppc-manual/vmx/vaddcuw.md new file mode 100644 index 00000000..37582b35 --- /dev/null +++ b/tools/ppc-manual/vmx/vaddcuw.md @@ -0,0 +1,133 @@ +# `vaddcuw` β€” Vector Add Carryout Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000180` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddcuw` | `vaddcuw` | β€” | Vector Add Carryout Unsigned Word | + +## Syntax + +```asm +vaddcuw [VD], [VA], [VB] +``` + +## Encoding + +### `vaddcuw` β€” form `VX` + +- **Opcode word:** `0x10000180` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `384` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddcuw: read | Source A vector register. | +| `VB` | vaddcuw: read | Source B vector register. | +| `VD` | vaddcuw: write | Destination vector register. | + +## Register Effects + +### `vaddcuw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vaddcuw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddcuw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:325`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L325) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:466`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L466) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3380-3390`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3380-L3390) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddcuw => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let (_, c) = a[i].overflowing_add(b[i]); + r[i] = if c { 1 } else { 0 }; + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Carry-out only β€” the sum is discarded.** Each of the four 32-bit lanes computes `1` if `VA[i] + VB[i]` overflows in unsigned arithmetic, else `0`. The actual modulo sum lives wherever a paired [`vadduwm`](vadduwm.md) is scheduled. +- **Big-endian word lanes.** Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. Each lane is 32-bit unsigned; output values are exactly `0` or `1`, padded to 32 bits. +- **Builds wide-integer adds.** Pair `vaddcuw` with [`vadduwm`](vadduwm.md) and a left-byte shift to chain four 32-bit adds into a single 128-bit add β€” the canonical Altivec implementation of `__uint128_t` arithmetic. To carry into the *next* lane you typically apply [`vsldoi`](vsldoi.md) by 4 bytes and a [`vadduwm`](vadduwm.md). +- **Unsigned only.** There is no `vaddcsw` (signed-carry) β€” the operation is intrinsically unsigned because "carry" is undefined for signed two's-complement. +- **No `VSCR[SAT]` update.** Modulo carry is always representable; nothing saturates. XER is also untouched (Altivec never updates `XER[CA]`). +- **No VMX128 sibling.** Only the 32-register VX form exists. +- **Aliasing legal.** `vaddcuw v3, v3, v4` works as expected. + +## Related Instructions + +- [`vadduwm`](vadduwm.md) β€” the modulo sum that `vaddcuw` complements; together they form a full 32-bit-with-carry add. +- [`vsubcuw`](vsubcuw.md) β€” the matching borrow-out (returns `1` when *no* borrow occurred β€” i.e. when `VA[i] >= VB[i]`). +- [`vsldoi`](vsldoi.md) β€” used to align the carry vector for the next lane during multi-precision chains. +- [`vaddubm`](vaddubm.md), [`vadduhm`](vadduhm.md) β€” modulo siblings at narrower lane widths (no carrying-instruction variant exists for 8- or 16-bit lanes). + +## IBM Reference + +- [AIX 7.3 β€” `vaddcuw` (Vector Add Carry-Out Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddcuw-vector-add-carryout-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” multi-precision arithmetic idiom](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddfp.md b/tools/ppc-manual/vmx/vaddfp.md new file mode 100644 index 00000000..5059af5a --- /dev/null +++ b/tools/ppc-manual/vmx/vaddfp.md @@ -0,0 +1,189 @@ +# `vaddfp` β€” Vector Add Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000000a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddfp` | `vaddfp` | β€” | Vector Add Floating Point | +| `vaddfp128` | `vaddfp128` | β€” | Vector128 Add Floating Point | + +## Syntax + +```asm +vaddfp [VD], [VA], [VB] +vaddfp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vaddfp` β€” form `VX` + +- **Opcode word:** `0x1000000a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `10` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vaddfp128` β€” form `VX128` + +- **Opcode word:** `0x14000010` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `16` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddfp: read; vaddfp128: read | Source A vector register. | +| `VB` | vaddfp: read; vaddfp128: read | Source B vector register. | +| `VD` | vaddfp: write; vaddfp128: write | Destination vector register. | + +## Register Effects + +### `vaddfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vaddfp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +for each 32-bit float lane i in 0..3: + VD[i] <- VA[i] + VB[i] +``` + +## C Translation Example + +```c +/* vaddfp VD, VA, VB β€” lane-wise float add */ +for (int i = 0; i < 4; ++i) v[insn.VD].f[i] = v[insn.VA].f[i] + v[insn.VB].f[i]; +``` + +## Implementation References + +**`vaddfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:341`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L341) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:438`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L438) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1984-1998`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1984-L1998) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddfp => { + // PPCBUG-435: VSCR.NJ=1 (Xbox 360 always boots with this set) requires + // flush-to-zero on subnormal inputs and outputs. Canary VMX float + // arithmetic flushes denormals unconditionally. + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + r[i] = vmx::flush_denorm(ai + bi); + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vaddfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:344`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L344) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:610`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L610) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1999-2011`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1999-L2011) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddfp128 => { + // PPCBUG-435: same as vaddfp. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + r[i] = vmx::flush_denorm(ai + bi); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Extended Pseudocode + +``` +; Four independent lane-wise IEEE-754 single-precision adds +for i in 0..3: + VD[i] <- VA[i] + VB[i] ; binary32, rounded to nearest + +; No FPSCR update (VMX uses VSCR, which only has NJ / SAT β€” and vaddfp doesn't saturate) +``` + +## Special Cases & Edge Conditions + +- **Lane indexing is big-endian.** Lane 0 is the **most significant** 4 bytes of the 128-bit register (the one that appears at the lowest byte offset after a `stvx`). Xenia's `Vec128::as_f32x4()` already reads lanes in PPC order on x86-64. When writing C that manipulates individual lanes, index `v.f[0]` as "the byte 0..3" of the big-endian layout. +- **Flush-denormals ("NJ") mode.** Altivec is independent of FPSCR β€” it has its own 2-bit VSCR (`NJ` for non-Java mode + `SAT` sticky-saturation). VMX float operations honour `VSCR[NJ]`: when set (the Xenon boot default), denormal inputs and outputs are flushed to zero. This is **opposite** to the scalar FPU, which has its own non-IEEE bit. Xenia sets `NJ = 1` at context creation ([`context.rs`](../../xenia-rs/crates/xenia-cpu/src/context.rs)). +- **No exception, no trap.** Altivec floats never raise exceptions. NaN inputs produce NaN outputs; `±∞ βˆ’ ±∞` yields a NaN; there is no VXISI-style status bit. `VSCR[SAT]` is **not** touched by `vaddfp` (it saturates integer ops, not floats). +- **Four independent lanes.** Each lane's operation is unaffected by the others. Aliasing between `VA`, `VB`, and `VD` is legal and common (`vaddfp v3, v3, v4`). +- **VMX128 sibling (`vaddfp128`).** Semantics identical; only the register encoding differs. VMX128 uses a 7-bit operand ID per source (and destination) built from two or three non-contiguous bit fields β€” see [`categories/vmx128.md`](../categories/vmx128.md). Any bit pattern encodable as a 32-register VX-form is also encodable as a VMX128 form, so compilers picked the more compact form that reached the needed register range. +- **On x86-64 hosts.** A natural compilation uses `_mm_add_ps` or AVX `vaddps`. These preserve lane indexing because PPC lane 0 maps to x86 lane 3 only if you treat the 128-bit value as "big-endian in memory" β€” i.e. byte-swap on load/store. With xenia's `_be` memory helpers, `_mm_add_ps` gives the right per-lane result. + +## Related Instructions + +- [`vsubfp`](vsubfp.md) β€” lane-wise float subtract. +- [`vmaddfp`](vmaddfp.md) β€” lane-wise `(VA Γ— VC) + VB` (fused multiply-add with single rounding). +- [`vnmsubfp`](vnmsubfp.md) β€” `βˆ’((VA Γ— VC) βˆ’ VB)`. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” IEEE-754-aware max/min (NaN propagation). +- [`vcmpeqfp`](vcmpeqfp.md), [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md), [`vcmpbfp`](vcmpbfp.md) β€” compares producing per-lane all-ones / all-zero masks. +- [`vrfin`](vrfin.md), [`vrfim`](vrfim.md), [`vrfip`](vrfip.md), [`vrfiz`](vrfiz.md) β€” round to integer (to-nearest / down / up / toward-zero). +- [`vmulfp`](vmulfp.md) β€” xenia's helper; not a native Altivec op, included for convenience. Hardware games use `vmaddfp v, va, vc, v0_zero` instead. + +## IBM Reference + +- [AIX 7.3 β€” `vaddfp` (Vector Add Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddfp-vector-add-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddsbs.md b/tools/ppc-manual/vmx/vaddsbs.md new file mode 100644 index 00000000..c6040edb --- /dev/null +++ b/tools/ppc-manual/vmx/vaddsbs.md @@ -0,0 +1,136 @@ +# `vaddsbs` β€” Vector Add Signed Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000300` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddsbs` | `vaddsbs` | β€” | Vector Add Signed Byte Saturate | + +## Syntax + +```asm +vaddsbs [VD], [VA], [VB] +``` + +## Encoding + +### `vaddsbs` β€” form `VX` + +- **Opcode word:** `0x10000300` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `768` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddsbs: read | Source A vector register. | +| `VB` | vaddsbs: read | Source B vector register. | +| `VD` | vaddsbs: write | Destination vector register. | +| `VSCR` | vaddsbs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vaddsbs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vaddsbs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vaddsbs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddsbs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:348`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L348) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:498`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L498) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3258-3269`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3258-L3269) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddsbs => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i8; 16]; let mut sat = false; + for i in 0..16 { + let (v, s) = crate::vmx::sat_add_i8(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sixteen signed-byte lanes, saturating.** Each `VD[i] = clamp(VA[i] + VB[i], -128, +127)` for `i = 0..15`, with both inputs interpreted as signed `int8`. Lane 0 is the most-significant byte (the byte at the lowest address after `stvx`). +- **`VSCR[SAT]` is sticky-set** when *any* lane saturates β€” either positively (overflow above `+127`) or negatively (underflow below `-128`). The SAT bit is never cleared by this op; software must use [`mtvscr`](mtvscr.md) to clear it. Xenia routes the OR of per-lane saturation flags into `ctx.set_vscr_sat(true)` exactly when at least one lane clamped (see `crate::vmx::sat_add_i8` in [`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **Compare with the modulo sibling.** [`vaddubm`](vaddubm.md) is bit-pattern-identical to a hypothetical `vaddsbm` and silently wraps without touching `VSCR[SAT]`. Use `vaddsbs` whenever clipping is desired and you need the sticky overflow flag. +- **Asymmetric clamp.** `+127 + 1 = +127`; `-128 + (-1) = -128`. Tests that look for "any saturation" should mask both saturation directions. +- **No XER side effects.** Altivec never updates `XER[CA]` / `XER[OV]`. The only status bit affected is `VSCR[SAT]`. +- **Aliasing legal.** `vaddsbs v3, v3, v4` is the standard accumulate idiom for a clamping sum. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vaddubs`](vaddubs.md) β€” same width, **unsigned** saturating add (clamps to `0..255`). +- [`vaddubm`](vaddubm.md) β€” same width, modulo (non-saturating) add; sign-agnostic. +- [`vaddshs`](vaddshs.md), [`vaddsws`](vaddsws.md) β€” signed saturating add at half / word width. +- [`vsubsbs`](vsubsbs.md) β€” the matching signed saturating subtract. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the sticky `VSCR[SAT]` bit observed here. + +## IBM Reference + +- [AIX 7.3 β€” `vaddsbs` (Vector Add Signed Byte Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddsbs-vector-add-signed-byte-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddshs.md b/tools/ppc-manual/vmx/vaddshs.md new file mode 100644 index 00000000..d42cc41f --- /dev/null +++ b/tools/ppc-manual/vmx/vaddshs.md @@ -0,0 +1,137 @@ +# `vaddshs` β€” Vector Add Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000340` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddshs` | `vaddshs` | β€” | Vector Add Signed Half Word Saturate | + +## Syntax + +```asm +vaddshs [VD], [VA], [VB] +``` + +## Encoding + +### `vaddshs` β€” form `VX` + +- **Opcode word:** `0x10000340` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `832` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddshs: read | Source A vector register. | +| `VB` | vaddshs: read | Source B vector register. | +| `VD` | vaddshs: write | Destination vector register. | +| `VSCR` | vaddshs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vaddshs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vaddshs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vaddshs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddshs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:356`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L356) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:505`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L505) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3306-3317`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3306-L3317) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddshs => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..8 { + let (v, s) = crate::vmx::sat_add_i16(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Eight signed-half lanes, saturating.** Each `VD[i] = clamp(VA[i] + VB[i], -32768, +32767)` for `i = 0..7`, with both inputs interpreted as signed `int16`. Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **`VSCR[SAT]` is sticky-set** if *any* lane clamps. Once set, it stays set until explicit clear via [`mtvscr`](mtvscr.md). Xenia uses `crate::vmx::sat_add_i16` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) which returns the per-lane saturation flag; the OR is written back via `ctx.set_vscr_sat(true)`. +- **The modulo counterpart is `vadduhm`.** Modulo add for signed and unsigned halves is bit-identical, so [`vadduhm`](vadduhm.md) covers both when wraparound is wanted; switch to `vaddshs` only when clipping with sign awareness is desired. +- **Asymmetric clamp.** `+32767 + 1 = +32767`; `-32768 + (-1) = -32768`. +- **Common 16-bit DSP idiom.** Audio mixing and fixed-point colour blending lean heavily on `vaddshs` to combine signed Q15 / Q1.15 quantities without wraparound artefacts. +- **No XER side effects, no NJ involvement** (this is an integer op). +- **No VMX128 sibling.** + +## Related Instructions + +- [`vadduhs`](vadduhs.md) β€” same width, unsigned saturating add (clamps to `0..0xFFFF`). +- [`vadduhm`](vadduhm.md) β€” same width, modulo add; sign-agnostic. +- [`vaddsbs`](vaddsbs.md), [`vaddsws`](vaddsws.md) β€” signed saturating add at byte / word width. +- [`vsubshs`](vsubshs.md) β€” the matching signed saturating subtract. +- [`vmhaddshs`](vmhaddshs.md), [`vmhraddshs`](vmhraddshs.md) β€” signed-half multiply-add with saturation, common for fixed-point DSP. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the `VSCR[SAT]` bit affected here. + +## IBM Reference + +- [AIX 7.3 β€” `vaddshs` (Vector Add Signed Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddshs-vector-add-signed-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddsws.md b/tools/ppc-manual/vmx/vaddsws.md new file mode 100644 index 00000000..24ea801e --- /dev/null +++ b/tools/ppc-manual/vmx/vaddsws.md @@ -0,0 +1,138 @@ +# `vaddsws` β€” Vector Add Signed Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000380` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddsws` | `vaddsws` | β€” | Vector Add Signed Word Saturate | + +## Syntax + +```asm +vaddsws [VD], [VA], [VB] +``` + +## Encoding + +### `vaddsws` β€” form `VX` + +- **Opcode word:** `0x10000380` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `896` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddsws: read | Source A vector register. | +| `VB` | vaddsws: read | Source B vector register. | +| `VD` | vaddsws: write | Destination vector register. | +| `VSCR` | vaddsws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vaddsws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vaddsws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vaddsws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddsws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:364`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L364) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:89`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L89) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:512`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L512) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3354-3365`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3354-L3365) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddsws => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::sat_add_i32(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Four signed-word lanes, saturating.** Each `VD[i] = clamp(VA[i] + VB[i], INT32_MIN, INT32_MAX)` for `i = 0..3`. Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **`VSCR[SAT]` is sticky-set** if any lane clamps. Xenia tracks this through `crate::vmx::sat_add_i32` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) and ORs the flag into the architectural `VSCR[SAT]`. +- **No multi-precision carry.** Unlike [`vaddcuw`](vaddcuw.md), `vaddsws` does not expose a per-lane carry/borrow β€” a saturated lane simply clips; it does not overflow into the adjacent lane. +- **Asymmetric clamp.** `INT32_MAX + 1 = INT32_MAX`; `INT32_MIN + (-1) = INT32_MIN`. +- **The modulo sibling is `vadduwm`.** Modulo add for signed and unsigned words is bit-identical; switch to `vaddsws` only when clipping with sign awareness is desired. +- **No XER side effects.** +- **No VMX128 sibling.** +- **Common usage.** Accumulate four 32-bit signed sums per cycle (e.g. dot products of int16 lanes after a [`vmsumshs`](vmsumshs.md) β€” which already saturates internally β€” for further accumulation across multiple iterations). + +## Related Instructions + +- [`vadduws`](vadduws.md) β€” same width, unsigned saturating add. +- [`vadduwm`](vadduwm.md) β€” same width, modulo (non-saturating) add; sign-agnostic. +- [`vaddsbs`](vaddsbs.md), [`vaddshs`](vaddshs.md) β€” signed saturating add at byte / half width. +- [`vsubsws`](vsubsws.md) β€” the matching signed saturating subtract. +- [`vmsumshs`](vmsumshs.md), [`vmsumuhs`](vmsumuhs.md) β€” saturating multiply-sum that often feeds a `vaddsws` chain. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the `VSCR[SAT]` bit. + +## IBM Reference + +- [AIX 7.3 β€” `vaddsws` (Vector Add Signed Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddsws-vector-add-signed-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddubm.md b/tools/ppc-manual/vmx/vaddubm.md new file mode 100644 index 00000000..cf708d57 --- /dev/null +++ b/tools/ppc-manual/vmx/vaddubm.md @@ -0,0 +1,132 @@ +# `vaddubm` β€” Vector Add Unsigned Byte Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000000` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddubm` | `vaddubm` | β€” | Vector Add Unsigned Byte Modulo | + +## Syntax + +```asm +vaddubm [VD], [VA], [VB] +``` + +## Encoding + +### `vaddubm` β€” form `VX` + +- **Opcode word:** `0x10000000` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `0` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddubm: read | Source A vector register. | +| `VB` | vaddubm: read | Source B vector register. | +| `VD` | vaddubm: write | Destination vector register. | + +## Register Effects + +### `vaddubm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vaddubm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddubm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:372`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L372) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:434`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L434) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3198-3205`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3198-L3205) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddubm => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i].wrapping_add(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sixteen independent byte lanes.** `VD[i] = (VA[i] + VB[i]) mod 256` for `i = 0..15`. Lane 0 is the most-significant byte (the byte at the lowest address after `stvx`). +- **Modulo wrap, not saturating.** Overflow silently wraps in 8-bit unsigned arithmetic β€” there is no carry-out and **`VSCR[SAT]` is not touched**. This is the same bit pattern as a signed-byte modulo add, so `vaddubm` is also the de-facto `vaddsbm` (which doesn't exist in the ISA β€” modulo arithmetic is sign-agnostic). +- **No carry, no flags.** XER is untouched (Altivec never updates `XER[CA]`/`XER[OV]`). The dedicated [`vaddcuw`](vaddcuw.md) instruction exists *only* because there is no SAT/CA byproduct β€” extracting the carry needs an explicit op. +- **Aliasing is legal.** `vaddubm v3, v3, v4` (in-place accumulate) is a single-cycle issue on Xenon's VMX pipe. +- **VSCR untouched.** Neither `SAT` nor `NJ` is read or written. Schedulable next to floats, compares and saturating ops without dependency stalls. +- **Pairs with a saturating sibling.** When you need 8-bit add with clamping, switch to [`vaddubs`](vaddubs.md) (unsigned saturate, range `0..0xFF`) or [`vaddsbs`](vaddsbs.md) (signed saturate, range `-128..+127`) β€” both of which *do* sticky-set `VSCR[SAT]`. +- **No VMX128 sibling.** The `vaddubm` opcode is not exposed as a `*128` form; the 32-register encoding is the only one available. + +## Related Instructions + +- [`vaddubs`](vaddubs.md) β€” same lane width, unsigned saturating add (`SAT` sticky-set on overflow). +- [`vaddsbs`](vaddsbs.md) β€” same lane width, signed saturating add. +- [`vadduhm`](vadduhm.md), [`vadduwm`](vadduwm.md) β€” modulo add with 8-lane half / 4-lane word width. +- [`vaddcuw`](vaddcuw.md) β€” produces the per-lane carry bits a 32-bit modulo add discards. +- [`vsububm`](vsububm.md) β€” the matching modulo subtract. +- [`vavgub`](vavgub.md) β€” unsigned byte average (carry-aware: `(a + b + 1) >> 1`), useful when byte addition needs rounding without overflow. + +## IBM Reference + +- [AIX 7.3 β€” `vaddubm` (Vector Add Unsigned Byte Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddubm-vector-add-unsigned-byte-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vaddubs.md b/tools/ppc-manual/vmx/vaddubs.md new file mode 100644 index 00000000..e2eaa1d7 --- /dev/null +++ b/tools/ppc-manual/vmx/vaddubs.md @@ -0,0 +1,138 @@ +# `vaddubs` β€” Vector Add Unsigned Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000200` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vaddubs` | `vaddubs` | β€” | Vector Add Unsigned Byte Saturate | + +## Syntax + +```asm +vaddubs [VD], [VA], [VB] +``` + +## Encoding + +### `vaddubs` β€” form `VX` + +- **Opcode word:** `0x10000200` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `512` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vaddubs: read | Source A vector register. | +| `VB` | vaddubs: read | Source B vector register. | +| `VD` | vaddubs: write | Destination vector register. | +| `VSCR` | vaddubs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vaddubs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vaddubs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vaddubs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vaddubs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:379`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L379) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:475`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L475) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3233-3245`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3233-L3245) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vaddubs => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + let mut sat = false; + for i in 0..16 { + let (v, s) = crate::vmx::sat_add_u8(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sixteen unsigned-byte lanes, saturating.** Each `VD[i] = min(VA[i] + VB[i], 0xFF)` for `i = 0..15`. Lane 0 is the most-significant byte after `stvx`. +- **`VSCR[SAT]` is sticky-set** if any lane saturates. Once set, it stays set until [`mtvscr`](mtvscr.md) clears it. Xenia computes this with `crate::vmx::sat_add_u8` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **One-sided clamp.** Only the upper bound applies (unsigned add cannot underflow). Distinct from [`vaddsbs`](vaddsbs.md), which clips at both `+127` and `-128`. +- **Pixel-blend workhorse.** Common usage is to add two unsigned-byte colour vectors with clamp-to-white at `0xFF`. Saturation behaves the same way as `_mm_adds_epu8` on x86 SSE2 β€” making it a one-to-one host translation candidate. +- **Versus modulo.** [`vaddubm`](vaddubm.md) wraps silently and never touches `VSCR[SAT]`. Use `vaddubs` when overflow indicates "too bright" / "out of range" and you want to flag it sticky. +- **No XER side effects.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vaddubm`](vaddubm.md) β€” same width, modulo (non-saturating) add. +- [`vaddsbs`](vaddsbs.md) β€” same width, signed saturating add (range `-128..+127`). +- [`vadduhs`](vadduhs.md), [`vadduws`](vadduws.md) β€” unsigned saturating add at half / word width. +- [`vsububs`](vsububs.md) β€” the matching unsigned saturating subtract (clamps to `0`). +- [`vavgub`](vavgub.md) β€” rounding average; alternative when you want `(a + b + 1) >> 1` without overflow worry. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the sticky `VSCR[SAT]` bit. + +## IBM Reference + +- [AIX 7.3 β€” `vaddubs` (Vector Add Unsigned Byte Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vaddubs-vector-add-unsigned-byte-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vadduhm.md b/tools/ppc-manual/vmx/vadduhm.md new file mode 100644 index 00000000..4f81fd29 --- /dev/null +++ b/tools/ppc-manual/vmx/vadduhm.md @@ -0,0 +1,131 @@ +# `vadduhm` β€” Vector Add Unsigned Half Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000040` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vadduhm` | `vadduhm` | β€” | Vector Add Unsigned Half Word Modulo | + +## Syntax + +```asm +vadduhm [VD], [VA], [VB] +``` + +## Encoding + +### `vadduhm` β€” form `VX` + +- **Opcode word:** `0x10000040` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `64` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vadduhm: read | Source A vector register. | +| `VB` | vadduhm: read | Source B vector register. | +| `VD` | vadduhm: write | Destination vector register. | + +## Register Effects + +### `vadduhm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vadduhm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vadduhm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:387`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L387) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:441`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L441) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3214-3221`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3214-L3221) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vadduhm => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i].wrapping_add(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Eight half-word lanes.** `VD[i] = (VA[i] + VB[i]) mod 65536` for `i = 0..7`. Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **Modulo wrap, not saturating.** Overflow silently wraps in 16-bit arithmetic; **`VSCR[SAT]` is not touched** and there is no carry-out. Sign-agnostic β€” modulo add for signed `int16` and unsigned `u16` is bit-pattern-identical, so this is also the de-facto `vaddshm`. +- **No XER, no NJ involvement.** +- **Aliasing legal.** `vadduhm v3, v3, v4` is a single-issue accumulate. +- **Pairs with saturating siblings.** Switch to [`vadduhs`](vadduhs.md) for unsigned clamp at `0xFFFF` or [`vaddshs`](vaddshs.md) for signed clamp at `Β±32767` when overflow needs to be detected via sticky `VSCR[SAT]`. +- **Common usage.** Multi-precision adds composed from 16-bit lanes; UV-coordinate accumulation; per-pixel half-precision counters. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vadduhs`](vadduhs.md) β€” same width, unsigned saturating add. +- [`vaddshs`](vaddshs.md) β€” same width, signed saturating add. +- [`vaddubm`](vaddubm.md), [`vadduwm`](vadduwm.md) β€” modulo add at byte / word width. +- [`vsubuhm`](vsubuhm.md) β€” the matching modulo subtract. +- [`vavguh`](vavguh.md) β€” unsigned half-word rounding average; useful when addition needs to stay representable. + +## IBM Reference + +- [AIX 7.3 β€” `vadduhm` (Vector Add Unsigned Half Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vadduhm-vector-add-unsigned-half-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vadduhs.md b/tools/ppc-manual/vmx/vadduhs.md new file mode 100644 index 00000000..17bb1018 --- /dev/null +++ b/tools/ppc-manual/vmx/vadduhs.md @@ -0,0 +1,136 @@ +# `vadduhs` β€” Vector Add Unsigned Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000240` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vadduhs` | `vadduhs` | β€” | Vector Add Unsigned Half Word Saturate | + +## Syntax + +```asm +vadduhs [VD], [VA], [VB] +``` + +## Encoding + +### `vadduhs` β€” form `VX` + +- **Opcode word:** `0x10000240` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `576` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vadduhs: read | Source A vector register. | +| `VB` | vadduhs: read | Source B vector register. | +| `VD` | vadduhs: write | Destination vector register. | +| `VSCR` | vadduhs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vadduhs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vadduhs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vadduhs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vadduhs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:394`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L394) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:482`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L482) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3282-3293`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3282-L3293) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vadduhs => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..8 { + let (v, s) = crate::vmx::sat_add_u16(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Eight unsigned-half lanes, saturating.** Each `VD[i] = min(VA[i] + VB[i], 0xFFFF)` for `i = 0..7`. Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **`VSCR[SAT]` is sticky-set** if any lane clamps. Cleared only by [`mtvscr`](mtvscr.md). Xenia uses `crate::vmx::sat_add_u16` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) and ORs the per-lane flag. +- **One-sided clamp.** Unsigned add cannot underflow, so only the upper bound `0xFFFF` ever clips. +- **The modulo counterpart is `vadduhm`.** Use `vadduhs` when "too large to fit" must be flagged or clipped β€” typical for accumulating Q16 unsigned counters. +- **No XER side effects.** +- **Maps directly to `_mm_adds_epu16`** on SSE2 hosts β€” semantically identical, including the sticky-saturation observation step (xenia recovers the SAT flag from the per-lane comparison). +- **No VMX128 sibling.** + +## Related Instructions + +- [`vadduhm`](vadduhm.md) β€” same width, modulo (non-saturating) add. +- [`vaddshs`](vaddshs.md) β€” same width, signed saturating add (range `-32768..+32767`). +- [`vaddubs`](vaddubs.md), [`vadduws`](vadduws.md) β€” unsigned saturating add at byte / word width. +- [`vsubuhs`](vsubuhs.md) β€” the matching unsigned saturating subtract. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the sticky `VSCR[SAT]` bit. + +## IBM Reference + +- [AIX 7.3 β€” `vadduhs` (Vector Add Unsigned Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vadduhs-vector-add-unsigned-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vadduwm.md b/tools/ppc-manual/vmx/vadduwm.md new file mode 100644 index 00000000..363e1c5b --- /dev/null +++ b/tools/ppc-manual/vmx/vadduwm.md @@ -0,0 +1,131 @@ +# `vadduwm` β€” Vector Add Unsigned Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000080` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vadduwm` | `vadduwm` | β€” | Vector Add Unsigned Word Modulo | + +## Syntax + +```asm +vadduwm [VD], [VA], [VB] +``` + +## Encoding + +### `vadduwm` β€” form `VX` + +- **Opcode word:** `0x10000080` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `128` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vadduwm: read | Source A vector register. | +| `VB` | vadduwm: read | Source B vector register. | +| `VD` | vadduwm: write | Destination vector register. | + +## Register Effects + +### `vadduwm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vadduwm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vadduwm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:402`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L402) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:448`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L448) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2396-2403`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2396-L2403) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vadduwm => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i].wrapping_add(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Four 32-bit word lanes.** `VD[i] = (VA[i] + VB[i]) mod 2^32` for `i = 0..3`. Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **Modulo wrap, not saturating.** Carry is dropped; **`VSCR[SAT]` is not touched**. Sign-agnostic β€” bit-pattern-identical for signed `int32` and unsigned `u32` modulo addition. +- **Multi-precision idiom.** Pair with [`vaddcuw`](vaddcuw.md) to recover the per-lane carry, then [`vsldoi`](vsldoi.md) the carry one word left and feed it back into another `vadduwm` to chain a 128-bit add. +- **No XER, no NJ involvement.** +- **Aliasing legal.** `vadduwm v3, v3, v4`. +- **No VMX128 sibling** in the `vadduwm` mnemonic specifically; `vaddfp128` covers the float case, but integer-modulo-word stays VMX-only. +- **Common usage.** RGBA8 packed-pixel sums; per-tile counters; BigInt limbs. + +## Related Instructions + +- [`vaddcuw`](vaddcuw.md) β€” produces the per-lane carry that `vadduwm` discards. +- [`vadduws`](vadduws.md), [`vaddsws`](vaddsws.md) β€” unsigned / signed saturating add at the same width. +- [`vaddubm`](vaddubm.md), [`vadduhm`](vadduhm.md) β€” modulo add at byte / half width. +- [`vsubuwm`](vsubuwm.md) β€” the matching modulo subtract. +- [`vsldoi`](vsldoi.md) β€” used to align carries during multi-precision chains. + +## IBM Reference + +- [AIX 7.3 β€” `vadduwm` (Vector Add Unsigned Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vadduwm-vector-add-unsigned-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Integer Arithmetic & multi-precision idiom](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vadduws.md b/tools/ppc-manual/vmx/vadduws.md new file mode 100644 index 00000000..cb20ad49 --- /dev/null +++ b/tools/ppc-manual/vmx/vadduws.md @@ -0,0 +1,137 @@ +# `vadduws` β€” Vector Add Unsigned Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000280` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vadduws` | `vadduws` | β€” | Vector Add Unsigned Word Saturate | + +## Syntax + +```asm +vadduws [VD], [VA], [VB] +``` + +## Encoding + +### `vadduws` β€” form `VX` + +- **Opcode word:** `0x10000280` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `640` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vadduws: read | Source A vector register. | +| `VB` | vadduws: read | Source B vector register. | +| `VD` | vadduws: write | Destination vector register. | +| `VSCR` | vadduws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vadduws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vadduws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vadduws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vadduws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:409`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L409) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:90`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L90) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:489`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L489) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3330-3341`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3330-L3341) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vadduws => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::sat_add_u32(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Four unsigned-word lanes, saturating.** Each `VD[i] = min(VA[i] + VB[i], 0xFFFF_FFFF)` for `i = 0..3`. Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **`VSCR[SAT]` is sticky-set** if any lane clamps. Cleared only via [`mtvscr`](mtvscr.md). Xenia uses `crate::vmx::sat_add_u32` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **One-sided clamp** at `UINT32_MAX`. There is no underflow path for unsigned add. +- **The modulo counterpart is `vadduwm`.** Use `vadduws` only when overflow needs to be visible / clamped; otherwise the modulo form is one cycle and never touches the sticky bit. +- **No XER side effects, no carry exposure.** Unlike `vadduwm + vaddcuw`, the saturating form does **not** make the carry available β€” it is fused into the clamp. +- **No VMX128 sibling.** +- **Common usage.** Pixel sums where four packed unsigned 32-bit accumulators must clip at white; counter overflow detection. + +## Related Instructions + +- [`vadduwm`](vadduwm.md) β€” same width, modulo add (no saturation, no SAT flag). +- [`vaddsws`](vaddsws.md) β€” same width, signed saturating add. +- [`vaddubs`](vaddubs.md), [`vadduhs`](vadduhs.md) β€” unsigned saturating add at byte / half width. +- [`vsubuws`](vsubuws.md) β€” the matching unsigned saturating subtract. +- [`vaddcuw`](vaddcuw.md) β€” explicit carry-out (paired with the modulo form). +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear the sticky `VSCR[SAT]` bit. + +## IBM Reference + +- [AIX 7.3 β€” `vadduws` (Vector Add Unsigned Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vadduws-vector-add-unsigned-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Saturating Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vand.md b/tools/ppc-manual/vmx/vand.md new file mode 100644 index 00000000..42d3096b --- /dev/null +++ b/tools/ppc-manual/vmx/vand.md @@ -0,0 +1,181 @@ +# `vand` β€” Vector Logical AND + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000404` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vand` | `vand` | β€” | Vector Logical AND | +| `vand128` | `vand128` | β€” | Vector128 Logical AND | + +## Syntax + +```asm +vand [VD], [VA], [VB] +vand128 [VD], [VA], [VB] +``` + +## Encoding + +### `vand` β€” form `VX` + +- **Opcode word:** `0x10000404` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1028` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vand128` β€” form `VX128` + +- **Opcode word:** `0x14000210` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `528` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vand: read; vand128: read | Source A vector register. | +| `VB` | vand: read; vand128: read | Source B vector register. | +| `VD` | vand: write; vand128: write | Destination vector register. | + +## Register Effects + +### `vand` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vand128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vand`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vand"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:423`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L423) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:91`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L91) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:521`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L521) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2208-2216`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2208-L2216) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vand | PpcOpcode::vand128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] & b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vand128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vand128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:426`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L426) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:91`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L91) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:619`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L619) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2208-2216`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2208-L2216) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vand | PpcOpcode::vand128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] & b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bitwise across the full 128 bits.** `VD = VA & VB`. Lane width is irrelevant β€” the AND is bit-for-bit and there is no lane boundary. Xenia chooses to express this as four `u32` ANDs, but any widening (`u8`, `u16`, `u64`, `u128`) is observationally identical. +- **No flags, no exceptions, no `VSCR` interaction.** Pure combinational op; one of the cheapest VMX instructions. +- **Common usage with compares.** Compare ops produce per-lane all-ones / all-zero masks; `vand` with the mask selects the matching lanes (clearing the rest). For "select-by-mask" with a non-zero alternative use [`vsel`](vsel.md) instead. +- **Idiom: clear lanes.** `vand VD, VD, vZero` zeroes a register; in practice [`vxor VD, VD, VD`](vxor.md) is preferred since it doesn't need a zero-vector source. +- **Aliasing legal.** All three operands may overlap. +- **VMX128 sibling (`vand128`).** Identical semantics with the extended 128-register encoding; xenia reuses one match arm via the `vmx_reg_triple` helper (see [`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs)). + +## Related Instructions + +- [`vandc`](vandc.md) β€” `VA & ~VB`; useful for clearing bits selected by a mask. +- [`vor`](vor.md), [`vxor`](vxor.md), [`vnor`](vnor.md) β€” the rest of the bitwise family. +- [`vsel`](vsel.md) β€” bit-wise select using a mask: `(VC & VB) | (~VC & VA)`. The recommended idiom whenever the "false" path is non-zero. +- [`vcmpequb`](vcmpequb.md) and other compares β€” natural mask producers. + +## IBM Reference + +- [AIX 7.3 β€” `vand` (Vector Logical AND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vand-vector-logical-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vandc.md b/tools/ppc-manual/vmx/vandc.md new file mode 100644 index 00000000..430a38a0 --- /dev/null +++ b/tools/ppc-manual/vmx/vandc.md @@ -0,0 +1,181 @@ +# `vandc` β€” Vector Logical AND with Complement + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000444` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vandc` | `vandc` | β€” | Vector Logical AND with Complement | +| `vandc128` | `vandc128` | β€” | Vector128 Logical AND with Complement | + +## Syntax + +```asm +vandc [VD], [VA], [VB] +vandc128 [VD], [VA], [VB] +``` + +## Encoding + +### `vandc` β€” form `VX` + +- **Opcode word:** `0x10000444` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1092` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vandc128` β€” form `VX128` + +- **Opcode word:** `0x14000250` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `592` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vandc: read; vandc128: read | Source A vector register. | +| `VB` | vandc: read; vandc128: read | Source B vector register. | +| `VD` | vandc: write; vandc128: write | Destination vector register. | + +## Register Effects + +### `vandc` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vandc128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vandc`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vandc"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:436`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L436) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:91`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L91) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:526`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L526) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2217-2225`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2217-L2225) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vandc | PpcOpcode::vandc128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] & !b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vandc128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vandc128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:439`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L439) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:91`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L91) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:621`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L621) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2217-2225`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2217-L2225) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vandc | PpcOpcode::vandc128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] & !b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bitwise AND-with-complement of the full 128 bits.** `VD = VA & ~VB`. Lane width is irrelevant β€” the operation is bit-for-bit. Order matters: `vandc VA, VB` is *not* the same as `vandc VB, VA`. +- **Standard "clear bits in mask" idiom.** Drop bits selected by the mask in `VB`: `vandc VD, VD, vMask`. Equivalent to `VD &= ~vMask`. Cheaper than synthesising the complement first with [`vnor`](vnor.md) and then ANDing. +- **Compare β†’ mask β†’ mask-out idiom.** A compare produces per-lane all-ones; pair with `vandc` to keep only the lanes where the compare was *false*. The complement avoids an extra [`vnor`](vnor.md) or `vxor` with all-ones. +- **No flags, no exceptions, no `VSCR` interaction.** +- **Aliasing legal.** `vandc VD, VD, VD` clears `VD` (`x & ~x = 0`). +- **VMX128 sibling (`vandc128`).** Identical semantics with the extended 128-register encoding; xenia reuses one match arm. + +## Related Instructions + +- [`vand`](vand.md) β€” the un-complemented sibling. +- [`vor`](vor.md), [`vxor`](vxor.md), [`vnor`](vnor.md) β€” the rest of the bitwise family. +- [`vsel`](vsel.md) β€” bitwise select using a third register; useful when the "false" branch is non-zero. +- [`vcmpequb`](vcmpequb.md) and other compares β€” natural mask producers. + +## IBM Reference + +- [AIX 7.3 β€” `vandc` (Vector Logical AND with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vandc-vector-logical-complement-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavgsb.md b/tools/ppc-manual/vmx/vavgsb.md new file mode 100644 index 00000000..2a4db5a3 --- /dev/null +++ b/tools/ppc-manual/vmx/vavgsb.md @@ -0,0 +1,130 @@ +# `vavgsb` β€” Vector Average Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000502` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavgsb` | `vavgsb` | β€” | Vector Average Signed Byte | + +## Syntax + +```asm +vavgsb [VD], [VA], [VB] +``` + +## Encoding + +### `vavgsb` β€” form `VX` + +- **Opcode word:** `0x10000502` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1282` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavgsb: read | Source A vector register. | +| `VB` | vavgsb: read | Source B vector register. | +| `VD` | vavgsb: write | Destination vector register. | + +## Register Effects + +### `vavgsb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavgsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavgsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:443`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L443) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:533`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L533) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3410-3417`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3410-L3417) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavgsb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i8; 16]; + for i in 0..16 { r[i] = crate::vmx::avg_i8(a[i], b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sixteen signed-byte rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, performed in arithmetic *wider* than 8 bits (so the `+1` cannot overflow). The result is then truncated back to `int8` β€” saturation never triggers because the average of two `int8` values fits in `int8`. Rounding is "round half up toward +∞". +- **Big-endian byte lanes.** Lane 0 is the most-significant byte after `stvx`. +- **No `VSCR[SAT]` impact.** Mathematical impossibility β€” `(a + b + 1) / 2` for `a, b ∈ [-128, 127]` always lies in `[-128, 127]`. Xenia's `crate::vmx::avg_i8` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) widens to `i16` before the add. +- **No XER side effects.** +- **Common usage.** Filtering / decimation passes, motion-compensation half-pel interpolation in older video codecs (the rounding-up bias matches MPEG/H.263 averaging conventions). +- **Aliasing legal.** `vavgsb v3, v3, v4` is a typical lowpass-step idiom. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavgub`](vavgub.md) β€” same width, unsigned rounding average. +- [`vavgsh`](vavgsh.md), [`vavgsw`](vavgsw.md) β€” signed rounding average at half / word width. +- [`vaddubm`](vaddubm.md), [`vaddsbs`](vaddsbs.md) β€” addition variants without the rounding-divide step. +- [`vsububm`](vsububm.md) β€” modulo subtract; needed for differential before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavgsb` (Vector Average Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavgsb-vector-average-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavgsh.md b/tools/ppc-manual/vmx/vavgsh.md new file mode 100644 index 00000000..8bfc6e76 --- /dev/null +++ b/tools/ppc-manual/vmx/vavgsh.md @@ -0,0 +1,130 @@ +# `vavgsh` β€” Vector Average Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000542` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavgsh` | `vavgsh` | β€” | Vector Average Signed Half Word | + +## Syntax + +```asm +vavgsh [VD], [VA], [VB] +``` + +## Encoding + +### `vavgsh` β€” form `VX` + +- **Opcode word:** `0x10000542` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1346` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavgsh: read | Source A vector register. | +| `VB` | vavgsh: read | Source B vector register. | +| `VD` | vavgsh: write | Destination vector register. | + +## Register Effects + +### `vavgsh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavgsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavgsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:450`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L450) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:535`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L535) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3426-3433`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3426-L3433) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavgsh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = crate::vmx::avg_i16(a[i], b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Eight signed-half rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, computed in 32-bit arithmetic to avoid overflow on the intermediate sum, then truncated back to `int16`. Rounding is half-up toward +∞. +- **Big-endian half lanes.** Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **No `VSCR[SAT]` impact.** The result is always representable in `int16`. Xenia's `crate::vmx::avg_i16` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) widens to `i32` before adding. +- **No XER side effects.** +- **Common usage.** Audio sample interpolation, fixed-point Q15 midpoint filters, video upscaling at 16-bit precision. +- **Aliasing legal.** `vavgsh v3, v3, v4` collapses two half-precision streams into one. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavguh`](vavguh.md) β€” same width, unsigned rounding average. +- [`vavgsb`](vavgsb.md), [`vavgsw`](vavgsw.md) β€” signed rounding average at byte / word width. +- [`vadduhm`](vadduhm.md), [`vaddshs`](vaddshs.md) β€” addition variants without the divide step. +- [`vsubuhm`](vsubuhm.md) β€” modulo subtract; difference computation before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavgsh` (Vector Average Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavgsh-vector-average-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavgsw.md b/tools/ppc-manual/vmx/vavgsw.md new file mode 100644 index 00000000..c5b968a0 --- /dev/null +++ b/tools/ppc-manual/vmx/vavgsw.md @@ -0,0 +1,129 @@ +# `vavgsw` β€” Vector Average Signed Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000582` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavgsw` | `vavgsw` | β€” | Vector Average Signed Word | + +## Syntax + +```asm +vavgsw [VD], [VA], [VB] +``` + +## Encoding + +### `vavgsw` β€” form `VX` + +- **Opcode word:** `0x10000582` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1410` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavgsw: read | Source A vector register. | +| `VB` | vavgsw: read | Source B vector register. | +| `VD` | vavgsw: write | Destination vector register. | + +## Register Effects + +### `vavgsw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavgsw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavgsw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:457`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L457) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:537`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L537) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3442-3449`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3442-L3449) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavgsw => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = crate::vmx::avg_i32(a[i], b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Four signed-word rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, computed in 64-bit arithmetic to avoid intermediate overflow, then truncated back to `int32`. Rounding is half-up toward +∞. +- **Big-endian word lanes.** Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **No `VSCR[SAT]` impact.** The mathematical result always fits in `int32`. Xenia's `crate::vmx::avg_i32` widens to `i64` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **No XER side effects.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavguw`](vavguw.md) β€” same width, unsigned rounding average. +- [`vavgsb`](vavgsb.md), [`vavgsh`](vavgsh.md) β€” signed rounding average at byte / half width. +- [`vadduwm`](vadduwm.md), [`vaddsws`](vaddsws.md) β€” addition variants without the divide step. +- [`vsubuwm`](vsubuwm.md) β€” modulo subtract; difference computation before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavgsw` (Vector Average Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavgsw-vector-average-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavgub.md b/tools/ppc-manual/vmx/vavgub.md new file mode 100644 index 00000000..a848a4d5 --- /dev/null +++ b/tools/ppc-manual/vmx/vavgub.md @@ -0,0 +1,130 @@ +# `vavgub` β€” Vector Average Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000402` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavgub` | `vavgub` | β€” | Vector Average Unsigned Byte | + +## Syntax + +```asm +vavgub [VD], [VA], [VB] +``` + +## Encoding + +### `vavgub` β€” form `VX` + +- **Opcode word:** `0x10000402` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1026` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavgub: read | Source A vector register. | +| `VB` | vavgub: read | Source B vector register. | +| `VD` | vavgub: write | Destination vector register. | + +## Register Effects + +### `vavgub` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavgub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavgub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:468`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L468) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:520`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L520) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3402-3409`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3402-L3409) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavgub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = crate::vmx::avg_u8(a[i], b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sixteen unsigned-byte rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, computed in 16-bit arithmetic so the `+1` cannot overflow, then truncated back to `u8`. Rounding is half-up. +- **Big-endian byte lanes.** Lane 0 is the most-significant byte after `stvx`. +- **No `VSCR[SAT]` impact.** The result always fits in `u8` (the average of two `u8` values is at most `255`). Xenia uses `crate::vmx::avg_u8` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **Equivalent to `_mm_avg_epu8`** on x86 SSE2 β€” semantically identical (rounding mode and width match). +- **Common usage.** Pixel-blend `(A + B + 1) / 2`, MPEG/H.264 half-pel motion-compensation averaging, downscale filters, alpha midpoint. +- **Aliasing legal.** `vavgub v3, v3, v4`. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavgsb`](vavgsb.md) β€” same width, signed rounding average. +- [`vavguh`](vavguh.md), [`vavguw`](vavguw.md) β€” unsigned rounding average at half / word width. +- [`vaddubm`](vaddubm.md), [`vaddubs`](vaddubs.md) β€” addition variants without the divide step. +- [`vsububm`](vsububm.md) β€” modulo subtract; difference before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavgub` (Vector Average Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavgub-vector-average-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavguh.md b/tools/ppc-manual/vmx/vavguh.md new file mode 100644 index 00000000..7a312108 --- /dev/null +++ b/tools/ppc-manual/vmx/vavguh.md @@ -0,0 +1,130 @@ +# `vavguh` β€” Vector Average Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000442` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavguh` | `vavguh` | β€” | Vector Average Unsigned Half Word | + +## Syntax + +```asm +vavguh [VD], [VA], [VB] +``` + +## Encoding + +### `vavguh` β€” form `VX` + +- **Opcode word:** `0x10000442` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1090` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavguh: read | Source A vector register. | +| `VB` | vavguh: read | Source B vector register. | +| `VD` | vavguh: write | Destination vector register. | + +## Register Effects + +### `vavguh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavguh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavguh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:475`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L475) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:525`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L525) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3418-3425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3418-L3425) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavguh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = crate::vmx::avg_u16(a[i], b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Eight unsigned-half rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, computed in 32-bit arithmetic to avoid the intermediate `+1` overflowing, then truncated to `u16`. Rounding is half-up. +- **Big-endian half lanes.** Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **No `VSCR[SAT]` impact.** The result always fits in `u16`. Xenia uses `crate::vmx::avg_u16` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **Equivalent to `_mm_avg_epu16`** on x86 SSE2 β€” same rounding, same width. +- **Common usage.** Higher-precision pixel blending (e.g. RGB565 sums after widening), Q16 unsigned filters. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavgsh`](vavgsh.md) β€” same width, signed rounding average. +- [`vavgub`](vavgub.md), [`vavguw`](vavguw.md) β€” unsigned rounding average at byte / word width. +- [`vadduhm`](vadduhm.md), [`vadduhs`](vadduhs.md) β€” addition variants without the divide step. +- [`vsubuhm`](vsubuhm.md) β€” modulo subtract; difference before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavguh` (Vector Average Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavguh-vector-average-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vavguw.md b/tools/ppc-manual/vmx/vavguw.md new file mode 100644 index 00000000..c7264f03 --- /dev/null +++ b/tools/ppc-manual/vmx/vavguw.md @@ -0,0 +1,130 @@ +# `vavguw` β€” Vector Average Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000482` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vavguw` | `vavguw` | β€” | Vector Average Unsigned Word | + +## Syntax + +```asm +vavguw [VD], [VA], [VB] +``` + +## Encoding + +### `vavguw` β€” form `VX` + +- **Opcode word:** `0x10000482` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1154` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vavguw: read | Source A vector register. | +| `VB` | vavguw: read | Source B vector register. | +| `VD` | vavguw: write | Destination vector register. | + +## Register Effects + +### `vavguw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vavguw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vavguw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:482`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L482) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:92`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L92) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:530`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L530) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3434-3441`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3434-L3441) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vavguw => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = crate::vmx::avg_u32(a[i], b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Four unsigned-word rounding averages.** Each `VD[i] = (VA[i] + VB[i] + 1) >> 1`, computed in 64-bit arithmetic to avoid intermediate overflow, then truncated to `u32`. Rounding is half-up. +- **Big-endian word lanes.** Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **No `VSCR[SAT]` impact.** The result always fits in `u32`. Xenia uses `crate::vmx::avg_u32` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **No SSE2 direct equivalent.** SSE2 only provides `_mm_avg_epu8` and `_mm_avg_epu16`; on x86 hosts xenia has to widen to 64-bit and do the average manually. +- **Common usage.** Per-tile counters; midpoint of two 32-bit packed values. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vavgsw`](vavgsw.md) β€” same width, signed rounding average. +- [`vavgub`](vavgub.md), [`vavguh`](vavguh.md) β€” unsigned rounding average at byte / half width. +- [`vadduwm`](vadduwm.md), [`vadduws`](vadduws.md) β€” addition variants without the divide step. +- [`vsubuwm`](vsubuwm.md) β€” modulo subtract; difference before averaging. + +## IBM Reference + +- [AIX 7.3 β€” `vavguw` (Vector Average Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vavguw-vector-average-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Average Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcfsx.md b/tools/ppc-manual/vmx/vcfsx.md new file mode 100644 index 00000000..17e5adbe --- /dev/null +++ b/tools/ppc-manual/vmx/vcfsx.md @@ -0,0 +1,131 @@ +# `vcfsx` β€” Vector Convert from Signed Fixed-Point Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000034a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcfs` | `vcfsx` | β€” | Vector Convert from Signed Fixed-Point Word | + +## Syntax + +```asm +vcfsx [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcfsx` β€” form `VX` + +- **Opcode word:** `0x1000034a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `842` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcfsx: read | Source B vector register. | +| `UIMM` | vcfsx: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcfsx: write | Destination vector register. | + +## Register Effects + +### `vcfsx` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vcfsx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcfsx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:500`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L500) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:93`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L93) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:509`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L509) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4306-4313`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4306-L4313) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcfsx => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = crate::vmx::cvt_i32_to_f32(b[i], uimm); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Convert signed-Q `int32` lane to `binary32`.** For each of the four word lanes, `VD[i] = (float)VB[i] / 2^UIMM`, where `UIMM` is the 5-bit immediate at bits 11..15 of the instruction. UIMM ranges 0..31; UIMM=0 is plain integer-to-float. +- **Big-endian word lanes.** Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **Use case.** Q-format fixed-point (`Qm.n`) β†’ IEEE float in one instruction. UIMM gives the fractional bit count, so `vcfsx vD, vB, 16` interprets each lane as Q15.16. +- **Inexact rounding.** Values whose magnitude exceeds `2^24` lose mantissa precision (only 24 bits in `binary32`'s significand). The default rounding mode is round-to-nearest-even; VMX has no per-instruction rounding control. +- **`VSCR[NJ]` (flush-denormals)** affects the output if the scaled value is sub-normal. Xenia's `crate::vmx::cvt_i32_to_f32` honours this via the architectural `VSCR[NJ]` snapshot. +- **No `VSCR[SAT]` or XER changes**, no exceptions raised. +- **No VMX128 sibling.** +- **Round-trip caveat.** `vctsxs` (the inverse) saturates instead of wrapping, so a `vcfsx`/`vctsxs` round-trip is *not* identity for values outside the signed-int32 representable range β€” important for fixed-point interpolation kernels. + +## Related Instructions + +- [`vcfux`](vcfux.md) β€” same shape, unsigned source. +- [`vctsxs`](vctsxs.md) β€” inverse: float β†’ signed-Q `int32` with saturation. +- [`vctuxs`](vctuxs.md) β€” inverse: float β†’ unsigned-Q `uint32` with saturation. +- [`vrfin`](vrfin.md), [`vrfiz`](vrfiz.md) β€” float-to-integer rounding modes when no Q-format scale is needed. + +## IBM Reference + +- [AIX 7.3 β€” `vcfsx` (Vector Convert from Signed Fixed-Point Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcfsx-vector-convert-from-signed-fixed-point-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Conversion Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcfux.md b/tools/ppc-manual/vmx/vcfux.md new file mode 100644 index 00000000..e98a343a --- /dev/null +++ b/tools/ppc-manual/vmx/vcfux.md @@ -0,0 +1,131 @@ +# `vcfux` β€” Vector Convert from Unsigned Fixed-Point Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000030a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcfu` | `vcfux` | β€” | Vector Convert from Unsigned Fixed-Point Word | + +## Syntax + +```asm +vcfux [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcfux` β€” form `VX` + +- **Opcode word:** `0x1000030a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `778` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcfux: read | Source B vector register. | +| `UIMM` | vcfux: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcfux: write | Destination vector register. | + +## Register Effects + +### `vcfux` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vcfux`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcfux"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:518`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L518) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:93`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L93) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:502`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L502) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4314-4321`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4314-L4321) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcfux => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = crate::vmx::cvt_u32_to_f32(b[i], uimm); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Convert unsigned-Q `uint32` lane to `binary32`.** For each of the four word lanes, `VD[i] = (float)VB[i] / 2^UIMM`. The 5-bit `UIMM` (bits 11..15) gives the Q-format fractional shift, in `0..31`. +- **Big-endian word lanes.** Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **Use case.** Unsigned Q-format fixed-point β†’ IEEE float; common for normalised colour channels (`vcfux vD, vColor, 8` rescales `0..255` to `0..0.996`). +- **Inexact rounding.** Magnitudes above `2^24` lose precision. Default rounding is round-to-nearest-even; VMX has no per-instruction rounding control. +- **`VSCR[NJ]`** affects sub-normal outputs. Xenia's `crate::vmx::cvt_u32_to_f32` honours the architectural snapshot. +- **No `VSCR[SAT]`, no XER changes, no exceptions.** +- **No VMX128 sibling.** +- **Round-trip caveat.** Pair with [`vctuxs`](vctuxs.md) for the inverse β€” but the inverse saturates rather than wraps, so floats above `2^32 βˆ’ 1` clamp to `0xFFFFFFFF` and stick `VSCR[SAT]`. + +## Related Instructions + +- [`vcfsx`](vcfsx.md) β€” same shape, signed source. +- [`vctuxs`](vctuxs.md) β€” inverse: float β†’ unsigned-Q `uint32` with saturation. +- [`vctsxs`](vctsxs.md) β€” inverse: float β†’ signed-Q `int32` with saturation. +- [`vrfin`](vrfin.md), [`vrfiz`](vrfiz.md) β€” float-to-integer rounding modes for the un-scaled case. + +## IBM Reference + +- [AIX 7.3 β€” `vcfux` (Vector Convert from Unsigned Fixed-Point Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcfux-vector-convert-from-unsigned-fixed-point-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Conversion Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpbfp.md b/tools/ppc-manual/vmx/vcmpbfp.md new file mode 100644 index 00000000..59c2dc47 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpbfp.md @@ -0,0 +1,220 @@ +# `vcmpbfp` β€” Vector Compare Bounds Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x100003c6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpbfp` | `vcmpbfp` | β€” | Vector Compare Bounds Floating Point | +| `vcmpbfp.` | `vcmpbfp` | Rc=1 | Vector Compare Bounds Floating Point | +| `vcmpbfp128` | `vcmpbfp128` | β€” | Vector128 Compare Bounds Floating Point | +| `vcmpbfp128.` | `vcmpbfp128` | Rc=1 | Vector128 Compare Bounds Floating Point | + +## Syntax + +```asm +vcmpbfp[Rc] [VD], [VA], [VB] +vcmpbfp128[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpbfp` β€” form `VC` + +- **Opcode word:** `0x100003c6` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `966` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +### `vcmpbfp128` β€” form `VX128_R` + +- **Opcode word:** `0x18000180` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `384` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpbfp: read; vcmpbfp128: read | Source A vector register. | +| `VB` | vcmpbfp: read; vcmpbfp128: read | Source B vector register. | +| `VD` | vcmpbfp: write; vcmpbfp128: write | Destination vector register. | +| `CR` | vcmpbfp: write (conditional); vcmpbfp128: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpbfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +### `vcmpbfp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpbfp`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. +- `vcmpbfp128`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpbfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpbfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:583`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L583) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:94`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L94) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:569`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L569) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3822-3847`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3822-L3847) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpbfp | PpcOpcode::vcmpbfp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vcmpbfp128); + let (ra, rb, rd) = if is_128 { + (instr.va128(), instr.vb128(), instr.vd128()) + } else { + (instr.ra(), instr.rb(), instr.rd()) + }; + let a = ctx.vr[ra].as_f32x4(); + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0u32; 4]; + let mut any_out = false; + for i in 0..4 { + let mut lane: u32 = 0; + if a[i].is_nan() || b[i].is_nan() || a[i] > b[i] { lane |= 0x8000_0000; any_out = true; } + if a[i].is_nan() || b[i].is_nan() || a[i] < -b[i] { lane |= 0x4000_0000; any_out = true; } + r[i] = lane; + } + let rc = if is_128 { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { + ctx.cr[6] = crate::context::CrField { + lt: false, gt: false, eq: !any_out, so: false, + }; + } + ctx.vr[rd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vcmpbfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpbfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:586`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L586) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:94`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L94) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:684`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L684) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3822-3847`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3822-L3847) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpbfp | PpcOpcode::vcmpbfp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vcmpbfp128); + let (ra, rb, rd) = if is_128 { + (instr.va128(), instr.vb128(), instr.vd128()) + } else { + (instr.ra(), instr.rb(), instr.rd()) + }; + let a = ctx.vr[ra].as_f32x4(); + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0u32; 4]; + let mut any_out = false; + for i in 0..4 { + let mut lane: u32 = 0; + if a[i].is_nan() || b[i].is_nan() || a[i] > b[i] { lane |= 0x8000_0000; any_out = true; } + if a[i].is_nan() || b[i].is_nan() || a[i] < -b[i] { lane |= 0x4000_0000; any_out = true; } + r[i] = lane; + } + let rc = if is_128 { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { + ctx.cr[6] = crate::context::CrField { + lt: false, gt: false, eq: !any_out, so: false, + }; + } + ctx.vr[rd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **"Bounds" compare, not equality.** Per word lane, sets two output bits: bit 0 (mask `0x80000000`) if `VA[i] > VB[i]` (out-of-range high) and bit 1 (mask `0x40000000`) if `VA[i] < -VB[i]` (out-of-range low). Bits 2..31 of each lane are zero. +- **NaN inputs are out-of-range in *both* directions.** Xenia sets both `0x80000000` and `0x40000000` if either input is NaN, matching the IBM manual: NaN is treated as "violates both bounds". +- **CR6 update when `Rc=1`.** CR6 is set as `[lt=0, gt=0, eq=(no-lane-out-of-range), so=0]` β€” i.e. only the `eq` bit signifies "all four lanes were within `Β±VB`". Useful as `bc 12,26` (branch if all in-range) for SIMD clamping loops. +- **No `VSCR[SAT]`, no XER changes, no exceptions.** +- **The convention is "is point inside box?"** β€” not a per-lane compare like the other `vcmp*` ops. Output is a flag-pair, not a boolean mask, so it does **not** plug directly into [`vsel`](vsel.md). To get a boolean, OR the two bits down with [`vor`](vor.md) and a shift. +- **VMX128 sibling (`vcmpbfp128`).** Identical semantics; the `Rc` bit lives at bit 27 of the VX128_R encoding. +- **Lane width is fixed at word.** Bounds check is single-precision float only; there is no `vcmpb*` for half / byte / int. + +## Related Instructions + +- [`vcmpeqfp`](vcmpeqfp.md) β€” element-wise `==` for floats. +- [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md) β€” element-wise `>` and `>=` for floats. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vor`](vor.md) β€” combine the two bits per lane into a boolean mask if needed. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” clamp values to a range without testing. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpbfp` (Vector Compare Bounds Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpbfp-vector-compare-bounds-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpeqfp.md b/tools/ppc-manual/vmx/vcmpeqfp.md new file mode 100644 index 00000000..7c69105d --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpeqfp.md @@ -0,0 +1,192 @@ +# `vcmpeqfp` β€” Vector Compare Equal-to Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x100000c6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpeqfp` | `vcmpeqfp` | β€” | Vector Compare Equal-to Floating Point | +| `vcmpeqfp.` | `vcmpeqfp` | Rc=1 | Vector Compare Equal-to Floating Point | +| `vcmpeqfp128` | `vcmpeqfp128` | β€” | Vector128 Compare Equal-to Floating Point | +| `vcmpeqfp128.` | `vcmpeqfp128` | Rc=1 | Vector128 Compare Equal-to Floating Point | + +## Syntax + +```asm +vcmpeqfp[Rc] [VD], [VA], [VB] +vcmpeqfp128[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpeqfp` β€” form `VC` + +- **Opcode word:** `0x100000c6` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `198` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +### `vcmpeqfp128` β€” form `VX128_R` + +- **Opcode word:** `0x18000000` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `0` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpeqfp: read; vcmpeqfp128: read | Source A vector register. | +| `VB` | vcmpeqfp: read; vcmpeqfp128: read | Source B vector register. | +| `VD` | vcmpeqfp: write; vcmpeqfp128: write | Destination vector register. | +| `CR` | vcmpeqfp: write (conditional); vcmpeqfp128: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpeqfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +### `vcmpeqfp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpeqfp`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. +- `vcmpeqfp128`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpeqfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpeqfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:623`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L623) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:94`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L94) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:560`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L560) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2173-2183`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2173-L2183) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpeqfp | PpcOpcode::vcmpeqfp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] == b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpeqfp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ +**`vcmpeqfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpeqfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:627`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L627) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:94`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L94) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:681`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L681) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2173-2183`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2173-L2183) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpeqfp | PpcOpcode::vcmpeqfp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] == b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpeqfp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane mask: all-ones / all-zero.** For each of the four word lanes, `VD[i] = (VA[i] == VB[i]) ? 0xFFFFFFFF : 0`. Lane 0 is the most-significant word. +- **NaN handling is IEEE-754: never equal.** `NaN == anything` is false (including `NaN == NaN`), so the lane stays zero. This is the standard quiet-compare behaviour β€” no exception, no sticky flag. +- **Sign of zero ignored.** `+0 == -0` per IEEE-754, so the lane is set to all-ones. +- **`VSCR[NJ]` β€” denormals.** With `NJ = 1` (Xenon default), denormal inputs are flushed to `Β±0` *before* the comparison; `Β±denormal == Β±0` then compares as true. This is one of the few VMX float ops where the NJ flag changes program-visible mask values. +- **CR6 update when `Rc=1`** (`vcmpeqfp.`). CR6 is `{any-true, 0, all-true, 0}` = `[lt = all-true, gt = 0, eq = all-false, so = 0]` in the standard mapping; xenia's `update_cr6_from_vmask` ([`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs)) handles the bit packing. Use `bc 12,24` for "all-equal" branches and `bc 4,26` for "any-equal". +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) to pick between two source vectors per lane. Or combine masks with [`vand`](vand.md) / [`vor`](vor.md) / [`vandc`](vandc.md) to express conjunctions. +- **No `VSCR[SAT]`, no XER changes, no traps** β€” even on signaling NaNs (Altivec's quiet-compare semantics). +- **VMX128 sibling (`vcmpeqfp128`).** Identical semantics with the extended 128-register encoding; xenia routes both opcodes to one match arm via `vmx_reg_triple`. + +## Related Instructions + +- [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md) β€” element-wise `>` and `>=` for floats. +- [`vcmpbfp`](vcmpbfp.md) β€” IEEE bounds check (`Β±VB`). +- [`vcmpequw`](vcmpequw.md) β€” same shape, integer compare. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vxor`](vxor.md) β€” mask consumers. +- [`vminfp`](vminfp.md), [`vmaxfp`](vmaxfp.md) β€” direct min / max without comparing. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpeqfp` (Vector Compare Equal-to Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpeqfp-vector-compare-equal-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpequb.md b/tools/ppc-manual/vmx/vcmpequb.md new file mode 100644 index 00000000..f7f83a08 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpequb.md @@ -0,0 +1,140 @@ +# `vcmpequb` β€” Vector Compare Equal-to Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000006` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpequb` | `vcmpequb` | β€” | Vector Compare Equal-to Unsigned Byte | +| `vcmpequb.` | `vcmpequb` | Rc=1 | Vector Compare Equal-to Unsigned Byte | + +## Syntax + +```asm +vcmpequb[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpequb` β€” form `VC` + +- **Opcode word:** `0x10000006` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `6` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpequb: read | Source A vector register. | +| `VB` | vcmpequb: read | Source B vector register. | +| `VD` | vcmpequb: write | Destination vector register. | +| `CR` | vcmpequb: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpequb` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpequb`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpequb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpequb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:719`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L719) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:95`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L95) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:557`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L557) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3723-3735`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3723-L3735) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpequb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = if a[i] == b[i] { 0xFF } else { 0 }; } + let v = xenia_types::Vec128::from_bytes(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte mask: all-ones / all-zero.** Sixteen byte lanes; `VD[i] = (VA[i] == VB[i]) ? 0xFF : 0x00`. Lane 0 is the most-significant byte after `stvx`. +- **Sign-agnostic.** Equality compare is identical for signed and unsigned bytes; there is no separate `vcmpeqsb`. +- **CR6 update when `Rc=1`** (`vcmpequb.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]` β€” built by xenia's `crate::vmx::cr6_flags_from_mask` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). Standard SIMD-search idiom: `vcmpequb. vMask, vData, vNeedle` then `bc 12,26` to branch when *no* lane matched. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) to pick per-byte between two source vectors. +- **Common usage.** `memchr` / `strlen` / character classification β€” compare against a broadcast byte (often built with [`vspltisb`](vspltisb.md)) and inspect CR6 for early-out. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpequh`](vcmpequh.md), [`vcmpequw`](vcmpequw.md) β€” equality compare at half / word width. +- [`vcmpgtub`](vcmpgtub.md), [`vcmpgtsb`](vcmpgtsb.md) β€” `>` at byte width, unsigned / signed. +- [`vsel`](vsel.md) β€” primary mask consumer. +- [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask combinators. +- [`vspltisb`](vspltisb.md), [`vspltb`](vspltb.md) β€” broadcast sources for needle patterns. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpequb` (Vector Compare Equal-to Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpequb-vector-compare-equal-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpequh.md b/tools/ppc-manual/vmx/vcmpequh.md new file mode 100644 index 00000000..ad69a970 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpequh.md @@ -0,0 +1,139 @@ +# `vcmpequh` β€” Vector Compare Equal-to Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000046` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpequh` | `vcmpequh` | β€” | Vector Compare Equal-to Unsigned Half Word | +| `vcmpequh.` | `vcmpequh` | Rc=1 | Vector Compare Equal-to Unsigned Half Word | + +## Syntax + +```asm +vcmpequh[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpequh` β€” form `VC` + +- **Opcode word:** `0x10000046` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `70` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpequh: read | Source A vector register. | +| `VB` | vcmpequh: read | Source B vector register. | +| `VD` | vcmpequh: write | Destination vector register. | +| `CR` | vcmpequh: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpequh` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpequh`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpequh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpequh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:723`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L723) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:95`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L95) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:558`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L558) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3736-3748`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3736-L3748) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpequh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = if a[i] == b[i] { 0xFFFF } else { 0 }; } + let v = xenia_types::Vec128::from_u16x8_array(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half mask: all-ones / all-zero.** Eight half-word lanes; `VD[i] = (VA[i] == VB[i]) ? 0xFFFF : 0x0000`. Lane 0 (`VD[0..1]` after `stvx`) is the most-significant half. +- **Sign-agnostic.** Equality is bit-identical for signed and unsigned halves; there is no `vcmpeqsh`. +- **CR6 update when `Rc=1`** (`vcmpequh.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. Use `bc 12,24` for "all-equal" branches and `bc 12,26` for "no-equal". +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per half-word. +- **Common usage.** UTF-16 character classification, audio-sample needle search, indexed-mesh deduplication. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpequb`](vcmpequb.md), [`vcmpequw`](vcmpequw.md) β€” equality compare at byte / word width. +- [`vcmpgtuh`](vcmpgtuh.md), [`vcmpgtsh`](vcmpgtsh.md) β€” `>` at half width, unsigned / signed. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vspltish`](vspltish.md), [`vsplth`](vsplth.md) β€” broadcast sources for needle patterns. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpequh` (Vector Compare Equal-to Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpequh-vector-compare-equal-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpequw.md b/tools/ppc-manual/vmx/vcmpequw.md new file mode 100644 index 00000000..6c46fd3f --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpequw.md @@ -0,0 +1,192 @@ +# `vcmpequw` β€” Vector Compare Equal-to Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000086` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpequw` | `vcmpequw` | β€” | Vector Compare Equal-to Unsigned Word | +| `vcmpequw.` | `vcmpequw` | Rc=1 | Vector Compare Equal-to Unsigned Word | +| `vcmpequw128` | `vcmpequw128` | β€” | Vector128 Compare Equal-to Unsigned Word | +| `vcmpequw128.` | `vcmpequw128` | Rc=1 | Vector128 Compare Equal-to Unsigned Word | + +## Syntax + +```asm +vcmpequw[Rc] [VD], [VA], [VB] +vcmpequw128[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpequw` β€” form `VC` + +- **Opcode word:** `0x10000086` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `134` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +### `vcmpequw128` β€” form `VX128_R` + +- **Opcode word:** `0x18000200` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `512` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpequw: read; vcmpequw128: read | Source A vector register. | +| `VB` | vcmpequw: read; vcmpequw128: read | Source B vector register. | +| `VD` | vcmpequw: write; vcmpequw128: write | Destination vector register. | +| `CR` | vcmpequw: write (conditional); vcmpequw128: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpequw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +### `vcmpequw128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpequw`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. +- `vcmpequw128`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpequw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpequw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:727`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L727) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:95`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L95) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:559`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L559) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2542-2552`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2542-L2552) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpequw | PpcOpcode::vcmpequw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] == b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpequw128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ +**`vcmpequw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpequw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:731`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L731) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:95`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L95) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:685`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L685) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2542-2552`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2542-L2552) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpequw | PpcOpcode::vcmpequw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] == b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpequw128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word mask: all-ones / all-zero.** Four word lanes; `VD[i] = (VA[i] == VB[i]) ? 0xFFFFFFFF : 0`. Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **Sign-agnostic.** Equality is bit-identical for signed and unsigned words; there is no `vcmpeqsw`. +- **CR6 update when `Rc=1`** (`vcmpequw.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. Classic "did all four 32-bit hash buckets match?" early-out pattern. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per word. +- **Common usage.** Hashtable probe matching, packed-RGBA pixel comparisons, packed-int handle equality. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **VMX128 sibling (`vcmpequw128`).** Identical semantics with the extended encoding; xenia routes both to one match arm via `vmx_reg_triple`. + +## Related Instructions + +- [`vcmpequb`](vcmpequb.md), [`vcmpequh`](vcmpequh.md) β€” equality compare at byte / half width. +- [`vcmpgtuw`](vcmpgtuw.md), [`vcmpgtsw`](vcmpgtsw.md) β€” `>` at word width, unsigned / signed. +- [`vcmpeqfp`](vcmpeqfp.md) β€” same shape, IEEE-754 single-precision equality. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vspltisw`](vspltisw.md), [`vspltw`](vspltw.md) β€” broadcast sources for needle patterns. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpequw` (Vector Compare Equal-to Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpequw-vector-compare-equal-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgefp.md b/tools/ppc-manual/vmx/vcmpgefp.md new file mode 100644 index 00000000..d53b8a6a --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgefp.md @@ -0,0 +1,192 @@ +# `vcmpgefp` β€” Vector Compare Greater-Than-or-Equal-to Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x100001c6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgefp` | `vcmpgefp` | β€” | Vector Compare Greater-Than-or-Equal-to Floating Point | +| `vcmpgefp.` | `vcmpgefp` | Rc=1 | Vector Compare Greater-Than-or-Equal-to Floating Point | +| `vcmpgefp128` | `vcmpgefp128` | β€” | Vector128 Compare Greater-Than-or-Equal-to Floating Point | +| `vcmpgefp128.` | `vcmpgefp128` | Rc=1 | Vector128 Compare Greater-Than-or-Equal-to Floating Point | + +## Syntax + +```asm +vcmpgefp[Rc] [VD], [VA], [VB] +vcmpgefp128[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgefp` β€” form `VC` + +- **Opcode word:** `0x100001c6` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `454` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +### `vcmpgefp128` β€” form `VX128_R` + +- **Opcode word:** `0x18000080` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `128` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgefp: read; vcmpgefp128: read | Source A vector register. | +| `VB` | vcmpgefp: read; vcmpgefp128: read | Source B vector register. | +| `VD` | vcmpgefp: write; vcmpgefp128: write | Destination vector register. | +| `CR` | vcmpgefp: write (conditional); vcmpgefp128: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgefp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +### `vcmpgefp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgefp`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. +- `vcmpgefp128`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgefp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgefp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:631`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L631) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:96`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L96) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:561`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L561) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2184-2194`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2184-L2194) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgefp | PpcOpcode::vcmpgefp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] >= b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpgefp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ +**`vcmpgefp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgefp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:635`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L635) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:96`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L96) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:682`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L682) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2184-2194`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2184-L2194) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgefp | PpcOpcode::vcmpgefp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] >= b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpgefp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane mask: all-ones / all-zero.** Four word lanes; `VD[i] = (VA[i] >= VB[i]) ? 0xFFFFFFFF : 0`. +- **NaN handling: false.** Any NaN input makes the comparison false (lane stays zero) β€” matches IEEE-754 quiet-compare semantics. There is no exception, no sticky flag. +- **`+0 >= -0` is true.** Zero signs are ignored. +- **`VSCR[NJ]` denormals.** With `NJ = 1` (Xenon default), denormal inputs are flushed to zero before the compare; this can flip a comparison's outcome relative to strict IEEE. +- **CR6 update when `Rc=1`** (`vcmpgefp.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. `bc 12,24` branches on "all four lanes β‰₯". +- **Compose with `vsel`.** The mask drives [`vsel`](vsel.md) for per-lane selection. +- **No `VSCR[SAT]`, no XER changes, no traps.** +- **VMX128 sibling (`vcmpgefp128`).** Identical semantics with the extended encoding. + +## Related Instructions + +- [`vcmpgtfp`](vcmpgtfp.md) β€” strict `>` for floats. +- [`vcmpeqfp`](vcmpeqfp.md) β€” equality for floats. +- [`vcmpbfp`](vcmpbfp.md) β€” bounds check `|VA| <= |VB|`. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” direct max / min when the mask isn't needed elsewhere. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgefp` (Vector Compare Greater-Than-or-Equal-to Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgefp-vector-compare-greater-than-equal-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtfp.md b/tools/ppc-manual/vmx/vcmpgtfp.md new file mode 100644 index 00000000..f327d056 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtfp.md @@ -0,0 +1,192 @@ +# `vcmpgtfp` β€” Vector Compare Greater-Than Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x100002c6` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtfp` | `vcmpgtfp` | β€” | Vector Compare Greater-Than Floating Point | +| `vcmpgtfp.` | `vcmpgtfp` | Rc=1 | Vector Compare Greater-Than Floating Point | +| `vcmpgtfp128` | `vcmpgtfp128` | β€” | Vector128 Compare Greater-Than Floating-Point | +| `vcmpgtfp128.` | `vcmpgtfp128` | Rc=1 | Vector128 Compare Greater-Than Floating-Point | + +## Syntax + +```asm +vcmpgtfp[Rc] [VD], [VA], [VB] +vcmpgtfp128[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtfp` β€” form `VC` + +- **Opcode word:** `0x100002c6` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `710` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +### `vcmpgtfp128` β€” form `VX128_R` + +- **Opcode word:** `0x18000100` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `256` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `XO` | extended opcode (compare) | +| 26 | `VA128h` | source A middle bit | +| 27 | `Rc` | record-form flag (updates CR6) | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtfp: read; vcmpgtfp128: read | Source A vector register. | +| `VB` | vcmpgtfp: read; vcmpgtfp128: read | Source B vector register. | +| `VD` | vcmpgtfp: write; vcmpgtfp128: write | Destination vector register. | +| `CR` | vcmpgtfp: write (conditional); vcmpgtfp128: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +### `vcmpgtfp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtfp`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. +- `vcmpgtfp128`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:639`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L639) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:96`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L96) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:565`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L565) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2195-2205`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2195-L2205) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtfp | PpcOpcode::vcmpgtfp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] > b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpgtfp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ +**`vcmpgtfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:643`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L643) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:96`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L96) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:683`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L683) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2195-2205`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2195-L2205) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtfp | PpcOpcode::vcmpgtfp128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_f32x4(); + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] > b[i] { 0xFFFF_FFFF } else { 0 }; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + let rc = if matches!(instr.opcode, PpcOpcode::vcmpgtfp128) { instr.vx128r_rc_bit() } else { instr.vc_rc_bit() }; + if rc { update_cr6_from_vmask(&r, ctx); } + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane mask: all-ones / all-zero.** Four word lanes; `VD[i] = (VA[i] > VB[i]) ? 0xFFFFFFFF : 0`. +- **NaN handling: false.** Any NaN input produces a false lane (no sticky flag, no exception) β€” matches IEEE-754 quiet-compare. +- **`+0 > -0` is false.** Zero signs ignored. +- **`VSCR[NJ]` denormals.** With `NJ = 1`, denormal inputs flush to zero before the compare. +- **CR6 update when `Rc=1`** (`vcmpgtfp.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. Use `bc 12,24` for "all-greater" branches and `bc 12,26` for "no-lane-greater". +- **Compose with `vsel`.** Mask plus [`vsel`](vsel.md) implements per-lane `if (a > b) x else y`. +- **No `VSCR[SAT]`, no XER changes, no traps.** +- **VMX128 sibling (`vcmpgtfp128`).** Identical semantics with the extended encoding. + +## Related Instructions + +- [`vcmpgefp`](vcmpgefp.md) β€” `>=` for floats. +- [`vcmpeqfp`](vcmpeqfp.md) β€” equality for floats. +- [`vcmpbfp`](vcmpbfp.md) β€” bounds check (`|VA| <= |VB|`). +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” direct max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtfp` (Vector Compare Greater-Than Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtfp-vector-compare-greater-than-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtsb.md b/tools/ppc-manual/vmx/vcmpgtsb.md new file mode 100644 index 00000000..82784a40 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtsb.md @@ -0,0 +1,140 @@ +# `vcmpgtsb` β€” Vector Compare Greater-Than Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000306` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtsb` | `vcmpgtsb` | β€” | Vector Compare Greater-Than Signed Byte | +| `vcmpgtsb.` | `vcmpgtsb` | Rc=1 | Vector Compare Greater-Than Signed Byte | + +## Syntax + +```asm +vcmpgtsb[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtsb` β€” form `VC` + +- **Opcode word:** `0x10000306` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `774` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtsb: read | Source A vector register. | +| `VB` | vcmpgtsb: read | Source B vector register. | +| `VD` | vcmpgtsb: write | Destination vector register. | +| `CR` | vcmpgtsb: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtsb` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtsb`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:735`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L735) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:566`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L566) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3762-3774`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3762-L3774) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtsb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = if a[i] > b[i] { 0xFF } else { 0 }; } + let v = xenia_types::Vec128::from_bytes(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte mask: all-ones / all-zero.** Sixteen byte lanes; `VD[i] = (int8(VA[i]) > int8(VB[i])) ? 0xFF : 0x00`. Lane 0 is the most-significant byte after `stvx`. +- **Sign matters.** Identical bit patterns to [`vcmpgtub`](vcmpgtub.md) compare differently because of the signed interpretation: e.g. `0xFF > 0x01` is `true` unsigned but `false` signed (`-1 > 1`). +- **CR6 update when `Rc=1`** (`vcmpgtsb.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]` β€” built by xenia's `crate::vmx::cr6_flags_from_mask`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) to select per byte. +- **Common usage.** Signed-byte audio thresholding, signed-difference sign extraction (`vsubsbs` then `vcmpgtsb`). +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtub`](vcmpgtub.md) β€” same width, unsigned `>`. +- [`vcmpequb`](vcmpequb.md) β€” equality at byte width. +- [`vcmpgtsh`](vcmpgtsh.md), [`vcmpgtsw`](vcmpgtsw.md) β€” signed `>` at half / word width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxsb`](vmaxsb.md), [`vminsb`](vminsb.md) β€” direct signed max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtsb` (Vector Compare Greater-Than Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtsb-vector-compare-greater-than-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtsh.md b/tools/ppc-manual/vmx/vcmpgtsh.md new file mode 100644 index 00000000..4f805a03 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtsh.md @@ -0,0 +1,140 @@ +# `vcmpgtsh` β€” Vector Compare Greater-Than Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000346` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtsh` | `vcmpgtsh` | β€” | Vector Compare Greater-Than Signed Half Word | +| `vcmpgtsh.` | `vcmpgtsh` | Rc=1 | Vector Compare Greater-Than Signed Half Word | + +## Syntax + +```asm +vcmpgtsh[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtsh` β€” form `VC` + +- **Opcode word:** `0x10000346` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `838` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtsh: read | Source A vector register. | +| `VB` | vcmpgtsh: read | Source B vector register. | +| `VD` | vcmpgtsh: write | Destination vector register. | +| `CR` | vcmpgtsh: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtsh` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtsh`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:739`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L739) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:567`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L567) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3788-3800`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3788-L3800) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtsh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = if a[i] > b[i] { 0xFFFF } else { 0 }; } + let v = xenia_types::Vec128::from_u16x8_array(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half mask: all-ones / all-zero.** Eight half-word lanes; `VD[i] = (int16(VA[i]) > int16(VB[i])) ? 0xFFFF : 0x0000`. Lane 0 is the most-significant half. +- **Sign matters.** `0x8000 > 0x0001` is `true` unsigned but `false` signed (`-32768 > 1`). Pick `vcmpgtsh` deliberately when sign bit affects ordering. +- **CR6 update when `Rc=1`** (`vcmpgtsh.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per half. +- **Common usage.** Q15 audio threshold detection, signed image-processing kernels. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtuh`](vcmpgtuh.md) β€” same width, unsigned `>`. +- [`vcmpequh`](vcmpequh.md) β€” equality at half width. +- [`vcmpgtsb`](vcmpgtsb.md), [`vcmpgtsw`](vcmpgtsw.md) β€” signed `>` at byte / word width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxsh`](vmaxsh.md), [`vminsh`](vminsh.md) β€” direct signed max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtsh` (Vector Compare Greater-Than Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtsh-vector-compare-greater-than-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtsw.md b/tools/ppc-manual/vmx/vcmpgtsw.md new file mode 100644 index 00000000..f3713d71 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtsw.md @@ -0,0 +1,137 @@ +# `vcmpgtsw` β€” Vector Compare Greater-Than Signed Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000386` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtsw` | `vcmpgtsw` | β€” | Vector Compare Greater-Than Signed Word | +| `vcmpgtsw.` | `vcmpgtsw` | Rc=1 | Vector Compare Greater-Than Signed Word | + +## Syntax + +```asm +vcmpgtsw[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtsw` β€” form `VC` + +- **Opcode word:** `0x10000386` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `902` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtsw: read | Source A vector register. | +| `VB` | vcmpgtsw: read | Source B vector register. | +| `VD` | vcmpgtsw: write | Destination vector register. | +| `CR` | vcmpgtsw: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtsw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtsw`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtsw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtsw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:743`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L743) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:568`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L568) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3811-3820`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3811-L3820) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtsw => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] > b[i] { 0xFFFFFFFF } else { 0 }; } + let v = xenia_types::Vec128::from_u32x4_array(r); + if instr.vc_rc_bit() { update_cr6_from_vmask(&r, ctx); } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word mask: all-ones / all-zero.** Four word lanes; `VD[i] = (int32(VA[i]) > int32(VB[i])) ? 0xFFFFFFFF : 0`. Lane 0 is the most-significant word. +- **Sign matters.** `0x8000_0000 > 0x0000_0001` is `true` unsigned but `false` signed (`INT32_MIN > 1`). +- **CR6 update when `Rc=1`** (`vcmpgtsw.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per word. +- **Common usage.** Z-buffer ordering, signed counter thresholds, "argmax" of int32 arrays. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtuw`](vcmpgtuw.md) β€” same width, unsigned `>`. +- [`vcmpequw`](vcmpequw.md) β€” equality at word width. +- [`vcmpgtsb`](vcmpgtsb.md), [`vcmpgtsh`](vcmpgtsh.md) β€” signed `>` at byte / half width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxsw`](vmaxsw.md), [`vminsw`](vminsw.md) β€” direct signed max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtsw` (Vector Compare Greater-Than Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtsw-vector-compare-greater-than-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtub.md b/tools/ppc-manual/vmx/vcmpgtub.md new file mode 100644 index 00000000..0db48830 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtub.md @@ -0,0 +1,140 @@ +# `vcmpgtub` β€” Vector Compare Greater-Than Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000206` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtub` | `vcmpgtub` | β€” | Vector Compare Greater-Than Unsigned Byte | +| `vcmpgtub.` | `vcmpgtub` | Rc=1 | Vector Compare Greater-Than Unsigned Byte | + +## Syntax + +```asm +vcmpgtub[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtub` β€” form `VC` + +- **Opcode word:** `0x10000206` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `518` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtub: read | Source A vector register. | +| `VB` | vcmpgtub: read | Source B vector register. | +| `VD` | vcmpgtub: write | Destination vector register. | +| `CR` | vcmpgtub: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtub` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtub`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:747`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L747) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:562`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L562) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3749-3761`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3749-L3761) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = if a[i] > b[i] { 0xFF } else { 0 }; } + let v = xenia_types::Vec128::from_bytes(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte mask: all-ones / all-zero.** Sixteen byte lanes; `VD[i] = (uint8(VA[i]) > uint8(VB[i])) ? 0xFF : 0x00`. Lane 0 is the most-significant byte after `stvx`. +- **Sign matters.** `0xFF > 0x01` is `true` unsigned but `false` signed (`-1 > 1`); pick `vcmpgtub` only when both sides should be treated as `0..255`. +- **CR6 update when `Rc=1`** (`vcmpgtub.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]` β€” built by xenia's `crate::vmx::cr6_flags_from_mask`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per byte. +- **Common usage.** Pixel "brighter than" tests, byte-level histogramming, threshold-based binarisation. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtsb`](vcmpgtsb.md) β€” same width, signed `>`. +- [`vcmpequb`](vcmpequb.md) β€” equality at byte width. +- [`vcmpgtuh`](vcmpgtuh.md), [`vcmpgtuw`](vcmpgtuw.md) β€” unsigned `>` at half / word width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxub`](vmaxub.md), [`vminub`](vminub.md) β€” direct unsigned max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtub` (Vector Compare Greater-Than Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtub-vector-compare-greater-than-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtuh.md b/tools/ppc-manual/vmx/vcmpgtuh.md new file mode 100644 index 00000000..1e5c3e60 --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtuh.md @@ -0,0 +1,140 @@ +# `vcmpgtuh` β€” Vector Compare Greater-Than Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000246` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtuh` | `vcmpgtuh` | β€” | Vector Compare Greater-Than Unsigned Half Word | +| `vcmpgtuh.` | `vcmpgtuh` | Rc=1 | Vector Compare Greater-Than Unsigned Half Word | + +## Syntax + +```asm +vcmpgtuh[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtuh` β€” form `VC` + +- **Opcode word:** `0x10000246` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `582` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtuh: read | Source A vector register. | +| `VB` | vcmpgtuh: read | Source B vector register. | +| `VD` | vcmpgtuh: write | Destination vector register. | +| `CR` | vcmpgtuh: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtuh` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtuh`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtuh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtuh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:751`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L751) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:563`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L563) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3775-3787`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3775-L3787) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtuh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = if a[i] > b[i] { 0xFFFF } else { 0 }; } + let v = xenia_types::Vec128::from_u16x8_array(r); + if instr.vc_rc_bit() { + let (t, f) = crate::vmx::cr6_flags_from_mask(v); + ctx.cr[6] = crate::context::CrField { lt: t, gt: false, eq: f, so: false }; + } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half mask: all-ones / all-zero.** Eight half-word lanes; `VD[i] = (uint16(VA[i]) > uint16(VB[i])) ? 0xFFFF : 0x0000`. Lane 0 is the most-significant half. +- **Sign matters.** `0xFFFF > 0x0001` is `true` unsigned but `false` signed (`-1 > 1`). +- **CR6 update when `Rc=1`** (`vcmpgtuh.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per half. +- **Common usage.** UTF-16 codepoint range testing, unsigned-half threshold binarisation. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtsh`](vcmpgtsh.md) β€” same width, signed `>`. +- [`vcmpequh`](vcmpequh.md) β€” equality at half width. +- [`vcmpgtub`](vcmpgtub.md), [`vcmpgtuw`](vcmpgtuw.md) β€” unsigned `>` at byte / word width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxuh`](vmaxuh.md), [`vminuh`](vminuh.md) β€” direct unsigned max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtuh` (Vector Compare Greater-Than Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtuh-vector-compare-greater-than-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vcmpgtuw.md b/tools/ppc-manual/vmx/vcmpgtuw.md new file mode 100644 index 00000000..a7dd19cf --- /dev/null +++ b/tools/ppc-manual/vmx/vcmpgtuw.md @@ -0,0 +1,137 @@ +# `vcmpgtuw` β€” Vector Compare Greater-Than Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VC](../forms/VC.md) Β· **Opcode:** `0x10000286` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcmpgtuw` | `vcmpgtuw` | β€” | Vector Compare Greater-Than Unsigned Word | +| `vcmpgtuw.` | `vcmpgtuw` | Rc=1 | Vector Compare Greater-Than Unsigned Word | + +## Syntax + +```asm +vcmpgtuw[Rc] [VD], [VA], [VB] +``` + +## Encoding + +### `vcmpgtuw` β€” form `VC` + +- **Opcode word:** `0x10000286` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `646` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21 | `Rc` | record-form flag (updates CR6) | +| 22–31 | `XO` | extended opcode (10 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vcmpgtuw: read | Source A vector register. | +| `VB` | vcmpgtuw: read | Source B vector register. | +| `VD` | vcmpgtuw: write | Destination vector register. | +| `CR` | vcmpgtuw: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | + +## Register Effects + +### `vcmpgtuw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** `CR` + +## Status-Register Effects + +- `vcmpgtuw`: **CR6** ← `[all-true, 0, all-false, 0]` when `Rc=1`. + +## 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 + +**`vcmpgtuw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcmpgtuw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:755`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L755) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:97`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L97) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:564`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L564) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3801-3810`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3801-L3810) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcmpgtuw => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] > b[i] { 0xFFFFFFFF } else { 0 }; } + let v = xenia_types::Vec128::from_u32x4_array(r); + if instr.vc_rc_bit() { update_cr6_from_vmask(&r, ctx); } + ctx.vr[instr.rd()] = v; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word mask: all-ones / all-zero.** Four word lanes; `VD[i] = (uint32(VA[i]) > uint32(VB[i])) ? 0xFFFFFFFF : 0`. Lane 0 is the most-significant word. +- **Sign matters.** `0x8000_0000 > 0x0000_0001` is `true` unsigned but `false` signed. +- **CR6 update when `Rc=1`** (`vcmpgtuw.`). CR6 = `[lt = all-true, gt = 0, eq = all-false, so = 0]`. +- **Compose with `vsel`.** Mask drives [`vsel`](vsel.md) per word. +- **Common usage.** Hashtable bucket selection, packed-RGBA bit-pattern ordering, ID range checks. +- **No `VSCR` interaction, no XER, no traps.** +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vcmpgtsw`](vcmpgtsw.md) β€” same width, signed `>`. +- [`vcmpequw`](vcmpequw.md) β€” equality at word width. +- [`vcmpgtub`](vcmpgtub.md), [`vcmpgtuh`](vcmpgtuh.md) β€” unsigned `>` at byte / half width. +- [`vsel`](vsel.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vxor`](vxor.md) β€” mask consumers / combinators. +- [`vmaxuw`](vmaxuw.md), [`vminuw`](vminuw.md) β€” direct unsigned max / min. + +## IBM Reference + +- [AIX 7.3 β€” `vcmpgtuw` (Vector Compare Greater-Than Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vcmpgtuw-vector-compare-greater-than-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Vector Compares](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vctsxs.md b/tools/ppc-manual/vmx/vctsxs.md new file mode 100644 index 00000000..af958e8b --- /dev/null +++ b/tools/ppc-manual/vmx/vctsxs.md @@ -0,0 +1,138 @@ +# `vctsxs` β€” Vector Convert to Signed Fixed-Point Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100003ca` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vctsxs` | `vctsxs` | β€” | Vector Convert to Signed Fixed-Point Word Saturate | + +## Syntax + +```asm +vctsxs [VD], [VB], [UIMM] +``` + +## Encoding + +### `vctsxs` β€” form `VX` + +- **Opcode word:** `0x100003ca` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `970` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vctsxs: read | Source B vector register. | +| `UIMM` | vctsxs: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vctsxs: write | Destination vector register. | +| `VSCR` | vctsxs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vctsxs` + +- **Reads (always):** `VB`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vctsxs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vctsxs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vctsxs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:536`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L536) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:98`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L98) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:517`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L517) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4281-4292`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4281-L4292) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vctsxs => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::cvt_f32_to_i32_sat(b[i], uimm); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Convert IEEE float lane to signed-Q `int32`, saturating.** For each of the four word lanes, `VD[i] = clamp(round_toward_zero(VB[i] * 2^UIMM), INT32_MIN, INT32_MAX)`. The 5-bit `UIMM` (bits 11..15) gives the Q-format fractional shift, in `0..31`. +- **Saturating, not wrapping.** Out-of-range floats clamp to `INT32_MIN` (negative overflow) or `INT32_MAX` (positive overflow) β€” *not* the wrap-around behaviour of x86 `cvttps2dq` (which produces `0x80000000` on overflow regardless of sign). Xenia's `crate::vmx::cvt_f32_to_i32_sat` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) handles the difference. +- **NaN β†’ 0.** A NaN input becomes `0` in the output lane and stickies `VSCR[SAT]`. (Many references state "NaN β†’ INT32_MIN"; verify against [`vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs) for the canonical xenia behaviour, which differs from POWER ISA wording.) +- **`VSCR[SAT]` is sticky-set** if any lane saturates (overflow or NaN). Cleared only by [`mtvscr`](mtvscr.md). +- **Rounding is truncate-toward-zero.** Always; no per-instruction rounding control. +- **`VSCR[NJ]` flushes denormal *inputs* to zero before scaling** (Xenon default). +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **No XER changes, no traps.** +- **No VMX128 sibling.** +- **Inverse of [`vcfsx`](vcfsx.md)**, but the inverse direction saturates rather than wraps β€” round-trips lose the magnitude of out-of-range values. + +## Related Instructions + +- [`vctuxs`](vctuxs.md) β€” same shape, unsigned destination. +- [`vcfsx`](vcfsx.md), [`vcfux`](vcfux.md) β€” inverse direction (int β†’ float with Q-shift). +- [`vrfin`](vrfin.md), [`vrfip`](vrfip.md), [`vrfim`](vrfim.md), [`vrfiz`](vrfiz.md) β€” float-to-float rounding modes (round-to-nearest, up, down, toward-zero) when staying in float. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear `VSCR[SAT]`. + +## IBM Reference + +- [AIX 7.3 β€” `vctsxs` (Vector Convert to Signed Fixed-Point Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vctsxs-vector-convert-signed-fixed-point-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Conversion Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vctuxs.md b/tools/ppc-manual/vmx/vctuxs.md new file mode 100644 index 00000000..c1ea0250 --- /dev/null +++ b/tools/ppc-manual/vmx/vctuxs.md @@ -0,0 +1,137 @@ +# `vctuxs` β€” Vector Convert to Unsigned Fixed-Point Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000038a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vctuxs` | `vctuxs` | β€” | Vector Convert to Unsigned Fixed-Point Word Saturate | + +## Syntax + +```asm +vctuxs [VD], [VB], [UIMM] +``` + +## Encoding + +### `vctuxs` β€” form `VX` + +- **Opcode word:** `0x1000038a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `906` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vctuxs: read | Source B vector register. | +| `UIMM` | vctuxs: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vctuxs: write | Destination vector register. | +| `VSCR` | vctuxs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vctuxs` + +- **Reads (always):** `VB`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vctuxs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vctuxs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vctuxs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:554`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L554) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:98`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L98) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:515`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L515) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4293-4304`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4293-L4304) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vctuxs => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::cvt_f32_to_u32_sat(b[i], uimm); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Convert IEEE float lane to unsigned-Q `uint32`, saturating.** For each of the four word lanes, `VD[i] = clamp(round_toward_zero(VB[i] * 2^UIMM), 0, UINT32_MAX)`. The 5-bit `UIMM` (bits 11..15) gives the Q-format fractional shift, in `0..31`. +- **Saturating, not wrapping.** Negative inputs clamp to `0`; values above `2^32 βˆ’ 1` clamp to `0xFFFF_FFFF`. NaN β†’ `0`. All clamping events sticky-set `VSCR[SAT]`. Xenia's `crate::vmx::cvt_f32_to_u32_sat` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)) handles the boundaries. +- **`VSCR[SAT]` sticky.** Cleared only by [`mtvscr`](mtvscr.md). +- **Rounding is truncate-toward-zero.** Always. +- **`VSCR[NJ]` flushes denormal inputs to zero before scaling** (Xenon default). +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **No XER changes, no traps.** +- **No VMX128 sibling.** +- **Common usage.** Float colour `[0.0, 1.0]` β†’ packed `0..2^Nβˆ’1` integer with `vctuxs vD, vColor, 8` (Q24.8 β†’ `0..255` after a [`vpkshus`](vpkshus.md)) or `, 32` for full unsigned-int range. + +## Related Instructions + +- [`vctsxs`](vctsxs.md) β€” same shape, signed destination. +- [`vcfsx`](vcfsx.md), [`vcfux`](vcfux.md) β€” inverse direction. +- [`vrfin`](vrfin.md), [`vrfip`](vrfip.md), [`vrfim`](vrfim.md), [`vrfiz`](vrfiz.md) β€” float-to-float rounding modes. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear `VSCR[SAT]`. + +## IBM Reference + +- [AIX 7.3 β€” `vctuxs` (Vector Convert to Unsigned Fixed-Point Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vctuxs-vector-convert-unsigned-fixed-point-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Conversion Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vexptefp.md b/tools/ppc-manual/vmx/vexptefp.md new file mode 100644 index 00000000..247c8963 --- /dev/null +++ b/tools/ppc-manual/vmx/vexptefp.md @@ -0,0 +1,180 @@ +# `vexptefp` β€” Vector 2 Raised to the Exponent Estimate Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000018a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vexptefp` | `vexptefp` | β€” | Vector 2 Raised to the Exponent Estimate Floating Point | +| `vexptefp128` | `vexptefp128` | β€” | Vector128 Log2 Estimate Floating Point | + +## Syntax + +```asm +vexptefp [VD], [VB] +vexptefp128 [VD], [VB] +``` + +## Encoding + +### `vexptefp` β€” form `VX` + +- **Opcode word:** `0x1000018a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `394` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vexptefp128` β€” form `VX128_3` + +- **Opcode word:** `0x180006b0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1712` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vexptefp: read; vexptefp128: read | Source B vector register. | +| `VD` | vexptefp: write; vexptefp128: write | Destination vector register. | + +## Register Effects + +### `vexptefp` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vexptefp128` + +- **Reads (always):** `VB` +- **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 + +**`vexptefp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vexptefp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:766`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L766) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:99`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L99) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:469`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L469) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4367-4376`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4367-L4376) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vexptefp | PpcOpcode::vexptefp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vexptefp128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].exp2(); } + ctx.vr[rd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vexptefp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vexptefp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:769`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L769) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:99`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L99) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:666`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L666) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4367-4376`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4367-L4376) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vexptefp | PpcOpcode::vexptefp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vexptefp128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].exp2(); } + ctx.vr[rd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane base-2 exponent.** Each of the four word lanes computes `VD[i] = 2^VB[i]` in `binary32`. **Note:** the IBM manual specifies a low-precision estimate (≀ 1/16 ULP relative error). Xenia uses Rust's `f32::exp2`, which is full-precision β€” programs that depend on hardware-quality estimation may observe small numerical differences. +- **Use `vlogefp` for the inverse.** The natural pair is `vexptefp(vlogefp(x)) = x` for positive finite `x`, modulo each estimate's error budget. +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **NaN, ±∞.** `2^NaN = NaN`; `2^(+∞) = +∞`; `2^(-∞) = +0`. Subnormal results may be flushed to `Β±0` if `VSCR[NJ] = 1` (Xenon default). +- **No exception, no `VSCR[SAT]` change, no XER change.** +- **VMX128 sibling (`vexptefp128`).** Identical semantics with the extended encoding. +- **Build natural exp / log via change-of-base.** `e^x = 2^(x * log2(e))`, so combine `vmaddfp` (multiply-by-constant) with `vexptefp`. + +## Related Instructions + +- [`vlogefp`](vlogefp.md) β€” base-2 logarithm (the inverse). +- [`vrefp`](vrefp.md) β€” reciprocal estimate. +- [`vrsqrtefp`](vrsqrtefp.md) β€” reciprocal-square-root estimate. +- [`vmaddfp`](vmaddfp.md) β€” fused multiply-add for change-of-base scaling. +- [`vmulfp`](vmulfp.md) β€” float multiply (xenia helper). + +## IBM Reference + +- [AIX 7.3 β€” `vexptefp` (Vector 2 Raised to the Exponent Estimate Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vexptefp-vector-2-raised-exponent-estimate-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Estimate Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vlogefp.md b/tools/ppc-manual/vmx/vlogefp.md new file mode 100644 index 00000000..950ac4d4 --- /dev/null +++ b/tools/ppc-manual/vmx/vlogefp.md @@ -0,0 +1,179 @@ +# `vlogefp` β€” Vector Log2 Estimate Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100001ca` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vlogefp` | `vlogefp` | β€” | Vector Log2 Estimate Floating Point | +| `vlogefp128` | `vlogefp128` | β€” | Vector128 Log2 Estimate Floating Point | + +## Syntax + +```asm +vlogefp [VD], [VB] +vlogefp128 [VD], [VB] +``` + +## Encoding + +### `vlogefp` β€” form `VX` + +- **Opcode word:** `0x100001ca` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `458` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vlogefp128` β€” form `VX128_3` + +- **Opcode word:** `0x180006f0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1776` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vlogefp: read; vlogefp128: read | Source B vector register. | +| `VD` | vlogefp: write; vlogefp128: write | Destination vector register. | + +## Register Effects + +### `vlogefp` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vlogefp128` + +- **Reads (always):** `VB` +- **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 + +**`vlogefp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vlogefp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:779`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L779) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:99`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L99) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:473`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L473) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4377-4386`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4377-L4386) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vlogefp | PpcOpcode::vlogefp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vlogefp128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].log2(); } + ctx.vr[rd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vlogefp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vlogefp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:782`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L782) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:99`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L99) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:667`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L667) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4377-4386`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4377-L4386) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vlogefp | PpcOpcode::vlogefp128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vlogefp128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = ctx.vr[rb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].log2(); } + ctx.vr[rd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane base-2 logarithm.** Each of the four word lanes computes `VD[i] = log2(VB[i])` in `binary32`. **Note:** the IBM manual specifies a low-precision estimate (≀ 1/32 ULP relative error). Xenia uses Rust's `f32::log2`, which is full-precision; hardware-precise programs may observe small numerical differences. +- **Use `vexptefp` for the inverse.** Pair gives `2^(log2(x)) β‰ˆ x` for positive finite `x`. +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **NaN, negatives, zero, ±∞.** `log2(negative)` and `log2(NaN)` produce NaN; `log2(+0) = -∞`; `log2(-0) = -∞` (per IEEE-754); `log2(+∞) = +∞`. None of these stickies `VSCR[SAT]` β€” float ops never touch SAT. +- **No exception, no `VSCR[SAT]` change, no XER change.** +- **VMX128 sibling (`vlogefp128`).** Identical semantics with the extended encoding. +- **Natural log via change-of-base.** `ln(x) = log2(x) * (1 / log2(e))` β€” multiply by a constant with `vmaddfp`. + +## Related Instructions + +- [`vexptefp`](vexptefp.md) β€” base-2 exponent (the inverse). +- [`vrefp`](vrefp.md) β€” reciprocal estimate. +- [`vrsqrtefp`](vrsqrtefp.md) β€” reciprocal-square-root estimate. +- [`vmaddfp`](vmaddfp.md) β€” fused multiply-add for change-of-base scaling. + +## IBM Reference + +- [AIX 7.3 β€” `vlogefp` (Vector log2 Estimate Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vlogefp-vector-log2-estimate-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Estimate Instructions](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaddfp.md b/tools/ppc-manual/vmx/vmaddfp.md new file mode 100644 index 00000000..72fc2312 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaddfp.md @@ -0,0 +1,196 @@ +# `vmaddfp` β€” Vector Multiply-Add Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x1000002e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaddfp` | `vmaddfp` | β€” | Vector Multiply-Add Floating Point | +| `vmaddfp128` | `vmaddfp128` | β€” | Vector128 Multiply Add Floating Point | + +## Syntax + +```asm +vmaddfp [VD], [VA], [VC], [VB] +vmaddfp128 [VD], [VA], [VB], [VD] +``` + +## Encoding + +### `vmaddfp` β€” form `VA` + +- **Opcode word:** `0x1000002e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `46` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +### `vmaddfp128` β€” form `VX128` + +- **Opcode word:** `0x140000d0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `208` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaddfp: read; vmaddfp128: read | Source A vector register. | +| `VC` | vmaddfp: read; vmaddfp128: read | Source C vector register / 3-bit selector. | +| `VB` | vmaddfp: read; vmaddfp128: read | Source B vector register. | +| `VD` | vmaddfp: write; vmaddfp128: write | Destination vector register. | + +## Register Effects + +### `vmaddfp` + +- **Reads (always):** `VA`, `VC`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vmaddfp128` + +- **Reads (always):** `VA`, `VC`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +for each 32-bit float lane i in 0..3: + VD[i] <- (VA[i] * VC[i]) + VB[i] +``` + +## 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 + +**`vmaddfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaddfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:801`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L801) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:100`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L100) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:588`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L588) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2038-2054`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2038-L2054) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaddfp => { + // vD = (vA * vC) + vB. AltiVec unconditionally flushes denormal + // *inputs* to 0 regardless of VSCR[NJ] (confirmed on POWER8 hw). + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let c = ctx.vr[instr.rc()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + let ci = vmx::flush_denorm(c[i]); + // PPCBUG-437: flush subnormal output too. + r[i] = vmx::flush_denorm(ai.mul_add(ci, bi)); + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vmaddfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaddfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:805`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L805) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:100`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L100) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:613`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L613) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2055-2073`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2055-L2073) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaddfp128 => { + // ISA: (VD) <- (VA Γ— VD) + VB. VD is both the second multiplicand and destination. + // Canary InstrEmit_vmaddfp128 (ppc_emit_altivec.cc:806-809): MulAdd(VA, VD, VB). + // Previous code computed ai.mul_add(bi, di) = VAΓ—VB+VD β€” VB and VD roles swapped + // (PPCBUG-424). Fix: ai.mul_add(di, bi) = VAΓ—VD+VB. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let d = ctx.vr[instr.vd128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + let di = vmx::flush_denorm(d[i]); + // PPCBUG-437. + r[i] = vmx::flush_denorm(ai.mul_add(di, bi)); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Fused multiply-add: `VD = (VA * VC) + VB`** per word lane (single rounding). No intermediate rounding between the multiply and the add β€” this is critical for numerical accuracy in DSP filters and reduces error in dot products. +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **NaN propagation, ±∞ arithmetic.** Standard IEEE-754: any NaN input yields NaN; `(+∞ * 0)` yields NaN; the sum of `+∞` and `-∞` (e.g. `(+∞ * 1) + -∞`) yields NaN. No trap, no sticky bit. +- **`VSCR[NJ]` denormals.** With `NJ = 1` (Xenon default), denormal inputs and outputs are flushed to `Β±0`. +- **No `VSCR[SAT]` change, no XER change, no exceptions.** +- **VMX128 sibling has surprising operand layout β€” `VD` is also a source.** Xenia's `vmaddfp128` reads `VA`, `VB`, *and `VD` itself* (as the accumulator), computing `VD = (VA * VB) + VD_prev` ([`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs)). The standard `vmaddfp` keeps the canonical 4-operand `VA, VC, VB β†’ VD` shape. **This is a real difference in operand encoding** (VX128_3 form vs. VA-form) that compilers must respect β€” VMX128 sacrifices the third source register slot for the extra register-file bits. +- **Aliasing legal.** `vmaddfp v3, v3, v3, v3` works (squares + adds itself). +- **Common usage.** Per-lane polynomial evaluation, dot-product accumulation, any matrix multiply inner loop. Pair four `vmaddfp` instructions to do a 4Γ—4 Γ— 4-vec multiply. + +## Related Instructions + +- [`vnmsubfp`](vnmsubfp.md) β€” `βˆ’((VA * VC) βˆ’ VB)`; fused negative-multiply-subtract. +- [`vaddfp`](vaddfp.md), [`vsubfp`](vsubfp.md) β€” plain float add / subtract. +- [`vmulfp`](vmulfp.md) β€” xenia helper for `VA * VC`; on hardware games use `vmaddfp v, va, vc, v0_zero`. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” min / max for clamping. +- [`vrefp`](vrefp.md), [`vrsqrtefp`](vrsqrtefp.md) β€” reciprocal / inverse-sqrt estimates that often appear in the same FMA chain. + +## IBM Reference + +- [AIX 7.3 β€” `vmaddfp` (Vector Multiply-Add Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaddfp-vector-multiply-add-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Multiply-Add Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxfp.md b/tools/ppc-manual/vmx/vmaxfp.md new file mode 100644 index 00000000..0c188ac0 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxfp.md @@ -0,0 +1,182 @@ +# `vmaxfp` β€” Vector Maximum Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000040a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxfp` | `vmaxfp` | β€” | Vector Maximum Floating Point | +| `vmaxfp128` | `vmaxfp128` | β€” | Vector128 Maximum Floating Point | + +## Syntax + +```asm +vmaxfp [VD], [VA], [VB] +vmaxfp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxfp` β€” form `VX` + +- **Opcode word:** `0x1000040a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1034` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vmaxfp128` β€” form `VX128` + +- **Opcode word:** `0x18000280` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `640` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxfp: read; vmaxfp128: read | Source A vector register. | +| `VB` | vmaxfp: read; vmaxfp128: read | Source B vector register. | +| `VD` | vmaxfp: write; vmaxfp128: write | Destination vector register. | + +## Register Effects + +### `vmaxfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vmaxfp128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:831`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L831) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:522`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L522) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2121-2128`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2121-L2128) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxfp => { + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = vmx::max_nan(a[i], b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vmaxfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:834`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L834) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:696`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L696) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2129-2136`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2129-L2136) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxfp128 => { + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = vmx::max_nan(a[i], b[i]); } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane IEEE max.** Four word lanes; `VD[i] = (VA[i] > VB[i]) ? VA[i] : VB[i]`. +- **NaN propagation surprise.** Xenia uses `if a > b { a } else { b }`, so any NaN comparison evaluates false and the result is `VB`. The IBM manual specifies "the larger of `VA[i]` and `VB[i]`, with NaN handling such that any NaN input yields a NaN result" β€” this is *not* what xenia does. Hardware's `vmaxfp(NaN, x) = NaN` while xenia returns `x`. **Worth checking against `vmx.rs` for any future correctness fixes.** +- **Sign of zero.** `vmaxfp(+0, -0)` returns `-0` in xenia (since `+0 > -0` is false β†’ returns `b = -0`). The hardware likely returns the sign-positive zero β€” also worth verifying. +- **`VSCR[NJ]` denormals.** With `NJ = 1` (Xenon default), denormal inputs are flushed to `Β±0` before comparison. +- **No `VSCR[SAT]` change, no XER change, no exceptions.** +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **Aliasing legal.** `vmaxfp v3, v3, v4` is the standard "clamp from below by `v4`" idiom. +- **VMX128 sibling (`vmaxfp128`).** Identical comparator semantics with the extended encoding. + +## Related Instructions + +- [`vminfp`](vminfp.md) β€” the per-lane minimum. +- [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” combine masks with arbitrary alternatives. +- [`vmaddfp`](vmaddfp.md) β€” fused multiply-add when the max is part of a polynomial. +- [`vmaxsw`](vmaxsw.md) β€” integer-word max if the lanes are signed integers. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxfp` (Vector Maximum Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxfp-vector-maximum-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxsb.md b/tools/ppc-manual/vmx/vmaxsb.md new file mode 100644 index 00000000..b94ef148 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxsb.md @@ -0,0 +1,130 @@ +# `vmaxsb` β€” Vector Maximum Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000102` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxsb` | `vmaxsb` | β€” | Vector Maximum Signed Byte | + +## Syntax + +```asm +vmaxsb [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxsb` β€” form `VX` + +- **Opcode word:** `0x10000102` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `258` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxsb: read | Source A vector register. | +| `VB` | vmaxsb: read | Source B vector register. | +| `VD` | vmaxsb: write | Destination vector register. | + +## Register Effects + +### `vmaxsb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:838`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L838) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:454`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L454) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4407-4414`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4407-L4414) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxsb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i8; 16]; + for i in 0..16 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte signed max.** Sixteen byte lanes; `VD[i] = max(int8(VA[i]), int8(VB[i]))`. Lane 0 is the most-significant byte. +- **Sign-aware ordering.** `vmaxsb(0xFF, 0x01) = 0x01` (i.e. `max(-1, 1) = 1`), versus [`vmaxub`](vmaxub.md) which would return `0xFF`. Pick `vmaxsb` deliberately when both sides are signed. +- **No `VSCR` interaction, no XER, no exceptions.** Pure compare-select. +- **Common usage.** Per-lane clamping with [`vminsb`](vminsb.md) implements `clamp(x, lo, hi)` with no branch. +- **Aliasing legal.** `vmaxsb v3, v3, v4` is "raise lower-bound to `v4`" idiom. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminsb`](vminsb.md) β€” the matching minimum. +- [`vmaxub`](vmaxub.md) β€” same width, unsigned max. +- [`vmaxsh`](vmaxsh.md), [`vmaxsw`](vmaxsw.md) β€” signed max at half / word width. +- [`vcmpgtsb`](vcmpgtsb.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection style with arbitrary fallbacks. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxsb` (Vector Maximum Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxsb-vector-maximum-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxsh.md b/tools/ppc-manual/vmx/vmaxsh.md new file mode 100644 index 00000000..c2c997f3 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxsh.md @@ -0,0 +1,130 @@ +# `vmaxsh` β€” Vector Maximum Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000142` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxsh` | `vmaxsh` | β€” | Vector Maximum Signed Half Word | + +## Syntax + +```asm +vmaxsh [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxsh` β€” form `VX` + +- **Opcode word:** `0x10000142` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `322` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxsh: read | Source A vector register. | +| `VB` | vmaxsh: read | Source B vector register. | +| `VD` | vmaxsh: write | Destination vector register. | + +## Register Effects + +### `vmaxsh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:845`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L845) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:460`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L460) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4439-4446`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4439-L4446) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxsh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half signed max.** Eight half-word lanes; `VD[i] = max(int16(VA[i]), int16(VB[i]))`. Lane 0 is the most-significant half. +- **Sign-aware ordering.** `vmaxsh(0x8000, 0x0001) = 0x0001` (i.e. `max(-32768, 1) = 1`), versus [`vmaxuh`](vmaxuh.md) which would return `0x8000`. +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Q15 audio peak detection; signed image-processing kernels. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminsh`](vminsh.md) β€” the matching minimum. +- [`vmaxuh`](vmaxuh.md) β€” same width, unsigned max. +- [`vmaxsb`](vmaxsb.md), [`vmaxsw`](vmaxsw.md) β€” signed max at byte / word width. +- [`vcmpgtsh`](vcmpgtsh.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection style with arbitrary fallbacks. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxsh` (Vector Maximum Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxsh-vector-maximum-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxsw.md b/tools/ppc-manual/vmx/vmaxsw.md new file mode 100644 index 00000000..6be39fcd --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxsw.md @@ -0,0 +1,130 @@ +# `vmaxsw` β€” Vector Maximum Signed Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000182` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxsw` | `vmaxsw` | β€” | Vector Maximum Signed Word | + +## Syntax + +```asm +vmaxsw [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxsw` β€” form `VX` + +- **Opcode word:** `0x10000182` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `386` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxsw: read | Source A vector register. | +| `VB` | vmaxsw: read | Source B vector register. | +| `VD` | vmaxsw: write | Destination vector register. | + +## Register Effects + +### `vmaxsw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxsw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxsw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:852`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L852) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:467`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L467) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4471-4478`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4471-L4478) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxsw => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word signed max.** Four word lanes; `VD[i] = max(int32(VA[i]), int32(VB[i]))`. Lane 0 is the most-significant word. +- **Sign-aware ordering.** `vmaxsw(0x8000_0000, 0x0000_0001) = 0x0000_0001` (i.e. `max(INT32_MIN, 1) = 1`). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Z-buffer "keep nearest" updates, signed counter ceilings. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminsw`](vminsw.md) β€” the matching minimum. +- [`vmaxuw`](vmaxuw.md) β€” same width, unsigned max. +- [`vmaxsb`](vmaxsb.md), [`vmaxsh`](vmaxsh.md) β€” signed max at byte / half width. +- [`vcmpgtsw`](vcmpgtsw.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxsw` (Vector Maximum Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxsw-vector-maximum-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxub.md b/tools/ppc-manual/vmx/vmaxub.md new file mode 100644 index 00000000..7c145775 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxub.md @@ -0,0 +1,130 @@ +# `vmaxub` β€” Vector Maximum Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000002` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxub` | `vmaxub` | β€” | Vector Maximum Unsigned Byte | + +## Syntax + +```asm +vmaxub [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxub` β€” form `VX` + +- **Opcode word:** `0x10000002` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `2` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxub: read | Source A vector register. | +| `VB` | vmaxub: read | Source B vector register. | +| `VD` | vmaxub: write | Destination vector register. | + +## Register Effects + +### `vmaxub` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:859`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L859) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:435`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L435) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4391-4398`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4391-L4398) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte unsigned max.** Sixteen byte lanes; `VD[i] = max(uint8(VA[i]), uint8(VB[i]))`. Lane 0 is the most-significant byte. +- **Unsigned ordering.** `vmaxub(0xFF, 0x01) = 0xFF`, opposite to [`vmaxsb`](vmaxsb.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Pixel "brighter of two" channel selection; alpha mask combining. +- **Aliasing legal.** `vmaxub v3, v3, v4` raises `v3`'s lower bound to `v4`. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminub`](vminub.md) β€” the matching minimum. +- [`vmaxsb`](vmaxsb.md) β€” same width, signed max. +- [`vmaxuh`](vmaxuh.md), [`vmaxuw`](vmaxuw.md) β€” unsigned max at half / word width. +- [`vcmpgtub`](vcmpgtub.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxub` (Vector Maximum Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxub-vector-maximum-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxuh.md b/tools/ppc-manual/vmx/vmaxuh.md new file mode 100644 index 00000000..e6e02204 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxuh.md @@ -0,0 +1,130 @@ +# `vmaxuh` β€” Vector Maximum Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000042` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxuh` | `vmaxuh` | β€” | Vector Maximum Unsigned Half Word | + +## Syntax + +```asm +vmaxuh [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxuh` β€” form `VX` + +- **Opcode word:** `0x10000042` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `66` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxuh: read | Source A vector register. | +| `VB` | vmaxuh: read | Source B vector register. | +| `VD` | vmaxuh: write | Destination vector register. | + +## Register Effects + +### `vmaxuh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxuh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxuh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:867`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L867) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:442`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L442) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4423-4430`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4423-L4430) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxuh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half unsigned max.** Eight half-word lanes; `VD[i] = max(uint16(VA[i]), uint16(VB[i]))`. Lane 0 is the most-significant half. +- **Unsigned ordering.** `vmaxuh(0xFFFF, 0x0001) = 0xFFFF`, opposite to [`vmaxsh`](vmaxsh.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Audio sample magnitude tracking; UTF-16 codepoint upper bound. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminuh`](vminuh.md) β€” the matching minimum. +- [`vmaxsh`](vmaxsh.md) β€” same width, signed max. +- [`vmaxub`](vmaxub.md), [`vmaxuw`](vmaxuw.md) β€” unsigned max at byte / word width. +- [`vcmpgtuh`](vcmpgtuh.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxuh` (Vector Maximum Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxuh-vector-maximum-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmaxuw.md b/tools/ppc-manual/vmx/vmaxuw.md new file mode 100644 index 00000000..c44300f0 --- /dev/null +++ b/tools/ppc-manual/vmx/vmaxuw.md @@ -0,0 +1,130 @@ +# `vmaxuw` β€” Vector Maximum Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000082` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaxuw` | `vmaxuw` | β€” | Vector Maximum Unsigned Word | + +## Syntax + +```asm +vmaxuw [VD], [VA], [VB] +``` + +## Encoding + +### `vmaxuw` β€” form `VX` + +- **Opcode word:** `0x10000082` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `130` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaxuw: read | Source A vector register. | +| `VB` | vmaxuw: read | Source B vector register. | +| `VD` | vmaxuw: write | Destination vector register. | + +## Register Effects + +### `vmaxuw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmaxuw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaxuw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:875`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L875) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:101`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L101) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:449`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L449) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4455-4462`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4455-L4462) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaxuw => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i].max(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word unsigned max.** Four word lanes; `VD[i] = max(uint32(VA[i]), uint32(VB[i]))`. Lane 0 is the most-significant word. +- **Unsigned ordering.** `vmaxuw(0x8000_0000, 0x0000_0001) = 0x8000_0000`, opposite to [`vmaxsw`](vmaxsw.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Hashtable bucket capacity tracking, packed 32-bit ID upper bounds. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vminuw`](vminuw.md) β€” the matching minimum. +- [`vmaxsw`](vmaxsw.md) β€” same width, signed max. +- [`vmaxub`](vmaxub.md), [`vmaxuh`](vmaxuh.md) β€” unsigned max at byte / half width. +- [`vcmpgtuw`](vcmpgtuw.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vmaxuw` (Vector Maximum Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmaxuw-vector-maximum-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmhaddshs.md b/tools/ppc-manual/vmx/vmhaddshs.md new file mode 100644 index 00000000..9d1f62fa --- /dev/null +++ b/tools/ppc-manual/vmx/vmhaddshs.md @@ -0,0 +1,146 @@ +# `vmhaddshs` β€” Vector Multiply-High and Add Signed Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000020` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmhaddshs` | `vmhaddshs` | β€” | Vector Multiply-High and Add Signed Signed Half Word Saturate | + +## Syntax + +```asm +vmhaddshs [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmhaddshs` β€” form `VA` + +- **Opcode word:** `0x10000020` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `32` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmhaddshs: read | Source A vector register. | +| `VB` | vmhaddshs: read | Source B vector register. | +| `VC` | vmhaddshs: read | Source C vector register / 3-bit selector. | +| `VD` | vmhaddshs: write | Destination vector register. | +| `VSCR` | vmhaddshs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vmhaddshs` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vmhaddshs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vmhaddshs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmhaddshs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:883`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L883) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:102`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L102) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:576`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L576) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3519-3533`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3519-L3533) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmhaddshs => { + // vD[i] = sat_i16((vA[i] * vB[i]) >> 15 + vC[i]) + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let c = crate::vmx::as_i16x8(ctx.vr[instr.rc()]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..8 { + let prod = (a[i] as i32 * b[i] as i32) >> 15; + let (v, s) = crate::vmx::sat_i32_to_i16(prod + c[i] as i32); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Q15 fixed-point multiply-add, saturating.** Eight half-word lanes; per lane: + ``` + prod = (int16(VA[i]) * int16(VB[i])) >> 15 ; truncating, no rounding + VD[i] = clamp(prod + int16(VC[i]), -32768, +32767) + ``` + The "h" in the mnemonic is "high half" β€” only the upper 17 bits of the 32-bit signed product survive (after >>15), then the accumulator is added. +- **Truncating, not rounding.** Bit 14 of the product is discarded silently. Use [`vmhraddshs`](vmhraddshs.md) when half-up rounding is needed (it adds `0x4000` to the product before the shift). The two are otherwise identical. +- **`VSCR[SAT]` is sticky-set** if `prod + VC[i]` overflows `int16`. Cleared only by [`mtvscr`](mtvscr.md). Xenia uses `crate::vmx::sat_i32_to_i16` ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). +- **Pathological case `0x8000 * 0x8000 >> 15`.** Equals `0x10000` in the un-saturated product = `+32768` after the shift, which overflows `int16` even before adding `VC`. The clamp then produces `+32767` and stickies SAT. This is the classic Q15 "minus-one-times-minus-one" gotcha. +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **No XER changes, no exceptions.** +- **No VMX128 sibling.** +- **Common usage.** Q15 IIR / FIR filter taps, fixed-point matrix-vector multiplies for audio. + +## Related Instructions + +- [`vmhraddshs`](vmhraddshs.md) β€” same operation with rounded multiply (`+0x4000` before `>> 15`). +- [`vmladduhm`](vmladduhm.md) β€” same shape, modulo (no shift, no saturate), unsigned half lanes. +- [`vmsumshs`](vmsumshs.md), [`vmsumshm`](vmsumshm.md) β€” multiply-sum across pairs of lanes. +- [`vaddshs`](vaddshs.md), [`vmaxsh`](vmaxsh.md) β€” saturating add and max at the same lane width, useful in the same DSP kernels. + +## IBM Reference + +- [AIX 7.3 β€” `vmhaddshs` (Vector Multiply-High and Add Signed Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmhaddshs-vector-multiply-high-add-signed-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Add Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmhraddshs.md b/tools/ppc-manual/vmx/vmhraddshs.md new file mode 100644 index 00000000..1396b82f --- /dev/null +++ b/tools/ppc-manual/vmx/vmhraddshs.md @@ -0,0 +1,146 @@ +# `vmhraddshs` β€” Vector Multiply-High Round and Add Signed Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000021` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmhraddshs` | `vmhraddshs` | β€” | Vector Multiply-High Round and Add Signed Signed Half Word Saturate | + +## Syntax + +```asm +vmhraddshs [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmhraddshs` β€” form `VA` + +- **Opcode word:** `0x10000021` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `33` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmhraddshs: read | Source A vector register. | +| `VB` | vmhraddshs: read | Source B vector register. | +| `VC` | vmhraddshs: read | Source C vector register / 3-bit selector. | +| `VD` | vmhraddshs: write | Destination vector register. | +| `VSCR` | vmhraddshs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vmhraddshs` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vmhraddshs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vmhraddshs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmhraddshs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:888`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L888) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:102`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L102) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:577`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L577) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3534-3548`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3534-L3548) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmhraddshs => { + // Rounded multiply-add: (vA[i]*vB[i] + 0x4000) >> 15 + vC[i], saturating. + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let c = crate::vmx::as_i16x8(ctx.vr[instr.rc()]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..8 { + let prod = (a[i] as i32 * b[i] as i32 + 0x4000) >> 15; + let (v, s) = crate::vmx::sat_i32_to_i16(prod + c[i] as i32); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Rounded Q15 fixed-point multiply-add, saturating.** Eight half-word lanes; per lane: + ``` + prod = (int16(VA[i]) * int16(VB[i]) + 0x4000) >> 15 ; round half-up + VD[i] = clamp(prod + int16(VC[i]), -32768, +32767) + ``` + Identical to [`vmhaddshs`](vmhaddshs.md) except for the `+0x4000` rounding bias before the shift. +- **Half-up rounding to even-magnitude.** The `+0x4000` bias rounds the discarded low 15 bits *toward* the nearest representable value, with ties broken away from zero. For most DSP work this is the desired behaviour and gives lower mean error than the truncating variant. +- **`VSCR[SAT]` is sticky-set** if the final sum overflows `int16`. The rounding bias can itself push a lane that was at `+32767` past the cap β€” important for tight Q15 audio where the truncating form might not have saturated. +- **Same `0x8000 * 0x8000 >> 15` gotcha** as `vmhaddshs`: the product is `+32768.5` rounded to `+32769`, which still saturates. +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **No XER changes, no exceptions.** +- **No VMX128 sibling.** +- **Common usage.** High-quality Q15 audio filter taps where round-toward-nearest is preferred over truncate-toward-zero. + +## Related Instructions + +- [`vmhaddshs`](vmhaddshs.md) β€” same op without the rounding bias. +- [`vmladduhm`](vmladduhm.md) β€” same shape, modulo (no shift, no saturate), unsigned. +- [`vmsumshs`](vmsumshs.md), [`vmsumshm`](vmsumshm.md) β€” multiply-sum across pairs of lanes. +- [`vaddshs`](vaddshs.md), [`vmaxsh`](vmaxsh.md) β€” saturating add and max at same lane width. + +## IBM Reference + +- [AIX 7.3 β€” `vmhraddshs` (Vector Multiply-High Round and Add Signed Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmhraddshs-vector-multiply-high-round-add-signed-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Add Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminfp.md b/tools/ppc-manual/vmx/vminfp.md new file mode 100644 index 00000000..127aa61b --- /dev/null +++ b/tools/ppc-manual/vmx/vminfp.md @@ -0,0 +1,182 @@ +# `vminfp` β€” Vector Minimum Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000044a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminfp` | `vminfp` | β€” | Vector Minimum Floating Point | +| `vminfp128` | `vminfp128` | β€” | Vector128 Minimum Floating Point | + +## Syntax + +```asm +vminfp [VD], [VA], [VB] +vminfp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vminfp` β€” form `VX` + +- **Opcode word:** `0x1000044a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1098` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vminfp128` β€” form `VX128` + +- **Opcode word:** `0x180002c0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `704` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminfp: read; vminfp128: read | Source A vector register. | +| `VB` | vminfp: read; vminfp128: read | Source B vector register. | +| `VD` | vminfp: write; vminfp128: write | Destination vector register. | + +## Register Effects + +### `vminfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vminfp128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:899`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L899) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:527`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L527) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2137-2144`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2137-L2144) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminfp => { + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = vmx::min_nan(a[i], b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vminfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:902`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L902) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:697`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L697) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2145-2152`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2145-L2152) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminfp128 => { + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = vmx::min_nan(a[i], b[i]); } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane IEEE min.** Four word lanes; `VD[i] = (VA[i] < VB[i]) ? VA[i] : VB[i]`. +- **NaN propagation surprise.** Xenia uses `if a < b { a } else { b }`, so any NaN comparison evaluates false and the result is `VB`. The IBM manual specifies NaN-propagating min β€” i.e. NaN inputs should yield NaN. Hardware's `vminfp(NaN, x) = NaN` while xenia returns `x`. **Worth checking against `vmx.rs` for any future correctness fixes.** +- **Sign of zero.** `vminfp(+0, -0)` returns `-0` in xenia (since `+0 < -0` is false β†’ returns `b = -0`); hardware likely returns the negative zero too via the same comparator. +- **`VSCR[NJ]` denormals.** With `NJ = 1` (Xenon default), denormal inputs are flushed to `Β±0` before comparison. +- **No `VSCR[SAT]` change, no XER change, no exceptions.** +- **Big-endian word lanes.** Lane 0 is the most-significant word. +- **Aliasing legal.** `vminfp v3, v3, v4` clamps `v3` from above by `v4`. +- **VMX128 sibling (`vminfp128`).** Identical comparator semantics with the extended encoding. + +## Related Instructions + +- [`vmaxfp`](vmaxfp.md) β€” the per-lane maximum. +- [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” combine masks with arbitrary alternatives. +- [`vmaddfp`](vmaddfp.md) β€” fused multiply-add when the min is part of a polynomial. +- [`vminsw`](vminsw.md) β€” integer-word min if the lanes are signed integers. + +## IBM Reference + +- [AIX 7.3 β€” `vminfp` (Vector Minimum Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminfp-vector-minimum-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminsb.md b/tools/ppc-manual/vmx/vminsb.md new file mode 100644 index 00000000..bf4598a3 --- /dev/null +++ b/tools/ppc-manual/vmx/vminsb.md @@ -0,0 +1,130 @@ +# `vminsb` β€” Vector Minimum Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000302` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminsb` | `vminsb` | β€” | Vector Minimum Signed Byte | + +## Syntax + +```asm +vminsb [VD], [VA], [VB] +``` + +## Encoding + +### `vminsb` β€” form `VX` + +- **Opcode word:** `0x10000302` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `770` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminsb: read | Source A vector register. | +| `VB` | vminsb: read | Source B vector register. | +| `VD` | vminsb: write | Destination vector register. | + +## Register Effects + +### `vminsb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:906`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L906) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:499`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L499) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4415-4422`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4415-L4422) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminsb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i8; 16]; + for i in 0..16 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte signed min.** Sixteen byte lanes; `VD[i] = min(int8(VA[i]), int8(VB[i]))`. Lane 0 is the most-significant byte. +- **Sign-aware ordering.** `vminsb(0xFF, 0x01) = 0xFF` (i.e. `min(-1, 1) = -1`), versus [`vminub`](vminub.md) which returns `0x01`. +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Pair with [`vmaxsb`](vmaxsb.md) for branchless `clamp(x, lo, hi)`. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxsb`](vmaxsb.md) β€” the matching maximum. +- [`vminub`](vminub.md) β€” same width, unsigned min. +- [`vminsh`](vminsh.md), [`vminsw`](vminsw.md) β€” signed min at half / word width. +- [`vcmpgtsb`](vcmpgtsb.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminsb` (Vector Minimum Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminsb-vector-minimum-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminsh.md b/tools/ppc-manual/vmx/vminsh.md new file mode 100644 index 00000000..44fa2199 --- /dev/null +++ b/tools/ppc-manual/vmx/vminsh.md @@ -0,0 +1,130 @@ +# `vminsh` β€” Vector Minimum Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000342` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminsh` | `vminsh` | β€” | Vector Minimum Signed Half Word | + +## Syntax + +```asm +vminsh [VD], [VA], [VB] +``` + +## Encoding + +### `vminsh` β€” form `VX` + +- **Opcode word:** `0x10000342` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `834` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminsh: read | Source A vector register. | +| `VB` | vminsh: read | Source B vector register. | +| `VD` | vminsh: write | Destination vector register. | + +## Register Effects + +### `vminsh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:913`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L913) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:506`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L506) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4447-4454`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4447-L4454) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminsh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half signed min.** Eight half-word lanes; `VD[i] = min(int16(VA[i]), int16(VB[i]))`. Lane 0 is the most-significant half. +- **Sign-aware ordering.** `vminsh(0x8000, 0x0001) = 0x8000` (i.e. `min(-32768, 1) = -32768`). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Q15 audio noise-floor computation; signed image-processing kernels. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxsh`](vmaxsh.md) β€” the matching maximum. +- [`vminuh`](vminuh.md) β€” same width, unsigned min. +- [`vminsb`](vminsb.md), [`vminsw`](vminsw.md) β€” signed min at byte / word width. +- [`vcmpgtsh`](vcmpgtsh.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminsh` (Vector Minimum Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminsh-vector-minimum-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminsw.md b/tools/ppc-manual/vmx/vminsw.md new file mode 100644 index 00000000..1bfe6efe --- /dev/null +++ b/tools/ppc-manual/vmx/vminsw.md @@ -0,0 +1,130 @@ +# `vminsw` β€” Vector Minimum Signed Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000382` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminsw` | `vminsw` | β€” | Vector Minimum Signed Word | + +## Syntax + +```asm +vminsw [VD], [VA], [VB] +``` + +## Encoding + +### `vminsw` β€” form `VX` + +- **Opcode word:** `0x10000382` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `898` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminsw: read | Source A vector register. | +| `VB` | vminsw: read | Source B vector register. | +| `VD` | vminsw: write | Destination vector register. | + +## Register Effects + +### `vminsw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminsw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminsw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:920`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L920) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:513`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L513) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4479-4486`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4479-L4486) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminsw => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word signed min.** Four word lanes; `VD[i] = min(int32(VA[i]), int32(VB[i]))`. Lane 0 is the most-significant word. +- **Sign-aware ordering.** `vminsw(0x8000_0000, 0x0000_0001) = 0x8000_0000` (i.e. `min(INT32_MIN, 1) = INT32_MIN`). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Z-buffer "keep furthest" updates, signed counter floors. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxsw`](vmaxsw.md) β€” the matching maximum. +- [`vminuw`](vminuw.md) β€” same width, unsigned min. +- [`vminsb`](vminsb.md), [`vminsh`](vminsh.md) β€” signed min at byte / half width. +- [`vcmpgtsw`](vcmpgtsw.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminsw` (Vector Minimum Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminsw-vector-minimum-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminub.md b/tools/ppc-manual/vmx/vminub.md new file mode 100644 index 00000000..0007edd0 --- /dev/null +++ b/tools/ppc-manual/vmx/vminub.md @@ -0,0 +1,130 @@ +# `vminub` β€” Vector Minimum Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000202` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminub` | `vminub` | β€” | Vector Minimum Unsigned Byte | + +## Syntax + +```asm +vminub [VD], [VA], [VB] +``` + +## Encoding + +### `vminub` β€” form `VX` + +- **Opcode word:** `0x10000202` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `514` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminub: read | Source A vector register. | +| `VB` | vminub: read | Source B vector register. | +| `VD` | vminub: write | Destination vector register. | + +## Register Effects + +### `vminub` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:927`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L927) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:476`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L476) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4399-4406`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4399-L4406) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte unsigned min.** Sixteen byte lanes; `VD[i] = min(uint8(VA[i]), uint8(VB[i]))`. Lane 0 is the most-significant byte. +- **Unsigned ordering.** `vminub(0xFF, 0x01) = 0x01`, opposite to [`vminsb`](vminsb.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Pixel "darker of two" channel selection; alpha mask intersection. +- **Aliasing legal.** `vminub v3, v3, v4` clamps `v3`'s upper bound to `v4`. +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxub`](vmaxub.md) β€” the matching maximum. +- [`vminsb`](vminsb.md) β€” same width, signed min. +- [`vminuh`](vminuh.md), [`vminuw`](vminuw.md) β€” unsigned min at half / word width. +- [`vcmpgtub`](vcmpgtub.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminub` (Vector Minimum Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminub-vector-minimum-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminuh.md b/tools/ppc-manual/vmx/vminuh.md new file mode 100644 index 00000000..0fe7632d --- /dev/null +++ b/tools/ppc-manual/vmx/vminuh.md @@ -0,0 +1,130 @@ +# `vminuh` β€” Vector Minimum Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000242` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminuh` | `vminuh` | β€” | Vector Minimum Unsigned Half Word | + +## Syntax + +```asm +vminuh [VD], [VA], [VB] +``` + +## Encoding + +### `vminuh` β€” form `VX` + +- **Opcode word:** `0x10000242` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `578` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminuh: read | Source A vector register. | +| `VB` | vminuh: read | Source B vector register. | +| `VD` | vminuh: write | Destination vector register. | + +## Register Effects + +### `vminuh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminuh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminuh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:935`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L935) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:483`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L483) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4431-4438`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4431-L4438) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminuh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-half unsigned min.** Eight half-word lanes; `VD[i] = min(uint16(VA[i]), uint16(VB[i]))`. Lane 0 is the most-significant half. +- **Unsigned ordering.** `vminuh(0xFFFF, 0x0001) = 0x0001`, opposite to [`vminsh`](vminsh.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Audio sample magnitude floor; UTF-16 codepoint lower bound. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxuh`](vmaxuh.md) β€” the matching maximum. +- [`vminsh`](vminsh.md) β€” same width, signed min. +- [`vminub`](vminub.md), [`vminuw`](vminuw.md) β€” unsigned min at byte / word width. +- [`vcmpgtuh`](vcmpgtuh.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminuh` (Vector Minimum Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminuh-vector-minimum-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vminuw.md b/tools/ppc-manual/vmx/vminuw.md new file mode 100644 index 00000000..2270f303 --- /dev/null +++ b/tools/ppc-manual/vmx/vminuw.md @@ -0,0 +1,130 @@ +# `vminuw` β€” Vector Minimum Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000282` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vminuw` | `vminuw` | β€” | Vector Minimum Unsigned Word | + +## Syntax + +```asm +vminuw [VD], [VA], [VB] +``` + +## Encoding + +### `vminuw` β€” form `VX` + +- **Opcode word:** `0x10000282` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `642` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vminuw: read | Source A vector register. | +| `VB` | vminuw: read | Source B vector register. | +| `VD` | vminuw: write | Destination vector register. | + +## Register Effects + +### `vminuw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vminuw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vminuw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:943`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L943) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:103`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L103) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:490`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L490) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4463-4470`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4463-L4470) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vminuw => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i].min(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word unsigned min.** Four word lanes; `VD[i] = min(uint32(VA[i]), uint32(VB[i]))`. Lane 0 is the most-significant word. +- **Unsigned ordering.** `vminuw(0x8000_0000, 0x0000_0001) = 0x0000_0001`, opposite to [`vminsw`](vminsw.md). +- **No `VSCR` interaction, no XER, no exceptions.** +- **Common usage.** Hashtable bucket capacity floors, packed 32-bit ID lower bounds. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmaxuw`](vmaxuw.md) β€” the matching maximum. +- [`vminsw`](vminsw.md) β€” same width, signed min. +- [`vminub`](vminub.md), [`vminuh`](vminuh.md) β€” unsigned min at byte / half width. +- [`vcmpgtuw`](vcmpgtuw.md) β€” separate compare-and-mask path. +- [`vsel`](vsel.md) β€” alternative selection. + +## IBM Reference + +- [AIX 7.3 β€” `vminuw` (Vector Minimum Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vminuw-vector-minimum-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Min/Max](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmladduhm.md b/tools/ppc-manual/vmx/vmladduhm.md new file mode 100644 index 00000000..f8901b70 --- /dev/null +++ b/tools/ppc-manual/vmx/vmladduhm.md @@ -0,0 +1,141 @@ +# `vmladduhm` β€” Vector Multiply-Low and Add Unsigned Half Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000022` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmladduhm` | `vmladduhm` | β€” | Vector Multiply-Low and Add Unsigned Half Word Modulo | + +## Syntax + +```asm +vmladduhm [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmladduhm` β€” form `VA` + +- **Opcode word:** `0x10000022` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `34` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmladduhm: read | Source A vector register. | +| `VB` | vmladduhm: read | Source B vector register. | +| `VC` | vmladduhm: read | Source C vector register / 3-bit selector. | +| `VD` | vmladduhm: write | Destination vector register. | + +## Register Effects + +### `vmladduhm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vmladduhm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmladduhm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:951`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L951) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:104`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L104) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:578`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L578) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3549-3560`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3549-L3560) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmladduhm => { + // Multiply-low add (modulo): vD[i] = u16(vA[i] * vB[i] + vC[i]). + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let c = ctx.vr[instr.rc()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { + r[i] = a[i].wrapping_mul(b[i]).wrapping_add(c[i]); + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Modulo multiply-low add.** Eight half-word lanes; per lane: + ``` + VD[i] = (uint16(VA[i]) * uint16(VB[i]) + uint16(VC[i])) mod 2^16 + ``` + Only the **low** 16 bits of the 32-bit product survive β€” the "ml" in the mnemonic = "multiply low" (versus `vmh*` for "multiply high"). This is the fastest of the multiply-add family because nothing saturates and nothing rounds. +- **Sign-agnostic.** Modulo multiply for signed `int16` and unsigned `u16` is bit-identical at the low 16 bits, so this single instruction serves both. +- **No `VSCR[SAT]` change.** Wrap is silent. +- **No XER, no exceptions.** +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **Aliasing legal.** `vmladduhm v3, v3, v4, v3` is the standard accumulate idiom (same register as both `VA` and `VC`). +- **No VMX128 sibling.** +- **Common usage.** Stride / index computation in vector loops, RGBA8 component recombination after a [`vupkhsb`](vupkhsb.md), per-element polynomial evaluation at half precision. + +## Related Instructions + +- [`vmhaddshs`](vmhaddshs.md) β€” saturating high-half signed multiply-add (Q15). +- [`vmhraddshs`](vmhraddshs.md) β€” same, with rounding. +- [`vmsumuhm`](vmsumuhm.md), [`vmsummbm`](vmsummbm.md) β€” multiply-sum across pairs of lanes. +- [`vadduhm`](vadduhm.md), [`vmaxuh`](vmaxuh.md) β€” companion modulo / max ops at half width. + +## IBM Reference + +- [AIX 7.3 β€” `vmladduhm` (Vector Multiply-Low and Add Unsigned Half Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmladduhm-vector-multiply-low-add-unsigned-half-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Add Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrghb.md b/tools/ppc-manual/vmx/vmrghb.md new file mode 100644 index 00000000..49c7d6c2 --- /dev/null +++ b/tools/ppc-manual/vmx/vmrghb.md @@ -0,0 +1,131 @@ +# `vmrghb` β€” Vector Merge High Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000000c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrghb` | `vmrghb` | β€” | Vector Merge High Byte | + +## Syntax + +```asm +vmrghb [VD], [VA], [VB] +``` + +## Encoding + +### `vmrghb` β€” form `VX` + +- **Opcode word:** `0x1000000c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `12` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrghb: read | Source A vector register. | +| `VB` | vmrghb: read | Source B vector register. | +| `VD` | vmrghb: write | Destination vector register. | + +## Register Effects + +### `vmrghb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrghb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrghb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:956`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L956) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:439`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L439) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3982-3989`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3982-L3989) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrghb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..8 { r[2*i] = a[i]; r[2*i+1] = b[i]; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the high (most-significant) eight bytes** of two vectors. After execution, `VD = {VA[0], VB[0], VA[1], VB[1], …, VA[7], VB[7]}`, i.e. the eight high-order bytes of `VA` are interleaved with the eight high-order bytes of `VB`. Because lane 0 is the most-significant byte (big-endian indexing), "high" means the byte that appears at the lowest address after `stvx`. +- **Pairs with [`vmrglb`](vmrglb.md).** Together they cover all 32 input bytes β€” `vmrghb` produces output of bytes 0..7 from each source, `vmrglb` of bytes 8..15. Two `vmrg*` instructions plus a [`stvx`](stvx.md) of each output produces the AoS-from-SoA transpose. +- **Useful for unpacking 8-bit channels.** `vmrghb vRG, vR, vG` followed by `vmrghb vRGBA, vRG, vBA` interleaves four byte-streams into RGBA pixels. +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** `vmrghb v3, v3, v3` doubles each high byte of `v3`. +- **No VMX128 sibling.** +- **Equivalent to x86 `_mm_unpackhi_epi8`** with operand orientation swapped (Altivec uses big-endian lane numbering, x86 little-endian, so "high" on PPC ↔ "low" lane indices on x86). + +## Related Instructions + +- [`vmrglb`](vmrglb.md) β€” the "low half" mirror. +- [`vmrghh`](vmrghh.md), [`vmrghw`](vmrghw.md) β€” high-half merge at half / word width. +- [`vperm`](vperm.md) β€” fully programmable permute when neither merge half fits. +- [`vsldoi`](vsldoi.md) β€” static-offset shift-double, often paired with `vmrg*` for AoS↔SoA conversions. +- [`vupkhsb`](vupkhsb.md) β€” sign-extending unpack of the high half. + +## IBM Reference + +- [AIX 7.3 β€” `vmrghb` (Vector Merge High Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrghb-vector-merge-high-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrghh.md b/tools/ppc-manual/vmx/vmrghh.md new file mode 100644 index 00000000..d0e2fbd4 --- /dev/null +++ b/tools/ppc-manual/vmx/vmrghh.md @@ -0,0 +1,131 @@ +# `vmrghh` β€” Vector Merge High Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000004c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrghh` | `vmrghh` | β€” | Vector Merge High Half Word | + +## Syntax + +```asm +vmrghh [VD], [VA], [VB] +``` + +## Encoding + +### `vmrghh` β€” form `VX` + +- **Opcode word:** `0x1000004c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `76` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrghh: read | Source A vector register. | +| `VB` | vmrghh: read | Source B vector register. | +| `VD` | vmrghh: write | Destination vector register. | + +## Register Effects + +### `vmrghh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrghh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrghh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:968`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L968) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:446`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L446) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3998-4005`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3998-L4005) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrghh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..4 { r[2*i] = a[i]; r[2*i+1] = b[i]; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the high (most-significant) four halves** of two vectors: `VD = {VA[0], VB[0], VA[1], VB[1], VA[2], VB[2], VA[3], VB[3]}`. +- **Pairs with [`vmrglh`](vmrglh.md)** to cover the eight halves of each source. Two-instruction transpose for half-word streams. +- **Common usage.** Interleave Q15 stereo audio: `vmrghh vL_R_high, vLeft, vRight` then `vmrglh vL_R_low, vLeft, vRight` and store to produce the natural L/R/L/R ordering. +- **Useful for half-precision colour split.** Merge two 4-channel half-precision streams. +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmrglh`](vmrglh.md) β€” the "low half" mirror. +- [`vmrghb`](vmrghb.md), [`vmrghw`](vmrghw.md) β€” high-half merge at byte / word width. +- [`vperm`](vperm.md) β€” programmable permute. +- [`vsldoi`](vsldoi.md) β€” static-offset shift-double. +- [`vupkhsh`](vupkhsh.md) β€” sign-extending unpack of the high half (4 halves β†’ 4 words). + +## IBM Reference + +- [AIX 7.3 β€” `vmrghh` (Vector Merge High Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrghh-vector-merge-high-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrghw.md b/tools/ppc-manual/vmx/vmrghw.md new file mode 100644 index 00000000..515d6ade --- /dev/null +++ b/tools/ppc-manual/vmx/vmrghw.md @@ -0,0 +1,180 @@ +# `vmrghw` β€” Vector Merge High Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000008c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrghw` | `vmrghw` | β€” | Vector Merge High Word | +| `vmrghw128` | `vmrghw128` | β€” | Vector128 Merge High Word | + +## Syntax + +```asm +vmrghw [VD], [VA], [VB] +vmrghw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmrghw` β€” form `VX` + +- **Opcode word:** `0x1000008c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `140` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vmrghw128` β€” form `VX128` + +- **Opcode word:** `0x18000300` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `768` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrghw: read; vmrghw128: read | Source A vector register. | +| `VB` | vmrghw: read; vmrghw128: read | Source B vector register. | +| `VD` | vmrghw: write; vmrghw128: write | Destination vector register. | + +## Register Effects + +### `vmrghw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vmrghw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrghw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrghw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:989`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L989) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:451`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L451) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2378-2385`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2378-L2385) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrghw | PpcOpcode::vmrghw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + // Merge high words: [a0, b0, a1, b1] + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(a[0], b[0], a[1], b[1]); + ctx.pc += 4; + } +``` +
+ +**`vmrghw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrghw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:992`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L992) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:698`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L698) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2378-2385`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2378-L2385) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrghw | PpcOpcode::vmrghw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + // Merge high words: [a0, b0, a1, b1] + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(a[0], b[0], a[1], b[1]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the high (most-significant) two words** of two vectors: `VD = {VA[0], VB[0], VA[1], VB[1]}`. Lane 0 (`VD[0..3]` after `stvx`) is the most-significant word. +- **Pairs with [`vmrglw`](vmrglw.md)** to cover the four words of each source. Two-instruction word-level transpose. +- **Common usage.** Interleave matrix rows during a 4Γ—4 transpose: four `vmrgh*`/`vmrgl*` pairs swap rows and columns of a 4Γ—4 packed-float matrix. +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** `vmrghw v3, v3, v3` doubles each high word. +- **VMX128 sibling (`vmrghw128`).** Identical semantics with the extended encoding; xenia routes via `vmx_reg_triple`. + +## Related Instructions + +- [`vmrglw`](vmrglw.md) β€” the "low half" mirror. +- [`vmrghb`](vmrghb.md), [`vmrghh`](vmrghh.md) β€” high-half merge at byte / half width. +- [`vperm`](vperm.md), [`vsldoi`](vsldoi.md) β€” programmable / static permute primitives. +- [`vupkhsh`](vupkhsh.md) β€” sign-extending unpack of the high half (4 halves β†’ 4 words). +- [`vspltw`](vspltw.md) β€” broadcast a single word for blending tasks. + +## IBM Reference + +- [AIX 7.3 β€” `vmrghw` (Vector Merge High Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrghw-vector-merge-high-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrglb.md b/tools/ppc-manual/vmx/vmrglb.md new file mode 100644 index 00000000..61cfbe7b --- /dev/null +++ b/tools/ppc-manual/vmx/vmrglb.md @@ -0,0 +1,130 @@ +# `vmrglb` β€” Vector Merge Low Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000010c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrglb` | `vmrglb` | β€” | Vector Merge Low Byte | + +## Syntax + +```asm +vmrglb [VD], [VA], [VB] +``` + +## Encoding + +### `vmrglb` β€” form `VX` + +- **Opcode word:** `0x1000010c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `268` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrglb: read | Source A vector register. | +| `VB` | vmrglb: read | Source B vector register. | +| `VD` | vmrglb: write | Destination vector register. | + +## Register Effects + +### `vmrglb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrglb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrglb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:996`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L996) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:458`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L458) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3990-3997`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3990-L3997) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrglb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..8 { r[2*i] = a[8+i]; r[2*i+1] = b[8+i]; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the low (least-significant) eight bytes** of two vectors: `VD = {VA[8], VB[8], VA[9], VB[9], …, VA[15], VB[15]}`. "Low" in PPC big-endian terms means the eight bytes at the higher byte addresses after `stvx`. +- **Pairs with [`vmrghb`](vmrghb.md).** Together they cover all 32 input bytes β€” one `vmrghb` plus one `vmrglb` is the standard 16-byte "interleave-then-store" pattern. +- **Common usage.** Second half of an AoS-from-SoA transpose for 8-bit channels (the high half is produced by `vmrghb`, the low half by `vmrglb`). +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** `vmrglb v3, v3, v3` doubles each low byte of `v3`. +- **No VMX128 sibling.** +- **Equivalent to x86 `_mm_unpacklo_epi8`** modulo lane-numbering convention. + +## Related Instructions + +- [`vmrghb`](vmrghb.md) β€” the "high half" mirror. +- [`vmrglh`](vmrglh.md), [`vmrglw`](vmrglw.md) β€” low-half merge at half / word width. +- [`vperm`](vperm.md), [`vsldoi`](vsldoi.md) β€” programmable / static permute primitives. +- [`vupklsb`](vupklsb.md) β€” sign-extending unpack of the low half. + +## IBM Reference + +- [AIX 7.3 β€” `vmrglb` (Vector Merge Low Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrglb-vector-merge-low-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrglh.md b/tools/ppc-manual/vmx/vmrglh.md new file mode 100644 index 00000000..2be233f7 --- /dev/null +++ b/tools/ppc-manual/vmx/vmrglh.md @@ -0,0 +1,129 @@ +# `vmrglh` β€” Vector Merge Low Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000014c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrglh` | `vmrglh` | β€” | Vector Merge Low Half Word | + +## Syntax + +```asm +vmrglh [VD], [VA], [VB] +``` + +## Encoding + +### `vmrglh` β€” form `VX` + +- **Opcode word:** `0x1000014c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `332` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrglh: read | Source A vector register. | +| `VB` | vmrglh: read | Source B vector register. | +| `VD` | vmrglh: write | Destination vector register. | + +## Register Effects + +### `vmrglh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrglh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrglh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1008`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1008) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:464`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L464) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4006-4013`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4006-L4013) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrglh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..4 { r[2*i] = a[4+i]; r[2*i+1] = b[4+i]; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the low (least-significant) four halves** of two vectors: `VD = {VA[4], VB[4], VA[5], VB[5], VA[6], VB[6], VA[7], VB[7]}`. +- **Pairs with [`vmrghh`](vmrghh.md)** to interleave the entire 8-half source range. The two instructions plus a [`stvx`](stvx.md) of each result produces an interleaved 16-half stream from two 8-half streams. +- **Common usage.** Stereo Q15 audio interleave (low half of stream); paired with `vupklsh` for sign-extending unpack. +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vmrghh`](vmrghh.md) β€” the "high half" mirror. +- [`vmrglb`](vmrglb.md), [`vmrglw`](vmrglw.md) β€” low-half merge at byte / word width. +- [`vperm`](vperm.md), [`vsldoi`](vsldoi.md) β€” programmable / static permute primitives. +- [`vupklsh`](vupklsh.md) β€” sign-extending unpack of the low half (4 halves β†’ 4 words). + +## IBM Reference + +- [AIX 7.3 β€” `vmrglh` (Vector Merge Low Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrglh-vector-merge-low-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmrglw.md b/tools/ppc-manual/vmx/vmrglw.md new file mode 100644 index 00000000..5b6d6d02 --- /dev/null +++ b/tools/ppc-manual/vmx/vmrglw.md @@ -0,0 +1,179 @@ +# `vmrglw` β€” Vector Merge Low Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000018c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmrglw` | `vmrglw` | β€” | Vector Merge Low Word | +| `vmrglw128` | `vmrglw128` | β€” | Vector128 Merge Low Word | + +## Syntax + +```asm +vmrglw [VD], [VA], [VB] +vmrglw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmrglw` β€” form `VX` + +- **Opcode word:** `0x1000018c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `396` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vmrglw128` β€” form `VX128` + +- **Opcode word:** `0x18000340` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `832` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmrglw: read; vmrglw128: read | Source A vector register. | +| `VB` | vmrglw: read; vmrglw128: read | Source B vector register. | +| `VD` | vmrglw: write; vmrglw128: write | Destination vector register. | + +## Register Effects + +### `vmrglw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vmrglw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmrglw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrglw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1030`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1030) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:470`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L470) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2386-2393`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2386-L2393) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrglw | PpcOpcode::vmrglw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + // Merge low words: [a2, b2, a3, b3] + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(a[2], b[2], a[3], b[3]); + ctx.pc += 4; + } +``` +
+ +**`vmrglw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmrglw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1033`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1033) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:105`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L105) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:699`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L699) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2386-2393`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2386-L2393) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmrglw | PpcOpcode::vmrglw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + // Merge low words: [a2, b2, a3, b3] + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(a[2], b[2], a[3], b[3]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Interleave the low (least-significant) two words** of two vectors: `VD = {VA[2], VB[2], VA[3], VB[3]}`. Lane 0 is the most-significant word. +- **Pairs with [`vmrghw`](vmrghw.md)** to cover the four words of each source. Two-instruction word-level transpose. +- **Common usage.** Bottom half of a 4Γ—4 packed-float matrix transpose; second-half RGBA pixel re-pack after a `vmrghw`. +- **No `VSCR` interaction, no XER, no exceptions.** Pure permute. +- **Aliasing legal.** +- **VMX128 sibling (`vmrglw128`).** Identical semantics with the extended encoding; xenia routes both via `vmx_reg_triple`. + +## Related Instructions + +- [`vmrghw`](vmrghw.md) β€” the "high half" mirror. +- [`vmrglb`](vmrglb.md), [`vmrglh`](vmrglh.md) β€” low-half merge at byte / half width. +- [`vperm`](vperm.md), [`vsldoi`](vsldoi.md) β€” programmable / static permute primitives. +- [`vspltw`](vspltw.md) β€” broadcast a single word for blending tasks. + +## IBM Reference + +- [AIX 7.3 β€” `vmrglw` (Vector Merge Low Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmrglw-vector-merge-low-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute / Merge](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsummbm.md b/tools/ppc-manual/vmx/vmsummbm.md new file mode 100644 index 00000000..353879c2 --- /dev/null +++ b/tools/ppc-manual/vmx/vmsummbm.md @@ -0,0 +1,146 @@ +# `vmsummbm` β€” Vector Multiply-Sum Mixed-Sign Byte Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000025` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsummbm` | `vmsummbm` | β€” | Vector Multiply-Sum Mixed-Sign Byte Modulo | + +## Syntax + +```asm +vmsummbm [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsummbm` β€” form `VA` + +- **Opcode word:** `0x10000025` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `37` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsummbm: read | Source A vector register. | +| `VB` | vmsummbm: read | Source B vector register. | +| `VC` | vmsummbm: read | Source C vector register / 3-bit selector. | +| `VD` | vmsummbm: write | Destination vector register. | + +## Register Effects + +### `vmsummbm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vmsummbm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsummbm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1037`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1037) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:580`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L580) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3579-3594`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3579-L3594) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsummbm => { + // signed bytes Γ— unsigned bytes, signed accumulator + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = ctx.vr[instr.rb()].as_bytes(); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rc()]); + let mut r = [0i32; 4]; + for i in 0..4 { + let mut s = c[i]; + for j in 0..4 { + s = s.wrapping_add(a[4*i+j] as i32 * b[4*i+j] as i32); + } + r[i] = s; + } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Mixed signedΓ—unsigned multiply-sum, modulo.** The "m" / "b" / "m" of `vmsummbm` decode as: `m`=mixed (signed `VA` Γ— unsigned `VB`), `b`=byte lanes, `m`=modulo accumulator. Per word lane: + ``` + VD[i] = (VC[i] + Ξ£_{j=0..3} int8(VA[4*i + j]) * uint8(VB[4*i + j])) mod 2^32 + ``` + Four signed-byte Γ— unsigned-byte products are summed with a signed-word accumulator from `VC`, into a single signed word. +- **Mixed signedness is unique to this instruction** β€” it's the canonical "signed pixel weight Γ— unsigned pixel value" combo for filter convolution. +- **No `VSCR[SAT]` change.** Modulo wrap; the saturating sibling for byte lanes does not exist (Altivec only provides a saturating `vmsum` for half-word widths). +- **Big-endian byte lanes.** Lane 0 is the most-significant byte; the four contributing bytes for output word `i` are bytes `4*i .. 4*i+3`. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Per-tile signed-weight pixel sums; H.264-style 4-tap signed filter on byte data. + +## Related Instructions + +- [`vmsumubm`](vmsumubm.md) β€” same shape, both sources unsigned (no signed weights). +- [`vmsumshm`](vmsumshm.md) / [`vmsumshs`](vmsumshs.md) β€” half-word Γ— half-word multiply-sum (modulo / saturate). +- [`vmsumuhm`](vmsumuhm.md) / [`vmsumuhs`](vmsumuhs.md) β€” unsigned half-word multiply-sum. +- [`vmladduhm`](vmladduhm.md) β€” per-lane multiply-add at half width (no horizontal reduction). +- [`vsumsws`](vsumsws.md), [`vsum4sbs`](vsum4sbs.md) β€” pure horizontal sums. + +## IBM Reference + +- [AIX 7.3 β€” `vmsummbm` (Vector Multiply-Sum Mixed-Sign Byte Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsummbm-vector-multiply-sum-mixed-sign-byte-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsumshm.md b/tools/ppc-manual/vmx/vmsumshm.md new file mode 100644 index 00000000..2ac9b6f1 --- /dev/null +++ b/tools/ppc-manual/vmx/vmsumshm.md @@ -0,0 +1,144 @@ +# `vmsumshm` β€” Vector Multiply-Sum Signed Half Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000028` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsumshm` | `vmsumshm` | β€” | Vector Multiply-Sum Signed Half Word Modulo | + +## Syntax + +```asm +vmsumshm [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsumshm` β€” form `VA` + +- **Opcode word:** `0x10000028` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `40` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsumshm: read | Source A vector register. | +| `VB` | vmsumshm: read | Source B vector register. | +| `VC` | vmsumshm: read | Source C vector register / 3-bit selector. | +| `VD` | vmsumshm: write | Destination vector register. | + +## Register Effects + +### `vmsumshm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vmsumshm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsumshm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1042`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1042) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:583`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L583) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3625-3638`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3625-L3638) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsumshm => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rc()]); + let mut r = [0i32; 4]; + for i in 0..4 { + let s = (a[2*i] as i32 * b[2*i] as i32) + .wrapping_add(a[2*i+1] as i32 * b[2*i+1] as i32) + .wrapping_add(c[i]); + r[i] = s; + } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed half-word multiply-sum, modulo.** Per word lane: + ``` + VD[i] = (VC[i] + int16(VA[2*i]) * int16(VB[2*i]) + + int16(VA[2*i+1]) * int16(VB[2*i+1])) mod 2^32 + ``` + Two signed-half Γ— signed-half products plus a signed-word accumulator β†’ one signed word per output lane. +- **Modulo wrap, never saturates.** **`VSCR[SAT]` is not touched** β€” wraparound silently. Use [`vmsumshs`](vmsumshs.md) for the saturating variant. +- **Big-endian half lanes.** Lane 0 is the most-significant half; output word `i` consumes halves `2*i` and `2*i+1`. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Q15 dot products of paired audio samples, 2-tap signed FIR coefficients. + +## Related Instructions + +- [`vmsumshs`](vmsumshs.md) β€” same shape with saturating output. +- [`vmsumuhm`](vmsumuhm.md) / [`vmsumuhs`](vmsumuhs.md) β€” unsigned half multiply-sum. +- [`vmsumubm`](vmsumubm.md), [`vmsummbm`](vmsummbm.md) β€” multiply-sum at byte width. +- [`vmhaddshs`](vmhaddshs.md), [`vmhraddshs`](vmhraddshs.md) β€” per-lane multiply-add (no horizontal reduction). +- [`vsum2sws`](vsum2sws.md), [`vsumsws`](vsumsws.md) β€” pure horizontal sums. + +## IBM Reference + +- [AIX 7.3 β€” `vmsumshm` (Vector Multiply-Sum Signed Half Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsumshm-vector-multiply-sum-signed-half-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsumshs.md b/tools/ppc-manual/vmx/vmsumshs.md new file mode 100644 index 00000000..15de23db --- /dev/null +++ b/tools/ppc-manual/vmx/vmsumshs.md @@ -0,0 +1,149 @@ +# `vmsumshs` β€” Vector Multiply-Sum Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000029` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsumshs` | `vmsumshs` | β€” | Vector Multiply-Sum Signed Half Word Saturate | + +## Syntax + +```asm +vmsumshs [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsumshs` β€” form `VA` + +- **Opcode word:** `0x10000029` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `41` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsumshs: read | Source A vector register. | +| `VB` | vmsumshs: read | Source B vector register. | +| `VC` | vmsumshs: read | Source C vector register / 3-bit selector. | +| `VD` | vmsumshs: write | Destination vector register. | +| `VSCR` | vmsumshs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vmsumshs` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vmsumshs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vmsumshs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsumshs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1047`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1047) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:584`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L584) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3639-3655`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3639-L3655) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsumshs => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rc()]); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + // Running-sum saturation: accumulate in i64, clamp once at end. + let s = (a[2*i] as i64 * b[2*i] as i64) + + (a[2*i+1] as i64 * b[2*i+1] as i64) + + c[i] as i64; + let (v, o) = crate::vmx::sat_i64_to_i32(s); + r[i] = v; sat |= o; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed half-word multiply-sum, saturating.** Per word lane: + ``` + VD[i] = clamp(VC[i] + int16(VA[2*i]) * int16(VB[2*i]) + + int16(VA[2*i+1]) * int16(VB[2*i+1]), INT32_MIN, INT32_MAX) + ``` + Two signed-half Γ— signed-half products plus a signed-word accumulator, clamped to `int32`. +- **Wide-then-clamp ordering.** Xenia accumulates into `i64` first and clamps the *final* sum to `int32`, exactly matching the IBM specification ([`crates/xenia-cpu/src/vmx.rs`](../../xenia-rs/crates/xenia-cpu/src/vmx.rs)). This avoids spurious mid-sum saturation that would happen if the products were clamped individually. +- **`VSCR[SAT]` is sticky-set** if any of the four lane sums saturates. Cleared only via [`mtvscr`](mtvscr.md). +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** High-precision dot products, audio FIR taps with overflow detection, signed-pixel filter convolution. + +## Related Instructions + +- [`vmsumshm`](vmsumshm.md) β€” same shape, modulo (no clamp, no SAT flag). +- [`vmsumuhs`](vmsumuhs.md) β€” unsigned half multiply-sum, saturating. +- [`vmsummbm`](vmsummbm.md), [`vmsumubm`](vmsumubm.md) β€” multiply-sum at byte width. +- [`vaddsws`](vaddsws.md) β€” saturating word add for further accumulation. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear `VSCR[SAT]`. + +## IBM Reference + +- [AIX 7.3 β€” `vmsumshs` (Vector Multiply-Sum Signed Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsumshs-vector-multiply-sum-signed-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsumubm.md b/tools/ppc-manual/vmx/vmsumubm.md new file mode 100644 index 00000000..c27bf253 --- /dev/null +++ b/tools/ppc-manual/vmx/vmsumubm.md @@ -0,0 +1,143 @@ +# `vmsumubm` β€” Vector Multiply-Sum Unsigned Byte Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000024` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsumubm` | `vmsumubm` | β€” | Vector Multiply-Sum Unsigned Byte Modulo | + +## Syntax + +```asm +vmsumubm [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsumubm` β€” form `VA` + +- **Opcode word:** `0x10000024` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `36` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsumubm: read | Source A vector register. | +| `VB` | vmsumubm: read | Source B vector register. | +| `VC` | vmsumubm: read | Source C vector register / 3-bit selector. | +| `VD` | vmsumubm: write | Destination vector register. | + +## Register Effects + +### `vmsumubm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vmsumubm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsumubm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1052`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1052) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:579`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L579) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3564-3578`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3564-L3578) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsumubm => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let c = ctx.vr[instr.rc()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let mut s = c[i]; + for j in 0..4 { + s = s.wrapping_add(a[4*i+j] as u32 * b[4*i+j] as u32); + } + r[i] = s; + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned byte multiply-sum, modulo.** Per word lane: + ``` + VD[i] = (VC[i] + Ξ£_{j=0..3} uint8(VA[4*i + j]) * uint8(VB[4*i + j])) mod 2^32 + ``` + Four unsigned-byte Γ— unsigned-byte products and an unsigned-word accumulator from `VC`, summed into one unsigned word per lane. +- **Modulo wrap, never saturates.** **`VSCR[SAT]` is not touched** β€” wraparound silently. +- **Big-endian byte lanes.** Lane 0 is the most-significant byte; output word `i` consumes bytes `4*i .. 4*i+3`. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Pixel-component dot products (RGBA Γ— weights packed as bytes); 4-tap unsigned convolution; per-pixel "intensity sum" where the weights are byte-quantised. + +## Related Instructions + +- [`vmsummbm`](vmsummbm.md) β€” same shape, signed Γ— unsigned (mixed-sign). +- [`vmsumuhm`](vmsumuhm.md) / [`vmsumuhs`](vmsumuhs.md) β€” unsigned half multiply-sum (modulo / saturate). +- [`vmsumshm`](vmsumshm.md) / [`vmsumshs`](vmsumshs.md) β€” signed half multiply-sum. +- [`vsum4ubs`](vsum4ubs.md) β€” pure horizontal sum of bytes into words. + +## IBM Reference + +- [AIX 7.3 β€” `vmsumubm` (Vector Multiply-Sum Unsigned Byte Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsumubm-vector-multiply-sum-unsigned-byte-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsumuhm.md b/tools/ppc-manual/vmx/vmsumuhm.md new file mode 100644 index 00000000..8e6f1d2a --- /dev/null +++ b/tools/ppc-manual/vmx/vmsumuhm.md @@ -0,0 +1,143 @@ +# `vmsumuhm` β€” Vector Multiply-Sum Unsigned Half Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000026` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsumuhm` | `vmsumuhm` | β€” | Vector Multiply-Sum Unsigned Half Word Modulo | + +## Syntax + +```asm +vmsumuhm [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsumuhm` β€” form `VA` + +- **Opcode word:** `0x10000026` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `38` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsumuhm: read | Source A vector register. | +| `VB` | vmsumuhm: read | Source B vector register. | +| `VC` | vmsumuhm: read | Source C vector register / 3-bit selector. | +| `VD` | vmsumuhm: write | Destination vector register. | + +## Register Effects + +### `vmsumuhm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vmsumuhm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsumuhm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1057`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1057) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:581`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L581) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3595-3608`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3595-L3608) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsumuhm => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let c = ctx.vr[instr.rc()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let s = (a[2*i] as u32 * b[2*i] as u32) + .wrapping_add(a[2*i+1] as u32 * b[2*i+1] as u32) + .wrapping_add(c[i]); + r[i] = s; + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word multiply-sum, modulo.** Per word lane: + ``` + VD[i] = (VC[i] + uint16(VA[2*i]) * uint16(VB[2*i]) + + uint16(VA[2*i+1]) * uint16(VB[2*i+1])) mod 2^32 + ``` + Two unsigned-half Γ— unsigned-half products and an unsigned-word accumulator β†’ one unsigned word per output lane. +- **Modulo wrap, never saturates.** **`VSCR[SAT]` is not touched.** +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Unsigned 16-bit FIR taps; pair-wise component sums for half-precision colour data. + +## Related Instructions + +- [`vmsumuhs`](vmsumuhs.md) β€” same shape, saturating output. +- [`vmsumshm`](vmsumshm.md) / [`vmsumshs`](vmsumshs.md) β€” signed half multiply-sum. +- [`vmsumubm`](vmsumubm.md), [`vmsummbm`](vmsummbm.md) β€” multiply-sum at byte width. +- [`vmladduhm`](vmladduhm.md) β€” per-lane multiply-add at half width. + +## IBM Reference + +- [AIX 7.3 β€” `vmsumuhm` (Vector Multiply-Sum Unsigned Half Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsumuhm-vector-multiply-sum-unsigned-half-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmsumuhs.md b/tools/ppc-manual/vmx/vmsumuhs.md new file mode 100644 index 00000000..93508dec --- /dev/null +++ b/tools/ppc-manual/vmx/vmsumuhs.md @@ -0,0 +1,148 @@ +# `vmsumuhs` β€” Vector Multiply-Sum Unsigned Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x10000027` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsumuhs` | `vmsumuhs` | β€” | Vector Multiply-Sum Unsigned Half Word Saturate | + +## Syntax + +```asm +vmsumuhs [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vmsumuhs` β€” form `VA` + +- **Opcode word:** `0x10000027` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `39` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsumuhs: read | Source A vector register. | +| `VB` | vmsumuhs: read | Source B vector register. | +| `VC` | vmsumuhs: read | Source C vector register / 3-bit selector. | +| `VD` | vmsumuhs: write | Destination vector register. | +| `VSCR` | vmsumuhs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vmsumuhs` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vmsumuhs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vmsumuhs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsumuhs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1062`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1062) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:107`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L107) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:582`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L582) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3609-3624`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3609-L3624) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsumuhs => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let c = ctx.vr[instr.rc()].as_u32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let s = (a[2*i] as u64 * b[2*i] as u64) + + (a[2*i+1] as u64 * b[2*i+1] as u64) + + c[i] as u64; + let (v, overflow) = if s > u32::MAX as u64 { (u32::MAX, true) } else { (s as u32, false) }; + r[i] = v; sat |= overflow; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word multiply-sum, saturating.** Per word lane: + ``` + VD[i] = clamp(VC[i] + uint16(VA[2*i]) * uint16(VB[2*i]) + + uint16(VA[2*i+1]) * uint16(VB[2*i+1]), 0, UINT32_MAX) + ``` + Two unsigned-half Γ— unsigned-half products plus an unsigned-word accumulator, clamped to `uint32`. +- **Wide-then-clamp ordering.** Xenia accumulates into `u64` first and clamps the *final* sum to `u32` ([`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs)) β€” matches the IBM spec. +- **`VSCR[SAT]` is sticky-set** if any lane clamps. Only the upper bound `0xFFFF_FFFF` ever triggers; unsigned overflow on the low side is impossible. +- **Big-endian half lanes.** Lane 0 is the most-significant half. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Per-pixel summed-area calculations with overflow detection; high-precision unsigned-half FIR convolution. + +## Related Instructions + +- [`vmsumuhm`](vmsumuhm.md) β€” same shape, modulo (no clamp, no SAT flag). +- [`vmsumshs`](vmsumshs.md) β€” signed half multiply-sum, saturating. +- [`vmsumubm`](vmsumubm.md), [`vmsummbm`](vmsummbm.md) β€” multiply-sum at byte width. +- [`vadduws`](vadduws.md) β€” unsigned saturating word add for further accumulation. +- [`mtvscr`](mtvscr.md) / [`mfvscr`](mfvscr.md) β€” read or clear `VSCR[SAT]`. + +## IBM Reference + +- [AIX 7.3 β€” `vmsumuhs` (Vector Multiply-Sum Unsigned Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmsumuhs-vector-multiply-sum-unsigned-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply-Sum Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmulesb.md b/tools/ppc-manual/vmx/vmulesb.md new file mode 100644 index 00000000..73016053 --- /dev/null +++ b/tools/ppc-manual/vmx/vmulesb.md @@ -0,0 +1,136 @@ +# `vmulesb` β€” Vector Multiply Even Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000308` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulesb` | `vmulesb` | β€” | Vector Multiply Even Signed Byte | + +## Syntax + +```asm +vmulesb [VD], [VA], [VB] +``` + +## Encoding + +### `vmulesb` β€” form `VX` + +- **Opcode word:** `0x10000308` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `776` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulesb: read | Source A vector register. | +| `VB` | vmulesb: read | Source B vector register. | +| `VD` | vmulesb: write | Destination vector register. | + +## Register Effects + +### `vmulesb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulesb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulesb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1086`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1086) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:108`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L108) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:501`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L501) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3469-3476`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3469-L3476) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulesb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = a[2 * i] as i16 * b[2 * i] as i16; } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Even-byte signed multiply, half-word result.** Per output half lane: + ``` + VD[i] = int16(int8(VA[2*i]) * int8(VB[2*i])) ; for i = 0..7 + ``` + Only the eight **even-indexed** byte lanes (lanes 0, 2, 4, …, 14 in big-endian) are read from each source. Each `int8 Γ— int8` product is widened to `int16`, producing eight half-word results that fill all of `VD`. +- **No saturation, no `VSCR[SAT]`.** The full 16-bit product of two signed bytes always fits in `int16` (range `-127*-128 = +16256 .. +127*+127 = +16129` is well within `Β±32767`), so no clipping is needed β€” even at the bit-pattern extremes `(-128) * (-128) = +16384` is representable. +- **Pairs with [`vmulosb`](vmulosb.md)** (odd-byte sibling). Together they consume all 16 bytes; two instructions are needed for a "multiply every lane" 16Γ—16-bit byte multiply. +- **Big-endian byte indexing.** Even byte indices `0, 2, 4, …, 14` correspond to the high-order halves of each half-word slot. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Signed-coefficient byte multiply for image filters; first half of a 16-byte signed multiply when paired with `vmulosb`. + +## Related Instructions + +- [`vmulosb`](vmulosb.md) β€” odd-byte sibling (lanes 1, 3, …, 15). +- [`vmuleub`](vmuleub.md), [`vmuloub`](vmuloub.md) β€” same split, unsigned. +- [`vmulesh`](vmulesh.md), [`vmulosh`](vmulosh.md) β€” same family at half width (β†’ word results). +- [`vmladduhm`](vmladduhm.md) β€” per-lane modulo multiply-add (low half only). +- [`vpkshus`](vpkshus.md) β€” saturating pack down from `int16` halves to `uint8` bytes (combine with `vmule*` for "scale + clamp" pipelines). + +## IBM Reference + +- [AIX 7.3 β€” `vmulesb` (Vector Multiply Even Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmulesb-vector-multiply-even-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmulesh.md b/tools/ppc-manual/vmx/vmulesh.md new file mode 100644 index 00000000..9e00d6d7 --- /dev/null +++ b/tools/ppc-manual/vmx/vmulesh.md @@ -0,0 +1,136 @@ +# `vmulesh` β€” Vector Multiply Even Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000348` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulesh` | `vmulesh` | β€” | Vector Multiply Even Signed Half Word | + +## Syntax + +```asm +vmulesh [VD], [VA], [VB] +``` + +## Encoding + +### `vmulesh` β€” form `VX` + +- **Opcode word:** `0x10000348` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `840` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulesh: read | Source A vector register. | +| `VB` | vmulesh: read | Source B vector register. | +| `VD` | vmulesh: write | Destination vector register. | + +## Register Effects + +### `vmulesh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulesh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulesh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1091`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1091) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:108`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L108) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:508`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L508) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3501-3508`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3501-L3508) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulesh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = a[2 * i] as i32 * b[2 * i] as i32; } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Even-half signed multiply, word result.** Per output word lane: + ``` + VD[i] = int32(int16(VA[2*i]) * int16(VB[2*i])) ; for i = 0..3 + ``` + Only the four **even-indexed** half lanes (lanes 0, 2, 4, 6 in big-endian) are read. Each `int16 Γ— int16` product is widened to `int32`, producing four word results. +- **No saturation, no `VSCR[SAT]`.** The full 32-bit product of two signed `int16` always fits β€” even `(-32768) * (-32768) = +1_073_741_824` is well within `INT32_MAX`. +- **Pairs with [`vmulosh`](vmulosh.md)** (odd-half sibling). Two instructions to cover all eight half lanes. +- **Big-endian half indexing.** Even half indices `0, 2, 4, 6` correspond to the high-order words of each word slot. +- **No XER, no exceptions.** +- **Aliasing legal.** +- **No VMX128 sibling.** +- **Common usage.** Q15 Γ— Q15 dot products with full 32-bit precision; signed-half-coefficient FIR taps; first half of a "multiply every half lane" sequence when paired with `vmulosh`. + +## Related Instructions + +- [`vmulosh`](vmulosh.md) β€” odd-half sibling (lanes 1, 3, 5, 7). +- [`vmuleuh`](vmuleuh.md), [`vmulouh`](vmulouh.md) β€” same split, unsigned. +- [`vmulesb`](vmulesb.md), [`vmulosb`](vmulosb.md) β€” same family at byte width (β†’ half-word results). +- [`vmsumshm`](vmsumshm.md), [`vmsumshs`](vmsumshs.md) β€” signed half multiply-sum across pairs (different shape). +- [`vmladduhm`](vmladduhm.md) β€” per-lane modulo multiply-add (low half only). + +## IBM Reference + +- [AIX 7.3 β€” `vmulesh` (Vector Multiply Even Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmulesh-vector-multiply-even-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Multiply Family](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmuleub.md b/tools/ppc-manual/vmx/vmuleub.md new file mode 100644 index 00000000..5e06b8b6 --- /dev/null +++ b/tools/ppc-manual/vmx/vmuleub.md @@ -0,0 +1,131 @@ +# `vmuleub` β€” Vector Multiply Even Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000208` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmuleub` | `vmuleub` | β€” | Vector Multiply Even Unsigned Byte | + +## Syntax + +```asm +vmuleub [VD], [VA], [VB] +``` + +## Encoding + +### `vmuleub` β€” form `VX` + +- **Opcode word:** `0x10000208` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `520` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmuleub: read | Source A vector register. | +| `VB` | vmuleub: read | Source B vector register. | +| `VD` | vmuleub: write | Destination vector register. | + +## Register Effects + +### `vmuleub` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmuleub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmuleub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1096`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1096) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:108`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L108) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:478`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L478) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3453-3460`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3453-L3460) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmuleub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[2 * i] as u16 * b[2 * i] as u16; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Even-lane multiply.** Only the *even-indexed* bytes of `VA` and `VB` participate β€” lanes 0, 2, 4, 6, 8, 10, 12, 14 (big-endian indexing, MSB-first). Each unsigned-byte Γ— unsigned-byte product widens to an unsigned 16-bit half-word and is written to the corresponding half-word of `VD`. The odd lanes are ignored. +- **Lane-count reduction.** Input has 16 byte lanes; output has 8 half-word lanes. The pairing is `VD.h[i] = VA.b[2*i] * VB.b[2*i]` for `i ∈ 0..7`. +- **No overflow possible.** 8-bit Γ— 8-bit unsigned ≀ `0xFF * 0xFF = 0xFE01`, which fits in 16 bits. `VSCR[SAT]` is **not** touched; this is a modulo-equivalent op even though no modulo is needed. +- **Pair with [`vmuloub`](vmuloub.md) to get all 16 products.** Software that wants every byte Γ— byte product typically issues `vmuleub` + `vmuloub` and then interleaves the two half-word vectors (`vmrghh`/`vmrglh`) or sums them (`vmsumubm`). +- **No `Rc`, no XER, no FPSCR.** VMX multiply never touches CR, CA, OV, or VSCR. +- **No VMX128 sibling.** Xbox 360 code that needs this pattern typically goes through [`vmsumubm`](vmsumubm.md) instead. + +## Related Instructions + +- [`vmuloub`](vmuloub.md) β€” odd-lane twin (bytes 1, 3, …, 15). +- [`vmulesb`](vmulesb.md), [`vmulosb`](vmulosb.md) β€” signed-byte even/odd multiplies. +- [`vmuleuh`](vmuleuh.md), [`vmulouh`](vmulouh.md) β€” unsigned-half-word even/odd multiplies (β†’ word lanes). +- [`vmulesh`](vmulesh.md), [`vmulosh`](vmulosh.md) β€” signed-half-word even/odd. +- [`vmsumubm`](vmsumubm.md) β€” fused multiply-sum unsigned-byte-modulo; often replaces the even/odd pair when the caller only needs the sum. +- [`vmrghh`](vmrghh.md), [`vmrglh`](vmrglh.md) β€” interleave the even/odd half-word results. + +## IBM Reference + +- [AIX 7.3 β€” `vmuleub` (Vector Multiply Even Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmuleub-vector-multiply-even-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmuleuh.md b/tools/ppc-manual/vmx/vmuleuh.md new file mode 100644 index 00000000..917fd613 --- /dev/null +++ b/tools/ppc-manual/vmx/vmuleuh.md @@ -0,0 +1,130 @@ +# `vmuleuh` β€” Vector Multiply Even Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000248` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmuleuh` | `vmuleuh` | β€” | Vector Multiply Even Unsigned Half Word | + +## Syntax + +```asm +vmuleuh [VD], [VA], [VB] +``` + +## Encoding + +### `vmuleuh` β€” form `VX` + +- **Opcode word:** `0x10000248` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `584` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmuleuh: read | Source A vector register. | +| `VB` | vmuleuh: read | Source B vector register. | +| `VD` | vmuleuh: write | Destination vector register. | + +## Register Effects + +### `vmuleuh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmuleuh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmuleuh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1101`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1101) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:108`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L108) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:485`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L485) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3485-3492`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3485-L3492) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmuleuh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[2 * i] as u32 * b[2 * i] as u32; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Even-lane half-word multiply.** Only half-word lanes 0, 2, 4, 6 of `VA` and `VB` participate (big-endian indexing). Each 16Γ—16 unsigned product widens to an unsigned 32-bit word and is written to the corresponding word lane of `VD`. The odd half-words are ignored. +- **Lane-count reduction.** 8 half-word input lanes β†’ 4 word output lanes. Pairing is `VD.w[i] = VA.h[2*i] * VB.h[2*i]` for `i ∈ 0..3`. +- **No overflow possible.** `0xFFFF * 0xFFFF = 0xFFFE0001` β€” fits in 32 bits. `VSCR[SAT]` is untouched. +- **Pair with [`vmulouh`](vmulouh.md)** to multiply every half-word lane. Interleave the two vectors with `vmrghw`/`vmrglw` (word-granularity) to rebuild the full element order, or feed both into [`vmsumuhm`](vmsumuhm.md) variants. +- **No `Rc`, no XER, no FPSCR.** +- **No VMX128 sibling.** Xenon code that needs 16-bit lane multiplies usually goes through [`vmsumuhm`](vmsumuhm.md) / [`vmsumuhs`](vmsumuhs.md). + +## Related Instructions + +- [`vmulouh`](vmulouh.md) β€” odd-half-word twin. +- [`vmulesh`](vmulesh.md), [`vmulosh`](vmulosh.md) β€” signed-half-word even/odd. +- [`vmuleub`](vmuleub.md), [`vmuloub`](vmuloub.md) β€” byte-granularity even/odd (β†’ half-word lanes). +- [`vmsumuhm`](vmsumuhm.md), [`vmsumuhs`](vmsumuhs.md) β€” fused multiply-sum unsigned-half-word (modulo / saturating). +- [`vmrghw`](vmrghw.md), [`vmrglw`](vmrglw.md) β€” interleave results. + +## IBM Reference + +- [AIX 7.3 β€” `vmuleuh` (Vector Multiply Even Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmuleuh-vector-multiply-even-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmulosb.md b/tools/ppc-manual/vmx/vmulosb.md new file mode 100644 index 00000000..ac908fdc --- /dev/null +++ b/tools/ppc-manual/vmx/vmulosb.md @@ -0,0 +1,130 @@ +# `vmulosb` β€” Vector Multiply Odd Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000108` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulosb` | `vmulosb` | β€” | Vector Multiply Odd Signed Byte | + +## Syntax + +```asm +vmulosb [VD], [VA], [VB] +``` + +## Encoding + +### `vmulosb` β€” form `VX` + +- **Opcode word:** `0x10000108` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `264` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulosb: read | Source A vector register. | +| `VB` | vmulosb: read | Source B vector register. | +| `VD` | vmulosb: write | Destination vector register. | + +## Register Effects + +### `vmulosb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulosb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulosb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1106`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1106) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:109`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L109) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:456`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L456) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3477-3484`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3477-L3484) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulosb => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = a[2 * i + 1] as i16 * b[2 * i + 1] as i16; } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Odd-lane signed-byte multiply.** Only the odd-indexed byte lanes (1, 3, 5, 7, 9, 11, 13, 15 β€” big-endian numbering) of `VA` and `VB` participate. Each pair is treated as signed 8-bit, multiplied, and sign-extended to a signed 16-bit result in the corresponding half-word of `VD`. Pairing: `VD.h[i] = (int8)VA.b[2*i+1] * (int8)VB.b[2*i+1]` for `i ∈ 0..7`. +- **Lane-count reduction.** 16 byte lanes β†’ 8 half-word lanes. +- **No overflow.** `(-128) * (-128) = 0x4000`, `(127) * (127) = 0x3F01` β€” both fit in int16. `VSCR[SAT]` is **not** set. +- **Pair with [`vmulesb`](vmulesb.md)** to get every signed byte Γ— byte product; interleave via `vmrghh`/`vmrglh`, or feed into [`vmsummbm`](vmsummbm.md) for a multiply-accumulate. +- **Signed vs. unsigned distinction.** The `s` in `vmulosb` makes the product arithmetic: negative operands sign-extend. Compare with [`vmuloub`](vmuloub.md) which zero-extends. +- **No `Rc`, no XER, no VSCR side-effect.** No VMX128 sibling. + +## Related Instructions + +- [`vmulesb`](vmulesb.md) β€” even-lane signed byte multiply. +- [`vmuloub`](vmuloub.md), [`vmuleub`](vmuleub.md) β€” unsigned byte twins. +- [`vmulosh`](vmulosh.md), [`vmulesh`](vmulesh.md) β€” signed half-word even/odd. +- [`vmsummbm`](vmsummbm.md) β€” fused signed-byte multiply-sum modulo. +- [`vmrghh`](vmrghh.md), [`vmrglh`](vmrglh.md) β€” interleave the even/odd half-word results. + +## IBM Reference + +- [AIX 7.3 β€” `vmulosb` (Vector Multiply Odd Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmulosb-vector-multiply-odd-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmulosh.md b/tools/ppc-manual/vmx/vmulosh.md new file mode 100644 index 00000000..164977cb --- /dev/null +++ b/tools/ppc-manual/vmx/vmulosh.md @@ -0,0 +1,130 @@ +# `vmulosh` β€” Vector Multiply Odd Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000148` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulosh` | `vmulosh` | β€” | Vector Multiply Odd Signed Half Word | + +## Syntax + +```asm +vmulosh [VD], [VA], [VB] +``` + +## Encoding + +### `vmulosh` β€” form `VX` + +- **Opcode word:** `0x10000148` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `328` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulosh: read | Source A vector register. | +| `VB` | vmulosh: read | Source B vector register. | +| `VD` | vmulosh: write | Destination vector register. | + +## Register Effects + +### `vmulosh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulosh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulosh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1111`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1111) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:109`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L109) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:462`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L462) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3509-3516`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3509-L3516) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulosh => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = a[2 * i + 1] as i32 * b[2 * i + 1] as i32; } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Odd-lane signed half-word multiply.** Only half-word lanes 1, 3, 5, 7 of `VA` and `VB` (big-endian numbering) participate. Each pair is treated as signed 16-bit, multiplied, and sign-extended to a signed 32-bit word in `VD`. Pairing: `VD.w[i] = (int16)VA.h[2*i+1] * (int16)VB.h[2*i+1]` for `i ∈ 0..3`. +- **Lane-count reduction.** 8 half-word lanes β†’ 4 word lanes. +- **No overflow.** `(-32768)*(-32768) = 0x40000000` β€” fits in int32. `VSCR[SAT]` is untouched. +- **Pair with [`vmulesh`](vmulesh.md)** for all eight products, then interleave with `vmrghw`/`vmrglw`. Feed into [`vmsumshm`](vmsumshm.md)/[`vmsumshs`](vmsumshs.md) for accumulation. +- **Signed arithmetic.** Negative inputs sign-extend before multiplication; contrast with [`vmulouh`](vmulouh.md). +- **No `Rc`, no XER.** No VMX128 sibling. + +## Related Instructions + +- [`vmulesh`](vmulesh.md) β€” even-lane signed half-word multiply. +- [`vmulouh`](vmulouh.md), [`vmuleuh`](vmuleuh.md) β€” unsigned half-word twins. +- [`vmulosb`](vmulosb.md), [`vmulesb`](vmulesb.md) β€” signed byte even/odd. +- [`vmhaddshs`](vmhaddshs.md), [`vmhraddshs`](vmhraddshs.md) β€” fused half-word fixed-point MAC variants. +- [`vmsumshm`](vmsumshm.md), [`vmsumshs`](vmsumshs.md) β€” signed multiply-sum modulo / saturating. + +## IBM Reference + +- [AIX 7.3 β€” `vmulosh` (Vector Multiply Odd Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmulosh-vector-multiply-odd-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmuloub.md b/tools/ppc-manual/vmx/vmuloub.md new file mode 100644 index 00000000..8b3b527a --- /dev/null +++ b/tools/ppc-manual/vmx/vmuloub.md @@ -0,0 +1,130 @@ +# `vmuloub` β€” Vector Multiply Odd Unsigned Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000008` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmuloub` | `vmuloub` | β€” | Vector Multiply Odd Unsigned Byte | + +## Syntax + +```asm +vmuloub [VD], [VA], [VB] +``` + +## Encoding + +### `vmuloub` β€” form `VX` + +- **Opcode word:** `0x10000008` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `8` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmuloub: read | Source A vector register. | +| `VB` | vmuloub: read | Source B vector register. | +| `VD` | vmuloub: write | Destination vector register. | + +## Register Effects + +### `vmuloub` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmuloub`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmuloub"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1116`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1116) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:109`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L109) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:437`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L437) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3461-3468`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3461-L3468) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmuloub => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[2 * i + 1] as u16 * b[2 * i + 1] as u16; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Odd-lane unsigned byte multiply.** Only the odd-indexed byte lanes (1, 3, 5, 7, 9, 11, 13, 15 β€” big-endian) of `VA` and `VB` participate. Each 8Γ—8 unsigned product widens to an unsigned 16-bit half-word in the corresponding half-word of `VD`. Pairing: `VD.h[i] = VA.b[2*i+1] * VB.b[2*i+1]` for `i ∈ 0..7`. +- **Lane-count reduction.** 16 byte lanes β†’ 8 half-word lanes. +- **No overflow.** `0xFF * 0xFF = 0xFE01` fits in 16 bits. `VSCR[SAT]` is not touched. +- **Pair with [`vmuleub`](vmuleub.md)** to get every byte product; re-interleave with `vmrghh`/`vmrglh`, or use [`vmsumubm`](vmsumubm.md) for a fused multiply-accumulate. +- **Unsigned arithmetic.** No sign-extension; negatives don't exist for `b` lanes in this op. Contrast with [`vmulosb`](vmulosb.md). +- **No `Rc`, no XER.** No VMX128 sibling. + +## Related Instructions + +- [`vmuleub`](vmuleub.md) β€” even-lane unsigned byte twin. +- [`vmulosb`](vmulosb.md), [`vmulesb`](vmulesb.md) β€” signed byte even/odd. +- [`vmulouh`](vmulouh.md), [`vmuleuh`](vmuleuh.md) β€” unsigned half-word even/odd. +- [`vmsumubm`](vmsumubm.md) β€” fused unsigned-byte multiply-sum. +- [`vmrghh`](vmrghh.md), [`vmrglh`](vmrglh.md) β€” interleave even/odd half-word products. + +## IBM Reference + +- [AIX 7.3 β€” `vmuloub` (Vector Multiply Odd Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmuloub-vector-multiply-odd-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vmulouh.md b/tools/ppc-manual/vmx/vmulouh.md new file mode 100644 index 00000000..590154fe --- /dev/null +++ b/tools/ppc-manual/vmx/vmulouh.md @@ -0,0 +1,130 @@ +# `vmulouh` β€” Vector Multiply Odd Unsigned Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000048` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulouh` | `vmulouh` | β€” | Vector Multiply Odd Unsigned Half Word | + +## Syntax + +```asm +vmulouh [VD], [VA], [VB] +``` + +## Encoding + +### `vmulouh` β€” form `VX` + +- **Opcode word:** `0x10000048` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `72` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulouh: read | Source A vector register. | +| `VB` | vmulouh: read | Source B vector register. | +| `VD` | vmulouh: write | Destination vector register. | + +## Register Effects + +### `vmulouh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulouh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulouh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1121`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1121) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:109`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L109) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:444`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L444) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3493-3500`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3493-L3500) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulouh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[2 * i + 1] as u32 * b[2 * i + 1] as u32; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Odd-lane unsigned half-word multiply.** Only half-word lanes 1, 3, 5, 7 (big-endian) of `VA` and `VB` participate. Each 16Γ—16 unsigned product widens to a 32-bit word in `VD`. Pairing: `VD.w[i] = VA.h[2*i+1] * VB.h[2*i+1]` for `i ∈ 0..3`. +- **Lane-count reduction.** 8 half-word lanes β†’ 4 word lanes. +- **No overflow.** `0xFFFF * 0xFFFF = 0xFFFE0001` fits in uint32. `VSCR[SAT]` is untouched. +- **Pair with [`vmuleuh`](vmuleuh.md)** to multiply every half-word; interleave via `vmrghw`/`vmrglw`, or feed into [`vmsumuhm`](vmsumuhm.md)/[`vmsumuhs`](vmsumuhs.md). +- **Unsigned arithmetic.** Zero-extension; contrast with [`vmulosh`](vmulosh.md). +- **No `Rc`, no XER.** No VMX128 sibling. + +## Related Instructions + +- [`vmuleuh`](vmuleuh.md) β€” even-lane unsigned half-word twin. +- [`vmulosh`](vmulosh.md), [`vmulesh`](vmulesh.md) β€” signed half-word even/odd. +- [`vmuloub`](vmuloub.md), [`vmuleub`](vmuleub.md) β€” byte-granularity even/odd. +- [`vmsumuhm`](vmsumuhm.md), [`vmsumuhs`](vmsumuhs.md) β€” fused unsigned multiply-sum modulo / saturating. +- [`vmrghw`](vmrghw.md), [`vmrglw`](vmrglw.md) β€” interleave word results. + +## IBM Reference + +- [AIX 7.3 β€” `vmulouh` (Vector Multiply Odd Unsigned Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vmulouh-vector-multiply-odd-unsigned-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vnmsubfp.md b/tools/ppc-manual/vmx/vnmsubfp.md new file mode 100644 index 00000000..34791175 --- /dev/null +++ b/tools/ppc-manual/vmx/vnmsubfp.md @@ -0,0 +1,194 @@ +# `vnmsubfp` β€” Vector Negative Multiply-Subtract Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x1000002f` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vnmsubfp` | `vnmsubfp` | β€” | Vector Negative Multiply-Subtract Floating Point | +| `vnmsubfp128` | `vnmsubfp128` | β€” | Vector128 Negative Multiply-Subtract Floating Point | + +## Syntax + +```asm +vnmsubfp [VD], [VA], [VC], [VB] +vnmsubfp128 [VD], [VA], [VD], [VB] +``` + +## Encoding + +### `vnmsubfp` β€” form `VA` + +- **Opcode word:** `0x1000002f` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `47` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +### `vnmsubfp128` β€” form `VX128` + +- **Opcode word:** `0x14000150` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `336` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vnmsubfp: read; vnmsubfp128: read | Source A vector register. | +| `VC` | vnmsubfp: read | Source C vector register / 3-bit selector. | +| `VB` | vnmsubfp: read; vnmsubfp128: read | Source B vector register. | +| `VD` | vnmsubfp: write; vnmsubfp128: read; vnmsubfp128: write | Destination vector register. | + +## Register Effects + +### `vnmsubfp` + +- **Reads (always):** `VA`, `VC`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vnmsubfp128` + +- **Reads (always):** `VA`, `VD`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +for each 32-bit float lane i in 0..3: + VD[i] <- βˆ’((VA[i] * VC[i]) βˆ’ VB[i]) +``` + +## 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 + +**`vnmsubfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vnmsubfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1154`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1154) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:110`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L110) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:589`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L589) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2074-2089`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2074-L2089) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vnmsubfp => { + // vD = -(vA * vC - vB) = vB - vA * vC. Same denorm-flush rule as vmaddfp. + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let c = ctx.vr[instr.rc()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + let ci = vmx::flush_denorm(c[i]); + // PPCBUG-426: single FMA rounding instead of two-step (b - a*c). + r[i] = vmx::flush_denorm(-ai.mul_add(ci, -bi)); + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vnmsubfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vnmsubfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1157`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1157) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:110`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L110) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:615`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L615) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2090-2107`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2090-L2107) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vnmsubfp128 => { + // VMX128 form: vD <- -((vA * vB) - vD) = vD - (vA * vB). Canary + // routes through `InstrEmit_vnmsubfp_` with the same arg-swap, + // which flushes all inputs unconditionally. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let d = ctx.vr[instr.vd128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + let di = vmx::flush_denorm(d[i]); + // PPCBUG-427: single FMA rounding. + r[i] = vmx::flush_denorm(-ai.mul_add(bi, -di)); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Lane-wise negative multiply-subtract.** Each of the four lanes computes `VD[i] = βˆ’((VA[i] Γ— VC[i]) βˆ’ VB[i])`, i.e. `VB[i] βˆ’ VA[i] Γ— VC[i]`. The multiply and the subsequent add are **not** a single fused rounding step in xenia β€” they're a multiply, a subtract, then a negate β€” but the PowerPC ISA specifies the sequence to behave *as if* it were fused (single IEEE-754 rounding). Hardware Xenon indeed rounds only once. +- **IEEE-754 binary32 lanes.** Follows `VSCR[NJ]`: denormal inputs/outputs flush to zero when `NJ = 1`. +- **No VSCR[SAT] update.** VMX float ops never set saturation. +- **No FPSCR effect.** Unlike scalar `fnmsub[s]`, `vnmsubfp` does not touch FPSCR. +- **NaN propagation.** A NaN in any of `VA`, `VB`, or `VC` yields a NaN in the corresponding lane. Sign-of-NaN is unspecified but stable in xenia (matches the x86 host's `vfnmadd`-family output). +- **Big-endian lane indexing.** Lane 0 is the MSB-most 4 bytes. +- **VMX128 sibling: [`vnmsubfp128`](vnmsubfp128.md).** Identical operation with access to `v0..v127`. +- **No `Rc` bit** on this opcode; it never touches CR. + +## Related Instructions + +- [`vmaddfp`](vmaddfp.md) β€” the positive-rounded fused MAC `(VA Γ— VC) + VB`. +- [`vaddfp`](vaddfp.md), [`vsubfp`](vsubfp.md) β€” the underlying adds/subs. +- [`vmulfp`](vmulfp.md) β€” xenia-convenience lane-wise float multiply (no native Altivec form; usually encoded as `vmaddfp VD, VA, VC, v0_zero`). +- [`vrefp`](vrefp.md), [`vrsqrtefp`](vrsqrtefp.md) β€” Newton iterations that pair with `vnmsubfp`. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” the other float-arithmetic primitives. + +## IBM Reference + +- [AIX 7.3 β€” `vnmsubfp` (Vector Negative Multiply-Subtract Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vnmsubfp-vector-negative-multiply-subtract-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vnor.md b/tools/ppc-manual/vmx/vnor.md new file mode 100644 index 00000000..a4fbbcf7 --- /dev/null +++ b/tools/ppc-manual/vmx/vnor.md @@ -0,0 +1,182 @@ +# `vnor` β€” Vector Logical NOR + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000504` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vnor` | `vnor` | β€” | Vector Logical NOR | +| `vnor128` | `vnor128` | β€” | Vector128 Logical NOR | + +## Syntax + +```asm +vnor [VD], [VA], [VB] +vnor128 [VD], [VA], [VB] +``` + +## Encoding + +### `vnor` β€” form `VX` + +- **Opcode word:** `0x10000504` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1284` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vnor128` β€” form `VX128` + +- **Opcode word:** `0x14000290` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `656` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vnor: read; vnor128: read | Source A vector register. | +| `VB` | vnor: read; vnor128: read | Source B vector register. | +| `VD` | vnor: write; vnor128: write | Destination vector register. | + +## Register Effects + +### `vnor` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vnor128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vnor`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vnor"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1168`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1168) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:110`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L110) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:534`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L534) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2244-2252`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2244-L2252) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vnor | PpcOpcode::vnor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = !(a[i] | b[i]); } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vnor128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vnor128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1171`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1171) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:110`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L110) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:623`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L623) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2244-2252`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2244-L2252) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vnor | PpcOpcode::vnor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = !(a[i] | b[i]); } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bitwise NOR across the full 128-bit register.** `VD = ~(VA | VB)`. The operation is lane-agnostic; PPC documents it per-bit, xenia implements it as four 32-bit lanes for convenience but the result is identical to 16-byte or 8-half-word decomposition. +- **`vnor VD, VA, VA` is the idiomatic `vnot`** (bitwise complement of `VA`). No dedicated `vnot` exists in base Altivec. +- **Aliasing is legal.** `vnor v3, v3, v4` or `vnor v3, v3, v3` are well-defined and common. +- **No flags.** No CR, XER, VSCR side-effect. +- **VMX128 sibling [`vnor128`](vnor128.md)** provides the same op with access to `v0..v127`; xenia shares the interpreter arm (`vmx_reg_triple` selects the right encoding helper). +- **Useful for mask inversion.** When a compare result needs to be inverted β€” e.g. "where not equal" β€” `vnor` of the compare result with itself is cheaper than a dedicated inversion. + +## Related Instructions + +- [`vand`](vand.md) β€” bitwise AND. +- [`vandc`](vandc.md) β€” `VA & ~VB` (useful as a fused inversion on the B side). +- [`vor`](vor.md), [`vxor`](vxor.md) β€” complete the boolean-primitive set. +- [`vsel`](vsel.md) β€” three-input bit-select; often fed by the inverse of a compare mask. +- [`vcmpequb`](vcmpequb.md) and related compares β€” produce the masks `vnor` is typically applied to. + +## IBM Reference + +- [AIX 7.3 β€” `vnor` (Vector Logical NOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vnor-vector-logical-nor-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 3 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vor.md b/tools/ppc-manual/vmx/vor.md new file mode 100644 index 00000000..479c4a3f --- /dev/null +++ b/tools/ppc-manual/vmx/vor.md @@ -0,0 +1,181 @@ +# `vor` β€” Vector Logical OR + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000484` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vor` | `vor` | β€” | Vector Logical OR | +| `vor128` | `vor128` | β€” | Vector128 Logical OR | + +## Syntax + +```asm +vor [VD], [VA], [VB] +vor128 [VD], [VA], [VB] +``` + +## Encoding + +### `vor` β€” form `VX` + +- **Opcode word:** `0x10000484` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1156` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vor128` β€” form `VX128` + +- **Opcode word:** `0x140002d0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `720` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vor: read; vor128: read | Source A vector register. | +| `VB` | vor: read; vor128: read | Source B vector register. | +| `VD` | vor: write; vor128: write | Destination vector register. | + +## Register Effects + +### `vor` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vor128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vor`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vor"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1186`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1186) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:111`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L111) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:531`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L531) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2226-2234`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2226-L2234) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vor | PpcOpcode::vor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] | b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vor128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vor128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1189`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1189) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:111`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L111) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:625`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L625) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2226-2234`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2226-L2234) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vor | PpcOpcode::vor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] | b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bitwise OR across the full 128-bit register.** Lane-agnostic; xenia implements it as four 32-bit lanes but the result is identical at any granularity. +- **`vor VD, VA, VA` is the idiomatic register move.** No dedicated "vmr" exists in base Altivec; compilers recognise the `vor v3, v4, v4` pattern as a move and schedule accordingly. +- **Aliasing is legal.** `vor v3, v3, v4` merges the mask in `v4` into `v3`. +- **No flags, no VSCR effect.** +- **VMX128 sibling [`vor128`](vor128.md).** Same operation, wider register file. +- **Common pattern: ORing a compare mask with a data vector** to force specific lanes to all-ones without needing a select. + +## Related Instructions + +- [`vand`](vand.md), [`vandc`](vandc.md) β€” AND / AND-with-complement. +- [`vnor`](vnor.md) β€” NOR, includes the idiom for bitwise NOT. +- [`vxor`](vxor.md) β€” XOR; also a common "zero register" via `vxor vD, vD, vD`. +- [`vsel`](vsel.md) β€” three-operand bit-select, often combined with OR of masks. + +## IBM Reference + +- [AIX 7.3 β€” `vor` (Vector Logical OR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vor-vector-logical-or-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 3 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vperm.md b/tools/ppc-manual/vmx/vperm.md new file mode 100644 index 00000000..e13be409 --- /dev/null +++ b/tools/ppc-manual/vmx/vperm.md @@ -0,0 +1,216 @@ +# `vperm` β€” Vector Permute + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x1000002b` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vperm` | `vperm` | β€” | Vector Permute | +| `vperm128` | `vperm128` | β€” | Vector128 Permute | + +## Syntax + +```asm +vperm [VD], [VA], [VB], [VC] +vperm128 [VD], [VA], [VB], [VC] +``` + +## Encoding + +### `vperm` β€” form `VA` + +- **Opcode word:** `0x1000002b` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `43` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +### `vperm128` β€” form `VX128_2` + +- **Opcode word:** `0x14000000` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `0` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 23–25 | `VC` | source C 3-bit field | +| 26 | `VA128h` | source A middle bit | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vperm: read; vperm128: read | Source A vector register. | +| `VB` | vperm: read; vperm128: read | Source B vector register. | +| `VC` | vperm: read; vperm128: read | Source C vector register / 3-bit selector. | +| `VD` | vperm: write; vperm128: write | Destination vector register. | + +## Register Effects + +### `vperm` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vperm128` + +- **Reads (always):** `VA`, `VB`, `VC` +- **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 + +**`vperm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vperm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1199`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1199) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:112`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L112) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:586`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L586) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2278-2302`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2278-L2302) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vperm | PpcOpcode::vperm128 => { + let (va, vb, vd); + let vc; + if matches!(instr.opcode, PpcOpcode::vperm128) { + va = instr.va128(); + vb = instr.vb128(); + vd = instr.vd128(); + vc = instr.vc128_2(); + } else { + va = instr.ra(); + vb = instr.rb(); + vd = instr.rd(); + vc = instr.rc(); + } + let a_bytes = ctx.vr[va].as_bytes(); + let b_bytes = ctx.vr[vb].as_bytes(); + let c_bytes = ctx.vr[vc].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { + let idx = (c_bytes[i] & 0x1F) as usize; + r[i] = if idx < 16 { a_bytes[idx] } else { b_bytes[idx - 16] }; + } + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`vperm128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vperm128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1202`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1202) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:112`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L112) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:605`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L605) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2278-2302`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2278-L2302) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vperm | PpcOpcode::vperm128 => { + let (va, vb, vd); + let vc; + if matches!(instr.opcode, PpcOpcode::vperm128) { + va = instr.va128(); + vb = instr.vb128(); + vd = instr.vd128(); + vc = instr.vc128_2(); + } else { + va = instr.ra(); + vb = instr.rb(); + vd = instr.rd(); + vc = instr.rc(); + } + let a_bytes = ctx.vr[va].as_bytes(); + let b_bytes = ctx.vr[vb].as_bytes(); + let c_bytes = ctx.vr[vc].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { + let idx = (c_bytes[i] & 0x1F) as usize; + r[i] = if idx < 16 { a_bytes[idx] } else { b_bytes[idx - 16] }; + } + ctx.vr[vd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-byte selector drives a cross-vector permute.** Each byte of `VC` is a 5-bit selector (low 5 bits used, upper 3 bits ignored). Bit 3 of that 5-bit field (i.e. the "16 bit") chooses which source: 0 selects from `VA`, 1 selects from `VB`. The low 4 bits index a byte within the chosen 16-byte operand. +- **`vperm` is the universal "16-byte reshuffle" primitive.** It can express any byte-level permutation of 32 source bytes (`VA β€– VB`) down to 16 destination bytes, including duplicates and drops. +- **Big-endian byte indexing.** `VC.b[0]` controls `VD.b[0]` (the MSB byte). Selector value 0 picks `VA.b[0]`, value 15 picks `VA.b[15]`, value 16 picks `VB.b[0]`, value 31 picks `VB.b[15]`. +- **Upper 3 bits of each `VC` byte are ignored.** Only bits 3..7 (the low 5) are consulted, so values like 0x1F and 0x5F both mean "byte 15 of VB". Software can use those upper bits for its own tagging. +- **Pair with [`lvsl`](lvsl.md) / [`lvsr`](lvsr.md) for unaligned 16-byte loads.** `lvsl` produces the selector that shifts "left" by `EA & 0xF` bytes; feeding that into `vperm` with two aligned `lvx` results yields the unaligned 16-byte view. +- **Aliasing legal.** `VD` may equal `VA` or `VB`. +- **VMX128 sibling [`vperm128`](vperm128.md).** Same shape with the 7-bit register file. The VMX128 encoding carries `VC` in the 3-bit `VC` sub-field of the `VX128_2` form β€” which only lets `VC` select one of **8** specific registers, not 128. In xenia's decoder this is `vc128()`. +- **No flags, no VSCR side-effect.** + +## Related Instructions + +- [`vsldoi`](vsldoi.md) β€” static-shift-by-`SHB` form; when the shift is a compile-time constant this is cheaper than `lvsl`+`vperm`. +- [`lvsl`](lvsl.md), [`lvsr`](lvsr.md) β€” generate the permute mask from an effective address. +- [`vmrghb`](vmrghb.md), [`vmrglb`](vmrglb.md), [`vmrghh`](vmrghh.md), [`vmrglh`](vmrglh.md), [`vmrghw`](vmrghw.md), [`vmrglw`](vmrglw.md) β€” dedicated merges that are a subset of `vperm`. +- [`vspltb`](vspltb.md), [`vsplth`](vsplth.md), [`vspltw`](vspltw.md) β€” splat-from-lane, also expressible via `vperm` + a constant mask. +- [`vpkuhum`](vpkuhum.md) and other `vpk*` β€” narrower-lane packs whose pattern can also be encoded in `vperm`. + +## IBM Reference + +- [AIX 7.3 β€” `vperm` (Vector Permute)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vperm-vector-permute-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkpx.md b/tools/ppc-manual/vmx/vpkpx.md new file mode 100644 index 00000000..28eb4a06 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkpx.md @@ -0,0 +1,130 @@ +# `vpkpx` β€” Vector Pack Pixel + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000030e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkp` | `vpkpx` | β€” | Vector Pack Pixel | + +## Syntax + +```asm +vpkpx [VD], [VA], [VB] +``` + +## Encoding + +### `vpkpx` β€” form `VX` + +- **Opcode word:** `0x1000030e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `782` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkpx: read | Source A vector register. | +| `VB` | vpkpx: read | Source B vector register. | +| `VD` | vpkpx: write | Destination vector register. | + +## Register Effects + +### `vpkpx` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vpkpx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkpx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1810`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1810) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:113`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L113) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:504`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L504) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4123-4131`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4123-L4131) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkpx => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u16; 8]; + for i in 0..4 { r[i] = crate::vmx::pack_pixel_555(a[i]); } + for i in 0..4 { r[4 + i] = crate::vmx::pack_pixel_555(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Pack 4Γ—4 pixel words β†’ 8Γ—16-bit 1-5-5-5 pixels.** For each 32-bit word lane, three bit-fields are sampled and concatenated into a 16-bit `1.5.5.5` (A.R.G.B) format, losing precision but not saturating. +- **Bit-layout of each output half-word.** Bit 0 of the output = bit 7 of the source byte (alpha); the next 5 bits come from the red channel's top 5 bits (bits 8..12 of the source word); then 5 bits of green (bits 16..20); then 5 bits of blue (bits 24..28). Xenia's helper is `vmx::pack_pixel_555` (in `crates/xenia-cpu/src/vmx.rs`). +- **No saturation / no rounding.** The op truncates the lower bits of each channel; `VSCR[SAT]` is **not** affected. +- **Big-endian lane order.** `VA`'s 4 words produce the first 4 output half-words (`VD.h[0..3]`); `VB`'s 4 words fill `VD.h[4..7]`. +- **Paired with [`vupkhpx`](vupkhpx.md) / [`vupklpx`](vupklpx.md)** β€” these unpack a 1-5-5-5 pixel back into a word-lane `0x00RRGGBB`-like form for further arithmetic. +- **No `Rc`, no XER.** No VMX128 sibling β€” game code that needs 555-pixel packing on Xenon either uses the scalar path or runs into the `vpkd3d128` family for richer D3D formats. + +## Related Instructions + +- [`vupkhpx`](vupkhpx.md), [`vupklpx`](vupklpx.md) β€” the inverse unpacks. +- [`vpkuhum`](vpkuhum.md), [`vpkuhus`](vpkuhus.md), [`vpkuwum`](vpkuwum.md), [`vpkuwus`](vpkuwus.md) β€” lane-halving packs (unsigned modulo / saturating). +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md), [`vpkswss`](vpkswss.md), [`vpkswus`](vpkswus.md) β€” signed-input saturating packs. +- [`vpkd3d128`](../vmx128/vpkd3d128.md) β€” VMX128-exclusive D3D-format pack (richer pixel formats than 1-5-5-5). + +## IBM Reference + +- [AIX 7.3 β€” `vpkpx` (Vector Pack Pixel32)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkpx-vector-pack-pixel32-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkshss.md b/tools/ppc-manual/vmx/vpkshss.md new file mode 100644 index 00000000..b968c367 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkshss.md @@ -0,0 +1,193 @@ +# `vpkshss` β€” Vector Pack Signed Half Word Signed Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000018e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkshss` | `vpkshss` | β€” | Vector Pack Signed Half Word Signed Saturate | +| `vpkshss128` | `vpkshss128` | β€” | Vector128 Pack Signed Half Word Signed Saturate | + +## Syntax + +```asm +vpkshss [VD], [VA], [VB] +vpkshss128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkshss` β€” form `VX` + +- **Opcode word:** `0x1000018e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `398` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkshss128` β€” form `VX128` + +- **Opcode word:** `0x14000200` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `512` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkshss: read; vpkshss128: read | Source A vector register. | +| `VB` | vpkshss: read; vpkshss128: read | Source B vector register. | +| `VD` | vpkshss: write; vpkshss128: write | Destination vector register. | +| `VSCR` | vpkshss: write; vpkshss128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkshss` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkshss128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkshss`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkshss128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkshss`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkshss"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1845`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1845) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:113`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L113) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:471`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L471) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4070-4082`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4070-L4082) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkshss | PpcOpcode::vpkshss128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkshss128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i16x8(ctx.vr[ra]); + let b = crate::vmx::as_i16x8(ctx.vr[rb]); + let mut r = [0i8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_i8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_i8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ +**`vpkshss128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkshss128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1848`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1848) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:113`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L113) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:618`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L618) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4070-4082`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4070-L4082) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkshss | PpcOpcode::vpkshss128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkshss128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i16x8(ctx.vr[ra]); + let b = crate::vmx::as_i16x8(ctx.vr[rb]); + let mut r = [0i8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_i8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_i8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed half-word β†’ signed byte saturating pack.** Each of the 16 input half-word lanes (8 from `VA`, 8 from `VB`) is clamped to the `int8` range `[βˆ’128, +127]`. Values outside that range produce the nearest extreme and **set the sticky `VSCR[SAT]`** bit. +- **Lane-count doubling.** 8+8 = 16 half-word lanes β†’ 16 byte lanes in `VD`. +- **Big-endian ordering.** `VA`'s 8 half-words fill `VD.b[0..7]`; `VB`'s 8 fill `VD.b[8..15]`. +- **Signed vs. unsigned output.** `vpkshss` has signed input and signed output. Compare with [`vpkshus`](vpkshus.md), which keeps signed input but clamps to `uint8` (`[0, 255]`). +- **`VSCR[SAT]` is sticky.** Once set it remains set until an `mtvscr` clears it. Software that needs a per-block saturation signal must clear before the kernel and test after. +- **No `Rc`, no XER / FPSCR.** +- **VMX128 sibling [`vpkshss128`](vpkshss128.md).** Same semantics, wider register file. + +## Related Instructions + +- [`vpkshus`](vpkshus.md) β€” signed β†’ unsigned saturating (same half-word input). +- [`vpkuhus`](vpkuhus.md), [`vpkuhum`](vpkuhum.md) β€” unsigned half-word input, unsigned byte output (saturating / modulo). +- [`vpkswss`](vpkswss.md), [`vpkswus`](vpkswus.md) β€” the word β†’ half-word analogues. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md) β€” the inverse unpacks that sign-extend bytes back to half-words. +- [`vaddsbs`](vaddsbs.md), [`vsubsbs`](vsubsbs.md) β€” other sources of byte-saturating arithmetic. + +## IBM Reference + +- [AIX 7.3 β€” `vpkshss` (Vector Pack Signed Half Word Signed Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkshss-vector-pack-signed-half-word-signed-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkshus.md b/tools/ppc-manual/vmx/vpkshus.md new file mode 100644 index 00000000..64723f98 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkshus.md @@ -0,0 +1,192 @@ +# `vpkshus` β€” Vector Pack Signed Half Word Unsigned Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000010e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkshus` | `vpkshus` | β€” | Vector Pack Signed Half Word Unsigned Saturate | +| `vpkshus128` | `vpkshus128` | β€” | Vector128 Pack Signed Half Word Unsigned Saturate | + +## Syntax + +```asm +vpkshus [VD], [VA], [VB] +vpkshus128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkshus` β€” form `VX` + +- **Opcode word:** `0x1000010e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `270` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkshus128` β€” form `VX128` + +- **Opcode word:** `0x14000240` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `576` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkshus: read; vpkshus128: read | Source A vector register. | +| `VB` | vpkshus: read; vpkshus128: read | Source B vector register. | +| `VD` | vpkshus: write; vpkshus128: write | Destination vector register. | +| `VSCR` | vpkshus: write; vpkshus128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkshus` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkshus128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkshus`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkshus128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkshus`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkshus"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1953`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1953) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:113`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L113) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:459`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L459) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4057-4069`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4057-L4069) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkshus | PpcOpcode::vpkshus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkshus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i16x8(ctx.vr[ra]); + let b = crate::vmx::as_i16x8(ctx.vr[rb]); + let mut r = [0u8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_u8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_u8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`vpkshus128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkshus128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1956`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1956) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:113`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L113) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:620`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L620) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4057-4069`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4057-L4069) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkshus | PpcOpcode::vpkshus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkshus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i16x8(ctx.vr[ra]); + let b = crate::vmx::as_i16x8(ctx.vr[rb]); + let mut r = [0u8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_u8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_i16_to_u8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed half-word β†’ unsigned byte saturating pack.** Each of the 16 input half-word lanes (8 from `VA`, 8 from `VB`) is interpreted as `int16` and clamped to `[0, 255]`. Negative values β†’ 0; values above 255 β†’ 255. Clamping any lane sticky-sets `VSCR[SAT]`. +- **Lane-count doubling.** 16 half-word lanes β†’ 16 byte lanes, `VA` filling `VD.b[0..7]` and `VB` filling `VD.b[8..15]`. +- **Difference from [`vpkshss`](vpkshss.md).** Both take signed half-words; `shss` clamps to `int8`, `shus` clamps to `uint8`. Choose `shus` when the signed negative half is not physically meaningful (e.g. after subtracting a clamped-at-zero value). +- **`VSCR[SAT]` is sticky.** +- **No `Rc`, no XER / FPSCR.** +- **VMX128 sibling [`vpkshus128`](vpkshus128.md).** Same behaviour with wider register file. + +## Related Instructions + +- [`vpkshss`](vpkshss.md) β€” signed β†’ signed clamp. +- [`vpkuhus`](vpkuhus.md) β€” unsigned input β†’ unsigned byte. +- [`vpkuhum`](vpkuhum.md) β€” unsigned input, truncating (modulo) pack. +- [`vpkswus`](vpkswus.md) β€” the word β†’ half-word signedβ†’unsigned analogue. +- [`vupkhub`](vupkhub.md)-family unpacks (if present) β€” the inverse. + +## IBM Reference + +- [AIX 7.3 β€” `vpkshus` (Vector Pack Signed Half Word Unsigned Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkshus-vector-pack-signed-half-word-unsigned-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkswss.md b/tools/ppc-manual/vmx/vpkswss.md new file mode 100644 index 00000000..0ae74c31 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkswss.md @@ -0,0 +1,193 @@ +# `vpkswss` β€” Vector Pack Signed Word Signed Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100001ce` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkswss` | `vpkswss` | β€” | Vector Pack Signed Word Signed Saturate | +| `vpkswss128` | `vpkswss128` | β€” | Vector128 Pack Signed Word Signed Saturate | + +## Syntax + +```asm +vpkswss [VD], [VA], [VB] +vpkswss128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkswss` β€” form `VX` + +- **Opcode word:** `0x100001ce` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `462` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkswss128` β€” form `VX128` + +- **Opcode word:** `0x14000280` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `640` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkswss: read; vpkswss128: read | Source A vector register. | +| `VB` | vpkswss: read; vpkswss128: read | Source B vector register. | +| `VD` | vpkswss: write; vpkswss128: write | Destination vector register. | +| `VSCR` | vpkswss: write; vpkswss128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkswss` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkswss128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkswss`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkswss128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkswss`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkswss"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1867`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1867) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:114`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L114) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:474`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L474) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4109-4121`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4109-L4121) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkswss | PpcOpcode::vpkswss128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkswss128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i32x4(ctx.vr[ra]); + let b = crate::vmx::as_i32x4(ctx.vr[rb]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_i16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_i16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ +**`vpkswss128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkswss128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1870`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1870) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:114`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L114) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:622`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L622) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4109-4121`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4109-L4121) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkswss | PpcOpcode::vpkswss128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkswss128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i32x4(ctx.vr[ra]); + let b = crate::vmx::as_i32x4(ctx.vr[rb]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_i16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_i16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed word β†’ signed half-word saturating pack.** Each of the 8 input word lanes (4 from `VA`, 4 from `VB`) is clamped to `[βˆ’32768, +32767]`. Out-of-range clamping sticky-sets `VSCR[SAT]`. +- **Lane-count doubling.** 4+4 = 8 word lanes β†’ 8 half-word lanes. +- **Ordering.** `VA`'s four words produce `VD.h[0..3]`; `VB`'s produce `VD.h[4..7]`. +- **Signed vs. unsigned output.** `vpkswss` preserves sign; [`vpkswus`](vpkswus.md) clamps the same signed-word input to `uint16`. +- **`VSCR[SAT]` is sticky.** +- **No `Rc`, no XER / FPSCR.** +- **VMX128 sibling [`vpkswss128`](vpkswss128.md).** + +## Related Instructions + +- [`vpkswus`](vpkswus.md) β€” signed β†’ unsigned saturating. +- [`vpkuwus`](vpkuwus.md), [`vpkuwum`](vpkuwum.md) β€” unsigned word input. +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md) β€” half-word input β†’ byte output analogues. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” the signed-half-word β†’ word unpacks that reverse a `vpkswss` pair. +- [`vaddsws`](vaddsws.md), [`vsubsws`](vsubsws.md) β€” word-saturating arithmetic producers. + +## IBM Reference + +- [AIX 7.3 β€” `vpkswss` (Vector Pack Signed Word Signed Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkswss-vector-pack-signed-word-signed-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkswus.md b/tools/ppc-manual/vmx/vpkswus.md new file mode 100644 index 00000000..e2863afc --- /dev/null +++ b/tools/ppc-manual/vmx/vpkswus.md @@ -0,0 +1,192 @@ +# `vpkswus` β€” Vector Pack Signed Word Unsigned Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000014e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkswus` | `vpkswus` | β€” | Vector Pack Signed Word Unsigned Saturate | +| `vpkswus128` | `vpkswus128` | β€” | Vector128 Pack Signed Word Unsigned Saturate | + +## Syntax + +```asm +vpkswus [VD], [VA], [VB] +vpkswus128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkswus` β€” form `VX` + +- **Opcode word:** `0x1000014e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `334` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkswus128` β€” form `VX128` + +- **Opcode word:** `0x140002c0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `704` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkswus: read; vpkswus128: read | Source A vector register. | +| `VB` | vpkswus: read; vpkswus128: read | Source B vector register. | +| `VD` | vpkswus: write; vpkswus128: write | Destination vector register. | +| `VSCR` | vpkswus: write; vpkswus128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkswus` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkswus128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkswus`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkswus128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkswus`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkswus"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1889`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1889) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:114`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L114) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:465`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L465) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4096-4108`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4096-L4108) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkswus | PpcOpcode::vpkswus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkswus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i32x4(ctx.vr[ra]); + let b = crate::vmx::as_i32x4(ctx.vr[rb]); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_u16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_u16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ +**`vpkswus128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkswus128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1892`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1892) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:114`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L114) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:624`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L624) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4096-4108`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4096-L4108) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkswus | PpcOpcode::vpkswus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkswus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = crate::vmx::as_i32x4(ctx.vr[ra]); + let b = crate::vmx::as_i32x4(ctx.vr[rb]); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_u16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_i32_to_u16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed word β†’ unsigned half-word saturating pack.** Each of the 8 input word lanes is interpreted as `int32` and clamped to `[0, 65535]`. Negatives β†’ 0, values above 65535 β†’ 65535, sticky-setting `VSCR[SAT]`. +- **Lane-count doubling.** 8 word lanes β†’ 8 half-word lanes, ordered as `VA` then `VB`. +- **Choose over [`vpkswss`](vpkswss.md)** when negative results shouldn't survive β€” e.g. clamped colour or intensity values that happen to have arrived in `int32` form. +- **`VSCR[SAT]` is sticky.** +- **No `Rc`, no XER / FPSCR.** +- **VMX128 sibling [`vpkswus128`](vpkswus128.md).** + +## Related Instructions + +- [`vpkswss`](vpkswss.md) β€” signed β†’ signed clamp. +- [`vpkuwus`](vpkuwus.md) β€” unsigned word input β†’ unsigned half-word. +- [`vpkuwum`](vpkuwum.md) β€” modulo (truncate) pack. +- [`vpkshus`](vpkshus.md) β€” the half-word β†’ byte analogue. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” signed-half-word unpacks. + +## IBM Reference + +- [AIX 7.3 β€” `vpkswus` (Vector Pack Signed Word Unsigned Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkswus-vector-pack-signed-word-unsigned-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkuhum.md b/tools/ppc-manual/vmx/vpkuhum.md new file mode 100644 index 00000000..a6739799 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkuhum.md @@ -0,0 +1,188 @@ +# `vpkuhum` β€” Vector Pack Unsigned Half Word Unsigned Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000000e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkuhum` | `vpkuhum` | β€” | Vector Pack Unsigned Half Word Unsigned Modulo | +| `vpkuhum128` | `vpkuhum128` | β€” | Vector128 Pack Unsigned Half Word Unsigned Modulo | + +## Syntax + +```asm +vpkuhum [VD], [VA], [VB] +vpkuhum128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkuhum` β€” form `VX` + +- **Opcode word:** `0x1000000e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `14` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkuhum128` β€” form `VX128` + +- **Opcode word:** `0x14000300` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `768` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkuhum: read; vpkuhum128: read | Source A vector register. | +| `VB` | vpkuhum: read; vpkuhum128: read | Source B vector register. | +| `VD` | vpkuhum: write; vpkuhum128: write | Destination vector register. | + +## Register Effects + +### `vpkuhum` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vpkuhum128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vpkuhum`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuhum"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1909`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1909) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:115`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L115) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:440`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L440) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4019-4030`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4019-L4030) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuhum | PpcOpcode::vpkuhum128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuhum128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u16x8(); + let b = ctx.vr[rb].as_u16x8(); + let mut r = [0u8; 16]; + for i in 0..8 { r[i] = a[i] as u8; } + for i in 0..8 { r[8 + i] = b[i] as u8; } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`vpkuhum128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuhum128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1912`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1912) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:115`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L115) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:626`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L626) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4019-4030`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4019-L4030) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuhum | PpcOpcode::vpkuhum128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuhum128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u16x8(); + let b = ctx.vr[rb].as_u16x8(); + let mut r = [0u8; 16]; + for i in 0..8 { r[i] = a[i] as u8; } + for i in 0..8 { r[8 + i] = b[i] as u8; } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word β†’ byte modulo pack.** Each of the 16 input half-word lanes (8 from `VA`, 8 from `VB`) is truncated to its low 8 bits. No saturation; values above 255 wrap modulo 256. +- **`VSCR[SAT]` is never touched.** This is the `-m` (modulo) form. For saturation use [`vpkuhus`](vpkuhus.md). +- **Lane-count doubling.** 16 half-word lanes β†’ 16 byte lanes, `VA`'s 8 half-words into `VD.b[0..7]` and `VB`'s 8 into `VD.b[8..15]`. +- **Cheap "low-byte extract" primitive.** Often used to repack per-channel results after a half-word arithmetic step. Contrast with shifting + masking. +- **No `Rc`, no XER.** +- **VMX128 sibling [`vpkuhum128`](vpkuhum128.md).** + +## Related Instructions + +- [`vpkuhus`](vpkuhus.md) β€” the saturating sibling. +- [`vpkuwum`](vpkuwum.md) β€” word β†’ half-word modulo pack. +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md) β€” signed half-word packs. +- [`vupkhub`](vupkhub.md) / [`vupklub`](vupklub.md) (if present) β€” zero-extending byte β†’ half-word unpacks that reverse this op. +- [`vperm`](vperm.md) β€” general-purpose alternative when the packing pattern is irregular. + +## IBM Reference + +- [AIX 7.3 β€” `vpkuhum` (Vector Pack Unsigned Half Word Unsigned Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkuhum-vector-pack-unsigned-half-word-unsigned-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkuhus.md b/tools/ppc-manual/vmx/vpkuhus.md new file mode 100644 index 00000000..d70d8d6f --- /dev/null +++ b/tools/ppc-manual/vmx/vpkuhus.md @@ -0,0 +1,192 @@ +# `vpkuhus` β€” Vector Pack Unsigned Half Word Unsigned Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000008e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkuhus` | `vpkuhus` | β€” | Vector Pack Unsigned Half Word Unsigned Saturate | +| `vpkuhus128` | `vpkuhus128` | β€” | Vector128 Pack Unsigned Half Word Unsigned Saturate | + +## Syntax + +```asm +vpkuhus [VD], [VA], [VB] +vpkuhus128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkuhus` β€” form `VX` + +- **Opcode word:** `0x1000008e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `142` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkuhus128` β€” form `VX128` + +- **Opcode word:** `0x14000340` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `832` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkuhus: read; vpkuhus128: read | Source A vector register. | +| `VB` | vpkuhus: read; vpkuhus128: read | Source B vector register. | +| `VD` | vpkuhus: write; vpkuhus128: write | Destination vector register. | +| `VSCR` | vpkuhus: write; vpkuhus128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkuhus` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkuhus128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkuhus`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkuhus128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkuhus`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuhus"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1931`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1931) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:115`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L115) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:452`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L452) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4044-4056`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4044-L4056) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuhus | PpcOpcode::vpkuhus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuhus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u16x8(); + let b = ctx.vr[rb].as_u16x8(); + let mut r = [0u8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_u16_to_u8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_u16_to_u8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`vpkuhus128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuhus128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1934`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1934) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:115`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L115) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:628`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L628) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4044-4056`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4044-L4056) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuhus | PpcOpcode::vpkuhus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuhus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u16x8(); + let b = ctx.vr[rb].as_u16x8(); + let mut r = [0u8; 16]; let mut sat = false; + for i in 0..8 { let (v, s) = crate::vmx::sat_u16_to_u8(a[i]); r[i] = v; sat |= s; } + for i in 0..8 { let (v, s) = crate::vmx::sat_u16_to_u8(b[i]); r[8 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word β†’ unsigned byte saturating pack.** Each of the 16 input half-word lanes (interpreted as `uint16`) is clamped to `[0, 255]`. Values above 255 produce 255 and sticky-set `VSCR[SAT]`. +- **Lane-count doubling.** 16 half-word lanes β†’ 16 byte lanes; `VA` first, then `VB`. +- **Pair with [`vpkuhum`](vpkuhum.md)** when saturation is not desired (truncate the low byte instead). +- **`VSCR[SAT]` is sticky.** +- **No `Rc`, no XER.** +- **VMX128 sibling [`vpkuhus128`](vpkuhus128.md).** + +## Related Instructions + +- [`vpkuhum`](vpkuhum.md) β€” modulo counterpart. +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md) β€” signed-input half-word packs. +- [`vpkuwus`](vpkuwus.md), [`vpkuwum`](vpkuwum.md) β€” word β†’ half-word analogues. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md) β€” sign-extending byte β†’ half-word unpacks. +- [`vperm`](vperm.md) β€” programmable alternative for irregular packs. + +## IBM Reference + +- [AIX 7.3 β€” `vpkuhus` (Vector Pack Unsigned Half Word Unsigned Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkuhus-vector-pack-unsigned-half-word-unsigned-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkuwum.md b/tools/ppc-manual/vmx/vpkuwum.md new file mode 100644 index 00000000..68747b4e --- /dev/null +++ b/tools/ppc-manual/vmx/vpkuwum.md @@ -0,0 +1,188 @@ +# `vpkuwum` β€” Vector Pack Unsigned Word Unsigned Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000004e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkuwum` | `vpkuwum` | β€” | Vector Pack Unsigned Word Unsigned Modulo | +| `vpkuwum128` | `vpkuwum128` | β€” | Vector128 Pack Unsigned Word Unsigned Modulo | + +## Syntax + +```asm +vpkuwum [VD], [VA], [VB] +vpkuwum128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkuwum` β€” form `VX` + +- **Opcode word:** `0x1000004e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `78` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkuwum128` β€” form `VX128` + +- **Opcode word:** `0x14000380` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `896` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkuwum: read; vpkuwum128: read | Source A vector register. | +| `VB` | vpkuwum: read; vpkuwum128: read | Source B vector register. | +| `VD` | vpkuwum: write; vpkuwum128: write | Destination vector register. | + +## Register Effects + +### `vpkuwum` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vpkuwum128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vpkuwum`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuwum"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1973`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1973) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:116`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L116) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:447`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L447) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4031-4042`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4031-L4042) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuwum | PpcOpcode::vpkuwum128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuwum128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u32x4(); + let b = ctx.vr[rb].as_u32x4(); + let mut r = [0u16; 8]; + for i in 0..4 { r[i] = a[i] as u16; } + for i in 0..4 { r[4 + i] = b[i] as u16; } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ +**`vpkuwum128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuwum128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1976`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1976) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:116`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L116) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:630`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L630) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4031-4042`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4031-L4042) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuwum | PpcOpcode::vpkuwum128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuwum128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u32x4(); + let b = ctx.vr[rb].as_u32x4(); + let mut r = [0u16; 8]; + for i in 0..4 { r[i] = a[i] as u16; } + for i in 0..4 { r[4 + i] = b[i] as u16; } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned word β†’ half-word modulo pack.** Each of the 8 input word lanes is truncated to its low 16 bits. +- **Lane-count doubling.** 4+4 word lanes β†’ 8 half-word lanes; `VA`'s 4 words into `VD.h[0..3]`, `VB`'s into `VD.h[4..7]`. +- **`VSCR[SAT]` never touched** (modulo variant). Use [`vpkuwus`](vpkuwus.md) for saturation. +- **Cheap low-half extract.** Typical after a 32-bit lane accumulator is "narrowed" back down to 16-bit for storage. +- **No `Rc`, no XER.** +- **VMX128 sibling [`vpkuwum128`](vpkuwum128.md).** + +## Related Instructions + +- [`vpkuwus`](vpkuwus.md) β€” saturating counterpart. +- [`vpkuhum`](vpkuhum.md) β€” half-word β†’ byte modulo. +- [`vpkswss`](vpkswss.md), [`vpkswus`](vpkswus.md) β€” signed word inputs. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” signed-half-word β†’ word unpacks. +- [`vperm`](vperm.md) β€” alternative for irregular patterns. + +## IBM Reference + +- [AIX 7.3 β€” `vpkuwum` (Vector Pack Unsigned Word Unsigned Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkuwum-vector-pack-unsigned-word-unsigned-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vpkuwus.md b/tools/ppc-manual/vmx/vpkuwus.md new file mode 100644 index 00000000..beef6846 --- /dev/null +++ b/tools/ppc-manual/vmx/vpkuwus.md @@ -0,0 +1,192 @@ +# `vpkuwus` β€” Vector Pack Unsigned Word Unsigned Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100000ce` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkuwus` | `vpkuwus` | β€” | Vector Pack Unsigned Word Unsigned Saturate | +| `vpkuwus128` | `vpkuwus128` | β€” | Vector128 Pack Unsigned Word Unsigned Saturate | + +## Syntax + +```asm +vpkuwus [VD], [VA], [VB] +vpkuwus128 [VD], [VA], [VB] +``` + +## Encoding + +### `vpkuwus` β€” form `VX` + +- **Opcode word:** `0x100000ce` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `206` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vpkuwus128` β€” form `VX128` + +- **Opcode word:** `0x140003c0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `960` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vpkuwus: read; vpkuwus128: read | Source A vector register. | +| `VB` | vpkuwus: read; vpkuwus128: read | Source B vector register. | +| `VD` | vpkuwus: write; vpkuwus128: write | Destination vector register. | +| `VSCR` | vpkuwus: write; vpkuwus128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vpkuwus` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +### `vpkuwus128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vpkuwus`: **VSCR[SAT]** may be stickied on saturating vector operations. +- `vpkuwus128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vpkuwus`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuwus"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1995`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1995) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:116`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L116) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:453`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L453) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4083-4095`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4083-L4095) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuwus | PpcOpcode::vpkuwus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuwus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u32x4(); + let b = ctx.vr[rb].as_u32x4(); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_u32_to_u16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_u32_to_u16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ +**`vpkuwus128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkuwus128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1998`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1998) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:116`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L116) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:632`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L632) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4083-4095`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4083-L4095) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkuwus | PpcOpcode::vpkuwus128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vpkuwus128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = ctx.vr[ra].as_u32x4(); + let b = ctx.vr[rb].as_u32x4(); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..4 { let (v, s) = crate::vmx::sat_u32_to_u16(a[i]); r[i] = v; sat |= s; } + for i in 0..4 { let (v, s) = crate::vmx::sat_u32_to_u16(b[i]); r[4 + i] = v; sat |= s; } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[rd] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned word β†’ unsigned half-word saturating pack.** Each of the 8 input word lanes (interpreted as `uint32`) is clamped to `[0, 65535]`. Overflow sticky-sets `VSCR[SAT]`. +- **Lane-count doubling.** 8 word lanes β†’ 8 half-word lanes; `VA` then `VB`. +- **Pair with [`vpkuwum`](vpkuwum.md)** when a modulo wrap is required. +- **`VSCR[SAT]` is sticky.** +- **No `Rc`, no XER.** +- **VMX128 sibling [`vpkuwus128`](vpkuwus128.md).** + +## Related Instructions + +- [`vpkuwum`](vpkuwum.md) β€” modulo counterpart. +- [`vpkswus`](vpkswus.md), [`vpkswss`](vpkswss.md) β€” signed-word input. +- [`vpkuhus`](vpkuhus.md) β€” half-word β†’ byte saturating pack. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” inverse unpack (sign-extending). +- [`vperm`](vperm.md) β€” irregular pack. + +## IBM Reference + +- [AIX 7.3 β€” `vpkuwus` (Vector Pack Unsigned Word Unsigned Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vpkuwus-vector-pack-unsigned-word-unsigned-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrefp.md b/tools/ppc-manual/vmx/vrefp.md new file mode 100644 index 00000000..15f9fec0 --- /dev/null +++ b/tools/ppc-manual/vmx/vrefp.md @@ -0,0 +1,176 @@ +# `vrefp` β€” Vector Reciprocal Estimate Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000010a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrefp` | `vrefp` | β€” | Vector Reciprocal Estimate Floating Point | +| `vrefp128` | `vrefp128` | β€” | Vector128 Reciprocal Estimate Floating Point | + +## Syntax + +```asm +vrefp [VD], [VB] +vrefp128 [VD], [VB] +``` + +## Encoding + +### `vrefp` β€” form `VX` + +- **Opcode word:** `0x1000010a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `266` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrefp128` β€” form `VX128_3` + +- **Opcode word:** `0x18000630` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1584` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrefp: read; vrefp128: read | Source B vector register. | +| `VD` | vrefp: write; vrefp128: write | Destination vector register. | + +## Register Effects + +### `vrefp` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrefp128` + +- **Reads (always):** `VB` +- **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 + +**`vrefp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrefp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1227`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1227) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:117`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L117) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:457`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L457) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2153-2161`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2153-L2161) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrefp | PpcOpcode::vrefp128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrefp128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrefp128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = 1.0 / b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrefp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrefp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1230`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1230) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:117`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L117) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:664`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L664) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2153-2161`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2153-L2161) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrefp | PpcOpcode::vrefp128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrefp128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrefp128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = 1.0 / b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Lane-wise reciprocal *estimate*.** Each 32-bit float lane of `VB` is approximated by `1.0 / VB[i]`. The PowerPC spec permits an **estimate** accurate to about 1/4096 (β‰ˆ12 bits); xenia-rs produces the *exact* IEEE-754 reciprocal by dividing, trading accuracy for simplicity. Game code that cares about bit-reproducible behaviour should Newton-iterate with [`vnmsubfp`](vnmsubfp.md) regardless of which backend computes the seed. +- **Standard Newton iteration.** `x₁ = xβ‚€ * (2 βˆ’ VB * xβ‚€)`, expressible as `vnmsubfp x₁, xβ‚€, VB, 2.0f` followed by `vmaddfp x₁, xβ‚€, x₁, 0.0f` (or similar). One iteration roughly doubles the valid bit count. +- **IEEE-754 binary32 lanes; `VSCR[NJ]` honoured** (denormals flush to zero when `NJ = 1`). +- **No VSCR[SAT] update, no FPSCR update, no exception.** Division by zero yields ±∞; division of zero yields ±∞ too (same sign convention). +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrefp128`](vrefp128.md).** + +## Related Instructions + +- [`vrsqrtefp`](vrsqrtefp.md) β€” reciprocal *square root* estimate, used with the same Newton scheme. +- [`vmaddfp`](vmaddfp.md), [`vnmsubfp`](vnmsubfp.md) β€” the building blocks of the Newton iteration. +- [`vexptefp`](vexptefp.md), [`vlogefp`](vlogefp.md) β€” other "estimate"-style transcendentals. +- [`vaddfp`](vaddfp.md), [`vsubfp`](vsubfp.md) β€” the float add/sub. + +## IBM Reference + +- [AIX 7.3 β€” `vrefp` (Vector Reciprocal Estimate Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrefp-vector-reciprocal-estimate-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrfim.md b/tools/ppc-manual/vmx/vrfim.md new file mode 100644 index 00000000..47163e58 --- /dev/null +++ b/tools/ppc-manual/vmx/vrfim.md @@ -0,0 +1,177 @@ +# `vrfim` β€” Vector Round to Floating-Point Integer toward -Infinity + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100002ca` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrfim` | `vrfim` | β€” | Vector Round to Floating-Point Integer toward -Infinity | +| `vrfim128` | `vrfim128` | β€” | Vector128 Round to Floating-Point Integer toward -Infinity | + +## Syntax + +```asm +vrfim [VD], [VB] +vrfim128 [VD], [VB] +``` + +## Encoding + +### `vrfim` β€” form `VX` + +- **Opcode word:** `0x100002ca` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `714` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrfim128` β€” form `VX128_3` + +- **Opcode word:** `0x18000330` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `816` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrfim: read; vrfim128: read | Source B vector register. | +| `VD` | vrfim: write; vrfim128: write | Destination vector register. | + +## Register Effects + +### `vrfim` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrfim128` + +- **Reads (always):** `VB` +- **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 + +**`vrfim`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfim"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1240`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1240) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:496`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L496) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2493-2501`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2493-L2501) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfim | PpcOpcode::vrfim128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfim128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfim128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].floor(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrfim128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfim128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1243`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1243) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:660`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L660) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2493-2501`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2493-L2501) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfim | PpcOpcode::vrfim128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfim128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfim128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].floor(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Round toward minus-infinity (floor).** Each 32-bit float lane of `VB` is rounded down to the nearest integer value still representable as a float. `3.2 β†’ 3.0`, `βˆ’3.2 β†’ βˆ’4.0`. +- **IEEE-754 binary32 output; `VSCR[NJ]` honoured** (denormal flush-to-zero). +- **Integer-too-big lanes are a no-op:** values already β‰₯ 2Β²Β³ in magnitude are all-integer and unchanged. +- **NaN propagation.** NaN input β†’ NaN output. `±∞` β†’ `±∞`. +- **No VSCR[SAT], no FPSCR update.** No "inexact" trap flag; this is the VMX rounding variant that deliberately ignores FPSCR's rounding mode. +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrfim128`](vrfim128.md).** + +## Related Instructions + +- [`vrfin`](vrfin.md) β€” round to nearest (ties-to-even). +- [`vrfip`](vrfip.md) β€” round toward +∞ (ceiling). +- [`vrfiz`](vrfiz.md) β€” round toward zero (truncate). +- [`vctsxs`](vctsxs.md), [`vctuxs`](vctuxs.md) β€” float β†’ fixed-point integer conversion with explicit scale. + +## IBM Reference + +- [AIX 7.3 β€” `vrfim` (Vector Round to Floating-Point Integer toward Minus Infinity)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrfim-vector-round-floating-point-integer-toward-minus-infinity-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrfin.md b/tools/ppc-manual/vmx/vrfin.md new file mode 100644 index 00000000..a30db051 --- /dev/null +++ b/tools/ppc-manual/vmx/vrfin.md @@ -0,0 +1,181 @@ +# `vrfin` β€” Vector Round to Floating-Point Integer Nearest + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000020a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrfin` | `vrfin` | β€” | Vector Round to Floating-Point Integer Nearest | +| `vrfin128` | `vrfin128` | β€” | Vector128 Round to Floating-Point Integer Nearest | + +## Syntax + +```asm +vrfin [VD], [VB] +vrfin128 [VD], [VB] +``` + +## Encoding + +### `vrfin` β€” form `VX` + +- **Opcode word:** `0x1000020a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `522` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrfin128` β€” form `VX128_3` + +- **Opcode word:** `0x18000370` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `880` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrfin: read; vrfin128: read | Source B vector register. | +| `VD` | vrfin: write; vrfin128: write | Destination vector register. | + +## Register Effects + +### `vrfin` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrfin128` + +- **Reads (always):** `VB` +- **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 + +**`vrfin`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfin"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1253`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1253) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:479`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L479) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2473-2483`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2473-L2483) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfin | PpcOpcode::vrfin128 => { + // PPCBUG-432: ISA round-to-nearest-even, NOT Rust's `round()` + // (which is round-half-away-from-zero). + let vb = if matches!(instr.opcode, PpcOpcode::vrfin128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfin128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].round_ties_even(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrfin128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfin128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1256`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1256) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:661`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L661) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2473-2483`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2473-L2483) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfin | PpcOpcode::vrfin128 => { + // PPCBUG-432: ISA round-to-nearest-even, NOT Rust's `round()` + // (which is round-half-away-from-zero). + let vb = if matches!(instr.opcode, PpcOpcode::vrfin128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfin128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].round_ties_even(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Round to nearest integer.** Each 32-bit float lane of `VB` is rounded to the nearest representable integer value. Xenia-rs uses Rust's `f32::round`, which rounds half-away-from-zero; the hardware Xenon actually implements round-ties-to-even. This is a known small mismatch tracked in xenia. +- **IEEE-754 binary32 output; `VSCR[NJ]` honoured.** +- **Integer-too-big lanes are no-ops** (|x| β‰₯ 2Β²Β³). +- **NaN and ±∞** pass through unchanged. +- **No VSCR[SAT], no FPSCR update.** +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrfin128`](vrfin128.md).** + +## Related Instructions + +- [`vrfim`](vrfim.md) β€” round toward βˆ’βˆž. +- [`vrfip`](vrfip.md) β€” round toward +∞. +- [`vrfiz`](vrfiz.md) β€” round toward zero. +- [`vctsxs`](vctsxs.md), [`vctuxs`](vctuxs.md) β€” float β†’ fixed-point integer. + +## IBM Reference + +- [AIX 7.3 β€” `vrfin` (Vector Round to Floating-Point Integer to Nearest)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrfin-vector-round-floating-point-integer-nearest-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrfip.md b/tools/ppc-manual/vmx/vrfip.md new file mode 100644 index 00000000..3c358b88 --- /dev/null +++ b/tools/ppc-manual/vmx/vrfip.md @@ -0,0 +1,177 @@ +# `vrfip` β€” Vector Round to Floating-Point Integer toward +Infinity + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000028a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrfip` | `vrfip` | β€” | Vector Round to Floating-Point Integer toward +Infinity | +| `vrfip128` | `vrfip128` | β€” | Vector128 Round to Floating-Point Integer toward +Infinity | + +## Syntax + +```asm +vrfip [VD], [VB] +vrfip128 [VD], [VB] +``` + +## Encoding + +### `vrfip` β€” form `VX` + +- **Opcode word:** `0x1000028a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `650` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrfip128` β€” form `VX128_3` + +- **Opcode word:** `0x180003b0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `944` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrfip: read; vrfip128: read | Source B vector register. | +| `VD` | vrfip: write; vrfip128: write | Destination vector register. | + +## Register Effects + +### `vrfip` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrfip128` + +- **Reads (always):** `VB` +- **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 + +**`vrfip`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfip"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1266`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1266) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:492`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L492) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2484-2492`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2484-L2492) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfip | PpcOpcode::vrfip128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfip128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfip128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].ceil(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrfip128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfip128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1269`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1269) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:662`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L662) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2484-2492`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2484-L2492) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfip | PpcOpcode::vrfip128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfip128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfip128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].ceil(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Round toward plus-infinity (ceiling).** Each 32-bit float lane of `VB` is rounded up. `3.2 β†’ 4.0`, `βˆ’3.2 β†’ βˆ’3.0`. +- **IEEE-754 binary32 output; `VSCR[NJ]` honoured.** +- **Integer-too-big lanes are no-ops.** +- **NaN and ±∞** pass through. +- **No VSCR[SAT], no FPSCR update.** +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrfip128`](vrfip128.md).** + +## Related Instructions + +- [`vrfim`](vrfim.md) β€” round toward βˆ’βˆž (the symmetric partner). +- [`vrfin`](vrfin.md) β€” round to nearest. +- [`vrfiz`](vrfiz.md) β€” round toward zero. +- [`vctsxs`](vctsxs.md), [`vctuxs`](vctuxs.md) β€” float β†’ fixed-point integer. + +## IBM Reference + +- [AIX 7.3 β€” `vrfip` (Vector Round to Floating-Point Integer toward Plus Infinity)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrfip-vector-round-floating-point-integer-toward-plus-infinity-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrfiz.md b/tools/ppc-manual/vmx/vrfiz.md new file mode 100644 index 00000000..3e45e081 --- /dev/null +++ b/tools/ppc-manual/vmx/vrfiz.md @@ -0,0 +1,175 @@ +# `vrfiz` β€” Vector Round to Floating-Point Integer toward Zero + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000024a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrfiz` | `vrfiz` | β€” | Vector Round to Floating-Point Integer toward Zero | +| `vrfiz128` | `vrfiz128` | β€” | Vector128 Round to Floating-Point Integer toward Zero | + +## Syntax + +```asm +vrfiz [VD], [VB] +vrfiz128 [VD], [VB] +``` + +## Encoding + +### `vrfiz` β€” form `VX` + +- **Opcode word:** `0x1000024a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `586` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrfiz128` β€” form `VX128_3` + +- **Opcode word:** `0x180003f0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1008` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrfiz: read; vrfiz128: read | Source B vector register. | +| `VD` | vrfiz: write; vrfiz128: write | Destination vector register. | + +## Register Effects + +### `vrfiz` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrfiz128` + +- **Reads (always):** `VB` +- **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 + +**`vrfiz`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfiz"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1279`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1279) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:486`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L486) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2464-2472`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2464-L2472) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfiz | PpcOpcode::vrfiz128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfiz128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfiz128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].trunc(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrfiz128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrfiz128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1282`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1282) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:118`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L118) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:663`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L663) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2464-2472`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2464-L2472) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrfiz | PpcOpcode::vrfiz128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrfiz128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrfiz128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = b[i].trunc(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Round toward zero (truncate).** Each 32-bit float lane of `VB` has its fractional part dropped. `3.7 β†’ 3.0`, `βˆ’3.7 β†’ βˆ’3.0`. +- **IEEE-754 binary32 output; `VSCR[NJ]` honoured.** +- **Integer-too-big lanes are no-ops.** +- **NaN and ±∞** pass through. +- **No VSCR[SAT], no FPSCR update.** `vrfiz` is the VMX analogue of C's `truncf`. +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrfiz128`](vrfiz128.md).** + +## Related Instructions + +- [`vrfin`](vrfin.md), [`vrfim`](vrfim.md), [`vrfip`](vrfip.md) β€” the other three rounding modes. +- [`vctsxs`](vctsxs.md), [`vctuxs`](vctuxs.md) β€” float β†’ signed / unsigned fixed-point (these truncate too, and also apply a `UIMM` power-of-two scale). + +## IBM Reference + +- [AIX 7.3 β€” `vrfiz` (Vector Round to Floating-Point Integer toward Zero)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrfiz-vector-round-floating-point-integer-toward-zero-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrlb.md b/tools/ppc-manual/vmx/vrlb.md new file mode 100644 index 00000000..49e14402 --- /dev/null +++ b/tools/ppc-manual/vmx/vrlb.md @@ -0,0 +1,129 @@ +# `vrlb` β€” Vector Rotate Left Integer Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000004` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrlb` | `vrlb` | β€” | Vector Rotate Left Integer Byte | + +## Syntax + +```asm +vrlb [VD], [VA], [VB] +``` + +## Encoding + +### `vrlb` β€” form `VX` + +- **Opcode word:** `0x10000004` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `4` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vrlb: read | Source A vector register. | +| `VB` | vrlb: read | Source B vector register. | +| `VD` | vrlb: write | Destination vector register. | + +## Register Effects + +### `vrlb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vrlb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrlb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1286`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1286) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:119`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L119) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:436`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L436) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3876-3883`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3876-L3883) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrlb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i].rotate_left((b[i] & 7) as u32); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane left-rotate of bytes.** For each of the 16 byte lanes, `VD.b[i] = rotate_left(VA.b[i], VB.b[i] & 7)`. The low 3 bits of each shift-count byte are used; upper bits are ignored. +- **Shift counts are per-lane, not scalar.** Unlike most CPUs' vector rotate, Altivec's shift/rotate takes a whole vector as the shift-count. If you want a uniform rotate, splat first with [`vspltb`](vspltb.md). +- **Big-endian byte lanes.** `VA.b[0]` is the most significant byte. +- **No overflow, no sticky saturation.** Rotation is information-preserving. +- **No `Rc`, no XER, no VSCR side-effect.** +- **No VMX128 sibling.** Xenon software that needs per-byte rotation typically uses `vrlw` on pre-swizzled data. + +## Related Instructions + +- [`vrlh`](vrlh.md), [`vrlw`](vrlw.md) β€” half-word / word rotates (same "per-lane rotate count" pattern). +- [`vslb`](vslb.md), [`vsrb`](vsrb.md), [`vsrab`](vsrab.md) β€” byte shift-left / logical-right / arithmetic-right. +- [`vsl`](vsl.md), [`vsr`](vsr.md), [`vslo`](vslo.md), [`vsro`](vsro.md) β€” whole-register shifts. +- [`vspltb`](vspltb.md) β€” splat to build a uniform shift-count vector. + +## IBM Reference + +- [AIX 7.3 β€” `vrlb` (Vector Rotate Left Integer Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrlb-vector-rotate-left-integer-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrlh.md b/tools/ppc-manual/vmx/vrlh.md new file mode 100644 index 00000000..1688528f --- /dev/null +++ b/tools/ppc-manual/vmx/vrlh.md @@ -0,0 +1,129 @@ +# `vrlh` β€” Vector Rotate Left Integer Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000044` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrlh` | `vrlh` | β€” | Vector Rotate Left Integer Half Word | + +## Syntax + +```asm +vrlh [VD], [VA], [VB] +``` + +## Encoding + +### `vrlh` β€” form `VX` + +- **Opcode word:** `0x10000044` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `68` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vrlh: read | Source A vector register. | +| `VB` | vrlh: read | Source B vector register. | +| `VD` | vrlh: write | Destination vector register. | + +## Register Effects + +### `vrlh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vrlh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrlh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1294`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1294) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:119`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L119) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:443`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L443) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3908-3915`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3908-L3915) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrlh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i].rotate_left((b[i] & 0xF) as u32); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane left-rotate of half-words.** For each of the 8 half-word lanes, `VD.h[i] = rotate_left(VA.h[i], VB.h[i] & 0xF)`. Low 4 bits of each shift-count half-word are used. +- **Per-lane shift counts.** Splat first (`vsplth`) if a uniform rotate is needed. +- **Big-endian half-word lanes.** Lane 0 is the most significant pair of bytes. +- **No overflow, no saturation.** Rotation is information-preserving. +- **No `Rc`, no XER, no VSCR effect.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vrlb`](vrlb.md), [`vrlw`](vrlw.md) β€” byte / word rotate siblings. +- [`vslh`](vslh.md), [`vsrh`](vsrh.md), [`vsrah`](vsrah.md) β€” half-word shift-left / logical-right / arithmetic-right. +- [`vsl`](vsl.md), [`vsr`](vsr.md) β€” bit-level whole-register shifts. +- [`vsplth`](vsplth.md) β€” splat to build a uniform shift-count vector. + +## IBM Reference + +- [AIX 7.3 β€” `vrlh` (Vector Rotate Left Integer Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrlh-vector-rotate-left-integer-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrlw.md b/tools/ppc-manual/vmx/vrlw.md new file mode 100644 index 00000000..73b65e96 --- /dev/null +++ b/tools/ppc-manual/vmx/vrlw.md @@ -0,0 +1,189 @@ +# `vrlw` β€” Vector Rotate Left Integer Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000084` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrlw` | `vrlw` | β€” | Vector Rotate Left Integer Word | +| `vrlw128` | `vrlw128` | β€” | Vector128 Rotate Left Word | + +## Syntax + +```asm +vrlw [VD], [VA], [VB] +vrlw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vrlw` β€” form `VX` + +- **Opcode word:** `0x10000084` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `132` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrlw128` β€” form `VX128` + +- **Opcode word:** `0x18000050` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `80` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vrlw: read; vrlw128: read | Source A vector register. | +| `VB` | vrlw: read; vrlw128: read | Source B vector register. | +| `VD` | vrlw: write; vrlw128: write | Destination vector register. | + +## Register Effects + +### `vrlw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrlw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vrlw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrlw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1308`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1308) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:119`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L119) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:450`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L450) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2450-2461`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2450-L2461) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrlw | PpcOpcode::vrlw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i].rotate_left(sh); + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrlw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrlw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1311`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1311) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:119`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L119) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:692`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L692) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2450-2461`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2450-L2461) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrlw | PpcOpcode::vrlw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i].rotate_left(sh); + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane left-rotate of words.** For each of the 4 word lanes, `VD.w[i] = rotate_left(VA.w[i], VB.w[i] & 0x1F)`. Low 5 bits of each shift-count word are used. +- **Per-lane shift counts.** Splat with [`vspltw`](vspltw.md) or [`vspltisw`](vspltisw.md) for uniform rotation. +- **Big-endian word lanes.** Lane 0 is the most significant 4 bytes. +- **No overflow, no saturation.** +- **No `Rc`, no XER, no VSCR effect.** +- **VMX128 sibling [`vrlw128`](vrlw128.md)** β€” same op with the wider register file. +- **Building block for [`vrlimi128`](../vmx128/vrlimi128.md).** VMX128 fuses a rotate with an immediate-masked insert for cheaper bitfield shuffles; `vrlw` is the plain variant that the XDK uses for scalar-style 32-bit rotates. + +## Related Instructions + +- [`vrlb`](vrlb.md), [`vrlh`](vrlh.md) β€” byte / half-word rotate siblings. +- [`vslw`](vslw.md), [`vsrw`](vsrw.md), [`vsraw`](vsraw.md) β€” word shift-left / logical-right / arithmetic-right. +- [`vsl`](vsl.md), [`vsr`](vsr.md) β€” bit-level whole-register shifts. +- [`vspltw`](vspltw.md), [`vspltisw`](vspltisw.md) β€” splat sources for uniform shift counts. +- [`vrlimi128`](../vmx128/vrlimi128.md) β€” rotate + mask-insert (VMX128-exclusive). + +## IBM Reference + +- [AIX 7.3 β€” `vrlw` (Vector Rotate Left Integer Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrlw-vector-rotate-left-integer-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vrsqrtefp.md b/tools/ppc-manual/vmx/vrsqrtefp.md new file mode 100644 index 00000000..7a6173d4 --- /dev/null +++ b/tools/ppc-manual/vmx/vrsqrtefp.md @@ -0,0 +1,176 @@ +# `vrsqrtefp` β€” Vector Reciprocal Square Root Estimate Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000014a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrsqrtefp` | `vrsqrtefp` | β€” | Vector Reciprocal Square Root Estimate Floating Point | +| `vrsqrtefp128` | `vrsqrtefp128` | β€” | Vector128 Reciprocal Square Root Estimate Floating Point | + +## Syntax + +```asm +vrsqrtefp [VD], [VB] +vrsqrtefp128 [VD], [VB] +``` + +## Encoding + +### `vrsqrtefp` β€” form `VX` + +- **Opcode word:** `0x1000014a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `330` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vrsqrtefp128` β€” form `VX128_3` + +- **Opcode word:** `0x18000670` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1648` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrsqrtefp: read; vrsqrtefp128: read | Source B vector register. | +| `VD` | vrsqrtefp: write; vrsqrtefp128: write | Destination vector register. | + +## Register Effects + +### `vrsqrtefp` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vrsqrtefp128` + +- **Reads (always):** `VB` +- **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 + +**`vrsqrtefp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrsqrtefp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1371`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1371) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:120`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L120) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:463`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L463) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2162-2170`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2162-L2170) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrsqrtefp | PpcOpcode::vrsqrtefp128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrsqrtefp128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrsqrtefp128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = 1.0 / b[i].sqrt(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vrsqrtefp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrsqrtefp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1374`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1374) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:120`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L120) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:665`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L665) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2162-2170`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2162-L2170) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrsqrtefp | PpcOpcode::vrsqrtefp128 => { + let vb = if matches!(instr.opcode, PpcOpcode::vrsqrtefp128) { instr.vb128() } else { instr.rb() }; + let vd = if matches!(instr.opcode, PpcOpcode::vrsqrtefp128) { instr.vd128() } else { instr.rd() }; + let b = ctx.vr[vb].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = 1.0 / b[i].sqrt(); } + ctx.vr[vd] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Lane-wise reciprocal-square-root *estimate*.** Each 32-bit float lane of `VB` is approximated by `1.0 / sqrt(VB[i])`. The PowerPC spec permits a 12-bit estimate; xenia-rs computes the exact IEEE-754 result. Games that depend on Xenon's low-precision estimate may need a helper to truncate bits to match hardware. +- **Standard Newton iteration (Quake-style):** `x₁ = xβ‚€ * (1.5 βˆ’ 0.5 * VB * xβ‚€Β²)`. One pass produces ~24 bits of precision β€” essentially indistinguishable from a true `1/sqrt`. +- **Negative input is a trap** in math terms but not in ISA terms: the hardware returns a QNaN. `sqrt(βˆ’x)` for `x > 0` β†’ QNaN. Zero produces `+∞` (and may sticky-set no bits). +- **IEEE-754 binary32; `VSCR[NJ]` honoured.** +- **No VSCR[SAT], no FPSCR update, no exception.** +- **Big-endian lane indexing.** +- **VMX128 sibling [`vrsqrtefp128`](vrsqrtefp128.md).** + +## Related Instructions + +- [`vrefp`](vrefp.md) β€” plain reciprocal estimate. +- [`vmaddfp`](vmaddfp.md), [`vnmsubfp`](vnmsubfp.md) β€” the Newton iteration primitives. +- [`vexptefp`](vexptefp.md), [`vlogefp`](vlogefp.md) β€” the other transcendental estimates. + +## IBM Reference + +- [AIX 7.3 β€” `vrsqrtefp` (Vector Reciprocal Square Root Estimate Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vrsqrtefp-vector-reciprocal-square-root-estimate-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsel.md b/tools/ppc-manual/vmx/vsel.md new file mode 100644 index 00000000..01b23c70 --- /dev/null +++ b/tools/ppc-manual/vmx/vsel.md @@ -0,0 +1,210 @@ +# `vsel` β€” Vector Conditional Select + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x1000002a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsel` | `vsel` | β€” | Vector Conditional Select | +| `vsel128` | `vsel128` | β€” | Vector128 Conditional Select | + +## Syntax + +```asm +vsel [VD], [VA], [VB], [VC] +vsel128 [VD], [VA], [VB], [VD] +``` + +## Encoding + +### `vsel` β€” form `VA` + +- **Opcode word:** `0x1000002a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `42` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +### `vsel128` β€” form `VX128` + +- **Opcode word:** `0x14000350` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `848` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsel: read; vsel128: read | Source A vector register. | +| `VB` | vsel: read; vsel128: read | Source B vector register. | +| `VC` | vsel: read | Source C vector register / 3-bit selector. | +| `VD` | vsel: write; vsel128: read; vsel128: write | Destination vector register. | + +## Register Effects + +### `vsel` + +- **Reads (always):** `VA`, `VB`, `VC` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsel128` + +- **Reads (always):** `VA`, `VB`, `VD` +- **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 + +**`vsel`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsel"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1386`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1386) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:121`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L121) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:585`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L585) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2253-2275`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2253-L2275) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsel | PpcOpcode::vsel128 => { + // vD = (vA & ~vC) | (vB & vC) + let (va, vb, vd); + let vc; + if matches!(instr.opcode, PpcOpcode::vsel128) { + va = instr.va128(); + vb = instr.vb128(); + vd = instr.vd128(); + vc = vd; // for 128, vC is encoded in vD field + } else { + va = instr.ra(); + vb = instr.rb(); + vd = instr.rd(); + vc = instr.rc(); + } + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let c = ctx.vr[vc].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = (a[i] & !c[i]) | (b[i] & c[i]); } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vsel128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsel128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1389`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1389) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:121`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L121) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:629`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L629) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2253-2275`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2253-L2275) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsel | PpcOpcode::vsel128 => { + // vD = (vA & ~vC) | (vB & vC) + let (va, vb, vd); + let vc; + if matches!(instr.opcode, PpcOpcode::vsel128) { + va = instr.va128(); + vb = instr.vb128(); + vd = instr.vd128(); + vc = vd; // for 128, vC is encoded in vD field + } else { + va = instr.ra(); + vb = instr.rb(); + vd = instr.rd(); + vc = instr.rc(); + } + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let c = ctx.vr[vc].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = (a[i] & !c[i]) | (b[i] & c[i]); } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-bit select.** `VD = (VA & ~VC) | (VB & VC)`. Evaluated bit-by-bit across the full 128-bit register β€” not per-lane. Any granularity (byte / half / word) is valid because each bit is independent. +- **Classic "bitwise conditional move".** The canonical use is: compare produces an all-ones / all-zeros mask in `VC`, then `vsel` picks between two data vectors. Because the mask is all-or-nothing per lane, `vsel` behaves identically to a per-lane conditional move in that common case. +- **Mask does not need to be all-ones / all-zeros.** Partial masks produce interleaved bits, which is useful for bitfield merges. +- **`vsel128` read pattern is atypical:** the destination `VD` is **also an input**. The VMX128 encoding reuses the destination's 7 bits to carry one of the three source operands (xenia's interpreter arm handles this β€” see `vsel128` Register Effects above). Compilers express this as `vsel v3, v4, v5, v3` even though `v3` is also the destination. +- **No flags, no VSCR.** No dedicated VMX128 separate-control-register sibling; `vsel128` covers the VMX128 case. +- **Cheaper than `vand` + `vandc` + `vor`.** `vsel` is a single-cycle primitive on Xenon. + +## Related Instructions + +- [`vand`](vand.md), [`vandc`](vandc.md), [`vor`](vor.md), [`vnor`](vnor.md), [`vxor`](vxor.md) β€” the boolean primitives `vsel` replaces when composed. +- [`vcmpequb`](vcmpequb.md), [`vcmpequh`](vcmpequh.md), [`vcmpequw`](vcmpequw.md), [`vcmpgtsb`](vcmpgtsb.md) and relatives β€” the usual source of the select mask. +- [`vperm`](vperm.md) β€” byte-level permute; uses an index vector rather than a boolean mask. + +## IBM Reference + +- [AIX 7.3 β€” `vsel` (Vector Conditional Select)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsel-vector-conditional-select-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 3 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsl.md b/tools/ppc-manual/vmx/vsl.md new file mode 100644 index 00000000..db5ffe01 --- /dev/null +++ b/tools/ppc-manual/vmx/vsl.md @@ -0,0 +1,128 @@ +# `vsl` β€” Vector Shift Left + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100001c4` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsl` | `vsl` | β€” | Vector Shift Left | + +## Syntax + +```asm +vsl [VD], [VA], [VB] +``` + +## Encoding + +### `vsl` β€” form `VX` + +- **Opcode word:** `0x100001c4` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `452` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsl: read | Source A vector register. | +| `VB` | vsl: read | Source B vector register. | +| `VD` | vsl: write | Destination vector register. | + +## Register Effects + +### `vsl` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsl`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsl"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1402`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1402) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:472`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L472) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3920-3926`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3920-L3926) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsl => { + let a = u128::from_be_bytes(ctx.vr[instr.ra()].as_bytes()); + let shift = (ctx.vr[instr.rb()].as_bytes()[15] & 7) as u32; + let r = if shift == 0 { a } else { a << shift }; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Whole-register bit shift-left.** The 128-bit value `VA` is shifted left by `N` bits, where `N = VB.b[15] & 7` β€” i.e. the low 3 bits of the **last** (least-significant) byte of `VB`. Bits shifted out the top are discarded; zero-fill on the right. +- **Shift count constraint.** The ISA requires the same 3-bit shift count in all 16 bytes of `VB`; behaviour is undefined otherwise (xenia-rs reads only byte 15 as above). Compilers guarantee this by splatting the shift count first. +- **Combine with [`vslo`](vslo.md) for up to 127-bit shifts.** `vslo` handles the byte-granular component; `vsl` picks up the remaining 0..7 bits. The canonical 128-bit shift-left is `vslo` followed by `vsl`. +- **Big-endian.** "Left" means toward the MSB end of the register. +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vslo`](vslo.md) β€” whole-register shift-left by *octets* (bytes). +- [`vsr`](vsr.md), [`vsro`](vsro.md) β€” the right-shift counterparts. +- [`vsldoi`](vsldoi.md) β€” static-immediate byte shift of `VA β€– VB`. +- [`vslb`](vslb.md), [`vslh`](vslh.md), [`vslw`](vslw.md) β€” per-lane logical shifts. + +## IBM Reference + +- [AIX 7.3 β€” `vsl` (Vector Shift Left)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsl-vector-shift-left-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vslb.md b/tools/ppc-manual/vmx/vslb.md new file mode 100644 index 00000000..ee7c3343 --- /dev/null +++ b/tools/ppc-manual/vmx/vslb.md @@ -0,0 +1,131 @@ +# `vslb` β€” Vector Shift Left Integer Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000104` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vslb` | `vslb` | β€” | Vector Shift Left Integer Byte | + +## Syntax + +```asm +vslb [VD], [VA], [VB] +``` + +## Encoding + +### `vslb` β€” form `VX` + +- **Opcode word:** `0x10000104` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `260` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vslb: read | Source A vector register. | +| `VB` | vslb: read | Source B vector register. | +| `VD` | vslb: write | Destination vector register. | + +## Register Effects + +### `vslb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vslb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1413`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1413) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:455`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L455) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3852-3859`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3852-L3859) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i] << (b[i] & 7); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical shift-left of bytes.** `VD.b[i] = VA.b[i] << (VB.b[i] & 7)` for `i ∈ 0..15`. Only the low 3 bits of each shift-count byte are honoured. +- **Per-lane shift counts.** To shift every byte by the same amount, splat with [`vspltb`](vspltb.md) or use an immediate splat [`vspltisb`](vspltisb.md). +- **Zero-fill.** Bits shifted out the top are discarded; the right end is zero-filled. Contrast with the rotate [`vrlb`](vrlb.md) which wraps. +- **Big-endian byte indexing.** +- **No flags, no VSCR.** No overflow signal β€” bits are silently lost. +- **No VMX128 sibling.** Xenon software uses `vslw`-on-prepackaged-data or [`vrlimi128`](../vmx128/vrlimi128.md) for common cases. + +## Related Instructions + +- [`vsrb`](vsrb.md) β€” logical-right twin. +- [`vsrab`](vsrab.md) β€” arithmetic-right (sign-extending) byte shift. +- [`vrlb`](vrlb.md) β€” left-rotate byte. +- [`vslh`](vslh.md), [`vslw`](vslw.md) β€” half-word / word logical-left shifts. +- [`vsl`](vsl.md), [`vslo`](vslo.md) β€” bit- and octet-level whole-register shifts. +- [`vspltb`](vspltb.md), [`vspltisb`](vspltisb.md) β€” splat sources for uniform shift counts. + +## IBM Reference + +- [AIX 7.3 β€” `vslb` (Vector Shift Left Integer Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vslb-vector-shift-left-integer-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsldoi.md b/tools/ppc-manual/vmx/vsldoi.md new file mode 100644 index 00000000..43f45f57 --- /dev/null +++ b/tools/ppc-manual/vmx/vsldoi.md @@ -0,0 +1,190 @@ +# `vsldoi` β€” Vector Shift Left Double by Octet Immediate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VA](../forms/VA.md) Β· **Opcode:** `0x1000002c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsldoi` | `vsldoi` | β€” | Vector Shift Left Double by Octet Immediate | +| `vsldoi128` | `vsldoi128` | β€” | Vector128 Shift Left Double by Octet Immediate | + +## Syntax + +```asm +vsldoi [VD], [VA], [VB], [SHB] +vsldoi128 [VD], [VA], [VB], [SHB] +``` + +## Encoding + +### `vsldoi` β€” form `VA` + +- **Opcode word:** `0x1000002c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `44` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT` | destination vector register | +| 11–15 | `VRA` | source A | +| 16–20 | `VRB` | source B | +| 21–25 | `VRC` | source C / shift | +| 26–31 | `XO` | extended opcode (6 bits) | + +### `vsldoi128` β€” form `VX128_5` + +- **Opcode word:** `0x10000010` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `16` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22–25 | `SH` | 4-bit shift amount | +| 26 | `VA128h` | source A middle bit | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsldoi: read; vsldoi128: read | Source A vector register. | +| `VB` | vsldoi: read; vsldoi128: read | Source B vector register. | +| `SHB` | vsldoi: read; vsldoi128: read | Shift amount (byte granularity, `vsldoi`). | +| `VD` | vsldoi: write; vsldoi128: write | Destination vector register. | + +## Register Effects + +### `vsldoi` + +- **Reads (always):** `VA`, `VB`, `SHB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsldoi128` + +- **Reads (always):** `VA`, `VB`, `SHB` +- **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 + +**`vsldoi`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsldoi"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1477`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1477) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:587`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L587) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2303-2314`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2303-L2314) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsldoi => { + let a_bytes = ctx.vr[instr.ra()].as_bytes(); + let b_bytes = ctx.vr[instr.rb()].as_bytes(); + let sh = ((instr.raw >> 6) & 0xF) as usize; // SH field bits 6-9 + let mut concat = [0u8; 32]; + concat[..16].copy_from_slice(&a_bytes); + concat[16..].copy_from_slice(&b_bytes); + let mut r = [0u8; 16]; + r.copy_from_slice(&concat[sh..sh + 16]); + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ +**`vsldoi128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsldoi128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1480`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1480) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:595`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L595) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2315-2327`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2315-L2327) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsldoi128 => { + let a_bytes = ctx.vr[instr.va128()].as_bytes(); + let b_bytes = ctx.vr[instr.vb128()].as_bytes(); + let sh = instr.vx128_5_sh() as usize; + let mut concat = [0u8; 32]; + concat[..16].copy_from_slice(&a_bytes); + concat[16..].copy_from_slice(&b_bytes); + let mut r = [0u8; 16]; + let sh = sh.min(16); + r.copy_from_slice(&concat[sh..sh + 16]); + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Static byte-level shift of `VA β€– VB`.** The 4-bit `SHB` immediate names a byte offset into the 32-byte concatenation `VA β€– VB`. The destination `VD` is the 16-byte window starting at that offset. Equivalently: `VD = (VA << (8 * SHB)) | (VB >> (8 * (16 βˆ’ SHB)))`, treating the 32-byte concatenation as a single big-endian value. +- **`SHB = 0` is a register move** from `VA` to `VD`. `SHB = 16` is ill-formed; the field is 4 bits (0..15) so the range is `SHB ∈ 0..=15`. +- **Compile-time shift only.** Unlike `vperm` / `vslo` / `vsro`, the shift is an immediate. When the shift is known at compile time, `vsldoi` is strictly cheaper than an `lvsl` + `vperm` pair. +- **Unaligned-load idiom.** `vsldoi` is the static-offset counterpart to the dynamic `lvsl` + `vperm` pattern. When the misalignment is known, emit `vsldoi vD, vAL, vAH, SHB` after two aligned `lvx` loads. +- **Big-endian byte indexing.** Lane 0 is the MSB. +- **No flags, no VSCR.** +- **VMX128 sibling [`vsldoi128`](vsldoi128.md)** with the wider register file; same 4-bit `SHB` immediate. + +## Related Instructions + +- [`vslo`](vslo.md), [`vsro`](vsro.md) β€” byte-level (octet) shifts using a per-register count, dynamic. +- [`vsl`](vsl.md), [`vsr`](vsr.md) β€” bit-level whole-register shifts. +- [`vperm`](vperm.md) β€” general-purpose programmable byte permute. +- [`lvsl`](lvsl.md), [`lvsr`](lvsr.md) β€” dynamic permute-control generators. +- [`vmrghb`](vmrghb.md), [`vmrglb`](vmrglb.md) β€” byte-granularity merges. + +## IBM Reference + +- [AIX 7.3 β€” `vsldoi` (Vector Shift Left Double by Octet Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsldoi-vector-shift-left-double-by-octet-immediate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vslh.md b/tools/ppc-manual/vmx/vslh.md new file mode 100644 index 00000000..42a342c4 --- /dev/null +++ b/tools/ppc-manual/vmx/vslh.md @@ -0,0 +1,130 @@ +# `vslh` β€” Vector Shift Left Integer Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000144` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vslh` | `vslh` | β€” | Vector Shift Left Integer Half Word | + +## Syntax + +```asm +vslh [VD], [VA], [VB] +``` + +## Encoding + +### `vslh` β€” form `VX` + +- **Opcode word:** `0x10000144` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `324` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vslh: read | Source A vector register. | +| `VB` | vslh: read | Source B vector register. | +| `VD` | vslh: write | Destination vector register. | + +## Register Effects + +### `vslh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vslh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1419`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1419) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:461`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L461) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3884-3891`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3884-L3891) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i] << (b[i] & 0xF); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical shift-left of half-words.** `VD.h[i] = VA.h[i] << (VB.h[i] & 0xF)` for `i ∈ 0..7`. Low 4 bits of each shift-count half-word are honoured. +- **Per-lane shift counts.** Splat with [`vsplth`](vsplth.md) / [`vspltish`](vspltish.md) for uniform shifts. +- **Zero-fill on the right.** Bits lost off the top. No sign propagation β€” use [`vsrah`](vsrah.md) if you need arithmetic right shift. +- **Big-endian half-word indexing.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsrh`](vsrh.md) β€” logical-right half-word. +- [`vsrah`](vsrah.md) β€” arithmetic-right half-word. +- [`vrlh`](vrlh.md) β€” half-word rotate. +- [`vslb`](vslb.md), [`vslw`](vslw.md) β€” byte / word logical-left shifts. +- [`vsplth`](vsplth.md), [`vspltish`](vspltish.md) β€” splats for shift counts. + +## IBM Reference + +- [AIX 7.3 β€” `vslh` (Vector Shift Left Integer Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vslh-vector-shift-left-integer-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vslo.md b/tools/ppc-manual/vmx/vslo.md new file mode 100644 index 00000000..9cffe32b --- /dev/null +++ b/tools/ppc-manual/vmx/vslo.md @@ -0,0 +1,183 @@ +# `vslo` β€” Vector Shift Left by Octet + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000040c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vslo` | `vslo` | β€” | Vector Shift Left by Octet | +| `vslo128` | `vslo128` | β€” | Vector128 Shift Left Octet | + +## Syntax + +```asm +vslo [VD], [VA], [VB] +vslo128 [VD], [VA], [VB] +``` + +## Encoding + +### `vslo` β€” form `VX` + +- **Opcode word:** `0x1000040c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1036` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vslo128` β€” form `VX128` + +- **Opcode word:** `0x14000390` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `912` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vslo: read; vslo128: read | Source A vector register. | +| `VB` | vslo: read; vslo128: read | Source B vector register. | +| `VD` | vslo: write; vslo128: write | Destination vector register. | + +## Register Effects + +### `vslo` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vslo128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vslo`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslo"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1496`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1496) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:523`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L523) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3935-3944`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3935-L3944) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslo | PpcOpcode::vslo128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vslo128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = u128::from_be_bytes(ctx.vr[ra].as_bytes()); + let nbytes = ((ctx.vr[rb].as_bytes()[15] >> 3) & 0xF) as u32; + let r = if nbytes == 0 { a } else { a << (nbytes * 8) }; + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ +**`vslo128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslo128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1499`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1499) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:631`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L631) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3935-3944`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3935-L3944) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslo | PpcOpcode::vslo128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vslo128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = u128::from_be_bytes(ctx.vr[ra].as_bytes()); + let nbytes = ((ctx.vr[rb].as_bytes()[15] >> 3) & 0xF) as u32; + let r = if nbytes == 0 { a } else { a << (nbytes * 8) }; + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Whole-register shift-left by octets (bytes).** `VA` is shifted left by `N` bytes, where `N = (VB.b[15] >> 3) & 0xF` β€” bits 1..4 of the last byte of `VB`. Right end is zero-filled. `N` saturates at 15 because only 4 bits are honoured. +- **Shift count constraint.** The ISA mandates a uniform 4-bit count across all of `VB`; xenia-rs reads only byte 15. Splat with [`vspltb`](vspltb.md) before invoking when the count is derived dynamically. +- **Pair with [`vsl`](vsl.md) for full bit-level shifts.** `vslo` contributes the byte-granular part; `vsl` contributes the 0..7 residual bits. +- **Big-endian.** "Left" = toward MSB = toward `VD.b[0]`. +- **No flags, no VSCR.** +- **VMX128 sibling [`vslo128`](vslo128.md).** + +## Related Instructions + +- [`vsl`](vsl.md) β€” the bit-level whole-register shift-left. +- [`vsro`](vsro.md) β€” shift-right by octets. +- [`vsldoi`](vsldoi.md) β€” static-immediate variant. +- [`vslb`](vslb.md), [`vslh`](vslh.md), [`vslw`](vslw.md) β€” per-lane shifts. + +## IBM Reference + +- [AIX 7.3 β€” `vslo` (Vector Shift Left by Octet)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vslo-vector-shift-left-by-octet-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vslw.md b/tools/ppc-manual/vmx/vslw.md new file mode 100644 index 00000000..b9363167 --- /dev/null +++ b/tools/ppc-manual/vmx/vslw.md @@ -0,0 +1,188 @@ +# `vslw` β€” Vector Shift Left Integer Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000184` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vslw` | `vslw` | β€” | Vector Shift Left Integer Word | +| `vslw128` | `vslw128` | β€” | Vector128 Shift Left Integer Word | + +## Syntax + +```asm +vslw [VD], [VA], [VB] +vslw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vslw` β€” form `VX` + +- **Opcode word:** `0x10000184` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `388` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vslw128` β€” form `VX128` + +- **Opcode word:** `0x180000d0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `208` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vslw: read; vslw128: read | Source A vector register. | +| `VB` | vslw: read; vslw128: read | Source B vector register. | +| `VD` | vslw: write; vslw128: write | Destination vector register. | + +## Register Effects + +### `vslw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vslw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vslw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1433`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1433) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:468`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L468) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2414-2425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2414-L2425) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslw | PpcOpcode::vslw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i] << sh; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vslw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vslw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1436`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1436) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:122`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L122) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:693`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L693) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2414-2425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2414-L2425) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vslw | PpcOpcode::vslw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i] << sh; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical shift-left of words.** `VD.w[i] = VA.w[i] << (VB.w[i] & 0x1F)` for `i ∈ 0..3`. Low 5 bits of each shift-count word are honoured. +- **Per-lane shift counts.** Splat with [`vspltw`](vspltw.md) / [`vspltisw`](vspltisw.md) for uniform shifts. +- **Zero-fill right.** Arithmetic right shift is [`vsraw`](vsraw.md). +- **Big-endian word indexing.** +- **No flags, no VSCR.** +- **VMX128 sibling [`vslw128`](vslw128.md).** + +## Related Instructions + +- [`vsrw`](vsrw.md) β€” logical-right word. +- [`vsraw`](vsraw.md) β€” arithmetic-right word. +- [`vrlw`](vrlw.md) β€” word rotate. +- [`vslb`](vslb.md), [`vslh`](vslh.md) β€” byte / half-word logical-left. +- [`vspltw`](vspltw.md), [`vspltisw`](vspltisw.md) β€” splats for shift counts. + +## IBM Reference + +- [AIX 7.3 β€” `vslw` (Vector Shift Left Integer Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vslw-vector-shift-left-integer-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vspltb.md b/tools/ppc-manual/vmx/vspltb.md new file mode 100644 index 00000000..4ea9198c --- /dev/null +++ b/tools/ppc-manual/vmx/vspltb.md @@ -0,0 +1,127 @@ +# `vspltb` β€” Vector Splat Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000020c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vspltb` | `vspltb` | β€” | Vector Splat Byte | + +## Syntax + +```asm +vspltb [VD], [VB], [UIMM] +``` + +## Encoding + +### `vspltb` β€” form `VX` + +- **Opcode word:** `0x1000020c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `524` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vspltb: read | Source B vector register. | +| `UIMM` | vspltb: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vspltb: write | Destination vector register. | + +## Register Effects + +### `vspltb` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vspltb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1503`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1503) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:480`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L480) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2349-2355`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2349-L2355) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltb => { + let uimm = ((instr.raw >> 16) & 0xF) as usize; + let b = ctx.vr[instr.rb()].as_bytes(); + let val = b[uimm]; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes([val; 16]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat one byte across all 16 lanes.** The `UIMM` field (bits 11–15) selects which byte of `VB` to replicate β€” `UIMM = 0` picks the MSB lane, `UIMM = 15` picks the LSB. Only the low 4 bits are meaningful. +- **Big-endian index.** `UIMM = 0` β†’ `VB.b[0]`, the most significant byte. This matches the layout after a `stvx` / `lvx` round-trip. +- **Typical use: broadcast a comparison selector or a shift count** so that a per-lane op (e.g. [`vslb`](vslb.md)) behaves as a scalar-style shift. +- **No flags, no VSCR.** +- **No VMX128 sibling.** Xenon replaces this with [`vspltisb`](vspltisb.md) for immediate constants, or with [`vpermwi128`](../vmx128/vpermwi128.md) / [`vperm`](vperm.md) for more complex splats. + +## Related Instructions + +- [`vsplth`](vsplth.md), [`vspltw`](vspltw.md) β€” half-word / word splat siblings. +- [`vspltisb`](vspltisb.md), [`vspltish`](vspltish.md), [`vspltisw`](vspltisw.md) β€” immediate splats (no source register needed). +- [`vperm`](vperm.md) β€” programmable byte permute; a splat is the special case where `VC = {k, k, …, k}`. +- [`vpermwi128`](../vmx128/vpermwi128.md) β€” word-level 4-way permute via 8-bit immediate (VMX128-only). + +## IBM Reference + +- [AIX 7.3 β€” `vspltb` (Vector Splat Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vspltb-vector-splat-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsplth.md b/tools/ppc-manual/vmx/vsplth.md new file mode 100644 index 00000000..857422f3 --- /dev/null +++ b/tools/ppc-manual/vmx/vsplth.md @@ -0,0 +1,127 @@ +# `vsplth` β€” Vector Splat Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000024c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsplth` | `vsplth` | β€” | Vector Splat Half Word | + +## Syntax + +```asm +vsplth [VD], [VB], [UIMM] +``` + +## Encoding + +### `vsplth` β€” form `VX` + +- **Opcode word:** `0x1000024c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `588` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vsplth: read | Source B vector register. | +| `UIMM` | vsplth: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vsplth: write | Destination vector register. | + +## Register Effects + +### `vsplth` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vsplth`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsplth"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1513`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1513) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:487`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L487) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2342-2348`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2342-L2348) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsplth => { + let uimm = ((instr.raw >> 16) & 0x7) as usize; + let b = ctx.vr[instr.rb()].as_u16x8(); + let val = b[uimm]; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array([val; 8]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat one half-word across all 8 lanes.** `UIMM` (bits 11–15, low 3 bits honoured) selects which of `VB`'s 8 half-word lanes is replicated. +- **Big-endian index.** `UIMM = 0` β†’ `VB.h[0]`, the most significant half-word. +- **Typical use: broadcast a 16-bit shift count or comparison operand.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vspltb`](vspltb.md), [`vspltw`](vspltw.md) β€” byte / word splat siblings. +- [`vspltish`](vspltish.md) β€” immediate-operand splat (no source register). +- [`vperm`](vperm.md) β€” programmable permute; a half-word splat maps to a per-byte selector of `{2k, 2k+1, 2k, 2k+1, …}`. +- [`vpermwi128`](../vmx128/vpermwi128.md) β€” word-level permute (VMX128-only). + +## IBM Reference + +- [AIX 7.3 β€” `vsplth` (Vector Splat Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsplth-vector-splat-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vspltisb.md b/tools/ppc-manual/vmx/vspltisb.md new file mode 100644 index 00000000..8b2134bd --- /dev/null +++ b/tools/ppc-manual/vmx/vspltisb.md @@ -0,0 +1,125 @@ +# `vspltisb` β€” Vector Splat Immediate Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000030c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vspltisb` | `vspltisb` | β€” | Vector Splat Immediate Signed Byte | + +## Syntax + +```asm +vspltisb [VD], [SIMM] +``` + +## Encoding + +### `vspltisb` β€” form `VX` + +- **Opcode word:** `0x1000030c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `780` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `SIMM` | vspltisb: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `VD` | vspltisb: write | Destination vector register. | + +## Register Effects + +### `vspltisb` + +- **Reads (always):** `SIMM` +- **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 + +**`vspltisb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltisb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1536`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1536) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:503`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L503) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2364-2369`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2364-L2369) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltisb => { + let simm = ((instr.raw >> 16) & 0x1F) as i8; + let simm = if simm & 0x10 != 0 { simm | !0x1F } else { simm }; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes([simm as u8; 16]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat a 5-bit signed immediate across all 16 byte lanes.** The `SIMM` field (bits 11–15) is sign-extended from 5 bits to 8 β€” so the representable range is `[βˆ’16, +15]`. Values `0x10..0x1F` decode as negative (`βˆ’16..βˆ’1`). +- **Constant-generation primitive.** `vspltisb vD, 0` is the canonical "all-bytes-zero" vector (same net effect as `vxor vD, vD, vD`). `vspltisb vD, -1` is the all-ones mask. `vspltisb vD, 1` broadcasts `{1, 1, …, 1}` for vector-increment tricks. +- **No source register.** The op doesn't read `VA` / `VB`; this saves a register-file read port and keeps constant-generation cheap. +- **Big-endian lane order** (all lanes identical anyway). +- **No flags, no VSCR.** +- **No VMX128 sibling.** Xenon uses the same encoding. + +## Related Instructions + +- [`vspltish`](vspltish.md), [`vspltisw`](vspltisw.md) β€” half-word and word immediate splats (still sign-extended from 5 bits). +- [`vspltb`](vspltb.md), [`vsplth`](vsplth.md), [`vspltw`](vspltw.md) β€” register-indexed splats. +- [`vxor`](vxor.md) β€” alternative "zero vector" idiom when `vD` is already known. + +## IBM Reference + +- [AIX 7.3 β€” `vspltisb` (Vector Splat Immediate Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vspltisb-vector-splat-immediate-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vspltish.md b/tools/ppc-manual/vmx/vspltish.md new file mode 100644 index 00000000..82e009aa --- /dev/null +++ b/tools/ppc-manual/vmx/vspltish.md @@ -0,0 +1,124 @@ +# `vspltish` β€” Vector Splat Immediate Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000034c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vspltish` | `vspltish` | β€” | Vector Splat Immediate Signed Half Word | + +## Syntax + +```asm +vspltish [VD], [SIMM] +``` + +## Encoding + +### `vspltish` β€” form `VX` + +- **Opcode word:** `0x1000034c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `844` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `SIMM` | vspltish: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `VD` | vspltish: write | Destination vector register. | + +## Register Effects + +### `vspltish` + +- **Reads (always):** `SIMM` +- **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 + +**`vspltish`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltish"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1551`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1551) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:510`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L510) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2370-2375`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2370-L2375) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltish => { + let simm = ((instr.raw >> 16) & 0x1F) as i16; + let simm = if simm & 0x10 != 0 { simm | !0x1F } else { simm }; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array([simm as u16; 8]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat a 5-bit signed immediate across all 8 half-word lanes.** `SIMM` is sign-extended from 5 bits to 16, so the representable range is `[βˆ’16, +15]`. +- **Constant-generation primitive.** `vspltish vD, 0` is "all-zero half-words"; `vspltish vD, -1` is `{0xFFFF, …}`; `vspltish vD, 1` is `{0x0001, …}` (typical for "increment every lane" patterns). +- **No source register.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vspltisb`](vspltisb.md), [`vspltisw`](vspltisw.md) β€” byte / word immediate splats. +- [`vsplth`](vsplth.md) β€” register-indexed half-word splat. +- [`vxor`](vxor.md) β€” alternative zero-vector idiom. + +## IBM Reference + +- [AIX 7.3 β€” `vspltish` (Vector Splat Immediate Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vspltish-vector-splat-immediate-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vspltisw.md b/tools/ppc-manual/vmx/vspltisw.md new file mode 100644 index 00000000..0aa243b4 --- /dev/null +++ b/tools/ppc-manual/vmx/vspltisw.md @@ -0,0 +1,172 @@ +# `vspltisw` β€” Vector Splat Immediate Signed Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000038c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vspltisw` | `vspltisw` | β€” | Vector Splat Immediate Signed Word | +| `vspltisw128` | `vspltisw128` | β€” | Vector128 Splat Immediate Signed Word | + +## Syntax + +```asm +vspltisw [VD], [SIMM] +vspltisw128 [VD], [SIMM] +``` + +## Encoding + +### `vspltisw` β€” form `VX` + +- **Opcode word:** `0x1000038c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `908` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vspltisw128` β€” form `VX128_3` + +- **Opcode word:** `0x18000770` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1904` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `SIMM` | vspltisw: read; vspltisw128: read | 16-bit signed immediate. Sign-extended to 64 bits before use. | +| `VD` | vspltisw: write; vspltisw128: write | Destination vector register. | + +## Register Effects + +### `vspltisw` + +- **Reads (always):** `SIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vspltisw128` + +- **Reads (always):** `SIMM` +- **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 + +**`vspltisw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltisw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1580`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1580) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:516`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L516) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2356-2363`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2356-L2363) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltisw | PpcOpcode::vspltisw128 => { + let simm = ((instr.raw >> 16) & 0x1F) as i32; + let simm = if simm & 0x10 != 0 { simm | !0x1F } else { simm }; // sign extend 5-bit + let val = simm as u32; + let vd = if matches!(instr.opcode, PpcOpcode::vspltisw128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(val, val, val, val); + ctx.pc += 4; + } +``` +
+ +**`vspltisw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltisw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1583`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1583) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:669`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L669) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2356-2363`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2356-L2363) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltisw | PpcOpcode::vspltisw128 => { + let simm = ((instr.raw >> 16) & 0x1F) as i32; + let simm = if simm & 0x10 != 0 { simm | !0x1F } else { simm }; // sign extend 5-bit + let val = simm as u32; + let vd = if matches!(instr.opcode, PpcOpcode::vspltisw128) { instr.vd128() } else { instr.rd() }; + ctx.vr[vd] = xenia_types::Vec128::from_u32x4(val, val, val, val); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat a 5-bit signed immediate across all 4 word lanes.** `SIMM` is sign-extended from 5 bits to 32, so the representable range is `[βˆ’16, +15]`. +- **Constant-generation primitive.** `vspltisw vD, 0` zeroes every lane; `vspltisw vD, -1` generates `{0xFFFFFFFF, …}` (the all-ones vector); `vspltisw vD, 1` is `{1, 1, 1, 1}` β€” useful for "lane index = 0, 1, 2, 3" constructions via an `lvewx`-style preload followed by this. +- **No source register.** +- **No flags, no VSCR.** +- **VMX128 sibling [`vspltisw128`](vspltisw128.md).** + +## Related Instructions + +- [`vspltisb`](vspltisb.md), [`vspltish`](vspltish.md) β€” byte / half-word immediate splats. +- [`vspltw`](vspltw.md) β€” register-indexed word splat. +- [`vxor`](vxor.md) β€” alternative zero-vector idiom. + +## IBM Reference + +- [AIX 7.3 β€” `vspltisw` (Vector Splat Immediate Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vspltisw-vector-splat-immediate-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vspltw.md b/tools/ppc-manual/vmx/vspltw.md new file mode 100644 index 00000000..5ff1a519 --- /dev/null +++ b/tools/ppc-manual/vmx/vspltw.md @@ -0,0 +1,173 @@ +# `vspltw` β€” Vector Splat Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000028c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vspltw` | `vspltw` | β€” | Vector Splat Word | +| `vspltw128` | `vspltw128` | β€” | Vector128 Splat Word | + +## Syntax + +```asm +vspltw [VD], [VB], [UIMM] +vspltw128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vspltw` β€” form `VX` + +- **Opcode word:** `0x1000028c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `652` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vspltw128` β€” form `VX128_3` + +- **Opcode word:** `0x18000730` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1840` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vspltw: read; vspltw128: read | Source B vector register. | +| `UIMM` | vspltw: read; vspltw128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vspltw: write; vspltw128: write | Destination vector register. | + +## Register Effects + +### `vspltw` + +- **Reads (always):** `VB`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vspltw128` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vspltw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1529`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1529) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:493`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L493) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2328-2334`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2328-L2334) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltw => { + let uimm = ((instr.raw >> 16) & 0x3) as usize; // UIMM (2 bits for word index) + let b = ctx.vr[instr.rb()].as_u32x4(); + let val = b[uimm]; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4(val, val, val, val); + ctx.pc += 4; + } +``` +
+ +**`vspltw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vspltw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1532`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1532) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:123`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L123) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:668`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L668) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2335-2341`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2335-L2341) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vspltw128 => { + let uimm = ((instr.raw >> 16) & 0x3) as usize; + let b = ctx.vr[instr.vb128()].as_u32x4(); + let val = b[uimm]; + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_u32x4(val, val, val, val); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Splat one word across all 4 lanes.** `UIMM` (bits 11–15, low 2 bits honoured) picks which of `VB`'s 4 word lanes is replicated. +- **Big-endian index.** `UIMM = 0` β†’ `VB.w[0]` (most significant word). +- **Typical use: broadcast a float or a 32-bit constant** (e.g. splatting a scalar result before feeding it to a per-lane multiply). +- **No flags, no VSCR.** +- **VMX128 sibling [`vspltw128`](vspltw128.md).** +- **Compares with [`vpermwi128`](../vmx128/vpermwi128.md):** `vpermwi128` generalises word splat to any 4-of-4 permutation using an 8-bit immediate (2 bits per output word). + +## Related Instructions + +- [`vspltb`](vspltb.md), [`vsplth`](vsplth.md) β€” byte / half-word splat siblings. +- [`vspltisw`](vspltisw.md) β€” immediate splat counterpart. +- [`vpermwi128`](../vmx128/vpermwi128.md) β€” VMX128 full word permute. +- [`vperm`](vperm.md) β€” byte-granular programmable permute. + +## IBM Reference + +- [AIX 7.3 β€” `vspltw` (Vector Splat Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vspltw-vector-splat-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsr.md b/tools/ppc-manual/vmx/vsr.md new file mode 100644 index 00000000..4fc18dea --- /dev/null +++ b/tools/ppc-manual/vmx/vsr.md @@ -0,0 +1,128 @@ +# `vsr` β€” Vector Shift Right + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100002c4` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsr` | `vsr` | β€” | Vector Shift Right | + +## Syntax + +```asm +vsr [VD], [VA], [VB] +``` + +## Encoding + +### `vsr` β€” form `VX` + +- **Opcode word:** `0x100002c4` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `708` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsr: read | Source A vector register. | +| `VB` | vsr: read | Source B vector register. | +| `VD` | vsr: write | Destination vector register. | + +## Register Effects + +### `vsr` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsr`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsr"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1587`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1587) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:495`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L495) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3927-3933`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3927-L3933) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsr => { + let a = u128::from_be_bytes(ctx.vr[instr.ra()].as_bytes()); + let shift = (ctx.vr[instr.rb()].as_bytes()[15] & 7) as u32; + let r = if shift == 0 { a } else { a >> shift }; + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Whole-register bit shift-right.** The 128-bit value `VA` is shifted right (toward the LSB end) by `N` bits, where `N = VB.b[15] & 7` β€” the low 3 bits of the last byte of `VB`. Bits shifted out the bottom are discarded; zero-fill on the top. +- **Shift count constraint.** The ISA mandates the same 3-bit count across all of `VB`; xenia-rs reads only byte 15. Splat the count before use. +- **Pair with [`vsro`](vsro.md) for up to 127-bit shifts.** `vsro` contributes the byte-granular component; `vsr` the 0..7 residual bits. +- **Big-endian.** "Right" means toward the LSB end (`VD.b[15]`). +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsro`](vsro.md) β€” whole-register shift-right by octets. +- [`vsl`](vsl.md), [`vslo`](vslo.md) β€” left-shift counterparts. +- [`vsldoi`](vsldoi.md) β€” static-immediate byte shift of `VA β€– VB`. +- [`vsrb`](vsrb.md), [`vsrh`](vsrh.md), [`vsrw`](vsrw.md) β€” per-lane logical shifts. + +## IBM Reference + +- [AIX 7.3 β€” `vsr` (Vector Shift Right)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsr-vector-shift-right-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsrab.md b/tools/ppc-manual/vmx/vsrab.md new file mode 100644 index 00000000..3b5c2afa --- /dev/null +++ b/tools/ppc-manual/vmx/vsrab.md @@ -0,0 +1,129 @@ +# `vsrab` β€” Vector Shift Right Algebraic Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000304` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsrab` | `vsrab` | β€” | Vector Shift Right Algebraic Byte | + +## Syntax + +```asm +vsrab [VD], [VA], [VB] +``` + +## Encoding + +### `vsrab` β€” form `VX` + +- **Opcode word:** `0x10000304` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `772` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsrab: read | Source A vector register. | +| `VB` | vsrab: read | Source B vector register. | +| `VD` | vsrab: write | Destination vector register. | + +## Register Effects + +### `vsrab` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsrab`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrab"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1599`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1599) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:500`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L500) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3868-3875`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3868-L3875) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrab => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0i8; 16]; + for i in 0..16 { r[i] = a[i] >> (b[i] & 7); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane arithmetic right-shift of signed bytes.** `VD.b[i] = (int8)VA.b[i] >> (VB.b[i] & 7)` β€” sign bit propagates into vacated high-order bits. Low 3 bits of each shift-count byte are honoured. +- **Per-lane shift counts.** Splat via [`vspltb`](vspltb.md) / [`vspltisb`](vspltisb.md) for uniform shifts. +- **Sign extension.** Negative inputs produce values that stay negative (toward `-1`), unlike logical shift [`vsrb`](vsrb.md) which zero-fills. +- **Big-endian byte lanes.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsrb`](vsrb.md) β€” logical-right byte shift. +- [`vslb`](vslb.md) β€” byte logical-left. +- [`vrlb`](vrlb.md) β€” byte rotate. +- [`vsrah`](vsrah.md), [`vsraw`](vsraw.md) β€” arithmetic-right half-word / word. + +## IBM Reference + +- [AIX 7.3 β€” `vsrab` (Vector Shift Right Arithmetic Integer Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsrab-vector-shift-right-algebraic-integer-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsrah.md b/tools/ppc-manual/vmx/vsrah.md new file mode 100644 index 00000000..576c6dc8 --- /dev/null +++ b/tools/ppc-manual/vmx/vsrah.md @@ -0,0 +1,129 @@ +# `vsrah` β€” Vector Shift Right Algebraic Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000344` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsrah` | `vsrah` | β€” | Vector Shift Right Algebraic Half Word | + +## Syntax + +```asm +vsrah [VD], [VA], [VB] +``` + +## Encoding + +### `vsrah` β€” form `VX` + +- **Opcode word:** `0x10000344` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `836` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsrah: read | Source A vector register. | +| `VB` | vsrah: read | Source B vector register. | +| `VD` | vsrah: write | Destination vector register. | + +## Register Effects + +### `vsrah` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsrah`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrah"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1606`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1606) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:507`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L507) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3900-3907`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3900-L3907) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrah => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = a[i] >> (b[i] & 0xF); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane arithmetic right-shift of signed half-words.** `VD.h[i] = (int16)VA.h[i] >> (VB.h[i] & 0xF)` β€” sign bit propagates. Low 4 bits of each shift-count half-word are honoured. +- **Per-lane shift counts.** Splat via [`vsplth`](vsplth.md) / [`vspltish`](vspltish.md) for uniform shifts. +- **Sign extension.** Distinct from the logical [`vsrh`](vsrh.md) which zero-fills. +- **Big-endian half-word lanes.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsrh`](vsrh.md) β€” logical-right half-word. +- [`vslh`](vslh.md) β€” half-word logical-left. +- [`vrlh`](vrlh.md) β€” half-word rotate. +- [`vsrab`](vsrab.md), [`vsraw`](vsraw.md) β€” arithmetic-right byte / word. + +## IBM Reference + +- [AIX 7.3 β€” `vsrah` (Vector Shift Right Arithmetic Integer Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsrah-vector-shift-right-algebraic-integer-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsraw.md b/tools/ppc-manual/vmx/vsraw.md new file mode 100644 index 00000000..69b66ea6 --- /dev/null +++ b/tools/ppc-manual/vmx/vsraw.md @@ -0,0 +1,187 @@ +# `vsraw` β€” Vector Shift Right Algebraic Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000384` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsraw` | `vsraw` | β€” | Vector Shift Right Algebraic Word | +| `vsraw128` | `vsraw128` | β€” | Vector128 Shift Right Arithmetic Word | + +## Syntax + +```asm +vsraw [VD], [VA], [VB] +vsraw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vsraw` β€” form `VX` + +- **Opcode word:** `0x10000384` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `900` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vsraw128` β€” form `VX128` + +- **Opcode word:** `0x18000150` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `336` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsraw: read; vsraw128: read | Source A vector register. | +| `VB` | vsraw: read; vsraw128: read | Source B vector register. | +| `VD` | vsraw: write; vsraw128: write | Destination vector register. | + +## Register Effects + +### `vsraw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsraw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsraw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsraw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1619`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1619) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:514`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L514) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2438-2449`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2438-L2449) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsraw | PpcOpcode::vsraw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = (a[i] as i32 >> sh) as u32; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vsraw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsraw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1622`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1622) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:694`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L694) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2438-2449`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2438-L2449) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsraw | PpcOpcode::vsraw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = (a[i] as i32 >> sh) as u32; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane arithmetic right-shift of signed words.** `VD.w[i] = (int32)VA.w[i] >> (VB.w[i] & 0x1F)` β€” sign bit propagates. Low 5 bits of each shift-count word are honoured. +- **Per-lane shift counts.** Splat via [`vspltw`](vspltw.md) / [`vspltisw`](vspltisw.md) for uniform shifts. +- **Sign extension** β€” distinct from [`vsrw`](vsrw.md) (zero-fill). +- **Big-endian word lanes.** +- **No flags, no VSCR.** +- **VMX128 sibling [`vsraw128`](vsraw128.md).** + +## Related Instructions + +- [`vsrw`](vsrw.md) β€” logical-right word. +- [`vslw`](vslw.md) β€” word logical-left. +- [`vrlw`](vrlw.md) β€” word rotate. +- [`vsrab`](vsrab.md), [`vsrah`](vsrah.md) β€” byte / half-word arithmetic-right. + +## IBM Reference + +- [AIX 7.3 β€” `vsraw` (Vector Shift Right Arithmetic Integer Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsraw-vector-shift-right-algebraic-integer-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsrb.md b/tools/ppc-manual/vmx/vsrb.md new file mode 100644 index 00000000..bb9c41de --- /dev/null +++ b/tools/ppc-manual/vmx/vsrb.md @@ -0,0 +1,129 @@ +# `vsrb` β€” Vector Shift Right Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000204` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsrb` | `vsrb` | β€” | Vector Shift Right Byte | + +## Syntax + +```asm +vsrb [VD], [VA], [VB] +``` + +## Encoding + +### `vsrb` β€” form `VX` + +- **Opcode word:** `0x10000204` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `516` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsrb: read | Source A vector register. | +| `VB` | vsrb: read | Source B vector register. | +| `VD` | vsrb: write | Destination vector register. | + +## Register Effects + +### `vsrb` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsrb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1626`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1626) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:477`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L477) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3860-3867`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3860-L3867) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrb => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i] >> (b[i] & 7); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical right-shift of bytes.** `VD.b[i] = VA.b[i] >> (VB.b[i] & 7)` β€” zero-fills the vacated high-order bits. Low 3 bits of each shift-count byte are honoured. +- **Per-lane shift counts.** Splat via [`vspltb`](vspltb.md) / [`vspltisb`](vspltisb.md) for uniform shifts. +- **Zero-fill.** For sign-preserving shift use [`vsrab`](vsrab.md). +- **Big-endian byte lanes.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsrab`](vsrab.md) β€” arithmetic-right (sign-extending) byte shift. +- [`vslb`](vslb.md) β€” byte logical-left. +- [`vrlb`](vrlb.md) β€” byte rotate. +- [`vsrh`](vsrh.md), [`vsrw`](vsrw.md) β€” half-word / word logical-right shifts. + +## IBM Reference + +- [AIX 7.3 β€” `vsrb` (Vector Shift Right Integer Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsrb-vector-shift-right-integer-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsrh.md b/tools/ppc-manual/vmx/vsrh.md new file mode 100644 index 00000000..821ab6cd --- /dev/null +++ b/tools/ppc-manual/vmx/vsrh.md @@ -0,0 +1,129 @@ +# `vsrh` β€” Vector Shift Right Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000244` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsrh` | `vsrh` | β€” | Vector Shift Right Half Word | + +## Syntax + +```asm +vsrh [VD], [VA], [VB] +``` + +## Encoding + +### `vsrh` β€” form `VX` + +- **Opcode word:** `0x10000244` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `580` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsrh: read | Source A vector register. | +| `VB` | vsrh: read | Source B vector register. | +| `VD` | vsrh: write | Destination vector register. | + +## Register Effects + +### `vsrh` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsrh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1633`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1633) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:484`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L484) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3892-3899`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3892-L3899) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrh => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i] >> (b[i] & 0xF); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical right-shift of half-words.** `VD.h[i] = VA.h[i] >> (VB.h[i] & 0xF)` β€” zero-fill. Low 4 bits of each shift-count half-word honoured. +- **Per-lane shift counts.** Splat via [`vsplth`](vsplth.md) / [`vspltish`](vspltish.md) for uniform shifts. +- **Zero-fill.** Use [`vsrah`](vsrah.md) for sign-preserving right shift. +- **Big-endian half-word lanes.** +- **No flags, no VSCR.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsrah`](vsrah.md) β€” arithmetic-right half-word. +- [`vslh`](vslh.md) β€” logical-left half-word. +- [`vrlh`](vrlh.md) β€” half-word rotate. +- [`vsrb`](vsrb.md), [`vsrw`](vsrw.md) β€” byte / word logical-right. + +## IBM Reference + +- [AIX 7.3 β€” `vsrh` (Vector Shift Right Integer Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsrh-vector-shift-right-integer-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsro.md b/tools/ppc-manual/vmx/vsro.md new file mode 100644 index 00000000..d0d74317 --- /dev/null +++ b/tools/ppc-manual/vmx/vsro.md @@ -0,0 +1,183 @@ +# `vsro` β€” Vector Shift Right Octet + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000044c` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsro` | `vsro` | β€” | Vector Shift Right Octet | +| `vsro128` | `vsro128` | β€” | Vector128 Shift Right Octet | + +## Syntax + +```asm +vsro [VD], [VA], [VB] +vsro128 [VD], [VA], [VB] +``` + +## Encoding + +### `vsro` β€” form `VX` + +- **Opcode word:** `0x1000044c` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1100` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vsro128` β€” form `VX128` + +- **Opcode word:** `0x140003d0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `976` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsro: read; vsro128: read | Source A vector register. | +| `VB` | vsro: read; vsro128: read | Source B vector register. | +| `VD` | vsro: write; vsro128: write | Destination vector register. | + +## Register Effects + +### `vsro` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsro128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsro`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsro"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1651`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1651) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:528`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L528) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3945-3954`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3945-L3954) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsro | PpcOpcode::vsro128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vsro128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = u128::from_be_bytes(ctx.vr[ra].as_bytes()); + let nbytes = ((ctx.vr[rb].as_bytes()[15] >> 3) & 0xF) as u32; + let r = if nbytes == 0 { a } else { a >> (nbytes * 8) }; + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ +**`vsro128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsro128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1654`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1654) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:633`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L633) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3945-3954`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3945-L3954) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsro | PpcOpcode::vsro128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vsro128); + let (ra, rb, rd) = if is_128 { (instr.va128(), instr.vb128(), instr.vd128()) } + else { (instr.ra(), instr.rb(), instr.rd()) }; + let a = u128::from_be_bytes(ctx.vr[ra].as_bytes()); + let nbytes = ((ctx.vr[rb].as_bytes()[15] >> 3) & 0xF) as u32; + let r = if nbytes == 0 { a } else { a >> (nbytes * 8) }; + ctx.vr[rd] = xenia_types::Vec128::from_bytes(r.to_be_bytes()); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Whole-register shift-right by octets (bytes).** `VA` is shifted right by `N` bytes, where `N = (VB.b[15] >> 3) & 0xF`. Top end is zero-filled. +- **Shift count constraint.** Uniform 4-bit count required across `VB`; xenia reads only byte 15. +- **Pair with [`vsr`](vsr.md) for full bit-level shifts.** `vsro` handles bytes; `vsr` handles the 0..7 residual. +- **Big-endian.** "Right" = toward LSB end (`VD.b[15]`). +- **No flags, no VSCR.** +- **VMX128 sibling [`vsro128`](vsro128.md).** + +## Related Instructions + +- [`vsr`](vsr.md) β€” bit-level whole-register shift-right. +- [`vslo`](vslo.md) β€” shift-left by octet. +- [`vsldoi`](vsldoi.md) β€” static-immediate variant. +- [`vsrb`](vsrb.md), [`vsrh`](vsrh.md), [`vsrw`](vsrw.md) β€” per-lane logical shifts. + +## IBM Reference + +- [AIX 7.3 β€” `vsro` (Vector Shift Right by Octet)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsro-vector-shift-right-by-octet-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsrw.md b/tools/ppc-manual/vmx/vsrw.md new file mode 100644 index 00000000..90f04683 --- /dev/null +++ b/tools/ppc-manual/vmx/vsrw.md @@ -0,0 +1,187 @@ +# `vsrw` β€” Vector Shift Right Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000284` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsrw` | `vsrw` | β€” | Vector Shift Right Word | +| `vsrw128` | `vsrw128` | β€” | Vector128 Shift Right Word | + +## Syntax + +```asm +vsrw [VD], [VA], [VB] +vsrw128 [VD], [VA], [VB] +``` + +## Encoding + +### `vsrw` β€” form `VX` + +- **Opcode word:** `0x10000284` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `644` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vsrw128` β€” form `VX128` + +- **Opcode word:** `0x180001d0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `464` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsrw: read; vsrw128: read | Source A vector register. | +| `VB` | vsrw: read; vsrw128: read | Source B vector register. | +| `VD` | vsrw: write; vsrw128: write | Destination vector register. | + +## Register Effects + +### `vsrw` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsrw128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsrw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1664`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1664) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:491`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L491) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2426-2437`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2426-L2437) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrw | PpcOpcode::vsrw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i] >> sh; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vsrw128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsrw128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1667`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1667) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:124`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L124) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:695`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L695) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2426-2437`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2426-L2437) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsrw | PpcOpcode::vsrw128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { + let sh = b[i] & 0x1F; + r[i] = a[i] >> sh; + } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-lane logical right-shift of words.** `VD.w[i] = VA.w[i] >> (VB.w[i] & 0x1F)` β€” zero-fill. Low 5 bits of each shift-count word honoured. +- **Per-lane shift counts.** Splat via [`vspltw`](vspltw.md) / [`vspltisw`](vspltisw.md) for uniform shifts. +- **Zero-fill.** Use [`vsraw`](vsraw.md) for sign-preserving right shift. +- **Big-endian word lanes.** +- **No flags, no VSCR.** +- **VMX128 sibling [`vsrw128`](vsrw128.md).** + +## Related Instructions + +- [`vsraw`](vsraw.md) β€” arithmetic-right word. +- [`vslw`](vslw.md) β€” logical-left word. +- [`vrlw`](vrlw.md) β€” word rotate. +- [`vsrb`](vsrb.md), [`vsrh`](vsrh.md) β€” byte / half-word logical-right. + +## IBM Reference + +- [AIX 7.3 β€” `vsrw` (Vector Shift Right Integer Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsrw-vector-shift-right-integer-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubcuw.md b/tools/ppc-manual/vmx/vsubcuw.md new file mode 100644 index 00000000..cd1d8540 --- /dev/null +++ b/tools/ppc-manual/vmx/vsubcuw.md @@ -0,0 +1,128 @@ +# `vsubcuw` β€” Vector Subtract Carryout Unsigned Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000580` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubcuw` | `vsubcuw` | β€” | Vector Subtract Carryout Unsigned Word | + +## Syntax + +```asm +vsubcuw [VD], [VA], [VB] +``` + +## Encoding + +### `vsubcuw` β€” form `VX` + +- **Opcode word:** `0x10000580` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1408` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubcuw: read | Source A vector register. | +| `VB` | vsubcuw: read | Source B vector register. | +| `VD` | vsubcuw: write | Destination vector register. | + +## Register Effects + +### `vsubcuw` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsubcuw`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubcuw"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1671`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1671) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:536`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L536) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3391-3399`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3391-L3399) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubcuw => { + // "Subtract Carryout": r = 1 if a >= b (no borrow), 0 otherwise. + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = if a[i] >= b[i] { 1 } else { 0 }; } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **"Borrow out" producer for unsigned word subtract.** Each of the 4 lanes produces `1` if `VA.w[i] >= VB.w[i]` (no borrow) and `0` otherwise. This is an **inverted** borrow β€” conventional borrow would be `1` on underflow, but Altivec's `vsubcuw` returns the opposite to match the `XER[CA]` convention used by scalar `subfc` / `subfe`. +- **Complements [`vadduwm`](vadduwm.md) / [`vaddcuw`](vaddcuw.md)** for 256-bit (or wider) multi-precision arithmetic. After the lane subtract, chain the 4-bit borrow vector into the next word via [`vsubeuwm`](vsubeuwm.md)-style helpers (or manual software glue, since Altivec has no direct `sube`). +- **No saturation, no flags, no VSCR effect.** Despite being in the "carry" family, `vsubcuw` doesn't touch `VSCR[SAT]`. +- **Big-endian word lanes.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsubuwm`](vsubuwm.md) β€” the difference value (unsigned modulo). +- [`vaddcuw`](vaddcuw.md) β€” the paired carry-out producer for unsigned-add. +- [`vadduwm`](vadduwm.md) β€” unsigned word add (modulo). + +## IBM Reference + +- [AIX 7.3 β€” `vsubcuw` (Vector Subtract and Write Carry-Out Unsigned Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubcuw-vector-subtract-write-carry-out-unsigned-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubfp.md b/tools/ppc-manual/vmx/vsubfp.md new file mode 100644 index 00000000..c473a8ef --- /dev/null +++ b/tools/ppc-manual/vmx/vsubfp.md @@ -0,0 +1,183 @@ +# `vsubfp` β€” Vector Subtract Floating Point + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000004a` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubfp` | `vsubfp` | β€” | Vector Subtract Floating Point | +| `vsubfp128` | `vsubfp128` | β€” | Vector128 Subtract Floating Point | + +## Syntax + +```asm +vsubfp [VD], [VA], [VB] +vsubfp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vsubfp` β€” form `VX` + +- **Opcode word:** `0x1000004a` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `74` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vsubfp128` β€” form `VX128` + +- **Opcode word:** `0x14000050` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `80` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubfp: read; vsubfp128: read | Source A vector register. | +| `VB` | vsubfp: read; vsubfp128: read | Source B vector register. | +| `VD` | vsubfp: write; vsubfp128: write | Destination vector register. | + +## Register Effects + +### `vsubfp` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vsubfp128` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +_No condition-register or status-register effects._ + +## Operation (pseudocode) + +``` +for each 32-bit float lane i in 0..3: + VD[i] <- VA[i] βˆ’ VB[i] +``` + +## 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 + +**`vsubfp`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubfp"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1686`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1686) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:445`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L445) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2012-2024`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2012-L2024) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubfp => { + // PPCBUG-435. + let a = ctx.vr[instr.ra()].as_f32x4(); + let b = ctx.vr[instr.rb()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + r[i] = vmx::flush_denorm(ai - bi); + } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vsubfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1689`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1689) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:611`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L611) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2025-2037`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2025-L2037) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubfp128 => { + // PPCBUG-435. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + r[i] = vmx::flush_denorm(ai - bi); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Lane-wise IEEE-754 binary32 subtract.** Each of the four lanes computes `VD[i] = VA[i] βˆ’ VB[i]`, rounded to nearest. +- **`VSCR[NJ]` honoured.** Denormals flushed to zero when `NJ = 1` (the Xenon boot default). +- **NaN propagation.** A NaN in either operand propagates to the destination lane. +- **`±∞ βˆ’ ±∞` β†’ NaN.** No exception, no VSCR[SAT] set. +- **No FPSCR update.** VMX float ops are independent of the scalar FPU's status register. +- **Big-endian lane indexing.** +- **VMX128 sibling [`vsubfp128`](vsubfp128.md).** +- **Aliasing legal.** `vsubfp v3, v3, v4` is fine. + +## Related Instructions + +- [`vaddfp`](vaddfp.md) β€” lane-wise float add. +- [`vmaddfp`](vmaddfp.md), [`vnmsubfp`](vnmsubfp.md) β€” fused multiply-accumulate variants. +- [`vmaxfp`](vmaxfp.md), [`vminfp`](vminfp.md) β€” IEEE-754-aware max/min. +- [`vcmpeqfp`](vcmpeqfp.md), [`vcmpgtfp`](vcmpgtfp.md), [`vcmpgefp`](vcmpgefp.md), [`vcmpbfp`](vcmpbfp.md) β€” compares. + +## IBM Reference + +- [AIX 7.3 β€” `vsubfp` (Vector Subtract Floating Point)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubfp-vector-subtract-floating-point-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubsbs.md b/tools/ppc-manual/vmx/vsubsbs.md new file mode 100644 index 00000000..b645dd3a --- /dev/null +++ b/tools/ppc-manual/vmx/vsubsbs.md @@ -0,0 +1,133 @@ +# `vsubsbs` β€” Vector Subtract Signed Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000700` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubsbs` | `vsubsbs` | β€” | Vector Subtract Signed Byte Saturate | + +## Syntax + +```asm +vsubsbs [VD], [VA], [VB] +``` + +## Encoding + +### `vsubsbs` β€” form `VX` + +- **Opcode word:** `0x10000700` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1792` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubsbs: read | Source A vector register. | +| `VB` | vsubsbs: read | Source B vector register. | +| `VD` | vsubsbs: write | Destination vector register. | +| `VSCR` | vsubsbs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsubsbs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsubsbs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsubsbs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubsbs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1693`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1693) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:546`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L546) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3270-3281`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3270-L3281) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubsbs => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i8x16(ctx.vr[instr.rb()]); + let mut r = [0i8; 16]; let mut sat = false; + for i in 0..16 { + let (v, s) = crate::vmx::sat_sub_i8(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i8x16(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed-byte saturating subtract.** `VD.b[i] = clamp_int8(VA.b[i] βˆ’ VB.b[i])`. Each lane computed as `int8`; any lane that goes below `βˆ’128` or above `+127` is clamped and sticky-sets `VSCR[SAT]`. Xenia's helper is `vmx::sat_sub_i8`. +- **Sticky VSCR[SAT].** Once set it remains set until explicit `mtvscr` clear. +- **Big-endian byte lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** +- **Compare with [`vsububs`](vsububs.md)** for the unsigned variant, or [`vsububm`](vsububm.md) for modulo wrap. + +## Related Instructions + +- [`vaddsbs`](vaddsbs.md) β€” signed byte saturating add. +- [`vsububs`](vsububs.md), [`vsububm`](vsububm.md) β€” unsigned byte sub (saturating / modulo). +- [`vsubshs`](vsubshs.md), [`vsubsws`](vsubsws.md) β€” signed saturating subtracts at wider lane widths. + +## IBM Reference + +- [AIX 7.3 β€” `vsubsbs` (Vector Subtract Signed Byte Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubsbs-vector-subtract-signed-byte-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubshs.md b/tools/ppc-manual/vmx/vsubshs.md new file mode 100644 index 00000000..58382e00 --- /dev/null +++ b/tools/ppc-manual/vmx/vsubshs.md @@ -0,0 +1,132 @@ +# `vsubshs` β€” Vector Subtract Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000740` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubshs` | `vsubshs` | β€” | Vector Subtract Signed Half Word Saturate | + +## Syntax + +```asm +vsubshs [VD], [VA], [VB] +``` + +## Encoding + +### `vsubshs` β€” form `VX` + +- **Opcode word:** `0x10000740` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1856` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubshs: read | Source A vector register. | +| `VB` | vsubshs: read | Source B vector register. | +| `VD` | vsubshs: write | Destination vector register. | +| `VSCR` | vsubshs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsubshs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsubshs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsubshs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubshs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1702`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1702) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:548`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L548) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3318-3329`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3318-L3329) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubshs => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i16; 8]; let mut sat = false; + for i in 0..8 { + let (v, s) = crate::vmx::sat_sub_i16(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed half-word saturating subtract.** `VD.h[i] = clamp_int16(VA.h[i] βˆ’ VB.h[i])` for 8 lanes. Overflow clamps to `Β±0x7FFF` and sticky-sets `VSCR[SAT]`. Xenia uses `vmx::sat_sub_i16`. +- **Sticky VSCR[SAT].** +- **Big-endian half-word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vaddshs`](vaddshs.md) β€” signed half-word saturating add. +- [`vsubuhs`](vsubuhs.md), [`vsubuhm`](vsubuhm.md) β€” unsigned half-word sub (sat / mod). +- [`vsubsbs`](vsubsbs.md), [`vsubsws`](vsubsws.md) β€” signed saturating subs at byte / word width. + +## IBM Reference + +- [AIX 7.3 β€” `vsubshs` (Vector Subtract Signed Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubshs-vector-subtract-signed-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubsws.md b/tools/ppc-manual/vmx/vsubsws.md new file mode 100644 index 00000000..e287432b --- /dev/null +++ b/tools/ppc-manual/vmx/vsubsws.md @@ -0,0 +1,132 @@ +# `vsubsws` β€” Vector Subtract Signed Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000780` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubsws` | `vsubsws` | β€” | Vector Subtract Signed Word Saturate | + +## Syntax + +```asm +vsubsws [VD], [VA], [VB] +``` + +## Encoding + +### `vsubsws` β€” form `VX` + +- **Opcode word:** `0x10000780` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1920` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubsws: read | Source A vector register. | +| `VB` | vsubsws: read | Source B vector register. | +| `VD` | vsubsws: write | Destination vector register. | +| `VSCR` | vsubsws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsubsws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsubsws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsubsws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubsws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1711`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1711) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:125`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L125) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:549`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L549) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3366-3377`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3366-L3377) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubsws => { + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let b = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::sat_sub_i32(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed word saturating subtract.** `VD.w[i] = clamp_int32(VA.w[i] βˆ’ VB.w[i])` for 4 lanes. Overflow clamps to `Β±0x7FFFFFFF` and sticky-sets `VSCR[SAT]`. Xenia uses `vmx::sat_sub_i32`. +- **Sticky VSCR[SAT].** +- **Big-endian word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vaddsws`](vaddsws.md) β€” signed word saturating add. +- [`vsubuws`](vsubuws.md), [`vsubuwm`](vsubuwm.md) β€” unsigned word sub (sat / mod). +- [`vsubsbs`](vsubsbs.md), [`vsubshs`](vsubshs.md) β€” byte / half-word signed saturating subs. + +## IBM Reference + +- [AIX 7.3 β€” `vsubsws` (Vector Subtract Signed Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubsws-vector-subtract-signed-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsububm.md b/tools/ppc-manual/vmx/vsububm.md new file mode 100644 index 00000000..e5f84c8b --- /dev/null +++ b/tools/ppc-manual/vmx/vsububm.md @@ -0,0 +1,128 @@ +# `vsububm` β€” Vector Subtract Unsigned Byte Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000400` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsububm` | `vsububm` | β€” | Vector Subtract Unsigned Byte Modulo | + +## Syntax + +```asm +vsububm [VD], [VA], [VB] +``` + +## Encoding + +### `vsububm` β€” form `VX` + +- **Opcode word:** `0x10000400` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1024` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsububm: read | Source A vector register. | +| `VB` | vsububm: read | Source B vector register. | +| `VD` | vsububm: write | Destination vector register. | + +## Register Effects + +### `vsububm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsububm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsububm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1720`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1720) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:519`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L519) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3206-3213`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3206-L3213) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsububm => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; + for i in 0..16 { r[i] = a[i].wrapping_sub(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned-byte modulo subtract.** `VD.b[i] = VA.b[i] βˆ’ VB.b[i]` as `u8` wrapping; underflow wraps silently. No saturation, no `VSCR[SAT]` update. +- **Useful as a signed sub too** because 8-bit two's-complement sub is bit-identical to unsigned modulo sub. The `m` suffix signals the modulo/wrap semantics regardless of signed interpretation. +- **Big-endian byte lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vadduwm`](vadduwm.md)/[`vadduhm`](vadduhm.md)/[`vaddubm`](vaddubm.md) β€” modulo adds at various widths. +- [`vsububs`](vsububs.md) β€” saturating sibling. +- [`vsubsbs`](vsubsbs.md) β€” signed saturating byte sub. +- [`vsubuhm`](vsubuhm.md), [`vsubuwm`](vsubuwm.md) β€” half-word / word modulo subs. + +## IBM Reference + +- [AIX 7.3 β€” `vsububm` (Vector Subtract Unsigned Byte Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsububm-vector-subtract-unsigned-byte-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsububs.md b/tools/ppc-manual/vmx/vsububs.md new file mode 100644 index 00000000..896c003f --- /dev/null +++ b/tools/ppc-manual/vmx/vsububs.md @@ -0,0 +1,134 @@ +# `vsububs` β€” Vector Subtract Unsigned Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000600` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsububs` | `vsububs` | β€” | Vector Subtract Unsigned Byte Saturate | + +## Syntax + +```asm +vsububs [VD], [VA], [VB] +``` + +## Encoding + +### `vsububs` β€” form `VX` + +- **Opcode word:** `0x10000600` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1536` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsububs: read | Source A vector register. | +| `VB` | vsububs: read | Source B vector register. | +| `VD` | vsububs: write | Destination vector register. | +| `VSCR` | vsububs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsububs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsububs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsububs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsububs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1744`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1744) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:538`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L538) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3246-3257`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3246-L3257) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsububs => { + let a = ctx.vr[instr.ra()].as_bytes(); + let b = ctx.vr[instr.rb()].as_bytes(); + let mut r = [0u8; 16]; let mut sat = false; + for i in 0..16 { + let (v, s) = crate::vmx::sat_sub_u8(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_bytes(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned-byte saturating subtract.** `VD.b[i] = clamp_u8(VA.b[i] βˆ’ VB.b[i])` per lane. Negative results clamp to 0 and sticky-set `VSCR[SAT]`. Xenia uses `vmx::sat_sub_u8`. +- **Sticky VSCR[SAT].** +- **Common image-processing primitive.** "Floor at zero" for per-channel differences (alpha compositing, edge detection, etc.). +- **Big-endian byte lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsububm`](vsububm.md) β€” modulo sibling. +- [`vsubsbs`](vsubsbs.md) β€” signed-byte saturating sub. +- [`vsubuhs`](vsubuhs.md), [`vsubuws`](vsubuws.md) β€” half-word / word unsigned saturating subs. +- [`vadduhs`](vadduhs.md), [`vadduws`](vadduws.md) β€” the add counterparts. + +## IBM Reference + +- [AIX 7.3 β€” `vsububs` (Vector Subtract Unsigned Byte Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsububs-vector-subtract-unsigned-byte-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubuhm.md b/tools/ppc-manual/vmx/vsubuhm.md new file mode 100644 index 00000000..f1b09048 --- /dev/null +++ b/tools/ppc-manual/vmx/vsubuhm.md @@ -0,0 +1,128 @@ +# `vsubuhm` β€” Vector Subtract Unsigned Half Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000440` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubuhm` | `vsubuhm` | β€” | Vector Subtract Unsigned Half Word Modulo | + +## Syntax + +```asm +vsubuhm [VD], [VA], [VB] +``` + +## Encoding + +### `vsubuhm` β€” form `VX` + +- **Opcode word:** `0x10000440` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1088` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubuhm: read | Source A vector register. | +| `VB` | vsubuhm: read | Source B vector register. | +| `VD` | vsubuhm: write | Destination vector register. | + +## Register Effects + +### `vsubuhm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsubuhm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubuhm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1728`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1728) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:524`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L524) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3222-3229`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3222-L3229) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubuhm => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; + for i in 0..8 { r[i] = a[i].wrapping_sub(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word modulo subtract.** `VD.h[i] = VA.h[i] βˆ’ VB.h[i]` as `u16` wrapping. No saturation, no `VSCR[SAT]`. +- **Signed-or-unsigned.** Two's-complement 16-bit sub is bit-identical modulo either interpretation. +- **Big-endian half-word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vadduhm`](vadduhm.md) β€” modulo add counterpart. +- [`vsubuhs`](vsubuhs.md) β€” saturating sibling. +- [`vsubshs`](vsubshs.md) β€” signed saturating half-word sub. +- [`vsububm`](vsububm.md), [`vsubuwm`](vsubuwm.md) β€” byte / word modulo subs. + +## IBM Reference + +- [AIX 7.3 β€” `vsubuhm` (Vector Subtract Unsigned Half Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubuhm-vector-subtract-unsigned-half-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubuhs.md b/tools/ppc-manual/vmx/vsubuhs.md new file mode 100644 index 00000000..7ee2438a --- /dev/null +++ b/tools/ppc-manual/vmx/vsubuhs.md @@ -0,0 +1,133 @@ +# `vsubuhs` β€” Vector Subtract Unsigned Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000640` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubuhs` | `vsubuhs` | β€” | Vector Subtract Unsigned Half Word Saturate | + +## Syntax + +```asm +vsubuhs [VD], [VA], [VB] +``` + +## Encoding + +### `vsubuhs` β€” form `VX` + +- **Opcode word:** `0x10000640` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1600` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubuhs: read | Source A vector register. | +| `VB` | vsubuhs: read | Source B vector register. | +| `VD` | vsubuhs: write | Destination vector register. | +| `VSCR` | vsubuhs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsubuhs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsubuhs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsubuhs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubuhs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1753`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1753) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:541`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L541) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3294-3305`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3294-L3305) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubuhs => { + let a = ctx.vr[instr.ra()].as_u16x8(); + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u16; 8]; let mut sat = false; + for i in 0..8 { + let (v, s) = crate::vmx::sat_sub_u16(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u16x8_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned half-word saturating subtract.** `VD.h[i] = clamp_u16(VA.h[i] βˆ’ VB.h[i])` per lane. Negative results clamp to 0 and sticky-set `VSCR[SAT]`. Xenia uses `vmx::sat_sub_u16`. +- **Sticky VSCR[SAT].** +- **Big-endian half-word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsubuhm`](vsubuhm.md) β€” modulo sibling. +- [`vsubshs`](vsubshs.md) β€” signed half-word saturating sub. +- [`vsububs`](vsububs.md), [`vsubuws`](vsubuws.md) β€” byte / word unsigned saturating subs. +- [`vadduhs`](vadduhs.md) β€” the unsigned half-word saturating add. + +## IBM Reference + +- [AIX 7.3 β€” `vsubuhs` (Vector Subtract Unsigned Half Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubuhs-vector-subtract-unsigned-half-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubuwm.md b/tools/ppc-manual/vmx/vsubuwm.md new file mode 100644 index 00000000..7284ea19 --- /dev/null +++ b/tools/ppc-manual/vmx/vsubuwm.md @@ -0,0 +1,129 @@ +# `vsubuwm` β€” Vector Subtract Unsigned Word Modulo + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000480` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubuwm` | `vsubuwm` | β€” | Vector Subtract Unsigned Word Modulo | + +## Syntax + +```asm +vsubuwm [VD], [VA], [VB] +``` + +## Encoding + +### `vsubuwm` β€” form `VX` + +- **Opcode word:** `0x10000480` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1152` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubuwm: read | Source A vector register. | +| `VB` | vsubuwm: read | Source B vector register. | +| `VD` | vsubuwm: write | Destination vector register. | + +## Register Effects + +### `vsubuwm` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vsubuwm`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubuwm"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1736`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1736) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:529`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L529) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2404-2411`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2404-L2411) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubuwm => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i].wrapping_sub(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned word modulo subtract.** `VD.w[i] = VA.w[i] βˆ’ VB.w[i]` as `u32` wrapping. +- **Signed-or-unsigned.** Two's-complement 32-bit sub is bit-identical modulo either interpretation. +- **Big-endian word lanes.** +- **No `Rc`, no XER, no VSCR effect.** +- **No VMX128 sibling.** Note `vsubcuw` is the paired borrow-producer. + +## Related Instructions + +- [`vadduwm`](vadduwm.md) β€” modulo add counterpart. +- [`vsubuws`](vsubuws.md) β€” saturating sibling. +- [`vsubsws`](vsubsws.md) β€” signed saturating word sub. +- [`vsubcuw`](vsubcuw.md) β€” produces the borrow-out mask (multi-precision subtract helper). +- [`vsububm`](vsububm.md), [`vsubuhm`](vsubuhm.md) β€” byte / half-word modulo subs. + +## IBM Reference + +- [AIX 7.3 β€” `vsubuwm` (Vector Subtract Unsigned Word Modulo)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubuwm-vector-subtract-unsigned-word-modulo-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsubuws.md b/tools/ppc-manual/vmx/vsubuws.md new file mode 100644 index 00000000..d2abb513 --- /dev/null +++ b/tools/ppc-manual/vmx/vsubuws.md @@ -0,0 +1,133 @@ +# `vsubuws` β€” Vector Subtract Unsigned Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000680` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsubuws` | `vsubuws` | β€” | Vector Subtract Unsigned Word Saturate | + +## Syntax + +```asm +vsubuws [VD], [VA], [VB] +``` + +## Encoding + +### `vsubuws` β€” form `VX` + +- **Opcode word:** `0x10000680` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1664` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsubuws: read | Source A vector register. | +| `VB` | vsubuws: read | Source B vector register. | +| `VD` | vsubuws: write | Destination vector register. | +| `VSCR` | vsubuws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsubuws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsubuws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsubuws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsubuws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1762`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1762) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:126`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L126) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:544`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L544) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3342-3353`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3342-L3353) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsubuws => { + let a = ctx.vr[instr.ra()].as_u32x4(); + let b = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::sat_sub_u32(a[i], b[i]); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned word saturating subtract.** `VD.w[i] = clamp_u32(VA.w[i] βˆ’ VB.w[i])` per lane. Negative results clamp to 0 and sticky-set `VSCR[SAT]`. Xenia uses `vmx::sat_sub_u32`. +- **Sticky VSCR[SAT].** +- **Big-endian word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsubuwm`](vsubuwm.md) β€” modulo sibling. +- [`vsubsws`](vsubsws.md) β€” signed word saturating sub. +- [`vsububs`](vsububs.md), [`vsubuhs`](vsubuhs.md) β€” byte / half-word unsigned saturating subs. +- [`vadduws`](vadduws.md) β€” the unsigned word saturating add. + +## IBM Reference + +- [AIX 7.3 β€” `vsubuws` (Vector Subtract Unsigned Word Saturate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsubuws-vector-subtract-unsigned-word-saturate-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsum2sws.md b/tools/ppc-manual/vmx/vsum2sws.md new file mode 100644 index 00000000..07429948 --- /dev/null +++ b/tools/ppc-manual/vmx/vsum2sws.md @@ -0,0 +1,133 @@ +# `vsum2sws` β€” Vector Sum Across Partial (1/2) Signed Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000688` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsum2sws` | `vsum2sws` | β€” | Vector Sum Across Partial (1/2) Signed Word Saturate | + +## Syntax + +```asm +vsum2sws [VD], [VA], [VB] +``` + +## Encoding + +### `vsum2sws` β€” form `VX` + +- **Opcode word:** `0x10000688` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1672` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsum2sws: read | Source A vector register. | +| `VB` | vsum2sws: read | Source B vector register. | +| `VD` | vsum2sws: write | Destination vector register. | +| `VSCR` | vsum2sws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsum2sws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsum2sws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsum2sws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsum2sws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1776`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1776) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:127`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L127) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:545`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L545) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3668-3679`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3668-L3679) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsum2sws => { + // Two 2-word partial sums at lanes 1 and 3. + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let s0 = a[0] as i64 + a[1] as i64 + c[1] as i64; + let s1 = a[2] as i64 + a[3] as i64 + c[3] as i64; + let (v0, sat0) = crate::vmx::sat_i64_to_i32(s0); + let (v1, sat1) = crate::vmx::sat_i64_to_i32(s1); + if sat0 | sat1 { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4([0, v0, 0, v1]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Two 2-word partial sums.** The four signed-word lanes of `VA` are split into two pairs: `{VA.w[0], VA.w[1]}` and `{VA.w[2], VA.w[3]}`. Each pair is summed, then added to the matching "anchor" word of `VB` (`VB.w[1]` and `VB.w[3]` respectively). Each 33-bit intermediate result is saturated to `int32`. +- **Output lane placement.** `VD.w[0] = 0`, `VD.w[1] = sat(VA.w[0] + VA.w[1] + VB.w[1])`, `VD.w[2] = 0`, `VD.w[3] = sat(VA.w[2] + VA.w[3] + VB.w[3])`. The zero lanes are specified in the ISA β€” software that wants a contiguous pair must `vmrglw` / `vmrghw` afterwards. +- **Sticky VSCR[SAT]** set when either saturating truncation occurs. +- **Big-endian word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsumsws`](vsumsws.md) β€” full 4-lane sum. +- [`vsum4sbs`](vsum4sbs.md), [`vsum4shs`](vsum4shs.md), [`vsum4ubs`](vsum4ubs.md) β€” per-word partial sums at narrower input widths. +- [`vaddsws`](vaddsws.md), [`vsubsws`](vsubsws.md) β€” word-saturating arithmetic. + +## IBM Reference + +- [AIX 7.3 β€” `vsum2sws` (Vector Sum across Partial (1/2) Saturated Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsum2sws-vector-sum-across-partial-12-saturated-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsum4sbs.md b/tools/ppc-manual/vmx/vsum4sbs.md new file mode 100644 index 00000000..06e5d2e3 --- /dev/null +++ b/tools/ppc-manual/vmx/vsum4sbs.md @@ -0,0 +1,135 @@ +# `vsum4sbs` β€” Vector Sum Across Partial (1/4) Signed Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000708` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsum4sbs` | `vsum4sbs` | β€” | Vector Sum Across Partial (1/4) Signed Byte Saturate | + +## Syntax + +```asm +vsum4sbs [VD], [VA], [VB] +``` + +## Encoding + +### `vsum4sbs` β€” form `VX` + +- **Opcode word:** `0x10000708` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1800` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsum4sbs: read | Source A vector register. | +| `VB` | vsum4sbs: read | Source B vector register. | +| `VD` | vsum4sbs: write | Destination vector register. | +| `VSCR` | vsum4sbs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsum4sbs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsum4sbs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsum4sbs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsum4sbs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1781`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1781) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:127`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L127) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:547`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L547) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3680-3692`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3680-L3692) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsum4sbs => { + let a = crate::vmx::as_i8x16(ctx.vr[instr.ra()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let s = a[4*i] as i64 + a[4*i+1] as i64 + a[4*i+2] as i64 + a[4*i+3] as i64 + c[i] as i64; + let (v, o) = crate::vmx::sat_i64_to_i32(s); + r[i] = v; sat |= o; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word 4-byte partial sum (signed).** For each of the 4 output word lanes, sum 4 signed bytes of `VA` plus the matching signed word of `VB`, then saturate to `int32`. Input byte layout: `VD.w[i] = sat(VA.b[4*i] + VA.b[4*i+1] + VA.b[4*i+2] + VA.b[4*i+3] + VB.w[i])`. +- **Sticky VSCR[SAT]** set on overflow. +- **Typical use: accumulate per-channel sums** (e.g. for a colour-averaging or luminance operator). +- **Big-endian byte / word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsum4shs`](vsum4shs.md) β€” signed half-word variant. +- [`vsum4ubs`](vsum4ubs.md) β€” unsigned byte variant. +- [`vsumsws`](vsumsws.md), [`vsum2sws`](vsum2sws.md) β€” reductions that accumulate fewer output lanes. +- [`vmsummbm`](vmsummbm.md) β€” fused signed-byte multiply-sum (different shape, same "horizontal reduce" flavour). + +## IBM Reference + +- [AIX 7.3 β€” `vsum4sbs` (Vector Sum across Partial (1/4) Saturated Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsum4sbs-vector-sum-across-partial-14-saturated-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsum4shs.md b/tools/ppc-manual/vmx/vsum4shs.md new file mode 100644 index 00000000..d645e282 --- /dev/null +++ b/tools/ppc-manual/vmx/vsum4shs.md @@ -0,0 +1,135 @@ +# `vsum4shs` β€” Vector Sum Across Partial (1/4) Signed Half Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000648` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsum4shs` | `vsum4shs` | β€” | Vector Sum Across Partial (1/4) Signed Half Word Saturate | + +## Syntax + +```asm +vsum4shs [VD], [VA], [VB] +``` + +## Encoding + +### `vsum4shs` β€” form `VX` + +- **Opcode word:** `0x10000648` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1608` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsum4shs: read | Source A vector register. | +| `VB` | vsum4shs: read | Source B vector register. | +| `VD` | vsum4shs: write | Destination vector register. | +| `VSCR` | vsum4shs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsum4shs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsum4shs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsum4shs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsum4shs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1786`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1786) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:127`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L127) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:543`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L543) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3706-3718`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3706-L3718) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsum4shs => { + let a = crate::vmx::as_i16x8(ctx.vr[instr.ra()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let s = a[2*i] as i64 + a[2*i+1] as i64 + c[i] as i64; + let (v, o) = crate::vmx::sat_i64_to_i32(s); + r[i] = v; sat |= o; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word 2-half-word partial sum (signed).** For each of the 4 output word lanes, sum 2 signed half-words of `VA` plus the matching signed word of `VB`, then saturate to `int32`. `VD.w[i] = sat(VA.h[2*i] + VA.h[2*i+1] + VB.w[i])`. +- **Sticky VSCR[SAT]** set on overflow. +- **Useful bridge between 16-bit multiply results and 32-bit accumulators.** Often pairs with `vmulesh` / `vmulosh`. +- **Big-endian half-word / word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsum4sbs`](vsum4sbs.md) β€” signed byte variant. +- [`vsum4ubs`](vsum4ubs.md) β€” unsigned byte variant. +- [`vsum2sws`](vsum2sws.md), [`vsumsws`](vsumsws.md) β€” wider reductions. +- [`vmhaddshs`](vmhaddshs.md), [`vmsumshm`](vmsumshm.md), [`vmsumshs`](vmsumshs.md) β€” fused multiply-sum cousins. + +## IBM Reference + +- [AIX 7.3 β€” `vsum4shs` (Vector Sum across Partial (1/4) Saturated Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsum4shs-vector-sum-across-partial-14-saturated-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsum4ubs.md b/tools/ppc-manual/vmx/vsum4ubs.md new file mode 100644 index 00000000..c6499bab --- /dev/null +++ b/tools/ppc-manual/vmx/vsum4ubs.md @@ -0,0 +1,135 @@ +# `vsum4ubs` β€” Vector Sum Across Partial (1/4) Unsigned Byte Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000608` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsum4ubs` | `vsum4ubs` | β€” | Vector Sum Across Partial (1/4) Unsigned Byte Saturate | + +## Syntax + +```asm +vsum4ubs [VD], [VA], [VB] +``` + +## Encoding + +### `vsum4ubs` β€” form `VX` + +- **Opcode word:** `0x10000608` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1544` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsum4ubs: read | Source A vector register. | +| `VB` | vsum4ubs: read | Source B vector register. | +| `VD` | vsum4ubs: write | Destination vector register. | +| `VSCR` | vsum4ubs: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsum4ubs` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsum4ubs`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsum4ubs`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsum4ubs"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1791`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1791) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:127`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L127) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:540`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L540) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3693-3705`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3693-L3705) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsum4ubs => { + let a = ctx.vr[instr.ra()].as_bytes(); + let c = ctx.vr[instr.rb()].as_u32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let s = a[4*i] as u64 + a[4*i+1] as u64 + a[4*i+2] as u64 + a[4*i+3] as u64 + c[i] as u64; + let (v, o) = if s > u32::MAX as u64 { (u32::MAX, true) } else { (s as u32, false) }; + r[i] = v; sat |= o; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Per-word 4-byte partial sum (unsigned).** For each of the 4 output word lanes, sum 4 unsigned bytes of `VA` plus the matching unsigned word of `VB`, saturating at `uint32::MAX`. `VD.w[i] = sat_u32(VA.b[4*i] + VA.b[4*i+1] + VA.b[4*i+2] + VA.b[4*i+3] + VB.w[i])`. +- **Sticky VSCR[SAT]** set on overflow β€” rare in practice, since `4 * 255 = 1020` plus any 32-bit `VB.w[i]` overflows only when `VB.w[i] > 0xFFFF_FBFF`. +- **Typical "count pixels" / "sum byte channels" primitive.** +- **Big-endian byte / word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** + +## Related Instructions + +- [`vsum4sbs`](vsum4sbs.md) β€” signed byte variant. +- [`vsum4shs`](vsum4shs.md) β€” signed half-word variant. +- [`vsumsws`](vsumsws.md), [`vsum2sws`](vsum2sws.md) β€” wider reductions. +- [`vmsumubm`](vmsumubm.md) β€” fused unsigned-byte multiply-sum. + +## IBM Reference + +- [AIX 7.3 β€” `vsum4ubs` (Vector Sum across Partial (1/4) Saturated Unsigned Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsum4ubs-vector-sum-across-partial-14-saturated-unsigned-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vsumsws.md b/tools/ppc-manual/vmx/vsumsws.md new file mode 100644 index 00000000..169639f5 --- /dev/null +++ b/tools/ppc-manual/vmx/vsumsws.md @@ -0,0 +1,133 @@ +# `vsumsws` β€” Vector Sum Across Signed Word Saturate + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x10000788` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vsumsws` | `vsumsws` | β€” | Vector Sum Across Signed Word Saturate | + +## Syntax + +```asm +vsumsws [VD], [VA], [VB] +``` + +## Encoding + +### `vsumsws` β€” form `VX` + +- **Opcode word:** `0x10000788` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1928` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vsumsws: read | Source A vector register. | +| `VB` | vsumsws: read | Source B vector register. | +| `VD` | vsumsws: write | Destination vector register. | +| `VSCR` | vsumsws: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vsumsws` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vsumsws`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vsumsws`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vsumsws"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1771`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1771) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:127`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L127) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:550`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L550) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3658-3667`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3658-L3667) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vsumsws => { + // vD[3] = sat_i32(vC[3] + sum over i in 0..4 of vA[i]) + let a = crate::vmx::as_i32x4(ctx.vr[instr.ra()]); + let c = crate::vmx::as_i32x4(ctx.vr[instr.rb()]); + let s = a.iter().map(|&x| x as i64).sum::() + c[3] as i64; + let (v, sat) = crate::vmx::sat_i64_to_i32(s); + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4([0, 0, 0, v]); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Full 4-lane reduction (signed word).** Sum all four signed words of `VA` plus `VB.w[3]`; saturate to `int32`. Output: `VD.w[3] = sat(VA.w[0]+VA.w[1]+VA.w[2]+VA.w[3]+VB.w[3])`; `VD.w[0..2] = 0`. +- **Only the last lane is meaningful.** The specification writes zeros into the first three lanes so software cannot accidentally consume them. +- **Sticky VSCR[SAT]** set on overflow. +- **Equivalent to a horizontal add-reduce**, common terminator for a multi-step dot-product or sum-of-products pipeline. +- **Big-endian word lanes.** +- **No `Rc`, no XER.** +- **No VMX128 sibling.** VMX128 instead uses `vmsum3fp128` / `vmsum4fp128` for float dot-products. + +## Related Instructions + +- [`vsum2sws`](vsum2sws.md) β€” two 2-word partial sums. +- [`vsum4sbs`](vsum4sbs.md), [`vsum4shs`](vsum4shs.md), [`vsum4ubs`](vsum4ubs.md) β€” per-word partial sums at narrower input widths. +- [`vmsumshm`](vmsumshm.md), [`vmsumshs`](vmsumshs.md) β€” fused multiply-sum variants. +- [`vmsum3fp128`](../vmx128/vmsum3fp128.md), [`vmsum4fp128`](../vmx128/vmsum4fp128.md) β€” VMX128 float dot-product helpers. + +## IBM Reference + +- [AIX 7.3 β€” `vsumsws` (Vector Sum across Saturated Signed Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vsumsws-vector-sum-across-saturated-signed-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupkhpx.md b/tools/ppc-manual/vmx/vupkhpx.md new file mode 100644 index 00000000..bcead0a4 --- /dev/null +++ b/tools/ppc-manual/vmx/vupkhpx.md @@ -0,0 +1,127 @@ +# `vupkhpx` β€” Vector Unpack High Pixel + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000034e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupkhp` | `vupkhpx` | β€” | Vector Unpack High Pixel | + +## Syntax + +```asm +vupkhpx [VD], [VB] +``` + +## Encoding + +### `vupkhpx` β€” form `VX` + +- **Opcode word:** `0x1000034e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `846` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupkhpx: read | Source B vector register. | +| `VD` | vupkhpx: write | Destination vector register. | + +## Register Effects + +### `vupkhpx` + +- **Reads (always):** `VB` +- **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 + +**`vupkhpx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupkhpx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2002`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2002) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:128`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L128) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:511`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L511) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4168-4174`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4168-L4174) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupkhpx => { + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = crate::vmx::unpack_pixel_555(b[i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unpack the high 4 of 8 pixel half-words.** The upper half-words of `VB` (`VB.h[0..3]`) are each decoded from 1-5-5-5 pixel format into a 32-bit word (`1.5.5.5 β†’ 1.8.8.8` with sign-extension on the alpha bit and zero-extension of each colour channel into the high 5 bits of a byte). Xenia uses `vmx::unpack_pixel_555`. +- **Output layout.** `VD.w[0..3]` receive the 4 decoded pixels in big-endian order. +- **Inverse of the high half of [`vpkpx`](vpkpx.md).** Unpacking loses no information beyond what the 1-5-5-5 format allows. +- **No saturation, no flags, no VSCR.** +- **No VMX128 sibling.** VMX128 code uses [`vupkd3d128`](../vmx128/vupkd3d128.md) for richer D3D formats. + +## Related Instructions + +- [`vupklpx`](vupklpx.md) β€” unpack the low 4 pixel half-words. +- [`vpkpx`](vpkpx.md) β€” the inverse pack. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md) β€” byte β†’ half-word sign-extending unpacks. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” half-word β†’ word sign-extending unpacks. +- [`vupkd3d128`](../vmx128/vupkd3d128.md) β€” VMX128 D3D-format unpack. + +## IBM Reference + +- [AIX 7.3 β€” `vupkhpx` (Vector Unpack High Pixel16)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupkhpx-vector-unpack-high-pixel16-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupkhsb.md b/tools/ppc-manual/vmx/vupkhsb.md new file mode 100644 index 00000000..5bb90cad --- /dev/null +++ b/tools/ppc-manual/vmx/vupkhsb.md @@ -0,0 +1,181 @@ +# `vupkhsb` β€” Vector Unpack High Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000020e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupkhsb` | `vupkhsb` | β€” | Vector Unpack High Signed Byte | +| `vupkhsb128` | `vupkhsb128` | β€” | Vector128 Unpack High Signed Byte | + +## Syntax + +```asm +vupkhsb [VD], [VB] +vupkhsb128 [VD], [VB] +``` + +## Encoding + +### `vupkhsb` β€” form `VX` + +- **Opcode word:** `0x1000020e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `526` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vupkhsb128` β€” form `VX128` + +- **Opcode word:** `0x18000380` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `896` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupkhsb: read; vupkhsb128: read | Source B vector register. | +| `VD` | vupkhsb: write; vupkhsb128: write | Destination vector register. | + +## Register Effects + +### `vupkhsb` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vupkhsb128` + +- **Reads (always):** `VB` +- **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 + +**`vupkhsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupkhsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2055`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2055) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:128`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L128) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:481`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L481) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4134-4143`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4134-L4143) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupkhsb | PpcOpcode::vupkhsb128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vupkhsb128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = crate::vmx::as_i8x16(ctx.vr[rb]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = b[i] as i16; } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ +**`vupkhsb128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupkhsb128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2058`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2058) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:128`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L128) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:700`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L700) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4134-4143`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4134-L4143) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupkhsb | PpcOpcode::vupkhsb128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vupkhsb128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = crate::vmx::as_i8x16(ctx.vr[rb]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = b[i] as i16; } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extend the high 8 of 16 bytes into half-words.** `VB.b[0..7]` are each reinterpreted as `int8` and sign-extended to `int16` in `VD.h[0..7]`. +- **Inverse of the high half of [`vpkshss`](vpkshss.md) / [`vpkshus`](vpkshus.md)** (within the `int8` range β€” larger `int16`s cannot survive a round-trip through a saturating pack). +- **Big-endian lane ordering** β€” `VB.b[0]` becomes `VD.h[0]`. +- **No saturation, no flags, no VSCR effect.** +- **VMX128 sibling [`vupkhsb128`](vupkhsb128.md).** + +## Related Instructions + +- [`vupklsb`](vupklsb.md) β€” unpacks the low 8 bytes (same sign-extension). +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” half-word β†’ word sign-extending unpacks. +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md) β€” the inverse pack (saturating). +- [`vupkhpx`](vupkhpx.md), [`vupklpx`](vupklpx.md) β€” pixel-format unpacks. + +## IBM Reference + +- [AIX 7.3 β€” `vupkhsb` (Vector Unpack High Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupkhsb-vector-unpack-high-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupkhsh.md b/tools/ppc-manual/vmx/vupkhsh.md new file mode 100644 index 00000000..89966252 --- /dev/null +++ b/tools/ppc-manual/vmx/vupkhsh.md @@ -0,0 +1,125 @@ +# `vupkhsh` β€” Vector Unpack High Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000024e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupkhsh` | `vupkhsh` | β€” | Vector Unpack High Signed Half Word | + +## Syntax + +```asm +vupkhsh [VD], [VB] +``` + +## Encoding + +### `vupkhsh` β€” form `VX` + +- **Opcode word:** `0x1000024e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `590` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupkhsh: read | Source B vector register. | +| `VD` | vupkhsh: write | Destination vector register. | + +## Register Effects + +### `vupkhsh` + +- **Reads (always):** `VB` +- **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 + +**`vupkhsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupkhsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2021`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2021) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:128`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L128) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:488`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L488) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4154-4160`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4154-L4160) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupkhsh => { + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = b[i] as i32; } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extend the high 4 of 8 half-words into words.** `VB.h[0..3]` are each reinterpreted as `int16` and sign-extended to `int32` in `VD.w[0..3]`. +- **Inverse of the high half of [`vpkswss`](vpkswss.md)** (within the `int16` range). +- **Big-endian lane ordering.** +- **No saturation, no flags, no VSCR effect.** +- **VMX128 sibling [`vupkhsh128`](vupkhsh128.md).** + +## Related Instructions + +- [`vupklsh`](vupklsh.md) β€” unpacks the low 4 half-words. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md) β€” byte β†’ half-word sign-extending unpacks. +- [`vpkswss`](vpkswss.md), [`vpkswus`](vpkswus.md) β€” the inverse pack (saturating). + +## IBM Reference + +- [AIX 7.3 β€” `vupkhsh` (Vector Unpack High Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupkhsh-vector-unpack-high-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupklpx.md b/tools/ppc-manual/vmx/vupklpx.md new file mode 100644 index 00000000..fb5c3469 --- /dev/null +++ b/tools/ppc-manual/vmx/vupklpx.md @@ -0,0 +1,126 @@ +# `vupklpx` β€” Vector Unpack Low Pixel + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100003ce` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupklp` | `vupklpx` | β€” | Vector Unpack Low Pixel | + +## Syntax + +```asm +vupklpx [VD], [VB] +``` + +## Encoding + +### `vupklpx` β€” form `VX` + +- **Opcode word:** `0x100003ce` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `974` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupklpx: read | Source B vector register. | +| `VD` | vupklpx: write | Destination vector register. | + +## Register Effects + +### `vupklpx` + +- **Reads (always):** `VB` +- **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 + +**`vupklpx`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupklpx"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2007`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2007) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:129`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L129) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:518`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L518) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4175-4181`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4175-L4181) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupklpx => { + let b = ctx.vr[instr.rb()].as_u16x8(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = crate::vmx::unpack_pixel_555(b[4 + i]); } + ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unpack the low 4 of 8 pixel half-words.** The lower half-words of `VB` (`VB.h[4..7]`) are each decoded from 1-5-5-5 pixel format into a 32-bit word (`1.5.5.5 β†’ 1.8.8.8`). Xenia uses `vmx::unpack_pixel_555`. +- **Output layout.** `VD.w[0..3]` receive the 4 decoded pixels in big-endian order. +- **Inverse of the low half of [`vpkpx`](vpkpx.md).** +- **No saturation, no flags, no VSCR.** +- **No VMX128 sibling.** VMX128 uses [`vupkd3d128`](../vmx128/vupkd3d128.md) for richer formats. + +## Related Instructions + +- [`vupkhpx`](vupkhpx.md) β€” high-half pixel unpack. +- [`vpkpx`](vpkpx.md) β€” the inverse pack. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md), [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” sign-extending unpacks. +- [`vupkd3d128`](../vmx128/vupkd3d128.md) β€” VMX128 D3D-format unpack. + +## IBM Reference + +- [AIX 7.3 β€” `vupklpx` (Vector Unpack Low Pixel16)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupklpx-vector-unpack-low-pixel16-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupklsb.md b/tools/ppc-manual/vmx/vupklsb.md new file mode 100644 index 00000000..06d87474 --- /dev/null +++ b/tools/ppc-manual/vmx/vupklsb.md @@ -0,0 +1,181 @@ +# `vupklsb` β€” Vector Unpack Low Signed Byte + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x1000028e` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupklsb` | `vupklsb` | β€” | Vector Unpack Low Signed Byte | +| `vupklsb128` | `vupklsb128` | β€” | Vector128 Unpack Low Signed Byte | + +## Syntax + +```asm +vupklsb [VD], [VB] +vupklsb128 [VD], [VB] +``` + +## Encoding + +### `vupklsb` β€” form `VX` + +- **Opcode word:** `0x1000028e` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `654` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vupklsb128` β€” form `VX128` + +- **Opcode word:** `0x180003c0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `960` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupklsb: read; vupklsb128: read | Source B vector register. | +| `VD` | vupklsb: write; vupklsb128: write | Destination vector register. | + +## Register Effects + +### `vupklsb` + +- **Reads (always):** `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vupklsb128` + +- **Reads (always):** `VB` +- **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 + +**`vupklsb`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupklsb"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2076`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2076) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:129`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L129) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:494`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L494) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4144-4153`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4144-L4153) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupklsb | PpcOpcode::vupklsb128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vupklsb128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = crate::vmx::as_i8x16(ctx.vr[rb]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = b[8 + i] as i16; } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ +**`vupklsb128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupklsb128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2079`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2079) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:129`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L129) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:701`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L701) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4144-4153`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4144-L4153) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupklsb | PpcOpcode::vupklsb128 => { + let is_128 = matches!(instr.opcode, PpcOpcode::vupklsb128); + let (rb, rd) = if is_128 { (instr.vb128(), instr.vd128()) } + else { (instr.rb(), instr.rd()) }; + let b = crate::vmx::as_i8x16(ctx.vr[rb]); + let mut r = [0i16; 8]; + for i in 0..8 { r[i] = b[8 + i] as i16; } + ctx.vr[rd] = crate::vmx::from_i16x8(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extend the low 8 of 16 bytes into half-words.** `VB.b[8..15]` are each reinterpreted as `int8` and sign-extended to `int16` in `VD.h[0..7]`. +- **Inverse of the low half of [`vpkshss`](vpkshss.md) / [`vpkshus`](vpkshus.md)** (within the `int8` range). +- **Big-endian lane ordering.** +- **No saturation, no flags, no VSCR effect.** +- **VMX128 sibling [`vupklsb128`](vupklsb128.md).** + +## Related Instructions + +- [`vupkhsb`](vupkhsb.md) β€” high-half byte unpack. +- [`vupkhsh`](vupkhsh.md), [`vupklsh`](vupklsh.md) β€” half-word β†’ word unpacks. +- [`vpkshss`](vpkshss.md), [`vpkshus`](vpkshus.md) β€” the inverse packs. +- [`vupkhpx`](vupkhpx.md), [`vupklpx`](vupklpx.md) β€” pixel-format unpacks. + +## IBM Reference + +- [AIX 7.3 β€” `vupklsb` (Vector Unpack Low Signed Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupklsb-vector-unpack-low-signed-byte-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vupklsh.md b/tools/ppc-manual/vmx/vupklsh.md new file mode 100644 index 00000000..a8659993 --- /dev/null +++ b/tools/ppc-manual/vmx/vupklsh.md @@ -0,0 +1,125 @@ +# `vupklsh` β€” Vector Unpack Low Signed Half Word + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100002ce` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupklsh` | `vupklsh` | β€” | Vector Unpack Low Signed Half Word | + +## Syntax + +```asm +vupklsh [VD], [VB] +``` + +## Encoding + +### `vupklsh` β€” form `VX` + +- **Opcode word:** `0x100002ce` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `718` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupklsh: read | Source B vector register. | +| `VD` | vupklsh: write | Destination vector register. | + +## Register Effects + +### `vupklsh` + +- **Reads (always):** `VB` +- **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 + +**`vupklsh`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupklsh"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2038`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2038) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:129`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L129) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:497`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L497) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4161-4167`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4161-L4167) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupklsh => { + let b = crate::vmx::as_i16x8(ctx.vr[instr.rb()]); + let mut r = [0i32; 4]; + for i in 0..4 { r[i] = b[4 + i] as i32; } + ctx.vr[instr.rd()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Sign-extend the low 4 of 8 half-words into words.** `VB.h[4..7]` are each reinterpreted as `int16` and sign-extended to `int32` in `VD.w[0..3]`. +- **Inverse of the low half of [`vpkswss`](vpkswss.md)** (within the `int16` range). +- **Big-endian lane ordering.** +- **No saturation, no flags, no VSCR effect.** +- **VMX128 sibling [`vupklsh128`](vupklsh128.md).** + +## Related Instructions + +- [`vupkhsh`](vupkhsh.md) β€” high-half half-word unpack. +- [`vupkhsb`](vupkhsb.md), [`vupklsb`](vupklsb.md) β€” byte β†’ half-word unpacks. +- [`vpkswss`](vpkswss.md), [`vpkswus`](vpkswus.md) β€” the inverse packs. + +## IBM Reference + +- [AIX 7.3 β€” `vupklsh` (Vector Unpack Low Signed Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vupklsh-vector-unpack-low-signed-half-word-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx/vxor.md b/tools/ppc-manual/vmx/vxor.md new file mode 100644 index 00000000..acba949a --- /dev/null +++ b/tools/ppc-manual/vmx/vxor.md @@ -0,0 +1,180 @@ +# `vxor` β€” Vector Logical XOR + +> **Category:** [VMX (Altivec)](../categories/vmx.md) Β· **Form:** [VX](../forms/VX.md) Β· **Opcode:** `0x100004c4` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vxor` | `vxor` | β€” | Vector Logical XOR | +| `vxor128` | `vxor128` | β€” | Vector128 Logical XOR | + +## Syntax + +```asm +vxor [VD], [VA], [VB] +vxor128 [VD], [VA], [VB] +``` + +## Encoding + +### `vxor` β€” form `VX` + +- **Opcode word:** `0x100004c4` +- **Primary opcode (bits 0–5):** `4` +- **Extended opcode:** `1220` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4) | +| 6–10 | `VRT/VD` | destination vector register | +| 11–15 | `VRA/VA` | source A vector register | +| 16–20 | `VRB/VB` | source B vector register | +| 21–31 | `XO` | extended opcode (11 bits) | + +### `vxor128` β€” form `VX128` + +- **Opcode word:** `0x14000310` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `784` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vxor: read; vxor128: read | Source A vector register. | +| `VB` | vxor: read; vxor128: read | Source B vector register. | +| `VD` | vxor: write; vxor128: write | Destination vector register. | + +## Register Effects + +### `vxor` + +- **Reads (always):** `VA`, `VB` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD` +- **Writes (conditional):** _none_ + +### `vxor128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vxor`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vxor"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2246`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2246) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:130`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L130) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:532`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L532) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2235-2243`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2235-L2243) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vxor | PpcOpcode::vxor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] ^ b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ +**`vxor128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vxor128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2249`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2249) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:130`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L130) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:627`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L627) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2235-2243`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2235-L2243) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vxor | PpcOpcode::vxor128 => { + let (va, vb, vd) = vmx_reg_triple(instr); + let a = ctx.vr[va].as_u32x4(); + let b = ctx.vr[vb].as_u32x4(); + let mut r = [0u32; 4]; + for i in 0..4 { r[i] = a[i] ^ b[i]; } + ctx.vr[vd] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Bitwise XOR across the full 128-bit register.** Lane-agnostic. +- **`vxor VD, VD, VD` is the canonical "vector zero" idiom.** Every Xenon compiler uses this to materialise the all-zero vector; xenia-rs's interpreter does not special-case it (it still reads the register), but the JIT / translator can fold it at emit time. +- **Aliasing legal.** `vxor v3, v3, v4` toggles bits from `v4` into `v3`. +- **No flags, no VSCR.** +- **VMX128 sibling [`vxor128`](vxor128.md).** Identical semantics; wider register file. +- **Compare-then-XOR** is the cheapest "mask flip" when `vnor` is not wanted (e.g. to toggle only certain bits, not every bit). + +## Related Instructions + +- [`vor`](vor.md), [`vand`](vand.md), [`vandc`](vandc.md), [`vnor`](vnor.md) β€” the rest of the Altivec boolean primitives. +- [`vsel`](vsel.md) β€” three-input bit-select; equivalent to `(VA & ~VC) | (VB & VC)`. +- [`vcmpequb`](vcmpequb.md), [`vcmpequh`](vcmpequh.md), [`vcmpequw`](vcmpequw.md) β€” produce masks XORed against. + +## IBM Reference + +- [AIX 7.3 β€” `vxor` (Vector Logical XOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-vxor-vector-logical-xor-instruction) +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 3 β€” Logical Operations](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) diff --git a/tools/ppc-manual/vmx128/vcfpsxws128.md b/tools/ppc-manual/vmx128/vcfpsxws128.md new file mode 100644 index 00000000..8d62be8a --- /dev/null +++ b/tools/ppc-manual/vmx128/vcfpsxws128.md @@ -0,0 +1,137 @@ +# `vcfpsxws128` β€” Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_3](../forms/VX128_3.md) Β· **Opcode:** `0x18000230` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcfpsxws128` | `vcfpsxws128` | β€” | Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate | + +## Syntax + +```asm +vcfpsxws128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcfpsxws128` β€” form `VX128_3` + +- **Opcode word:** `0x18000230` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `560` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcfpsxws128: read | Source B vector register. | +| `UIMM` | vcfpsxws128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcfpsxws128: write | Destination vector register. | +| `VSCR` | vcfpsxws128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vcfpsxws128` + +- **Reads (always):** `VB`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vcfpsxws128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vcfpsxws128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcfpsxws128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:539`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L539) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:93`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L93) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:656`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L656) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4323-4334`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4323-L4334) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcfpsxws128 => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0i32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::cvt_f32_to_i32_sat(b[i], uimm); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.vd128()] = crate::vmx::from_i32x4(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Float β†’ signed fixed-point (int32) with explicit scale.** Each lane computes `VD.w[i] = sat_int32(VB[i] * 2^UIMM)`, truncating toward zero and clamping to `[βˆ’2^31, 2^31βˆ’1]`. `UIMM` is a 5-bit unsigned bias (range 0..31) that specifies a power-of-two pre-scale on the float value. +- **Use case: fixed-point pipelines.** The `UIMM` pre-scale lets game code convert a `[0.0, 1.0]` float channel into a `uint16`-range fixed-point value in one instruction (e.g. `UIMM = 15` β†’ scale by 32768). +- **Sticky VSCR[SAT]** set whenever a lane clamps (including NaN inputs, which xenia's `cvt_f32_to_i32_sat` treats as 0 and flags saturation). +- **`VSCR[NJ]` honoured** on the float input side. +- **VMX128 register-fusion** applies to `VD` and `VB`: 7-bit register IDs via `VD128l β€– VD128h` and `VB128l β€– VB128h`. +- **No IBM AIX entry** β€” this is Xenon-only. The closest standard Altivec op is [`vctsxs`](../vmx/vctsxs.md). +- **No `Rc`, no XER / FPSCR.** + +## Related Instructions + +- [`vctsxs`](../vmx/vctsxs.md) β€” the standard Altivec equivalent (same semantics, 32-register file). +- [`vcfpuxws128`](vcfpuxws128.md) β€” unsigned variant (clamps to `uint32`). +- [`vcsxwfp128`](vcsxwfp128.md), [`vcuxwfp128`](vcuxwfp128.md) β€” the inverse (int β†’ float with scale). +- [`vrfiz`](../vmx/vrfiz.md) β€” plain truncate-to-float-integer without scale. + +## IBM Reference + +- No IBM AIX entry β€” this instruction is exclusive to the Xbox 360's VMX128 extension. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions (Microsoft internal documentation); semantics cross-referenced with [IBM AltiVec Technology Programmer's Interface Manual Β§`vctsxs`](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf). diff --git a/tools/ppc-manual/vmx128/vcfpuxws128.md b/tools/ppc-manual/vmx128/vcfpuxws128.md new file mode 100644 index 00000000..dfc183ec --- /dev/null +++ b/tools/ppc-manual/vmx128/vcfpuxws128.md @@ -0,0 +1,137 @@ +# `vcfpuxws128` β€” Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_3](../forms/VX128_3.md) Β· **Opcode:** `0x18000270` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcfpuxws128` | `vcfpuxws128` | β€” | Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate | + +## Syntax + +```asm +vcfpuxws128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcfpuxws128` β€” form `VX128_3` + +- **Opcode word:** `0x18000270` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `624` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcfpuxws128: read | Source B vector register. | +| `UIMM` | vcfpuxws128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcfpuxws128: write | Destination vector register. | +| `VSCR` | vcfpuxws128: write | Vector Status and Control Register (NJ/SAT bits). | + +## Register Effects + +### `vcfpuxws128` + +- **Reads (always):** `VB`, `UIMM` +- **Reads (conditional):** _none_ +- **Writes (always):** `VD`, `VSCR` +- **Writes (conditional):** _none_ + +## Status-Register Effects + +- `vcfpuxws128`: **VSCR[SAT]** may be stickied on saturating vector operations. + +## 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 + +**`vcfpuxws128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcfpuxws128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:557`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L557) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:93`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L93) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:657`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L657) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4335-4346`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4335-L4346) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcfpuxws128 => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0u32; 4]; let mut sat = false; + for i in 0..4 { + let (v, s) = crate::vmx::cvt_f32_to_u32_sat(b[i], uimm); + r[i] = v; sat |= s; + } + if sat { ctx.set_vscr_sat(true); } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Float β†’ unsigned fixed-point (uint32) with explicit scale.** Each lane computes `VD.w[i] = sat_uint32(VB[i] * 2^UIMM)`, truncating toward zero and clamping to `[0, 2^32βˆ’1]`. `UIMM` is a 5-bit unsigned bias (range 0..31). +- **Negative floats clamp to 0** and sticky-set `VSCR[SAT]`. +- **NaN inputs** β†’ 0 with `VSCR[SAT]` set (xenia's `cvt_f32_to_u32_sat`). +- **`VSCR[NJ]` honoured** for denormal inputs. +- **VMX128 register-fusion** applies to `VD` and `VB` (7-bit IDs). +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER / FPSCR.** + +## Related Instructions + +- [`vctuxs`](../vmx/vctuxs.md) β€” the standard Altivec equivalent (uint32 clamp with scale). +- [`vcfpsxws128`](vcfpsxws128.md) β€” signed variant. +- [`vcuxwfp128`](vcuxwfp128.md) β€” the inverse (uint β†’ float with scale). +- [`vrfiz`](../vmx/vrfiz.md) β€” plain truncate-to-integer-float without scale. + +## IBM Reference + +- No IBM AIX entry β€” this instruction is exclusive to the Xbox 360's VMX128 extension. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions (Microsoft internal documentation); cross-referenced with [IBM AltiVec Technology Programmer's Interface Manual Β§`vctuxs`](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf). diff --git a/tools/ppc-manual/vmx128/vcsxwfp128.md b/tools/ppc-manual/vmx128/vcsxwfp128.md new file mode 100644 index 00000000..4eb13030 --- /dev/null +++ b/tools/ppc-manual/vmx128/vcsxwfp128.md @@ -0,0 +1,131 @@ +# `vcsxwfp128` β€” Vector128 Convert From Signed Fixed-Point Word to Floating-Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_3](../forms/VX128_3.md) Β· **Opcode:** `0x180002b0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcsxwfp128` | `vcsxwfp128` | β€” | Vector128 Convert From Signed Fixed-Point Word to Floating-Point | + +## Syntax + +```asm +vcsxwfp128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcsxwfp128` β€” form `VX128_3` + +- **Opcode word:** `0x180002b0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `688` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcsxwfp128: read | Source B vector register. | +| `UIMM` | vcsxwfp128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcsxwfp128: write | Destination vector register. | + +## Register Effects + +### `vcsxwfp128` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vcsxwfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcsxwfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:503`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L503) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:98`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L98) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:658`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L658) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4347-4354`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4347-L4354) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcsxwfp128 => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = crate::vmx::as_i32x4(ctx.vr[instr.vb128()]); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = crate::vmx::cvt_i32_to_f32(b[i], uimm); } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Signed fixed-point (int32) β†’ float with explicit scale.** Each lane computes `VD[i] = (float)VB.w[i] * 2^-UIMM` (equivalently `(int32)VB.w[i] / 2^UIMM`). `UIMM` is a 5-bit unsigned bias that specifies a post-scale β€” the inverse direction of [`vcfpsxws128`](vcfpsxws128.md), so the `UIMM`s should match for a round-trip. +- **IEEE-754 binary32 output, round-to-nearest.** Values outside the exactly-representable range (`|x| > 2^24`) lose low-order bits; no saturation on the float side. +- **No `VSCR[SAT]` effect** β€” conversion in this direction never saturates. +- **`VSCR[NJ]` does not affect the int β†’ float path.** +- **VMX128 register-fusion** applies (7-bit register IDs). +- **No IBM AIX entry** β€” Xenon-only. Closest standard Altivec op is [`vcfsx`](../vmx/vcfsx.md). +- **No `Rc`, no XER / FPSCR.** + +## Related Instructions + +- [`vcfsx`](../vmx/vcfsx.md) β€” the standard Altivec `int32 β†’ float` with scale. +- [`vcuxwfp128`](vcuxwfp128.md) β€” unsigned-int variant. +- [`vcfpsxws128`](vcfpsxws128.md), [`vcfpuxws128`](vcfpuxws128.md) β€” the inverse (float β†’ int with scale). + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions; cross-referenced with [IBM AltiVec Technology Programmer's Interface Manual Β§`vcfsx`](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf). diff --git a/tools/ppc-manual/vmx128/vcuxwfp128.md b/tools/ppc-manual/vmx128/vcuxwfp128.md new file mode 100644 index 00000000..99e15425 --- /dev/null +++ b/tools/ppc-manual/vmx128/vcuxwfp128.md @@ -0,0 +1,131 @@ +# `vcuxwfp128` β€” Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_3](../forms/VX128_3.md) Β· **Opcode:** `0x180002f0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vcuxwfp128` | `vcuxwfp128` | β€” | Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point | + +## Syntax + +```asm +vcuxwfp128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vcuxwfp128` β€” form `VX128_3` + +- **Opcode word:** `0x180002f0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `752` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vcuxwfp128: read | Source B vector register. | +| `UIMM` | vcuxwfp128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vcuxwfp128: write | Destination vector register. | + +## Register Effects + +### `vcuxwfp128` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vcuxwfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vcuxwfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:521`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L521) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:98`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L98) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:659`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L659) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4355-4362`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4355-L4362) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vcuxwfp128 => { + let uimm = (instr.raw >> 16) & 0x1F; + let b = ctx.vr[instr.vb128()].as_u32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { r[i] = crate::vmx::cvt_u32_to_f32(b[i], uimm); } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unsigned fixed-point (uint32) β†’ float with explicit scale.** Each lane computes `VD[i] = (float)VB.w[i] * 2^-UIMM`. Treats the 32-bit input as unsigned, so values β‰₯ `0x80000000` produce positive floats (unlike `vcsxwfp128` which would produce negatives). +- **IEEE-754 binary32 output, round-to-nearest.** Precision loss above `2^24`. +- **No `VSCR[SAT]` effect.** +- **`VSCR[NJ]` does not affect the uint β†’ float path.** +- **VMX128 register-fusion** applies. +- **No IBM AIX entry** β€” Xenon-only. Closest standard is [`vcfux`](../vmx/vcfux.md). +- **No `Rc`, no XER / FPSCR.** + +## Related Instructions + +- [`vcfux`](../vmx/vcfux.md) β€” the standard Altivec `uint32 β†’ float` with scale. +- [`vcsxwfp128`](vcsxwfp128.md) β€” signed-int variant. +- [`vcfpuxws128`](vcfpuxws128.md), [`vcfpsxws128`](vcfpsxws128.md) β€” the inverse (float β†’ int with scale). + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions; cross-referenced with [IBM AltiVec Technology Programmer's Interface Manual Β§`vcfux`](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf). diff --git a/tools/ppc-manual/vmx128/vmaddcfp128.md b/tools/ppc-manual/vmx128/vmaddcfp128.md new file mode 100644 index 00000000..1db1a621 --- /dev/null +++ b/tools/ppc-manual/vmx128/vmaddcfp128.md @@ -0,0 +1,148 @@ +# `vmaddcfp128` β€” Vector128 Multiply Add Floating Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128](../forms/VX128.md) Β· **Opcode:** `0x14000110` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmaddcfp128` | `vmaddcfp128` | β€” | Vector128 Multiply Add Floating Point | + +## Syntax + +```asm +vmaddcfp128 [VD], [VA], [VD], [VB] +``` + +## Encoding + +### `vmaddcfp128` β€” form `VX128` + +- **Opcode word:** `0x14000110` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `272` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmaddcfp128: read | Source A vector register. | +| `VD` | vmaddcfp128: read; vmaddcfp128: write | Destination vector register. | +| `VB` | vmaddcfp128: read | Source B vector register. | + +## Register Effects + +### `vmaddcfp128` + +- **Reads (always):** `VA`, `VD`, `VB` +- **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 + +**`vmaddcfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmaddcfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:812`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L812) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:100`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L100) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:614`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L614) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4492-4509`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4492-L4509) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmaddcfp128 => { + // ISA: (VD) <- (VA Γ— VD) + VB. Canary InstrEmit_vmaddcfp128 (cc:819): MulAdd(VA, VD, VB). + // Previous code computed di.mul_add(bi, ai) = VDΓ—VB+VA β€” both operands wrong + // (PPCBUG-425). Fix: ai.mul_add(di, bi) = VAΓ—VD+VB. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let d = ctx.vr[instr.vd128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + let di = vmx::flush_denorm(d[i]); + // PPCBUG-437: flush subnormal output too. + r[i] = vmx::flush_denorm(ai.mul_add(di, bi)); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Xbox-specific fused multiply-add variant.** Each lane computes `VD[i] = VD[i] * VB[i] + VA[i]` β€” note that `VD` is both source and destination (xenia reads `VD` first, then writes). This is *not* the standard [`vmaddfp`](../vmx/vmaddfp.md) operand order: the "addend" position is `VA`, the other factor is `VB`, and `VD` carries the on-going accumulator. The mnemonic's trailing `c` denotes "accumulator-in-VD" rather than a separate `VC` operand. +- **Fused, single-rounding.** Xenia uses `f32::mul_add`, which maps to a host FMA instruction when available. Bit-for-bit result depends on host support; xenia-canary's LLVM path emits the equivalent IR node. +- **IEEE-754 binary32 lanes; `VSCR[NJ]` honoured.** +- **No VSCR[SAT], no FPSCR update.** +- **NaN propagation** per IEEE-754. +- **VMX128 register-fusion** (7-bit IDs on `VA`, `VB`, `VD`). +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vmaddfp`](../vmx/vmaddfp.md), [`vmaddfp128`](../vmx/vmaddfp.md) β€” standard fused `(VA Γ— VC) + VB`. +- [`vmulfp128`](vmulfp128.md) β€” plain lane-wise float multiply. +- [`vnmsubfp`](../vmx/vnmsubfp.md) β€” negative-multiply-subtract. +- [`vmsum3fp128`](vmsum3fp128.md), [`vmsum4fp128`](vmsum4fp128.md) β€” dot-product reductions. + +## IBM Reference + +- No IBM AIX entry β€” this is an Xbox 360 VMX128 extension. Its semantics differ from the base Altivec [`vmaddfp`](../vmx/vmaddfp.md) in the operand order (accumulator in `VD`, not `VC`). +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for the base FMA semantics. diff --git a/tools/ppc-manual/vmx128/vmsum3fp128.md b/tools/ppc-manual/vmx128/vmsum3fp128.md new file mode 100644 index 00000000..66c50fc8 --- /dev/null +++ b/tools/ppc-manual/vmx128/vmsum3fp128.md @@ -0,0 +1,141 @@ +# `vmsum3fp128` β€” Vector128 Multiply Sum 3-way Floating Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128](../forms/VX128.md) Β· **Opcode:** `0x14000190` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsum3fp128` | `vmsum3fp128` | β€” | Vector128 Multiply Sum 3-way Floating Point | + +## Syntax + +```asm +vmsum3fp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmsum3fp128` β€” form `VX128` + +- **Opcode word:** `0x14000190` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `400` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsum3fp128: read | Source A vector register. | +| `VB` | vmsum3fp128: read | Source B vector register. | +| `VD` | vmsum3fp128: write | Destination vector register. | + +## Register Effects + +### `vmsum3fp128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmsum3fp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsum3fp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1067`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1067) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:106`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L106) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:616`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L616) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4513-4523`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4513-L4523) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsum3fp128 => { + // PPCBUG-436: flush per-product intermediates (not just the final sum). + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let p0 = vmx::flush_denorm(a[0] * b[0]); + let p1 = vmx::flush_denorm(a[1] * b[1]); + let p2 = vmx::flush_denorm(a[2] * b[2]); + let s = vmx::flush_denorm(p0 + p1 + p2); + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4(s, s, s, s); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **3-way float dot product.** Computes `s = VA[0]*VB[0] + VA[1]*VB[1] + VA[2]*VB[2]` (ignoring lane 3 β€” the "w" component of a homogeneous vector) and **broadcasts `s` to every lane of `VD`**. Typical call site: 3D vector dot products where the w-component is padding. +- **Scalar-result-splatted-across-lanes.** Consuming code can then use any lane of `VD` as the dot-product result. +- **Rounding.** Xenia performs two adds in sequence (no fused triple-add in Rust). The order matches the spec but the summation order affects round-off by ~1 ulp. Games that need deterministic cross-host behaviour typically pre-scale their inputs. +- **IEEE-754 binary32; `VSCR[NJ]` honoured.** +- **No VSCR[SAT], no FPSCR update.** +- **VMX128 register-fusion** (7-bit IDs on `VA`, `VB`, `VD`). +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vmsum4fp128`](vmsum4fp128.md) β€” 4-way dot-product (includes the w-lane). +- [`vmulfp128`](vmulfp128.md), [`vaddfp`](../vmx/vaddfp.md) β€” the building blocks. +- [`vmaddcfp128`](vmaddcfp128.md), [`vmaddfp`](../vmx/vmaddfp.md) β€” fused MAC variants. +- [`vsumsws`](../vmx/vsumsws.md) β€” integer sum-reduction analogue. + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. A 3-way dot product is a direct mirror of D3D9's `float3 dot`. +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for the base float arithmetic semantics. diff --git a/tools/ppc-manual/vmx128/vmsum4fp128.md b/tools/ppc-manual/vmx128/vmsum4fp128.md new file mode 100644 index 00000000..84638d53 --- /dev/null +++ b/tools/ppc-manual/vmx128/vmsum4fp128.md @@ -0,0 +1,142 @@ +# `vmsum4fp128` β€” Vector128 Multiply Sum 4-way Floating-Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128](../forms/VX128.md) Β· **Opcode:** `0x140001d0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmsum4fp128` | `vmsum4fp128` | β€” | Vector128 Multiply Sum 4-way Floating-Point | + +## Syntax + +```asm +vmsum4fp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmsum4fp128` β€” form `VX128` + +- **Opcode word:** `0x140001d0` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `464` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmsum4fp128: read | Source A vector register. | +| `VB` | vmsum4fp128: read | Source B vector register. | +| `VD` | vmsum4fp128: write | Destination vector register. | + +## Register Effects + +### `vmsum4fp128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmsum4fp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmsum4fp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1077`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1077) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:106`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L106) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:617`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L617) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4524-4535`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4524-L4535) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmsum4fp128 => { + // PPCBUG-436. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let p0 = vmx::flush_denorm(a[0] * b[0]); + let p1 = vmx::flush_denorm(a[1] * b[1]); + let p2 = vmx::flush_denorm(a[2] * b[2]); + let p3 = vmx::flush_denorm(a[3] * b[3]); + let s = vmx::flush_denorm(p0 + p1 + p2 + p3); + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4(s, s, s, s); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **4-way float dot product.** Computes `s = VA[0]*VB[0] + VA[1]*VB[1] + VA[2]*VB[2] + VA[3]*VB[3]` (the full xyzw dot) and **broadcasts `s` to every lane of `VD`**. +- **Scalar-result-splatted-across-lanes.** Direct mirror of HLSL/GLSL's `float4 dot`. +- **Rounding.** Three sequential adds; round-off order affects result by ~1 ulp. Not an FMA in xenia. +- **IEEE-754 binary32; `VSCR[NJ]` honoured.** +- **No VSCR[SAT], no FPSCR update.** +- **VMX128 register-fusion** (7-bit IDs on `VA`, `VB`, `VD`). +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vmsum3fp128`](vmsum3fp128.md) β€” 3-way dot-product (ignores the w-lane). +- [`vmulfp128`](vmulfp128.md), [`vaddfp`](../vmx/vaddfp.md) β€” the building blocks. +- [`vmaddcfp128`](vmaddcfp128.md), [`vmaddfp`](../vmx/vmaddfp.md) β€” fused MAC variants. +- [`vsumsws`](../vmx/vsumsws.md) β€” integer sum-reduction analogue. + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. Directly mirrors D3D9's `float4 dot`. +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 5 β€” Floating-Point Arithmetic](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for base float semantics. diff --git a/tools/ppc-manual/vmx128/vmulfp128.md b/tools/ppc-manual/vmx128/vmulfp128.md new file mode 100644 index 00000000..1816a41e --- /dev/null +++ b/tools/ppc-manual/vmx128/vmulfp128.md @@ -0,0 +1,142 @@ +# `vmulfp128` β€” Vector128 Multiply Floating-Point + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128](../forms/VX128.md) Β· **Opcode:** `0x14000090` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vmulfp128` | `vmulfp128` | β€” | Vector128 Multiply Floating-Point | + +## Syntax + +```asm +vmulfp128 [VD], [VA], [VB] +``` + +## Encoding + +### `vmulfp128` β€” form `VX128` + +- **Opcode word:** `0x14000090` +- **Primary opcode (bits 0–5):** `5` +- **Extended opcode:** `144` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (4 or 5) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `VA128l` | source A low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21 | `VA128H` | source A high bit | +| 22 | `β€”` | reserved | +| 23–25 | `VC` | optional VC / XO sub-field | +| 26 | `VA128h` | source A middle bit | +| 27 | `β€”` | reserved | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VA` | vmulfp128: read | Source A vector register. | +| `VB` | vmulfp128: read | Source B vector register. | +| `VD` | vmulfp128: write | Destination vector register. | + +## Register Effects + +### `vmulfp128` + +- **Reads (always):** `VA`, `VB` +- **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 + +**`vmulfp128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vmulfp128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1126`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1126) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:108`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L108) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:612`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L612) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2108-2120`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2108-L2120) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vmulfp128 => { + // PPCBUG-435 + PPCBUG-437. + let a = ctx.vr[instr.va128()].as_f32x4(); + let b = ctx.vr[instr.vb128()].as_f32x4(); + let mut r = [0f32; 4]; + for i in 0..4 { + let ai = vmx::flush_denorm(a[i]); + let bi = vmx::flush_denorm(b[i]); + r[i] = vmx::flush_denorm(ai * bi); + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_f32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Lane-wise float multiply β€” Xenon-only.** Base Altivec has no dedicated `vmulfp`; the pattern on traditional PowerPC is `vmaddfp vD, vA, vC, v_zero`. Xenon adds this direct instruction, saving the zero-register setup. +- **IEEE-754 binary32, round-to-nearest.** Each of the four lanes computes `VD[i] = VA[i] * VB[i]`. +- **`VSCR[NJ]` honoured** (denormals flush-to-zero). +- **NaN propagation** per IEEE-754. +- **No VSCR[SAT], no FPSCR update, no exceptions.** +- **VMX128 register-fusion** (7-bit IDs). +- **No IBM AIX entry** β€” Xbox-specific; contrast with the `vmaddfp`-with-zero workaround used on non-Xenon Altivec. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vmaddfp`](../vmx/vmaddfp.md), [`vmaddcfp128`](vmaddcfp128.md) β€” fused MAC forms. +- [`vaddfp`](../vmx/vaddfp.md), [`vsubfp`](../vmx/vsubfp.md) β€” lane-wise float add/sub. +- [`vmsum3fp128`](vmsum3fp128.md), [`vmsum4fp128`](vmsum4fp128.md) β€” dot-product reductions. + +## IBM Reference + +- No IBM AIX entry β€” this instruction is exclusive to the Xbox 360's VMX128 extension. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. Non-Xenon Altivec code emits `vmaddfp vD, vA, vC, v_zero` to achieve the same effect. +- [IBM AltiVec Technology Programmer's Interface Manual Β§`vmaddfp`](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for the underlying float semantics. diff --git a/tools/ppc-manual/vmx128/vpermwi128.md b/tools/ppc-manual/vmx128/vpermwi128.md new file mode 100644 index 00000000..d1b5a7cb --- /dev/null +++ b/tools/ppc-manual/vmx128/vpermwi128.md @@ -0,0 +1,139 @@ +# `vpermwi128` β€” Vector128 Permutate Word Immediate + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_P](../forms/VX128_P.md) Β· **Opcode:** `0x18000210` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpermwi128` | `vpermwi128` | β€” | Vector128 Permutate Word Immediate | + +## Syntax + +```asm +vpermwi128 [VD], [VB], [UIMM] +``` + +## Encoding + +### `vpermwi128` β€” form `VX128_P` + +- **Opcode word:** `0x18000210` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `528` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `PERMl` | permute selector low 5 bits | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–22 | `β€”` | reserved | +| 23–25 | `PERMh` | permute selector high 3 bits | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vpermwi128: read | Source B vector register. | +| `UIMM` | vpermwi128: read | 16-bit unsigned immediate. Zero-extended. | +| `VD` | vpermwi128: write | Destination vector register. | + +## Register Effects + +### `vpermwi128` + +- **Reads (always):** `VB`, `UIMM` +- **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 + +**`vpermwi128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpermwi128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1207`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1207) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:112`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L112) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:642`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L642) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4537-4548`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4537-L4548) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpermwi128 => { + let imm = instr.vx128_p_perm(); + let b = ctx.vr[instr.vb128()].as_u32x4(); + let mut r = [0u32; 4]; + // Output lane i ← b[(imm >> (2 * (3-i))) & 3] + for i in 0..4 { + let sel = ((imm >> (2 * (3 - i))) & 3) as usize; + r[i] = b[sel]; + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Word-level 4-way permute via an 8-bit immediate.** The 8-bit `PERM` immediate (carried in fields `PERMh β€– PERMl` of the encoding) is treated as **four 2-bit selectors**, one per output word lane. Each 2-bit field selects which of `VB`'s 4 word lanes is copied to the corresponding output lane. +- **Bit layout of the immediate.** Output lane 0 (big-endian MSB word) is selected by bits 6–7 of `PERM`; lane 1 by bits 4–5; lane 2 by bits 2–3; lane 3 by bits 0–1. (In xenia: `sel = (imm >> (2 * (3-i))) & 3`.) +- **Super-set of [`vspltw`](../vmx/vspltw.md).** A splat is `vpermwi128 vD, vB, 0x00` (all lanes = word 0), `0x55` (all = word 1), `0xAA` (all = word 2), `0xFF` (all = word 3). Arbitrary shuffles like "xyzw β†’ wzyx" are a single-instruction operation. +- **Immediate-only.** No dynamic selector vector; contrast with [`vperm`](../vmx/vperm.md). +- **Single-source.** Unlike `vperm`/`vperm128`, `vpermwi128` only reshuffles one register (`VB`); it cannot interleave two operands. +- **VMX128 register-fusion** on `VD` and `VB` (7-bit IDs). +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER, no VSCR.** + +## Related Instructions + +- [`vperm`](../vmx/vperm.md), [`vperm128`](../vmx/vperm.md) β€” general byte-granularity permute (two-source). +- [`vspltw`](../vmx/vspltw.md), [`vspltw128`](../vmx/vspltw.md) β€” single-word splat (special case of `vpermwi128`). +- [`vsldoi`](../vmx/vsldoi.md) β€” static-immediate byte rotate of two registers. +- [`vrlimi128`](vrlimi128.md) β€” rotate + mask-insert (per-word rotate with an insert mask). + +## IBM Reference + +- No IBM AIX entry β€” this instruction is exclusive to the Xbox 360's VMX128 extension. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. Functionally equivalent to HLSL's `.xyzw`-suffix swizzle on `float4`. +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 6 β€” Permute and Formatting](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for the base permute semantics. diff --git a/tools/ppc-manual/vmx128/vpkd3d128.md b/tools/ppc-manual/vmx128/vpkd3d128.md new file mode 100644 index 00000000..d3e232ea --- /dev/null +++ b/tools/ppc-manual/vmx128/vpkd3d128.md @@ -0,0 +1,185 @@ +# `vpkd3d128` β€” Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_4](../forms/VX128_4.md) Β· **Opcode:** `0x18000610` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vpkd3d128` | `vpkd3d128` | β€” | Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert | + +## Syntax + +```asm +(no disassembly template) +``` + +## Encoding + +### `vpkd3d128` β€” form `VX128_4` + +- **Opcode word:** `0x18000610` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1552` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–23 | `XO` | extended opcode | +| 24–25 | `z` | sub-operation selector | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vpkd3d128: read | Source B vector register. | +| `VD` | vpkd3d128: write | Destination vector register. | + +## Register Effects + +### `vpkd3d128` + +- **Reads (always):** `VB` +- **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 + +**`vpkd3d128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vpkd3d128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2088`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2088) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:112`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L112) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:648`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L648) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4191-4248`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4191-L4248) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vpkd3d128 => { + use crate::vmx::D3dPackType; + let uimm = crate::decoder::extract_vx128_uimm5(instr.raw); + let pack = (uimm & 3) as usize; + let shift = instr.vx128_4_z() as usize; + let ty = D3dPackType::from_immediate(uimm >> 2); + let src = ctx.vr[instr.vb128()]; + let out = match ty { + D3dPackType::D3dColor => crate::vmx::pack_d3dcolor(src), + D3dPackType::NormShort2 => crate::vmx::pack_normshort2(src), + D3dPackType::NormPacked32 => crate::vmx::pack_normpacked32(src), + D3dPackType::Float16_2 => crate::vmx::pack_float16_2(src), + D3dPackType::NormShort4 => crate::vmx::pack_normshort4(src), + D3dPackType::Float16_4 => crate::vmx::pack_float16_4(src), + D3dPackType::NormPacked64 => crate::vmx::pack_normpacked64(src), + D3dPackType::Other(t) => { + tracing::warn!( + raw = format_args!("{:#010x}", instr.raw), + uimm, + ty = t, + "vpkd3d128: unhandled pack type at {:#010x}", + ctx.pc, + ); + src + } + }; + // Post-pack permutation: merge packed `out` into previous `vd` + // per canary ppc_emit_altivec.cc:2126-2188 MakePermuteMask tables. + // MakePermuteMask(r0,l0, r1,l1, r2,l2, r3,l3): result[i] = if ri==0 { prev[li] } else { out[li] } + let result = if pack == 0 { + out + } else { + // (source_reg, lane): 0=prev vd, 1=packed out + const PERM: [[[(u8, u8); 4]; 4]; 3] = [ + // pack=1 (VPACK_32): places out[3] at lane (3-shift) + [[(0,0),(0,1),(0,2),(1,3)], [(0,0),(0,1),(1,3),(0,3)], + [(0,0),(1,3),(0,2),(0,3)], [(1,3),(0,1),(0,2),(0,3)]], + // pack=2 (64-bit): places out[2..3] at lanes (2-shift)..(3-shift) + [[(0,0),(0,1),(1,2),(1,3)], [(0,0),(1,2),(1,3),(0,3)], + [(1,2),(1,3),(0,2),(0,3)], [(1,3),(0,1),(0,2),(0,3)]], + // pack=3 (64-bit): same as pack=2 except shift=3 selects out[2] at lane 3 + [[(0,0),(0,1),(1,2),(1,3)], [(0,0),(1,2),(1,3),(0,3)], + [(1,2),(1,3),(0,2),(0,3)], [(0,0),(0,1),(0,2),(1,2)]], + ]; + let prev = ctx.vr[instr.vd128()]; + let pw = prev.as_u32x4(); + let ow = out.as_u32x4(); + let sel = PERM[pack - 1][shift]; + xenia_types::Vec128::from_u32x4_array([ + if sel[0].0 == 0 { pw[sel[0].1 as usize] } else { ow[sel[0].1 as usize] }, + if sel[1].0 == 0 { pw[sel[1].1 as usize] } else { ow[sel[1].1 as usize] }, + if sel[2].0 == 0 { pw[sel[2].1 as usize] } else { ow[sel[2].1 as usize] }, + if sel[3].0 == 0 { pw[sel[3].1 as usize] } else { ow[sel[3].1 as usize] }, + ]) + }; + ctx.vr[instr.vd128()] = result; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Pack four float lanes into a single D3D-format 32-bit word.** The `IMM` field and the `z` sub-operation selector (together carried in bits 6–10 of the encoding in xenia's layout) choose *which* D3D format to emit: + - `D3dColor` β€” pack 4Γ—float `[0.0, 1.0]` lanes into a 32-bit RGBA8 (A in high byte, B in low byte) β€” the canonical Direct3D 9 `D3DCOLOR` format. Xenia's helper is `vmx::pack_d3dcolor`. + - Other formats (RGBA16, compressed colour, etc.) are not yet implemented in xenia-rs; the interpreter logs a warning and passes through unchanged. +- **Also performs rotate-left-immediate and mask-insert.** The mnemonic is "Pack D3Dtype, Rotate Left Immediate and Mask Insert": the result of the pack step is rotated and merged into an existing `VD` under an immediate mask. Xenia currently emits only the pack step and overwrites `VD` wholesale; games rarely rely on the rotate-and-insert aspect. +- **Sub-operation via the `z` field** (2 bits) + `IMM` (5 bits) gives 7 bits of format selection; the practical set used by Xenon games is small (D3DCOLOR is the dominant one). +- **No saturation signal.** The packer saturates floats beyond `[0.0, 1.0]` silently; `VSCR[SAT]` is not touched. +- **VMX128 register-fusion** on `VD` and `VB`. +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vupkd3d128`](vupkd3d128.md) β€” the inverse (unpack a D3D-format word back into 4 floats). +- [`vpkpx`](../vmx/vpkpx.md) β€” the standard Altivec 1-5-5-5 pixel pack. +- [`vpkshus`](../vmx/vpkshus.md), [`vpkuhus`](../vmx/vpkuhus.md) β€” byte-range saturating packs (an alternative colour-packing path). +- [`vcfpsxws128`](vcfpsxws128.md), [`vcfpuxws128`](vcfpuxws128.md) β€” conversion with explicit scale; software sometimes pre-scales floats to `[0, 255]` before using these in place of `vpkd3d128`. + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. The "D3D" in the mnemonic refers directly to Direct3D 9 vertex/pixel formats (the `D3DDECLTYPE_*` enumeration). +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. +- Microsoft D3D9 documentation: `D3DDECLTYPE_D3DCOLOR`, `D3DDECLTYPE_UBYTE4N`, etc. diff --git a/tools/ppc-manual/vmx128/vrlimi128.md b/tools/ppc-manual/vmx128/vrlimi128.md new file mode 100644 index 00000000..b5fdfbae --- /dev/null +++ b/tools/ppc-manual/vmx128/vrlimi128.md @@ -0,0 +1,141 @@ +# `vrlimi128` β€” Vector128 Rotate Left Immediate and Mask Insert + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_4](../forms/VX128_4.md) Β· **Opcode:** `0x18000710` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vrlimi128` | `vrlimi128` | β€” | Vector128 Rotate Left Immediate and Mask Insert | + +## Syntax + +```asm +vrlimi128 [VD], [VB], [IMM], [z] +``` + +## Encoding + +### `vrlimi128` β€” form `VX128_4` + +- **Opcode word:** `0x18000710` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `1808` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–23 | `XO` | extended opcode | +| 24–25 | `z` | sub-operation selector | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vrlimi128: read | Source B vector register. | +| `VD` | vrlimi128: write | Destination vector register. | + +## Register Effects + +### `vrlimi128` + +- **Reads (always):** `VB` +- **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 + +**`vrlimi128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vrlimi128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:1315`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L1315) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:119`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L119) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:649`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L649) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3962-3977`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3962-L3977) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vrlimi128 => { + let shift = instr.vx128_4_z() as usize; + let mask = instr.vx128_4_imm(); + let b = ctx.vr[instr.vb128()].as_u32x4(); + let d = ctx.vr[instr.vd128()].as_u32x4(); + let rot = [b[shift % 4], b[(shift + 1) % 4], b[(shift + 2) % 4], b[(shift + 3) % 4]]; + let mut r = [0u32; 4]; + for i in 0..4 { + // mask bit 3 corresponds to word 0 (BE-first). Use rot when + // the corresponding mask bit is set. + let use_rot = (mask >> (3 - i)) & 1 == 1; + r[i] = if use_rot { rot[i] } else { d[i] }; + } + ctx.vr[instr.vd128()] = xenia_types::Vec128::from_u32x4_array(r); + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Rotate-left-word + mask-insert in one step.** `VB` is rotated left by `IMM & 3` word positions (word-granular, 0..3 β€” not bits). The resulting rotated vector is merged into the pre-existing `VD` under control of a 4-bit "insert mask" (`fmask`, from bits 26–29 of the encoding in xenia's layout): mask bit `i` = 1 keeps lane `i` from the rotated `VB`; mask bit = 0 keeps lane `i` from the old `VD`. +- **Destructive destination.** `VD` is both source and destination β€” software must preserve its value or pre-initialise it. +- **Typical use: selective-lane overwrite.** Games use this to "rewrite lane `n` of a vector with a shuffled component" without a full permute. A common pattern is "insert a scalar into lane `i` of a vector" where the scalar has been pre-loaded to a known word of `VB`. +- **Mask bit ↔ lane mapping.** Big-endian: mask bit 3 (MSB of the 4-bit mask) controls lane 0; bit 0 controls lane 3. (In xenia: `use_rot = (mask >> (3 βˆ’ i)) & 1`.) +- **VMX128 register-fusion** on `VD` and `VB`. +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER, no VSCR.** + +## Related Instructions + +- [`vrlw`](../vmx/vrlw.md), [`vrlw128`](../vmx/vrlw.md) β€” per-lane bit-level rotate (word-granular shift, not lane-granular). +- [`vpermwi128`](vpermwi128.md) β€” immediate 4-way word permute (no merge). +- [`vsel`](../vmx/vsel.md), [`vsel128`](../vmx/vsel.md) β€” general bit-select; `vrlimi128` is the specialised "rotate + insert" equivalent. +- [`vsldoi`](../vmx/vsldoi.md) β€” byte-level immediate shift. + +## IBM Reference + +- No IBM AIX entry β€” this instruction is exclusive to the Xbox 360's VMX128 extension. The mnemonic is an adaptation of the scalar `rlwimi` (rotate-left-word-immediate-mask-insert) pattern for vectors. +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. +- [IBM AltiVec Technology Programmer's Interface Manual, Chapter 4 β€” Integer Shift / Rotate](https://www.nxp.com/docs/en/reference-manual/ALTIVECPIM.pdf) for the base rotate semantics. diff --git a/tools/ppc-manual/vmx128/vupkd3d128.md b/tools/ppc-manual/vmx128/vupkd3d128.md new file mode 100644 index 00000000..e8c4aeb9 --- /dev/null +++ b/tools/ppc-manual/vmx128/vupkd3d128.md @@ -0,0 +1,154 @@ +# `vupkd3d128` β€” Vector128 Unpack D3Dtype + +> **Category:** [VMX128](../categories/vmx128.md) Β· **Form:** [VX128_3](../forms/VX128_3.md) Β· **Opcode:** `0x180007f0` + + + +## Assembler Mnemonics + +| Mnemonic | XML entry | Flags | Description | +| --- | --- | --- | --- | +| `vupkd3d128` | `vupkd3d128` | β€” | Vector128 Unpack D3Dtype | + +## Syntax + +```asm +(no disassembly template) +``` + +## Encoding + +### `vupkd3d128` β€” form `VX128_3` + +- **Opcode word:** `0x180007f0` +- **Primary opcode (bits 0–5):** `6` +- **Extended opcode:** `2032` +- **Synchronising:** no + +| Bits | Field | Meaning | +| --- | --- | --- | +| 0–5 | `OPCD` | primary opcode (6) | +| 6–10 | `VD128l` | destination low 5 bits | +| 11–15 | `IMM` | 5-bit immediate | +| 16–20 | `VB128l` | source B low 5 bits | +| 21–27 | `XO` | extended opcode | +| 28–29 | `VD128h` | destination high 2 bits | +| 30–31 | `VB128h` | source B high 2 bits | + +## Operands + +| Field | Role | Description | +| --- | --- | --- | +| `VB` | vupkd3d128: read | Source B vector register. | +| `VD` | vupkd3d128: write | Destination vector register. | + +## Register Effects + +### `vupkd3d128` + +- **Reads (always):** `VB` +- **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 + +**`vupkd3d128`** +- xenia-canary XML: [`tools/ppc-instructions.xml` β€” search for `mnem="vupkd3d128"`](../../xenia-canary/tools/ppc-instructions.xml) +- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:2194`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L2194) +- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:128`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L128) +- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:670`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L670) +- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4249-4275`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4249-L4275) +
xenia-rs interpreter body (frozen snapshot) + +```rust + PpcOpcode::vupkd3d128 => { + use crate::vmx::D3dPackType; + let uimm = crate::decoder::extract_vx128_uimm5(instr.raw); + let ty = D3dPackType::from_immediate(uimm >> 2); + let src = ctx.vr[instr.vb128()]; + let out = match ty { + D3dPackType::D3dColor => crate::vmx::unpack_d3dcolor(src), + D3dPackType::NormShort2 => crate::vmx::unpack_normshort2(src), + D3dPackType::NormPacked32 => crate::vmx::unpack_normpacked32(src), + D3dPackType::Float16_2 => crate::vmx::unpack_float16_2(src), + D3dPackType::NormShort4 => crate::vmx::unpack_normshort4(src), + D3dPackType::Float16_4 => crate::vmx::unpack_float16_4(src), + D3dPackType::NormPacked64 => crate::vmx::unpack_normpacked64(src), + D3dPackType::Other(t) => { + tracing::warn!( + raw = format_args!("{:#010x}", instr.raw), + uimm, + ty = t, + "vupkd3d128: unhandled pack type at {:#010x}", + ctx.pc, + ); + src + } + }; + ctx.vr[instr.vd128()] = out; + ctx.pc += 4; + } +``` +
+ + + +## Special Cases & Edge Conditions + +- **Unpack a D3D-format word into 4 float lanes.** The `IMM` field in the encoding selects the target format: + - `D3dColor` β€” decode a 32-bit RGBA8 (`D3DCOLOR`) into 4 float lanes in `[0.0, 1.0]`. Xenia's helper is `vmx::unpack_d3dcolor`. + - Other formats (UBYTE4N, SHORT2N, etc.) are not yet implemented in xenia-rs; the interpreter logs a warning and passes `VB` through unchanged. +- **Inverse of [`vpkd3d128`](vpkd3d128.md).** The same format code used to pack must be used to unpack. +- **Source-width is a single 32-bit word** of `VB` (typically lane 0; the helpers read the appropriate component). The other three input word lanes are ignored for `D3DCOLOR`. +- **IEEE-754 binary32 outputs,** already normalised to `[0.0, 1.0]` (integer value divided by 255, then cast to float). +- **No `VSCR[SAT]` effect**, no FPSCR, no exceptions. +- **VMX128 register-fusion** on `VD` and `VB`. +- **No IBM AIX entry** β€” Xenon-only. +- **No `Rc`, no XER.** + +## Related Instructions + +- [`vpkd3d128`](vpkd3d128.md) β€” the inverse pack. +- [`vupkhpx`](../vmx/vupkhpx.md), [`vupklpx`](../vmx/vupklpx.md) β€” standard Altivec 1-5-5-5 pixel unpacks. +- [`vupkhsb`](../vmx/vupkhsb.md), [`vupklsb`](../vmx/vupklsb.md) β€” sign-extending byteβ†’half-word unpacks (the integer analogue). +- [`vcsxwfp128`](vcsxwfp128.md), [`vcuxwfp128`](vcuxwfp128.md) β€” int β†’ float with scale; sometimes used as an alternate decode path. + +## IBM Reference + +- No IBM AIX entry β€” Xbox 360 VMX128 extension only. "D3D" denotes the Direct3D 9 vertex/pixel format catalogue (`D3DDECLTYPE_*`). +- Xbox 360 XDK, Altivec-128 (VMX128) extensions. +- Microsoft D3D9 documentation: `D3DDECLTYPE_D3DCOLOR`, `D3DDECLTYPE_UBYTE4N`, etc. diff --git a/tools/run-canary.sh b/tools/run-canary.sh new file mode 100755 index 00000000..af1baeb6 --- /dev/null +++ b/tools/run-canary.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Launch Xenia Canary on the title β€” the oracle, not our renderer and not the port. +# +# tools/run-canary.sh [extra canary flags...] +# +# πŸ”΄ THE VERSION THIS REPLACES WAS BROKEN IN TWO WAYS, and both are worth +# knowing because both cost a session: +# +# * it pointed at `xenia-rs/sylpheed.iso`, a SYMLINK β€” Wine cannot resolve one +# and reports "path invalid", which reads as a corrupt image rather than a +# path problem. Point at the real file. +# * it hardcoded `/home/fabi/RE Project Sylpheed/...`, so it worked on exactly +# one machine, and `xenia-rs` is retired (docs/agents/CONSOLIDATION.md). +# +# ⚠️ Standing constraints, none of them optional: +# * ONE emulator process at a time β€” ours or Canary, never both. +# * Canary runs MUTED (`mute=true` in its config). +# * NEVER judge a crash, a hang or a freeze from a Bash-launched run. A SIGKILL +# that looked like the binary turned out to be the editor's process +# supervisor. Ask the human to run it natively. +set -euo pipefail + +PROJECT="${SYLPH_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +CANARY="${SYLPH_CANARY_BIN:-$PROJECT/xenia-canary/build/bin/Linux/Release/xenia_canary}" +ISO="${SYLPH_ISO:-$PROJECT/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}" + +[ -x "$CANARY" ] || { echo "no canary binary at $CANARY β€” set SYLPH_CANARY_BIN" >&2; exit 1; } +[ -f "$ISO" ] || { echo "no ISO at $ISO β€” set SYLPH_ISO" >&2; exit 1; } +[ -L "$ISO" ] && echo "⚠️ $ISO is a SYMLINK; Wine will report 'path invalid'" >&2 + +exec "$CANARY" "$ISO" --log_level=3 --disable_instruction_infocache=true "$@"