chore: add migration/ bundle for cross-machine setup
Bundles state that lives OUTSIDE the xenia-rs repo so a fresh clone on
another machine can be brought up to identical configuration via
migration/setup.sh:
- claude-memory/ ~/.claude/projects/-home-fabi-RE-Project-Sylpheed/memory/
(103 files, 1.1 MB - MEMORY.md + every
project_xenia_rs_*.md from audits
addis_signext through audit-058)
- project-root/dot-claude/ <project-root>/.claude/settings.json
(Stop hook + permissions)
- project-root/ppc-manual/ <project-root>/ppc-manual/
(PowerPC reference docs, 397 files, 3.7 MB)
- project-root/run-canary.sh <project-root>/run-canary.sh
- README.md Human-readable setup checklist
- setup.sh Idempotent installer (also reclones
xenia-canary at pinned HEAD 6de80dffe)
- MANIFEST.md Per-file mapping + per-file-not-bundled
restoration recipe
Excluded from bundle (not shippable via git):
- Sylpheed ISO (7.8 GB; copyright; manual copy required)
- sylpheed.db (395 MB; regenerable from XEX via analysis tooling)
- target/ build artifacts (rebuild on target)
- audit-runs probe firehoses (.log/.stdout/.stderr ~11 GB; rerun if needed)
- audit-runs memory dumps (.bin ~4.5 GB; rerun audit-026/027/029 if needed)
- xenia-canary checkout (setup.sh reclones from
git.mc02.dev/fabi/Xenia-Canary.git at HEAD 6de80dffe)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
138
migration/project-root/ppc-manual/alu/addcx.md
Normal file
138
migration/project-root/ppc-manual/alu/addcx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `addcx` — Add Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000014`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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.
|
||||
139
migration/project-root/ppc-manual/alu/addex.md
Normal file
139
migration/project-root/ppc-manual/alu/addex.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `addex` — Add Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000114`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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.
|
||||
116
migration/project-root/ppc-manual/alu/addi.md
Normal file
116
migration/project-root/ppc-manual/alu/addi.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# `addi` — Add Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x38000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
123
migration/project-root/ppc-manual/alu/addic.md
Normal file
123
migration/project-root/ppc-manual/alu/addic.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `addic` — Add Immediate Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x30000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
132
migration/project-root/ppc-manual/alu/addicx.md
Normal file
132
migration/project-root/ppc-manual/alu/addicx.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `addic.` — Add Immediate Carrying and Record
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x34000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
121
migration/project-root/ppc-manual/alu/addis.md
Normal file
121
migration/project-root/ppc-manual/alu/addis.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `addis` — Add Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x3c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
136
migration/project-root/ppc-manual/alu/addmex.md
Normal file
136
migration/project-root/ppc-manual/alu/addmex.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `addmex` — Add to Minus One Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d4`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
148
migration/project-root/ppc-manual/alu/addx.md
Normal file
148
migration/project-root/ppc-manual/alu/addx.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# `addx` — Add
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000214`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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).
|
||||
135
migration/project-root/ppc-manual/alu/addzex.md
Normal file
135
migration/project-root/ppc-manual/alu/addzex.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# `addzex` — Add to Zero Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000194`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
123
migration/project-root/ppc-manual/alu/andcx.md
Normal file
123
migration/project-root/ppc-manual/alu/andcx.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `andcx` — AND with Complement
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000078`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
127
migration/project-root/ppc-manual/alu/andisx.md
Normal file
127
migration/project-root/ppc-manual/alu/andisx.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `andis.` — AND Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x74000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
126
migration/project-root/ppc-manual/alu/andix.md
Normal file
126
migration/project-root/ppc-manual/alu/andix.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `andi.` — AND Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x70000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
122
migration/project-root/ppc-manual/alu/andx.md
Normal file
122
migration/project-root/ppc-manual/alu/andx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `andx` — AND
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000038`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
153
migration/project-root/ppc-manual/alu/cmp.md
Normal file
153
migration/project-root/ppc-manual/alu/cmp.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# `cmp` — Compare
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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, GT: 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)
|
||||
143
migration/project-root/ppc-manual/alu/cmpi.md
Normal file
143
migration/project-root/ppc-manual/alu/cmpi.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# `cmpi` — Compare Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x2c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
127
migration/project-root/ppc-manual/alu/cmpl.md
Normal file
127
migration/project-root/ppc-manual/alu/cmpl.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `cmpl` — Compare Logical
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000040`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
127
migration/project-root/ppc-manual/alu/cmpli.md
Normal file
127
migration/project-root/ppc-manual/alu/cmpli.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `cmpli` — Compare Logical Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x28000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
119
migration/project-root/ppc-manual/alu/cntlzdx.md
Normal file
119
migration/project-root/ppc-manual/alu/cntlzdx.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# `cntlzdx` — Count Leading Zeros Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000074`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
121
migration/project-root/ppc-manual/alu/cntlzwx.md
Normal file
121
migration/project-root/ppc-manual/alu/cntlzwx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `cntlzwx` — Count Leading Zeros Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000034`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
135
migration/project-root/ppc-manual/alu/divdux.md
Normal file
135
migration/project-root/ppc-manual/alu/divdux.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# `divdux` — Divide Doubleword Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000392`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
136
migration/project-root/ppc-manual/alu/divdx.md
Normal file
136
migration/project-root/ppc-manual/alu/divdx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `divdx` — Divide Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0003d2`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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`.
|
||||
137
migration/project-root/ppc-manual/alu/divwux.md
Normal file
137
migration/project-root/ppc-manual/alu/divwux.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# `divwux` — Divide Word Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000396`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
138
migration/project-root/ppc-manual/alu/divwx.md
Normal file
138
migration/project-root/ppc-manual/alu/divwx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `divwx` — Divide Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0003d6`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
111
migration/project-root/ppc-manual/alu/eieio.md
Normal file
111
migration/project-root/ppc-manual/alu/eieio.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
122
migration/project-root/ppc-manual/alu/eqvx.md
Normal file
122
migration/project-root/ppc-manual/alu/eqvx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `eqvx` — Equivalent
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000238`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
121
migration/project-root/ppc-manual/alu/extsbx.md
Normal file
121
migration/project-root/ppc-manual/alu/extsbx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `extsbx` — Extend Sign Byte
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000774`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
121
migration/project-root/ppc-manual/alu/extshx.md
Normal file
121
migration/project-root/ppc-manual/alu/extshx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `extshx` — Extend Sign Half Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000734`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
120
migration/project-root/ppc-manual/alu/extswx.md
Normal file
120
migration/project-root/ppc-manual/alu/extswx.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# `extswx` — Extend Sign Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0007b4`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
111
migration/project-root/ppc-manual/alu/isync.md
Normal file
111
migration/project-root/ppc-manual/alu/isync.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# `isync` — Instruction Synchronize
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c00012c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
124
migration/project-root/ppc-manual/alu/mulhdux.md
Normal file
124
migration/project-root/ppc-manual/alu/mulhdux.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `mulhdux` — Multiply High Doubleword Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000012`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
124
migration/project-root/ppc-manual/alu/mulhdx.md
Normal file
124
migration/project-root/ppc-manual/alu/mulhdx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `mulhdx` — Multiply High Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000092`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
126
migration/project-root/ppc-manual/alu/mulhwux.md
Normal file
126
migration/project-root/ppc-manual/alu/mulhwux.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `mulhwux` — Multiply High Word Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000016`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
126
migration/project-root/ppc-manual/alu/mulhwx.md
Normal file
126
migration/project-root/ppc-manual/alu/mulhwx.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `mulhwx` — Multiply High Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000096`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
130
migration/project-root/ppc-manual/alu/mulldx.md
Normal file
130
migration/project-root/ppc-manual/alu/mulldx.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# `mulldx` — Multiply Low Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d2`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
120
migration/project-root/ppc-manual/alu/mulli.md
Normal file
120
migration/project-root/ppc-manual/alu/mulli.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# `mulli` — Multiply Low Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x1c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
146
migration/project-root/ppc-manual/alu/mullwx.md
Normal file
146
migration/project-root/ppc-manual/alu/mullwx.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# `mullwx` — Multiply Low Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d6`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
122
migration/project-root/ppc-manual/alu/nandx.md
Normal file
122
migration/project-root/ppc-manual/alu/nandx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `nandx` — NAND
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0003b8`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
132
migration/project-root/ppc-manual/alu/negx.md
Normal file
132
migration/project-root/ppc-manual/alu/negx.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `negx` — Negate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0000d0`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
124
migration/project-root/ppc-manual/alu/norx.md
Normal file
124
migration/project-root/ppc-manual/alu/norx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `norx` — NOR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0000f8`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
122
migration/project-root/ppc-manual/alu/orcx.md
Normal file
122
migration/project-root/ppc-manual/alu/orcx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `orcx` — OR with Complement
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000338`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
115
migration/project-root/ppc-manual/alu/ori.md
Normal file
115
migration/project-root/ppc-manual/alu/ori.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# `ori` — OR Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x60000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
113
migration/project-root/ppc-manual/alu/oris.md
Normal file
113
migration/project-root/ppc-manual/alu/oris.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# `oris` — OR Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x64000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::oris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
122
migration/project-root/ppc-manual/alu/orx.md
Normal file
122
migration/project-root/ppc-manual/alu/orx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `orx` — OR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000378`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
137
migration/project-root/ppc-manual/alu/rldclx.md
Normal file
137
migration/project-root/ppc-manual/alu/rldclx.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# `rldclx` — Rotate Left Doubleword then Clear Left
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MDS](../forms/MDS.md) · **Opcode:** `0x78000010`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
136
migration/project-root/ppc-manual/alu/rldcrx.md
Normal file
136
migration/project-root/ppc-manual/alu/rldcrx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `rldcrx` — Rotate Left Doubleword then Clear Right
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MDS](../forms/MDS.md) · **Opcode:** `0x78000012`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
142
migration/project-root/ppc-manual/alu/rldiclx.md
Normal file
142
migration/project-root/ppc-manual/alu/rldiclx.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
142
migration/project-root/ppc-manual/alu/rldicrx.md
Normal file
142
migration/project-root/ppc-manual/alu/rldicrx.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
138
migration/project-root/ppc-manual/alu/rldicx.md
Normal file
138
migration/project-root/ppc-manual/alu/rldicx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `rldicx` — Rotate Left Doubleword Immediate then Clear
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MD](../forms/MD.md) · **Opcode:** `0x78000008`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
136
migration/project-root/ppc-manual/alu/rldimix.md
Normal file
136
migration/project-root/ppc-manual/alu/rldimix.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
139
migration/project-root/ppc-manual/alu/rlwimix.md
Normal file
139
migration/project-root/ppc-manual/alu/rlwimix.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
145
migration/project-root/ppc-manual/alu/rlwinmx.md
Normal file
145
migration/project-root/ppc-manual/alu/rlwinmx.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
139
migration/project-root/ppc-manual/alu/rlwnmx.md
Normal file
139
migration/project-root/ppc-manual/alu/rlwnmx.md
Normal file
@@ -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`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
124
migration/project-root/ppc-manual/alu/sldx.md
Normal file
124
migration/project-root/ppc-manual/alu/sldx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `sldx` — Shift Left Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000036`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
124
migration/project-root/ppc-manual/alu/slwx.md
Normal file
124
migration/project-root/ppc-manual/alu/slwx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `slwx` — Shift Left Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000030`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
132
migration/project-root/ppc-manual/alu/sradix.md
Normal file
132
migration/project-root/ppc-manual/alu/sradix.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `sradix` — Shift Right Algebraic Doubleword Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XS](../forms/XS.md) · **Opcode:** `0x7c000674`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
136
migration/project-root/ppc-manual/alu/sradx.md
Normal file
136
migration/project-root/ppc-manual/alu/sradx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `sradx` — Shift Right Algebraic Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000634`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
132
migration/project-root/ppc-manual/alu/srawix.md
Normal file
132
migration/project-root/ppc-manual/alu/srawix.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `srawix` — Shift Right Algebraic Word Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000670`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
138
migration/project-root/ppc-manual/alu/srawx.md
Normal file
138
migration/project-root/ppc-manual/alu/srawx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `srawx` — Shift Right Algebraic Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000630`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
123
migration/project-root/ppc-manual/alu/srdx.md
Normal file
123
migration/project-root/ppc-manual/alu/srdx.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `srdx` — Shift Right Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000436`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
125
migration/project-root/ppc-manual/alu/srwx.md
Normal file
125
migration/project-root/ppc-manual/alu/srwx.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# `srwx` — Shift Right Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000430`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
138
migration/project-root/ppc-manual/alu/subfcx.md
Normal file
138
migration/project-root/ppc-manual/alu/subfcx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `subfcx` — Subtract From Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000010`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
138
migration/project-root/ppc-manual/alu/subfex.md
Normal file
138
migration/project-root/ppc-manual/alu/subfex.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `subfex` — Subtract From Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000110`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
131
migration/project-root/ppc-manual/alu/subficx.md
Normal file
131
migration/project-root/ppc-manual/alu/subficx.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# `subficx` — Subtract From Immediate Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x20000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
145
migration/project-root/ppc-manual/alu/subfmex.md
Normal file
145
migration/project-root/ppc-manual/alu/subfmex.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# `subfmex` — Subtract From Minus One Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d0`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
148
migration/project-root/ppc-manual/alu/subfx.md
Normal file
148
migration/project-root/ppc-manual/alu/subfx.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# `subfx` — Subtract From
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000050`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
146
migration/project-root/ppc-manual/alu/subfzex.md
Normal file
146
migration/project-root/ppc-manual/alu/subfzex.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# `subfzex` — Subtract From Zero Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000190`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
113
migration/project-root/ppc-manual/alu/sync.md
Normal file
113
migration/project-root/ppc-manual/alu/sync.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# `sync` — Synchronize
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0004ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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.
|
||||
115
migration/project-root/ppc-manual/alu/xori.md
Normal file
115
migration/project-root/ppc-manual/alu/xori.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# `xori` — XOR Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x68000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
114
migration/project-root/ppc-manual/alu/xoris.md
Normal file
114
migration/project-root/ppc-manual/alu/xoris.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# `xoris` — XOR Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x6c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xoris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
121
migration/project-root/ppc-manual/alu/xorx.md
Normal file
121
migration/project-root/ppc-manual/alu/xorx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `xorx` — XOR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000278`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## 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)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## 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)
|
||||
Reference in New Issue
Block a user