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:
MechaCat02
2026-05-10 21:38:38 +02:00
parent 8e709b0a24
commit e6d43a23ac
505 changed files with 86028 additions and 0 deletions

View 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 05):** `31`
- **Extended opcode:** `10`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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.

View 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 05):** `31`
- **Extended opcode:** `138`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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.

View 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 05):** `14`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `12`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `13`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `15`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `31`
- **Extended opcode:** `234`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `266`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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).

View 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 05):** `31`
- **Extended opcode:** `202`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `60`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `29`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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 3247 (in PowerISA bit numbering, equivalent to bits 1631 of the low 32 bits) of `RS`. Bits 031 and bits 4863 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 3247, no information from `RS[0:31]` survives. Useful as a quick "extract bits 3247, 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)

View 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 05):** `28`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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 4863 only; for higher bits use [`andisx`](andisx.md) (covers bits 3247) 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 047, 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 3247).
- [`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)

View 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 05):** `31`
- **Extended opcode:** `28`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `0`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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`, 07). |
## 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 (07), 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)

View 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 05):** `11`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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`, 07). |
## 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 (07), 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)

View 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 05):** `31`
- **Extended opcode:** `32`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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`, 07). |
## 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 07.** 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)

View 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 05):** `10`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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`, 07). |
## 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)

View 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 05):** `31`
- **Extended opcode:** `58`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `26`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `457`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `489`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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`.

View 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 05):** `31`
- **Extended opcode:** `459`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `491`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `854`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `284`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `954`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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 055 of `RA`; bits 5663 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)

View 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 05):** `31`
- **Extended opcode:** `922`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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 047 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)

View 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 05):** `31`
- **Extended opcode:** `986`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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 031 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)

View 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 05):** `19`
- **Extended opcode:** `150`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (19) |
| 610 | `BT/BO` | target / branch options |
| 1115 | `BA/BI` | source A / CR bit to test |
| 1620 | `BB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `9`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `73`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `11`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `75`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `233`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `7`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `31`
- **Extended opcode:** `235`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `476`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `104`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `124`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `412`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `24`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `25`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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 3247 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)

View 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 05):** `31`
- **Extended opcode:** `444`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `RB` | source B GPR |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2730 | `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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `RB` | source B GPR |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2730 | `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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `sh` | shift amount low 5 bits |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2729 | `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 1620 + 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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `sh` | shift amount low 5 bits |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2729 | `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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `sh` | shift amount low 5 bits |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2729 | `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 1620 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)

View 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 05):** `30`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (30) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `sh` | shift amount low 5 bits |
| 2126 | `mb/me` | 6-bit mask field (swapped halves) |
| 2729 | `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)

View 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 05):** `20`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `SH/RB` | shift amount or source B |
| 2125 | `MB` | mask begin |
| 2630 | `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)

View 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 05):** `21`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `SH/RB` | shift amount or source B |
| 2125 | `MB` | mask begin |
| 2630 | `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)

View 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 05):** `23`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `SH/RB` | shift amount or source B |
| 2125 | `MB` | mask begin |
| 2630 | `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)

View 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 05):** `31`
- **Extended opcode:** `27`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `24`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `826`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RS` | source GPR |
| 1115 | `RA` | destination GPR |
| 1620 | `sh` | shift amount low 5 bits |
| 2129 | `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 1620 (`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)

View 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 05):** `31`
- **Extended opcode:** `794`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `824`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `792`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `539`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `536`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)

View 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 05):** `31`
- **Extended opcode:** `8`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `136`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `8`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `31`
- **Extended opcode:** `232`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `40`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `200`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode (31) |
| 610 | `RT` | destination GPR |
| 1115 | `RA` | source A |
| 1620 | `RB` | source B |
| 21 | `OE` | overflow-enable flag |
| 2230 | `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)

View 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 05):** `31`
- **Extended opcode:** `598`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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.

View 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 05):** `26`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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)

View 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 05):** `27`
- **Extended opcode:** —
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT` | destination GPR (or RS when storing) |
| 1115 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
| 1631 | `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 3247 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)

View 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 05):** `31`
- **Extended opcode:** `316`
- **Synchronising:** no
| Bits | Field | Meaning |
| --- | --- | --- |
| 05 | `OPCD` | primary opcode |
| 610 | `RT/FRT/VRT` | destination |
| 1115 | `RA/FRA/VRA` | source A |
| 1620 | `RB/FRB/VRB` | source B |
| 2130 | `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)