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:
115
migration/project-root/ppc-manual/README.md
Normal file
115
migration/project-root/ppc-manual/README.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# PowerPC Instruction Manual (Xenia Xbox 360 Subset)
|
||||
|
||||
A reference for the **Xenon** PowerPC dialect used by the Xbox 360. Its
|
||||
primary audience is an AI agent translating PPC assembly functions into
|
||||
equivalent C. The content is derived from the two authoritative sources in
|
||||
this repository — **xenia-canary** (C++ emulator) and **xenia-rs** (Rust
|
||||
rewrite) — and may be deepened with the IBM AIX PowerPC reference.
|
||||
|
||||
- **455** distinct XML-level instructions (one page each).
|
||||
- **350** instruction family pages (VMX128 siblings folded).
|
||||
- **598** assembly mnemonics once runtime `Rc`/`OE`/`LK` variants are expanded — all resolvable through `index.json`.
|
||||
|
||||
## How to use this manual (translation agent)
|
||||
|
||||
1. Parse the 32-bit instruction word and identify the mnemonic. Resolve it
|
||||
through [`index.json`](index.json): every assembly form (including
|
||||
`add.`, `addo.`, `bclrl`, …) is a top-level key pointing at a page.
|
||||
2. Open the page referenced by `index.json[mnem].page`. The page is in a
|
||||
fixed format — see the "Page anatomy" section below.
|
||||
3. Emit a C translation consistent with the page's pseudocode, the
|
||||
registers-affected list, and the status-register effects.
|
||||
|
||||
## Page anatomy
|
||||
|
||||
Every instruction page has the same sections, in this order:
|
||||
|
||||
| Section | Purpose |
|
||||
| --- | --- |
|
||||
| **Assembler Mnemonics** | Table of every runtime variant (Rc/OE/LK) the base XML entry covers, plus VMX128 siblings. |
|
||||
| **Syntax** | Canonical assembly template with `[OE]`/`[Rc]`/`[LK]` bracketed-modifier notation. |
|
||||
| **Encoding** | Form name, opcode word, primary/extended opcodes, and bit-layout table. |
|
||||
| **Operands** | Every bit-field operand, its role per variant, and its meaning. |
|
||||
| **Register Effects** | Unconditional vs. conditional reads and writes, per variant. |
|
||||
| **Status-Register Effects** | CR0/CR1/CR6, XER[CA/OV/SO], FPSCR, VSCR updates. |
|
||||
| **Operation** | PPC-style pseudocode (`RT <- …`, `EXTS(…)`, `MEM(EA, n)`). |
|
||||
| **C Translation Example** | Minimal idiomatic C rendering a translator could emit. |
|
||||
| **Implementation References** | Direct links into `xenia-canary/` and `xenia-rs/` with line numbers. |
|
||||
| **Special Cases & Edge Conditions** | RA=0, alignment, endian byte-reverse, reservation, SPR remapping, VMX128 fusion. |
|
||||
| **Related Instructions** | Sibling cross-links. |
|
||||
| **IBM Reference** | Optional link to IBM AIX PPC reference for canonical pseudocode. |
|
||||
|
||||
Sections between the `<!-- GENERATED: BEGIN -->` and `<!-- GENERATED: END -->`
|
||||
sentinels are produced by [`generator/generate_manual.py`](generator/generate_manual.py)
|
||||
and re-generated on every run. Sections outside the sentinels are
|
||||
hand-written and preserved across re-runs.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Bit numbering** follows PowerPC (big-endian, bit 0 = MSB).
|
||||
- **GPRs** are 64-bit. 32-bit operations operate on bits `[32:63]` and
|
||||
conventionally write the low 32 bits with zero- or sign-extension into
|
||||
the high 32 bits. Page pseudocode makes this explicit when it matters.
|
||||
- **Vector registers** are 128-bit with **lane 0 at the most-significant
|
||||
byte** (big-endian lane indexing). On x86 hosts byte-swap is applied at
|
||||
load/store to preserve this invariant.
|
||||
- **CR** is 8 × 4-bit fields `CR0..CR7`, each `{LT, GT, EQ, SO}`. The record
|
||||
form of arithmetic instructions writes CR0 (integer) or CR1 (FPU); the
|
||||
record form of vector compare writes CR6 = `{all-true, 0, all-false, 0}`.
|
||||
- **XER** holds `SO`, `OV`, and `CA` at bits 32, 33, 34 respectively
|
||||
(PPC bit numbering), plus a 7-bit string length used by `lswi`/`stswi`.
|
||||
|
||||
## Categories
|
||||
|
||||
| Category | Families | XML entries | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [Integer ALU](categories/alu.md) | 70 | 70 | Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition. |
|
||||
| [Branch & System](categories/branch.md) | 9 | 9 | Unconditional / conditional branches, branch to LR/CTR, traps, system call. |
|
||||
| [Control / CR / SPR](categories/control.md) | 26 | 26 | Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio). |
|
||||
| [Floating-Point](categories/fpu.md) | 33 | 33 | IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves. |
|
||||
| [Memory](categories/memory.md) | 56 | 112 | Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx. |
|
||||
| [VMX (Altivec)](categories/vmx.md) | 144 | 193 | 128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers. |
|
||||
| [VMX128](categories/vmx128.md) | 12 | 12 | Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields. |
|
||||
|
||||
## Forms
|
||||
|
||||
| Form | Count | Page |
|
||||
| --- | --- | --- |
|
||||
| `A` | 21 | [forms/A.md](forms/A.md) |
|
||||
| `B` | 1 | [forms/B.md](forms/B.md) |
|
||||
| `D` | 40 | [forms/D.md](forms/D.md) |
|
||||
| `DCBZ` | 2 | [forms/DCBZ.md](forms/DCBZ.md) |
|
||||
| `DS` | 5 | [forms/DS.md](forms/DS.md) |
|
||||
| `I` | 1 | [forms/I.md](forms/I.md) |
|
||||
| `M` | 3 | [forms/M.md](forms/M.md) |
|
||||
| `MD` | 4 | [forms/MD.md](forms/MD.md) |
|
||||
| `MDS` | 2 | [forms/MDS.md](forms/MDS.md) |
|
||||
| `SC` | 1 | [forms/SC.md](forms/SC.md) |
|
||||
| `VA` | 14 | [forms/VA.md](forms/VA.md) |
|
||||
| `VC` | 13 | [forms/VC.md](forms/VC.md) |
|
||||
| `VX` | 117 | [forms/VX.md](forms/VX.md) |
|
||||
| `VX128` | 34 | [forms/VX128.md](forms/VX128.md) |
|
||||
| `VX128_1` | 16 | [forms/VX128_1.md](forms/VX128_1.md) |
|
||||
| `VX128_2` | 1 | [forms/VX128_2.md](forms/VX128_2.md) |
|
||||
| `VX128_3` | 15 | [forms/VX128_3.md](forms/VX128_3.md) |
|
||||
| `VX128_4` | 2 | [forms/VX128_4.md](forms/VX128_4.md) |
|
||||
| `VX128_5` | 1 | [forms/VX128_5.md](forms/VX128_5.md) |
|
||||
| `VX128_P` | 1 | [forms/VX128_P.md](forms/VX128_P.md) |
|
||||
| `VX128_R` | 5 | [forms/VX128_R.md](forms/VX128_R.md) |
|
||||
| `X` | 117 | [forms/X.md](forms/X.md) |
|
||||
| `XFL` | 1 | [forms/XFL.md](forms/XFL.md) |
|
||||
| `XFX` | 4 | [forms/XFX.md](forms/XFX.md) |
|
||||
| `XL` | 12 | [forms/XL.md](forms/XL.md) |
|
||||
| `XO` | 21 | [forms/XO.md](forms/XO.md) |
|
||||
| `XS` | 1 | [forms/XS.md](forms/XS.md) |
|
||||
|
||||
## Regenerating this manual
|
||||
|
||||
```bash
|
||||
python3 generator/generate_manual.py
|
||||
```
|
||||
|
||||
Re-running the generator is safe — it only rewrites sections between
|
||||
`<!-- GENERATED: BEGIN -->` / `<!-- GENERATED: END -->` sentinels. Add
|
||||
your hand-written content below the `END` marker and it will be
|
||||
preserved.
|
||||
159
migration/project-root/ppc-manual/TEMPLATE.md
Normal file
159
migration/project-root/ppc-manual/TEMPLATE.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Page Template — Canonical Structure
|
||||
|
||||
This file is the **reference for every instruction page** in the manual.
|
||||
It documents the section order, what each section is for, and the
|
||||
formatting conventions that the Phase 1 generator emits and Phase 2
|
||||
reviewers enhance.
|
||||
|
||||
Do **not** copy this file to create a new page. Instruction pages are
|
||||
produced by `generator/generate_manual.py` and should stay under the
|
||||
generator's control.
|
||||
|
||||
---
|
||||
|
||||
## Page anatomy (section order)
|
||||
|
||||
Every page follows this skeleton:
|
||||
|
||||
```markdown
|
||||
# `<base-mnem>` — <Short description>
|
||||
|
||||
> **Category:** [<Group label>](../categories/<group>.md) · **Form:** [<FORM>](../forms/<FORM>.md) · **Opcode:** `0x........`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
<table — runtime Rc/OE/LK variants + VMX128 siblings + memory u/x/ux siblings>
|
||||
|
||||
## Syntax
|
||||
<fenced `asm` block with bracketed-modifier notation, one template per variant>
|
||||
|
||||
## Encoding
|
||||
<per XML-entry subsection: opcode word, primary/extended, sync flag, bit-layout table>
|
||||
|
||||
## Operands
|
||||
<field table — name, role per mnemonic, IBM-style description>
|
||||
|
||||
## Register Effects
|
||||
<per-mnemonic: unconditional / conditional reads + writes>
|
||||
|
||||
## Status-Register Effects
|
||||
<CR0/CR1/CR6, XER[CA/OV/SO], FPSCR, VSCR updates — only ones that apply>
|
||||
|
||||
## Operation (pseudocode)
|
||||
<fenced PPC-style pseudocode block — IBM convention>
|
||||
|
||||
## C Translation Example
|
||||
<fenced `c` block — minimal, idiomatic rendering a translator could emit>
|
||||
|
||||
## Implementation References
|
||||
<xenia-canary XML / emit line, xenia-rs opcode / decoder / interpreter line range,
|
||||
plus a <details> block with the frozen interpreter body snapshot>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
<hand-written. RA0 / alignment / endian / reservation / SPR-remap / VMX128 fusion>
|
||||
|
||||
## Related Instructions
|
||||
<hand-written. Cross-links to siblings that didn't land on the same page>
|
||||
|
||||
## IBM Reference
|
||||
<optional. Link to IBM AIX PowerPC reference when it adds value>
|
||||
```
|
||||
|
||||
### Sentinel markers
|
||||
|
||||
The `<!-- GENERATED: BEGIN -->` / `<!-- GENERATED: END -->` pair
|
||||
separates machine-generated content from hand-written enhancement.
|
||||
|
||||
- **Inside the sentinels:** rewritten on every generator run. Do not
|
||||
edit — your changes will be overwritten.
|
||||
- **Outside the sentinels:** preserved across regenerations. Put all
|
||||
Phase 2 enhancements there.
|
||||
|
||||
If a page's generated section is missing the `END` sentinel the
|
||||
generator assumes a human has fully taken over and leaves the file
|
||||
untouched.
|
||||
|
||||
---
|
||||
|
||||
## Writing conventions
|
||||
|
||||
### Pseudocode
|
||||
|
||||
Follow IBM's AIX reference style:
|
||||
|
||||
```
|
||||
EA <- (RA|0) + EXTS(d)
|
||||
RT <- ZEXT32_to_64(MEM(EA, 4))
|
||||
```
|
||||
|
||||
- `<-` for assignment, `(X)` for "value of register X".
|
||||
- `(RA|0)` for RA0 fields — literal 0 when the encoded register is 0.
|
||||
- `EXTS(x)` = sign-extend, `ZEXT/SEXT<n>_to_<m>(x)` explicit when helpful.
|
||||
- `||` for bit concatenation.
|
||||
- `MEM(EA, n)` for an n-byte big-endian memory read.
|
||||
- `CR[BF] <- ...` for CR field updates.
|
||||
- Register bit numbering in pseudocode is **PowerPC big-endian** (bit 0
|
||||
is the MSB). `(RS)[56:63]` is the low byte of RS.
|
||||
|
||||
### C translation
|
||||
|
||||
- Use a pseudo-context of `r[]` (GPRs), `f[]` (FPRs), `v[]` (128-bit
|
||||
vectors), `cr[]` (CR fields), and scalars `xer`, `lr`, `ctr`, `pc`.
|
||||
- Prefer `int64_t`/`uint64_t` for 64-bit ops; cast to `int32_t` for
|
||||
32-bit sub-word ops and explicitly sign/zero-extend.
|
||||
- Use `mem_read_u32_be` / `mem_write_vec128_be` style helpers to make
|
||||
the big-endian memory model explicit.
|
||||
- Show the base form always; add one annotated variant (e.g. `if
|
||||
(insn.Rc) ...`) only when it genuinely changes the translation.
|
||||
|
||||
### Bit ordering
|
||||
|
||||
PowerPC big-endian bit numbering is used throughout the manual (bit 0
|
||||
is MSB). The same convention is used by IBM's reference and by the XML
|
||||
source. Do not switch to Intel-style LSB-first numbering.
|
||||
|
||||
### Vector lane indexing
|
||||
|
||||
Altivec / VMX uses **big-endian lane indexing**: lane 0 is the
|
||||
most-significant 16 bytes (or 4 words / 2 doublewords) of the vector.
|
||||
On little-endian hosts (x86-64) byte-swap is applied at load/store to
|
||||
preserve this invariant — call that out in the "Special Cases" section
|
||||
when relevant.
|
||||
|
||||
---
|
||||
|
||||
## Example: fully-reviewed page
|
||||
|
||||
See `alu/addx.md` after Phase 2 review for the canonical look. It
|
||||
demonstrates:
|
||||
|
||||
- Complete operand descriptions (not TODO stubs).
|
||||
- Pseudocode with explicit CR/XER updates.
|
||||
- C translation covering the base form plus Rc and OE variants.
|
||||
- "Special Cases" calling out 32-bit vs 64-bit overflow tracking.
|
||||
- Cross-links to `addcx`, `addex`, `subfx`.
|
||||
|
||||
---
|
||||
|
||||
## Golden-path spot-check list (Phase 2 review order)
|
||||
|
||||
These pages are reviewed first; they anchor the expected quality bar:
|
||||
|
||||
| Page | Reason |
|
||||
| --- | --- |
|
||||
| `alu/addx.md` | XO form with Rc+OE — representative ALU |
|
||||
| `alu/addi.md` | D form with RA0 semantics — representative immediate |
|
||||
| `memory/lwz.md` | D form load family (lwz/lwzu/lwzx/lwzux) |
|
||||
| `memory/stvx.md` | Vector store with alignment mask |
|
||||
| `memory/lvsl.md` | Permute-control generator |
|
||||
| `branch/bclrx.md` | BO/BI conditional with LK |
|
||||
| `branch/bx.md` | Absolute/relative branch |
|
||||
| `control/mfspr.md` | SPR halves-swap encoding |
|
||||
| `control/mtcrf.md` | CR field-mask update |
|
||||
| `fpu/faddx.md` | Double-precision FPU + CR1 via Rc |
|
||||
| `vmx/vaddfp.md` | VMX + VMX128 sibling on one page |
|
||||
| `vmx/vperm.md` | Byte permute — lane-indexing discipline |
|
||||
| `vmx128/vpkd3d128.md` | VMX128 orphan (no non-128 sibling) |
|
||||
138
migration/project-root/ppc-manual/alu/addcx.md
Normal file
138
migration/project-root/ppc-manual/alu/addcx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `addcx` — Add Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000014`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addc` | `addcx` | — | Add Carrying |
|
||||
| `addco` | `addcx` | OE=1 | Add Carrying |
|
||||
| `addc.` | `addcx` | Rc=1 | Add Carrying |
|
||||
| `addco.` | `addcx` | OE=1, Rc=1 | Add Carrying |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addc[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addcx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000014`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `10`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addcx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | addcx: read | Source GPR. |
|
||||
| `RD` | addcx: write | Destination GPR. |
|
||||
| `CA` | addcx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `OE` | addcx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | addcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addcx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + (RB)
|
||||
CA <- carry_out_of_32_or_64_bit_add((RA), (RB))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:64`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L64)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:861`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L861)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:190-205`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L190-L205)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addcx => {
|
||||
// PPCBUG-013+020: 32-bit truncation; CA from u32 unsigned compare.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let result32 = ra32.wrapping_add(rb32);
|
||||
ctx.xer_ca = if result32 < ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Carry-out is mandatory.** `XER[CA]` is updated unconditionally — `addcx` exists *to* produce the carry. It seeds a multi-word add chain that continues with [`addex`](addex.md) for middle words and [`addzex`](addzex.md)/[`addmex`](addmex.md) for the final word.
|
||||
- **Carry detection by overflow comparison.** Xenia computes `CA = (result < RA)` — the standard unsigned-add overflow test. Equivalent to `CA = (RA + RB) >> 64` mathematically. This is correct for the 64-bit operand width that the Xenon implements; the spec also allows a 32-bit width selected by the implementation but the 970/Xenon use 64-bit add throughout.
|
||||
- **No trap on signed overflow.** `addco`/`addco.` only set `XER[OV]` and sticky `XER[SO]`; they do not raise an exception. Xenia-rs leaves the `OE` branch as a `// TODO` (see [`addx`](addx.md) for the same gap).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `Rc=1` CR0 compare reads `result as i32 as i64` in [`interpreter.rs:97`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L97); spec demands the full 64-bit signed compare. Flag this as a xenia-rs quirk if you need bit-exact behaviour.
|
||||
- **`XER[SO]` is sticky** — only `mcrxr` clears it. The `Rc=1` form folds it into `CR0[SO]`.
|
||||
- **Operand aliasing is legal**, just like [`addx`](addx.md). `addc r3, r3, r3` simply doubles `r3` and records whether the result wrapped.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addx`](addx.md) — same operation, but does **not** update `XER[CA]`.
|
||||
- [`addex`](addex.md) — `RA + RB + XER[CA]`; chains a multi-word add after `addcx`.
|
||||
- [`addmex`](addmex.md), [`addzex`](addzex.md) — terminate a carry chain by adding `−1` or `0` to `XER[CA]`.
|
||||
- [`addic`](addic.md), [`addicx`](addicx.md) — D-form immediate variants that also write `XER[CA]`.
|
||||
- [`subfcx`](subfcx.md) — the dual: produces a borrow-out in `XER[CA]`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addc` (Add Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addc-add-carrying-instruction)
|
||||
- PowerISA v2.07B, Book I, §3.3.8 — Fixed-Point Add with Carry; defines `XER[CA]` semantics independent of operand width.
|
||||
139
migration/project-root/ppc-manual/alu/addex.md
Normal file
139
migration/project-root/ppc-manual/alu/addex.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `addex` — Add Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000114`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `adde` | `addex` | — | Add Extended |
|
||||
| `addeo` | `addex` | OE=1 | Add Extended |
|
||||
| `adde.` | `addex` | Rc=1 | Add Extended |
|
||||
| `addeo.` | `addex` | OE=1, Rc=1 | Add Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
adde[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000114`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `138`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addex: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | addex: read | Source GPR. |
|
||||
| `CA` | addex: read | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `RD` | addex: write | Destination GPR. |
|
||||
| `OE` | addex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | addex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addex`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`, `CA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + (RB) + CA
|
||||
CA <- carry_out_of_the_add
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:83`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L83)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:868`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L868)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:206-222`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L206-L222)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addex => {
|
||||
// PPCBUG-014+020: 32-bit truncation; CA from u32 unsigned compare.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = ra32.wrapping_add(rb32).wrapping_add(ca);
|
||||
ctx.xer_ca = if result32 < ra32 || (ca != 0 && result32 == ra32) { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128) + (ca as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Carry-in is consumed and carry-out is produced.** `addex` is the middle link of a multi-word add chain seeded by [`addcx`](addcx.md): `RT ← RA + RB + XER[CA]`, then `XER[CA] ← carry_out`.
|
||||
- **Carry-out detection handles both edges.** Xenia checks `result < ra OR (ca != 0 && result == ra)` — that second clause covers the case where adding the carry-in alone causes the result to *exactly equal* `RA` (i.e. `RB == ~0 && CA == 1`), which still constitutes overflow. The naive `result < ra` test misses it.
|
||||
- **No trap on signed overflow.** `addeo`/`addeo.` only update `XER[OV]` and sticky `XER[SO]`; xenia-rs leaves the `OE` branch unimplemented.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `Rc=1` arm uses `result as i32 as i64`. For multi-word adds whose final word is the high 32 bits of a 64-bit value, this distinction matters; see [`addx`](addx.md).
|
||||
- **`XER[CA]` must be initialised** by an earlier [`addcx`](addcx.md), [`subfcx`](subfcx.md), or `mtspr` to XER. Reading stale `CA` from an unrelated instruction is the most common bug in hand-written multi-word arithmetic.
|
||||
- **`XER[SO]` is sticky** until cleared by `mcrxr`; `Rc=1` copies it into `CR0[SO]`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addcx`](addcx.md) — seeds the carry chain (no `CA` read, sets `CA`).
|
||||
- [`addmex`](addmex.md), [`addzex`](addzex.md) — terminate the chain (`RA + −1 + CA`, `RA + 0 + CA`).
|
||||
- [`addx`](addx.md) — plain add without `XER[CA]`.
|
||||
- [`subfex`](subfex.md) — dual: `~RA + RB + XER[CA]`, used for multi-word subtract.
|
||||
- [`addic`](addic.md) / [`addicx`](addicx.md) — immediate carrying adds that *initialise* a chain.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `adde` (Add Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-adde-add-extended-instruction)
|
||||
- PowerISA v2.07B, Book I, §3.3.8 — defines the `RA + RB + CA` carry-chain composition.
|
||||
116
migration/project-root/ppc-manual/alu/addi.md
Normal file
116
migration/project-root/ppc-manual/alu/addi.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# `addi` — Add Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x38000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addi` | `addi` | — | Add Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addi [RD], [RA0], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addi` — form `D`
|
||||
|
||||
- **Opcode word:** `0x38000000`
|
||||
- **Primary opcode (bits 0–5):** `14`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | addi: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `SIMM` | addi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | addi: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addi`
|
||||
|
||||
- **Reads (always):** `RA0`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if RA = 0 then RT <- EXTS(SIMM)
|
||||
else RT <- (RA) + EXTS(SIMM)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* addi RT, RA, SIMM — RA=0 means literal 0 */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
r[insn.RT] = base + (uint64_t)(int64_t)(int16_t)insn.SIMM;
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:103`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L103)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:338`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L338)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:114-120`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L114-L120)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addi => {
|
||||
// PPCBUG-001: 32-bit ABI. `li rT, -1` (= addi rT, r0, -1) must produce
|
||||
// 0x00000000_FFFFFFFF, not 0xFFFFFFFF_FFFFFFFF (sign-extended simm16).
|
||||
let ra_val = if instr.ra() == 0 { 0 } else { ctx.gpr[instr.ra()] };
|
||||
ctx.gpr[instr.rd()] = ra_val.wrapping_add(instr.simm16() as i64 as u64) as u32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA0` semantics.** When the encoded `RA` field is `0` the operand is the literal constant `0`, **not** the value of `r0`. This lets `addi rT, 0, SIMM` load a constant (the `li rT, SIMM` simplified mnemonic). To use `r0`'s value you must use a register-register add (`add RT, r0, RB` through a temp) or an instruction without `RA0` semantics.
|
||||
- **No flags written.** Unlike `add`, `addi` cannot be `Rc` or `OE` — no CR or XER update. Use [`addic`](addic.md) if you need `XER[CA]`, or [`addicx`](addicx.md) (`addic.`) if you need both `XER[CA]` and a CR0 update.
|
||||
- **Immediate is 16-bit signed** (`SIMM`, range `−32768 … +32767`), sign-extended to 64 bits before the add. No carry/overflow is produced regardless of the result.
|
||||
- **Simplified mnemonics.** Assemblers recognise several aliases that all assemble to `addi`:
|
||||
- `li RT, SIMM` ≡ `addi RT, 0, SIMM` (load immediate; relies on `RA0`).
|
||||
- `la RT, D(RA)` ≡ `addi RT, RA, D` (load address; purely syntactic).
|
||||
- `subi RT, RA, SIMM` ≡ `addi RT, RA, −SIMM`.
|
||||
- **PC-relative idiom.** `addi RT, RA, D` is the low-half completion of a two-instruction address load preceded by [`addis`](addis.md) `RT, 0, HI`. The assembler emits `@ha`/`@l` relocations so the low half can be negative without corrupting the high half (add-compensation).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addis`](addis.md) — same encoding family but the immediate is shifted left by 16 bits. Together they build any 32-bit constant or PC-relative address.
|
||||
- [`addic`](addic.md), [`addicx`](addicx.md) — D-form adds that **do** set `XER[CA]` (and CR0 for the record form).
|
||||
- [`addx`](addx.md) — the register-register form.
|
||||
- [`subfic`](subfic.md) — reverse-subtract immediate (`imm − RA`) with carry.
|
||||
- [`ori`](ori.md), [`oris`](oris.md) — the alternative D-form constant-building instructions (but these don't add, they OR).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addi` (Add Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addi-add-immediate-instruction)
|
||||
- [AIX 7.3 — `li` (Load Immediate, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-li-load-immediate)
|
||||
123
migration/project-root/ppc-manual/alu/addic.md
Normal file
123
migration/project-root/ppc-manual/alu/addic.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `addic` — Add Immediate Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x30000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addic` | `addic` | — | Add Immediate Carrying |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addic [RD], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addic` — form `D`
|
||||
|
||||
- **Opcode word:** `0x30000000`
|
||||
- **Primary opcode (bits 0–5):** `12`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addic: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | addic: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | addic: write | Destination GPR. |
|
||||
| `CA` | addic: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addic`
|
||||
|
||||
- **Reads (always):** `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addic`: **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + EXTS(SIMM)
|
||||
CA <- carry_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addic`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addic"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:117`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L117)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:336`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L336)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:135-144`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L135-L144)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addic => {
|
||||
// PPCBUG-002: 32-bit ABI. CA must be from a 32-bit unsigned compare;
|
||||
// canary's `AddDidCarry` truncates both operands to int32 first.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let imm32 = instr.simm16() as i32 as u32;
|
||||
let result32 = ra32.wrapping_add(imm32);
|
||||
ctx.xer_ca = if result32 < ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Immediate is sign-extended.** `SIMM` is a 16-bit signed value extended to 64 bits before the add. So `addic r3, r4, -1` adds `0xFFFFFFFFFFFFFFFF` to `r4` — it does not zero-extend.
|
||||
- **`XER[CA]` always written.** Unlike [`addi`](addi.md), this instruction exists to seed a multi-word add chain with an immediate. Carry-out is computed with the same `result < ra` unsigned-overflow check as [`addcx`](addcx.md).
|
||||
- **No `Rc` bit available.** This is the *non-record* form. For a record-form variant that also updates `CR0`, use [`addicx`](addicx.md) (`addic.`).
|
||||
- **No `OE` bit either.** `addic` cannot raise / observe signed overflow — only the carry. If you need `XER[OV]` you must use the XO-form [`addcx`](addcx.md) with `OE=1`.
|
||||
- **`RA = 0` reads register r0.** Unlike [`addi`](addi.md), `addic` does **not** treat the `RA` field of zero as a literal zero. The PowerISA gives this instruction the regular `RA` semantics, not `RA0`.
|
||||
- **Subtract immediate carrying via negation.** There is no `subic` mnemonic; assemblers synthesise `subic RT, RA, value` as `addic RT, RA, -value` (when `value` fits in 16 bits signed).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addicx`](addicx.md) — same operation plus `Rc=1` CR0 update.
|
||||
- [`addi`](addi.md) — D-form add immediate without `XER[CA]`.
|
||||
- [`addis`](addis.md) — shifted form (immediate << 16).
|
||||
- [`addcx`](addcx.md) — XO-form: register operands, sets `XER[CA]`.
|
||||
- [`subfic`](subfic.md) — D-form: `RT ← SIMM − RA` with `XER[CA]`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addic` (Add Immediate Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addic-add-immediate-carrying-instruction)
|
||||
132
migration/project-root/ppc-manual/alu/addicx.md
Normal file
132
migration/project-root/ppc-manual/alu/addicx.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `addic.` — Add Immediate Carrying and Record
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x34000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addic.` | `addic.` | — | Add Immediate Carrying and Record |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addic. [RD], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addic.` — form `D`
|
||||
|
||||
- **Opcode word:** `0x34000000`
|
||||
- **Primary opcode (bits 0–5):** `13`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addic.: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | addic.: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | addic.: write | Destination GPR. |
|
||||
| `CA` | addic.: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `CR` | addic.: write | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addic.`
|
||||
|
||||
- **Reads (always):** `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`, `CR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addic.`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addic.`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addic."`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:127`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L127)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:337`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L337)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:145-154`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L145-L154)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addicx => {
|
||||
// PPCBUG-003: same fix as addic plus CR0 i32 view.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let imm32 = instr.simm16() as i32 as u32;
|
||||
let result32 = ra32.wrapping_add(imm32);
|
||||
ctx.xer_ca = if result32 < ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`Rc` bit is implicit, not encoded.** `addic.` has its *own* primary opcode (13) distinct from `addic`'s (12); there is no `Rc` field to set. The two forms are sibling D-form instructions, not flag variants of one encoding.
|
||||
- **CR0 update is unconditional.** Unlike XO-form `Rc=1` instructions, `addic.` always updates `CR0` from the result; the `.` is part of the mnemonic itself.
|
||||
- **Common idiom: `addic. rN, rN, -1`** — decrements `rN` and sets `CR0[EQ]` when it reaches zero, in a single instruction. Frequently used as a loop counter (often paired with `bne+ loop`).
|
||||
- **`XER[CA]` written same as [`addic`](addic.md).** The carry-out from the unsigned 64-bit add is recorded; the `.` only adds the CR update on top.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:65`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L65) computes `result as i32 as i64`; spec demands a full 64-bit compare-to-zero. The truncation is a documented xenia-rs quirk shared with the rest of the carrying-add family.
|
||||
- **`SIMM` is sign-extended** to 64 bits before the add — `addic. r3, r4, -1` adds `~0` and never sets `CR0[EQ]` unless `r4 == 1`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addic`](addic.md) — same op without the CR0 update.
|
||||
- [`addi`](addi.md), [`addis`](addis.md) — immediate adds without `XER[CA]`.
|
||||
- [`addcx`](addcx.md) — XO-form register equivalent.
|
||||
- [`subfic`](subfic.md) — `RT ← SIMM − RA` with `XER[CA]` (no record form exists).
|
||||
- [`cmpi`](cmpi.md) — explicit immediate compare when the carry side-effect would be unwanted.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addic.` (Add Immediate Carrying and Record)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addic-add-immediate-carrying-record-instruction)
|
||||
121
migration/project-root/ppc-manual/alu/addis.md
Normal file
121
migration/project-root/ppc-manual/alu/addis.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `addis` — Add Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x3c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addis` | `addis` | — | Add Immediate Shifted |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addis [RD], [RA0], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addis` — form `D`
|
||||
|
||||
- **Opcode word:** `0x3c000000`
|
||||
- **Primary opcode (bits 0–5):** `15`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA0` | addis: read | Source GPR; when the encoded register number is 0 the operand is the literal 64-bit zero, **not** `r0`. |
|
||||
| `SIMM` | addis: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | addis: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addis`
|
||||
|
||||
- **Reads (always):** `RA0`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if RA = 0 then RT <- EXTS(SIMM) << 16
|
||||
else RT <- (RA) + (EXTS(SIMM) << 16)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* addis RT, RA, SIMM — RA=0 means literal 0 */
|
||||
uint64_t base = (insn.RA == 0) ? 0 : r[insn.RA];
|
||||
r[insn.RT] = base + ((uint64_t)(int64_t)(int16_t)insn.SIMM << 16);
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addis`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addis"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:138`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L138)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:339`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L339)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:121-134`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L121-L134)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addis => {
|
||||
// Xbox 360 user mode is 32-bit ABI (MSR.SF=0), so addis must
|
||||
// produce a value whose upper 32 bits don't pollute downstream
|
||||
// 64-bit arithmetic. The PPC ISA in 64-bit mode sign-extends
|
||||
// simm16 before the shift, producing 0xFFFFFFFF_xxxx0000 for
|
||||
// negative simm16 (high bit set). When this value flows into
|
||||
// a 64-bit subfc against a zero-extended lwz value, the unsigned
|
||||
// 64-bit comparison yields wrong CA. Truncate to 32 bits to
|
||||
// simulate 32-bit ABI behavior.
|
||||
let ra_val = if instr.ra() == 0 { 0u64 } else { ctx.gpr[instr.ra()] };
|
||||
let result = ra_val.wrapping_add((instr.simm16() as i64 as u64) << 16);
|
||||
ctx.gpr[instr.rd()] = result as u32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA0` semantics.** When the `RA` field encodes 0, the operand is the literal 64-bit zero, **not** `r0`. This makes `addis RT, 0, hi16` the canonical "load high half" idiom. To use `r0`'s actual value as a base, copy it via `mr` first or use a different opcode.
|
||||
- **Immediate is sign-extended *then* shifted left 16.** So `addis r3, 0, 0x8000` writes `0xFFFFFFFF80000000`, not `0x000000008000_0000`. The 32-bit sign extension surprise is the most common bug in hand-written PPC assembly.
|
||||
- **Forms the high half of a 32-bit immediate.** The classic `lis rT, hi; ori rT, rT, lo` (or `lis`/`addi`) sequence builds a full 32-bit constant. `lis rT, val` is a simplified mnemonic for `addis rT, 0, val`.
|
||||
- **No `XER[CA]`, no `XER[OV]`, no `Rc`.** This instruction has no status side-effects whatsoever. Use [`addic`](addic.md) or [`addcx`](addcx.md) if a carry is required.
|
||||
- **64-bit `RA` operand.** The shift-and-add is 64-bit on the Xenon; the immediate's sign-extension fills the high 48 bits. So `addis r3, r4, -1` adds `0xFFFFFFFFFFFF0000` to a 64-bit `r4`.
|
||||
- **No overflow detection.** `lis r3, 0x7FFF; addis r3, r3, 0x7FFF` happily wraps without comment.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addi`](addi.md) — D-form add immediate, no shift; same `RA0` rule.
|
||||
- [`addic`](addic.md), [`addicx`](addicx.md) — immediate adds that also write `XER[CA]`.
|
||||
- [`oris`](oris.md), [`ori`](ori.md) — pair with `addis`/`lis` to build 32-bit constants without affecting CR or XER.
|
||||
- [`addx`](addx.md), [`addcx`](addcx.md) — XO-form register adds.
|
||||
- `lis` (simplified) — assembler shorthand for `addis RT, 0, value`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addis` (Add Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addis-add-immediate-shifted-instruction)
|
||||
- [AIX 7.3 — `lis` (Load Immediate Shifted, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-li-lis-load-immediate-load-immediate-shifted)
|
||||
136
migration/project-root/ppc-manual/alu/addmex.md
Normal file
136
migration/project-root/ppc-manual/alu/addmex.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `addmex` — Add to Minus One Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d4`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addme` | `addmex` | — | Add to Minus One Extended |
|
||||
| `addmeo` | `addmex` | OE=1 | Add to Minus One Extended |
|
||||
| `addme.` | `addmex` | Rc=1 | Add to Minus One Extended |
|
||||
| `addmeo.` | `addmex` | OE=1, Rc=1 | Add to Minus One Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addme[OE][Rc] [RD], [RA]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addmex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0001d4`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `234`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addmex: read | Source GPR (`r0`–`r31`). |
|
||||
| `CA` | addmex: read; addmex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `RD` | addmex: write | Destination GPR. |
|
||||
| `OE` | addmex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | addmex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addmex`
|
||||
|
||||
- **Reads (always):** `RA`, `CA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addmex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + CA + 0xFFFF_FFFF_FFFF_FFFF
|
||||
CA <- carry_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addmex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addmex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:152`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L152)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:873`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L873)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:239-254`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L239-L254)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addmex => {
|
||||
// PPCBUG-016+020: 32-bit truncation. RT = RA + CA - 1.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = ra32.wrapping_add(ca).wrapping_sub(1);
|
||||
ctx.xer_ca = if ra32 != 0 || ca != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = (ra32 as i32 as i128) + (ca as i128) - 1;
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No `RB` field used.** `addmex` is encoded in XO-form but ignores the `RB` slot — assemblers must still emit a value (typically zero). Disassemblers that parse a non-zero `RB` should not flag it as illegal; it is simply unused.
|
||||
- **Operation is `RA + CA + (−1)`**, i.e. `RA - 1 + CA`. Used to terminate a multi-word *subtract* chain when the high source word is implicitly all-ones (e.g. computing `-x` as `~x + 1` across 128 bits).
|
||||
- **Carry-out predicate is `RA != 0 OR CA != 0`.** Equivalently, `CA' = NOT(RA == 0 AND CA == 0)`. Adding `−1` to anything except a zero-with-no-carry produces a carry-out (no borrow needed). This terse form in xenia-rs is correct but easy to misread.
|
||||
- **Overflow not implemented in xenia-rs.** The `OE=1` path is silently a no-op; spec says set `XER[OV]` if the signed result wraps.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:139`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L139) — same quirk as the rest of the add family.
|
||||
- **`XER[CA]` must be initialised** by an earlier carrying instruction. `addme` is a *terminator*, not a seed.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addzex`](addzex.md) — terminate a chain with `RA + 0 + CA` (no `−1`).
|
||||
- [`addex`](addex.md) — middle-of-chain `RA + RB + CA`.
|
||||
- [`addcx`](addcx.md) — seeds a chain.
|
||||
- [`subfmex`](subfmex.md) — subtract dual: `~RA + (−1) + CA`.
|
||||
- [`negx`](negx.md) — single-instruction two's-complement negate.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addme` (Add to Minus One Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addme-add-minus-one-extended-instruction)
|
||||
148
migration/project-root/ppc-manual/alu/addx.md
Normal file
148
migration/project-root/ppc-manual/alu/addx.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# `addx` — Add
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000214`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `add` | `addx` | — | Add |
|
||||
| `addo` | `addx` | OE=1 | Add |
|
||||
| `add.` | `addx` | Rc=1 | Add |
|
||||
| `addo.` | `addx` | OE=1, Rc=1 | Add |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
add[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000214`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `266`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | addx: read | Source GPR. |
|
||||
| `RD` | addx: write | Destination GPR. |
|
||||
| `OE` | addx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | addx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + (RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* add / add. / addo / addo. (XO-form) */
|
||||
uint64_t a = r[insn.RA], b = r[insn.RB];
|
||||
uint64_t result = a + b;
|
||||
r[insn.RT] = result;
|
||||
if (insn.OE) { bool ov = (~(a ^ b) & (a ^ result)) >> 63;
|
||||
if (ov) { xer.OV = 1; xer.SO = 1; } else xer.OV = 0; }
|
||||
if (insn.Rc) update_cr0_signed((int64_t)result);
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:50`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L50)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:875`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L875)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:175-189`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L175-L189)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addx => {
|
||||
// PPCBUG-012+020: 32-bit ABI writeback truncation + CR0 i32 view.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let result32 = ra32.wrapping_add(rb32);
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = (ra32 as i32 as i128) + (rb32 as i32 as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
RT <- (RA) + (RB) ; modulo 2^64, carry discarded
|
||||
if OE then
|
||||
XER[OV] <- (~(RA ^ RB) & (RA ^ RT))[0] ; signed overflow: same-sign inputs, opposite-sign result
|
||||
XER[SO] <- XER[SO] | XER[OV]
|
||||
if Rc then
|
||||
CR0[LT,GT,EQ] <- signed_compare(RT, 0) ; 64-bit comparison on the Xenon
|
||||
CR0[SO] <- XER[SO]
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No trap on overflow.** `addo` / `addo.` record overflow in `XER[OV]` and sticky-set `XER[SO]`. A trap can only be produced by a separate `td`/`tw` instruction examining the result.
|
||||
- **Signed-overflow predicate.** Overflow occurs iff both addends share a sign bit and the result has the opposite sign bit: `OV = ((~(a ^ b)) & (a ^ rt)) >> 63`. Unsigned carry is *not* tracked — use [`addcx`](addcx.md) when you need `XER[CA]`.
|
||||
- **`XER[SO]` is sticky.** Once set, it remains set until cleared by `mcrxr`. The `.` record forms copy it into `CR0[SO]`.
|
||||
- **64-bit CR update on Xenon.** The Xbox 360 Xenon CPU is 64-bit, so `add.` compares the full 64-bit result against zero. **Xenia-rs presently truncates to 32 bits** before the CR update (`result as i32 as i64` in [`interpreter.rs:95`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L95)). If your translator must match xenia bit-for-bit, emit a 32-bit compare; if it must be spec-correct, emit a 64-bit compare. Most Xbox 360 object code works either way because results that overflow 32 bits are rare outside of explicit 64-bit math.
|
||||
- **OE overflow detection not emulated in xenia-rs.** The `addo` / `addo.` branch in `interpreter.rs` is a `TODO` stub. A faithful translator should still emit the overflow check — titles rarely observe `XER[OV]`, but it's occasionally used by profiling / sanity-checking code paths.
|
||||
- **Operand aliasing.** `add r3, r3, r3`, `add r3, r3, r4`, `add r3, r4, r3` are all legal. The addition reads both source operands before writing `RT`.
|
||||
- **No immediate form.** For `RT = RA + imm` use [`addi`](addi.md) / [`addis`](addis.md). Those are distinct opcodes, not a flag on `add`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addcx`](addcx.md) — produces the carry-out in `XER[CA]`.
|
||||
- [`addex`](addex.md) — sums `(RA) + (RB) + XER[CA]` (carry-in chain).
|
||||
- [`addmex`](addmex.md), [`addzex`](addzex.md) — add to `−1` or `0` with carry-in (used to propagate borrows across multiword subtracts).
|
||||
- [`addi`](addi.md), [`addis`](addis.md) — D-form immediate adds; no `Rc`/`OE`.
|
||||
- [`addic`](addic.md), [`addicx`](addicx.md) — D-form adds that set `XER[CA]`.
|
||||
- [`subfx`](subfx.md) — the dual: `RT ← (RB) − (RA)`.
|
||||
- [`negx`](negx.md) — two's-complement negate.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `add` (Add)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-add-instruction)
|
||||
- [PowerISA v2.07B, Book I, §3.3.8 — Fixed-Point Arithmetic Instructions](https://openpowerfoundation.org/specifications/isa/) (overflow predicate, CR0 / `XER[SO]` semantics).
|
||||
135
migration/project-root/ppc-manual/alu/addzex.md
Normal file
135
migration/project-root/ppc-manual/alu/addzex.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# `addzex` — Add to Zero Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000194`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `addze` | `addzex` | — | Add to Zero Extended |
|
||||
| `addzeo` | `addzex` | OE=1 | Add to Zero Extended |
|
||||
| `addze.` | `addzex` | Rc=1 | Add to Zero Extended |
|
||||
| `addzeo.` | `addzex` | OE=1, Rc=1 | Add to Zero Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
addze[OE][Rc] [RD], [RA]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `addzex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000194`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `202`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | addzex: read | Source GPR (`r0`–`r31`). |
|
||||
| `CA` | addzex: read; addzex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `RD` | addzex: write | Destination GPR. |
|
||||
| `OE` | addzex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | addzex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `addzex`
|
||||
|
||||
- **Reads (always):** `RA`, `CA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `addzex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) + CA
|
||||
CA <- carry_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`addzex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addzex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:172`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L172)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:8`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L8)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:870`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L870)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:223-238`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L223-L238)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::addzex => {
|
||||
// PPCBUG-015+020: 32-bit truncation.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = ra32.wrapping_add(ca);
|
||||
ctx.xer_ca = if result32 < ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = (ra32 as i32 as i128) + (ca as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No `RB` field used.** Like [`addmex`](addmex.md), this XO-form instruction ignores the `RB` slot. Assemblers emit zero there.
|
||||
- **Operation is `RA + 0 + CA` ≡ `RA + CA`.** Used to terminate the *high word* of a multi-word add chain seeded by [`addcx`](addcx.md). After the low-word `addc` produces the carry, all middle words use [`addex`](addex.md), and the final word that has no register operand uses `addze`.
|
||||
- **Carry-out is the simple unsigned overflow test** `result < ra` — same predicate as [`addcx`](addcx.md). `CA' = 1` only if `RA == ~0 && CA == 1`.
|
||||
- **`OE=1` not implemented in xenia-rs.** The interpreter has no overflow branch at all; spec asks for the standard signed-overflow detect.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs** (truncation in [`interpreter.rs:128`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L128) — see [`addx`](addx.md) for context).
|
||||
- **Common idiom: extracting a carry as a 0/1.** `addze rT, 0` (or `addze rT, rN` where `rN == 0`) materialises `XER[CA]` into `rT` as a plain integer.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`addmex`](addmex.md) — terminate with `RA + (−1) + CA` instead of `+0`.
|
||||
- [`addex`](addex.md) — middle of a multi-word add chain.
|
||||
- [`addcx`](addcx.md) — seeds the chain.
|
||||
- [`subfzex`](subfzex.md) — subtract dual: `~RA + 0 + CA`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `addze` (Add to Zero Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-addze-add-zero-extended-instruction)
|
||||
123
migration/project-root/ppc-manual/alu/andcx.md
Normal file
123
migration/project-root/ppc-manual/alu/andcx.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `andcx` — AND with Complement
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000078`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `andc` | `andcx` | — | AND with Complement |
|
||||
| `andc.` | `andcx` | Rc=1 | AND with Complement |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
andc[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `andcx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000078`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `60`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | andcx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | andcx: read | Source GPR. |
|
||||
| `RA` | andcx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | andcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `andcx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `andcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) & ~(RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`andcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:647`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L647)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:768`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L768)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:534-541`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L534-L541)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::andcx => {
|
||||
// PPCBUG-033: !rb on u64 flips upper 32 bits — active poisoning.
|
||||
let rs32 = ctx.gpr[instr.rs()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = (rs32 & !rb32) as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`andc RA, RS, RB` computes `RS AND (NOT RB)`.** The complement is applied to `RB`, not `RS`. Useful for clearing a bitmask: `andc r3, r3, r4` clears in `r3` every bit set in `r4`.
|
||||
- **Common idiom: `andc r3, r3, r3`** zeroes `r3` (every bit ANDed with its own complement). Cheaper-looking than `xor r3, r3, r3` on some pipelines but functionally identical; the assembler often prefers the `xor` idiom.
|
||||
- **Operand convention is the X-form one** (`RA` is the destination, `RS` and `RB` are sources). Same gotcha as [`andx`](andx.md).
|
||||
- **No `OE`/`XER` side effects.** Only `CR0` is updated when `Rc=1`.
|
||||
- **64-bit operation** on Xenon; the AND is computed across all 64 bits of `RS` and `~RB`. Xenia-rs uses Rust's bitwise `!` on `u64`, which is the correct full-width complement.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:352`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L352) — same truncation pattern.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`andx`](andx.md) — plain AND (no complement).
|
||||
- [`nandx`](nandx.md) — NAND (`~(RS & RB)`).
|
||||
- [`orcx`](orcx.md) — OR with complement; sister `c` form.
|
||||
- [`eqvx`](eqvx.md) — `~(RS ^ RB)` (NXOR / equivalence).
|
||||
- [`norx`](norx.md) — NOR.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `andc` (AND with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andc-complement-instruction)
|
||||
127
migration/project-root/ppc-manual/alu/andisx.md
Normal file
127
migration/project-root/ppc-manual/alu/andisx.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `andis.` — AND Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x74000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `andis.` | `andis.` | — | AND Immediate Shifted |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
andis. [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `andis.` — form `D`
|
||||
|
||||
- **Opcode word:** `0x74000000`
|
||||
- **Primary opcode (bits 0–5):** `29`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | andis.: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | andis.: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | andis.: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | andis.: write | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `andis.`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `andis.`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`andis.`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andis."`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:665`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L665)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:352`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L352)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:505-511`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L505-L511)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::andisx => {
|
||||
// PPCBUG-023: 32-bit ABI CR0 view. `andis. rA, rS, 0x8000` to test
|
||||
// sign bit of a 32-bit word now correctly classifies bit 31 = 1 as LT.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & ((instr.uimm16() as u64) << 16);
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Always `Rc=1`.** Like [`andix`](andix.md), the dot is part of the mnemonic; no plain `andis` exists.
|
||||
- **Immediate is shifted left 16, zero-extended.** Effective mask is `(UIMM << 16) & 0xFFFFFFFF`, so the only bits that can survive in `RA` are bits 32–47 (in PowerISA bit numbering, equivalent to bits 16–31 of the low 32 bits) of `RS`. Bits 0–31 and bits 48–63 of `RA` are forced to zero.
|
||||
- **Together with `andi.` covers the entire low 32 bits.** Any 32-bit mask can be applied with `andis. + andi.` (two instructions). Larger masks need `rlwinm` or a constructed register operand to [`andx`](andx.md).
|
||||
- **High 32 bits of result are always zero.** Because the immediate is at bits 32–47, no information from `RS[0:31]` survives. Useful as a quick "extract bits 32–47, zero the rest" primitive.
|
||||
- **CR0 update is unconditional** and uses the standard signed-compare-to-zero semantics with `XER[SO]` folded into `SO`.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `result as i32 as i64` truncation in [`interpreter.rs:326`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L326) is harmless: the result is bounded by `0x00000000_FFFF0000`, which fits the 32-bit window exactly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`andix`](andix.md) — companion (immediate not shifted).
|
||||
- [`andx`](andx.md), [`andcx`](andcx.md) — register AND.
|
||||
- [`oris`](oris.md), [`xoris`](xoris.md) — sister immediate-shifted logicals.
|
||||
- [`rlwinmx`](rlwinmx.md) — full mask-and-rotate when the bits of interest aren't aligned to a 16-bit boundary.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `andis.` (AND Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andis-immediate-shifted-instruction)
|
||||
126
migration/project-root/ppc-manual/alu/andix.md
Normal file
126
migration/project-root/ppc-manual/alu/andix.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `andi.` — AND Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x70000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `andi.` | `andi.` | — | AND Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
andi. [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `andi.` — form `D`
|
||||
|
||||
- **Opcode word:** `0x70000000`
|
||||
- **Primary opcode (bits 0–5):** `28`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | andi.: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | andi.: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | andi.: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | andi.: write | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `andi.`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `andi.`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]` (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`andi.`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andi."`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:657`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L657)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:351`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L351)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:499-504`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L499-L504)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::andix => {
|
||||
// PPCBUG-020: 32-bit ABI CR0 view.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & (instr.uimm16() as u64);
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Always `Rc=1`.** There is no `andi` (without the dot). The mnemonic is `andi.` and the encoding always updates `CR0`. If you need a non-record AND-with-immediate, you have to materialise the immediate first (e.g. with `li`/`lis`) and use [`andx`](andx.md).
|
||||
- **Immediate is zero-extended.** The 16-bit `UIMM` is widened with zeros, so `andi. rA, rS, 0xFFFF` masks `rS` to its low 16 bits — the high 48 bits of the 64-bit register are forced to zero.
|
||||
- **Cannot mask the high half of a register in one instruction.** The immediate covers bits 48–63 only; for higher bits use [`andisx`](andisx.md) (covers bits 32–47) or compose with `rlwinm`/`rldicl`.
|
||||
- **CR0 update is unconditional.** This is part of the encoding, not a flag — the primary opcode (28) *is* `andi.`.
|
||||
- **Common idiom: `andi. r0, rN, mask`** to test bits without disturbing the source — but note `r0` is overwritten and `CR0` is set. If you only need the CR result, prefer `extrwi`/`rlwinm.` for arbitrary masks.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** Since the AND result has zeros in bits 0–47, the low-32 truncation in [`interpreter.rs:321`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L321) is harmless here — the result fits in 16 bits, so spec and xenia agree.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`andisx`](andisx.md) — same op with immediate shifted left 16 (covers bits 32–47).
|
||||
- [`andx`](andx.md), [`andcx`](andcx.md) — register AND family.
|
||||
- [`ori`](ori.md), [`oris`](oris.md), [`xori`](xori.md), [`xoris`](xoris.md) — sister immediate logicals (notably *without* a record form).
|
||||
- [`rlwinmx`](rlwinmx.md) — for masks that don't fit into a 16-bit immediate.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `andi.` (AND Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-andi-immediate-instruction)
|
||||
122
migration/project-root/ppc-manual/alu/andx.md
Normal file
122
migration/project-root/ppc-manual/alu/andx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `andx` — AND
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000038`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `and` | `andx` | — | AND |
|
||||
| `and.` | `andx` | Rc=1 | AND |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
and[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `andx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000038`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `28`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | andx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | andx: read | Source GPR. |
|
||||
| `RA` | andx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | andx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `andx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `andx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) & (RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`andx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:637`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L637)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:9`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L9)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:760`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L760)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:528-533`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L528-L533)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::andx => {
|
||||
// PPCBUG-032+020: 32-bit ABI CR0 view (latent under clean inputs).
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] & ctx.gpr[instr.rb()];
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operand convention is reversed.** Unlike the arithmetic XO-form (`add RT, RA, RB`), the logical X-form writes `RA` and reads `RS`/`RB`: `and RA, RS, RB`. The destination is the **second** operand encoded. This convention applies to the entire and/or/xor family; mixing them up is a frequent disassembly error.
|
||||
- **No `OE`, no `XER[CA]`, no `XER[OV]`.** Logical operations never affect XER. Only `Rc=1` updates `CR0` (signed compare against zero, with `SO ← XER[SO]`).
|
||||
- **64-bit AND on Xenon.** Both inputs are 64-bit GPRs; the result is the bitwise AND of all 64 bits.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The interpreter's `Rc=1` path in [`interpreter.rs:347`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L347) compares `result as i32 as i64`. For an AND whose high 32 bits are non-zero but low 32 bits are zero (e.g. `r3 = 0x1_0000_0000`, `and. r4, r3, r3`), spec sets CR0 to GT but xenia would set EQ. Flag this if reproducing CR-sensitive behaviour.
|
||||
- **Operand aliasing.** `and RA, RA, RA` is a no-op except for the optional CR0 update — this is the canonical "test register against zero" pattern when no `cmpwi` is desired (though `cmpwi` is more typical).
|
||||
- **No simplified mnemonic for AND-immediate.** Use [`andix`](andix.md) (`andi.`) or [`andisx`](andisx.md) (`andis.`) for immediate operands; both are *always* record forms (no plain `andi`).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`andcx`](andcx.md) — AND with complement: `RA ← RS & ~RB`.
|
||||
- [`andix`](andix.md), [`andisx`](andisx.md) — D-form AND immediate (always `Rc=1`).
|
||||
- [`nandx`](nandx.md) — NAND.
|
||||
- [`orx`](orx.md), [`orcx`](orcx.md), [`xorx`](xorx.md), [`eqvx`](eqvx.md), [`norx`](norx.md) — sister logical instructions.
|
||||
- [`cmp`](cmp.md), [`cmpi`](cmpi.md) — explicit zero/value test when CR-only effect is wanted without overwriting `RA`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `and` (AND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-instruction)
|
||||
- [AIX 7.3 — Reference: PowerPC instruction set](https://www.ibm.com/docs/en/aix/7.3.0?topic=reference-instruction-set)
|
||||
153
migration/project-root/ppc-manual/alu/cmp.md
Normal file
153
migration/project-root/ppc-manual/alu/cmp.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# `cmp` — Compare
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cmp` | `cmp` | — | Compare |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cmp [CRFD], [L], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cmp` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000000`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `0`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `L` | cmp: read | Operand-length bit for compare instructions (`0 ⇒ 32-bit`, `1 ⇒ 64-bit`). |
|
||||
| `RA` | cmp: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | cmp: read | Source GPR. |
|
||||
| `CRFD` | cmp: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cmp`
|
||||
|
||||
- **Reads (always):** `L`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS((RB)[32:63])
|
||||
else a,b <- (RA), (RB)
|
||||
CR[BF] <- signed_compare(a, b) || XER[SO]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cmp`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmp"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:523`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L523)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:749`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L749)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:863-885`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L863-L885)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cmp => {
|
||||
let bf = instr.crfd();
|
||||
if instr.l() {
|
||||
let ra = ctx.gpr[instr.ra()] as i64;
|
||||
let rb = ctx.gpr[instr.rb()] as i64;
|
||||
ctx.cr[bf] = crate::context::CrField {
|
||||
lt: ra < rb,
|
||||
gt: ra > rb,
|
||||
eq: ra == rb,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
} else {
|
||||
let ra = ctx.gpr[instr.ra()] as i32;
|
||||
let rb = ctx.gpr[instr.rb()] as i32;
|
||||
ctx.cr[bf] = crate::context::CrField {
|
||||
lt: ra < rb,
|
||||
gt: ra > rb,
|
||||
eq: ra == rb,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
if L = 0 then ; 32-bit compare
|
||||
a <- EXTS((RA)[32:63]) ; sign-extend low word to 64
|
||||
b <- EXTS((RB)[32:63])
|
||||
else ; 64-bit compare
|
||||
a <- (RA)
|
||||
b <- (RB)
|
||||
CR[BF] <- { LT: a <s b, GT: a >s b, EQ: a = b, SO: XER[SO] } ; signed
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`BF` is a CR field (0–7), not a bit.** The `crfD` operand encodes which of the eight 4-bit CR fields is updated. Assemblers write it as `crN` where `N ∈ 0..7`. The simplified mnemonic `cmpw RA, RB` ≡ `cmp cr0, 0, RA, RB` is universal in Xbox 360 code.
|
||||
- **`L` bit selects width.** `L = 0` (the usual `cmpw` / `cmpd`-is-rare path) performs a *32-bit* signed compare of `RA[32:63]` and `RB[32:63]`, both sign-extended to 64 bits. `L = 1` (`cmpd`) performs a full 64-bit signed compare.
|
||||
- **Signed.** Use [`cmpl`](cmpl.md) / [`cmpli`](cmpli.md) for unsigned comparisons. Confusing signed/unsigned is the most common compare-family bug in hand-written asm.
|
||||
- **SO is always copied from `XER[SO]`.** This makes overflow observable across arithmetic/compare sequences: an `addo.` followed by `beq` can branch on the record-form flag while `bso` can inspect the sticky overflow.
|
||||
- **`cr0` is the default for record-form ALU**; by convention assemblers and generators reserve `cr0` for the chain of `Rc=1` instructions and use `cr1..cr7` (or `cmp` to an explicit field) for standalone compares. Don't assume `cmp` writes `cr0` unless the `BF` operand says so.
|
||||
- **No register is written** beyond the 4-bit CR field. `cmp` has no `Rc` or `OE` bit.
|
||||
- **Xenia-rs quirk.** The interpreter recomputes `EQ` after the signed compare to guard against a subtract-cancellation edge case; this is a defensive belt-and-braces against the 32-bit narrowing path. Functionally equivalent to the spec.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cmpi`](cmpi.md) — signed compare against a 16-bit immediate.
|
||||
- [`cmpl`](cmpl.md), [`cmpli`](cmpli.md) — unsigned versions.
|
||||
- [`cmpw`](cmp.md), [`cmpd`](cmp.md) — simplified mnemonics selecting `L`.
|
||||
- [`mcrxr`](mcrxr.md) — move `XER[SO..CA]` into a CR field and clear them; used to reset sticky overflow.
|
||||
- Every `Rc=1` ALU instruction ([`addx`](addx.md), [`subfx`](subfx.md), [`andx`](andx.md), …) — these implicitly perform a signed-compare-to-zero into `cr0`; use explicit `cmp` only when comparing two non-zero values or using a non-zero CR field.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cmp` (Compare)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmp-compare-instruction)
|
||||
- [AIX 7.3 — `cmpw` / `cmpd` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmpw-compare-word)
|
||||
143
migration/project-root/ppc-manual/alu/cmpi.md
Normal file
143
migration/project-root/ppc-manual/alu/cmpi.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# `cmpi` — Compare Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x2c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cmpi` | `cmpi` | — | Compare Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cmpi [CRFD], [L], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cmpi` — form `D`
|
||||
|
||||
- **Opcode word:** `0x2c000000`
|
||||
- **Primary opcode (bits 0–5):** `11`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `L` | cmpi: read | Operand-length bit for compare instructions (`0 ⇒ 32-bit`, `1 ⇒ 64-bit`). |
|
||||
| `RA` | cmpi: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | cmpi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | cmpi: write | Destination GPR. |
|
||||
| `CRFD` | cmpi: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cmpi`
|
||||
|
||||
- **Reads (always):** `L`, `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if L = 0 then a,b <- EXTS((RA)[32:63]), EXTS(SIMM)
|
||||
else a,b <- (RA), EXTS(SIMM)
|
||||
CR[BF] <- signed_compare(a, b) || XER[SO]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cmpi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:552`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L552)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:335`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L335)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:824-849`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L824-L849)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cmpi => {
|
||||
let bf = instr.crfd();
|
||||
if instr.l() {
|
||||
// 64-bit compare. Compare directly so boundary i64 values
|
||||
// (e.g. ra=i64::MIN, imm=1) don't mis-sign through a
|
||||
// wrapped subtract.
|
||||
let ra = ctx.gpr[instr.ra()] as i64;
|
||||
let imm = instr.simm16() as i64;
|
||||
ctx.cr[bf] = crate::context::CrField {
|
||||
lt: ra < imm,
|
||||
gt: ra > imm,
|
||||
eq: ra == imm,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
} else {
|
||||
let ra = ctx.gpr[instr.ra()] as i32;
|
||||
let imm = instr.simm16() as i32;
|
||||
ctx.cr[bf] = crate::context::CrField {
|
||||
lt: ra < imm,
|
||||
gt: ra > imm,
|
||||
eq: ra == imm,
|
||||
so: ctx.xer_so != 0,
|
||||
};
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Immediate is sign-extended.** `SIMM` is treated as a signed 16-bit value in the range `[-32768, 32767]` and sign-extended to the operand width. Use [`cmpli`](cmpli.md) for unsigned comparisons against a 16-bit value.
|
||||
- **`L` bit selects width.** `L = 0` (the usual `cmpwi`) compares the low 32 bits of `RA` (sign-extended) against `EXTS(SIMM)`; `L = 1` (`cmpdi`) does a full 64-bit signed compare. Most Xbox 360 code uses `cmpwi` because pointers and counters are 32-bit ABI.
|
||||
- **Simplified mnemonics dominate disassembly.** `cmpwi crN, RA, SIMM` ≡ `cmpi crN, 0, RA, SIMM` and `cmpdi crN, RA, SIMM` ≡ `cmpi crN, 1, RA, SIMM`. The default CR field is `cr0` if omitted.
|
||||
- **`BF` is a CR field (0–7), not a bit.** Same convention as [`cmp`](cmp.md). Distinct standalone compares should target `cr1..cr7` to avoid clobbering the implicit `cr0` chain set up by `Rc=1` arithmetic.
|
||||
- **SO is copied from `XER[SO]`.** This makes overflow observable downstream of an `addo.` / `mulo.` etc. via `bso`/`bns`.
|
||||
- **Xenia-rs quirk.** The interpreter recomputes `EQ` after the signed subtract, defending against the same 32-bit narrowing edge case noted in [`cmp`](cmp.md). Functionally equivalent to spec.
|
||||
- **No register written** other than the 4-bit CR field — there is no `Rc` or `OE` bit.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cmp`](cmp.md) — register-register signed compare.
|
||||
- [`cmpli`](cmpli.md) — unsigned compare against immediate (zero-extended).
|
||||
- [`cmpl`](cmpl.md) — unsigned register compare.
|
||||
- `cmpwi`, `cmpdi` (simplified mnemonics) — select `L=0` / `L=1`.
|
||||
- [`mcrxr`](mcrxr.md) — clear sticky overflow before a fresh compare sequence.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cmpi` (Compare Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpi-compare-immediate-instruction)
|
||||
- [AIX 7.3 — `cmpwi` / `cmpdi` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmpwi-compare-word-immediate)
|
||||
127
migration/project-root/ppc-manual/alu/cmpl.md
Normal file
127
migration/project-root/ppc-manual/alu/cmpl.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `cmpl` — Compare Logical
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000040`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cmpl` | `cmpl` | — | Compare Logical |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cmpl [CRFD], [L], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cmpl` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000040`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `32`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `L` | cmpl: read | Operand-length bit for compare instructions (`0 ⇒ 32-bit`, `1 ⇒ 64-bit`). |
|
||||
| `RA` | cmpl: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | cmpl: read | Source GPR. |
|
||||
| `CRFD` | cmpl: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cmpl`
|
||||
|
||||
- **Reads (always):** `L`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if L = 0 then a,b <- (RA)[32:63], (RB)[32:63]
|
||||
else a,b <- (RA), (RB)
|
||||
CR[BF] <- unsigned_compare(a, b) || XER[SO]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cmpl`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpl"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:579`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L579)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:761`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L761)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:886-894`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L886-L894)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cmpl => {
|
||||
let bf = instr.crfd();
|
||||
if instr.l() {
|
||||
ctx.update_cr_unsigned(bf, ctx.gpr[instr.ra()], ctx.gpr[instr.rb()]);
|
||||
} else {
|
||||
ctx.update_cr_unsigned(bf, ctx.gpr[instr.ra()] as u32 as u64, ctx.gpr[instr.rb()] as u32 as u64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Unsigned compare.** Treats both operands as unsigned magnitudes. The simplified mnemonics are `cmplw` (`L=0`) and `cmpld` (`L=1`).
|
||||
- **`L = 0`: 32-bit operands.** Xenia narrows both registers via `as u32 as u64` so the high 32 bits of `RA`/`RB` are ignored — this matches spec `(RA)[32:63]` semantics. Most Xbox 360 code uses this mode.
|
||||
- **`L = 1`: full 64-bit unsigned compare.** Used in 64-bit pointer arithmetic; rare in game code but appears in kernel-side helpers.
|
||||
- **SO is copied from `XER[SO]`.** `cmpl` does not clear or set sticky overflow; it just exposes the current `SO` in the destination CR field's `SO` slot.
|
||||
- **`BF` is a CR field 0–7.** Same convention as [`cmp`](cmp.md). Two consecutive `cmpl` instructions with the same `BF` simply overwrite the previous result.
|
||||
- **Common signed/unsigned bug.** Misusing `cmp` instead of `cmpl` (or vice versa) for pointer comparisons is the canonical bug in PPC porting; pointers are always unsigned in C semantics. Always cross-check the comparison polarity in disassembly.
|
||||
- **No `Rc`/`OE`** and no GPR write — purely a CR-field producer.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cmp`](cmp.md) — signed register compare.
|
||||
- [`cmpli`](cmpli.md) — unsigned compare against a 16-bit immediate.
|
||||
- [`cmpi`](cmpi.md) — signed immediate compare.
|
||||
- `cmplw`, `cmpld` (simplified) — preferred forms in disassembly.
|
||||
- [`mcrxr`](mcrxr.md) — clear sticky overflow.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cmpl` (Compare Logical)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpl-compare-logical-instruction)
|
||||
- [AIX 7.3 — `cmplw` / `cmpld` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmplw-compare-logical-word)
|
||||
127
migration/project-root/ppc-manual/alu/cmpli.md
Normal file
127
migration/project-root/ppc-manual/alu/cmpli.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `cmpli` — Compare Logical Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x28000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cmpli` | `cmpli` | — | Compare Logical Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cmpli [CRFD], [L], [RA], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cmpli` — form `D`
|
||||
|
||||
- **Opcode word:** `0x28000000`
|
||||
- **Primary opcode (bits 0–5):** `10`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `L` | cmpli: read | Operand-length bit for compare instructions (`0 ⇒ 32-bit`, `1 ⇒ 64-bit`). |
|
||||
| `RA` | cmpli: read | Source GPR (`r0`–`r31`). |
|
||||
| `UIMM` | cmpli: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `CRFD` | cmpli: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cmpli`
|
||||
|
||||
- **Reads (always):** `L`, `RA`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if L = 0 then a,b <- (RA)[32:63], UIMM
|
||||
else a,b <- (RA), (0 || UIMM)
|
||||
CR[BF] <- unsigned_compare(a, b) || XER[SO]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cmpli`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpli"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:608`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L608)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:13`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L13)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:334`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L334)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:850-862`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L850-L862)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cmpli => {
|
||||
let bf = instr.crfd();
|
||||
if instr.l() {
|
||||
let ra = ctx.gpr[instr.ra()];
|
||||
let imm = instr.uimm16() as u64;
|
||||
ctx.update_cr_unsigned(bf, ra, imm);
|
||||
} else {
|
||||
let ra = ctx.gpr[instr.ra()] as u32 as u64;
|
||||
let imm = instr.uimm16() as u64;
|
||||
ctx.update_cr_unsigned(bf, ra, imm);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Immediate is zero-extended.** `UIMM` is a 16-bit value extended with zeros, so the comparable range is `[0, 65535]`. To compare against a value with high bits set, materialise it in a register with `lis`/`ori` and use [`cmpl`](cmpl.md).
|
||||
- **`L` bit selects width.** `L = 0` (`cmplwi`) zero-extends `RA[32:63]` to 64 bits and compares against the 16-bit immediate (also zero-extended). `L = 1` (`cmpldi`) compares the full 64-bit `RA` against the immediate.
|
||||
- **Simplified mnemonics dominate.** `cmplwi cr0, RA, UIMM` ≡ `cmpli cr0, 0, RA, UIMM`; the assembler injects the `L` bit automatically.
|
||||
- **No sign-extension surprises.** Unlike [`cmpi`](cmpi.md), the immediate cannot be negative; `cmpli` always tests an unsigned magnitude.
|
||||
- **Common idiom: `cmplwi rA, 0`** to test a register for zero — slightly clearer in disassembly than `cmpwi rA, 0` because it doesn't suggest signed semantics. Both produce the same `EQ` result for a zero argument.
|
||||
- **`BF` chooses one of 8 CR fields**; same convention as `cmp`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cmpl`](cmpl.md) — register-register unsigned compare.
|
||||
- [`cmpi`](cmpi.md) — signed compare against a 16-bit immediate.
|
||||
- [`cmp`](cmp.md) — register-register signed compare.
|
||||
- `cmplwi`, `cmpldi` (simplified) — most common form seen in disassembly.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cmpli` (Compare Logical Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cmpli-compare-logical-immediate-instruction)
|
||||
- [AIX 7.3 — `cmplwi` / `cmpldi` (simplified mnemonics)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-cmplwi-compare-logical-word-immediate)
|
||||
119
migration/project-root/ppc-manual/alu/cntlzdx.md
Normal file
119
migration/project-root/ppc-manual/alu/cntlzdx.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# `cntlzdx` — Count Leading Zeros Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000074`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cntlzd` | `cntlzdx` | — | Count Leading Zeros Doubleword |
|
||||
| `cntlzd.` | `cntlzdx` | Rc=1 | Count Leading Zeros Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cntlzd [RA], [RS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cntlzdx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000074`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `58`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | cntlzdx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | cntlzdx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | cntlzdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cntlzdx`
|
||||
|
||||
- **Reads (always):** `RS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `cntlzdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- number_of_leading_zero_bits((RS)) ; n in 0..64
|
||||
RA <- zero_extend(n)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cntlzdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cntlzdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:674`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L674)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:15`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L15)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:767`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L767)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:615-619`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L615-L619)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cntlzdx => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()].leading_zeros() as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Result range is `0..=64`.** When `RS == 0`, every bit is a leading zero and `RA = 64`. When the high bit (`RS[0]`) is set, `RA = 0`. Any intermediate value yields the count of high-order zeros before the first 1-bit.
|
||||
- **Counts across the full 64 bits.** Use [`cntlzwx`](cntlzwx.md) when you only want to count the low 32 bits.
|
||||
- **Useful as `floor(log2(x)) = 63 − cntlzd(x)`** for nonzero `x`. Frequently used in fast normalization, priority encoders, and bit-vector operations.
|
||||
- **`RB` field unused.** This is X-form but only `RS` is read; `RB` is a placeholder slot.
|
||||
- **`Rc=1` quirk.** `update_cr_signed(0, RA as i64)` is correct in xenia-rs because the result fits in 7 bits and is non-negative. The CR0 result will always be `EQ` (when `RS != 0` and `RA != 0`? — actually `EQ` only when `RS[0] == 1`, i.e. `RA == 0`) or `GT` (when `RS != 0` so `RA > 0`); never `LT`. `EQ` corresponds to "high bit set in `RS`", a useful one-instruction sign test for negative-as-signed values.
|
||||
- **No `XER` side effects.** Counts neither overflow nor carry.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cntlzwx`](cntlzwx.md) — 32-bit version (counts only `RS[32:63]`).
|
||||
- [`sldx`](sldx.md), [`srdx`](srdx.md) — pair with `cntlzd` for normalisation.
|
||||
- [`rldiclx`](rldiclx.md) — for extracting individual bit positions once the leading-zero count is known.
|
||||
- [`cmpi`](cmpi.md) / [`cmp`](cmp.md) — alternative for testing the high bit.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cntlzd` (Count Leading Zeros Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cntlzd-count-leading-zeros-double-word-instruction)
|
||||
121
migration/project-root/ppc-manual/alu/cntlzwx.md
Normal file
121
migration/project-root/ppc-manual/alu/cntlzwx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `cntlzwx` — Count Leading Zeros Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000034`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cntlzw` | `cntlzwx` | — | Count Leading Zeros Word |
|
||||
| `cntlzw.` | `cntlzwx` | Rc=1 | Count Leading Zeros Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cntlzw[Rc] [RA], [RS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cntlzwx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000034`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `26`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | cntlzwx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | cntlzwx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | cntlzwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cntlzwx`
|
||||
|
||||
- **Reads (always):** `RS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `cntlzwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- number_of_leading_zero_bits((RS)[32:63]) ; n in 0..32
|
||||
RA <- zero_extend(n)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cntlzwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cntlzwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:689`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L689)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:15`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L15)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:758`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L758)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:608-614`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L608-L614)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::cntlzwx => {
|
||||
// Result is 0..=32, fits in u32 with bit 31 always zero, so the
|
||||
// CR0 view is benign — use the catch-all 32-bit form for consistency.
|
||||
ctx.gpr[instr.ra()] = (ctx.gpr[instr.rs()] as u32).leading_zeros() as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operates only on the low 32 bits.** `RS[0:31]` is ignored; the count is taken on `RS[32:63]`. Result range is `0..=32`.
|
||||
- **`RA = 32` when the low 32 bits are zero**, regardless of the high 32 bits. A common pitfall: `cntlzw` after computing a 64-bit value can give a counter-intuitive result when the leading 1-bit lives in the high half.
|
||||
- **`RA = 0` when bit 32 (the sign bit of the low word) is set.** This makes `cntlzw RA, RS; cmpwi RA, 0` a one-instruction-pair "is the low half negative" test, though `srawi RA, RS, 31` is more idiomatic.
|
||||
- **High 32 bits of the result are zero.** `RA[0:31] = 0`, `RA[32:63] = count`.
|
||||
- **`Rc=1` CR0 update is small-positive-only.** Result fits in 6 bits; xenia's `as i32 as i64` truncation in [`interpreter.rs:404`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L404) is harmless. CR0 will be `EQ` only when `RS[32]` (sign bit of low word) is 1, otherwise `GT`.
|
||||
- **Useful for `floor(log2)` of a 32-bit value.** `31 - cntlzw(x)` for nonzero `x`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cntlzdx`](cntlzdx.md) — 64-bit version (counts the full register).
|
||||
- [`slwx`](slwx.md), [`srwx`](srwx.md), [`srawx`](srawx.md) — 32-bit shifts often paired with `cntlzw` for normalisation.
|
||||
- [`rlwinmx`](rlwinmx.md) — to mask off bits before counting.
|
||||
- [`cmpi`](cmpi.md), [`cmpl`](cmpl.md) — alternative ways to detect zero or sign.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cntlzw` (Count Leading Zeros Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cntlzw-count-leading-zeros-word-instruction)
|
||||
135
migration/project-root/ppc-manual/alu/divdux.md
Normal file
135
migration/project-root/ppc-manual/alu/divdux.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# `divdux` — Divide Doubleword Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000392`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `divdu` | `divdux` | — | Divide Doubleword Unsigned |
|
||||
| `divduo` | `divdux` | OE=1 | Divide Doubleword Unsigned |
|
||||
| `divdu.` | `divdux` | Rc=1 | Divide Doubleword Unsigned |
|
||||
| `divduo.` | `divdux` | OE=1, Rc=1 | Divide Doubleword Unsigned |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
divdu[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `divdux` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000392`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `457`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | divdux: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | divdux: read | Source GPR. |
|
||||
| `RD` | divdux: write | Destination GPR. |
|
||||
| `OE` | divdux: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | divdux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `divdux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `divdux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) /u (RB) ; undefined if RB=0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`divdux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divdux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:217`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L217)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:876`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L876)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:480-496`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L480-L496)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::divdux => {
|
||||
let ra = ctx.gpr[instr.ra()];
|
||||
let rb = ctx.gpr[instr.rb()];
|
||||
let ov = overflow::divd_ov_unsigned(rb);
|
||||
if ov {
|
||||
ctx.gpr[instr.rd()] = 0;
|
||||
} else {
|
||||
ctx.gpr[instr.rd()] = ra / rb;
|
||||
}
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, ov);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Single undefined case.** Division by zero (`RB == 0`). There is no `INT_MIN/−1` overflow because both operands are unsigned. Xenia-rs returns 0 for the divide-by-zero case ([`interpreter.rs:306`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L306)); spec leaves `RT` boundedly undefined.
|
||||
- **No trap on Xenon.** As with [`divdx`](divdx.md), the processor does not raise an exception; consuming code must guard `RB` first (typically `cmpdi rb, 0; beq skip`).
|
||||
- **`OE=1` should set `XER[OV]`** on `RB == 0`; xenia-rs ignores `OE` here.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:311`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L311) uses `as i64` directly, so the CR0 sign comparison reflects the full 64-bit unsigned quotient cast to signed. For very large unsigned quotients (`> INT64_MAX`) this CR0 will report `LT` even though the unsigned interpretation is positive — a rare but real source of CR-misuse bugs.
|
||||
- **Slow.** Same ~70-cycle non-pipelined cost as the signed variant; consider reciprocal multiply for hot loops.
|
||||
- **Truncating quotient.** Same C-style toward-zero rounding (trivially equal to floor for unsigned).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`divdx`](divdx.md) — signed 64-bit divide.
|
||||
- [`divwux`](divwux.md), [`divwx`](divwx.md) — 32-bit unsigned/signed.
|
||||
- [`mulldx`](mulldx.md), [`mulhdux`](mulhdux.md) — multiply pair for remainder calculation.
|
||||
- [`cmpli`](cmpli.md), [`cmpl`](cmpl.md) — guard the divisor.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `divdu` (Divide Doubleword Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divdu-divide-double-word-unsigned-instruction)
|
||||
136
migration/project-root/ppc-manual/alu/divdx.md
Normal file
136
migration/project-root/ppc-manual/alu/divdx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `divdx` — Divide Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0003d2`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `divd` | `divdx` | — | Divide Doubleword |
|
||||
| `divdo` | `divdx` | OE=1 | Divide Doubleword |
|
||||
| `divd.` | `divdx` | Rc=1 | Divide Doubleword |
|
||||
| `divdo.` | `divdx` | OE=1, Rc=1 | Divide Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
divd[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `divdx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0003d2`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `489`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | divdx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | divdx: read | Source GPR. |
|
||||
| `RD` | divdx: write | Destination GPR. |
|
||||
| `OE` | divdx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | divdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `divdx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `divdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and RB=-1)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`divdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:192`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L192)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:878`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L878)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:463-479`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L463-L479)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::divdx => {
|
||||
let ra = ctx.gpr[instr.ra()] as i64;
|
||||
let rb = ctx.gpr[instr.rb()] as i64;
|
||||
let ov = overflow::divd_ov_signed(ra, rb);
|
||||
if ov {
|
||||
ctx.gpr[instr.rd()] = 0;
|
||||
} else {
|
||||
ctx.gpr[instr.rd()] = (ra / rb) as u64;
|
||||
}
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, ov);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Two undefined-behaviour cases.** Division by zero (`RB == 0`) and signed-min divided by negative-one (`RA == INT64_MIN && RB == -1`, which would mathematically produce `2^63`, unrepresentable in `i64`). PowerISA leaves `RT` *boundedly undefined* in both cases; **xenia-rs returns 0** ([`interpreter.rs:293`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L293)). Matching this behaviour bit-for-bit is a defacto-spec on Xenon.
|
||||
- **No exception raised.** Xenon does not trap on either undefined case; the consuming code is expected to have validated `RB` first, e.g. with `cmpdi`/`bne`. If you need a trap, follow the divide with [`tw`](../control/tw.md)/`twi` (these live outside the ALU page set).
|
||||
- **`OE=1` should set `XER[OV]`** for both undefined cases plus any operand triggering overflow; xenia-rs does not implement the `OE` branch.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit here.** Unlike most ALU pages, [`interpreter.rs:298`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L298) uses `as i64` (no `as i32` truncation) — divide is one of the few xenia-rs instructions that already matches Xenon spec for the CR0 width.
|
||||
- **Latency.** Integer divide is the slowest ALU instruction on Xenon — 70+ cycles, non-pipelined. Hot inner loops avoid it via reciprocal-multiply or shift; expect to see `mulhwu`-based reciprocals in optimised disassembly.
|
||||
- **Rounds toward zero.** The signed quotient truncates toward zero, matching C99/C++11 `/` semantics. Use [`mulldx`](mulldx.md) and a subtract to recover the remainder; there is no `divmod` instruction.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`divdux`](divdux.md) — unsigned 64-bit divide.
|
||||
- [`divwx`](divwx.md), [`divwux`](divwux.md) — 32-bit signed/unsigned variants.
|
||||
- [`mulldx`](mulldx.md), [`mulhdx`](mulhdx.md) — multiply pair used to compute the remainder.
|
||||
- [`cmpi`](cmpi.md), [`cmp`](cmp.md) — guard the divisor before invoking divide.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `divd` (Divide Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divd-divide-double-word-instruction)
|
||||
- PowerISA v2.07B, Book I, §3.3.9 — defines the boundedly-undefined behaviour for `RB=0` and `INT_MIN/−1`.
|
||||
137
migration/project-root/ppc-manual/alu/divwux.md
Normal file
137
migration/project-root/ppc-manual/alu/divwux.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# `divwux` — Divide Word Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000396`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `divwu` | `divwux` | — | Divide Word Unsigned |
|
||||
| `divwuo` | `divwux` | OE=1 | Divide Word Unsigned |
|
||||
| `divwu.` | `divwux` | Rc=1 | Divide Word Unsigned |
|
||||
| `divwuo.` | `divwux` | OE=1, Rc=1 | Divide Word Unsigned |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
divwu[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `divwux` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000396`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `459`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | divwux: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | divwux: read | Source GPR. |
|
||||
| `RD` | divwux: write | Destination GPR. |
|
||||
| `OE` | divwux: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | divwux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `divwux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `divwux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA)[32:63] /u (RB)[32:63]) zero-extended to 64 ; undefined if RB=0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`divwux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divwux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:269`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L269)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:877`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L877)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:413-430`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L413-L430)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::divwux => {
|
||||
// PPCBUG-020: 32-bit ABI CR0 view.
|
||||
let ra = ctx.gpr[instr.ra()] as u32;
|
||||
let rb = ctx.gpr[instr.rb()] as u32;
|
||||
let ov = overflow::divw_ov_unsigned(rb);
|
||||
if ov {
|
||||
ctx.gpr[instr.rd()] = 0;
|
||||
} else {
|
||||
ctx.gpr[instr.rd()] = (ra / rb) as u64;
|
||||
}
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, ov);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit operands, zero-extended result.** Both `RA` and `RB` are read as their low 32 bits, unsigned (`as u32`); the quotient is computed as `u32`, then *zero-extended* to 64 bits. The high 32 bits of `RA`/`RB` are ignored on input and the high 32 bits of `RT` are zero on output.
|
||||
- **Single undefined case.** Division by zero (`RB == 0`); xenia-rs returns 0 ([`interpreter.rs:251`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L251)). No `INT_MIN/-1` case because the operands are unsigned.
|
||||
- **No trap on Xenon.** Same as [`divdx`](divdx.md) — silent undefined result.
|
||||
- **`OE=1` should set `XER[OV]` on `RB == 0`**; xenia-rs ignores this.
|
||||
- **`Rc=1` CR0 update.** [`interpreter.rs:256`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L256) uses `as i32 as i64` — for an unsigned 32-bit quotient stored in the low 32 bits with high zeros, this matches spec exactly; the i32 view will be negative iff the unsigned quotient ≥ 2^31. Worth flagging when comparing CR0 against zero after a large `divwu`.
|
||||
- **Truncating quotient.** Floor division for non-negative integers; matches C `unsigned` semantics.
|
||||
- **Same slow non-pipelined latency** as `divw`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`divwx`](divwx.md) — signed 32-bit divide.
|
||||
- [`divdux`](divdux.md), [`divdx`](divdx.md) — 64-bit variants.
|
||||
- [`mullwx`](mullwx.md) — pair to recover the remainder.
|
||||
- [`cmplwi`](cmpli.md) (simplified) — guard the divisor.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `divwu` (Divide Word Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divwu-divide-word-unsigned-instruction)
|
||||
138
migration/project-root/ppc-manual/alu/divwx.md
Normal file
138
migration/project-root/ppc-manual/alu/divwx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `divwx` — Divide Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0003d6`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `divw` | `divwx` | — | Divide Word |
|
||||
| `divwo` | `divwx` | OE=1 | Divide Word |
|
||||
| `divw.` | `divwx` | Rc=1 | Divide Word |
|
||||
| `divwo.` | `divwx` | OE=1, Rc=1 | Divide Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
divw[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `divwx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0003d6`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `491`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | divwx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | divwx: read | Source GPR. |
|
||||
| `RD` | divwx: write | Destination GPR. |
|
||||
| `OE` | divwx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
| `CR` | divwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `divwx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `OE`, `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `divwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or overflow
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`divwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:242`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L242)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:21`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L21)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:879`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L879)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:394-412`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L394-L412)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::divwx => {
|
||||
// PPCBUG-010+011 coupled: 32-bit ABI. Quotient zero-extended to u64
|
||||
// (canary explicitly uses ZeroExtend(v, INT64_TYPE)). CR0 view via i32.
|
||||
let ra = ctx.gpr[instr.ra()] as i32;
|
||||
let rb = ctx.gpr[instr.rb()] as i32;
|
||||
let ov = overflow::divw_ov_signed(ra, rb);
|
||||
if ov {
|
||||
ctx.gpr[instr.rd()] = 0;
|
||||
} else {
|
||||
ctx.gpr[instr.rd()] = (ra / rb) as u32 as u64;
|
||||
}
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, ov);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit operands, sign-extended result.** Both `RA` and `RB` are read as their low 32 bits, signed; the quotient is computed as `i32`, then sign-extended to 64 bits before being stored in `RT`. The high 32 bits of `RA`/`RB` are *ignored*.
|
||||
- **Two undefined cases:** `RB == 0` and `RA == INT32_MIN && RB == -1` (quotient `2^31` is unrepresentable). Xenia-rs returns 0 for both ([`interpreter.rs:238`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L238)); PowerISA leaves `RT` boundedly undefined.
|
||||
- **No trap on Xenon.** Like [`divdx`](divdx.md), the processor silently produces an undefined value instead of raising an exception.
|
||||
- **`OE=1` should set `XER[OV]`** in both undefined cases; xenia-rs does not implement this.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:243`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L243) uses `as i32 as i64`. This is *correct* for `divw` because the result is already a sign-extended 32-bit value — high bits agree with the low-32 sign extension. So spec and xenia agree for this instruction.
|
||||
- **Truncating quotient.** Rounds toward zero, matching C `/` for `int32_t`.
|
||||
- **Slow.** Same ~30-cycle non-pipelined cost as 64-bit divide; faster than `divd` because the underlying datapath is narrower but still much slower than multiply-then-shift reciprocal sequences.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`divwux`](divwux.md) — unsigned 32-bit divide.
|
||||
- [`divdx`](divdx.md), [`divdux`](divdux.md) — 64-bit variants.
|
||||
- [`mullwx`](mullwx.md) — pair with subtract to obtain the remainder.
|
||||
- [`extsw`](extswx.md) — manual sign-extend if you only have a 64-bit value but want 32-bit divide semantics.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `divw` (Divide Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divw-divide-word-instruction)
|
||||
111
migration/project-root/ppc-manual/alu/eieio.md
Normal file
111
migration/project-root/ppc-manual/alu/eieio.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# `eieio` — Enforce In-Order Execution of I/O
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0006ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `eieio` | `eieio` | — | Enforce In-Order Execution of I/O |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
eieio
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `eieio` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0006ac`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `854`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `eieio`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
enforce in-order execution of I/O
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`eieio`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="eieio"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:749`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L749)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:23`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L23)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:844`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L844)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Memory-ordering barrier for caching-inhibited / guarded storage.** `eieio` ensures all preceding loads/stores to caching-inhibited or guarded memory complete before any subsequent such accesses begin. It is *weaker* than [`sync`](sync.md): it does not order cacheable storage and does not flush the store queue.
|
||||
- **No register or CR effects.** Every operand field is unused; assemblers emit the canonical `0x7c0006ac` word.
|
||||
- **Used at MMIO boundaries.** Driver code touching device registers (e.g. the GPU command processor on Xenon) typically pairs writes with `eieio` to enforce write ordering at the bus.
|
||||
- **Xenia-rs is a no-op.** The interpreter trivially advances PC ([`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267)). Because xenia-rs is a single-threaded interpreter targeting userland Xbox 360 binaries — which never see real MMIO — this is correct: the host's natural program order suffices.
|
||||
- **Categorised under ALU here**, but operationally it's a memory ordering primitive (xenia-canary places it in `ppc_emit_memory.cc`). Disassembly tools may bin it differently.
|
||||
- **Distinct from `sync` and `isync`.** All three share xenia's no-op arm in [`interpreter.rs:1266`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1266); on real hardware they have very different semantics and latencies.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sync`](sync.md) — heavy memory barrier (orders *all* storage).
|
||||
- [`isync`](isync.md) — instruction-fetch barrier; refetches and re-executes after the boundary.
|
||||
- `lwsync` — lighter weight than `sync`; not in this page set.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `eieio` (Enforce In-Order Execution of I/O)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-eieio-enforce-in-order-execution-i-o-instruction)
|
||||
122
migration/project-root/ppc-manual/alu/eqvx.md
Normal file
122
migration/project-root/ppc-manual/alu/eqvx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `eqvx` — Equivalent
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000238`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `eqv` | `eqvx` | — | Equivalent |
|
||||
| `eqv.` | `eqvx` | Rc=1 | Equivalent |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
eqv[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `eqvx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000238`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `284`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | eqvx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | eqvx: read | Source GPR. |
|
||||
| `RA` | eqvx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | eqvx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `eqvx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `eqvx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- ~((RS) ^ (RB))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`eqvx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="eqvx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:704`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L704)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:796`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L796)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:578-586`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L578-L586)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::eqvx => {
|
||||
// PPCBUG-031: `eqv rA, rA, rA` is a common "set to all-ones" idiom;
|
||||
// 64-bit form gave 0xFFFFFFFFFFFFFFFF but 32-bit ABI expects 0x00000000FFFFFFFF.
|
||||
let rs32 = ctx.gpr[instr.rs()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = (!(rs32 ^ rb32)) as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **NXOR / equivalence.** `RA ← ~(RS XOR RB)`. A bit in `RA` is 1 iff the corresponding bits of `RS` and `RB` are equal. Useful as a per-bit equality test feeding into a `cntlzw` for run-length analysis.
|
||||
- **Idiom: `eqv RA, RS, RS`** sets every bit to 1 — a one-instruction `RA = -1`. Cheaper than `li RA, -1` followed by `oris`/`ori` for full 64-bit `-1`.
|
||||
- **Operand convention is X-form** (`RA` is destination; `RS`, `RB` are sources).
|
||||
- **64-bit operation** on Xenon; `~` is full 64-bit on `u64`.
|
||||
- **No `OE`, no `XER` side effects.** Only `Rc=1` updates `CR0`.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:382`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L382) truncates with `as i32 as i64`. Significant when the high 32 bits of the result differ from the low 32 — e.g. `eqv. RA, RS, RB` with `RS == 0x1_0000_0000`, `RB == 0`: spec sees `0xFFFFFFFE_FFFFFFFF` (`LT`), xenia sees `0xFFFFFFFFFFFFFFFF` (`LT`) — actually both negative here, but the *exact* CR contents differ for finer cases.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`xorx`](xorx.md) — base XOR (`eqv` is `xor` then `not`).
|
||||
- [`andx`](andx.md), [`orx`](orx.md), [`nandx`](nandx.md), [`norx`](norx.md), [`andcx`](andcx.md), [`orcx`](orcx.md) — full logical family.
|
||||
- [`xori`](xori.md), [`xoris`](xoris.md) — immediate XOR (no immediate `eqv` exists).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `eqv` (Equivalent)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-eqv-equivalent-instruction)
|
||||
121
migration/project-root/ppc-manual/alu/extsbx.md
Normal file
121
migration/project-root/ppc-manual/alu/extsbx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `extsbx` — Extend Sign Byte
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000774`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `extsb` | `extsbx` | — | Extend Sign Byte |
|
||||
| `extsb.` | `extsbx` | Rc=1 | Extend Sign Byte |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
extsb[Rc] [RA], [RS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `extsbx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000774`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `954`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | extsbx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | extsbx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | extsbx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `extsbx`
|
||||
|
||||
- **Reads (always):** `RS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `extsbx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- EXTS_8_to_64((RS)[56:63])
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`extsbx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extsbx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:714`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L714)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:849`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L849)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:589-595`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L589-L595)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::extsbx => {
|
||||
// PPCBUG-034: 32-bit ABI — sign-extend byte to i32, write zero-extended.
|
||||
// PPCBUG-036 (coupled): CR0 must view result as i32, not i64.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i8 as i32 as u32 as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Sign-extends the low 8 bits of `RS` to 64 bits.** Bit 56 of `RS` (the sign bit of the byte) becomes bits 0–55 of `RA`; bits 56–63 are copied verbatim.
|
||||
- **Common after a byte load.** `lbz` zero-extends from memory; `extsb` converts the result to a signed-byte view. Many compilers emit this pair; the recent ISA `lba`/`lbau` family is *not* available on the Xenon, so this two-instruction sequence is the canonical pattern.
|
||||
- **`Rc=1` updates CR0 from the full 64-bit signed value** — but xenia-rs truncates to 32 bits in [`interpreter.rs:384`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L384). For `extsb.` this is harmless because the result fits in 8 bits sign-extended; the low 32 bits already encode the sign correctly.
|
||||
- **Operand convention** is the X-form one (`RA` destination, `RS` source). Same as the rest of the logical family.
|
||||
- **No `XER` side effects.**
|
||||
- **`RB` field unused.** Set to 0 by assemblers; ignored on decode.
|
||||
- **Aliasing is fine.** `extsb r3, r3` rewrites `r3` in place.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`extshx`](extshx.md) — sign-extend half-word (16 bits).
|
||||
- [`extswx`](extswx.md) — sign-extend word (32 bits).
|
||||
- [`rlwinmx`](rlwinmx.md), [`rldiclx`](rldiclx.md) — for *zero*-extending or extracting non-byte-aligned fields.
|
||||
- `lbz`, `lha` (memory ops, outside this page set) — pair with this for signed byte loads.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `extsb` (Extend Sign Byte)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsb-extend-sign-byte-instruction)
|
||||
121
migration/project-root/ppc-manual/alu/extshx.md
Normal file
121
migration/project-root/ppc-manual/alu/extshx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `extshx` — Extend Sign Half Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000734`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `extsh` | `extshx` | — | Extend Sign Half Word |
|
||||
| `extsh.` | `extshx` | Rc=1 | Extend Sign Half Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
extsh[Rc] [RA], [RS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `extshx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000734`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `922`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | extshx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | extshx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | extshx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `extshx`
|
||||
|
||||
- **Reads (always):** `RS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `extshx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- EXTS_16_to_64((RS)[48:63])
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`extshx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extshx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:727`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L727)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:847`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L847)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:596-602`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L596-L602)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::extshx => {
|
||||
// PPCBUG-035: same shape as extsbx for halfwords.
|
||||
// PPCBUG-037 (coupled): CR0 i32 view.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i16 as i32 as u32 as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Sign-extends the low 16 bits of `RS` to 64 bits.** Bit 48 (the sign bit of the half-word) is replicated through bits 0–47 of `RA`.
|
||||
- **Pairs with `lhz`** to convert an unsigned half-word load into a signed half-word value. Note that `lha` already does the sign extension on load — `extsh` is mostly emitted when the half-word is computed in a register first.
|
||||
- **`Rc=1` CR0 update.** Xenia-rs uses `as i32 as i64` ([`interpreter.rs:389`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L389)) — harmless here because the sign-extended 16-bit value fits in 32 bits exactly.
|
||||
- **Operand convention** is the X-form one (`RA` destination, `RS` source).
|
||||
- **No `XER` side effects.**
|
||||
- **`RB` field unused.**
|
||||
- **Aliasing is fine.** `extsh r3, r3` is the standard "promote `r3`'s low 16 bits to a signed 64-bit value" sequence.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`extsbx`](extsbx.md) — sign-extend byte.
|
||||
- [`extswx`](extswx.md) — sign-extend word (32 bits).
|
||||
- [`rlwinmx`](rlwinmx.md) — when masking/zero-extending without sign-extension.
|
||||
- `lha` (memory op, outside this set) — combined load + sign-extend.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `extsh` (Extend Sign Half Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsh-extend-sign-half-word-instruction)
|
||||
120
migration/project-root/ppc-manual/alu/extswx.md
Normal file
120
migration/project-root/ppc-manual/alu/extswx.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# `extswx` — Extend Sign Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0007b4`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `extsw` | `extswx` | — | Extend Sign Word |
|
||||
| `extsw.` | `extswx` | Rc=1 | Extend Sign Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
extsw[Rc] [RA], [RS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `extswx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0007b4`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `986`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | extswx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RA` | extswx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | extswx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `extswx`
|
||||
|
||||
- **Reads (always):** `RS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `extswx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- EXTS_32_to_64((RS)[32:63])
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`extswx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extswx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:740`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L740)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:25`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L25)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:852`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L852)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:603-607`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L603-L607)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::extswx => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] as i32 as i64 as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Sign-extends the low 32 bits of `RS` to 64 bits.** Bit 32 (sign bit of the word) is replicated through bits 0–31 of `RA`.
|
||||
- **Used heavily in 32-to-64-bit promotion.** Most Xbox 360 ABI parameters are 32-bit; promoting a 32-bit `int` to a 64-bit GPR requires this instruction. Many functions emit it on entry to canonicalise their argument registers.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:399`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L399) uses `as i64` (no truncation) — one of the few xenia-rs sites where the spec width is honoured. The signed compare in CR0 reflects the full sign-extended value.
|
||||
- **Operand convention** is the X-form one (`RA` destination, `RS` source).
|
||||
- **No `XER` side effects.**
|
||||
- **`RB` field unused.**
|
||||
- **Aliasing is fine.** `extsw r3, r3` is the canonical "sign-extend in place" idiom.
|
||||
- **Distinct from `srawi RA, RS, 31`**, which produces the *sign mask* (`-1` if negative else `0`) rather than the sign-extended value.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`extsbx`](extsbx.md), [`extshx`](extshx.md) — narrower sign extensions.
|
||||
- [`srawix`](srawix.md) — to derive a sign mask instead.
|
||||
- [`rldiclx`](rldiclx.md) — to *zero*-extend the low 32 bits.
|
||||
- `lwa` / `lwax` (memory ops) — combined load-and-sign-extend; lives outside this set.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `extsw` (Extend Sign Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-extsw-extend-sign-word-instruction)
|
||||
111
migration/project-root/ppc-manual/alu/isync.md
Normal file
111
migration/project-root/ppc-manual/alu/isync.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# `isync` — Instruction Synchronize
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c00012c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `isync` | `isync` | — | Instruction Synchronize |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
isync
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `isync` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c00012c`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `150`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `isync`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
instruction-stream synchronisation — discards speculative state.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`isync`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="isync"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:759`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L759)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:32`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L32)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:714`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L714)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Instruction-fetch barrier.** Discards any speculatively fetched/decoded instructions and forces all subsequent ones to be re-fetched after preceding instructions complete. Required after self-modifying code, JIT-emitted code, and after MMU/page-table changes.
|
||||
- **Stronger than [`sync`](sync.md) for instruction stream**, weaker for memory stream — `isync` does not order stores against later loads. It only forces a fetch refresh.
|
||||
- **Common idiom: `dcbf` / `icbi` / `sync` / `isync`** — flush data cache, invalidate instruction cache, drain memory, refetch — used by JITs and self-modifying loaders.
|
||||
- **No operands.** Encoded as a fixed-form `XL` instruction; assemblers always emit `0x4c00012c`.
|
||||
- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267) handles `sync`/`eieio`/`isync` together. Because xenia interprets in straight-line program order without any speculative instruction cache, no barrier behaviour is needed for correctness.
|
||||
- **Privilege level: user.** Unlike most cache management ops, `isync` is unprivileged and frequently appears in userland trampolines.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sync`](sync.md) — heavy memory barrier.
|
||||
- [`eieio`](eieio.md) — I/O ordering for caching-inhibited storage.
|
||||
- `icbi`, `dcbf`, `dcbst` — cache management ops (outside this page set) usually paired with `isync`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `isync` (Instruction Synchronize)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-isync-instruction-synchronize-instruction)
|
||||
124
migration/project-root/ppc-manual/alu/mulhdux.md
Normal file
124
migration/project-root/ppc-manual/alu/mulhdux.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `mulhdux` — Multiply High Doubleword Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000012`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulhdu` | `mulhdux` | — | Multiply High Doubleword Unsigned |
|
||||
| `mulhdu.` | `mulhdux` | Rc=1 | Multiply High Doubleword Unsigned |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulhdu[Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulhdux` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000012`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `9`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulhdux: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mulhdux: read | Source GPR. |
|
||||
| `RD` | mulhdux: write | Destination GPR. |
|
||||
| `CR` | mulhdux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulhdux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mulhdux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA) * (RB))[0:63] ; high 64 of unsigned 64×64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulhdux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhdux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:311`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L311)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:860`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L860)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:454-462`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L454-L462)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulhdux => {
|
||||
let ra = ctx.gpr[instr.ra()] as u128;
|
||||
let rb = ctx.gpr[instr.rb()] as u128;
|
||||
ctx.gpr[instr.rd()] = (ra.wrapping_mul(rb) >> 64) as u64;
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Returns the high 64 bits of an unsigned 64×64 product.** Operands are zero-extended (treated as unsigned) before multiplication. Pair with [`mulldx`](mulldx.md) for the low 64 bits to form a full 128-bit unsigned product.
|
||||
- **No `OE` bit.** No overflow signal — the high half is a defined function of the inputs even when the product fills 128 bits.
|
||||
- **Xenia uses native `u128`.** [`interpreter.rs:284`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L284) widens both operands then shifts. Note `as u128` *zero*-extends, in contrast to [`mulhdx`](mulhdx.md)'s `as i64 as i128` which sign-extends — this is the entire semantic difference.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** Uses `as i64` directly. Because the high half is unsigned, treating it as signed for CR0 means very large unsigned values appear `LT` — keep this in mind when interpreting the CR0 bits after `mulhdu.`.
|
||||
- **Used in reciprocal-multiply division strategies.** Compilers may strength-reduce divide-by-constant into `mulhdu` plus a shift; appears in optimised disassembly.
|
||||
- **Slow.** Same multi-cycle cost as the signed variant.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mulldx`](mulldx.md) — low 64 bits of the same unsigned product.
|
||||
- [`mulhdx`](mulhdx.md) — signed high half.
|
||||
- [`mulhwux`](mulhwux.md) — 32-bit unsigned high half.
|
||||
- [`divdux`](divdux.md) — 64-bit unsigned divide; sometimes replaced by reciprocal `mulhdu`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulhdu` (Multiply High Doubleword Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhdu-multiply-high-double-word-unsigned-instruction)
|
||||
124
migration/project-root/ppc-manual/alu/mulhdx.md
Normal file
124
migration/project-root/ppc-manual/alu/mulhdx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `mulhdx` — Multiply High Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000092`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulhd` | `mulhdx` | — | Multiply High Doubleword |
|
||||
| `mulhd.` | `mulhdx` | Rc=1 | Multiply High Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulhd[Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulhdx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000092`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `73`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulhdx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mulhdx: read | Source GPR. |
|
||||
| `RD` | mulhdx: write | Destination GPR. |
|
||||
| `CR` | mulhdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulhdx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mulhdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA) * (RB))[0:63] ; high 64 of signed 64×64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulhdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:297`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L297)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:864`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L864)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:445-453`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L445-L453)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulhdx => {
|
||||
let ra = ctx.gpr[instr.ra()] as i64 as i128;
|
||||
let rb = ctx.gpr[instr.rb()] as i64 as i128;
|
||||
ctx.gpr[instr.rd()] = (ra.wrapping_mul(rb) >> 64) as u64;
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Returns the high 64 bits of a signed 64×64 product.** Pair with [`mulldx`](mulldx.md) (which returns the low 64 bits) to obtain the full 128-bit product. Both must be issued separately; PowerPC has no fused multiply-double-wide instruction.
|
||||
- **No `OE` bit.** This XO-form instruction has no overflow-enable variant — there is no "high half overflow" because the high half is always defined.
|
||||
- **Xenia widens to `i128` natively.** [`interpreter.rs:275`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L275) does the multiply in 128 bits then extracts the high 64. The `i64 as i128` casts ensure signed extension on both sides.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:278`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L278) uses `as i64` directly. CR0 reflects the sign of the high half: `LT` if the product is negative, `GT` if positive and large enough to overflow into the high half, `EQ` if the product fits in 64 bits *signed* (so the high half is the sign-extension of the low half — but xenia's check uses raw signed-zero compare, which equates only when the high half is exactly zero, i.e. the product is in `[0, 2^63)`).
|
||||
- **Use [`mulhdux`](mulhdux.md) for the unsigned high half.** The two instructions differ in whether the operands are sign- or zero-extended before the multiply.
|
||||
- **Slow.** 64-bit multiply is multi-cycle on Xenon; combining `mulhd` with `mulld` for a full 128-bit product roughly doubles the cost.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mulldx`](mulldx.md) — low 64 bits of the same signed product.
|
||||
- [`mulhdux`](mulhdux.md) — high 64 bits, unsigned interpretation.
|
||||
- [`mullwx`](mullwx.md), [`mulhwx`](mulhwx.md), [`mulhwux`](mulhwux.md) — 32-bit family.
|
||||
- [`divdx`](divdx.md), [`divdux`](divdux.md) — 64-bit division.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulhd` (Multiply High Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhd-multiply-high-double-word-instruction)
|
||||
126
migration/project-root/ppc-manual/alu/mulhwux.md
Normal file
126
migration/project-root/ppc-manual/alu/mulhwux.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `mulhwux` — Multiply High Word Unsigned
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000016`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulhwu` | `mulhwux` | — | Multiply High Word Unsigned |
|
||||
| `mulhwu.` | `mulhwux` | Rc=1 | Multiply High Word Unsigned |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulhwu[Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulhwux` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000016`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `11`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulhwux: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mulhwux: read | Source GPR. |
|
||||
| `RD` | mulhwux: write | Destination GPR. |
|
||||
| `CR` | mulhwux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulhwux`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mulhwux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- high_32_of_unsigned_multiply((RA)[32:63], (RB)[32:63]) zero-extended to 64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulhwux`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhwux"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:347`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L347)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:862`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L862)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:383-393`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L383-L393)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulhwux => {
|
||||
// PPCBUG-020: 32-bit ABI CR0 view.
|
||||
let ra = ctx.gpr[instr.ra()] as u32 as u64;
|
||||
let rb = ctx.gpr[instr.rb()] as u32 as u64;
|
||||
let result = ra.wrapping_mul(rb);
|
||||
ctx.gpr[instr.rd()] = (result >> 32) & 0xFFFF_FFFF;
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Inputs are the low 32 bits, zero-extended.** `RA[32:63]` and `RB[32:63]` are treated as unsigned, widened to 64-bit `u64`, multiplied; the high 32 bits of the 64-bit product land in `RT[32:63]`. Xenia masks the high 32 bits of `RT` to zero ([`interpreter.rs:231`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L231)).
|
||||
- **Pair with [`mullwx`](mullwx.md) for the full 64-bit unsigned product.** `mullw` returns the low 32 sign-extended; for unsigned use, pair `mulhwu` with `rlwinm` to mask the low half. Xbox 360 compilers commonly emit this combination.
|
||||
- **No `OE` bit.** Same family rule.
|
||||
- **`Rc=1` CR0 update.** Uses `as i32 as i64` ([`interpreter.rs:234`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L234)). Because the result is bounded by `0xFFFFFFFF` and stored only in the low 32 bits, this CR0 will report `LT` for any unsigned high half ≥ `0x80000000` — a known signed/unsigned interpretation pitfall when `Rc=1` is used with `mulhwu`.
|
||||
- **Common idiom for multi-precision arithmetic.** `mulhwu` + `mullw` + `addc` chains build extended-precision multiplies entirely in 32-bit ops; useful for cryptographic code that targets the Xenon's 32-bit ABI.
|
||||
- **Multi-cycle latency** like the rest of the multiply family.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mullwx`](mullwx.md) — low 32 bits of the same product (sign-extended).
|
||||
- [`mulhwx`](mulhwx.md) — signed high half.
|
||||
- [`mulhdux`](mulhdux.md) — 64-bit unsigned high half.
|
||||
- [`addcx`](addcx.md), [`addex`](addex.md) — used to chain 32-bit products into wider precision.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulhwu` (Multiply High Word Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhwu-multiply-high-word-unsigned-instruction)
|
||||
126
migration/project-root/ppc-manual/alu/mulhwx.md
Normal file
126
migration/project-root/ppc-manual/alu/mulhwx.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# `mulhwx` — Multiply High Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000096`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulhw` | `mulhwx` | — | Multiply High Word |
|
||||
| `mulhw.` | `mulhwx` | Rc=1 | Multiply High Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulhw[Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulhwx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000096`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `75`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulhwx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mulhwx: read | Source GPR. |
|
||||
| `RD` | mulhwx: write | Destination GPR. |
|
||||
| `CR` | mulhwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulhwx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mulhwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- high_32_of_signed_multiply((RA)[32:63], (RB)[32:63]) sign-extended to 64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulhwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:326`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L326)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:865`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L865)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:372-382`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L372-L382)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulhwx => {
|
||||
// PPCBUG-020: 32-bit ABI CR0 view.
|
||||
let ra = ctx.gpr[instr.ra()] as i32 as i64;
|
||||
let rb = ctx.gpr[instr.rb()] as i32 as i64;
|
||||
let result = ra.wrapping_mul(rb);
|
||||
ctx.gpr[instr.rd()] = ((result >> 32) as u32) as u64;
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Inputs are the low 32 bits, signed-extended.** `RA[32:63]` and `RB[32:63]` are sign-extended to 64-bit signed values, multiplied, and the *high* 32 bits of the 64-bit product are returned in `RT[32:63]`. The high 32 bits of `RT` are *implementation-defined* per spec but xenia masks them to zero ([`interpreter.rs:222`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L222) `& 0xFFFF_FFFF`).
|
||||
- **Pair with [`mullwx`](mullwx.md) for the full 64-bit product.** Both can issue independently — no fused 32×32→64 instruction.
|
||||
- **No `OE` bit.** Like all `mulh*` instructions, no overflow flag is produced; the high half is by definition defined.
|
||||
- **Xenia-rs quirk: high 32 bits zeroed.** Because spec says they're "undefined", legitimately matching either zero, sign-extension, or garbage. Xenia chooses zero, which differs from the literal Xenon behaviour (which sign-extends in some microarchitecture cases). For game code that doesn't read those bits, the difference is invisible.
|
||||
- **`Rc=1` CR0 update.** [`interpreter.rs:225`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L225) uses `as i32 as i64` — operates on the truncated low 32 bits, which is correct for the *defined* portion of the result.
|
||||
- **Multi-cycle latency.** Multiply is the slowest pipelined ALU op; `mulhw` shares the divider/multiplier unit.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mullwx`](mullwx.md) — low 32 bits of a signed 32×32 product.
|
||||
- [`mulhwux`](mulhwux.md) — high 32 bits, unsigned interpretation.
|
||||
- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md), [`mulldx`](mulldx.md) — 64-bit family.
|
||||
- [`divwx`](divwx.md) — sometimes replaced by reciprocal-mul-then-shift in compiled code.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulhw` (Multiply High Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulhw-multiply-high-word-instruction)
|
||||
130
migration/project-root/ppc-manual/alu/mulldx.md
Normal file
130
migration/project-root/ppc-manual/alu/mulldx.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# `mulldx` — Multiply Low Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d2`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulld` | `mulldx` | — | Multiply Low Doubleword |
|
||||
| `mulldo` | `mulldx` | OE=1 | Multiply Low Doubleword |
|
||||
| `mulld.` | `mulldx` | Rc=1 | Multiply Low Doubleword |
|
||||
| `mulldo.` | `mulldx` | OE=1, Rc=1 | Multiply Low Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulld[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulldx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0001d2`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `233`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulldx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mulldx: read | Source GPR. |
|
||||
| `RD` | mulldx: write | Destination GPR. |
|
||||
| `CR` | mulldx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | mulldx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulldx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mulldx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA) * (RB))[64:127] ; low 64 of signed 64×64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulldx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulldx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:368`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L368)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:872`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L872)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:433-444`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L433-L444)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulldx => {
|
||||
let ra = ctx.gpr[instr.ra()] as i64;
|
||||
let rb = ctx.gpr[instr.rb()] as i64;
|
||||
ctx.gpr[instr.rd()] = ra.wrapping_mul(rb) as u64;
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, overflow::mulld_ov(ra, rb));
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Returns the low 64 bits of a signed 64×64 product.** Equivalent to `(int64_t)(RA * RB)` modulo `2^64`. Both operands are full 64-bit signed; no truncation on input.
|
||||
- **High bits silently lost.** The high 64 bits of the true product are discarded; pair with [`mulhdx`](mulhdx.md) (signed) or [`mulhdux`](mulhdux.md) (unsigned) to recover them.
|
||||
- **`OE=1` should set `XER[OV]`** when the 128-bit signed product cannot be represented in 64 bits — i.e. when `mulhd RA, RB` is not the sign-extension of `mulld RA, RB`. **Xenia-rs does not implement** `OE` ([`interpreter.rs:264`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L264) has no `oe()` branch).
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:269`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L269) uses `as i64` — full 64-bit signed compare. One of the few non-truncating CR0 sites in xenia-rs; means `mulld.` gives spec-correct CR0 even when the result has non-zero high 32 bits.
|
||||
- **Same instruction for signed and unsigned low halves.** Modular arithmetic is identical; only the high half (`mulhd` vs `mulhdu`) distinguishes the interpretations.
|
||||
- **Multi-cycle latency** — slowest of the ALU pipelines after divide.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) — signed/unsigned high halves of the same multiply.
|
||||
- [`mullwx`](mullwx.md) — 32-bit signed multiply (low 64).
|
||||
- [`mulli`](mulli.md) — D-form: `RT ← (RA[32:63]) × SIMM`.
|
||||
- [`divdx`](divdx.md), [`divdux`](divdux.md) — 64-bit divide, often paired with `mulld` for remainders.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulld` (Multiply Low Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulld-multiply-low-double-word-instruction)
|
||||
120
migration/project-root/ppc-manual/alu/mulli.md
Normal file
120
migration/project-root/ppc-manual/alu/mulli.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# `mulli` — Multiply Low Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x1c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mulli` | `mulli` | — | Multiply Low Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mulli [RD], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mulli` — form `D`
|
||||
|
||||
- **Opcode word:** `0x1c000000`
|
||||
- **Primary opcode (bits 0–5):** `7`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mulli: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | mulli: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | mulli: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mulli`
|
||||
|
||||
- **Reads (always):** `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA) * EXTS(SIMM))[64:127]
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mulli`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulli"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:382`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L382)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:332`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L332)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:165-172`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L165-L172)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mulli => {
|
||||
// PPCBUG-004: 32-bit ABI. Read RA as i32 (low 32, sign-extended for
|
||||
// multiply), product fits in 32 bits per ISA (overflow wraps).
|
||||
let ra = ctx.gpr[instr.ra()] as i32 as i64;
|
||||
let imm = instr.simm16() as i64;
|
||||
ctx.gpr[instr.rd()] = (ra.wrapping_mul(imm) as u32) as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **64-bit operand, sign-extended 16-bit immediate.** Xenia reads the full 64-bit `RA` as `i64` and the immediate as a sign-extended `i64` ([`interpreter.rs:80-81`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L80-L81)) — note this differs from the PPC pseudocode header which writes `(RA) * EXTS(SIMM)` as a 64-bit operation but other implementations sometimes treat it as 32×32. On the Xenon (and in xenia-rs), it is genuinely 64-bit.
|
||||
- **Returns the low 64 bits.** No high half is produced — equivalent to `(int64_t)RA * (int64_t)SIMM` modulo `2^64`. There is no `mulhi`-immediate instruction.
|
||||
- **No `Rc`, no `OE`.** This D-form has no flag bits — strictly `RT ← RA * SIMM`. To check overflow, compare the result to `(int32_t)RA * SIMM` after the fact, or use [`mulldx`](mulldx.md) with `OE=1` after materialising the immediate.
|
||||
- **Common compiler idiom.** `mulli` is heavily used for fixed-stride array indexing (`r3 *= sizeof_struct`) when the size is a small signed constant.
|
||||
- **No carry.** `XER[CA]` is untouched.
|
||||
- **Same multi-cycle latency** as `mullw` / `mulld`. Compilers strength-reduce `mulli rD, rA, 2^k` to a left shift and `mulli rD, rA, 3` to `add+shift` when the immediate has cheap structure.
|
||||
- **Aliasing fine.** `mulli r3, r3, 5` rewrites in place.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mullwx`](mullwx.md) — register-register low 32 (signed).
|
||||
- [`mulldx`](mulldx.md) — register-register low 64 (signed).
|
||||
- [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) — high halves (no immediate variant).
|
||||
- [`addi`](addi.md) — add immediate; sometimes substituted by compilers when the multiplier is `2^k+1` etc.
|
||||
- [`slwx`](slwx.md), [`sldx`](sldx.md) — shifts often replace `mulli` for power-of-two multipliers.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mulli` (Multiply Low Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulli-multiply-low-immediate-instruction)
|
||||
146
migration/project-root/ppc-manual/alu/mullwx.md
Normal file
146
migration/project-root/ppc-manual/alu/mullwx.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# `mullwx` — Multiply Low Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d6`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mullw` | `mullwx` | — | Multiply Low Word |
|
||||
| `mullwo` | `mullwx` | OE=1 | Multiply Low Word |
|
||||
| `mullw.` | `mullwx` | Rc=1 | Multiply Low Word |
|
||||
| `mullwo.` | `mullwx` | OE=1, Rc=1 | Multiply Low Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mullw[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mullwx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0001d6`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `235`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | mullwx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | mullwx: read | Source GPR. |
|
||||
| `RD` | mullwx: write | Destination GPR. |
|
||||
| `CR` | mullwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | mullwx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mullwx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mullwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ((RA)[32:63]) * ((RB)[32:63]) ; signed 32×32 → 64
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mullwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mullwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:390`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L390)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:57`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L57)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:874`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L874)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:357-371`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L357-L371)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mullwx => {
|
||||
// PPCBUG-009: 32-bit ABI. Truncate product to u32 — overflow detection
|
||||
// (mullw_ov) still uses the full i64 product to catch the overflow.
|
||||
let ra = ctx.gpr[instr.ra()] as i32 as i64;
|
||||
let rb = ctx.gpr[instr.rb()] as i32 as i64;
|
||||
let product = ra.wrapping_mul(rb);
|
||||
ctx.gpr[instr.rd()] = product as u32 as u64;
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, overflow::mullw_ov(product));
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, ctx.gpr[instr.rd()] as u32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
prod64 <- sign_extend_32_to_64((RA)[32:63]) *s sign_extend_32_to_64((RB)[32:63])
|
||||
RT <- prod64 ; 64-bit result
|
||||
if OE then
|
||||
XER[OV] <- (prod64 ≠ sign_extend_32_to_64(prod64[32:63])) ; set when product doesn't fit in 32 bits
|
||||
XER[SO] <- XER[SO] | XER[OV]
|
||||
if Rc then
|
||||
CR0 <- signed_compare(RT, 0) || XER[SO]
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Inputs are the low 32 bits.** `mullw` only looks at `RA[32:63]` and `RB[32:63]`; the high 32 bits of each source are ignored. This is a 32-bit × 32-bit → 64-bit signed multiply. For full 64-bit operands use [`mulldx`](mulldx.md).
|
||||
- **Result is sign-extended to 64 bits.** The 64-bit product fits into a 64-bit GPR without loss. Subsequent 32-bit consumers see `RT[32:63]` (the low 32 bits of the product); use [`mulhwx`](mulhwx.md) for the signed high 32 bits or [`mulhwux`](mulhwux.md) for the unsigned high 32 bits, computed in parallel without this instruction.
|
||||
- **`OE` overflow test is 32-bit.** `XER[OV]` is set iff the 64-bit signed product cannot be represented in 32 bits — equivalently, iff `RT[32] ≠ RT[33] = … = RT[63]` (sign bit disagrees with the next 32 bits). Xenia-rs does **not** implement this; `OE` on `mullwo` is a no-op in the interpreter.
|
||||
- **Xenia-rs CR0 update bug footprint.** The interpreter computes CR0 from `result as i32 as i64` — the low 32 bits sign-extended. For a 32×32→64 multiply the high 32 bits may be non-zero even when the low 32 bits are zero, so xenia's CR0 can differ from the spec's (which compares the full 64-bit product to zero). In practice this matters only for code that relies on `mullw.` to detect overflow via CR0 — extremely rare.
|
||||
- **Latency.** On the Xenon, `mullw` has higher latency than add/sub; many hot inner loops avoid it by strength-reduction or shift-add chains. This is irrelevant for correctness but sometimes explains surprising instruction sequences in disassembly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mulhwx`](mulhwx.md) — signed high 32 bits of the same 32×32 product.
|
||||
- [`mulhwux`](mulhwux.md) — unsigned high 32 bits of a 32×32 product.
|
||||
- [`mulli`](mulli.md) — D-form: `RT ← (RA[32:63]) × SIMM` (low 64 bits, signed).
|
||||
- [`mulldx`](mulldx.md), [`mulhdx`](mulhdx.md), [`mulhdux`](mulhdux.md) — 64-bit multiplies (low/high, signed/unsigned).
|
||||
- [`divwx`](divwx.md), [`divwux`](divwux.md) — 32-bit signed / unsigned division.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mullw` (Multiply Low Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mullw-multiply-low-word-instruction)
|
||||
- [AIX 7.3 — `mulli` (Multiply Low Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mulli-multiply-low-immediate-instruction)
|
||||
122
migration/project-root/ppc-manual/alu/nandx.md
Normal file
122
migration/project-root/ppc-manual/alu/nandx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `nandx` — NAND
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0003b8`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `nand` | `nandx` | — | NAND |
|
||||
| `nand.` | `nandx` | Rc=1 | NAND |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
nand[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `nandx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0003b8`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `476`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | nandx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | nandx: read | Source GPR. |
|
||||
| `RA` | nandx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | nandx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `nandx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `nandx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- ~((RS) & (RB))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`nandx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="nandx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:753`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L753)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:812`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L812)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:570-577`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L570-L577)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::nandx => {
|
||||
// PPCBUG-030: same shape — operate in u32.
|
||||
let rs32 = ctx.gpr[instr.rs()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = (!(rs32 & rb32)) as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ~(RS AND RB)`.** Bit-wise NAND. Since `nand RA, RS, RS = ~RS` (NOT-OR-self), the simplified mnemonic `not RA, RS` assembles to `nor RA, RS, RS` (note: NOR, not NAND). NAND-self is equivalent — both produce `~RS` — but the assembler prefers the NOR form by convention.
|
||||
- **Operand convention is X-form** (`RA` destination, `RS`/`RB` sources).
|
||||
- **64-bit operation** on Xenon; `~` operates on the full `u64`.
|
||||
- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0` (signed compare to zero).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:377`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L377) truncates with `as i32 as i64`. NAND results frequently have all-ones high bits when the low half AND is non-saturating, so the truncation can change CR0 semantics in subtle ways — call out as a quirk if reproducing CR-sensitive behaviour.
|
||||
- **Idiom: NAND of two equal values produces NOT.** `nand. RA, RS, RS` ≡ `~RS` with CR0 update. Sometimes used by compilers when `not.` is unavailable in their tablegen.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`andx`](andx.md), [`andcx`](andcx.md) — base AND family.
|
||||
- [`norx`](norx.md) — assembler-preferred form for "NOT" via `nor RA, RS, RS`.
|
||||
- [`eqvx`](eqvx.md) — NXOR.
|
||||
- [`orx`](orx.md), [`orcx`](orcx.md), [`xorx`](xorx.md) — full logical family.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `nand` (NAND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-nand-instruction)
|
||||
132
migration/project-root/ppc-manual/alu/negx.md
Normal file
132
migration/project-root/ppc-manual/alu/negx.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `negx` — Negate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0000d0`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `neg` | `negx` | — | Negate |
|
||||
| `nego` | `negx` | OE=1 | Negate |
|
||||
| `neg.` | `negx` | Rc=1 | Negate |
|
||||
| `nego.` | `negx` | OE=1, Rc=1 | Negate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
neg[OE][Rc] [RD], [RA]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `negx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0000d0`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `104`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | negx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RD` | negx: write | Destination GPR. |
|
||||
| `CR` | negx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | negx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `negx`
|
||||
|
||||
- **Reads (always):** `RA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `negx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ~(RA) + 1
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`negx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="negx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L406)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:866`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L866)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:342-356`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L342-L356)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::negx => {
|
||||
// PPCBUG-006: 32-bit ABI. `(!ra).wrapping_add(1)` on u64 always
|
||||
// sets upper 32 bits — every neg poisoned the GPR. neg_ov also
|
||||
// checks at 64-bit INT_MIN; should be 32-bit INT_MIN.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let result32 = (!ra32).wrapping_add(1);
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
overflow::apply(ctx, ra32 == 0x8000_0000);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Two's-complement negate.** `RT ← ~RA + 1`, equivalent to `0 − RA`. A specialisation of [`subfx`](subfx.md) where `RB` is implicit zero.
|
||||
- **`INT64_MIN` is its own negation.** `neg(0x8000000000000000) = 0x8000000000000000` — the only fixed point. `nego` should set `XER[OV]` in this case (it is the canonical signed-overflow trigger), but **xenia-rs does not implement `OE`** ([`interpreter.rs:201`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L201) has no `oe()` branch).
|
||||
- **`RB` field unused.** Set to 0 by assemblers; ignored.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:204`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L204). Important: `neg.` of a 64-bit value with high bits set will give a CR0 that doesn't match spec (which compares the full 64-bit `~RA + 1` to zero).
|
||||
- **No carry produced.** Use [`subfic`](subficx.md) `RT, RA, 0` (`RT ← 0 − RA` with carry) when you need the borrow.
|
||||
- **Latency: single cycle.** Negate is the cheapest XO-form ALU operation (cheaper than `subf` despite being a special case, because there's no `RB` operand fetch).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfx`](subfx.md) — generalisation: `neg RT, RA` ≡ `subf RT, RA, 0` (but the latter requires materialising 0 in a register).
|
||||
- [`subfic`](subficx.md) — `RT ← SIMM − RA` with `XER[CA]`; `subfic RT, RA, 0` produces a borrow.
|
||||
- [`addx`](addx.md), [`addmex`](addmex.md), [`addzex`](addzex.md) — for chained negation (multi-word two's complement).
|
||||
- `not` (simplified) — bit-wise complement via `nor RA, RS, RS`; distinct from negate.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `neg` (Negate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-neg-negate-instruction)
|
||||
124
migration/project-root/ppc-manual/alu/norx.md
Normal file
124
migration/project-root/ppc-manual/alu/norx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `norx` — NOR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0000f8`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `nor` | `norx` | — | NOR |
|
||||
| `nor.` | `norx` | Rc=1 | NOR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
nor[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `norx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0000f8`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `124`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | norx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | norx: read | Source GPR. |
|
||||
| `RA` | norx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | norx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `norx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `norx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- ~((RS) | (RB))
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`norx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="norx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:763`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L763)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:777`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L777)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:562-569`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L562-L569)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::norx => {
|
||||
// PPCBUG-029: `not` simplified mnemonic — every `not` poisoned the GPR.
|
||||
let rs32 = ctx.gpr[instr.rs()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = (!(rs32 | rb32)) as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ~(RS OR RB)`.** Bit-wise NOR. The canonical idiom for one-instruction NOT: **`not RA, RS` is the simplified mnemonic for `nor RA, RS, RS`** — both source operands the same yields `~RS`. Almost every disassembly contains this pattern.
|
||||
- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources).
|
||||
- **64-bit operation** on Xenon; full 64-bit complement via `!` on `u64`.
|
||||
- **No `OE` or `XER` side effects.**
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:372`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L372) uses `as i32 as i64`. Note that NOR almost always produces results with high bits set (since the OR rarely covers all 64 bits), so the truncated CR0 is usually `LT` (negative low half) where spec might give a different signed compare for the full 64-bit value.
|
||||
- **`nor.` after a clear-low operation is a common pattern** for testing whether some high-bit mask is empty.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`orx`](orx.md), [`orcx`](orcx.md) — base OR family.
|
||||
- [`andx`](andx.md), [`andcx`](andcx.md), [`nandx`](nandx.md) — AND family.
|
||||
- [`eqvx`](eqvx.md) — NXOR.
|
||||
- [`xorx`](xorx.md) — XOR.
|
||||
- `not` (simplified mnemonic for `nor RA, RS, RS`).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `nor` (NOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-nor-instruction)
|
||||
- [AIX 7.3 — `not` (simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-not-complement-register)
|
||||
122
migration/project-root/ppc-manual/alu/orcx.md
Normal file
122
migration/project-root/ppc-manual/alu/orcx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `orcx` — OR with Complement
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000338`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `orc` | `orcx` | — | OR with Complement |
|
||||
| `orc.` | `orcx` | Rc=1 | OR with Complement |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
orc[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `orcx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000338`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `412`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | orcx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | orcx: read | Source GPR. |
|
||||
| `RA` | orcx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | orcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `orcx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `orcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) | ~(RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`orcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="orcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:800`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L800)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:807`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L807)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:548-555`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L548-L555)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::orcx => {
|
||||
// PPCBUG-028: same shape as andcx — operate in u32.
|
||||
let rs32 = ctx.gpr[instr.rs()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = (rs32 | !rb32) as u64;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← RS OR (NOT RB)`.** The complement is on `RB`. Useful for setting bits *outside* a mask — e.g. `orc r3, r3, r4` sets in `r3` every bit *not* set in `r4`.
|
||||
- **Idiom: `orc RA, RS, RS`** = `RS | ~RS` = `-1` (all ones). Cheaper-looking than constructing `−1` via `lis`+`ori`, but the assembler usually prefers `li RA, -1` or `eqv RA, RS, RS`.
|
||||
- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources).
|
||||
- **64-bit operation** on Xenon; xenia uses Rust's `!` on `u64` for full-width complement.
|
||||
- **No `OE` or `XER` side effects.**
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:362`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L362). Because `~RB` typically has high bits set, `orc.` results often appear `LT` in the truncated CR0.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`orx`](orx.md) — base OR (no complement).
|
||||
- [`andcx`](andcx.md) — AND-with-complement; sister `c` form.
|
||||
- [`norx`](norx.md), [`nandx`](nandx.md) — full-result complements.
|
||||
- [`eqvx`](eqvx.md) — `~(RS XOR RB)`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `orc` (OR with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-orc-complement-instruction)
|
||||
115
migration/project-root/ppc-manual/alu/ori.md
Normal file
115
migration/project-root/ppc-manual/alu/ori.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# `ori` — OR Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x60000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `ori` | `ori` | — | OR Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
ori [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `ori` — form `D`
|
||||
|
||||
- **Opcode word:** `0x60000000`
|
||||
- **Primary opcode (bits 0–5):** `24`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | ori: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | ori: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | ori: write | Source GPR (`r0`–`r31`). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `ori`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) | (0x0000 || UIMM)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`ori`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ori"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:810`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L810)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:347`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L347)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:512-515`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L512-L515)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No record form.** Unlike [`andix`](andix.md), `ori` does **not** update `CR0` — there is no `ori.`. If you need a CR update after OR-immediate, follow it with `cmpwi` or use [`orx`](orx.md) with `Rc=1`.
|
||||
- **Immediate is zero-extended.** Only the low 16 bits of `RA` can be affected; the high 48 bits are passed through from `RS` unchanged.
|
||||
- **`ori 0, 0, 0` is the canonical NOP.** All PowerPC NOPs assemble to this encoding (`0x60000000`). Disassemblers usually display this as `nop`.
|
||||
- **Common idiom: build a 32-bit constant via `lis` + `ori`.** `lis r3, hi16; ori r3, r3, lo16` materialises any 32-bit immediate with no CR or XER disturbance.
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:330`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L330) — full `u64` OR; high bits unchanged from `RS`.
|
||||
- **`RA = 0` reads `r0`** (not the literal zero). Different from `addi`'s `RA0` semantics; `ori` uses the regular `RA` interpretation.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`oris`](oris.md) — same op with the immediate shifted left 16.
|
||||
- [`orx`](orx.md) — register-register; supports `Rc=1`.
|
||||
- [`xori`](xori.md), [`xoris`](xoris.md), [`andix`](andix.md), [`andisx`](andisx.md) — sister immediate logicals.
|
||||
- `nop` (simplified) — `ori 0, 0, 0`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `ori` (OR Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-ori-immediate-instruction)
|
||||
- [AIX 7.3 — `nop` (simplified)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-nop-no-operation)
|
||||
113
migration/project-root/ppc-manual/alu/oris.md
Normal file
113
migration/project-root/ppc-manual/alu/oris.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# `oris` — OR Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x64000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `oris` | `oris` | — | OR Immediate Shifted |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
oris [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `oris` — form `D`
|
||||
|
||||
- **Opcode word:** `0x64000000`
|
||||
- **Primary opcode (bits 0–5):** `25`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | oris: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | oris: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | oris: write | Source GPR (`r0`–`r31`). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `oris`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) | (UIMM || 0x0000)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`oris`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="oris"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:821`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L821)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:348`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L348)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:516-519`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L516-L519)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::oris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No record form.** No `oris.` — same as [`ori`](ori.md). For CR0 updates use [`orx`](orx.md) with `Rc=1`.
|
||||
- **Immediate is zero-extended *then* shifted left 16.** Only bits 32–47 of `RA` (in PowerISA bit numbering) can be affected; the high 32 bits and low 16 bits of `RA` come from `RS` unchanged.
|
||||
- **Common pair with `lis`** to load a 32-bit constant: `lis r3, hi16` (= `addis r3, 0, hi16`), then `ori r3, r3, lo16`. **For unsigned constants whose low half has the high bit set**, `lis` followed by `ori` works cleanly because `ori` is zero-extending; using `addi` instead would sign-extend `lo16` and corrupt the constant.
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:334`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L334).
|
||||
- **No `XER`, no `CR` effect.** Pure register OR.
|
||||
- **`RA = 0` reads `r0`** (not literal zero); see [`ori`](ori.md).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`ori`](ori.md) — companion (immediate not shifted).
|
||||
- [`addis`](addis.md) — D-form add-immediate-shifted; pairs with `ori` to build constants.
|
||||
- [`xoris`](xoris.md), [`andisx`](andisx.md) — sister immediate-shifted logicals.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `oris` (OR Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-oris-immediate-shifted-instruction)
|
||||
122
migration/project-root/ppc-manual/alu/orx.md
Normal file
122
migration/project-root/ppc-manual/alu/orx.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# `orx` — OR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000378`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `or` | `orx` | — | OR |
|
||||
| `or.` | `orx` | Rc=1 | OR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
or[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `orx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000378`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `444`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | orx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | orx: read | Source GPR. |
|
||||
| `RA` | orx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | orx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `orx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `orx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) | (RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`orx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="orx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:773`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L773)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:59`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L59)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:809`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L809)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:542-547`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L542-L547)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::orx => {
|
||||
// PPCBUG-032+020: 32-bit ABI CR0 view.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ctx.gpr[instr.rb()];
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Canonical "register move".** `or RA, RS, RS` copies `RS` to `RA` — assemblers expose this as the simplified mnemonic **`mr RA, RS`** (move register). It is the single most common instruction in PPC disassembly after loads/stores.
|
||||
- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources).
|
||||
- **64-bit operation** on Xenon; full bitwise OR across 64 bits.
|
||||
- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0`.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:357`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L357) truncates with `as i32 as i64`. For `or. RA, RS, RS` (i.e. `mr.`), this means CR0 reflects the low 32 bits of `RS` only — distinguishable from spec only when the high 32 bits are non-zero with all-zero low 32.
|
||||
- **`or 26, 26, 26` is the Xbox 360 NOP variant** historically used to mark cache lines or signal the dispatch unit (alongside `nop` ≡ `ori 0,0,0`). Disassembly may show this — it has no architectural effect.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`orcx`](orcx.md) — OR with complement.
|
||||
- [`norx`](norx.md) — NOR (and the basis for `not`).
|
||||
- [`andx`](andx.md), [`xorx`](xorx.md), [`eqvx`](eqvx.md) — sister logicals.
|
||||
- [`ori`](ori.md), [`oris`](oris.md) — D-form immediate variants (no record form).
|
||||
- `mr` (simplified) — `or RA, RS, RS`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `or` (OR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-instruction-1)
|
||||
- [AIX 7.3 — `mr` (Move Register, simplified)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-mr-move-register)
|
||||
137
migration/project-root/ppc-manual/alu/rldclx.md
Normal file
137
migration/project-root/ppc-manual/alu/rldclx.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# `rldclx` — Rotate Left Doubleword then Clear Left
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MDS](../forms/MDS.md) · **Opcode:** `0x78000010`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldcl` | `rldclx` | — | Rotate Left Doubleword then Clear Left |
|
||||
| `rldcl.` | `rldclx` | Rc=1 | Rotate Left Doubleword then Clear Left |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldcl[Rc] [RA], [RS], [RB], [MB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldclx` — form `MDS`
|
||||
|
||||
- **Opcode word:** `0x78000010`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `RB` | source B GPR |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–30 | `XO` | extended opcode |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldclx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | rldclx: read | Source GPR. |
|
||||
| `MB` | rldclx: read | Mask begin bit. |
|
||||
| `RA` | rldclx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldclx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldclx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`, `MB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldclx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldclx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldclx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:856`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L856)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:733`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L733)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:802-811`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L802-L811)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldclx => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = ctx.gpr[instr.rb()] & 0x3F;
|
||||
let mb = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh as u32);
|
||||
let mask = rld_mask_left(mb);
|
||||
ctx.gpr[instr.ra()] = rotated & mask;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL64(RS, RB[58:63]) & MASK(MB, 63)`.** Rotate `RS` left by `RB & 0x3F`, then *clear* bits to the left of `MB` — i.e. keep bits `MB..63`, force bits `0..MB-1` to zero.
|
||||
- **Shift comes from a register.** Unlike [`rldiclx`](rldiclx.md), the rotate amount is dynamic. Only the low 6 bits of `RB` are used (`& 0x3F`); the upper 58 bits are silently ignored.
|
||||
- **`MB` is a split 6-bit field.** Bit 5 of the encoded `mb/me` is *swapped* into bit position 5 (raw bit 30) — xenia decodes via `(instr.mb() << 1) | ((raw >> 1) & 1)` ([`interpreter.rs:587`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L587)). This MDS form is unusual; if you write a decoder, follow this exact bit assembly.
|
||||
- **Mask generation.** `rld_mask_left(MB)` is `(1 << (64 - MB)) - 1` — i.e. clear bits `0..MB-1`, keep bits `MB..63`. When `MB = 0` the mask is all ones; when `MB = 63` only bit 63 survives.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:592`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L592) uses `as i64` directly — no truncation. The rotate-and-mask family is one of the few xenia-rs instruction groups that already does the spec-correct 64-bit CR0 compare.
|
||||
- **No `XER` effect.**
|
||||
- **Use over [`rldiclx`](rldiclx.md)** when the shift amount is computed at runtime (e.g. via `cntlzd` for normalisation).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldcrx`](rldcrx.md) — sister: clear *right* instead of left.
|
||||
- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md), [`rldicx`](rldicx.md) — immediate-shift variants.
|
||||
- [`rldimix`](rldimix.md) — rotate and mask insert.
|
||||
- [`rlwnmx`](rlwnmx.md), [`rlwinmx`](rlwinmx.md) — 32-bit cousins.
|
||||
- [`sldx`](sldx.md), [`srdx`](srdx.md) — preferred for plain 64-bit shifts.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldcl` (Rotate Left Doubleword then Clear Left)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldcl-rotate-left-double-word-then-clear-left-instruction)
|
||||
136
migration/project-root/ppc-manual/alu/rldcrx.md
Normal file
136
migration/project-root/ppc-manual/alu/rldcrx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `rldcrx` — Rotate Left Doubleword then Clear Right
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MDS](../forms/MDS.md) · **Opcode:** `0x78000012`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldcr` | `rldcrx` | — | Rotate Left Doubleword then Clear Right |
|
||||
| `rldcr.` | `rldcrx` | Rc=1 | Rotate Left Doubleword then Clear Right |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldcr[Rc] [RA], [RS], [RB], [ME]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldcrx` — form `MDS`
|
||||
|
||||
- **Opcode word:** `0x78000012`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `RB` | source B GPR |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–30 | `XO` | extended opcode |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldcrx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | rldcrx: read | Source GPR. |
|
||||
| `ME` | rldcrx: read | Mask end bit. |
|
||||
| `RA` | rldcrx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldcrx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldcrx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`, `ME`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldcrx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldcrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldcrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:881`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L881)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:734`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L734)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:812-821`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L812-L821)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldcrx => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = ctx.gpr[instr.rb()] & 0x3F;
|
||||
let me = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh as u32);
|
||||
let mask = rld_mask_right(me);
|
||||
ctx.gpr[instr.ra()] = rotated & mask;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL64(RS, RB[58:63]) & MASK(0, ME)`.** Rotate `RS` left by `RB & 0x3F`, then *clear* bits to the right of `ME` — keep bits `0..ME`, force bits `ME+1..63` to zero.
|
||||
- **Shift from register.** Same as [`rldclx`](rldclx.md): only the low 6 bits of `RB` count.
|
||||
- **`ME` is a split 6-bit field.** Same swap-decoded layout as `MB` in `rldclx`: `(instr.mb() << 1) | ((raw >> 1) & 1)` ([`interpreter.rs:597`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L597)). Note that even though it represents `ME` here, xenia reads it from `instr.mb()` because the field shares the same encoding slot.
|
||||
- **Mask generation.** `rld_mask_right(ME)` = `~((1 << (63 - ME)) - 1)` keeping bits `0..ME`. When `ME = 63` the mask is all ones; when `ME = 0` only bit 0 survives.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:602`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L602)).
|
||||
- **No `XER` effect.**
|
||||
- **Useful for left-shift with arbitrary discard.** `rldcr RA, RS, RB, 63 - n` is functionally close to a left-shift-and-mask sequence, with the rotate variant additionally allowing wrap-around.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldclx`](rldclx.md) — sister: clear *left* instead of right.
|
||||
- [`rldicrx`](rldicrx.md) — immediate-shift form.
|
||||
- [`rldicx`](rldicx.md), [`rldiclx`](rldiclx.md), [`rldimix`](rldimix.md) — full immediate rotate-and-mask family.
|
||||
- [`sldx`](sldx.md) — plain 64-bit logical left shift (often a strength-reduced equivalent).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldcr` (Rotate Left Doubleword then Clear Right)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldcr-rotate-left-double-word-then-clear-right-instruction)
|
||||
142
migration/project-root/ppc-manual/alu/rldiclx.md
Normal file
142
migration/project-root/ppc-manual/alu/rldiclx.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# `rldiclx` — Rotate Left Doubleword Immediate then Clear Left
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MD](../forms/MD.md) · **Opcode:** `0x78000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldicl` | `rldiclx` | — | Rotate Left Doubleword Immediate then Clear Left |
|
||||
| `rldicl.` | `rldiclx` | Rc=1 | Rotate Left Doubleword Immediate then Clear Left |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldicl[Rc] [RA], [RS], [SH], [MB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldiclx` — form `MD`
|
||||
|
||||
- **Opcode word:** `0x78000000`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `sh` | shift amount low 5 bits |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–29 | `XO` | extended opcode |
|
||||
| 30 | `sh5` | shift amount high bit |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldiclx: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rldiclx: read | Shift amount. |
|
||||
| `MB` | rldiclx: read | Mask begin bit. |
|
||||
| `RA` | rldiclx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldiclx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldiclx`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `MB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldiclx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldiclx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldiclx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:929`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L929)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:728`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L728)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:762-771`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L762-L771)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldiclx => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = instr.sh64();
|
||||
let mb = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rld_mask_left(mb);
|
||||
ctx.gpr[instr.ra()] = rotated & mask;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL64(RS, SH) & MASK(MB, 63)`.** Rotate left by `SH`, then clear bits 0 through `MB-1`. The mask retains bits `MB..63`.
|
||||
- **The Swiss-army knife of bit extraction.** Many assembler shorthands lower to this single instruction:
|
||||
- `srdi RA, RS, n` ≡ `rldicl RA, RS, 64-n, n` — logical right shift by `n`.
|
||||
- `clrldi RA, RS, n` ≡ `rldicl RA, RS, 0, n` — clear top `n` bits.
|
||||
- `extrdi RA, RS, n, b` ≡ `rldicl RA, RS, b+n, 64-n` — extract `n` bits starting at `b`.
|
||||
- **`SH` is 6 bits, immediate** (bits 16–20 + bit 30). Xenia uses `instr.sh64()` to assemble them.
|
||||
- **`MB` is 6 bits, split-encoded** (`(instr.mb() << 1) | ((raw >> 1) & 1)`).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:551`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L551)).
|
||||
- **No `XER` effect.**
|
||||
- **Often appears in compiled disassembly** as a generic 64-bit shift. Decoding back to the simplified mnemonic above makes the intent obvious.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldicrx`](rldicrx.md) — clear-right counterpart (`MASK(0, ME)`).
|
||||
- [`rldicx`](rldicx.md) — clear both ends.
|
||||
- [`rldclx`](rldclx.md) — register-shift version.
|
||||
- [`rlwinmx`](rlwinmx.md) — 32-bit cousin.
|
||||
- `srdi`, `clrldi`, `extrdi` (simplified mnemonics).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldicl` (Rotate Left Doubleword Immediate then Clear Left)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldicl-rotate-left-double-word-immediate-then-clear-left-instruction)
|
||||
- [AIX 7.3 — Simplified shift/extract mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-rotate-shift)
|
||||
142
migration/project-root/ppc-manual/alu/rldicrx.md
Normal file
142
migration/project-root/ppc-manual/alu/rldicrx.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# `rldicrx` — Rotate Left Doubleword Immediate then Clear Right
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MD](../forms/MD.md) · **Opcode:** `0x78000004`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldicr` | `rldicrx` | — | Rotate Left Doubleword Immediate then Clear Right |
|
||||
| `rldicr.` | `rldicrx` | Rc=1 | Rotate Left Doubleword Immediate then Clear Right |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldicr[Rc] [RA], [RS], [SH], [ME]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldicrx` — form `MD`
|
||||
|
||||
- **Opcode word:** `0x78000004`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `sh` | shift amount low 5 bits |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–29 | `XO` | extended opcode |
|
||||
| 30 | `sh5` | shift amount high bit |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldicrx: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rldicrx: read | Shift amount. |
|
||||
| `ME` | rldicrx: read | Mask end bit. |
|
||||
| `RA` | rldicrx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldicrx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldicrx`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `ME`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldicrx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldicrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldicrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:957`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L957)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:729`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L729)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:772-781`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L772-L781)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldicrx => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = instr.sh64();
|
||||
let me = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rld_mask_right(me);
|
||||
ctx.gpr[instr.ra()] = rotated & mask;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL64(RS, SH) & MASK(0, ME)`.** Rotate left by `SH`, then clear bits `ME+1..63`.
|
||||
- **Common simplified mnemonics:**
|
||||
- `sldi RA, RS, n` ≡ `rldicr RA, RS, n, 63-n` — logical left shift by `n`.
|
||||
- `clrrdi RA, RS, n` ≡ `rldicr RA, RS, 0, 63-n` — clear low `n` bits.
|
||||
- `extldi RA, RS, n, b` ≡ `rldicr RA, RS, b, n-1` — extract `n` bits from position `b` left-aligned.
|
||||
- **`SH` is 6 bits, immediate.** Same `instr.sh64()` decode as the rest of the family.
|
||||
- **`ME` is 6 bits, split-encoded.** Xenia stores it via `instr.mb()` — the field shares the slot with `MB` from sister instructions; the operation just interprets it as the right edge.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:561`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L561)).
|
||||
- **No `XER` effect.**
|
||||
- **Heavily emitted by 64-bit code generators** for left-shift-and-clear sequences. Recognising the simplified mnemonics aids disassembly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldiclx`](rldiclx.md) — clear-left counterpart (`MASK(MB, 63)`).
|
||||
- [`rldicx`](rldicx.md) — clear both ends.
|
||||
- [`rldcrx`](rldcrx.md) — register-shift version.
|
||||
- [`rldimix`](rldimix.md) — insert under mask.
|
||||
- [`rlwinmx`](rlwinmx.md) — 32-bit cousin.
|
||||
- `sldi`, `clrrdi`, `extldi` (simplified mnemonics).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldicr` (Rotate Left Doubleword Immediate then Clear Right)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldicr-rotate-left-double-word-immediate-then-clear-right-instruction)
|
||||
138
migration/project-root/ppc-manual/alu/rldicx.md
Normal file
138
migration/project-root/ppc-manual/alu/rldicx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `rldicx` — Rotate Left Doubleword Immediate then Clear
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MD](../forms/MD.md) · **Opcode:** `0x78000008`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldic` | `rldicx` | — | Rotate Left Doubleword Immediate then Clear |
|
||||
| `rldic.` | `rldicx` | Rc=1 | Rotate Left Doubleword Immediate then Clear |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldic[Rc] [RA], [RS], [SH], [MB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldicx` — form `MD`
|
||||
|
||||
- **Opcode word:** `0x78000008`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `sh` | shift amount low 5 bits |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–29 | `XO` | extended opcode |
|
||||
| 30 | `sh5` | shift amount high bit |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldicx: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rldicx: read | Shift amount. |
|
||||
| `MB` | rldicx: read | Mask begin bit. |
|
||||
| `RA` | rldicx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldicx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldicx`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `MB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldicx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldicx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldicx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:906`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L906)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:730`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L730)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:782-791`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L782-L791)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldicx => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = instr.sh64();
|
||||
let mb = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rld_mask_left(mb) & rld_mask_right(63 - sh);
|
||||
ctx.gpr[instr.ra()] = rotated & mask;
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL64(RS, SH) & MASK(MB, 63 - SH)`.** Rotate `RS` left by `SH` bits, then mask off both ends: clear bits `0..MB-1` *and* clear bits `64-SH..63`. This is the "clear at both edges" variant — useful for inserting a field into an otherwise-zero register.
|
||||
- **`SH` is a 6-bit immediate** spanning bits 16–20 plus bit 30 of the instruction word. Xenia uses the helper `instr.sh64()` ([`interpreter.rs:566`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L566)) to assemble the 6 bits.
|
||||
- **`MB` is also 6-bit, split-encoded** like the rest of the `rld*` family: `(instr.mb() << 1) | ((raw >> 1) & 1)`.
|
||||
- **Mask is computed as `MASK_LEFT(MB) AND MASK_RIGHT(63 - SH)`.** This produces the equivalent of "left-shift `RS` by `SH` then clear high bits above bit `MB`" — a common pattern when `MB ≤ 63 - SH`.
|
||||
- **Equivalent to a logical shift when `MB = 0`.** `rldic RA, RS, SH, 0` ≡ `sldi RA, RS, SH` (an alias the assembler may prefer).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:571`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L571) uses `as i64` directly.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) — clear-only-one-side variants.
|
||||
- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) — register-shift forms.
|
||||
- [`rldimix`](rldimix.md) — insert under mask.
|
||||
- [`rlwinmx`](rlwinmx.md) — 32-bit cousin.
|
||||
- `sldi` (simplified) — `rldic RA, RS, n, 0`; assemblers prefer this for plain logical left shifts.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldic` (Rotate Left Doubleword Immediate then Clear)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldic-rotate-left-double-word-immediate-then-clear-instruction)
|
||||
136
migration/project-root/ppc-manual/alu/rldimix.md
Normal file
136
migration/project-root/ppc-manual/alu/rldimix.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `rldimix` — Rotate Left Doubleword Immediate then Mask Insert
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [MD](../forms/MD.md) · **Opcode:** `0x7800000c`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rldimi` | `rldimix` | — | Rotate Left Doubleword Immediate then Mask Insert |
|
||||
| `rldimi.` | `rldimix` | Rc=1 | Rotate Left Doubleword Immediate then Mask Insert |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rldimi[Rc] [RA], [RS], [SH], [MB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rldimix` — form `MD`
|
||||
|
||||
- **Opcode word:** `0x7800000c`
|
||||
- **Primary opcode (bits 0–5):** `30`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (30) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `sh` | shift amount low 5 bits |
|
||||
| 21–26 | `mb/me` | 6-bit mask field (swapped halves) |
|
||||
| 27–29 | `XO` | extended opcode |
|
||||
| 30 | `sh5` | shift amount high bit |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rldimix: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rldimix: read | Shift amount. |
|
||||
| `MB` | rldimix: read | Mask begin bit. |
|
||||
| `RA` | rldimix: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rldimix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rldimix`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `MB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rldimix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rldimix`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldimix"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:985`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L985)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:731`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L731)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:792-801`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L792-L801)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rldimix => {
|
||||
let rs = ctx.gpr[instr.rs()];
|
||||
let sh = instr.sh64();
|
||||
let mb = instr.mb_md();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rld_mask_left(mb) & rld_mask_right(63 - sh);
|
||||
ctx.gpr[instr.ra()] = (rotated & mask) | (ctx.gpr[instr.ra()] & !mask);
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← (ROTL64(RS, SH) & MASK) | (RA & ~MASK)`.** *Reads* the prior `RA` so it can preserve the bits outside the mask — this is the only `rld*` instruction with `RA` as both source and destination.
|
||||
- **Mask is `MASK_LEFT(MB) AND MASK_RIGHT(63 - SH)`** ([`interpreter.rs:578`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L578)) — same span as [`rldicx`](rldicx.md), but the un-masked region is preserved in the destination instead of being zeroed.
|
||||
- **Use to insert a bit-field.** Common idiom: `rldimi RA, RS, b, mask_start` writes `RS`'s low (`64 - mask_start`) bits into `RA` starting at bit `b`.
|
||||
- **`SH` and `MB` decoding** is identical to the rest of the family (6-bit `sh` via `instr.sh64()`, 6-bit `mb` via the swap layout).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly.
|
||||
- **No `XER` effect.**
|
||||
- **Compile-time pattern.** When you see `rldimi r3, r4, n, m`, the compiler is splicing a value into `r3`; recover the meaning by computing the mask `MASK(m, 63 - n)`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rldicx`](rldicx.md), [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) — same form family, but they zero outside the mask instead of preserving.
|
||||
- [`rlwimix`](rlwimix.md) — 32-bit insert cousin.
|
||||
- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) — register-shift forms (no insert variant).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rldimi` (Rotate Left Doubleword Immediate then Mask Insert)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rldimi-rotate-left-double-word-immediate-then-mask-insert-instruction)
|
||||
139
migration/project-root/ppc-manual/alu/rlwimix.md
Normal file
139
migration/project-root/ppc-manual/alu/rlwimix.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `rlwimix` — Rotate Left Word Immediate then Mask Insert
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [M](../forms/M.md) · **Opcode:** `0x50000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rlwimi` | `rlwimix` | — | Rotate Left Word Immediate then Mask Insert |
|
||||
| `rlwimi.` | `rlwimix` | Rc=1 | Rotate Left Word Immediate then Mask Insert |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rlwimi[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rlwimix` — form `M`
|
||||
|
||||
- **Opcode word:** `0x50000000`
|
||||
- **Primary opcode (bits 0–5):** `20`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `SH/RB` | shift amount or source B |
|
||||
| 21–25 | `MB` | mask begin |
|
||||
| 26–30 | `ME` | mask end |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rlwimix: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rlwimix: read | Shift amount. |
|
||||
| `MB` | rlwimix: read | Mask begin bit. |
|
||||
| `ME` | rlwimix: read | Mask end bit. |
|
||||
| `RA` | rlwimix: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rlwimix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rlwimix`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `MB`, `ME`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rlwimix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rlwimix`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwimix"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1010`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1010)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:344`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L344)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:737-749`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L737-L749)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rlwimix => {
|
||||
let rs = ctx.gpr[instr.rs()] as u32;
|
||||
let sh = instr.sh();
|
||||
let mb = instr.mb();
|
||||
let me = instr.me();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rlw_mask(mb, me);
|
||||
let ra = ctx.gpr[instr.ra()] as u32;
|
||||
ctx.gpr[instr.ra()] = ((rotated & mask) | (ra & !mask)) as u64;
|
||||
// PPCBUG-025: 32-bit ABI CR0 view.
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← (ROTL32(RS[32:63], SH) & MASK) | (RA[32:63] & ~MASK)`.** Reads the low 32 bits of `RS`, rotates them, then *inserts* under the mask back into the low 32 bits of `RA`. The high 32 bits of `RA` are *implementation-defined* per spec; **xenia-rs zeroes them** (the `as u32` cast at [`interpreter.rs:529`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L529) discards them on read, then `as u64` zero-extends on write).
|
||||
- **Mask follows the standard `MB..ME` PPC convention.** Both `MB` and `ME` are 5-bit fields; the mask is contiguous when `MB <= ME`, and *wraps* around (a "donut" mask: bits `MB..31` and `0..ME`) when `MB > ME`. Xenia's `rlw_mask(mb, me)` helper handles both cases.
|
||||
- **`SH` is 5 bits.** Rotate amount is `SH mod 32`; values `≥ 32` are not encodable in this M-form.
|
||||
- **Used for bit-field insertion** (`insrwi RA, RS, n, b` ≡ `rlwimi RA, RS, 32-(b+n), b, b+n-1`). Compilers emit `rlwimi` extensively for struct-bitfield writes.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:531`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L531). Since the high 32 bits of the result are zero, this matches spec's compare on the (defined) low half — but if a real Xenon left high bits non-zero, behaviour would diverge.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rlwinmx`](rlwinmx.md) — same mask family but zeroes outside (no read-modify-write).
|
||||
- [`rlwnmx`](rlwnmx.md) — register-shift variant of `rlwinm`.
|
||||
- [`rldimix`](rldimix.md) — 64-bit insert cousin.
|
||||
- `insrwi`, `inslwi` (simplified mnemonics for common insert patterns).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rlwimi` (Rotate Left Word Immediate then Mask Insert)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwimi-rotate-left-word-immediate-then-mask-insert-instruction)
|
||||
145
migration/project-root/ppc-manual/alu/rlwinmx.md
Normal file
145
migration/project-root/ppc-manual/alu/rlwinmx.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# `rlwinmx` — Rotate Left Word Immediate then AND with Mask
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [M](../forms/M.md) · **Opcode:** `0x54000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rlwinm` | `rlwinmx` | — | Rotate Left Word Immediate then AND with Mask |
|
||||
| `rlwinm.` | `rlwinmx` | Rc=1 | Rotate Left Word Immediate then AND with Mask |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rlwinm[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rlwinmx` — form `M`
|
||||
|
||||
- **Opcode word:** `0x54000000`
|
||||
- **Primary opcode (bits 0–5):** `21`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `SH/RB` | shift amount or source B |
|
||||
| 21–25 | `MB` | mask begin |
|
||||
| 26–30 | `ME` | mask end |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rlwinmx: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | rlwinmx: read | Shift amount. |
|
||||
| `MB` | rlwinmx: read | Mask begin bit. |
|
||||
| `ME` | rlwinmx: read | Mask end bit. |
|
||||
| `RA` | rlwinmx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rlwinmx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rlwinmx`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`, `MB`, `ME`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rlwinmx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rlwinmx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwinmx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1046`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1046)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:345`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L345)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:725-736`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L725-L736)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rlwinmx => {
|
||||
let rs = ctx.gpr[instr.rs()] as u32;
|
||||
let sh = instr.sh();
|
||||
let mb = instr.mb();
|
||||
let me = instr.me();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rlw_mask(mb, me);
|
||||
ctx.gpr[instr.ra()] = (rotated & mask) as u64;
|
||||
// PPCBUG-024: 32-bit ABI CR0 view.
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL32(RS[32:63], SH) & MASK(MB, ME)`.** Take the low 32 bits of `RS`, rotate them left by `SH`, AND with a 32-bit mask. The high 32 bits of `RA` are zero (`as u64` zero-extension on the result).
|
||||
- **The 32-bit Swiss army knife.** Most 32-bit shift/extract simplified mnemonics expand to this single instruction:
|
||||
- `slwi RA, RS, n` ≡ `rlwinm RA, RS, n, 0, 31-n` — logical left shift.
|
||||
- `srwi RA, RS, n` ≡ `rlwinm RA, RS, 32-n, n, 31` — logical right shift.
|
||||
- `clrlwi RA, RS, n` ≡ `rlwinm RA, RS, 0, n, 31` — clear high `n` bits.
|
||||
- `clrrwi RA, RS, n` ≡ `rlwinm RA, RS, 0, 0, 31-n` — clear low `n` bits.
|
||||
- `extlwi`, `extrwi`, `clrlslwi` — full mnemonic family in PowerISA appendix.
|
||||
- **Mask convention `MB..ME`** is contiguous when `MB ≤ ME`. When `MB > ME`, the mask is the *complement* of bits `ME+1..MB-1` — a donut/wrap mask. Xenia's `rlw_mask` handles both.
|
||||
- **`SH` is 5 bits**, rotate amount `0..31`.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:518`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L518). Since the result fits in 32 bits, the truncation matches spec exactly.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rlwimix`](rlwimix.md) — same mask family with read-modify-write insert.
|
||||
- [`rlwnmx`](rlwnmx.md) — register-shift version.
|
||||
- [`rldiclx`](rldiclx.md), [`rldicrx`](rldicrx.md) — 64-bit cousins.
|
||||
- [`slwx`](slwx.md), [`srwx`](srwx.md), [`srawix`](srawix.md) — straight 32-bit shift instructions.
|
||||
- `slwi`, `srwi`, `clrlwi`, `clrrwi`, `extlwi`, `extrwi` (simplified mnemonics).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rlwinm` (Rotate Left Word Immediate then AND with Mask)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwinm-rotate-left-word-immediate-then-mask-instruction)
|
||||
- [AIX 7.3 — Rotate / shift simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-rotate-shift)
|
||||
139
migration/project-root/ppc-manual/alu/rlwnmx.md
Normal file
139
migration/project-root/ppc-manual/alu/rlwnmx.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# `rlwnmx` — Rotate Left Word then AND with Mask
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [M](../forms/M.md) · **Opcode:** `0x5c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `rlwnm` | `rlwnmx` | — | Rotate Left Word then AND with Mask |
|
||||
| `rlwnm.` | `rlwnmx` | Rc=1 | Rotate Left Word then AND with Mask |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
rlwnm[Rc] [RA], [RS], [RB], [MB], [ME]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `rlwnmx` — form `M`
|
||||
|
||||
- **Opcode word:** `0x5c000000`
|
||||
- **Primary opcode (bits 0–5):** `23`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `SH/RB` | shift amount or source B |
|
||||
| 21–25 | `MB` | mask begin |
|
||||
| 26–30 | `ME` | mask end |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | rlwnmx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | rlwnmx: read | Source GPR. |
|
||||
| `MB` | rlwnmx: read | Mask begin bit. |
|
||||
| `ME` | rlwnmx: read | Mask end bit. |
|
||||
| `RA` | rlwnmx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | rlwnmx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `rlwnmx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`, `MB`, `ME`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `rlwnmx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`rlwnmx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwnmx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1101`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1101)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:61`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L61)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:346`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L346)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:750-761`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L750-L761)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::rlwnmx => {
|
||||
let rs = ctx.gpr[instr.rs()] as u32;
|
||||
let sh = ctx.gpr[instr.rb()] as u32 & 0x1F;
|
||||
let mb = instr.mb();
|
||||
let me = instr.me();
|
||||
let rotated = rs.rotate_left(sh);
|
||||
let mask = rlw_mask(mb, me);
|
||||
ctx.gpr[instr.ra()] = (rotated & mask) as u64;
|
||||
// PPCBUG-026: 32-bit ABI CR0 view.
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ROTL32(RS[32:63], RB[59:63]) & MASK(MB, ME)`.** Identical to [`rlwinmx`](rlwinmx.md) except the rotate amount comes from the low 5 bits of `RB`. Xenia masks with `& 0x1F` ([`interpreter.rs:535`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L535)).
|
||||
- **Use over `rlwinm`** when the rotate amount is dynamic (e.g. computed from a `cntlzw` for normalisation, or read from a parameter).
|
||||
- **Mask is still 5+5 bits immediate** — `MB` and `ME` are not register-sourced; only the shift is. This is the M-form's quirk: only one of (`SH`, `MB`, `ME`) is variable across the family.
|
||||
- **Donut masks supported.** `MB > ME` produces a wraparound mask, same as `rlwinm`.
|
||||
- **High 32 bits of `RA` are zero** (32-bit operation, then `as u64` zero-extends).
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:540`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L540) — harmless because the result already fits in 32 bits.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`rlwinmx`](rlwinmx.md) — same op, immediate shift.
|
||||
- [`rlwimix`](rlwimix.md) — insert variant (no register-shift form exists for insert).
|
||||
- [`rldclx`](rldclx.md), [`rldcrx`](rldcrx.md) — 64-bit register-shift cousins.
|
||||
- [`slwx`](slwx.md), [`srwx`](srwx.md) — straight 32-bit shifts.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `rlwnm` (Rotate Left Word then AND with Mask)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-rlwnm-rotate-left-word-then-mask-instruction)
|
||||
124
migration/project-root/ppc-manual/alu/sldx.md
Normal file
124
migration/project-root/ppc-manual/alu/sldx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `sldx` — Shift Left Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000036`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sld` | `sldx` | — | Shift Left Doubleword |
|
||||
| `sld.` | `sldx` | Rc=1 | Shift Left Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sld[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sldx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000036`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `27`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | sldx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | sldx: read | Source GPR. |
|
||||
| `RA` | sldx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | sldx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sldx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `sldx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[57:63]
|
||||
RA <- ((RS) << n) if n < 64 else 0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`sldx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sldx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1122`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1122)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:759`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L759)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:676-683`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L676-L683)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sldx => {
|
||||
let sh = ctx.gpr[instr.rb()] & 0x7F;
|
||||
ctx.gpr[instr.ra()] = if sh < 64 {
|
||||
ctx.gpr[instr.rs()] << sh
|
||||
} else { 0 };
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **64-bit logical left shift.** `RA ← RS << (RB & 0x7F)` if the shift count is `< 64`, otherwise `RA = 0`. Bits shifted past bit 0 are discarded.
|
||||
- **Critical: shift count is *7 bits*, not 6.** PowerISA reads `RB[57:63]` (7 bits, `0..127`). Counts in `[64, 127]` produce zero, *not* `RS << (count mod 64)`. Xenia respects this with `& 0x7F` and an explicit `if sh < 64` check ([`interpreter.rs:464`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L464)). C semantics' undefined behaviour for `<<` with a count `>= width` is a spec-violation source if you naïvely translate.
|
||||
- **No `XER[CA]` produced** by left shifts. Logical right [`srdx`](srdx.md) and arithmetic right [`sradx`](sradx.md) differ here — arithmetic right *does* set `CA`.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:467`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L467) uses `as i64` directly. CR0 reflects the sign of the full 64-bit shifted value (which is 0 for shifts ≥ 64, otherwise either `LT`/`GT`/`EQ`).
|
||||
- **Strength-reduced from `mulli` for power-of-two multipliers.**
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`slwx`](slwx.md) — 32-bit logical left shift.
|
||||
- [`srdx`](srdx.md) — 64-bit logical right shift.
|
||||
- [`sradx`](sradx.md), [`sradix`](sradix.md) — 64-bit arithmetic right shifts.
|
||||
- [`rldicrx`](rldicrx.md) — `sldi` simplified mnemonic uses this.
|
||||
- [`mulli`](mulli.md) — for non-power-of-two multipliers.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sld` (Shift Left Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sld-shift-left-double-word-instruction)
|
||||
124
migration/project-root/ppc-manual/alu/slwx.md
Normal file
124
migration/project-root/ppc-manual/alu/slwx.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `slwx` — Shift Left Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000030`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `slw` | `slwx` | — | Shift Left Word |
|
||||
| `slw.` | `slwx` | Rc=1 | Shift Left Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
slw[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `slwx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000030`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `24`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | slwx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | slwx: read | Source GPR. |
|
||||
| `RA` | slwx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | slwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `slwx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `slwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[58:63]
|
||||
RA <- ((RS) << n) & 0x0000_0000_FFFF_FFFF if n < 32 else 0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`slwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="slwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1141`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1141)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:757`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L757)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:622-631`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L622-L631)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::slwx => {
|
||||
// PPCBUG-044: 32-bit ABI CR0 view. A result with bit 31 set
|
||||
// (e.g. 0x80000000) is negative in i32 view but positive in i64.
|
||||
let sh = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = if sh < 32 {
|
||||
((ctx.gpr[instr.rs()] as u32) << sh) as u64
|
||||
} else { 0 };
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit logical left shift, zero-extended to 64.** `RA ← (RS[32:63] << (RB & 0x3F))[32:63]` if `(RB & 0x3F) < 32`, else `RA = 0`. The high 32 bits of `RA` are always zero (zero-extension of the 32-bit result).
|
||||
- **Shift count is 6 bits**, `RB[58:63]` — not 7 like [`sldx`](sldx.md). Counts in `[32, 63]` produce zero. Xenia reads the full register but the explicit `if sh < 32` guard in [`interpreter.rs:417`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L417) prevents Rust UB.
|
||||
- **Spec quirk worth flagging:** xenia reads `ctx.gpr[instr.rb()] as u32`, which uses the low 32 bits of `RB`, not the spec's `RB & 0x3F`. For ordinary code these agree (counts ≤ 63), but a maliciously high `RB` could in principle differ. In practice this is a non-issue.
|
||||
- **No `XER[CA]` for left shifts.**
|
||||
- **`Rc=1` CR0 update truncates to 32 bits** ([`interpreter.rs:420`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L420)). Since the high 32 bits are zero, this matches spec exactly.
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sldx`](sldx.md) — 64-bit logical left shift.
|
||||
- [`srwx`](srwx.md), [`srawx`](srawx.md), [`srawix`](srawix.md) — 32-bit right shifts.
|
||||
- [`rlwinmx`](rlwinmx.md) — `slwi` simplified mnemonic uses this.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `slw` (Shift Left Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-slw-shift-left-word-instruction)
|
||||
132
migration/project-root/ppc-manual/alu/sradix.md
Normal file
132
migration/project-root/ppc-manual/alu/sradix.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `sradix` — Shift Right Algebraic Doubleword Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XS](../forms/XS.md) · **Opcode:** `0x7c000674`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sradi` | `sradix` | — | Shift Right Algebraic Doubleword Immediate |
|
||||
| `sradi.` | `sradix` | Rc=1 | Shift Right Algebraic Doubleword Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sradi[Rc] [RA], [RS], [SH]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sradix` — form `XS`
|
||||
|
||||
- **Opcode word:** `0x7c000674`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `826`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RS` | source GPR |
|
||||
| 11–15 | `RA` | destination GPR |
|
||||
| 16–20 | `sh` | shift amount low 5 bits |
|
||||
| 21–29 | `XO` | extended opcode (9 bits) |
|
||||
| 30 | `sh5` | shift amount high bit |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | sradix: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | sradix: read | Shift amount. |
|
||||
| `RA` | sradix: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | sradix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CA` | sradix: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sradix`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `sradix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- ((RS) >>a SH) sign-extended
|
||||
CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`sradix`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sradix"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1230`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1230)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:743`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L743)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:709-722`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L709-L722)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sradix => {
|
||||
let rs = ctx.gpr[instr.rs()] as i64;
|
||||
let sh = instr.sh64();
|
||||
if sh == 0 {
|
||||
ctx.gpr[instr.ra()] = rs as u64;
|
||||
ctx.xer_ca = 0;
|
||||
} else {
|
||||
let result = rs >> sh;
|
||||
ctx.xer_ca = if rs < 0 && (rs as u64) << (64 - sh) != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.ra()] = result as u64;
|
||||
}
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← (i64)RS >> SH`**, with `XER[CA]` set when `RS` is negative AND any one-bit was shifted out.
|
||||
- **`SH` is 6 bits.** Encoded in bits 16–20 (`sh`) plus bit 30 (`sh5`); xenia uses `instr.sh64()` to assemble the 6 bits ([`interpreter.rs:496`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L496)). Range `0..63`.
|
||||
- **`SH = 0`** is a no-op (sign-extends `RS` to itself), and explicitly clears `XER[CA]` ([`interpreter.rs:498`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L498)). This matches spec.
|
||||
- **Spec divergence: 6-bit immediate, no saturation arm.** Unlike [`sradx`](sradx.md) which has a 7-bit register count and saturates at `≥ 64`, `sradi` always uses a count `< 64` so no special saturation case is needed.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:506`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L506).
|
||||
- **Idiom: `sradi rA, rS, n; addze rA, rA`** — signed integer divide by `2^n` rounded toward zero (the textbook PPC sequence).
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sradx`](sradx.md) — register-shift form.
|
||||
- [`srawix`](srawix.md), [`srawx`](srawx.md) — 32-bit arithmetic right.
|
||||
- [`addzex`](addzex.md) — pair for signed-divide-rounding.
|
||||
- [`rldiclx`](rldiclx.md) — when arithmetic semantics not required (logical shift), `srdi` simplified mnemonic.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sradi` (Shift Right Algebraic Doubleword Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sradi-shift-right-algebraic-double-word-immediate-instruction)
|
||||
136
migration/project-root/ppc-manual/alu/sradx.md
Normal file
136
migration/project-root/ppc-manual/alu/sradx.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# `sradx` — Shift Right Algebraic Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000634`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `srad` | `sradx` | — | Shift Right Algebraic Doubleword |
|
||||
| `srad.` | `sradx` | Rc=1 | Shift Right Algebraic Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
srad[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sradx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000634`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `794`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | sradx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | sradx: read | Source GPR. |
|
||||
| `RA` | sradx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | sradx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CA` | sradx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sradx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `sradx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[57:63]
|
||||
RA <- ((RS) >>a n) sign-extended if n < 64
|
||||
CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`sradx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sradx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1201`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1201)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:841`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L841)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:692-708`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L692-L708)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sradx => {
|
||||
let rs = ctx.gpr[instr.rs()] as i64;
|
||||
let sh = ctx.gpr[instr.rb()] & 0x7F;
|
||||
if sh == 0 {
|
||||
ctx.gpr[instr.ra()] = rs as u64;
|
||||
ctx.xer_ca = 0;
|
||||
} else if sh < 64 {
|
||||
let result = rs >> sh;
|
||||
ctx.xer_ca = if rs < 0 && (rs as u64) << (64 - sh) != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.ra()] = result as u64;
|
||||
} else {
|
||||
ctx.gpr[instr.ra()] = if rs < 0 { u64::MAX } else { 0 };
|
||||
ctx.xer_ca = if rs < 0 { 1 } else { 0 };
|
||||
}
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **64-bit arithmetic (sign-propagating) right shift.** `RA ← (i64)RS >> (RB & 0x7F)` with bits shifted in matching the sign bit of `RS`. Counts ≥ 64 saturate: `RA` becomes all-ones if `RS < 0`, else zero.
|
||||
- **`XER[CA]` is the "lost-ones" indicator.** `CA = 1` iff `RS` is negative AND any of the bits shifted out were `1`. This makes `srad` / `sradi` the standard idiom for "divide negative integer by power of 2 with round-toward-zero" — followed by `addze` to compensate when `CA = 1`.
|
||||
- **Three branches in xenia.** `sh == 0` (no shift, `CA=0`), `sh < 64` (normal shift, `CA` per spec), and `sh ≥ 64` (saturate to `0` or `−1`, `CA` reflects sign). The `(rs as u64) << (64 - sh) != 0` check at [`interpreter.rs:486`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L486) extracts whether any non-zero bit was shifted out.
|
||||
- **Shift count is 7 bits.** Same as [`sldx`](sldx.md): `RB[57:63]`.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:489`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L489).
|
||||
- **No `OE` bit.**
|
||||
- **Used by signed-divide-by-power-of-2 idiom:** `srad rA, rS, n; addze rA, rA` produces `rS / 2^n` with truncation toward zero rather than toward `-∞`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`sradix`](sradix.md) — immediate-shift form (`SH` 6-bit immediate).
|
||||
- [`srawx`](srawx.md), [`srawix`](srawix.md) — 32-bit arithmetic right shifts.
|
||||
- [`srdx`](srdx.md) — 64-bit *logical* right shift (no `XER[CA]`).
|
||||
- [`addzex`](addzex.md) — companion for the divide-rounding idiom.
|
||||
- [`sldx`](sldx.md) — left shift.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `srad` (Shift Right Algebraic Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srad-shift-right-algebraic-double-word-instruction)
|
||||
132
migration/project-root/ppc-manual/alu/srawix.md
Normal file
132
migration/project-root/ppc-manual/alu/srawix.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# `srawix` — Shift Right Algebraic Word Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000670`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `srawi` | `srawix` | — | Shift Right Algebraic Word Immediate |
|
||||
| `srawi.` | `srawix` | Rc=1 | Shift Right Algebraic Word Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
srawi[Rc] [RA], [RS], [SH]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `srawix` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000670`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `824`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | srawix: read | Source GPR (alias for RD in some stores). |
|
||||
| `SH` | srawix: read | Shift amount. |
|
||||
| `RA` | srawix: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | srawix: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CA` | srawix: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `srawix`
|
||||
|
||||
- **Reads (always):** `RS`, `SH`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `srawix`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- ((RS)[32:63] >>a SH) sign-extended
|
||||
CA <- (RS[32] signed) && any_low_bit_shifted_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`srawix`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srawix"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1291`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1291)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:843`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L843)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:661-675`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L661-L675)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::srawix => {
|
||||
// PPCBUG-042+043 coupled: same shape as srawx for the sh-immediate form.
|
||||
let rs = ctx.gpr[instr.rs()] as i32;
|
||||
let sh = instr.sh();
|
||||
if sh == 0 {
|
||||
ctx.gpr[instr.ra()] = rs as u32 as u64;
|
||||
ctx.xer_ca = 0;
|
||||
} else {
|
||||
let result = rs >> sh;
|
||||
ctx.xer_ca = if rs < 0 && (rs as u32) << (32 - sh) != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.ra()] = result as u32 as u64;
|
||||
}
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← ((i32)RS >> SH) sign-extended`** with `XER[CA]` set when `RS` is negative AND any low bit was shifted out.
|
||||
- **`SH` is 5 bits** (immediate, range `0..31`). Unlike [`srawx`](srawx.md), there is no saturation case because the count cannot exceed 31. Xenia reads it via `instr.sh()`.
|
||||
- **`SH = 0`** sign-extends `RS[32:63]` to 64 bits and clears `CA`. This is *not* a no-op when `RS`'s high 32 bits differ from the sign extension of bit 32.
|
||||
- **Common idiom: `srawi rA, rS, 31`** materialises the 32-bit sign of `rS` as `0` or `−1` — the canonical "sign mask" pattern. Often used for branchless `abs` or conditional negation.
|
||||
- **Idiom: `srawi rA, rS, n; addze rA, rA`** — divide signed by `2^n` rounding toward zero.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:457`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L457) — matches spec because the sign-extended result has consistent low/high 32-bit signs.
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`srawx`](srawx.md) — register-shift form.
|
||||
- [`sradix`](sradix.md), [`sradx`](sradx.md) — 64-bit arithmetic right.
|
||||
- [`addzex`](addzex.md) — divide-rounding companion.
|
||||
- [`extswx`](extswx.md) — `srawi rA, rS, 0` is functionally a sign-extend-32-to-64 plus `CA = 0` clear; `extsw` is preferred when CA isn't wanted.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `srawi` (Shift Right Algebraic Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srawi-shift-right-algebraic-word-immediate-instruction)
|
||||
138
migration/project-root/ppc-manual/alu/srawx.md
Normal file
138
migration/project-root/ppc-manual/alu/srawx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `srawx` — Shift Right Algebraic Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000630`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sraw` | `srawx` | — | Shift Right Algebraic Word |
|
||||
| `sraw.` | `srawx` | Rc=1 | Shift Right Algebraic Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sraw[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `srawx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000630`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `792`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | srawx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | srawx: read | Source GPR. |
|
||||
| `RA` | srawx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | srawx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CA` | srawx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `srawx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`, `CA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `srawx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[58:63]
|
||||
RA <- ((RS)[32:63] >>a n) sign-extended
|
||||
CA <- 1 if (signed RS < 0) && any_bit_shifted_out else 0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`srawx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srawx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1262`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1262)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:840`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L840)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:642-660`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L642-L660)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::srawx => {
|
||||
// PPCBUG-041+043 coupled: 32-bit ABI writeback truncation + CR0 i32.
|
||||
// CA logic is independently correct (uses u32 shifted-out test).
|
||||
let rs = ctx.gpr[instr.rs()] as i32;
|
||||
let sh = ctx.gpr[instr.rb()] as u32 & 0x3F;
|
||||
if sh == 0 {
|
||||
ctx.gpr[instr.ra()] = rs as u32 as u64;
|
||||
ctx.xer_ca = 0;
|
||||
} else if sh < 32 {
|
||||
let result = rs >> sh;
|
||||
ctx.xer_ca = if rs < 0 && (rs as u32) << (32 - sh) != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.ra()] = result as u32 as u64;
|
||||
} else {
|
||||
ctx.gpr[instr.ra()] = if rs < 0 { 0xFFFF_FFFFu64 } else { 0 };
|
||||
ctx.xer_ca = if rs < 0 { 1 } else { 0 };
|
||||
}
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit arithmetic right shift, sign-extended to 64.** `RA ← ((i32)RS >> n) sign-extended`, with `XER[CA]` set when `RS[32] = 1` (negative) AND any low bit was shifted out.
|
||||
- **Shift count is 6 bits**, `RB[58:63]`. Counts `≥ 32` saturate: `RA = -1` (all-ones, sign-extended) if `RS < 0`, else `0`. Xenia handles this in three branches ([`interpreter.rs:432-444`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L432-L444)).
|
||||
- **`SH = 0`** sign-extends `RS` to 64 bits and clears `XER[CA]` — like `extsw`, but additionally writing CA.
|
||||
- **Result is always sign-extended to 64 bits.** `RA[0:31]` matches the sign of `RA[32]`. This is the key difference from [`srwx`](srwx.md) (zero-extension).
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:443`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L443) — but since the result is sign-extended, the low 32 bits' sign matches the full 64-bit sign, so spec and xenia agree here.
|
||||
- **Used with [`addzex`](addzex.md)** for signed divide by `2^n` rounding toward zero.
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`srawix`](srawix.md) — immediate-shift form.
|
||||
- [`sradx`](sradx.md), [`sradix`](sradix.md) — 64-bit arithmetic right shifts.
|
||||
- [`srwx`](srwx.md) — 32-bit *logical* right shift (no `XER[CA]`).
|
||||
- [`addzex`](addzex.md) — companion for divide-rounding idiom.
|
||||
- [`slwx`](slwx.md) — left shift.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sraw` (Shift Right Algebraic Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sraw-shift-right-algebraic-word-instruction)
|
||||
123
migration/project-root/ppc-manual/alu/srdx.md
Normal file
123
migration/project-root/ppc-manual/alu/srdx.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `srdx` — Shift Right Doubleword
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000436`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `srd` | `srdx` | — | Shift Right Doubleword |
|
||||
| `srd.` | `srdx` | Rc=1 | Shift Right Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
srd[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `srdx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000436`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `539`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | srdx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | srdx: read | Source GPR. |
|
||||
| `RA` | srdx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | srdx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `srdx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `srdx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[57:63]
|
||||
RA <- ((RS) >> n) if n < 64 else 0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`srdx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srdx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1161`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1161)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:821`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L821)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:684-691`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L684-L691)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::srdx => {
|
||||
let sh = ctx.gpr[instr.rb()] & 0x7F;
|
||||
ctx.gpr[instr.ra()] = if sh < 64 {
|
||||
ctx.gpr[instr.rs()] >> sh
|
||||
} else { 0 };
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **64-bit logical right shift.** `RA ← RS >> (RB & 0x7F)` if the count is `< 64`, else `RA = 0`. Bits shifted in from the high end are zero (no sign extension).
|
||||
- **Shift count is 7 bits** (`RB[57:63]`). Counts `64..127` produce zero, not `RS >> (count mod 64)`. Xenia respects this with `& 0x7F` and an explicit `if sh < 64` check ([`interpreter.rs:472`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L472)).
|
||||
- **No `XER[CA]` produced.** This is the logical right shift; for arithmetic shift with `XER[CA]` use [`sradx`](sradx.md).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:475`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L475). Result is non-negative as a signed value (high bit is always cleared by the shift), so CR0 will only ever be `EQ` or `GT`.
|
||||
- **No `OE` bit.**
|
||||
- **The `srdi` simplified mnemonic** uses [`rldiclx`](rldiclx.md) instead — `rldicl rA, rS, 64-n, n` — because it can be combined with masking. `srd` is for runtime-variable counts.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`srwx`](srwx.md) — 32-bit logical right shift.
|
||||
- [`sradx`](sradx.md), [`sradix`](sradix.md) — 64-bit arithmetic right.
|
||||
- [`sldx`](sldx.md) — 64-bit left shift.
|
||||
- [`rldiclx`](rldiclx.md) — `srdi` immediate-shift expansion.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `srd` (Shift Right Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srd-shift-right-double-word-instruction)
|
||||
125
migration/project-root/ppc-manual/alu/srwx.md
Normal file
125
migration/project-root/ppc-manual/alu/srwx.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# `srwx` — Shift Right Word
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000430`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `srw` | `srwx` | — | Shift Right Word |
|
||||
| `srw.` | `srwx` | Rc=1 | Shift Right Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
srw[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `srwx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000430`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `536`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | srwx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | srwx: read | Source GPR. |
|
||||
| `RA` | srwx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | srwx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `srwx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `srwx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
n <- (RB)[58:63]
|
||||
RA <- ((RS)[32:63] >> n) zero-extended if n < 32 else 0
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`srwx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srwx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1180`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1180)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:65`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L65)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:820`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L820)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:632-641`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L632-L641)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::srwx => {
|
||||
// PPCBUG-044: 32-bit ABI CR0 view (zero-extended right shift can never
|
||||
// have bit 31 set, but use the canonical form for consistency).
|
||||
let sh = ctx.gpr[instr.rb()] as u32;
|
||||
ctx.gpr[instr.ra()] = if sh < 32 {
|
||||
((ctx.gpr[instr.rs()] as u32) >> sh) as u64
|
||||
} else { 0 };
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit logical right shift, zero-extended to 64.** `RA ← (u32)RS >> (RB & 0x3F)` if count `< 32`, else `RA = 0`. The high 32 bits of `RA` are always zero.
|
||||
- **Shift count is 6 bits**, `RB[58:63]`. Counts `[32, 63]` produce zero (not `RS >> (count mod 32)`); xenia's explicit `if sh < 32` guards against Rust UB ([`interpreter.rs:425`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L425)).
|
||||
- **No `XER[CA]` produced.** For arithmetic shift with `XER[CA]` use [`srawx`](srawx.md).
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:428`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L428). Since the result has zeroed high 32 bits and zeroed sign bit (high bit of the 32-bit result is always 0 after a non-zero shift), CR0 will be `EQ` or `GT`.
|
||||
- **No `OE` bit.**
|
||||
- **`srwi` simplified mnemonic** uses [`rlwinmx`](rlwinmx.md), not this instruction. `srw` is for runtime-variable counts.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`srdx`](srdx.md) — 64-bit logical right shift.
|
||||
- [`srawx`](srawx.md), [`srawix`](srawix.md) — 32-bit arithmetic right.
|
||||
- [`slwx`](slwx.md) — 32-bit left shift.
|
||||
- [`rlwinmx`](rlwinmx.md) — `srwi` immediate expansion.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `srw` (Shift Right Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-srw-shift-right-word-instruction)
|
||||
138
migration/project-root/ppc-manual/alu/subfcx.md
Normal file
138
migration/project-root/ppc-manual/alu/subfcx.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `subfcx` — Subtract From Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000010`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subfc` | `subfcx` | — | Subtract From Carrying |
|
||||
| `subfco` | `subfcx` | OE=1 | Subtract From Carrying |
|
||||
| `subfc.` | `subfcx` | Rc=1 | Subtract From Carrying |
|
||||
| `subfco.` | `subfcx` | OE=1, Rc=1 | Subtract From Carrying |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subfc[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subfcx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000010`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `8`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subfcx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | subfcx: read | Source GPR. |
|
||||
| `RD` | subfcx: write | Destination GPR. |
|
||||
| `CR` | subfcx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | subfcx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subfcx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subfcx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ~(RA) + (RB) + 1
|
||||
CA <- carry_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subfcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:441`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L441)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:859`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L859)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:270-287`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L270-L287)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subfcx => {
|
||||
// PPCBUG-007: 32-bit ABI. The `rb >= ra` u64 unsigned compare is
|
||||
// exactly the shape that broke addis. Defensive 32-bit truncation
|
||||
// is required for correct CA even after upstream cleanup.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let result32 = rb32.wrapping_sub(ra32);
|
||||
ctx.xer_ca = if rb32 >= ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_diff = (rb32 as i32 as i128) - (ra32 as i32 as i128);
|
||||
overflow::apply(ctx, true_diff != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RT ← RB − RA` with `XER[CA]` set on no-borrow.** Same operand-order convention as [`subfx`](subfx.md): the *first* source is subtracted *from* the second.
|
||||
- **`XER[CA] = 1` means *no borrow occurred*** — i.e. `RB >= RA` as unsigned. PowerISA encodes this as the carry-out of `~RA + RB + 1`, not as a borrow flag. Xenia's `if rb >= ra` test ([`interpreter.rs:157`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L157)) is the correct boolean encoding.
|
||||
- **No trap on signed overflow.** `subfco` / `subfco.` set `XER[OV]` and sticky `XER[SO]`; xenia-rs leaves the `OE` arm unimplemented.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:160`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L160) truncates with `as i32 as i64`. Spec demands a full 64-bit signed compare for `subfc.`.
|
||||
- **Seeds a multi-word subtract chain.** Use as the low-word op; continue with [`subfex`](subfex.md) for middle words and [`subfmex`](subfmex.md)/[`subfzex`](subfzex.md) for the high word.
|
||||
- **Operand aliasing fine.** `subfc r3, r3, r3` always yields `0` with `CA = 1`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfx`](subfx.md) — same op without `XER[CA]`.
|
||||
- [`subfex`](subfex.md) — `~RA + RB + CA` (chain continuation).
|
||||
- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) — chain terminators.
|
||||
- [`subfic`](subficx.md) — D-form: `RT ← SIMM − RA` with `XER[CA]`.
|
||||
- [`addcx`](addcx.md) — dual: addition seed-of-chain.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subfc` (Subtract From Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfc-subtract-from-carrying-instruction)
|
||||
138
migration/project-root/ppc-manual/alu/subfex.md
Normal file
138
migration/project-root/ppc-manual/alu/subfex.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# `subfex` — Subtract From Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000110`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subfe` | `subfex` | — | Subtract From Extended |
|
||||
| `subfeo` | `subfex` | OE=1 | Subtract From Extended |
|
||||
| `subfe.` | `subfex` | Rc=1 | Subtract From Extended |
|
||||
| `subfeo.` | `subfex` | OE=1, Rc=1 | Subtract From Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subfe[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subfex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000110`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `136`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subfex: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | subfex: read | Source GPR. |
|
||||
| `RD` | subfex: write | Destination GPR. |
|
||||
| `CR` | subfex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | subfex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subfex`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subfex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ~(RA) + (RB) + CA
|
||||
CA <- carry_out
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subfex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:468`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L468)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:867`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L867)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:288-306`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L288-L306)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subfex => {
|
||||
// PPCBUG-008: 32-bit ABI. Compute in u32 space — `!ra` on u64 always
|
||||
// pollutes the upper 32 bits, making this an active poisoner.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = (!ra32).wrapping_add(rb32).wrapping_add(ca);
|
||||
ctx.xer_ca = if rb32 > ra32 || (rb32 == ra32 && ca != 0) { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
// RT <- !RA + RB + CA == RB - RA - 1 + CA (32-bit semantics).
|
||||
let true_sum = (rb32 as i32 as i128) - (ra32 as i32 as i128) - 1 + (ca as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RT ← ~RA + RB + XER[CA]`.** The middle link of a multi-word subtract chain seeded by [`subfcx`](subfcx.md). `XER[CA]` propagates the borrow from the previous word.
|
||||
- **Carry-out predicate handles the boundary case.** Xenia computes `CA' = (rb > ra) || (rb == ra && CA != 0)` ([`interpreter.rs:170`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L170)). The second clause covers when `RB == RA` and the previous chain added a `+1` from the input carry — without it, the carry-out would be wrong.
|
||||
- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores it.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:173`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L173).
|
||||
- **`XER[CA]` must be initialised** (typically by [`subfcx`](subfcx.md)). Reading stale `CA` is a frequent multi-word-subtract bug.
|
||||
- **Symmetry with [`addex`](addex.md).** `subfe RT, RA, RB` ≡ `adde RT, ~RA, RB` (with the implicit complement).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfcx`](subfcx.md) — seeds the chain (no `CA` read).
|
||||
- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) — terminate the chain (`~RA + −1 + CA`, `~RA + 0 + CA`).
|
||||
- [`subfx`](subfx.md) — plain subtract.
|
||||
- [`addex`](addex.md) — dual.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subfe` (Subtract From Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfe-subtract-from-extended-instruction)
|
||||
131
migration/project-root/ppc-manual/alu/subficx.md
Normal file
131
migration/project-root/ppc-manual/alu/subficx.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# `subficx` — Subtract From Immediate Carrying
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x20000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subfic` | `subficx` | — | Subtract From Immediate Carrying |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subfic [RD], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subficx` — form `D`
|
||||
|
||||
- **Opcode word:** `0x20000000`
|
||||
- **Primary opcode (bits 0–5):** `8`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subficx: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | subficx: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
| `RD` | subficx: write | Destination GPR. |
|
||||
| `CA` | subficx: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subficx`
|
||||
|
||||
- **Reads (always):** `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subficx`: **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subficx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subficx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:459`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L459)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:333`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L333)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:155-164`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L155-L164)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subficx => {
|
||||
// PPCBUG-005: 32-bit ABI. Sign-extended imm has bits 32-63 set for
|
||||
// negative SIMM, poisoning the writeback. Canary uses 32-bit form.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let imm32 = instr.simm16() as i32 as u32;
|
||||
let result32 = imm32.wrapping_sub(ra32);
|
||||
ctx.xer_ca = if imm32 >= ra32 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RT ← SIMM − RA` with `XER[CA]` always set.** Note the operand order: the *immediate* is the minuend, not the subtrahend. `subfic rD, rA, 1` computes `1 - rA`, useful for negation-plus-one or one's-complement-style operations.
|
||||
- **Immediate is sign-extended** to 64 bits before the subtract. So `subfic rD, rA, -1` computes `-1 - rA`, equivalent to `~rA`.
|
||||
- **`XER[CA] = 1` when `SIMM >= RA`** (no borrow). Computed in xenia as `if imm >= ra` ([`interpreter.rs:73`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L73)) — comparing the sign-extended unsigned representations.
|
||||
- **No `Rc`, no `OE`.** This D-form has no flag bits beyond the implicit `CA` write.
|
||||
- **No record-form variant.** There is no `subfic.` in the ISA; if you need CR0 to also reflect the result, follow with a `cmpwi`.
|
||||
- **Synthesised "subtract immediate"**. Assemblers sometimes accept `subi rD, rA, value` as a shorthand for `addi rD, rA, -value`, but for the carry-producing variant you must use `subfic` explicitly.
|
||||
- **Common idiom: `subfic rD, rA, 0`** computes `-rA` and sets `CA` according to whether `rA == 0` (no borrow) or `rA != 0` (borrow). Equivalent to [`negx`](negx.md) when `CA` doesn't matter.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfcx`](subfcx.md) — XO-form register version.
|
||||
- [`subfx`](subfx.md) — register subtract without `XER[CA]`.
|
||||
- [`addic`](addic.md), [`addicx`](addicx.md) — dual: add immediate carrying.
|
||||
- [`negx`](negx.md) — equivalent to `subfic rD, rA, 0` when `CA` is unwanted.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subfic` (Subtract From Immediate Carrying)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfic-subtract-from-immediate-carrying-instruction)
|
||||
145
migration/project-root/ppc-manual/alu/subfmex.md
Normal file
145
migration/project-root/ppc-manual/alu/subfmex.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# `subfmex` — Subtract From Minus One Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c0001d0`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subfme` | `subfmex` | — | Subtract From Minus One Extended |
|
||||
| `subfmeo` | `subfmex` | OE=1 | Subtract From Minus One Extended |
|
||||
| `subfme.` | `subfmex` | Rc=1 | Subtract From Minus One Extended |
|
||||
| `subfmeo.` | `subfmex` | OE=1, Rc=1 | Subtract From Minus One Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subfme[OE][Rc] [RD], [RA]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subfmex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c0001d0`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `232`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subfmex: read | Source GPR (`r0`–`r31`). |
|
||||
| `CA` | subfmex: read; subfmex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `RD` | subfmex: write | Destination GPR. |
|
||||
| `CR` | subfmex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | subfmex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subfmex`
|
||||
|
||||
- **Reads (always):** `RA`, `CA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subfmex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subfmex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfmex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:486`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L486)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:871`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L871)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:325-341`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L325-L341)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subfmex => {
|
||||
// PPCBUG-019: also fixes the always-true CA edge — `!ra` on u64
|
||||
// is non-zero when ra32==0xFFFFFFFF and ca==0, so CA was stuck at 1.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = (!ra32).wrapping_add(ca).wrapping_sub(1);
|
||||
ctx.xer_ca = if (!ra32) != 0 || ca != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = -(ra32 as i32 as i128) - 2 + (ca as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RT ← ~RA + (−1) + XER[CA]` ≡ `~RA − 1 + CA`.** Terminator for a multi-word subtract chain when the high "minuend" word is implicitly all-ones (e.g. when computing `~x` style negation across many words).
|
||||
- **`RB` field unused.** XO-form but only `RA` is read.
|
||||
- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:191`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L191)). Only when `RA == ~0` (all ones) AND `CA == 0` does `CA'` become 0 — every other case produces no borrow on this final word.
|
||||
- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores it.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:194`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L194).
|
||||
- **`XER[CA]` must be initialised** (typically by a [`subfcx`](subfcx.md) or [`subfex`](subfex.md) earlier in the chain).
|
||||
- **Symmetric with [`addmex`](addmex.md)**, the add-side terminator.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfzex`](subfzex.md) — terminate with `~RA + 0 + CA` instead.
|
||||
- [`subfex`](subfex.md) — middle-of-chain.
|
||||
- [`subfcx`](subfcx.md) — chain seed.
|
||||
- [`addmex`](addmex.md) — dual.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subfme` (Subtract From Minus One Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfme-subtract-from-minus-one-extended-instruction)
|
||||
148
migration/project-root/ppc-manual/alu/subfx.md
Normal file
148
migration/project-root/ppc-manual/alu/subfx.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# `subfx` — Subtract From
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000050`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subf` | `subfx` | — | Subtract From |
|
||||
| `subfo` | `subfx` | OE=1 | Subtract From |
|
||||
| `subf.` | `subfx` | Rc=1 | Subtract From |
|
||||
| `subfo.` | `subfx` | OE=1, Rc=1 | Subtract From |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subf[OE][Rc] [RD], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subfx` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000050`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `40`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subfx: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | subfx: read | Source GPR. |
|
||||
| `RD` | subfx: write | Destination GPR. |
|
||||
| `CR` | subfx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | subfx: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subfx`
|
||||
|
||||
- **Reads (always):** `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subfx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- ~(RA) + (RB) + 1 ; = (RB) − (RA)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subfx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:427`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L427)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:863`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L863)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:255-269`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L255-L269)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subfx => {
|
||||
// PPCBUG-017+020: 32-bit truncation.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let rb32 = ctx.gpr[instr.rb()] as u32;
|
||||
let result32 = rb32.wrapping_sub(ra32);
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_diff = (rb32 as i32 as i128) - (ra32 as i32 as i128);
|
||||
overflow::apply(ctx, true_diff != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Extended Pseudocode
|
||||
|
||||
```
|
||||
RT <- ~(RA) + (RB) + 1 ; = (RB) − (RA)
|
||||
if OE then
|
||||
XER[OV] <- signed_overflow_of_subtract((RB), (RA), RT)
|
||||
XER[SO] <- XER[SO] | XER[OV]
|
||||
if Rc then
|
||||
CR0[LT,GT,EQ] <- signed_compare(RT, 0)
|
||||
CR0[SO] <- XER[SO]
|
||||
```
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operand order gotcha.** `subf RT, RA, RB` computes `RT ← RB − RA`, **not** `RA − RB`. This reverses the intuitive ordering seen in x86/ARM. The assembler exposes a simplified mnemonic `sub RT, RX, RY` ≡ `subf RT, RY, RX` that restores the natural order — watch for both forms in disassembly.
|
||||
- **Implemented as add-with-complement.** Hardware (and xenia) compute `~RA + RB + 1`. All overflow/CR semantics are the same as [`addx`](addx.md) with one operand complemented.
|
||||
- **No `XER[CA]` update** — use [`subfcx`](subfcx.md) if you need a borrow-out bit.
|
||||
- **No trap on overflow.** `subfo` / `subfo.` only record the event in `XER[OV]` and sticky-set `XER[SO]`.
|
||||
- **Signed-overflow predicate.** `OV = ((RA ^ RB) & (RB ^ RT)) >> 63` — set when operands have different signs and the result's sign differs from `RB`'s.
|
||||
- **64-bit CR update on Xenon** (xenia-rs truncates to 32 bits; see [`addx`](addx.md) note).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfcx`](subfcx.md) — subtract-from producing `XER[CA]` (borrow-out).
|
||||
- [`subfex`](subfex.md) — `~RA + RB + XER[CA]` (subtract-with-borrow chain).
|
||||
- [`subfmex`](subfmex.md), [`subfzex`](subfzex.md) — subtract-from `−1` / `0` with carry-in (propagates borrows).
|
||||
- [`subfic`](subfic.md) — D-form: `RT ← SIMM − RA` with `XER[CA]`.
|
||||
- [`negx`](negx.md) — specialises to `0 − RA`.
|
||||
- [`addx`](addx.md) — inverse; shares overflow machinery.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subf` (Subtract From)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subf-subtract-from-instruction)
|
||||
- [AIX 7.3 — `sub` (simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-sub-subtract)
|
||||
146
migration/project-root/ppc-manual/alu/subfzex.md
Normal file
146
migration/project-root/ppc-manual/alu/subfzex.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# `subfzex` — Subtract From Zero Extended
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000190`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `subfze` | `subfzex` | — | Subtract From Zero Extended |
|
||||
| `subfzeo` | `subfzex` | OE=1 | Subtract From Zero Extended |
|
||||
| `subfze.` | `subfzex` | Rc=1 | Subtract From Zero Extended |
|
||||
| `subfzeo.` | `subfzex` | OE=1, Rc=1 | Subtract From Zero Extended |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
subfze[OE][Rc] [RD], [RA]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `subfzex` — form `XO`
|
||||
|
||||
- **Opcode word:** `0x7c000190`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `200`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (31) |
|
||||
| 6–10 | `RT` | destination GPR |
|
||||
| 11–15 | `RA` | source A |
|
||||
| 16–20 | `RB` | source B |
|
||||
| 21 | `OE` | overflow-enable flag |
|
||||
| 22–30 | `XO` | extended opcode (9 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RA` | subfzex: read | Source GPR (`r0`–`r31`). |
|
||||
| `CA` | subfzex: read; subfzex: write | XER[CA] carry bit. Read by add-with-carry/subtract-with-borrow instructions, written by carrying instructions. |
|
||||
| `RD` | subfzex: write | Destination GPR. |
|
||||
| `CR` | subfzex: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `OE` | subfzex: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `subfzex`
|
||||
|
||||
- **Reads (always):** `RA`, `CA`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`, `CA`
|
||||
- **Writes (conditional):** `CR`, `OE`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `subfzex`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`.; **XER[CA]** ← carry-out of the add / borrow-in of the subtract (always).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`subfzex`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfzex"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:504`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L504)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:83`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L83)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:869`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L869)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:307-324`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L307-L324)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::subfzex => {
|
||||
// PPCBUG-018: same active-poisoning shape as subfex; operate in u32.
|
||||
let ra32 = ctx.gpr[instr.ra()] as u32;
|
||||
let ca = ctx.xer_ca as u32;
|
||||
let result32 = (!ra32).wrapping_add(ca);
|
||||
// RT <- !RA + CA (no -1 term). 32-bit carry-out only when
|
||||
// !ra32 = u32::MAX (i.e. ra32 = 0) AND ca = 1.
|
||||
ctx.xer_ca = if ra32 == 0 && ca != 0 { 1 } else { 0 };
|
||||
ctx.gpr[instr.rd()] = result32 as u64;
|
||||
if instr.oe() {
|
||||
let true_sum = -(ra32 as i32 as i128) - 1 + (ca as i128);
|
||||
overflow::apply(ctx, true_sum != (result32 as i32) as i128);
|
||||
}
|
||||
if instr.rc_bit() {
|
||||
ctx.update_cr_signed(0, result32 as i32 as i64);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RT ← ~RA + 0 + XER[CA]` ≡ `~RA + CA`.** The subtract-side high-word terminator for a multi-word subtract chain. Implements `0 - (...) - borrow` for the high word.
|
||||
- **`RB` field unused.**
|
||||
- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:180`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L180)). Only `RA == ~0 && CA == 0` produces `CA' = 0`; every other case gives no-borrow.
|
||||
- **`OE=1`** should set `XER[OV]` on signed overflow; xenia-rs ignores.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:183`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L183).
|
||||
- **`XER[CA]` must be initialised** by an earlier carrying instruction.
|
||||
- **Common idiom: extracting `XER[CA]` as 0/-1.** `subfze rT, rN` (where `rN == 0`) materialises `XER[CA]` to `0` or `-1` (`-1 = ~0 + CA = -1 + CA`); pair with [`addzex`](addzex.md) for `0/1` instead.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`subfmex`](subfmex.md) — terminator with `~RA + (−1) + CA`.
|
||||
- [`subfex`](subfex.md), [`subfcx`](subfcx.md) — chain middle / seed.
|
||||
- [`addzex`](addzex.md) — dual; produces 0/1 from `CA`.
|
||||
- [`negx`](negx.md) — `subfze` with `RA = 0` and `CA = 1` is functionally similar.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `subfze` (Subtract From Zero Extended)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-subfze-subtract-from-zero-extended-instruction)
|
||||
113
migration/project-root/ppc-manual/alu/sync.md
Normal file
113
migration/project-root/ppc-manual/alu/sync.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# `sync` — Synchronize
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c0004ac`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sync` | `sync` | — | Synchronize |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sync
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sync` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c0004ac`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `598`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sync`
|
||||
|
||||
- **Reads (always):** _none_
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
multi-thread memory barrier (heavy). L=0 full sync; L=1 lightweight sync.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`sync`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sync"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:754`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_memory.cc#L754)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:85`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L85)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:825`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L825)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1691-1693`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1691-L1693)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Heavy multi-thread memory barrier.** All memory accesses (loads and stores, cacheable and not) issued by this thread before `sync` complete with respect to all other threads/processors before any subsequent memory access begins. Drains the store queue.
|
||||
- **`L` field selects sync class.** `L=0` is full *hwsync* (the default). `L=1` is `lwsync` — orders only loads-after-loads, loads-after-stores, and stores-after-stores (not stores-after-loads). The Xenon implements both via the same encoding with `L` (bit 9) selecting variant. Most disassembly shows the unsuffixed `sync` mnemonic, which assembles to `L=0`.
|
||||
- **No register or CR effects.** Pure ordering primitive.
|
||||
- **Used to implement release semantics.** A typical lock-release sequence is `sync; stw r0, lock`. Acquire side uses `lwsync` after the load.
|
||||
- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1267) collapses `sync`, `eieio`, `isync` into PC-advance. Since xenia is single-threaded interpretation, host program order subsumes all PPC ordering.
|
||||
- **Distinct from [`isync`](isync.md)**, which orders the *instruction* stream — `sync` does not refetch instructions.
|
||||
- **Slow on real hardware.** Hundreds of cycles when the store queue is full; hot paths avoid `sync` and use `lwsync` or no barrier when only single-thread ordering is needed.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`isync`](isync.md) — instruction-fetch barrier.
|
||||
- [`eieio`](eieio.md) — lighter I/O barrier for caching-inhibited storage.
|
||||
- `lwsync` — same encoding, `L=1`; not separately enumerated in this page set.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sync` (Synchronize)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sync-synchronize-instruction)
|
||||
- PowerISA v2.07B, Book II, §1.7 — defines `hwsync`/`lwsync`/`ptesync` semantics.
|
||||
115
migration/project-root/ppc-manual/alu/xori.md
Normal file
115
migration/project-root/ppc-manual/alu/xori.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# `xori` — XOR Immediate
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x68000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `xori` | `xori` | — | XOR Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
xori [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `xori` — form `D`
|
||||
|
||||
- **Opcode word:** `0x68000000`
|
||||
- **Primary opcode (bits 0–5):** `26`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | xori: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | xori: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | xori: write | Source GPR (`r0`–`r31`). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `xori`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) ^ (0x0000 || UIMM)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`xori`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xori"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:839`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L839)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:349`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L349)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:520-523`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L520-L523)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No record form.** Like [`ori`](ori.md), there is no `xori.`. To get a CR0 update follow with `cmpwi` or use [`xorx`](xorx.md) with `Rc=1`.
|
||||
- **Immediate is zero-extended** to 64 bits. Only the low 16 bits of `RA` can be flipped; the high 48 bits are passed through from `RS` unchanged.
|
||||
- **`xori 0, 0, 0` is a valid NOP encoding** but the canonical NOP is `ori 0, 0, 0`. Disassemblers should still display this as `xori r0, r0, 0` or recognise it as a no-op.
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:338`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L338) — full `u64` XOR with the immediate.
|
||||
- **No `XER`, no `CR`** side effects.
|
||||
- **`RA = 0` reads `r0`** (not literal zero); see [`ori`](ori.md).
|
||||
- **Useful for masked toggle.** `xori rA, rS, mask` flips the bits of `rS` indicated by `mask` (low 16 bits only).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`xoris`](xoris.md) — companion (immediate shifted left 16).
|
||||
- [`xorx`](xorx.md) — register-register XOR.
|
||||
- [`eqvx`](eqvx.md) — `~(RS ^ RB)`.
|
||||
- [`ori`](ori.md), [`andix`](andix.md) — sister immediate logicals.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `xori` (XOR Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xori-immediate-instruction)
|
||||
114
migration/project-root/ppc-manual/alu/xoris.md
Normal file
114
migration/project-root/ppc-manual/alu/xoris.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# `xoris` — XOR Immediate Shifted
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x6c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `xoris` | `xoris` | — | XOR Immediate Shifted |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
xoris [RA], [RS], [UIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `xoris` — form `D`
|
||||
|
||||
- **Opcode word:** `0x6c000000`
|
||||
- **Primary opcode (bits 0–5):** `27`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | xoris: read | Source GPR (alias for RD in some stores). |
|
||||
| `UIMM` | xoris: read | 16-bit unsigned immediate. Zero-extended. |
|
||||
| `RA` | xoris: write | Source GPR (`r0`–`r31`). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `xoris`
|
||||
|
||||
- **Reads (always):** `RS`, `UIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) ^ (UIMM || 0x0000)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`xoris`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xoris"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:846`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L846)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:350`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L350)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:524-527`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L524-L527)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xoris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No record form.** Like all immediate logicals other than `andi.`/`andis.`, `xoris` does not update CR0.
|
||||
- **Immediate is zero-extended *then* shifted left 16.** Only bits 32–47 of `RA` (PowerISA bit numbering) can be flipped; the high 32 bits and low 16 bits of `RA` come from `RS` unchanged.
|
||||
- **Common pattern with [`xori`](xori.md)** to flip arbitrary 32-bit bitmasks: `xoris RA, RS, hi16; xori RA, RA, lo16`.
|
||||
- **64-bit operation.** [`interpreter.rs:342`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L342).
|
||||
- **No `XER`, no `CR`.**
|
||||
- **`RA = 0` reads `r0`** (not literal zero).
|
||||
- **Used to toggle the high half of a 32-bit word**, e.g. `xoris r3, r3, 0x8000` flips bit 32 (the sign bit of the low word) — a one-instruction sign-flip on a 32-bit value.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`xori`](xori.md) — companion (immediate not shifted).
|
||||
- [`xorx`](xorx.md) — register-register XOR.
|
||||
- [`oris`](oris.md), [`andisx`](andisx.md) — sister immediate-shifted logicals.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `xoris` (XOR Immediate Shifted)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xoris-immediate-shifted-instruction)
|
||||
121
migration/project-root/ppc-manual/alu/xorx.md
Normal file
121
migration/project-root/ppc-manual/alu/xorx.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `xorx` — XOR
|
||||
|
||||
> **Category:** [Integer ALU](../categories/alu.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000278`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `xor` | `xorx` | — | XOR |
|
||||
| `xor.` | `xorx` | Rc=1 | XOR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
xor[Rc] [RA], [RS], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `xorx` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000278`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `316`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `RS` | xorx: read | Source GPR (alias for RD in some stores). |
|
||||
| `RB` | xorx: read | Source GPR. |
|
||||
| `RA` | xorx: write | Source GPR (`r0`–`r31`). |
|
||||
| `CR` | xorx: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `xorx`
|
||||
|
||||
- **Reads (always):** `RS`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RA`
|
||||
- **Writes (conditional):** `CR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `xorx`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RA <- (RS) ^ (RB)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`xorx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xorx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:829`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc#L829)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:132`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L132)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:798`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L798)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:556-561`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L556-L561)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xorx => {
|
||||
// PPCBUG-032+020: 32-bit ABI CR0 view.
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ctx.gpr[instr.rb()];
|
||||
if instr.rc_bit() { ctx.update_cr_signed(0, ctx.gpr[instr.ra()] as u32 as i32 as i64); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`RA ← RS XOR RB`.** Bit-wise XOR.
|
||||
- **Idiom: `xor RA, RS, RS`** zeroes `RA` — the canonical "clear register" instruction. Cheaper than `li RA, 0` because no immediate-extraction stage is involved.
|
||||
- **Operand convention** is X-form (`RA` destination, `RS`/`RB` sources).
|
||||
- **64-bit operation** on Xenon.
|
||||
- **No `OE` or `XER` side effects.** Only `Rc=1` updates `CR0`.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:367`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L367) truncates with `as i32 as i64`. For `xor.` whose result has differing high/low halves, spec and xenia diverge; `xor. RA, RS, RS` gives `EQ` either way.
|
||||
- **Useful as bitmask toggle.** `xor r3, r3, r4` flips in `r3` every bit set in `r4`.
|
||||
- **No `XER[CA]`.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`xori`](xori.md), [`xoris`](xoris.md) — D-form immediate variants.
|
||||
- [`eqvx`](eqvx.md) — NXOR (`~(RS ^ RB)`).
|
||||
- [`andx`](andx.md), [`orx`](orx.md), [`norx`](norx.md), [`nandx`](nandx.md) — sister logicals.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `xor` (XOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-xor-instruction)
|
||||
164
migration/project-root/ppc-manual/branch/bcctrx.md
Normal file
164
migration/project-root/ppc-manual/branch/bcctrx.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# `bcctrx` — Branch Conditional to Count Register
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000420` · _sync_
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `bcctr` | `bcctrx` | — | Branch Conditional to Count Register |
|
||||
| `bcctrl` | `bcctrx` | LK=1 | Branch Conditional to Count Register |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
bcctr[LK] [BO], [BI]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `bcctrx` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000420`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `528`
|
||||
- **Synchronising:** yes
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `LK` | bcctrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
|
||||
| `BO` | bcctrx: read | 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. |
|
||||
| `BI` | bcctrx: read | CR bit index (0–31) selected by BO's condition test. |
|
||||
| `CR` | bcctrx: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CTR` | bcctrx: read | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. |
|
||||
| `LR` | bcctrx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `bcctrx`
|
||||
|
||||
- **Reads (always):** `LK`, `BO`, `BI`, `CR`, `CTR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** `LR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
cond_ok <- BO[0] | (CR[BI] ≡ BO[1])
|
||||
if cond_ok then NIA <- CTR[0:61] || 0b00
|
||||
if LK then LR <- CIA + 4
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`bcctrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcctrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:250`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L250)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:721`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L721)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:962-981`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L962-L981)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bcctrx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
|
||||
let cond_ok = (bo & 0b10000) != 0
|
||||
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
|
||||
|
||||
if cond_ok {
|
||||
let next_pc = ctx.pc + 4;
|
||||
ctx.pc = (ctx.ctr as u32) & !3;
|
||||
if instr.lk() {
|
||||
ctx.lr = next_pc as u64;
|
||||
}
|
||||
} else {
|
||||
if instr.lk() {
|
||||
ctx.lr = (ctx.pc + 4) as u64;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **No CTR decrement.** Unlike [`bcx`](bcx.md) and [`bclrx`](bclrx.md), `bcctr` cannot decrement CTR (the CTR is the *target*). The PowerISA reserves `BO[2] = 0` encodings — they are *invalid* on `bcctrx`. xenia silently ignores `BO[2]`/`BO[3]` and treats every `bcctr` as a pure CR-conditional branch, which matches both the canary emit and real Xenon hardware behaviour.
|
||||
- **CTR alignment mask.** The target is `CTR & ~3`. Like `bclr`, the low two bits are stripped — a misaligned CTR is silently rounded down rather than trapping.
|
||||
- **BO encoding (CR-only subset).** Because CTR-test bits are unused, only four `BO` patterns are meaningful:
|
||||
|
||||
| BO (binary) | Meaning |
|
||||
| --- | --- |
|
||||
| `0100z` | branch if `CR[BI]` false |
|
||||
| `0101z` | branch if `CR[BI]` true |
|
||||
| `1z1zz` | branch always (`bctr`) |
|
||||
| `0000z`/`001at`/etc. | reserved — implementation-defined |
|
||||
|
||||
- **Indirect call/dispatch idiom.** `mtctr rN; bctrl` is the canonical PPC indirect call: load function pointer into CTR, call. The xenia interpreter writes `LR ← CIA + 4` only when the branch is taken — this matches the PowerISA, but contrast with `bcx` where `LK` always writes LR (even if the branch is not taken). The C-translation reference handles this asymmetry explicitly.
|
||||
- **`bctr` for switch tables.** Compilers emit `bctr` (not `bctrl`) for jump-table dispatch, with CTR loaded from a base + (index*4) lookup. Xenia honours this by simply jumping to `CTR & ~3`.
|
||||
- **Synchronisation.** Marked `sync` in xenia's XML — context-synchronising. JIT backends must ensure prior side effects have committed before the indirect transfer.
|
||||
- **No prediction hint sensitivity.** Xenon predicts indirect branches via a separate target cache; the `BO[4]` hint is mostly cosmetic for `bcctr`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`bclrx`](bclrx.md) — branch conditional to **LR** (function returns).
|
||||
- [`bcx`](bcx.md) — branch conditional to displacement (B-form).
|
||||
- [`bx`](bx.md) — unconditional displacement branch (I-form).
|
||||
- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) — load/read CTR via `mtspr 9` / `mfspr 9`.
|
||||
- [`sc`](sc.md) — alternative control-flow exit (system call).
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion |
|
||||
| --- | --- |
|
||||
| `bctr` | `bcctr BO=0b10100, BI=0` — unconditional indirect branch |
|
||||
| `bctrl` | `bcctrl BO=0b10100, BI=0` — unconditional indirect call |
|
||||
| `beqctr crN` | `bcctr BO=0b01100, BI=4·N+2` — call CTR if `crN.EQ` |
|
||||
| `bnectr crN` | `bcctr BO=0b00100, BI=4·N+2` — call CTR if `crN.NE` |
|
||||
| `bltctr crN` | `bcctr BO=0b01100, BI=4·N+0` — call CTR if `crN.LT` |
|
||||
| `bgectr crN` | `bcctr BO=0b00100, BI=4·N+0` — call CTR if `crN.GE` |
|
||||
| `bgtctr crN` | `bcctr BO=0b01100, BI=4·N+1` — call CTR if `crN.GT` |
|
||||
| `blectr crN` | `bcctr BO=0b00100, BI=4·N+1` — call CTR if `crN.LE` |
|
||||
|
||||
The unconditional `bctr`/`bctrl` are by far the most common in Xbox 360 disassembly (compiler-emitted indirect calls and switch dispatch).
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `bcctr` (Branch Conditional to Count Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bcctr-bcctrl-branch-conditional-count-register-instruction)
|
||||
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)
|
||||
180
migration/project-root/ppc-manual/branch/bclrx.md
Normal file
180
migration/project-root/ppc-manual/branch/bclrx.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# `bclrx` — Branch Conditional to Link Register
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000020` · _sync_
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `bclr` | `bclrx` | — | Branch Conditional to Link Register |
|
||||
| `bclrl` | `bclrx` | LK=1 | Branch Conditional to Link Register |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
bclr[LK] [BO], [BI]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `bclrx` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000020`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `16`
|
||||
- **Synchronising:** yes
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `LK` | bclrx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
|
||||
| `BO` | bclrx: read | 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. |
|
||||
| `BI` | bclrx: read | CR bit index (0–31) selected by BO's condition test. |
|
||||
| `CR` | bclrx: read (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CTR` | bclrx: read (conditional); bclrx: write (conditional) | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. |
|
||||
| `LR` | bclrx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `bclrx`
|
||||
|
||||
- **Reads (always):** `LK`, `BO`, `BI`
|
||||
- **Reads (conditional):** `CR`, `CTR`
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** `CTR`, `LR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if ¬BO[2] then CTR <- CTR − 1
|
||||
ctr_ok <- BO[2] | ((CTR ≠ 0) XOR BO[3])
|
||||
cond_ok <- BO[0] | (CR[BI] ≡ BO[1])
|
||||
if ctr_ok & cond_ok then NIA <- LR[0:61] || 0b00
|
||||
if LK then LR <- CIA + 4
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* bclr/bclrl — branch conditional to LR */
|
||||
if (!(insn.BO & 4)) ctr -= 1;
|
||||
bool ctr_ok = (insn.BO & 4) || ((ctr != 0) ^ !!(insn.BO & 2));
|
||||
bool cond_ok = (insn.BO & 16) || (cr_bit(insn.BI) == !!(insn.BO & 8));
|
||||
uint32_t next = pc + 4;
|
||||
if (ctr_ok && cond_ok) pc = lr & ~3u; else pc = next;
|
||||
if (insn.LK) lr = next;
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`bclrx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bclrx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:282`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L282)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:711`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L711)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:939-961`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L939-L961)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bclrx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
|
||||
if bo & 0b00100 == 0 {
|
||||
ctx.ctr = ctx.ctr.wrapping_sub(1);
|
||||
}
|
||||
|
||||
let ctr_ok = (bo & 0b00100) != 0
|
||||
|| (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0));
|
||||
let cond_ok = (bo & 0b10000) != 0
|
||||
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
|
||||
|
||||
let next_pc = ctx.pc + 4;
|
||||
if ctr_ok && cond_ok {
|
||||
ctx.pc = (ctx.lr as u32) & !3;
|
||||
} else {
|
||||
ctx.pc = next_pc;
|
||||
}
|
||||
if instr.lk() {
|
||||
ctx.lr = next_pc as u64;
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## BO Encoding (5 bits)
|
||||
|
||||
`BO` controls two independent tests and two "hints". Bit 0 is the MSB.
|
||||
|
||||
| BO (binary) | CTR decrement? | CTR test | CR test | Meaning |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `0000z` | yes | `CTR ≠ 0` | `¬CR[BI]` | decrement, branch if CTR ≠ 0 **and** CR[BI] false |
|
||||
| `0001z` | yes | `CTR = 0` | `¬CR[BI]` | decrement, branch if CTR = 0 **and** CR[BI] false |
|
||||
| `001at` | yes | `CTR ≠ 0` / `CTR = 0` | — | decrement, branch on CTR only |
|
||||
| `0100z` | no | — | `¬CR[BI]` | branch if CR[BI] false |
|
||||
| `0101z` | no | — | `CR[BI]` | branch if CR[BI] true |
|
||||
| `011at` | no | — | — | branch always (`z` and `t` are prediction hints) |
|
||||
| `1z00z` | yes | `CTR ≠ 0` | — | decrement, branch if CTR ≠ 0 |
|
||||
| `1z01z` | yes | `CTR = 0` | — | decrement, branch if CTR = 0 |
|
||||
| `1z1zz` | no | — | — | branch always |
|
||||
|
||||
Bit **`BO[0]` = 1** disables the CR test; **`BO[2]` = 1** disables the CTR decrement/test. `BO[1]` and `BO[3]` select the polarity of each test. `BO[4]` is a branch-prediction hint (0 = not taken, 1 = taken; ignored on the Xenon in most cases).
|
||||
|
||||
The most common bclr instance in Xbox 360 disassembly is `BO = 0b10100` → `blr` (branch always to LR), the function epilogue. `BO = 0b01100, BI = 2` → `beqlr` (return if `cr0.EQ`), also common.
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **LR alignment mask.** The target address is `LR & ~3` — the low 2 bits are cleared. This silently ignores a misaligned LR; incoming code should always produce 4-byte-aligned LR values.
|
||||
- **Ordering of CTR decrement and branch.** The CTR is decremented **first**, then compared to zero **after** the decrement. So after `bdnz` at `CTR = 1`, the CTR becomes `0` and the branch is not taken.
|
||||
- **Self-referential LR write.** `bclrl` writes `LR ← CIA + 4` **before** reading `LR` to set `NIA`. Per the PowerISA, `bclrl` reads the *old* `LR` for the branch target and writes the *new* `LR` with the return address, atomically from software's perspective. Xenia implements it this way (`next_pc` captured first, then `lr` written).
|
||||
- **Branch prediction hints (`BO[4]`).** The Xenon does static prediction on the basis of these hints, but behaviour is architecturally unobservable. Translators may ignore them.
|
||||
- **Synchronisation.** `bclr` is **context-synchronising** (hence the `sync` flag in xenia's XML). Translators must ensure side-effecting instructions preceding the branch have committed — trivial in a sequential C translation but relevant for JIT backends.
|
||||
- **xenia's `LR_HALT_SENTINEL`.** Xenia sets `LR` to `0xBCBCBCBC` at thread start; when the top-level guest function returns via `blr`, the interpreter loop halts cleanly. Translators replicating guest behaviour don't need this — but if you generate a test harness, the sentinel is a convenient "function returned" signal.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`bcctrx`](bcctrx.md) — branch conditional to **CTR** (used by indirect calls / vtables).
|
||||
- [`bcx`](bcx.md) — branch conditional to an immediate displacement (D-form).
|
||||
- [`bx`](bx.md) — unconditional branch (I-form).
|
||||
- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) — set/get LR via `mtspr 8, …` / `mfspr …, 8`.
|
||||
- [`sc`](sc.md) — system call (alternative control-flow exit).
|
||||
|
||||
## Simplified Mnemonics
|
||||
|
||||
Assemblers fold common `BO`/`BI` patterns to single mnemonics:
|
||||
|
||||
| Simplified | Expansion |
|
||||
| --- | --- |
|
||||
| `blr` | `bclr BO=0b10100, BI=0` — branch always to LR |
|
||||
| `blrl` | `bclrl BO=0b10100, BI=0` — branch always to LR with link (tail-call trampoline) |
|
||||
| `beqlr crN` | `bclr BO=0b01100, BI=4·N+2` — return if `crN.EQ` |
|
||||
| `bnelr crN` | `bclr BO=0b00100, BI=4·N+2` — return if `crN.NE` |
|
||||
| `bltlr crN` | `bclr BO=0b01100, BI=4·N+0` — return if `crN.LT` |
|
||||
| `bgelr crN` | `bclr BO=0b00100, BI=4·N+0` — return if `crN.GE` |
|
||||
| `bgtlr crN` | `bclr BO=0b01100, BI=4·N+1` — return if `crN.GT` |
|
||||
| `blelr crN` | `bclr BO=0b00100, BI=4·N+1` — return if `crN.LE` |
|
||||
|
||||
Xbox 360 disassemblers almost always emit the simplified form; the translation agent should learn to recognise them.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `bclr` (Branch Conditional to Link Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bclr-bclrl-branch-conditional-link-register-instruction)
|
||||
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)
|
||||
192
migration/project-root/ppc-manual/branch/bcx.md
Normal file
192
migration/project-root/ppc-manual/branch/bcx.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# `bcx` — Branch Conditional
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [B](../forms/B.md) · **Opcode:** `0x40000000` · _sync_
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `bc` | `bcx` | — | Branch Conditional |
|
||||
| `bcl` | `bcx` | LK=1 | Branch Conditional |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
bc[LK][AA] [BO], [BI], [ADDR]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `bcx` — form `B`
|
||||
|
||||
- **Opcode word:** `0x40000000`
|
||||
- **Primary opcode (bits 0–5):** `16`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** yes
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `BO` | branch options |
|
||||
| 11–15 | `BI` | CR bit to test |
|
||||
| 16–29 | `BD` | signed 14-bit word-offset target |
|
||||
| 30 | `AA` | absolute-address flag |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `LK` | bcx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
|
||||
| `AA` | bcx: read | Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address. |
|
||||
| `BO` | bcx: read | 5-bit branch options — selects CTR decrement, CTR test polarity, and CR bit test polarity. See `forms/XL.md`. |
|
||||
| `BI` | bcx: read | CR bit index (0–31) selected by BO's condition test. |
|
||||
| `ADDR` | bcx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). |
|
||||
| `CR` | bcx: read (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CTR` | bcx: read (conditional); bcx: write (conditional) | Count register. Decremented and optionally tested by conditional branches when `BO[2]=0`. |
|
||||
| `LR` | bcx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `bcx`
|
||||
|
||||
- **Reads (always):** `LK`, `AA`, `BO`, `BI`, `ADDR`
|
||||
- **Reads (conditional):** `CR`, `CTR`
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** `CTR`, `LR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
if ¬BO[2] then CTR <- CTR − 1
|
||||
ctr_ok <- BO[2] | ((CTR ≠ 0) XOR BO[3])
|
||||
cond_ok <- BO[0] | (CR[BI] ≡ BO[1])
|
||||
if ctr_ok & cond_ok then NIA <- CIA + EXTS(BD || 0b00) (AA=0)
|
||||
EXTS(BD || 0b00) (AA=1)
|
||||
if LK then LR <- CIA + 4
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`bcx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:173`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L173)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:340`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L340)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:908-938`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L908-L938)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bcx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
|
||||
// Decrement CTR if needed
|
||||
if bo & 0b00100 == 0 {
|
||||
ctx.ctr = ctx.ctr.wrapping_sub(1);
|
||||
}
|
||||
|
||||
let ctr_ok = (bo & 0b00100) != 0
|
||||
|| (((ctx.ctr as u32) != 0) ^ ((bo & 0b00010) != 0));
|
||||
let cond_ok = (bo & 0b10000) != 0
|
||||
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
|
||||
|
||||
if ctr_ok && cond_ok {
|
||||
let target = if instr.aa() {
|
||||
instr.bd() as u32
|
||||
} else {
|
||||
ctx.pc.wrapping_add(instr.bd() as u32)
|
||||
};
|
||||
if instr.lk() {
|
||||
ctx.lr = (ctx.pc + 4) as u64;
|
||||
}
|
||||
ctx.pc = target;
|
||||
} else {
|
||||
if instr.lk() {
|
||||
ctx.lr = (ctx.pc + 4) as u64;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **14-bit signed displacement.** `BD` is a 14-bit signed word-count, scaled by 4 — yielding a ±32 KiB byte range (`−2^15 … +2^15 − 4`). For longer-range conditional control flow, compilers emit a short `bc` over an unconditional `b`.
|
||||
- **CTR decrement happens before the test.** `BO[2]=0` decrements CTR *first*, then `ctr_ok` evaluates against the new value. The classic `bdnz loop` loops `N` times when CTR is initialised to `N`.
|
||||
- **LR write is unconditional in xenia.** Xenia writes `LR ← CIA + 4` whenever `LK=1`, even on the not-taken path. This matches the PowerISA: `bcl` always sets `LR` regardless of branch outcome — exploited by `bcl 20, 31, $+4` as a self-PC capture (PIC trick).
|
||||
- **`BO` encoding** — see `bclrx.md` for the full 5-bit table. `bcx` supports the full set, including CTR-only branches (`bdnz`, `bdz`).
|
||||
- **Branch hint encoding.** PPC overloads `BO[4]` as a static prediction hint: 0 = "predict not taken", 1 = "predict taken". The Xenon honours it for forward branches; backwards conditional branches are predicted taken regardless. Translators may ignore the hint.
|
||||
- **Synchronisation.** Marked `sync` — like all branches, `bcx` is context-synchronising. Trivial in interpretation; matters for JIT reorder windows.
|
||||
- **No `Rc`.** B-form has no record bit; the apparent `Rc` operand-table entry under "Status-Register Effects" is N/A here.
|
||||
|
||||
### BO/BI encoding (compact table)
|
||||
|
||||
| BO | Effect | Common simplified |
|
||||
| --- | --- | --- |
|
||||
| `0000z` | dec CTR, branch if `CTR≠0` & `¬CR[BI]` | `bdnzf BI, addr` |
|
||||
| `0001z` | dec CTR, branch if `CTR=0` & `¬CR[BI]` | `bdzf BI, addr` |
|
||||
| `0010y` | dec CTR, branch if `CTR≠0` | `bdnz addr` |
|
||||
| `0011y` | dec CTR, branch if `CTR=0` | `bdz addr` |
|
||||
| `0100z` | branch if `¬CR[BI]` | `bf BI, addr` (or `bne`/`bge`/...) |
|
||||
| `0101z` | branch if `CR[BI]` | `bt BI, addr` (or `beq`/`blt`/...) |
|
||||
| `1z1zz` | branch always | `b addr` (prefer plain `b` though) |
|
||||
|
||||
Bit `z` is the prediction hint (`0` = not taken, `1` = taken).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`bx`](bx.md) — unconditional displacement branch (24-bit range).
|
||||
- [`bclrx`](bclrx.md) — branch conditional to LR (function return).
|
||||
- [`bcctrx`](bcctrx.md) — branch conditional to CTR (indirect call / dispatch).
|
||||
- [`crand`](../control/crand.md), [`cror`](../control/cror.md), … — combine multiple CR bits before a single `bc`.
|
||||
- [`mtctr`](../control/mtspr.md), [`mfctr`](../control/mfspr.md) — set/get loop counter for `bdnz`/`bdz`.
|
||||
- [`sc`](sc.md) — alternative control-flow exit.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
The `bc` mnemonic is rarely written directly; assemblers fold most uses into form-specific aliases:
|
||||
|
||||
| Simplified | Expansion |
|
||||
| --- | --- |
|
||||
| `beq crN, addr` | `bc 0b01100, 4·N+2, addr` — branch if `crN.EQ` |
|
||||
| `bne crN, addr` | `bc 0b00100, 4·N+2, addr` — branch if `crN.NE` |
|
||||
| `blt crN, addr` | `bc 0b01100, 4·N+0, addr` — branch if `crN.LT` |
|
||||
| `bge crN, addr` | `bc 0b00100, 4·N+0, addr` — branch if `crN.GE` |
|
||||
| `bgt crN, addr` | `bc 0b01100, 4·N+1, addr` — branch if `crN.GT` |
|
||||
| `ble crN, addr` | `bc 0b00100, 4·N+1, addr` — branch if `crN.LE` |
|
||||
| `bso crN, addr` | `bc 0b01100, 4·N+3, addr` — branch on summary overflow |
|
||||
| `bns crN, addr` | `bc 0b00100, 4·N+3, addr` — branch on no SO |
|
||||
| `bdnz addr` | `bc 0b10000, 0, addr` — decrement CTR, branch if non-zero |
|
||||
| `bdz addr` | `bc 0b10010, 0, addr` — decrement CTR, branch if zero |
|
||||
| `bdnzt BI, addr` | combined CTR + CR test (rare) |
|
||||
|
||||
When `crN` is omitted in disassembly, `cr0` is implied.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `bc` (Branch Conditional)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-bc-branch-conditional-instruction)
|
||||
- [AIX 7.3 — Branch simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)
|
||||
133
migration/project-root/ppc-manual/branch/bx.md
Normal file
133
migration/project-root/ppc-manual/branch/bx.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# `bx` — Branch
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [I](../forms/I.md) · **Opcode:** `0x48000000` · _sync_
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `b` | `bx` | — | Branch |
|
||||
| `bl` | `bx` | LK=1 | Branch |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
b[LK][AA] [ADDR]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `bx` — form `I`
|
||||
|
||||
- **Opcode word:** `0x48000000`
|
||||
- **Primary opcode (bits 0–5):** `18`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** yes
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–29 | `LI` | signed 24-bit word-offset target |
|
||||
| 30 | `AA` | absolute-address flag |
|
||||
| 31 | `LK` | link flag (bl/ba/bla) |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `LK` | bx: read | Link bit. When 1, LR ← address-of-next-instruction before the branch is taken. |
|
||||
| `AA` | bx: read | Absolute-address bit. When 1, the branch target is the sign-extended displacement itself; when 0, it is added to the current instruction address. |
|
||||
| `ADDR` | bx: read | Encoded branch target displacement (24-bit for I-form, 14-bit for B-form, word-shifted). |
|
||||
| `LR` | bx: write (conditional) | Link register. Written by `bl`/`bla`/`bcl`/`bclrl`/`bcctrl`; read by `bclr`/`bclrl`. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `bx`
|
||||
|
||||
- **Reads (always):** `LK`, `AA`, `ADDR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** `LR`
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
NIA <- (CIA + EXTS(LI || 0b00)) if AA=0
|
||||
<- EXTS(LI || 0b00) if AA=1
|
||||
if LK then LR <- CIA + 4
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* b / bl / ba / bla — unconditional branch (I-form, primary 18) */
|
||||
int32_t li = (int32_t)(insn.LI << 2); /* sign-extended word-offset */
|
||||
uint32_t target = insn.AA ? (uint32_t)li : (uint32_t)(pc + li);
|
||||
uint32_t next = pc + 4;
|
||||
if (insn.LK) lr = next; /* bl / bla save return addr */
|
||||
pc = target;
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`bx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:154`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L154)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:11`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L11)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:342`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L342)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:897-907`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L897-L907)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bx => {
|
||||
let target = if instr.aa() {
|
||||
instr.li() as u32
|
||||
} else {
|
||||
ctx.pc.wrapping_add(instr.li() as u32)
|
||||
};
|
||||
if instr.lk() {
|
||||
ctx.lr = (ctx.pc + 4) as u64;
|
||||
}
|
||||
ctx.pc = target;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **24-bit word-aligned target.** `LI` is a 24-bit signed word-count. Hardware concatenates `LI || 0b00` (adds the implicit two low zero bits) and sign-extends to 64 bits before using it as an address. The displacement range is therefore ±32 MiB in bytes (`−2^25 … +2^25 − 4`).
|
||||
- **Four mnemonics, one opcode.** The four runtime variants selected by `AA` and `LK`:
|
||||
- `b` — `AA = 0, LK = 0` — PC-relative, no link.
|
||||
- `bl` — `AA = 0, LK = 1` — PC-relative, `LR = CIA + 4` (the ubiquitous function-call primitive).
|
||||
- `ba` — `AA = 1, LK = 0` — absolute, no link.
|
||||
- `bla` — `AA = 1, LK = 1` — absolute, link.
|
||||
Xbox 360 code almost exclusively uses `b` and `bl`; `ba` / `bla` appear only in kernel / firmware stubs.
|
||||
- **Target alignment.** `LI` is scaled by 4, so all targets are 4-byte aligned by construction. There is no low-bit encoding of ARM-style Thumb — PPC has one instruction width.
|
||||
- **LR write is *before* the branch.** In `bl`, `LR` receives `CIA + 4` (the address of the instruction *after* the branch) before execution transfers to the target. Nested calls naturally overwrite LR; callees must spill it ([`mflr`](../control/mfspr.md) + `std`) before making their own `bl`.
|
||||
- **Indirect tail calls.** A tail call to an indirect target is encoded as `mtctr` + `bctr` (see [`bcctrx`](bcctrx.md)), not `bx` — `bx` has no register-based form.
|
||||
- **No condition test.** Use [`bcx`](bcx.md) for conditional displacement branches or [`bclrx`](bclrx.md) / [`bcctrx`](bcctrx.md) for conditional LR/CTR jumps.
|
||||
- **Speculative execution.** The Xenon fetches past `bx`; translators that mask control flow must treat the target as a single-destination control transfer.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`bcx`](bcx.md) — conditional displacement branch (B-form, ±32 KiB range).
|
||||
- [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) — branch to LR / CTR, conditional and unconditional.
|
||||
- [`mtlr`](../control/mtspr.md), [`mflr`](../control/mfspr.md) — LR save/restore for nested `bl` calls.
|
||||
- [`sc`](sc.md) — system call; an alternative control-flow exit to the kernel.
|
||||
|
||||
## Simplified Mnemonics
|
||||
|
||||
Assemblers emit `b`, `bl`, `ba`, `bla` for the four runtime combinations. There is no further simplification.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `b` (Branch)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-b-branch-instruction)
|
||||
- [AIX 7.3 — `bl` (Branch with Link, simplified mnemonic)](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-branch-simplified)
|
||||
129
migration/project-root/ppc-manual/branch/sc.md
Normal file
129
migration/project-root/ppc-manual/branch/sc.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# `sc` — System Call
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [SC](../forms/SC.md) · **Opcode:** `0x44000002` · _sync_
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `sc` | `sc` | — | System Call |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
sc [LEV]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `sc` — form `SC`
|
||||
|
||||
- **Opcode word:** `0x44000002`
|
||||
- **Primary opcode (bits 0–5):** `17`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** yes
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (17) |
|
||||
| 6–19 | `—` | reserved |
|
||||
| 20–26 | `LEV` | exception level |
|
||||
| 27–29 | `—` | reserved |
|
||||
| 30 | `1` | fixed 1 |
|
||||
| 31 | `—` | reserved |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `LEV` | sc: read | System-call exception level (for `sc`). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `sc`
|
||||
|
||||
- **Reads (always):** `LEV`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
system_call_exception(LEV)
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`sc`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sc"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:455`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L455)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:63`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L63)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:341`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L341)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:984-997`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L984-L997)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sc => {
|
||||
// PPCBUG-064: log non-zero LEV (`sc 2` is the Xbox 360 hypervisor-call
|
||||
// convention; canary dispatches it to a different handler than `sc 0`).
|
||||
// Routing LEV=2 requires a StepResult variant extension; deferred.
|
||||
let lev = (instr.raw >> 5) & 0x7F;
|
||||
if lev != 0 {
|
||||
tracing::warn!(
|
||||
"sc with LEV={} at {:#010x}: dispatched as plain SystemCall (HVcall routing not implemented)",
|
||||
lev, ctx.pc
|
||||
);
|
||||
}
|
||||
ctx.pc += 4;
|
||||
return StepResult::SystemCall;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`LEV` field — kernel vs hypervisor.** The 7-bit `LEV` operand selects the privilege level of the syscall:
|
||||
- `LEV = 0` — supervisor (kernel) syscall. Standard application → kernel transition; targets the `0xC00` system-call vector.
|
||||
- `LEV = 1` — reserved.
|
||||
- `LEV = 2` — **hypervisor syscall** (`HVcall`). On the Xenon, `sc 2` traps to the Xbox 360 hypervisor; this is how the kernel itself talks to the supervisor below it (e.g., for security operations, encrypted-memory accesses, page table updates).
|
||||
- **`sc` as written by titles.** Almost all guest game code uses `LEV = 0` to call `XboxKrnl.exe`. Game disassembly will show large jump tables of small thunks each ending in `li r0, syscall_no; sc; blr`.
|
||||
- **No condition or status side effects.** `sc` updates *no* general-purpose register on entry — neither LR nor CR. The kernel sees the GPR/FPR snapshot as-is and reads the syscall number out of `r0` (Xbox 360 ABI convention, not architectural).
|
||||
- **Return path.** Hardware returns from `sc` via [`rfid`](../control/mtmsrd.md)-class instructions in the kernel handler; from the application's perspective execution resumes at `CIA + 4`. Xenia's interpreter realises this by simply pre-incrementing `pc` then returning `StepResult::SystemCall` — the host driver dispatches the syscall and re-enters the loop.
|
||||
- **xenia divergence vs hardware.** xenia-rs *does not* model the `0xC00` exception vector or save SRR0/SRR1; the `LEV` operand is currently ignored. All `sc` instructions are treated identically and serviced by the host. This is sufficient because Xbox 360 titles don't observe SRR registers and the host kernel is implemented natively.
|
||||
- **Synchronisation.** Marked `sync` in xenia's XML — `sc` is context-synchronising (hardware completes all prior instructions before raising the exception). JITs must flush pending state before emitting the host call.
|
||||
- **Reserved bits.** Bit 30 is fixed `1`; bits 6–19 and 27–29 are reserved (must be 0). The 1-bit field at position 30 distinguishes the `sc` encoding from `scv` (later PowerISA addition, not present on the Xenon).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`bx`](bx.md), [`bcx`](bcx.md), [`bclrx`](bclrx.md), [`bcctrx`](bcctrx.md) — ordinary control flow alternatives.
|
||||
- [`tw`](tw.md), [`twi`](twi.md), [`td`](td.md), [`tdi`](tdi.md) — synchronous *trap* exceptions; another way to enter the kernel.
|
||||
- [`mtmsr`](../control/mtmsr.md), [`mtmsrd`](../control/mtmsrd.md) — machine-state changes used by the kernel's `sc` handler on return (`rfid`/`hrfid` chain not separately documented in this manual).
|
||||
- [`isync`](../control/mtmsr.md) — context-synchronising sibling; `sc` itself implies an isync-like fence.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `sc` (System Call)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-sc-system-call-instruction)
|
||||
- PowerISA v2.07B, Book III §7 — System Linkage interrupt definitions and `LEV` field semantics.
|
||||
184
migration/project-root/ppc-manual/branch/td.md
Normal file
184
migration/project-root/ppc-manual/branch/td.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# `td` — Trap Doubleword
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000088`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `td` | `td` | — | Trap Doubleword |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
td [TO], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `td` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000088`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `68`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `TO` | td: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
|
||||
| `RA` | td: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | td: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `td`
|
||||
|
||||
- **Reads (always):** `TO`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`td`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="td"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:552`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L552)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:769`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L769)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
|
||||
// PPCBUG-063: save CIA before incrementing so a trap handler reads
|
||||
// the faulting instruction address, not CIA+4.
|
||||
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
|
||||
// typed-trap convention used by the CRT/kernel for C++ exception
|
||||
// class dispatch). The audit notes this is relevant to the Sylpheed
|
||||
// throw investigation; routing the type code via a payload requires
|
||||
// a StepResult enum extension that's deferred for now.
|
||||
let trap_pc = ctx.pc;
|
||||
let a = ctx.gpr[instr.ra()];
|
||||
let b = match instr.opcode {
|
||||
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
|
||||
_ => ctx.gpr[instr.rb()],
|
||||
};
|
||||
let width = match instr.opcode {
|
||||
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
|
||||
_ => trap::TrapWidth::Doubleword,
|
||||
};
|
||||
let fired = trap::evaluate(instr.to(), a, b, width);
|
||||
if fired {
|
||||
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
|
||||
&& instr.to() == 31 && instr.ra() == 0 {
|
||||
Some(instr.simm16() as u16)
|
||||
} else { None };
|
||||
tracing::warn!(
|
||||
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
|
||||
trap_pc, instr.opcode, instr.to(), a, b,
|
||||
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
|
||||
);
|
||||
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
|
||||
// can read the faulting instruction address from ctx.pc.
|
||||
return StepResult::Trap;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **`TO` mask encoding (5 bits, MSB-first).** Each bit selects one comparison; the trap fires if *any* selected condition is true. Both operands are treated as 64-bit doublewords:
|
||||
|
||||
| Bit | Mnemonic | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `TO[0]` (16) | LT | `(int64) RA < (int64) RB` |
|
||||
| `TO[1]` (8) | GT | `(int64) RA > (int64) RB` |
|
||||
| `TO[2]` (4) | EQ | `RA == RB` |
|
||||
| `TO[3]` (2) | LGT | `(uint64) RA < (uint64) RB` (logical less) |
|
||||
| `TO[4]` (1) | LLT | `(uint64) RA > (uint64) RB` (logical greater — historical naming) |
|
||||
|
||||
- **`TO = 31` is unconditional trap.** All five bits set ⇒ the trap fires regardless of operand values; the simplified mnemonic `trap` is `tw 31, 0, 0` for words and **`td 31, 0, 0`** for doublewords. The PowerISA also uses `tdi 31, 0, 0` (or `twi`) as a debugger break.
|
||||
- **64-bit comparison.** Unlike [`tw`](tw.md), `td` always compares the full 64-bit GPRs. On the Xenon (64-bit) this is meaningful; PPC32 implementations don't have `td`.
|
||||
- **No register effects.** Only the side effect is the trap. No CR/LR/CTR/XER updates.
|
||||
- **Hardware behaviour.** When the trap fires, hardware raises a Program interrupt with `SRR1[TRAP]` set and vectors to `0x700`. The Xbox 360 hypervisor / kernel handles it (assertion failure, debugger trap, etc.).
|
||||
- **xenia simplification.** xenia-rs collapses all four trap variants (`td`, `tdi`, `tw`, `twi`) into one match arm that *unconditionally* logs and returns `StepResult::Trap` — it does **not** evaluate `TO` against the operands. This is a material divergence from the spec: in xenia every trap fires even if the condition is false. Real Xenon code rarely uses non-trivial `TO` masks (typical use is the unconditional `trap` for `__assert` / debugger break), so the divergence is normally invisible.
|
||||
- **Distinguishing assert vs. break.** Compilers commonly emit `tdne r3, r3` (impossible) or `tdi 0, r0, 0` patterns that *cannot* trap as inert markers. Xenia's blanket trap would mis-fire on these — a small known bug; track it if you see spurious traps.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`tdi`](tdi.md) — same condition test against a 16-bit signed immediate (D-form).
|
||||
- [`tw`](tw.md) / [`twi`](twi.md) — 32-bit (word) variants for comparing low halves of GPRs.
|
||||
- [`sc`](sc.md) — synchronous kernel entry via system-call exception (different vector, different intent).
|
||||
- [`mtmsr`](../control/mtmsr.md) — kernel returns from the trap handler via `rfid`-family instructions.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `td RA, RB` (=`tdu`) | `td 31, RA, RB` | unconditional trap |
|
||||
| `tdeq RA, RB` | `td 4, RA, RB` | `RA == RB` |
|
||||
| `tdne RA, RB` | `td 24, RA, RB` | `RA != RB` |
|
||||
| `tdlt RA, RB` | `td 16, RA, RB` | signed less than |
|
||||
| `tdle RA, RB` | `td 20, RA, RB` | signed less or equal |
|
||||
| `tdgt RA, RB` | `td 8, RA, RB` | signed greater than |
|
||||
| `tdge RA, RB` | `td 12, RA, RB` | signed greater or equal |
|
||||
| `tdllt RA, RB` | `td 2, RA, RB` | unsigned less than |
|
||||
| `tdlge RA, RB` | `td 5, RA, RB` | unsigned greater or equal |
|
||||
| `tdlgt RA, RB` | `td 1, RA, RB` | unsigned greater than |
|
||||
| `tdlle RA, RB` | `td 6, RA, RB` | unsigned less or equal |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `td` (Trap Doubleword)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-td-trap-doubleword-instruction)
|
||||
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
|
||||
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions (`TO` semantics).
|
||||
173
migration/project-root/ppc-manual/branch/tdi.md
Normal file
173
migration/project-root/ppc-manual/branch/tdi.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# `tdi` — Trap Doubleword Immediate
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x08000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `tdi` | `tdi` | — | Trap Doubleword Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
tdi [TO], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `tdi` — form `D`
|
||||
|
||||
- **Opcode word:** `0x08000000`
|
||||
- **Primary opcode (bits 0–5):** `2`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `TO` | tdi: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
|
||||
| `RA` | tdi: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | tdi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `tdi`
|
||||
|
||||
- **Reads (always):** `TO`, `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`tdi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tdi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:568`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L568)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:327`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L327)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
|
||||
// PPCBUG-063: save CIA before incrementing so a trap handler reads
|
||||
// the faulting instruction address, not CIA+4.
|
||||
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
|
||||
// typed-trap convention used by the CRT/kernel for C++ exception
|
||||
// class dispatch). The audit notes this is relevant to the Sylpheed
|
||||
// throw investigation; routing the type code via a payload requires
|
||||
// a StepResult enum extension that's deferred for now.
|
||||
let trap_pc = ctx.pc;
|
||||
let a = ctx.gpr[instr.ra()];
|
||||
let b = match instr.opcode {
|
||||
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
|
||||
_ => ctx.gpr[instr.rb()],
|
||||
};
|
||||
let width = match instr.opcode {
|
||||
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
|
||||
_ => trap::TrapWidth::Doubleword,
|
||||
};
|
||||
let fired = trap::evaluate(instr.to(), a, b, width);
|
||||
if fired {
|
||||
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
|
||||
&& instr.to() == 31 && instr.ra() == 0 {
|
||||
Some(instr.simm16() as u16)
|
||||
} else { None };
|
||||
tracing::warn!(
|
||||
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
|
||||
trap_pc, instr.opcode, instr.to(), a, b,
|
||||
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
|
||||
);
|
||||
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
|
||||
// can read the faulting instruction address from ctx.pc.
|
||||
return StepResult::Trap;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Immediate is sign-extended.** `SIMM` is treated as a 16-bit signed value, then sign-extended to 64 bits before comparison. To trap against a small unsigned constant, the same encoding works because both signed and unsigned interpretations agree for `SIMM ∈ [0, 0x7FFF]`.
|
||||
- **`TO` mask.** Identical bit layout to [`td`](td.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true.
|
||||
- **`TO = 31` is unconditional.** `tdi 31, 0, 0` is a debugger / assert trap. Compilers sometimes use it as a "should not reach" marker.
|
||||
- **64-bit comparison only.** Unlike [`twi`](twi.md), `tdi` always compares the full 64-bit GPR — it has no PPC32 analogue. The Xenon's PPC64 mode makes this meaningful.
|
||||
- **No register effects.** Pure side effect on success: Program interrupt → vector `0x700` with `SRR1[TRAP]=1`.
|
||||
- **xenia simplification.** xenia-rs unconditionally treats `tdi` as a fired trap, regardless of `TO`/`RA`/`SIMM` values. This diverges from the spec — real hardware would silently fall through when no `TO` bit's condition holds. Most title code uses only the unconditional `trap` form, so the divergence is normally invisible; non-firing assertion patterns (e.g. `tdi 0, r0, 0`) will mis-fire under xenia.
|
||||
- **Reserved bits.** Bits 6–10 carry the `TO` field; there is no `Rc` / `OE` on D-form trap immediates.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`td`](td.md) — register-register doubleword trap (X-form).
|
||||
- [`twi`](twi.md) / [`tw`](tw.md) — 32-bit-comparison siblings.
|
||||
- [`sc`](sc.md) — kernel-entry counterpart via system call exception.
|
||||
- [`mtmsrd`](mtmsrd.md) (control category) — kernel `rfid`-style return path after handling.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `tdi RA, value` | `tdi 31, RA, value` | unconditional trap |
|
||||
| `tdeqi RA, value` | `tdi 4, RA, value` | `RA == EXTS(value)` |
|
||||
| `tdnei RA, value` | `tdi 24, RA, value` | `RA != EXTS(value)` |
|
||||
| `tdlti RA, value` | `tdi 16, RA, value` | signed less than |
|
||||
| `tdlei RA, value` | `tdi 20, RA, value` | signed less or equal |
|
||||
| `tdgti RA, value` | `tdi 8, RA, value` | signed greater than |
|
||||
| `tdgei RA, value` | `tdi 12, RA, value` | signed greater or equal |
|
||||
| `tdllti RA, value` | `tdi 2, RA, value` | unsigned less than |
|
||||
| `tdlgei RA, value` | `tdi 5, RA, value` | unsigned greater or equal |
|
||||
| `tdlgti RA, value` | `tdi 1, RA, value` | unsigned greater than |
|
||||
| `tdllei RA, value` | `tdi 6, RA, value` | unsigned less or equal |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `tdi` (Trap Doubleword Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tdi-trap-doubleword-immediate-instruction)
|
||||
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
|
||||
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.
|
||||
184
migration/project-root/ppc-manual/branch/tw.md
Normal file
184
migration/project-root/ppc-manual/branch/tw.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# `tw` — Trap Word
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000008`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `tw` | `tw` | — | Trap Word |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
tw [TO], [RA], [RB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `tw` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000008`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `4`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `TO` | tw: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
|
||||
| `RA` | tw: read | Source GPR (`r0`–`r31`). |
|
||||
| `RB` | tw: read | Source GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `tw`
|
||||
|
||||
- **Reads (always):** `TO`, `RA`, `RB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`tw`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tw"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:583`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L583)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:750`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L750)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
|
||||
// PPCBUG-063: save CIA before incrementing so a trap handler reads
|
||||
// the faulting instruction address, not CIA+4.
|
||||
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
|
||||
// typed-trap convention used by the CRT/kernel for C++ exception
|
||||
// class dispatch). The audit notes this is relevant to the Sylpheed
|
||||
// throw investigation; routing the type code via a payload requires
|
||||
// a StepResult enum extension that's deferred for now.
|
||||
let trap_pc = ctx.pc;
|
||||
let a = ctx.gpr[instr.ra()];
|
||||
let b = match instr.opcode {
|
||||
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
|
||||
_ => ctx.gpr[instr.rb()],
|
||||
};
|
||||
let width = match instr.opcode {
|
||||
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
|
||||
_ => trap::TrapWidth::Doubleword,
|
||||
};
|
||||
let fired = trap::evaluate(instr.to(), a, b, width);
|
||||
if fired {
|
||||
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
|
||||
&& instr.to() == 31 && instr.ra() == 0 {
|
||||
Some(instr.simm16() as u16)
|
||||
} else { None };
|
||||
tracing::warn!(
|
||||
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
|
||||
trap_pc, instr.opcode, instr.to(), a, b,
|
||||
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
|
||||
);
|
||||
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
|
||||
// can read the faulting instruction address from ctx.pc.
|
||||
return StepResult::Trap;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit comparison only.** `tw` compares the *low 32 bits* of `RA` and `RB`. The high halves of the 64-bit GPRs on the Xenon are ignored. Use [`td`](td.md) for full 64-bit comparisons.
|
||||
- **`TO` mask (5 bits, MSB-first).** Same encoding as the doubleword form:
|
||||
|
||||
| Bit | Mnemonic | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `TO[0]` (16) | LT | `(int32) RA < (int32) RB` |
|
||||
| `TO[1]` (8) | GT | `(int32) RA > (int32) RB` |
|
||||
| `TO[2]` (4) | EQ | `(uint32) RA == (uint32) RB` |
|
||||
| `TO[3]` (2) | LGT | `(uint32) RA < (uint32) RB` |
|
||||
| `TO[4]` (1) | LLT | `(uint32) RA > (uint32) RB` |
|
||||
|
||||
- **`tw 31, 0, 0` is `trap`.** The simplified mnemonic `trap` expands to `tw 31, r0, r0` — all five `TO` bits set ⇒ unconditional trap. Compilers and the kernel use this as the assertion / debugger break primitive; it appears as `0x7FE00008` in raw bytes.
|
||||
- **Conditional asserts.** GCC's `__builtin_trap` and MSVC's `__assert` macros emit `tw` variants like `twge`/`twlt` to fault on bound-check failures.
|
||||
- **No register effects.** Side effect only: Program interrupt (`0x700`) with `SRR1[TRAP]=1`.
|
||||
- **xenia simplification.** xenia-rs collapses `td/tdi/tw/twi` into a single arm that *unconditionally* logs and returns `StepResult::Trap` — the `TO` operand is **not evaluated**. Real hardware would silently fall through when no `TO` bit's condition holds. In practice titles use mostly the unconditional `trap`, so the divergence rarely manifests, but inert-marker patterns like `tw 0, r0, r0` will fire under xenia.
|
||||
- **Inert encoding.** `tw 0, r0, r0` (no `TO` bits set) can never trap on real hardware. It encodes as `0x7C000008` — sometimes used as a structured-NOP marker. Watch for it in xenia traces.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`twi`](twi.md) — same 32-bit comparison against a 16-bit signed immediate.
|
||||
- [`td`](td.md) / [`tdi`](tdi.md) — 64-bit (doubleword) variants.
|
||||
- [`sc`](sc.md) — kernel entry via system-call exception (different vector).
|
||||
- [`mtmsr`](../control/mtmsr.md) — kernel returns from `0x700` via `rfid`.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `trap` | `tw 31, 0, 0` | unconditional |
|
||||
| `tweq RA, RB` | `tw 4, RA, RB` | `RA == RB` |
|
||||
| `twne RA, RB` | `tw 24, RA, RB` | `RA != RB` |
|
||||
| `twlt RA, RB` | `tw 16, RA, RB` | signed less than |
|
||||
| `twle RA, RB` | `tw 20, RA, RB` | signed less or equal |
|
||||
| `twgt RA, RB` | `tw 8, RA, RB` | signed greater than |
|
||||
| `twge RA, RB` | `tw 12, RA, RB` | signed greater or equal |
|
||||
| `twllt RA, RB` | `tw 2, RA, RB` | unsigned less than |
|
||||
| `twlge RA, RB` | `tw 5, RA, RB` | unsigned greater or equal |
|
||||
| `twlgt RA, RB` | `tw 1, RA, RB` | unsigned greater than |
|
||||
| `twlle RA, RB` | `tw 6, RA, RB` | unsigned less or equal |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `tw` (Trap Word)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-tw-trap-word-instruction)
|
||||
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
|
||||
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.
|
||||
172
migration/project-root/ppc-manual/branch/twi.md
Normal file
172
migration/project-root/ppc-manual/branch/twi.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# `twi` — Trap Word Immediate
|
||||
|
||||
> **Category:** [Branch & System](../categories/branch.md) · **Form:** [D](../forms/D.md) · **Opcode:** `0x0c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `twi` | `twi` | — | Trap Word Immediate |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
tw [TO], [RA], [SIMM]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `twi` — form `D`
|
||||
|
||||
- **Opcode word:** `0x0c000000`
|
||||
- **Primary opcode (bits 0–5):** `3`
|
||||
- **Extended opcode:** —
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT` | destination GPR (or RS when storing) |
|
||||
| 11–15 | `RA` | source GPR (0 ⇒ literal 0 for RA0 forms) |
|
||||
| 16–31 | `D/SI/UI` | 16-bit signed or unsigned immediate |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `TO` | twi: read | Trap-on condition mask (5 bits) — LT, GT, EQ, LGT, LLT bits. |
|
||||
| `RA` | twi: read | Source GPR (`r0`–`r31`). |
|
||||
| `SIMM` | twi: read | 16-bit signed immediate. Sign-extended to 64 bits before use. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `twi`
|
||||
|
||||
- **Reads (always):** `TO`, `RA`, `SIMM`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** _none_
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`twi`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="twi"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:601`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L601)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:87`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L87)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:328`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L328)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1762-1796`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1762-L1796)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::tw | PpcOpcode::twi | PpcOpcode::td | PpcOpcode::tdi => {
|
||||
// PPCBUG-063: save CIA before incrementing so a trap handler reads
|
||||
// the faulting instruction address, not CIA+4.
|
||||
// PPCBUG-065: log the SIMM type code on `twi 31, r0, IMM` (Xbox 360
|
||||
// typed-trap convention used by the CRT/kernel for C++ exception
|
||||
// class dispatch). The audit notes this is relevant to the Sylpheed
|
||||
// throw investigation; routing the type code via a payload requires
|
||||
// a StepResult enum extension that's deferred for now.
|
||||
let trap_pc = ctx.pc;
|
||||
let a = ctx.gpr[instr.ra()];
|
||||
let b = match instr.opcode {
|
||||
PpcOpcode::twi | PpcOpcode::tdi => instr.simm16() as i64 as u64,
|
||||
_ => ctx.gpr[instr.rb()],
|
||||
};
|
||||
let width = match instr.opcode {
|
||||
PpcOpcode::tw | PpcOpcode::twi => trap::TrapWidth::Word,
|
||||
_ => trap::TrapWidth::Doubleword,
|
||||
};
|
||||
let fired = trap::evaluate(instr.to(), a, b, width);
|
||||
if fired {
|
||||
let typed_trap_simm = if matches!(instr.opcode, PpcOpcode::twi)
|
||||
&& instr.to() == 31 && instr.ra() == 0 {
|
||||
Some(instr.simm16() as u16)
|
||||
} else { None };
|
||||
tracing::warn!(
|
||||
"Trap fired at {:#010x}: {:?} TO={} a={:#x} b={:#x}{}",
|
||||
trap_pc, instr.opcode, instr.to(), a, b,
|
||||
typed_trap_simm.map_or(String::new(), |t| format!(" typed_trap_simm={:#06x}", t))
|
||||
);
|
||||
// Leave ctx.pc at CIA (NOT NIA) so trap handlers / SEH delivery
|
||||
// can read the faulting instruction address from ctx.pc.
|
||||
return StepResult::Trap;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **32-bit comparison against sign-extended immediate.** `SIMM` is a 16-bit signed value, sign-extended to 32 bits, then compared against the low 32 bits of `RA`. The high half of `RA` is *ignored*.
|
||||
- **`TO` mask.** Identical to [`tw`](tw.md): bit 0 = signed LT, 1 = signed GT, 2 = EQ, 3 = unsigned LT (LGT), 4 = unsigned GT (LLT). Trap fires if any selected bit's condition is true.
|
||||
- **`twi 31, 0, 0` is unconditional trap.** All `TO` bits set ⇒ guaranteed trap. The simplified mnemonic family (`twnei`, `twgei`, …) is much more common in real code: bound checks, null checks, integer-divide-by-zero pre-checks.
|
||||
- **Compiler usage.** Xbox 360 GCC emits `twnei rN, -1` and similar to validate handle-style return values; the kernel handler turns the trap into an exception delivered to the title.
|
||||
- **No register effects.** Side effect: Program interrupt → vector `0x700` with `SRR1[TRAP]=1`.
|
||||
- **xenia simplification.** Same as the other three trap forms — xenia-rs unconditionally returns `StepResult::Trap` whenever it decodes any of `tdi`/`twi`/`td`/`tw`, regardless of the `TO` mask or operands. This means `twi 0, r0, 0` (architecturally a guaranteed-no-trap encoding) will spuriously fire under xenia. Keep this in mind when triaging unexpected trap signals.
|
||||
- **No `Rc` / `OE`.** D-form trap immediates have neither.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`tw`](tw.md) — register-register 32-bit trap (X-form).
|
||||
- [`tdi`](tdi.md) / [`td`](td.md) — 64-bit (doubleword) siblings.
|
||||
- [`sc`](sc.md) — alternative synchronous kernel entry.
|
||||
- [`cmpi`](../alu/cmpi.md), [`cmpli`](../alu/cmpli.md) — set CR for a subsequent [`bcx`](bcx.md) when you want a regular branch instead of a trap.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Triggered when |
|
||||
| --- | --- | --- |
|
||||
| `tweqi RA, value` | `twi 4, RA, value` | `RA == EXTS(value)` |
|
||||
| `twnei RA, value` | `twi 24, RA, value` | `RA != EXTS(value)` |
|
||||
| `twlti RA, value` | `twi 16, RA, value` | signed less than |
|
||||
| `twlei RA, value` | `twi 20, RA, value` | signed less or equal |
|
||||
| `twgti RA, value` | `twi 8, RA, value` | signed greater than |
|
||||
| `twgei RA, value` | `twi 12, RA, value` | signed greater or equal |
|
||||
| `twllti RA, value` | `twi 2, RA, value` | unsigned less than |
|
||||
| `twlgei RA, value` | `twi 5, RA, value` | unsigned greater or equal |
|
||||
| `twlgti RA, value` | `twi 1, RA, value` | unsigned greater than |
|
||||
| `twllei RA, value` | `twi 6, RA, value` | unsigned less or equal |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `twi` (Trap Word Immediate)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-twi-trap-word-immediate-instruction)
|
||||
- [AIX 7.3 — Trap simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-trap-simplified)
|
||||
- PowerISA v2.07B, Book I §3.3.11 — fixed-point trap instructions.
|
||||
82
migration/project-root/ppc-manual/categories/alu.md
Normal file
82
migration/project-root/ppc-manual/categories/alu.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Integer ALU
|
||||
|
||||
Fixed-point add/sub/multiply/divide, logical, rotate, shift, compare, count-leading-zeros, sign-extension, trap-on-condition.
|
||||
|
||||
**70 families** · **70 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`addcx`](addcx.md) | `XO` | Add Carrying | `addcx` |
|
||||
| [`addex`](addex.md) | `XO` | Add Extended | `addex` |
|
||||
| [`addi`](addi.md) | `D` | Add Immediate | `addi` |
|
||||
| [`addic`](addic.md) | `D` | Add Immediate Carrying | `addic` |
|
||||
| [`addic.`](addicx.md) | `D` | Add Immediate Carrying and Record | `addic.` |
|
||||
| [`addis`](addis.md) | `D` | Add Immediate Shifted | `addis` |
|
||||
| [`addmex`](addmex.md) | `XO` | Add to Minus One Extended | `addmex` |
|
||||
| [`addx`](addx.md) | `XO` | Add | `addx` |
|
||||
| [`addzex`](addzex.md) | `XO` | Add to Zero Extended | `addzex` |
|
||||
| [`andcx`](andcx.md) | `X` | AND with Complement | `andcx` |
|
||||
| [`andi.`](andix.md) | `D` | AND Immediate | `andi.` |
|
||||
| [`andis.`](andisx.md) | `D` | AND Immediate Shifted | `andis.` |
|
||||
| [`andx`](andx.md) | `X` | AND | `andx` |
|
||||
| [`cmp`](cmp.md) | `X` | Compare | `cmp` |
|
||||
| [`cmpi`](cmpi.md) | `D` | Compare Immediate | `cmpi` |
|
||||
| [`cmpl`](cmpl.md) | `X` | Compare Logical | `cmpl` |
|
||||
| [`cmpli`](cmpli.md) | `D` | Compare Logical Immediate | `cmpli` |
|
||||
| [`cntlzdx`](cntlzdx.md) | `X` | Count Leading Zeros Doubleword | `cntlzdx` |
|
||||
| [`cntlzwx`](cntlzwx.md) | `X` | Count Leading Zeros Word | `cntlzwx` |
|
||||
| [`divdux`](divdux.md) | `XO` | Divide Doubleword Unsigned | `divdux` |
|
||||
| [`divdx`](divdx.md) | `XO` | Divide Doubleword | `divdx` |
|
||||
| [`divwux`](divwux.md) | `XO` | Divide Word Unsigned | `divwux` |
|
||||
| [`divwx`](divwx.md) | `XO` | Divide Word | `divwx` |
|
||||
| [`eieio`](eieio.md) | `X` | Enforce In-Order Execution of I/O | `eieio` |
|
||||
| [`eqvx`](eqvx.md) | `X` | Equivalent | `eqvx` |
|
||||
| [`extsbx`](extsbx.md) | `X` | Extend Sign Byte | `extsbx` |
|
||||
| [`extshx`](extshx.md) | `X` | Extend Sign Half Word | `extshx` |
|
||||
| [`extswx`](extswx.md) | `X` | Extend Sign Word | `extswx` |
|
||||
| [`isync`](isync.md) | `XL` | Instruction Synchronize | `isync` |
|
||||
| [`mulhdux`](mulhdux.md) | `XO` | Multiply High Doubleword Unsigned | `mulhdux` |
|
||||
| [`mulhdx`](mulhdx.md) | `XO` | Multiply High Doubleword | `mulhdx` |
|
||||
| [`mulhwux`](mulhwux.md) | `XO` | Multiply High Word Unsigned | `mulhwux` |
|
||||
| [`mulhwx`](mulhwx.md) | `XO` | Multiply High Word | `mulhwx` |
|
||||
| [`mulldx`](mulldx.md) | `XO` | Multiply Low Doubleword | `mulldx` |
|
||||
| [`mulli`](mulli.md) | `D` | Multiply Low Immediate | `mulli` |
|
||||
| [`mullwx`](mullwx.md) | `XO` | Multiply Low Word | `mullwx` |
|
||||
| [`nandx`](nandx.md) | `X` | NAND | `nandx` |
|
||||
| [`negx`](negx.md) | `XO` | Negate | `negx` |
|
||||
| [`norx`](norx.md) | `X` | NOR | `norx` |
|
||||
| [`orcx`](orcx.md) | `X` | OR with Complement | `orcx` |
|
||||
| [`ori`](ori.md) | `D` | OR Immediate | `ori` |
|
||||
| [`oris`](oris.md) | `D` | OR Immediate Shifted | `oris` |
|
||||
| [`orx`](orx.md) | `X` | OR | `orx` |
|
||||
| [`rldclx`](rldclx.md) | `MDS` | Rotate Left Doubleword then Clear Left | `rldclx` |
|
||||
| [`rldcrx`](rldcrx.md) | `MDS` | Rotate Left Doubleword then Clear Right | `rldcrx` |
|
||||
| [`rldiclx`](rldiclx.md) | `MD` | Rotate Left Doubleword Immediate then Clear Left | `rldiclx` |
|
||||
| [`rldicrx`](rldicrx.md) | `MD` | Rotate Left Doubleword Immediate then Clear Right | `rldicrx` |
|
||||
| [`rldicx`](rldicx.md) | `MD` | Rotate Left Doubleword Immediate then Clear | `rldicx` |
|
||||
| [`rldimix`](rldimix.md) | `MD` | Rotate Left Doubleword Immediate then Mask Insert | `rldimix` |
|
||||
| [`rlwimix`](rlwimix.md) | `M` | Rotate Left Word Immediate then Mask Insert | `rlwimix` |
|
||||
| [`rlwinmx`](rlwinmx.md) | `M` | Rotate Left Word Immediate then AND with Mask | `rlwinmx` |
|
||||
| [`rlwnmx`](rlwnmx.md) | `M` | Rotate Left Word then AND with Mask | `rlwnmx` |
|
||||
| [`sldx`](sldx.md) | `X` | Shift Left Doubleword | `sldx` |
|
||||
| [`slwx`](slwx.md) | `X` | Shift Left Word | `slwx` |
|
||||
| [`sradix`](sradix.md) | `XS` | Shift Right Algebraic Doubleword Immediate | `sradix` |
|
||||
| [`sradx`](sradx.md) | `X` | Shift Right Algebraic Doubleword | `sradx` |
|
||||
| [`srawix`](srawix.md) | `X` | Shift Right Algebraic Word Immediate | `srawix` |
|
||||
| [`srawx`](srawx.md) | `X` | Shift Right Algebraic Word | `srawx` |
|
||||
| [`srdx`](srdx.md) | `X` | Shift Right Doubleword | `srdx` |
|
||||
| [`srwx`](srwx.md) | `X` | Shift Right Word | `srwx` |
|
||||
| [`subfcx`](subfcx.md) | `XO` | Subtract From Carrying | `subfcx` |
|
||||
| [`subfex`](subfex.md) | `XO` | Subtract From Extended | `subfex` |
|
||||
| [`subficx`](subficx.md) | `D` | Subtract From Immediate Carrying | `subficx` |
|
||||
| [`subfmex`](subfmex.md) | `XO` | Subtract From Minus One Extended | `subfmex` |
|
||||
| [`subfx`](subfx.md) | `XO` | Subtract From | `subfx` |
|
||||
| [`subfzex`](subfzex.md) | `XO` | Subtract From Zero Extended | `subfzex` |
|
||||
| [`sync`](sync.md) | `X` | Synchronize | `sync` |
|
||||
| [`xori`](xori.md) | `D` | XOR Immediate | `xori` |
|
||||
| [`xoris`](xoris.md) | `D` | XOR Immediate Shifted | `xoris` |
|
||||
| [`xorx`](xorx.md) | `X` | XOR | `xorx` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
21
migration/project-root/ppc-manual/categories/branch.md
Normal file
21
migration/project-root/ppc-manual/categories/branch.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Branch & System
|
||||
|
||||
Unconditional / conditional branches, branch to LR/CTR, traps, system call.
|
||||
|
||||
**9 families** · **9 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`bcctrx`](bcctrx.md) | `XL` | Branch Conditional to Count Register | `bcctrx` |
|
||||
| [`bclrx`](bclrx.md) | `XL` | Branch Conditional to Link Register | `bclrx` |
|
||||
| [`bcx`](bcx.md) | `B` | Branch Conditional | `bcx` |
|
||||
| [`bx`](bx.md) | `I` | Branch | `bx` |
|
||||
| [`sc`](sc.md) | `SC` | System Call | `sc` |
|
||||
| [`td`](td.md) | `X` | Trap Doubleword | `td` |
|
||||
| [`tdi`](tdi.md) | `D` | Trap Doubleword Immediate | `tdi` |
|
||||
| [`tw`](tw.md) | `X` | Trap Word | `tw` |
|
||||
| [`twi`](twi.md) | `D` | Trap Word Immediate | `twi` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
38
migration/project-root/ppc-manual/categories/control.md
Normal file
38
migration/project-root/ppc-manual/categories/control.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Control / CR / SPR
|
||||
|
||||
Condition-register logical ops, CR field moves, mfspr/mtspr/mtcrf, time-base reads, synchronisation (sync, isync, eieio).
|
||||
|
||||
**26 families** · **26 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`crand`](crand.md) | `XL` | Condition Register AND | `crand` |
|
||||
| [`crandc`](crandc.md) | `XL` | Condition Register AND with Complement | `crandc` |
|
||||
| [`creqv`](creqv.md) | `XL` | Condition Register Equivalent | `creqv` |
|
||||
| [`crnand`](crnand.md) | `XL` | Condition Register NAND | `crnand` |
|
||||
| [`crnor`](crnor.md) | `XL` | Condition Register NOR | `crnor` |
|
||||
| [`cror`](cror.md) | `XL` | Condition Register OR | `cror` |
|
||||
| [`crorc`](crorc.md) | `XL` | Condition Register OR with Complement | `crorc` |
|
||||
| [`crxor`](crxor.md) | `XL` | Condition Register XOR | `crxor` |
|
||||
| [`mcrf`](mcrf.md) | `XL` | Move Condition Register Field | `mcrf` |
|
||||
| [`mcrfs`](mcrfs.md) | `X` | Move to Condition Register from FPSCR | `mcrfs` |
|
||||
| [`mcrxr`](mcrxr.md) | `X` | Move to Condition Register from XER | `mcrxr` |
|
||||
| [`mfcr`](mfcr.md) | `X` | Move from Condition Register | `mfcr` |
|
||||
| [`mffsx`](mffsx.md) | `X` | Move from FPSCR | `mffsx` |
|
||||
| [`mfmsr`](mfmsr.md) | `X` | Move from Machine State Register | `mfmsr` |
|
||||
| [`mfspr`](mfspr.md) | `XFX` | Move from Special-Purpose Register | `mfspr` |
|
||||
| [`mftb`](mftb.md) | `XFX` | Move from Time Base | `mftb` |
|
||||
| [`mfvscr`](mfvscr.md) | `VX` | Move from VSCR | `mfvscr` |
|
||||
| [`mtcrf`](mtcrf.md) | `XFX` | Move to Condition Register Fields | `mtcrf` |
|
||||
| [`mtfsb0x`](mtfsb0x.md) | `X` | Move to FPSCR Bit 0 | `mtfsb0x` |
|
||||
| [`mtfsb1x`](mtfsb1x.md) | `X` | Move to FPSCR Bit 1 | `mtfsb1x` |
|
||||
| [`mtfsfix`](mtfsfix.md) | `X` | Move to FPSCR Field Immediate | `mtfsfix` |
|
||||
| [`mtfsfx`](mtfsfx.md) | `XFL` | Move to FPSCR Fields | `mtfsfx` |
|
||||
| [`mtmsr`](mtmsr.md) | `X` | Move to Machine State Register | `mtmsr` |
|
||||
| [`mtmsrd`](mtmsrd.md) | `X` | Move to Machine State Register Doubleword | `mtmsrd` |
|
||||
| [`mtspr`](mtspr.md) | `XFX` | Move to Special-Purpose Register | `mtspr` |
|
||||
| [`mtvscr`](mtvscr.md) | `VX` | Move to VSCR | `mtvscr` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
45
migration/project-root/ppc-manual/categories/fpu.md
Normal file
45
migration/project-root/ppc-manual/categories/fpu.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Floating-Point
|
||||
|
||||
IEEE-754 add/sub/mul/div/sqrt, fused multiply-add, conversions, compares, FPSCR moves.
|
||||
|
||||
**33 families** · **33 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`fabsx`](fabsx.md) | `X` | Floating Absolute Value | `fabsx` |
|
||||
| [`faddsx`](faddsx.md) | `A` | Floating Add Single | `faddsx` |
|
||||
| [`faddx`](faddx.md) | `A` | Floating Add | `faddx` |
|
||||
| [`fcfidx`](fcfidx.md) | `X` | Floating Convert From Integer Doubleword | `fcfidx` |
|
||||
| [`fcmpo`](fcmpo.md) | `X` | Floating Compare Ordered | `fcmpo` |
|
||||
| [`fcmpu`](fcmpu.md) | `X` | Floating Compare Unordered | `fcmpu` |
|
||||
| [`fctidx`](fctidx.md) | `X` | Floating Convert to Integer Doubleword | `fctidx` |
|
||||
| [`fctidzx`](fctidzx.md) | `X` | Floating Convert to Integer Doubleword with Round Toward Zero | `fctidzx` |
|
||||
| [`fctiwx`](fctiwx.md) | `X` | Floating Convert to Integer Word | `fctiwx` |
|
||||
| [`fctiwzx`](fctiwzx.md) | `X` | Floating Convert to Integer Word with Round Toward Zero | `fctiwzx` |
|
||||
| [`fdivsx`](fdivsx.md) | `A` | Floating Divide Single | `fdivsx` |
|
||||
| [`fdivx`](fdivx.md) | `A` | Floating Divide | `fdivx` |
|
||||
| [`fmaddsx`](fmaddsx.md) | `A` | Floating Multiply-Add Single | `fmaddsx` |
|
||||
| [`fmaddx`](fmaddx.md) | `A` | Floating Multiply-Add | `fmaddx` |
|
||||
| [`fmrx`](fmrx.md) | `X` | Floating Move Register | `fmrx` |
|
||||
| [`fmsubsx`](fmsubsx.md) | `A` | Floating Multiply-Subtract Single | `fmsubsx` |
|
||||
| [`fmsubx`](fmsubx.md) | `A` | Floating Multiply-Subtract | `fmsubx` |
|
||||
| [`fmulsx`](fmulsx.md) | `A` | Floating Multiply Single | `fmulsx` |
|
||||
| [`fmulx`](fmulx.md) | `A` | Floating Multiply | `fmulx` |
|
||||
| [`fnabsx`](fnabsx.md) | `X` | Floating Negative Absolute Value | `fnabsx` |
|
||||
| [`fnegx`](fnegx.md) | `X` | Floating Negate | `fnegx` |
|
||||
| [`fnmaddsx`](fnmaddsx.md) | `A` | Floating Negative Multiply-Add Single | `fnmaddsx` |
|
||||
| [`fnmaddx`](fnmaddx.md) | `A` | Floating Negative Multiply-Add | `fnmaddx` |
|
||||
| [`fnmsubsx`](fnmsubsx.md) | `A` | Floating Negative Multiply-Subtract Single | `fnmsubsx` |
|
||||
| [`fnmsubx`](fnmsubx.md) | `A` | Floating Negative Multiply-Subtract | `fnmsubx` |
|
||||
| [`fresx`](fresx.md) | `A` | Floating Reciprocal Estimate Single | `fresx` |
|
||||
| [`frspx`](frspx.md) | `X` | Floating Round to Single | `frspx` |
|
||||
| [`frsqrtex`](frsqrtex.md) | `A` | Floating Reciprocal Square Root Estimate | `frsqrtex` |
|
||||
| [`fselx`](fselx.md) | `A` | Floating Select | `fselx` |
|
||||
| [`fsqrtsx`](fsqrtsx.md) | `A` | Floating Square Root Single | `fsqrtsx` |
|
||||
| [`fsqrtx`](fsqrtx.md) | `A` | Floating Square Root | `fsqrtx` |
|
||||
| [`fsubsx`](fsubsx.md) | `A` | Floating Subtract Single | `fsubsx` |
|
||||
| [`fsubx`](fsubx.md) | `A` | Floating Subtract | `fsubx` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
68
migration/project-root/ppc-manual/categories/memory.md
Normal file
68
migration/project-root/ppc-manual/categories/memory.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Memory
|
||||
|
||||
Loads/stores for byte, half, word, doubleword, float, multiple and string; cache management (dcbt, dcbf, dcbz); reservation pair lwarx/stwcx.
|
||||
|
||||
**56 families** · **112 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`dcbf`](dcbf.md) | `X` | Data Cache Block Flush | `dcbf` |
|
||||
| [`dcbi`](dcbi.md) | `X` | Data Cache Block Invalidate | `dcbi` |
|
||||
| [`dcbst`](dcbst.md) | `X` | Data Cache Block Store | `dcbst` |
|
||||
| [`dcbt`](dcbt.md) | `X` | Data Cache Block Touch | `dcbt` |
|
||||
| [`dcbtst`](dcbtst.md) | `X` | Data Cache Block Touch for Store | `dcbtst` |
|
||||
| [`dcbz`](dcbz.md) | `DCBZ` | Data Cache Block Clear to Zero | `dcbz`, `dcbz128` |
|
||||
| [`icbi`](icbi.md) | `X` | Instruction Cache Block Invalidate | `icbi` |
|
||||
| [`lbz`](lbz.md) | `D` | Load Byte and Zero | `lbz`, `lbzu`, `lbzux`, `lbzx` |
|
||||
| [`ld`](ld.md) | `DS` | Load Doubleword | `ld`, `ldu`, `ldux`, `ldx` |
|
||||
| [`ldarx`](ldarx.md) | `X` | Load Doubleword and Reserve Indexed | `ldarx` |
|
||||
| [`ldbrx`](ldbrx.md) | `X` | Load Doubleword Byte-Reverse Indexed | `ldbrx` |
|
||||
| [`lfd`](lfd.md) | `D` | Load Floating-Point Double | `lfd`, `lfdu`, `lfdux`, `lfdx` |
|
||||
| [`lfs`](lfs.md) | `D` | Load Floating-Point Single | `lfs`, `lfsu`, `lfsux`, `lfsx` |
|
||||
| [`lha`](lha.md) | `D` | Load Half Word Algebraic | `lha`, `lhau`, `lhaux`, `lhax` |
|
||||
| [`lhbrx`](lhbrx.md) | `X` | Load Half Word Byte-Reverse Indexed | `lhbrx` |
|
||||
| [`lhz`](lhz.md) | `D` | Load Half Word and Zero | `lhz`, `lhzu`, `lhzux`, `lhzx` |
|
||||
| [`lmw`](lmw.md) | `D` | Load Multiple Word | `lmw` |
|
||||
| [`lswi`](lswi.md) | `X` | Load String Word Immediate | `lswi` |
|
||||
| [`lswx`](lswx.md) | `X` | Load String Word Indexed | `lswx` |
|
||||
| [`lvebx`](lvebx.md) | `X` | Load Vector Element Byte Indexed | `lvebx` |
|
||||
| [`lvehx`](lvehx.md) | `X` | Load Vector Element Half Word Indexed | `lvehx` |
|
||||
| [`lvewx`](lvewx.md) | `X` | Load Vector Element Word Indexed | `lvewx`, `lvewx128` |
|
||||
| [`lvlx`](lvlx.md) | `X` | Load Vector Left Indexed | `lvlx`, `lvlx128` |
|
||||
| [`lvlxl`](lvlxl.md) | `X` | Load Vector Left Indexed LRU | `lvlxl`, `lvlxl128` |
|
||||
| [`lvrx`](lvrx.md) | `X` | Load Vector Right Indexed | `lvrx`, `lvrx128` |
|
||||
| [`lvrxl`](lvrxl.md) | `X` | Load Vector Right Indexed LRU | `lvrxl`, `lvrxl128` |
|
||||
| [`lvx`](lvx.md) | `X` | Load Vector Indexed | `lvx`, `lvx128` |
|
||||
| [`lvxl`](lvxl.md) | `X` | Load Vector Indexed LRU | `lvxl`, `lvxl128` |
|
||||
| [`lwa`](lwa.md) | `DS` | Load Word Algebraic | `lwa`, `lwaux`, `lwax` |
|
||||
| [`lwarx`](lwarx.md) | `X` | Load Word and Reserve Indexed | `lwarx` |
|
||||
| [`lwbrx`](lwbrx.md) | `X` | Load Word Byte-Reverse Indexed | `lwbrx` |
|
||||
| [`lwz`](lwz.md) | `D` | Load Word and Zero | `lwz`, `lwzu`, `lwzux`, `lwzx` |
|
||||
| [`stb`](stb.md) | `D` | Store Byte | `stb`, `stbu`, `stbux`, `stbx` |
|
||||
| [`std`](std.md) | `DS` | Store Doubleword | `std`, `stdu`, `stdux`, `stdx` |
|
||||
| [`stdbrx`](stdbrx.md) | `X` | Store Doubleword Byte-Reverse Indexed | `stdbrx` |
|
||||
| [`stdcx`](stdcx.md) | `X` | Store Doubleword Conditional Indexed | `stdcx` |
|
||||
| [`stfd`](stfd.md) | `D` | Store Floating-Point Double | `stfd`, `stfdu`, `stfdux`, `stfdx` |
|
||||
| [`stfiwx`](stfiwx.md) | `X` | Store Floating-Point as Integer Word Indexed | `stfiwx` |
|
||||
| [`stfs`](stfs.md) | `D` | Store Floating-Point Single | `stfs`, `stfsu`, `stfsux`, `stfsx` |
|
||||
| [`sth`](sth.md) | `D` | Store Half Word | `sth`, `sthu`, `sthux`, `sthx` |
|
||||
| [`sthbrx`](sthbrx.md) | `X` | Store Half Word Byte-Reverse Indexed | `sthbrx` |
|
||||
| [`stmw`](stmw.md) | `D` | Store Multiple Word | `stmw` |
|
||||
| [`stswi`](stswi.md) | `X` | Store String Word Immediate | `stswi` |
|
||||
| [`stswx`](stswx.md) | `X` | Store String Word Indexed | `stswx` |
|
||||
| [`stvebx`](stvebx.md) | `X` | Store Vector Element Byte Indexed | `stvebx` |
|
||||
| [`stvehx`](stvehx.md) | `X` | Store Vector Element Half Word Indexed | `stvehx` |
|
||||
| [`stvewx`](stvewx.md) | `X` | Store Vector Element Word Indexed | `stvewx`, `stvewx128` |
|
||||
| [`stvlx`](stvlx.md) | `X` | Store Vector Left Indexed | `stvlx`, `stvlx128` |
|
||||
| [`stvlxl`](stvlxl.md) | `X` | Store Vector Left Indexed LRU | `stvlxl`, `stvlxl128` |
|
||||
| [`stvrx`](stvrx.md) | `X` | Store Vector Right Indexed | `stvrx`, `stvrx128` |
|
||||
| [`stvrxl`](stvrxl.md) | `X` | Store Vector Right Indexed LRU | `stvrxl`, `stvrxl128` |
|
||||
| [`stvx`](stvx.md) | `X` | Store Vector Indexed | `stvx`, `stvx128` |
|
||||
| [`stvxl`](stvxl.md) | `X` | Store Vector Indexed LRU | `stvxl`, `stvxl128` |
|
||||
| [`stw`](stw.md) | `D` | Store Word | `stw`, `stwu`, `stwux`, `stwx` |
|
||||
| [`stwbrx`](stwbrx.md) | `X` | Store Word Byte-Reverse Indexed | `stwbrx` |
|
||||
| [`stwcx`](stwcx.md) | `X` | Store Word Conditional Indexed | `stwcx` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
156
migration/project-root/ppc-manual/categories/vmx.md
Normal file
156
migration/project-root/ppc-manual/categories/vmx.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# VMX (Altivec)
|
||||
|
||||
128-bit SIMD over 32 registers V0–V31. Integer/float arithmetic, logical, compare, permute/merge, pack/unpack, saturation helpers.
|
||||
|
||||
**144 families** · **193 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`lvsl`](lvsl.md) | `X` | Load Vector for Shift Left Indexed | `lvsl`, `lvsl128` |
|
||||
| [`lvsr`](lvsr.md) | `X` | Load Vector for Shift Right Indexed | `lvsr`, `lvsr128` |
|
||||
| [`vaddcuw`](vaddcuw.md) | `VX` | Vector Add Carryout Unsigned Word | `vaddcuw` |
|
||||
| [`vaddfp`](vaddfp.md) | `VX` | Vector Add Floating Point | `vaddfp`, `vaddfp128` |
|
||||
| [`vaddsbs`](vaddsbs.md) | `VX` | Vector Add Signed Byte Saturate | `vaddsbs` |
|
||||
| [`vaddshs`](vaddshs.md) | `VX` | Vector Add Signed Half Word Saturate | `vaddshs` |
|
||||
| [`vaddsws`](vaddsws.md) | `VX` | Vector Add Signed Word Saturate | `vaddsws` |
|
||||
| [`vaddubm`](vaddubm.md) | `VX` | Vector Add Unsigned Byte Modulo | `vaddubm` |
|
||||
| [`vaddubs`](vaddubs.md) | `VX` | Vector Add Unsigned Byte Saturate | `vaddubs` |
|
||||
| [`vadduhm`](vadduhm.md) | `VX` | Vector Add Unsigned Half Word Modulo | `vadduhm` |
|
||||
| [`vadduhs`](vadduhs.md) | `VX` | Vector Add Unsigned Half Word Saturate | `vadduhs` |
|
||||
| [`vadduwm`](vadduwm.md) | `VX` | Vector Add Unsigned Word Modulo | `vadduwm` |
|
||||
| [`vadduws`](vadduws.md) | `VX` | Vector Add Unsigned Word Saturate | `vadduws` |
|
||||
| [`vand`](vand.md) | `VX` | Vector Logical AND | `vand`, `vand128` |
|
||||
| [`vandc`](vandc.md) | `VX` | Vector Logical AND with Complement | `vandc`, `vandc128` |
|
||||
| [`vavgsb`](vavgsb.md) | `VX` | Vector Average Signed Byte | `vavgsb` |
|
||||
| [`vavgsh`](vavgsh.md) | `VX` | Vector Average Signed Half Word | `vavgsh` |
|
||||
| [`vavgsw`](vavgsw.md) | `VX` | Vector Average Signed Word | `vavgsw` |
|
||||
| [`vavgub`](vavgub.md) | `VX` | Vector Average Unsigned Byte | `vavgub` |
|
||||
| [`vavguh`](vavguh.md) | `VX` | Vector Average Unsigned Half Word | `vavguh` |
|
||||
| [`vavguw`](vavguw.md) | `VX` | Vector Average Unsigned Word | `vavguw` |
|
||||
| [`vcfsx`](vcfsx.md) | `VX` | Vector Convert from Signed Fixed-Point Word | `vcfsx` |
|
||||
| [`vcfux`](vcfux.md) | `VX` | Vector Convert from Unsigned Fixed-Point Word | `vcfux` |
|
||||
| [`vcmpbfp`](vcmpbfp.md) | `VC` | Vector Compare Bounds Floating Point | `vcmpbfp`, `vcmpbfp128` |
|
||||
| [`vcmpeqfp`](vcmpeqfp.md) | `VC` | Vector Compare Equal-to Floating Point | `vcmpeqfp`, `vcmpeqfp128` |
|
||||
| [`vcmpequb`](vcmpequb.md) | `VC` | Vector Compare Equal-to Unsigned Byte | `vcmpequb` |
|
||||
| [`vcmpequh`](vcmpequh.md) | `VC` | Vector Compare Equal-to Unsigned Half Word | `vcmpequh` |
|
||||
| [`vcmpequw`](vcmpequw.md) | `VC` | Vector Compare Equal-to Unsigned Word | `vcmpequw`, `vcmpequw128` |
|
||||
| [`vcmpgefp`](vcmpgefp.md) | `VC` | Vector Compare Greater-Than-or-Equal-to Floating Point | `vcmpgefp`, `vcmpgefp128` |
|
||||
| [`vcmpgtfp`](vcmpgtfp.md) | `VC` | Vector Compare Greater-Than Floating Point | `vcmpgtfp`, `vcmpgtfp128` |
|
||||
| [`vcmpgtsb`](vcmpgtsb.md) | `VC` | Vector Compare Greater-Than Signed Byte | `vcmpgtsb` |
|
||||
| [`vcmpgtsh`](vcmpgtsh.md) | `VC` | Vector Compare Greater-Than Signed Half Word | `vcmpgtsh` |
|
||||
| [`vcmpgtsw`](vcmpgtsw.md) | `VC` | Vector Compare Greater-Than Signed Word | `vcmpgtsw` |
|
||||
| [`vcmpgtub`](vcmpgtub.md) | `VC` | Vector Compare Greater-Than Unsigned Byte | `vcmpgtub` |
|
||||
| [`vcmpgtuh`](vcmpgtuh.md) | `VC` | Vector Compare Greater-Than Unsigned Half Word | `vcmpgtuh` |
|
||||
| [`vcmpgtuw`](vcmpgtuw.md) | `VC` | Vector Compare Greater-Than Unsigned Word | `vcmpgtuw` |
|
||||
| [`vctsxs`](vctsxs.md) | `VX` | Vector Convert to Signed Fixed-Point Word Saturate | `vctsxs` |
|
||||
| [`vctuxs`](vctuxs.md) | `VX` | Vector Convert to Unsigned Fixed-Point Word Saturate | `vctuxs` |
|
||||
| [`vexptefp`](vexptefp.md) | `VX` | Vector 2 Raised to the Exponent Estimate Floating Point | `vexptefp`, `vexptefp128` |
|
||||
| [`vlogefp`](vlogefp.md) | `VX` | Vector Log2 Estimate Floating Point | `vlogefp`, `vlogefp128` |
|
||||
| [`vmaddfp`](vmaddfp.md) | `VA` | Vector Multiply-Add Floating Point | `vmaddfp`, `vmaddfp128` |
|
||||
| [`vmaxfp`](vmaxfp.md) | `VX` | Vector Maximum Floating Point | `vmaxfp`, `vmaxfp128` |
|
||||
| [`vmaxsb`](vmaxsb.md) | `VX` | Vector Maximum Signed Byte | `vmaxsb` |
|
||||
| [`vmaxsh`](vmaxsh.md) | `VX` | Vector Maximum Signed Half Word | `vmaxsh` |
|
||||
| [`vmaxsw`](vmaxsw.md) | `VX` | Vector Maximum Signed Word | `vmaxsw` |
|
||||
| [`vmaxub`](vmaxub.md) | `VX` | Vector Maximum Unsigned Byte | `vmaxub` |
|
||||
| [`vmaxuh`](vmaxuh.md) | `VX` | Vector Maximum Unsigned Half Word | `vmaxuh` |
|
||||
| [`vmaxuw`](vmaxuw.md) | `VX` | Vector Maximum Unsigned Word | `vmaxuw` |
|
||||
| [`vmhaddshs`](vmhaddshs.md) | `VA` | Vector Multiply-High and Add Signed Signed Half Word Saturate | `vmhaddshs` |
|
||||
| [`vmhraddshs`](vmhraddshs.md) | `VA` | Vector Multiply-High Round and Add Signed Signed Half Word Saturate | `vmhraddshs` |
|
||||
| [`vminfp`](vminfp.md) | `VX` | Vector Minimum Floating Point | `vminfp`, `vminfp128` |
|
||||
| [`vminsb`](vminsb.md) | `VX` | Vector Minimum Signed Byte | `vminsb` |
|
||||
| [`vminsh`](vminsh.md) | `VX` | Vector Minimum Signed Half Word | `vminsh` |
|
||||
| [`vminsw`](vminsw.md) | `VX` | Vector Minimum Signed Word | `vminsw` |
|
||||
| [`vminub`](vminub.md) | `VX` | Vector Minimum Unsigned Byte | `vminub` |
|
||||
| [`vminuh`](vminuh.md) | `VX` | Vector Minimum Unsigned Half Word | `vminuh` |
|
||||
| [`vminuw`](vminuw.md) | `VX` | Vector Minimum Unsigned Word | `vminuw` |
|
||||
| [`vmladduhm`](vmladduhm.md) | `VA` | Vector Multiply-Low and Add Unsigned Half Word Modulo | `vmladduhm` |
|
||||
| [`vmrghb`](vmrghb.md) | `VX` | Vector Merge High Byte | `vmrghb` |
|
||||
| [`vmrghh`](vmrghh.md) | `VX` | Vector Merge High Half Word | `vmrghh` |
|
||||
| [`vmrghw`](vmrghw.md) | `VX` | Vector Merge High Word | `vmrghw`, `vmrghw128` |
|
||||
| [`vmrglb`](vmrglb.md) | `VX` | Vector Merge Low Byte | `vmrglb` |
|
||||
| [`vmrglh`](vmrglh.md) | `VX` | Vector Merge Low Half Word | `vmrglh` |
|
||||
| [`vmrglw`](vmrglw.md) | `VX` | Vector Merge Low Word | `vmrglw`, `vmrglw128` |
|
||||
| [`vmsummbm`](vmsummbm.md) | `VA` | Vector Multiply-Sum Mixed-Sign Byte Modulo | `vmsummbm` |
|
||||
| [`vmsumshm`](vmsumshm.md) | `VA` | Vector Multiply-Sum Signed Half Word Modulo | `vmsumshm` |
|
||||
| [`vmsumshs`](vmsumshs.md) | `VA` | Vector Multiply-Sum Signed Half Word Saturate | `vmsumshs` |
|
||||
| [`vmsumubm`](vmsumubm.md) | `VA` | Vector Multiply-Sum Unsigned Byte Modulo | `vmsumubm` |
|
||||
| [`vmsumuhm`](vmsumuhm.md) | `VA` | Vector Multiply-Sum Unsigned Half Word Modulo | `vmsumuhm` |
|
||||
| [`vmsumuhs`](vmsumuhs.md) | `VA` | Vector Multiply-Sum Unsigned Half Word Saturate | `vmsumuhs` |
|
||||
| [`vmulesb`](vmulesb.md) | `VX` | Vector Multiply Even Signed Byte | `vmulesb` |
|
||||
| [`vmulesh`](vmulesh.md) | `VX` | Vector Multiply Even Signed Half Word | `vmulesh` |
|
||||
| [`vmuleub`](vmuleub.md) | `VX` | Vector Multiply Even Unsigned Byte | `vmuleub` |
|
||||
| [`vmuleuh`](vmuleuh.md) | `VX` | Vector Multiply Even Unsigned Half Word | `vmuleuh` |
|
||||
| [`vmulosb`](vmulosb.md) | `VX` | Vector Multiply Odd Signed Byte | `vmulosb` |
|
||||
| [`vmulosh`](vmulosh.md) | `VX` | Vector Multiply Odd Signed Half Word | `vmulosh` |
|
||||
| [`vmuloub`](vmuloub.md) | `VX` | Vector Multiply Odd Unsigned Byte | `vmuloub` |
|
||||
| [`vmulouh`](vmulouh.md) | `VX` | Vector Multiply Odd Unsigned Half Word | `vmulouh` |
|
||||
| [`vnmsubfp`](vnmsubfp.md) | `VA` | Vector Negative Multiply-Subtract Floating Point | `vnmsubfp`, `vnmsubfp128` |
|
||||
| [`vnor`](vnor.md) | `VX` | Vector Logical NOR | `vnor`, `vnor128` |
|
||||
| [`vor`](vor.md) | `VX` | Vector Logical OR | `vor`, `vor128` |
|
||||
| [`vperm`](vperm.md) | `VA` | Vector Permute | `vperm`, `vperm128` |
|
||||
| [`vpkpx`](vpkpx.md) | `VX` | Vector Pack Pixel | `vpkpx` |
|
||||
| [`vpkshss`](vpkshss.md) | `VX` | Vector Pack Signed Half Word Signed Saturate | `vpkshss`, `vpkshss128` |
|
||||
| [`vpkshus`](vpkshus.md) | `VX` | Vector Pack Signed Half Word Unsigned Saturate | `vpkshus`, `vpkshus128` |
|
||||
| [`vpkswss`](vpkswss.md) | `VX` | Vector Pack Signed Word Signed Saturate | `vpkswss`, `vpkswss128` |
|
||||
| [`vpkswus`](vpkswus.md) | `VX` | Vector Pack Signed Word Unsigned Saturate | `vpkswus`, `vpkswus128` |
|
||||
| [`vpkuhum`](vpkuhum.md) | `VX` | Vector Pack Unsigned Half Word Unsigned Modulo | `vpkuhum`, `vpkuhum128` |
|
||||
| [`vpkuhus`](vpkuhus.md) | `VX` | Vector Pack Unsigned Half Word Unsigned Saturate | `vpkuhus`, `vpkuhus128` |
|
||||
| [`vpkuwum`](vpkuwum.md) | `VX` | Vector Pack Unsigned Word Unsigned Modulo | `vpkuwum`, `vpkuwum128` |
|
||||
| [`vpkuwus`](vpkuwus.md) | `VX` | Vector Pack Unsigned Word Unsigned Saturate | `vpkuwus`, `vpkuwus128` |
|
||||
| [`vrefp`](vrefp.md) | `VX` | Vector Reciprocal Estimate Floating Point | `vrefp`, `vrefp128` |
|
||||
| [`vrfim`](vrfim.md) | `VX` | Vector Round to Floating-Point Integer toward -Infinity | `vrfim`, `vrfim128` |
|
||||
| [`vrfin`](vrfin.md) | `VX` | Vector Round to Floating-Point Integer Nearest | `vrfin`, `vrfin128` |
|
||||
| [`vrfip`](vrfip.md) | `VX` | Vector Round to Floating-Point Integer toward +Infinity | `vrfip`, `vrfip128` |
|
||||
| [`vrfiz`](vrfiz.md) | `VX` | Vector Round to Floating-Point Integer toward Zero | `vrfiz`, `vrfiz128` |
|
||||
| [`vrlb`](vrlb.md) | `VX` | Vector Rotate Left Integer Byte | `vrlb` |
|
||||
| [`vrlh`](vrlh.md) | `VX` | Vector Rotate Left Integer Half Word | `vrlh` |
|
||||
| [`vrlw`](vrlw.md) | `VX` | Vector Rotate Left Integer Word | `vrlw`, `vrlw128` |
|
||||
| [`vrsqrtefp`](vrsqrtefp.md) | `VX` | Vector Reciprocal Square Root Estimate Floating Point | `vrsqrtefp`, `vrsqrtefp128` |
|
||||
| [`vsel`](vsel.md) | `VA` | Vector Conditional Select | `vsel`, `vsel128` |
|
||||
| [`vsl`](vsl.md) | `VX` | Vector Shift Left | `vsl` |
|
||||
| [`vslb`](vslb.md) | `VX` | Vector Shift Left Integer Byte | `vslb` |
|
||||
| [`vsldoi`](vsldoi.md) | `VA` | Vector Shift Left Double by Octet Immediate | `vsldoi`, `vsldoi128` |
|
||||
| [`vslh`](vslh.md) | `VX` | Vector Shift Left Integer Half Word | `vslh` |
|
||||
| [`vslo`](vslo.md) | `VX` | Vector Shift Left by Octet | `vslo`, `vslo128` |
|
||||
| [`vslw`](vslw.md) | `VX` | Vector Shift Left Integer Word | `vslw`, `vslw128` |
|
||||
| [`vspltb`](vspltb.md) | `VX` | Vector Splat Byte | `vspltb` |
|
||||
| [`vsplth`](vsplth.md) | `VX` | Vector Splat Half Word | `vsplth` |
|
||||
| [`vspltisb`](vspltisb.md) | `VX` | Vector Splat Immediate Signed Byte | `vspltisb` |
|
||||
| [`vspltish`](vspltish.md) | `VX` | Vector Splat Immediate Signed Half Word | `vspltish` |
|
||||
| [`vspltisw`](vspltisw.md) | `VX` | Vector Splat Immediate Signed Word | `vspltisw`, `vspltisw128` |
|
||||
| [`vspltw`](vspltw.md) | `VX` | Vector Splat Word | `vspltw`, `vspltw128` |
|
||||
| [`vsr`](vsr.md) | `VX` | Vector Shift Right | `vsr` |
|
||||
| [`vsrab`](vsrab.md) | `VX` | Vector Shift Right Algebraic Byte | `vsrab` |
|
||||
| [`vsrah`](vsrah.md) | `VX` | Vector Shift Right Algebraic Half Word | `vsrah` |
|
||||
| [`vsraw`](vsraw.md) | `VX` | Vector Shift Right Algebraic Word | `vsraw`, `vsraw128` |
|
||||
| [`vsrb`](vsrb.md) | `VX` | Vector Shift Right Byte | `vsrb` |
|
||||
| [`vsrh`](vsrh.md) | `VX` | Vector Shift Right Half Word | `vsrh` |
|
||||
| [`vsro`](vsro.md) | `VX` | Vector Shift Right Octet | `vsro`, `vsro128` |
|
||||
| [`vsrw`](vsrw.md) | `VX` | Vector Shift Right Word | `vsrw`, `vsrw128` |
|
||||
| [`vsubcuw`](vsubcuw.md) | `VX` | Vector Subtract Carryout Unsigned Word | `vsubcuw` |
|
||||
| [`vsubfp`](vsubfp.md) | `VX` | Vector Subtract Floating Point | `vsubfp`, `vsubfp128` |
|
||||
| [`vsubsbs`](vsubsbs.md) | `VX` | Vector Subtract Signed Byte Saturate | `vsubsbs` |
|
||||
| [`vsubshs`](vsubshs.md) | `VX` | Vector Subtract Signed Half Word Saturate | `vsubshs` |
|
||||
| [`vsubsws`](vsubsws.md) | `VX` | Vector Subtract Signed Word Saturate | `vsubsws` |
|
||||
| [`vsububm`](vsububm.md) | `VX` | Vector Subtract Unsigned Byte Modulo | `vsububm` |
|
||||
| [`vsububs`](vsububs.md) | `VX` | Vector Subtract Unsigned Byte Saturate | `vsububs` |
|
||||
| [`vsubuhm`](vsubuhm.md) | `VX` | Vector Subtract Unsigned Half Word Modulo | `vsubuhm` |
|
||||
| [`vsubuhs`](vsubuhs.md) | `VX` | Vector Subtract Unsigned Half Word Saturate | `vsubuhs` |
|
||||
| [`vsubuwm`](vsubuwm.md) | `VX` | Vector Subtract Unsigned Word Modulo | `vsubuwm` |
|
||||
| [`vsubuws`](vsubuws.md) | `VX` | Vector Subtract Unsigned Word Saturate | `vsubuws` |
|
||||
| [`vsum2sws`](vsum2sws.md) | `VX` | Vector Sum Across Partial (1/2) Signed Word Saturate | `vsum2sws` |
|
||||
| [`vsum4sbs`](vsum4sbs.md) | `VX` | Vector Sum Across Partial (1/4) Signed Byte Saturate | `vsum4sbs` |
|
||||
| [`vsum4shs`](vsum4shs.md) | `VX` | Vector Sum Across Partial (1/4) Signed Half Word Saturate | `vsum4shs` |
|
||||
| [`vsum4ubs`](vsum4ubs.md) | `VX` | Vector Sum Across Partial (1/4) Unsigned Byte Saturate | `vsum4ubs` |
|
||||
| [`vsumsws`](vsumsws.md) | `VX` | Vector Sum Across Signed Word Saturate | `vsumsws` |
|
||||
| [`vupkhpx`](vupkhpx.md) | `VX` | Vector Unpack High Pixel | `vupkhpx` |
|
||||
| [`vupkhsb`](vupkhsb.md) | `VX` | Vector Unpack High Signed Byte | `vupkhsb`, `vupkhsb128` |
|
||||
| [`vupkhsh`](vupkhsh.md) | `VX` | Vector Unpack High Signed Half Word | `vupkhsh` |
|
||||
| [`vupklpx`](vupklpx.md) | `VX` | Vector Unpack Low Pixel | `vupklpx` |
|
||||
| [`vupklsb`](vupklsb.md) | `VX` | Vector Unpack Low Signed Byte | `vupklsb`, `vupklsb128` |
|
||||
| [`vupklsh`](vupklsh.md) | `VX` | Vector Unpack Low Signed Half Word | `vupklsh` |
|
||||
| [`vxor`](vxor.md) | `VX` | Vector Logical XOR | `vxor`, `vxor128` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
24
migration/project-root/ppc-manual/categories/vmx128.md
Normal file
24
migration/project-root/ppc-manual/categories/vmx128.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# VMX128
|
||||
|
||||
Xbox-360-specific Altivec extension that widens the vector register file to 128 registers (V0–V127). Register IDs are encoded with bit-fusion across non-contiguous fields.
|
||||
|
||||
**12 families** · **12 XML entries**.
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
| Family | Form | Description | Members |
|
||||
| --- | --- | --- | --- |
|
||||
| [`vcfpsxws128`](vcfpsxws128.md) | `VX128_3` | Vector128 Convert From Floating-Point to Signed Fixed-Point Word Saturate | `vcfpsxws128` |
|
||||
| [`vcfpuxws128`](vcfpuxws128.md) | `VX128_3` | Vector128 Convert From Floating-Point to Unsigned Fixed-Point Word Saturate | `vcfpuxws128` |
|
||||
| [`vcsxwfp128`](vcsxwfp128.md) | `VX128_3` | Vector128 Convert From Signed Fixed-Point Word to Floating-Point | `vcsxwfp128` |
|
||||
| [`vcuxwfp128`](vcuxwfp128.md) | `VX128_3` | Vector128 Convert From Unsigned Fixed-Point Word to Floating-Point | `vcuxwfp128` |
|
||||
| [`vmaddcfp128`](vmaddcfp128.md) | `VX128` | Vector128 Multiply Add Floating Point | `vmaddcfp128` |
|
||||
| [`vmsum3fp128`](vmsum3fp128.md) | `VX128` | Vector128 Multiply Sum 3-way Floating Point | `vmsum3fp128` |
|
||||
| [`vmsum4fp128`](vmsum4fp128.md) | `VX128` | Vector128 Multiply Sum 4-way Floating-Point | `vmsum4fp128` |
|
||||
| [`vmulfp128`](vmulfp128.md) | `VX128` | Vector128 Multiply Floating-Point | `vmulfp128` |
|
||||
| [`vpermwi128`](vpermwi128.md) | `VX128_P` | Vector128 Permutate Word Immediate | `vpermwi128` |
|
||||
| [`vpkd3d128`](vpkd3d128.md) | `VX128_4` | Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert | `vpkd3d128` |
|
||||
| [`vrlimi128`](vrlimi128.md) | `VX128_4` | Vector128 Rotate Left Immediate and Mask Insert | `vrlimi128` |
|
||||
| [`vupkd3d128`](vupkd3d128.md) | `VX128_3` | Vector128 Unpack D3Dtype | `vupkd3d128` |
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
127
migration/project-root/ppc-manual/control/crand.md
Normal file
127
migration/project-root/ppc-manual/control/crand.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# `crand` — Condition Register AND
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000202`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crand` | `crand` | — | Condition Register AND |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crand [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crand` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000202`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `257`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crand: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crand: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crand: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crand`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crand`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crand"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:352`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L352)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:717`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L717)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Bit-level granularity.** All eight CR-logical instructions operate on **single CR bits**, not whole 4-bit fields. `CRBD`, `CRBA`, `CRBB` are 5-bit absolute indices into the 32-bit CR register: `BI = 4·field + bit-within-field`, where bit 0 = LT, 1 = GT, 2 = EQ, 3 = SO.
|
||||
- **Operation.** `CR[CRBD] ← CR[CRBA] AND CR[CRBB]`. All other CR bits are preserved.
|
||||
- **Same-source / same-destination quirks.** Identical sources and destinations are legal: `crand 6, 6, 6` is a NOP-style "force CR bit 6 to itself"; `crand bt, bt, bt` reads-then-writes the same bit (no observable change). Compilers exploit `crxor BT,BT,BT` ("clear bit") and `creqv BT,BT,BT` ("set bit") for similar tricks — see those pages.
|
||||
- **Combining branch conditions.** The classic use: synthesise complex branch conditions from multiple compare results. Example: `cmpw cr0, r3, r4; cmpw cr1, r5, r6; crand 4*cr0+2, 4*cr0+2, 4*cr1+2; beq cr0, label` branches if `r3==r4 AND r5==r6` using a single conditional branch.
|
||||
- **No `Rc` / `OE`.** XL-form CR-logical ops never set CR0 or XER; they only update the named CR bit.
|
||||
- **Not synchronising.** Pure data-flow on CR; freely reorderable.
|
||||
- **xenia status.** xenia-rs decodes `crand` (decoder slot 540) but the interpreter snapshot is not embedded on this page — implementation lives in [`crates/xenia-cpu/src/interpreter.rs`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs). xenia-canary's `InstrEmit_crand` emits the equivalent host AND of the two CR bits.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`crandc`](crandc.md) — AND with complement: `CR[BT] ← CR[BA] AND ¬CR[BB]`.
|
||||
- [`cror`](cror.md), [`crorc`](crorc.md) — OR / OR-with-complement.
|
||||
- [`crnand`](crnand.md), [`crnor`](crnor.md) — negated AND / OR.
|
||||
- [`crxor`](crxor.md), [`creqv`](creqv.md) — XOR and equivalence (XNOR).
|
||||
- [`mcrf`](mcrf.md) — copy a whole 4-bit CR field.
|
||||
- [`bcx`](../branch/bcx.md) consumers — the typical reason to compute composite CR bits.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
- `crmove BT, BA` ≡ `cror BT, BA, BA` (use `cror`, not `crand`).
|
||||
- `crset BT` ≡ `creqv BT, BT, BT` (set to 1).
|
||||
- `crclr BT` ≡ `crxor BT, BT, BT` (clear to 0).
|
||||
|
||||
`crand` itself has no dedicated simplified mnemonic.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crand` (Condition Register AND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crand-condition-register-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
119
migration/project-root/ppc-manual/control/crandc.md
Normal file
119
migration/project-root/ppc-manual/control/crandc.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# `crandc` — Condition Register AND with Complement
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000102`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crandc` | `crandc` | — | Condition Register AND with Complement |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crandc [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crandc` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000102`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `129`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crandc: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crandc: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crandc: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crandc`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crandc`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crandc"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:361`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L361)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:713`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L713)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← CR[CRBA] AND ¬CR[CRBB]` — a one-instruction "A and not B" that would otherwise need a complement-then-AND sequence. All other CR bits are preserved.
|
||||
- **Bit-level operands.** `CRBD`, `CRBA`, `CRBB` are 5-bit indices into the 32 CR bits (0=CR0.LT, 1=CR0.GT, 2=CR0.EQ, 3=CR0.SO, 4=CR1.LT, …, 31=CR7.SO). They need not lie in the same CR field.
|
||||
- **Identity / corner cases.** `crandc BT, BA, BA` always yields 0 (a clear-bit idiom, equivalent to but slower than `crxor BT, BT, BT`). `crandc BT, BA, BB` with `BB` always 0 reduces to `crmove BT, BA`.
|
||||
- **Use case.** Synthesises "branch if A *and not* B" predicates without a dedicated `cmp` of `B`. Example: branch only if `cr0.EQ` *and not* `cr1.SO` — `crandc 2, 2, 7` then `beq` on `cr0`.
|
||||
- **No `Rc` / `OE`.** Pure CR-bit dataflow; doesn't update CR0 or XER.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Interpreter handles via the generic CR-logical helper. xenia-canary's `InstrEmit_crandc` emits a host AND of `A` and bitwise-NOT of `B`.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`crand`](crand.md), [`cror`](cror.md), [`crorc`](crorc.md) — closest siblings.
|
||||
- [`crnand`](crnand.md), [`crnor`](crnor.md), [`crxor`](crxor.md), [`creqv`](creqv.md) — full set of CR Boolean ops.
|
||||
- [`mcrf`](mcrf.md) — copy entire CR field; complementary "broad" CR move.
|
||||
- [`bcx`](../branch/bcx.md) — typical consumer of synthesised CR bits.
|
||||
|
||||
`crandc` has no dedicated simplified mnemonic. See [`crand`](crand.md) for the standard `crmove` / `crset` / `crclr` family.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crandc` (Condition Register AND with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crandc-condition-register-complement-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
124
migration/project-root/ppc-manual/control/creqv.md
Normal file
124
migration/project-root/ppc-manual/control/creqv.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `creqv` — Condition Register Equivalent
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000242`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `creqv` | `creqv` | — | Condition Register Equivalent |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
creqv [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `creqv` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000242`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `289`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | creqv: read | CR source bit A (0–31). |
|
||||
| `CRBB` | creqv: read | CR source bit B (0–31). |
|
||||
| `CRBD` | creqv: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `creqv`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`creqv`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="creqv"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:370`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L370)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:718`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L718)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← ¬(CR[CRBA] XOR CR[CRBB])` — i.e. logical equivalence (XNOR). Result is 1 iff `CRBA` and `CRBB` agree.
|
||||
- **`crset BT` idiom.** With identical operands, `creqv BT, BT, BT` always yields 1 (any bit XNOR'd with itself is 1). This is the canonical PowerPC **set-to-1** for a single CR bit; assemblers recognise the simplified mnemonic `crset BT`.
|
||||
- **Bit-level operands.** Like all CR-logical ops, the three operands are 5-bit absolute CR-bit indices (0..31). Mixing CR fields is fine.
|
||||
- **Use case.** Branch on "A == B" of two prior compare results. Example: `crxor` of CR0.SO and CR1.SO gives "differ"; `creqv` gives "agree".
|
||||
- **No `Rc` / `OE`.** Doesn't touch CR0, XER, or any other state beyond the named bit.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Interpreter dispatches through the generic CR-logical helper; canary emits the host XNOR equivalent. The `crset` simplified form is the most common occurrence in real Xbox 360 code.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md) — AND family.
|
||||
- [`cror`](cror.md), [`crorc`](crorc.md), [`crnor`](crnor.md), [`crnand`](crnand.md) — OR family.
|
||||
- [`crxor`](crxor.md) — the dual; `crxor BT, BT, BT` is the standard **clear-to-0** idiom.
|
||||
- [`mcrf`](mcrf.md) — bulk CR-field move.
|
||||
- [`bcx`](../branch/bcx.md) — consumes the synthesised bit.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Effect |
|
||||
| --- | --- | --- |
|
||||
| `crset BT` | `creqv BT, BT, BT` | force `CR[BT] ← 1` |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `creqv` (Condition Register Equivalent)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-creqv-condition-register-equivalent-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
121
migration/project-root/ppc-manual/control/crnand.md
Normal file
121
migration/project-root/ppc-manual/control/crnand.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `crnand` — Condition Register NAND
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c0001c2`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crnand` | `crnand` | — | Condition Register NAND |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crnand [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crnand` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c0001c2`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `225`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crnand: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crnand: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crnand: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crnand`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crnand`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crnand"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:379`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L379)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:716`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L716)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← ¬(CR[CRBA] AND CR[CRBB])`. Result is 0 only when both source bits are 1; otherwise 1.
|
||||
- **Bit-level operands.** 5-bit absolute CR-bit indices, identical to the rest of the CR-logical family. Source and destination bits may all be in different CR fields.
|
||||
- **Identity case.** `crnand BT, BA, BA` ≡ `¬CR[BA]` — a one-instruction CR-bit invert. Less common than `crxor` against a `crset`-prepared 1-bit, but legal.
|
||||
- **Use case.** Branch on "NOT (A AND B)". Less common than the De Morgan equivalent (`cror BT, ¬A, ¬B`), but saves an extra `crnot` step.
|
||||
- **No `Rc` / `OE`.** No CR0 / XER side effects.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Decoded by the generic XL-form CR-logical handler; the interpreter snapshot is shared with `crand`/`cror`/etc. xenia-canary's `InstrEmit_crnand` emits a host AND followed by NOT.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md) — non-negated AND siblings.
|
||||
- [`crnor`](crnor.md) — negated OR (the De Morgan dual).
|
||||
- [`cror`](cror.md), [`crorc`](crorc.md) — OR family.
|
||||
- [`crxor`](crxor.md), [`creqv`](creqv.md) — XOR / XNOR.
|
||||
- [`mcrf`](mcrf.md) — copy a 4-bit CR field wholesale.
|
||||
- [`bcx`](../branch/bcx.md) — typical consumer.
|
||||
|
||||
`crnand` has no dedicated simplified mnemonic. Use `crand` + a separate complement, or use `crnand BT, BA, BA` to invert a single bit.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crnand` (Condition Register NAND)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crnand-condition-register-nand-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
125
migration/project-root/ppc-manual/control/crnor.md
Normal file
125
migration/project-root/ppc-manual/control/crnor.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# `crnor` — Condition Register NOR
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000042`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crnor` | `crnor` | — | Condition Register NOR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crnor [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crnor` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000042`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `33`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crnor: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crnor: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crnor: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crnor`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crnor`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crnor"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:388`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L388)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:712`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L712)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← ¬(CR[CRBA] OR CR[CRBB])`. Result is 1 only when both source bits are 0; otherwise 0.
|
||||
- **`crnot BT, BA` idiom.** With `BA == BB`, `crnor BT, BA, BA` ≡ `¬CR[BA]` — this is the canonical PowerPC **single-bit invert**, recognised by assemblers as the simplified mnemonic `crnot BT, BA`.
|
||||
- **Bit-level operands.** 5-bit absolute CR-bit indices (0..31). The three bits may live in any combination of the eight CR fields.
|
||||
- **Use case.** Branch on "neither A nor B"; or, with `crnot`, simply complement a CR bit before consuming it in a `bcx`.
|
||||
- **No `Rc` / `OE`.** Pure CR-bit dataflow; CR0/XER untouched.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Decoded via the generic CR-logical handler. xenia-canary's `InstrEmit_crnor` emits a host OR followed by NOT.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cror`](cror.md), [`crorc`](crorc.md) — non-negated OR siblings.
|
||||
- [`crnand`](crnand.md) — negated AND (the De Morgan dual).
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md) — AND family.
|
||||
- [`crxor`](crxor.md), [`creqv`](creqv.md) — XOR / XNOR.
|
||||
- [`mcrf`](mcrf.md) — bulk CR-field copy.
|
||||
- [`bcx`](../branch/bcx.md) — typical consumer of synthesised CR bits.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Effect |
|
||||
| --- | --- | --- |
|
||||
| `crnot BT, BA` | `crnor BT, BA, BA` | `CR[BT] ← ¬CR[BA]` (invert single bit) |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crnor` (Condition Register NOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crnor-condition-register-nor-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
125
migration/project-root/ppc-manual/control/cror.md
Normal file
125
migration/project-root/ppc-manual/control/cror.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# `cror` — Condition Register OR
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000382`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cror` | `cror` | — | Condition Register OR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
cror [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `cror` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000382`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `449`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | cror: read | CR source bit A (0–31). |
|
||||
| `CRBB` | cror: read | CR source bit B (0–31). |
|
||||
| `CRBD` | cror: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `cror`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`cror`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cror"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:397`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L397)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:720`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L720)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← CR[CRBA] OR CR[CRBB]`. All other CR bits are preserved.
|
||||
- **`crmove BT, BA` idiom.** With `BA == BB`, `cror BT, BA, BA` is the canonical PowerPC **single-bit copy** between CR bits, recognised by assemblers as the simplified mnemonic `crmove BT, BA`. This is the standard way to relocate a CR bit (e.g., promote `cr1.EQ` to `cr0.EQ` so a default-`cr0` branch can consume it).
|
||||
- **Bit-level operands.** Three independent 5-bit CR-bit indices; mixing CR fields is the whole point of this family.
|
||||
- **Use case.** Branch on "A OR B" of two prior compare results — saves an extra branch by collapsing two conditions.
|
||||
- **No `Rc` / `OE`.** Pure CR-bit dataflow.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Most-used CR-logical instruction in real code (almost always as `crmove`). Decoded by the generic XL-form CR-logical handler; canary emits a host OR.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md) — AND family.
|
||||
- [`crorc`](crorc.md) — OR with complement.
|
||||
- [`crnor`](crnor.md), [`crnand`](crnand.md) — negated forms.
|
||||
- [`crxor`](crxor.md), [`creqv`](creqv.md) — XOR / XNOR.
|
||||
- [`mcrf`](mcrf.md) — copy a 4-bit CR field wholesale.
|
||||
- [`bcx`](../branch/bcx.md), [`bclrx`](../branch/bclrx.md), [`bcctrx`](../branch/bcctrx.md) — typical consumers.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Effect |
|
||||
| --- | --- | --- |
|
||||
| `crmove BT, BA` | `cror BT, BA, BA` | `CR[BT] ← CR[BA]` (single-bit copy) |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `cror` (Condition Register OR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-cror-condition-register-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
121
migration/project-root/ppc-manual/control/crorc.md
Normal file
121
migration/project-root/ppc-manual/control/crorc.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# `crorc` — Condition Register OR with Complement
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000342`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crorc` | `crorc` | — | Condition Register OR with Complement |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crorc [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crorc` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000342`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `417`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crorc: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crorc: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crorc: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crorc`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crorc`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crorc"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L406)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:719`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L719)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← CR[CRBA] OR ¬CR[CRBB]` — a one-instruction "A or not B" implication. Result is 0 only when `A=0` and `B=1`.
|
||||
- **Logical implication.** `B → A` ≡ `¬B ∨ A`, which is exactly `crorc BT, BA, BB`. Useful for predicates of the form "if B holds, then A must hold".
|
||||
- **Identity case.** `crorc BT, BA, BA` always yields 1 (`A ∨ ¬A`), an alternative to `creqv` for setting a bit.
|
||||
- **Bit-level operands.** 5-bit absolute CR-bit indices; sources and destination may all be in different CR fields.
|
||||
- **Use case.** Compose "if B then A" guards without a separate complement step.
|
||||
- **No `Rc` / `OE`.** Doesn't update CR0 or XER.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Decoded by the generic CR-logical handler; canary emits OR-with-NOT directly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`cror`](cror.md), [`crnor`](crnor.md) — non-complement OR / NOR.
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md), [`crnand`](crnand.md) — AND family.
|
||||
- [`crxor`](crxor.md), [`creqv`](creqv.md) — XOR / XNOR.
|
||||
- [`mcrf`](mcrf.md) — bulk CR-field copy.
|
||||
- [`bcx`](../branch/bcx.md) — typical consumer.
|
||||
|
||||
`crorc` has no dedicated simplified mnemonic. See [`cror`](cror.md) for `crmove`.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crorc` (Condition Register OR with Complement)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crorc-condition-register-complement-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
124
migration/project-root/ppc-manual/control/crxor.md
Normal file
124
migration/project-root/ppc-manual/control/crxor.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# `crxor` — Condition Register XOR
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000182`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `crxor` | `crxor` | — | Condition Register XOR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
crxor [CRBD], [CRBA], [CRBB]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `crxor` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000182`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `193`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRBA` | crxor: read | CR source bit A (0–31). |
|
||||
| `CRBB` | crxor: read | CR source bit B (0–31). |
|
||||
| `CRBD` | crxor: write | CR destination bit (0–31). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `crxor`
|
||||
|
||||
- **Reads (always):** `CRBA`, `CRBB`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRBD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`crxor`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crxor"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:415`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L415)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:17`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L17)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:715`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L715)
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** `CR[CRBD] ← CR[CRBA] XOR CR[CRBB]`. Result is 1 iff the two source bits differ.
|
||||
- **`crclr BT` idiom.** With identical operands, `crxor BT, BT, BT` always yields 0 (any bit XOR'd with itself is 0). This is the canonical PowerPC **clear-to-0** for a single CR bit; assemblers recognise the simplified mnemonic `crclr BT`. Compilers emit it before variadic-argument calls (PPC ABI uses `cr1.SO` to flag presence of FP arguments).
|
||||
- **Bit-level operands.** Three independent 5-bit absolute CR-bit indices (0..31).
|
||||
- **Use case.** Branch on "A != B"; or, with the `crclr` idiom, zero a CR bit before fall-through CR computation.
|
||||
- **No `Rc` / `OE`.** No CR0 / XER side effects.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia status.** Common enough in real code (typically as `crclr 6` for the variadic-FP marker) that translators often special-case the `crclr` pattern. xenia-canary's `InstrEmit_crxor` emits a host XOR; xenia-rs decodes via the generic CR-logical handler.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`creqv`](creqv.md) — the dual; `creqv BT, BT, BT` is the standard **set-to-1** idiom.
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md), [`crnand`](crnand.md) — AND family.
|
||||
- [`cror`](cror.md), [`crorc`](crorc.md), [`crnor`](crnor.md) — OR family.
|
||||
- [`mcrf`](mcrf.md) — bulk CR-field copy.
|
||||
- [`bcx`](../branch/bcx.md), [`bclrx`](../branch/bclrx.md) — typical consumers of synthesised CR bits.
|
||||
|
||||
### Simplified Mnemonics
|
||||
|
||||
| Simplified | Expansion | Effect |
|
||||
| --- | --- | --- |
|
||||
| `crclr BT` | `crxor BT, BT, BT` | force `CR[BT] ← 0` |
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `crxor` (Condition Register XOR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-crxor-condition-register-xor-instruction)
|
||||
- [AIX 7.3 — Condition register simplified mnemonics](https://www.ibm.com/docs/en/aix/7.3.0?topic=mnemonics-condition-register-logical-simplified)
|
||||
129
migration/project-root/ppc-manual/control/mcrf.md
Normal file
129
migration/project-root/ppc-manual/control/mcrf.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# `mcrf` — Move Condition Register Field
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [XL](../forms/XL.md) · **Opcode:** `0x4c000000`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mcrf` | `mcrf` | — | Move Condition Register Field |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mcrf [CRFD], [CRFS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mcrf` — form `XL`
|
||||
|
||||
- **Opcode word:** `0x4c000000`
|
||||
- **Primary opcode (bits 0–5):** `19`
|
||||
- **Extended opcode:** `0`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode (19) |
|
||||
| 6–10 | `BT/BO` | target / branch options |
|
||||
| 11–15 | `BA/BI` | source A / CR bit to test |
|
||||
| 16–20 | `BB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `LK` | link flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRFS` | mcrf: read | CR source field. |
|
||||
| `CRFD` | mcrf: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mcrf`
|
||||
|
||||
- **Reads (always):** `CRFS`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mcrf`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrf"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:424`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L424)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:710`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L710)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1683-1686`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1683-L1686)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mcrf => {
|
||||
ctx.cr[instr.crfd()] = ctx.cr[instr.crfs()];
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Field-level (4-bit) move.** Unlike the bit-level CR-logical family ([`crand`](crand.md), …, [`crxor`](crxor.md)), `mcrf` copies *all four* bits of a CR field (LT, GT, EQ, SO) in one instruction. `CRFD` and `CRFS` are 3-bit field indices (0..7), each naming a 4-bit slice of the 32-bit CR.
|
||||
- **No source-field clobber.** The source field is read, not modified — `mcrf 0, 1` copies CR1 into CR0 leaving CR1 intact.
|
||||
- **Same-field is a NOP.** `mcrf cr0, cr0` reads-then-writes the same field; xenia's interpreter still does the assignment but the architectural state is unchanged.
|
||||
- **Use case.** Promote a non-default compare result into `cr0` so a default-`cr0` simplified branch (`beq label`) can consume it without spelling out `cr1`/`cr2`/etc. The alternative — `crmove` — would require four `cror` instructions to move all four bits.
|
||||
- **No CR0/XER side effects.** Pure CR-field dataflow.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia exact match.** xenia-rs models the CR as an array of eight 4-bit fields, so `mcrf` is a single struct copy (`ctx.cr[crfd] = ctx.cr[crfs]`). Matches PowerISA semantics exactly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mfcr`](mfcr.md) — read the entire 32-bit CR into a GPR.
|
||||
- [`mtcrf`](mtcrf.md) — write selected CR fields from a GPR (uses an 8-bit field-mask).
|
||||
- [`mcrxr`](mcrxr.md) — copy `XER[SO..CA]` into a CR field and clear them.
|
||||
- [`mcrfs`](mcrfs.md) — copy an FPSCR field into a CR field.
|
||||
- [`crand`](crand.md), [`crandc`](crandc.md), [`creqv`](creqv.md), [`crnand`](crnand.md), [`crnor`](crnor.md), [`cror`](cror.md), [`crorc`](crorc.md), [`crxor`](crxor.md) — bit-level alternatives.
|
||||
- [`bcx`](../branch/bcx.md) — typical consumer.
|
||||
|
||||
`mcrf` has no simplified mnemonics.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mcrf` (Move Condition Register Field)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrf-move-condition-register-field-instruction)
|
||||
155
migration/project-root/ppc-manual/control/mcrfs.md
Normal file
155
migration/project-root/ppc-manual/control/mcrfs.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# `mcrfs` — Move to Condition Register from FPSCR
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0xfc000080`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mcrfs` | `mcrfs` | — | Move to Condition Register from FPSCR |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mcrfs [CRFD], [CRFS]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mcrfs` — form `X`
|
||||
|
||||
- **Opcode word:** `0xfc000080`
|
||||
- **Primary opcode (bits 0–5):** `63`
|
||||
- **Extended opcode:** `64`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CRFS` | mcrfs: read | CR source field. |
|
||||
| `FPSCR` | mcrfs: read; mcrfs: write | Floating-Point Status and Control Register. |
|
||||
| `CRFD` | mcrfs: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mcrfs`
|
||||
|
||||
- **Reads (always):** `CRFS`, `FPSCR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`, `FPSCR`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
- `mcrfs`: **FPSCR** updated per IEEE-754 flags (FX, FEX, FPRF, FR, FI, exceptions).
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mcrfs`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrfs"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:371`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L371)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:904`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L904)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4716-4745`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4716-L4745)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mcrfs => {
|
||||
let crfd = instr.crfd();
|
||||
let crfs = instr.crfs();
|
||||
let shift = 28 - (crfs as u32 * 4);
|
||||
let nibble = ((ctx.fpscr >> shift) & 0xF) as u8;
|
||||
ctx.cr[crfd] = crate::context::CrField::from_u8(nibble);
|
||||
// Clearable exception bits: 0 (FX), 3 (OX), 4 (UX), 5 (ZX),
|
||||
// 6 (XX), 7 (VXSNAN), 8 (VXISI), 9 (VXIDI), 10 (VXZDZ),
|
||||
// 11 (VXIMZ), 12 (VXVC), 21 (VXSOFT), 22 (VXSQRT), 23 (VXCVI).
|
||||
// (Bit positions are PowerISA MSB-0; here 'FPSCR bit n' means
|
||||
// the bit at (31-n) in our little-endian u32.)
|
||||
const CLEARABLE_MASK: u32 =
|
||||
(1 << 31) | (1 << (31 - 3)) | (1 << (31 - 4)) |
|
||||
(1 << (31 - 5)) | (1 << (31 - 6)) | (1 << (31 - 7)) |
|
||||
(1 << (31 - 8)) | (1 << (31 - 9)) | (1 << (31 - 10)) |
|
||||
(1 << (31 - 11)) | (1 << (31 - 12)) |
|
||||
(1 << (31 - 21)) | (1 << (31 - 22)) | (1 << (31 - 23));
|
||||
let nibble_mask = 0xFu32 << shift;
|
||||
ctx.fpscr &= !(nibble_mask & CLEARABLE_MASK);
|
||||
// PPCBUG-068: recompute the VX summary bit. If any VX* exception
|
||||
// bit remains set, VX must remain set; if all are cleared, VX
|
||||
// must clear. (FEX recomputation omitted — xenia doesn't model
|
||||
// enabled-exception dispatch.)
|
||||
if ctx.fpscr & fpscr::VX_ALL != 0 {
|
||||
ctx.fpscr |= fpscr::VX;
|
||||
} else {
|
||||
ctx.fpscr &= !fpscr::VX;
|
||||
}
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** Copies one 4-bit FPSCR field into the chosen CR field, then **clears the source FPSCR exception-status bits** (sticky-bit reset). The non-exception status bits (FPRF, etc.) are *not* cleared.
|
||||
- **Bits cleared in FPSCR.** The architectural rule is: any bit in the source FPSCR field that is one of {FX, OX, UX, ZX, XX, VXSNAN, VXISI, VXIDI, VXZDZ, VXIMZ, VXVC, VXSOFT, VXSQRT, VXCVI} is reset to 0 after the copy. FEX and VX (summary bits) are subsequently re-derived. Many other FPSCR bits (rounding mode, FPRF, FR/FI) are not affected — even if they fall in `CRFS`.
|
||||
- **CR field destination.** `CRFD` is a 3-bit field index (0..7); the four bits land in their natural positions (LT, GT, EQ, SO) of the chosen CR field. After `mcrfs`, `crf` can be tested with the usual conditional branches.
|
||||
- **Use case.** Inspect a particular FPSCR exception group, then act on it with a `bc` — e.g. test FPSCR[24..27] (the FI / FR / VXSNAN / VXISI cluster) and branch.
|
||||
- **Privilege.** Non-privileged on the Xenon — application-visible.
|
||||
- **xenia status.** Decoded (decoder slot 727), but the interpreter does **not** ship a body in the snapshot on this page — `mcrfs` is rare in title code. xenia's FPSCR model is incomplete (most exception bits are stubbed), so even when implemented, the cleared bits typically have no observable effect.
|
||||
- **No `Rc`.** X-form, but the `Rc` bit position is unused (reserved 0).
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mffsx`](mffsx.md) — read entire FPSCR into an FPR.
|
||||
- [`mtfsfx`](mtfsfx.md), [`mtfsb0x`](mtfsb0x.md), [`mtfsb1x`](mtfsb1x.md), [`mtfsfix`](mtfsfix.md) — write FPSCR bits/fields.
|
||||
- [`mcrf`](mcrf.md) — copy a CR field to another CR field.
|
||||
- [`mcrxr`](mcrxr.md) — analogous copy from XER (also clears).
|
||||
|
||||
`mcrfs` has no simplified mnemonics.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mcrfs` (Move to Condition Register from FPSCR)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrfs-move-condition-register-from-fpscr-instruction)
|
||||
- PowerISA v2.07B, Book I §4.6 — FPSCR layout (sticky exception bits and which clear semantics apply).
|
||||
145
migration/project-root/ppc-manual/control/mcrxr.md
Normal file
145
migration/project-root/ppc-manual/control/mcrxr.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# `mcrxr` — Move to Condition Register from XER
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000400`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mcrxr` | `mcrxr` | — | Move to Condition Register from XER |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mcrxr [CRFD]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mcrxr` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000400`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `512`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CR` | mcrxr: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `CRFD` | mcrxr: write | CR destination field (`crf`, 0–7). |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mcrxr`
|
||||
|
||||
- **Reads (always):** `CR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `CRFD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mcrxr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrxr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:433`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L433)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:51`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L51)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:814`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L814)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:4694-4706`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L4694-L4706)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mcrxr => {
|
||||
let crfd = instr.crfd();
|
||||
ctx.cr[crfd] = crate::context::CrField {
|
||||
lt: ctx.xer_so != 0,
|
||||
gt: ctx.xer_ov != 0,
|
||||
eq: ctx.xer_ca != 0,
|
||||
so: false,
|
||||
};
|
||||
ctx.xer_so = 0;
|
||||
ctx.xer_ov = 0;
|
||||
ctx.xer_ca = 0;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** Copies XER's top 4 status bits into a CR field, **then atomically clears those XER bits**. Layout in the destination CR field after the move:
|
||||
|
||||
| CR bit | Source XER bit | Meaning |
|
||||
| --- | --- | --- |
|
||||
| LT | XER[SO] | summary overflow (sticky) |
|
||||
| GT | XER[OV] | overflow (last `OE=1` op) |
|
||||
| EQ | XER[CA] | carry |
|
||||
| SO | 0 (cleared) | — |
|
||||
|
||||
- **Sticky-bit reset.** XER[SO], XER[OV], and XER[CA] are all *zeroed* after the copy. This is the **only** architecturally clean way to sample-then-clear XER's overflow/carry state — `mfxer` reads but does not clear.
|
||||
- **Use case.** Saturating-arithmetic loops sample XER[OV] periodically; `mcrxr cr0; bso cr0, overflow` is the canonical "did overflow happen since last check?" idiom.
|
||||
- **CR field destination.** `CRFD` is a 3-bit index (0..7). All other CR fields are preserved.
|
||||
- **No reads of GPRs.** `mcrxr` reads only XER, writes only the chosen CR field and XER.
|
||||
- **xenia exact match.** xenia-rs implements the full sample-and-clear semantics: writes `lt = SO`, `gt = OV`, `eq = CA`, `so = false`, then zeroes `xer_so`, `xer_ov`, `xer_ca`. Matches PowerISA exactly.
|
||||
- **Deprecated in newer PowerISA.** PowerISA v2.06+ marked `mcrxr` deprecated in favour of `mcrxrx` and explicit `mfxer`/`mtxer` patterns, but the Xenon predates that; titles still emit it freely.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mfcr`](mfcr.md), [`mtcrf`](mtcrf.md) — bulk CR <-> GPR moves.
|
||||
- [`mcrf`](mcrf.md) — CR-field copy.
|
||||
- [`mcrfs`](mcrfs.md) — analogous copy from FPSCR (also clears certain bits).
|
||||
- [`mfspr`](mfspr.md) (with `SPR=1`, i.e. `mfxer`) — non-clearing read of XER into a GPR.
|
||||
- [`mtspr`](mtspr.md) (with `SPR=1`, i.e. `mtxer`) — explicit XER write.
|
||||
|
||||
`mcrxr` has no simplified mnemonics.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mcrxr` (Move to Condition Register from XER)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mcrxr-move-condition-register-from-xer-instruction)
|
||||
117
migration/project-root/ppc-manual/control/mfcr.md
Normal file
117
migration/project-root/ppc-manual/control/mfcr.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# `mfcr` — Move from Condition Register
|
||||
|
||||
> **Category:** [Control / CR / SPR](../categories/control.md) · **Form:** [X](../forms/X.md) · **Opcode:** `0x7c000026`
|
||||
|
||||
<!-- GENERATED: BEGIN -->
|
||||
|
||||
## Assembler Mnemonics
|
||||
|
||||
| Mnemonic | XML entry | Flags | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `mfcr` | `mfcr` | — | Move from Condition Register |
|
||||
|
||||
## Syntax
|
||||
|
||||
```asm
|
||||
mfcr [RD]
|
||||
```
|
||||
|
||||
## Encoding
|
||||
|
||||
### `mfcr` — form `X`
|
||||
|
||||
- **Opcode word:** `0x7c000026`
|
||||
- **Primary opcode (bits 0–5):** `31`
|
||||
- **Extended opcode:** `19`
|
||||
- **Synchronising:** no
|
||||
|
||||
| Bits | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | `OPCD` | primary opcode |
|
||||
| 6–10 | `RT/FRT/VRT` | destination |
|
||||
| 11–15 | `RA/FRA/VRA` | source A |
|
||||
| 16–20 | `RB/FRB/VRB` | source B |
|
||||
| 21–30 | `XO` | extended opcode (10 bits) |
|
||||
| 31 | `Rc` | record-form flag |
|
||||
|
||||
## Operands
|
||||
|
||||
| Field | Role | Description |
|
||||
| --- | --- | --- |
|
||||
| `CR` | mfcr: read | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. |
|
||||
| `RD` | mfcr: write | Destination GPR. |
|
||||
|
||||
## Register Effects
|
||||
|
||||
### `mfcr`
|
||||
|
||||
- **Reads (always):** `CR`
|
||||
- **Reads (conditional):** _none_
|
||||
- **Writes (always):** `RD`
|
||||
- **Writes (conditional):** _none_
|
||||
|
||||
## Status-Register Effects
|
||||
|
||||
_No condition-register or status-register effects._
|
||||
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
RT <- 0x00000000 || CR
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
|
||||
## Implementation References
|
||||
|
||||
**`mfcr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfcr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:625`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L625)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:753`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L753)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1627-1630`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1627-L1630)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfcr => {
|
||||
ctx.gpr[instr.rd()] = ctx.cr() as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
## Special Cases & Edge Conditions
|
||||
|
||||
- **Operation.** Packs all 32 CR bits into the low half of `RD`; the upper 32 bits of `RD` are zeroed. CR field 0 ends up in bits 32..35 of `RD` (i.e. bits 0..3 of the 32-bit packed value), CR field 7 in bits 60..63 (bits 28..31).
|
||||
- **No CR side effect.** `mfcr` is a read; CR is unmodified. The XL-form's nominal `Rc` bit is unused on this opcode.
|
||||
- **Saving CR across calls.** The Xbox 360 / SysV ABI requires non-volatile CR fields (CR2..CR4) to be preserved across calls. Standard prologue: `mfcr r12; stw r12, 8(r1)`. Epilogue restores via [`mtcrf`](mtcrf.md).
|
||||
- **Bit ordering.** PowerPC numbers bits big-endian (bit 0 = MSB). The encoding into the GPR follows the same convention: CR0.LT lands in bit 32 of the doubleword (the MSB of the low word). C-side translations should mask with `0xFFFFFFFFu` before consuming.
|
||||
- **`mfocrf` variant.** PowerISA defines `mfocrf` (one CR field), encoded as `mfcr` with the high bit of FXM set. xenia-rs decodes both as the same opcode and ignores the FXM hint, returning the entire CR. This is benign — the spec says implementations may treat `mfocrf` as `mfcr`.
|
||||
- **Not synchronising.** Reorderable.
|
||||
- **xenia exact match.** xenia-rs packs its eight `CrField` structs into a `u64` via `ctx.cr()`, mirroring spec semantics.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
- [`mtcrf`](mtcrf.md) — inverse: write selected CR fields from a GPR.
|
||||
- [`mcrf`](mcrf.md), [`mcrxr`](mcrxr.md), [`mcrfs`](mcrfs.md) — narrower CR-field moves.
|
||||
- [`mfspr`](mfspr.md), [`mtspr`](mtspr.md) — generic SPR moves; CR is *not* an SPR (it has its own opcode).
|
||||
|
||||
`mfcr` has no simplified mnemonics. `mfocrf RT, FXM` is a related encoding handled by the same xenia-rs slot.
|
||||
|
||||
## IBM Reference
|
||||
|
||||
- [AIX 7.3 — `mfcr` (Move from Condition Register)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-mfcr-move-from-condition-register-instruction)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user