docs(ppc-manual): quote Canary and our own decoder, not the retired xenia-rs #42
@@ -1,21 +1,12 @@
|
||||
> 📌 **Adopted 2026-09-13** from an untracked directory in the project root,
|
||||
> where it had lived on one disk since April — see
|
||||
> [`docs/agents/CONSOLIDATION.md`](../../docs/agents/CONSOLIDATION.md).
|
||||
>
|
||||
> ⚠️ **The generator's inputs have moved.** `generator/generate_manual.py` reads
|
||||
> `xenia-canary/tools/ppc-instructions.xml` **and the `xenia-rs` source tree**.
|
||||
> That emulator is retired: its decoder now lives here as
|
||||
> [`crates/sylpheed-ppc`](../../crates/sylpheed-ppc), and the generator must be
|
||||
> repointed before it is run again. The manual's *content* is unaffected — it is
|
||||
> checked in, and nothing regenerates it implicitly.
|
||||
|
||||
# PowerPC Instruction Manual (Xenia Xbox 360 Subset)
|
||||
|
||||
A reference for the **Xenon** PowerPC dialect used by the Xbox 360. Its
|
||||
primary audience is an AI agent translating PPC assembly functions into
|
||||
equivalent C. The content is derived from the two authoritative sources in
|
||||
this repository — **xenia-canary** (C++ emulator) and **xenia-rs** (Rust
|
||||
rewrite) — and may be deepened with the IBM AIX PowerPC reference.
|
||||
equivalent C. The content is derived from **Xenia Canary** — the reference
|
||||
emulator: its instruction table and its PPC emitters, quoted at a pinned
|
||||
upstream commit — and from Sylpheed's own decoder, `crates/sylpheed-ppc`, which
|
||||
produces the disassembly in `sylpheed.db`. It may be deepened with the IBM AIX
|
||||
PowerPC reference.
|
||||
|
||||
- **455** distinct XML-level instructions (one page each).
|
||||
- **350** instruction family pages (VMX128 siblings folded).
|
||||
@@ -45,7 +36,7 @@ Every instruction page has the same sections, in this order:
|
||||
| **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. |
|
||||
| **Implementation References** | Canary's XML entry and emitter (linked at the pinned commit, with a frozen snapshot of the emitter), and Sylpheed's opcode/decoder lines. |
|
||||
| **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. |
|
||||
|
||||
@@ -47,8 +47,9 @@ Every page follows this skeleton:
|
||||
<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>
|
||||
<Canary XML entry + emitter line (linked at the pinned upstream commit), Sylpheed
|
||||
opcode / decoder lines (crates/sylpheed-ppc), plus a <details> block with the
|
||||
frozen Canary emitter snapshot — and the helper it calls, if it only delegates>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -72,13 +72,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,30 +88,31 @@ CA <- carry_out_of_32_or_64_bit_add((RA), (RB))
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addcx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:64`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L64)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:8`](../../../crates/sylpheed-ppc/src/opcode.rs#L8)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:976`](../../../crates/sylpheed-ppc/src/decoder.rs#L976)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addcx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RD <- (RA) + (RB)
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.XO.RA);
|
||||
Value* rb = f.LoadGPR(i.XO.RB);
|
||||
Value* v = f.Add(ra, rb);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow(EFLAGS OF?);
|
||||
} else {
|
||||
f.StoreCA(AddDidCarry(f, ra, rb));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -120,7 +123,7 @@ CA <- carry_out_of_32_or_64_bit_add((RA), (RB))
|
||||
- **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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs); 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.
|
||||
|
||||
|
||||
@@ -72,13 +72,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,31 +88,32 @@ CA <- carry_out_of_the_add
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:83`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L83)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:9`](../../../crates/sylpheed-ppc/src/opcode.rs#L9)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:983`](../../../crates/sylpheed-ppc/src/decoder.rs#L983)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RD <- (RA) + (RB) + XER[CA]
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.XO.RA);
|
||||
Value* rb = f.LoadGPR(i.XO.RB);
|
||||
Value* v = f.AddWithCarry(ra, rb, f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
f.StoreCA(AddWithCarryDidCarry(f, ra, rb, f.LoadCA()));
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
if (i.XO.OE) {
|
||||
// Stub implementation.
|
||||
// TODO: Handle overflow flag.
|
||||
// NOTE: 535507D4 (Raiden Fighters Aces) never seems to rely on this
|
||||
// behavior either, despite OE being set.
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -71,21 +71,26 @@ 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addi"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:103`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L103)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:10`](../../../crates/sylpheed-ppc/src/opcode.rs#L10)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:453`](../../../crates/sylpheed-ppc/src/decoder.rs#L453)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addi(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if RA = 0 then
|
||||
// RT <- EXTS(SI)
|
||||
// else
|
||||
// RT <- (RA) + EXTS(SI)
|
||||
Value* si = f.LoadConstantInt64(XEEXTS16(i.D.DS));
|
||||
Value* v = si;
|
||||
if (i.D.RA) {
|
||||
v = f.Add(f.LoadGPR(i.D.RA), si);
|
||||
}
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -64,13 +64,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -78,24 +80,22 @@ CA <- carry_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addic"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:117`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L117)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:11`](../../../crates/sylpheed-ppc/src/opcode.rs#L11)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:451`](../../../crates/sylpheed-ppc/src/decoder.rs#L451)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addic(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- (RA) + EXTS(SI)
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.D.RA);
|
||||
Value* v = f.Add(ra, f.LoadConstantInt64(XEEXTS16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
f.StoreCA(AddDidCarry(f, ra, f.LoadConstantInt64(XEEXTS16(i.D.DS))));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ addic. [RD], [RA], [SIMM]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,24 +85,23 @@ addic. [RD], [RA], [SIMM]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addic."`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:127`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L127)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:12`](../../../crates/sylpheed-ppc/src/opcode.rs#L12)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:452`](../../../crates/sylpheed-ppc/src/decoder.rs#L452)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addicx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- (RA) + EXTS(SI)
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.D.RA);
|
||||
Value* v = f.Add(f.LoadGPR(i.D.RA), f.LoadConstantInt64(XEEXTS16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
f.StoreCA(AddDidCarry(f, ra, f.LoadConstantInt64(XEEXTS16(i.D.DS))));
|
||||
f.UpdateCR(0, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -116,7 +113,7 @@ addic. [RD], [RA], [SIMM]
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:65`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -71,28 +71,26 @@ 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addis"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:138`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L138)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:13`](../../../crates/sylpheed-ppc/src/opcode.rs#L13)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:454`](../../../crates/sylpheed-ppc/src/decoder.rs#L454)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addis(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if RA = 0 then
|
||||
// RT <- EXTS(SI) || i16.0
|
||||
// else
|
||||
// RT <- (RA) + EXTS(SI) || i16.0
|
||||
Value* si = f.LoadConstantInt64(XEEXTS16(i.D.DS) << 16);
|
||||
Value* v = si;
|
||||
if (i.D.RA) {
|
||||
v = f.Add(f.LoadGPR(i.D.RA), si);
|
||||
}
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -71,13 +71,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,30 +87,32 @@ CA <- carry_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addmex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:152`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L152)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:14`](../../../crates/sylpheed-ppc/src/opcode.rs#L14)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:988`](../../../crates/sylpheed-ppc/src/decoder.rs#L988)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addmex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- (RA) + CA - 1
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.XO.RA);
|
||||
Value* v = f.AddWithCarry(ra, f.LoadConstantInt64(-1), f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// With XER[SO] update too.
|
||||
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
} else {
|
||||
// Just CA update.
|
||||
f.StoreCA(AddWithCarryDidCarry(f, ra, f.LoadConstantInt64(-1), f.LoadCA()));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -120,7 +124,7 @@ CA <- carry_out
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:139`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — 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
|
||||
|
||||
@@ -82,29 +82,26 @@ 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:50`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L50)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:15`](../../../crates/sylpheed-ppc/src/opcode.rs#L15)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:990`](../../../crates/sylpheed-ppc/src/decoder.rs#L990)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RD <- (RA) + (RB)
|
||||
Value* v = f.Add(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow(EFLAGS OF?);
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -127,7 +124,7 @@ if Rc then
|
||||
- **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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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`.
|
||||
|
||||
@@ -71,13 +71,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,30 +87,32 @@ CA <- carry_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="addzex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:172`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L172)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:16`](../../../crates/sylpheed-ppc/src/opcode.rs#L16)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:985`](../../../crates/sylpheed-ppc/src/decoder.rs#L985)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_addzex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- (RA) + CA
|
||||
// CA <- carry bit
|
||||
Value* ra = f.LoadGPR(i.XO.RA);
|
||||
Value* v = f.AddWithCarry(ra, f.LoadZeroInt64(), f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// With XER[SO] update too.
|
||||
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
} else {
|
||||
// Just CA update.
|
||||
f.StoreCA(AddWithCarryDidCarry(f, ra, f.LoadZeroInt64(), f.LoadCA()));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -120,7 +124,7 @@ CA <- carry_out
|
||||
- **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).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs** (truncation in [`interpreter.rs:128`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — 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
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,22 +82,22 @@ RA <- (RS) & ~(RB)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andcx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:647`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L647)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:17`](../../../crates/sylpheed-ppc/src/opcode.rs#L17)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:883`](../../../crates/sylpheed-ppc/src/decoder.rs#L883)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_andcx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) & ¬(RB)
|
||||
Value* ra = f.AndNot(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -108,7 +110,7 @@ RA <- (RS) & ~(RB)
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:352`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — same truncation pattern.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ andis. [RA], [RS], [UIMM]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,21 +84,21 @@ andis. [RA], [RS], [UIMM]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andis."`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:665`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L665)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:18`](../../../crates/sylpheed-ppc/src/opcode.rs#L18)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:467`](../../../crates/sylpheed-ppc/src/decoder.rs#L467)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_andisx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) & (i32.0 || UI || i16.0)
|
||||
Value* ra =
|
||||
f.And(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
f.UpdateCR(0, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -113,7 +111,7 @@ andis. [RA], [RS], [UIMM]
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The `result as i32 as i64` truncation in [`interpreter.rs:326`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) is harmless: the result is bounded by `0x00000000_FFFF0000`, which fits the 32-bit window exactly.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ andi. [RA], [RS], [UIMM]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,20 +84,20 @@ andi. [RA], [RS], [UIMM]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andi."`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:657`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L657)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:19`](../../../crates/sylpheed-ppc/src/opcode.rs#L19)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:466`](../../../crates/sylpheed-ppc/src/decoder.rs#L466)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_andix(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) & (i48.0 || UI)
|
||||
Value* ra = f.And(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
f.UpdateCR(0, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -112,7 +110,7 @@ andi. [RA], [RS], [UIMM]
|
||||
- **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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) is harmless here — the result fits in 16 bits, so spec and xenia agree.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,20 +82,22 @@ RA <- (RS) & (RB)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="andx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:637`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L637)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:20`](../../../crates/sylpheed-ppc/src/opcode.rs#L20)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:875`](../../../crates/sylpheed-ppc/src/decoder.rs#L875)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_andx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) & (RB)
|
||||
Value* ra = f.And(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -104,7 +108,7 @@ RA <- (RS) & (RB)
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** The interpreter's `Rc=1` path in [`interpreter.rs:347`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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`).
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,37 +83,41 @@ CR[BF] <- signed_compare(a, b) || XER[SO]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmp"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:523`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L523)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:27`](../../../crates/sylpheed-ppc/src/opcode.rs#L27)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:864`](../../../crates/sylpheed-ppc/src/decoder.rs#L864)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cmp(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if L = 0 then
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// b <- EXTS((RB)[32:63])
|
||||
// else
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if a < b then
|
||||
// c <- 0b100
|
||||
// else if a > b then
|
||||
// c <- 0b010
|
||||
// else
|
||||
// c <- 0b001
|
||||
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
|
||||
uint32_t BF = i.X.RT >> 2;
|
||||
uint32_t L = i.X.RT & 1;
|
||||
Value* lhs;
|
||||
Value* rhs;
|
||||
if (L) {
|
||||
lhs = f.LoadGPR(i.X.RA);
|
||||
rhs = f.LoadGPR(i.X.RB);
|
||||
} else {
|
||||
lhs = f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE);
|
||||
rhs = f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE);
|
||||
}
|
||||
f.UpdateCR(BF, lhs, rhs);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,40 +82,39 @@ CR[BF] <- signed_compare(a, b) || XER[SO]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpi"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:552`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L552)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:28`](../../../crates/sylpheed-ppc/src/opcode.rs#L28)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:450`](../../../crates/sylpheed-ppc/src/decoder.rs#L450)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cmpi(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if L = 0 then
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// else
|
||||
// a <- (RA)
|
||||
// if a < EXTS(SI) then
|
||||
// c <- 0b100
|
||||
// else if a > EXTS(SI) then
|
||||
// c <- 0b010
|
||||
// else
|
||||
// c <- 0b001
|
||||
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
|
||||
uint32_t BF = i.D.RT >> 2;
|
||||
uint32_t L = i.D.RT & 1;
|
||||
Value* lhs;
|
||||
Value* rhs;
|
||||
if (L) {
|
||||
lhs = f.LoadGPR(i.D.RA);
|
||||
rhs = f.LoadConstantInt64(XEEXTS16(i.D.DS));
|
||||
} else {
|
||||
lhs = f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE);
|
||||
rhs = f.LoadConstantInt32(int32_t(XEEXTS16(i.D.DS)));
|
||||
}
|
||||
f.UpdateCR(BF, lhs, rhs);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,23 +83,41 @@ CR[BF] <- unsigned_compare(a, b) || XER[SO]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpl"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:579`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L579)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:29`](../../../crates/sylpheed-ppc/src/opcode.rs#L29)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:876`](../../../crates/sylpheed-ppc/src/decoder.rs#L876)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cmpl(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if L = 0 then
|
||||
// a <- i32.0 || (RA)[32:63]
|
||||
// b <- i32.0 || (RB)[32:63]
|
||||
// else
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if a <u b then
|
||||
// c <- 0b100
|
||||
// else if a >u b then
|
||||
// c <- 0b010
|
||||
// else
|
||||
// c <- 0b001
|
||||
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
|
||||
uint32_t BF = i.X.RT >> 2;
|
||||
uint32_t L = i.X.RT & 1;
|
||||
Value* lhs;
|
||||
Value* rhs;
|
||||
if (L) {
|
||||
lhs = f.LoadGPR(i.X.RA);
|
||||
rhs = f.LoadGPR(i.X.RB);
|
||||
} else {
|
||||
lhs = f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE);
|
||||
rhs = f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE);
|
||||
}
|
||||
f.UpdateCR(BF, lhs, rhs, false);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -65,13 +65,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -79,27 +81,39 @@ CR[BF] <- unsigned_compare(a, b) || XER[SO]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cmpli"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:608`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L608)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:30`](../../../crates/sylpheed-ppc/src/opcode.rs#L30)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:449`](../../../crates/sylpheed-ppc/src/decoder.rs#L449)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cmpli(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if L = 0 then
|
||||
// a <- i32.0 || (RA)[32:63]
|
||||
// else
|
||||
// a <- (RA)
|
||||
// if a <u i48.0 || SI then
|
||||
// c <- 0b100
|
||||
// else if a >u i48.0 || SI then
|
||||
// c <- 0b010
|
||||
// else
|
||||
// c <- 0b001
|
||||
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
|
||||
uint32_t BF = i.D.RT >> 2;
|
||||
uint32_t L = i.D.RT & 1;
|
||||
Value* lhs;
|
||||
Value* rhs;
|
||||
if (L) {
|
||||
lhs = f.LoadGPR(i.D.RA);
|
||||
rhs = f.LoadConstantUint64(i.D.DS);
|
||||
} else {
|
||||
lhs = f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE);
|
||||
rhs = f.LoadConstantUint32(i.D.DS);
|
||||
}
|
||||
f.UpdateCR(BF, lhs, rhs, false);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,19 +82,27 @@ RA <- zero_extend(n)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cntlzdx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:674`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L674)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:32`](../../../crates/sylpheed-ppc/src/opcode.rs#L32)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:882`](../../../crates/sylpheed-ppc/src/decoder.rs#L882)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cntlzdx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- 0
|
||||
// do while n < 64
|
||||
// if (RS)[n] = 1 then leave n
|
||||
// n <- n + 1
|
||||
// RA <- n
|
||||
Value* v = f.CountLeadingZeros(f.LoadGPR(i.X.RT));
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,21 +82,27 @@ RA <- zero_extend(n)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cntlzwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:689`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L689)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:33`](../../../crates/sylpheed-ppc/src/opcode.rs#L33)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:873`](../../../crates/sylpheed-ppc/src/decoder.rs#L873)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_cntlzwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- 32
|
||||
// do while n < 64
|
||||
// if (RS)[n] = 1 then leave n
|
||||
// n <- n + 1
|
||||
// RA <- n - 32
|
||||
Value* v = f.CountLeadingZeros(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE));
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -106,7 +114,7 @@ RA <- zero_extend(n)
|
||||
- **`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`.
|
||||
- **`Rc=1` CR0 update is small-positive-only.** Result fits in 6 bits; xenia's `as i32 as i64` truncation in [`interpreter.rs:404`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,31 +86,37 @@ RT <- (RA) /u (RB) ; undefined if RB=0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divdux"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:217`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L217)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:52`](../../../crates/sylpheed-ppc/src/opcode.rs#L52)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:991`](../../../crates/sylpheed-ppc/src/decoder.rs#L991)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_divdux(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// dividend <- (RA)
|
||||
// divisor <- (RB)
|
||||
// if divisor = 0 then
|
||||
// if OE = 1 then
|
||||
// XER[OV] <- 1
|
||||
// return
|
||||
// RT <- dividend ÷ divisor
|
||||
Value* divisor = f.LoadGPR(i.XO.RB);
|
||||
// TODO(benvanik): check if zero
|
||||
// if OE=1, set XER[OV] = 1
|
||||
// else skip the divide
|
||||
Value* v = f.Div(f.LoadGPR(i.XO.RA), divisor, ARITHMETIC_UNSIGNED);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// If we are OE=1 we need to clear the overflow bit.
|
||||
// e.update_xer_with_overflow(e.get_uint64(0));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -116,10 +124,10 @@ RT <- (RA) /u (RB) ; undefined if RB=0
|
||||
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)); 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.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:311`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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).
|
||||
|
||||
|
||||
@@ -70,13 +70,15 @@ RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,31 +86,37 @@ RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and RB
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divdx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:192`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L192)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:53`](../../../crates/sylpheed-ppc/src/opcode.rs#L53)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:993`](../../../crates/sylpheed-ppc/src/decoder.rs#L993)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_divdx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// dividend <- (RA)
|
||||
// divisor <- (RB)
|
||||
// if divisor = 0 then
|
||||
// if OE = 1 then
|
||||
// XER[OV] <- 1
|
||||
// return
|
||||
// RT <- dividend ÷ divisor
|
||||
Value* divisor = f.LoadGPR(i.XO.RB);
|
||||
// TODO(benvanik): check if zero
|
||||
// if OE=1, set XER[OV] = 1
|
||||
// else skip the divide
|
||||
Value* v = f.Div(f.LoadGPR(i.XO.RA), divisor);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// If we are OE=1 we need to clear the overflow bit.
|
||||
// e.update_xer_with_overflow(e.get_uint64(0));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -116,10 +124,10 @@ RT <- (RA) /s (RB) ; undefined if RB=0 or (RA=-2^63 and RB
|
||||
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit here.** Unlike most ALU pages, [`interpreter.rs:298`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,32 +86,40 @@ RT <- ((RA)[32:63] /u (RB)[32:63]) zero-extended to 64 ; undefined if RB=0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divwux"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:269`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L269)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:54`](../../../crates/sylpheed-ppc/src/opcode.rs#L54)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:992`](../../../crates/sylpheed-ppc/src/decoder.rs#L992)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_divwux(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// dividend[0:31] <- (RA)[32:63]
|
||||
// divisor[0:31] <- (RB)[32:63]
|
||||
// if divisor = 0 then
|
||||
// if OE = 1 then
|
||||
// XER[OV] <- 1
|
||||
// return
|
||||
// RT[32:63] <- dividend ÷ divisor
|
||||
// RT[0:31] <- undefined
|
||||
Value* divisor = f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE);
|
||||
// TODO(benvanik): check if zero
|
||||
// if OE=1, set XER[OV] = 1
|
||||
// else skip the divide
|
||||
Value* v = f.Div(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), divisor,
|
||||
ARITHMETIC_UNSIGNED);
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// If we are OE=1 we need to clear the overflow bit.
|
||||
// e.update_xer_with_overflow(e.get_uint64(0));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,10 +128,10 @@ RT <- ((RA)[32:63] /u (RB)[32:63]) zero-extended to 64 ; undefined if RB=0
|
||||
## 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.
|
||||
- **Single undefined case.** Division by zero (`RB == 0`); xenia-rs returns 0 ([`interpreter.rs:251`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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`.
|
||||
- **`Rc=1` CR0 update.** [`interpreter.rs:256`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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`.
|
||||
|
||||
|
||||
@@ -70,13 +70,15 @@ RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or ov
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,33 +86,39 @@ RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or ov
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:242`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L242)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:55`](../../../crates/sylpheed-ppc/src/opcode.rs#L55)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:994`](../../../crates/sylpheed-ppc/src/decoder.rs#L994)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_divwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// dividend[0:31] <- (RA)[32:63]
|
||||
// divisor[0:31] <- (RB)[32:63]
|
||||
// if divisor = 0 then
|
||||
// if OE = 1 then
|
||||
// XER[OV] <- 1
|
||||
// return
|
||||
// RT[32:63] <- dividend ÷ divisor
|
||||
// RT[0:31] <- undefined
|
||||
Value* divisor = f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE);
|
||||
// TODO(benvanik): check if zero
|
||||
// if OE=1, set XER[OV] = 1
|
||||
// else skip the divide
|
||||
Value* v = f.Div(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), divisor);
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
// If we are OE=1 we need to clear the overflow bit.
|
||||
// e.update_xer_with_overflow(e.get_uint64(0));
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -119,10 +127,10 @@ RT <- ((RA)[32:63] /s (RB)[32:63]) sign-extended to 64 ; undefined if RB=0 or ov
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)); 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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:243`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
|
||||
@@ -61,13 +61,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -75,17 +77,17 @@ enforce in-order execution of I/O
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="eieio"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:749`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_memory.cc#L749)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:57`](../../../crates/sylpheed-ppc/src/opcode.rs#L57)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:959`](../../../crates/sylpheed-ppc/src/decoder.rs#L959)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_eieio(PPCHIRBuilder& f, const InstrData& i) {
|
||||
f.MemoryBarrier();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -96,9 +98,9 @@ enforce in-order execution of I/O
|
||||
- **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.
|
||||
- **Xenia-rs is a no-op.** The interpreter trivially advances PC ([`interpreter.rs:1267`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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.
|
||||
- **Distinct from `sync` and `isync`.** All three share xenia's no-op arm in [`interpreter.rs:1266`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs); on real hardware they have very different semantics and latencies.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,23 +82,22 @@ RA <- ~((RS) ^ (RB))
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="eqvx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:704`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L704)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:59`](../../../crates/sylpheed-ppc/src/opcode.rs#L59)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:911`](../../../crates/sylpheed-ppc/src/decoder.rs#L911)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_eqvx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) == (RB)
|
||||
Value* ra = f.Not(f.Xor(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -109,7 +110,7 @@ RA <- ~((RS) ^ (RB))
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:382`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
|
||||
@@ -65,13 +65,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -79,21 +81,25 @@ RA <- EXTS_8_to_64((RS)[56:63])
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extsbx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:714`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L714)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:60`](../../../crates/sylpheed-ppc/src/opcode.rs#L60)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:964`](../../../crates/sylpheed-ppc/src/decoder.rs#L964)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_extsbx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// s <- (RS)[56]
|
||||
// RA[56:63] <- (RS)[56:63]
|
||||
// RA[0:55] <- i56.s
|
||||
Value* rt = f.LoadGPR(i.X.RT);
|
||||
rt = f.SignExtend(f.Truncate(rt, INT8_TYPE), INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, rt);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, rt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -103,7 +109,7 @@ RA <- EXTS_8_to_64((RS)[56:63])
|
||||
|
||||
- **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.
|
||||
- **`Rc=1` updates CR0 from the full 64-bit signed value** — but xenia-rs truncates to 32 bits in [`interpreter.rs:384`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). 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.
|
||||
|
||||
@@ -65,13 +65,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -79,21 +81,25 @@ RA <- EXTS_16_to_64((RS)[48:63])
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extshx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:727`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L727)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:61`](../../../crates/sylpheed-ppc/src/opcode.rs#L61)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:962`](../../../crates/sylpheed-ppc/src/decoder.rs#L962)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_extshx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// s <- (RS)[48]
|
||||
// RA[48:63] <- (RS)[48:63]
|
||||
// RA[0:47] <- 48.s
|
||||
Value* rt = f.LoadGPR(i.X.RT);
|
||||
rt = f.SignExtend(f.Truncate(rt, INT16_TYPE), INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, rt);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, rt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -103,7 +109,7 @@ RA <- EXTS_16_to_64((RS)[48:63])
|
||||
|
||||
- **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.
|
||||
- **`Rc=1` CR0 update.** Xenia-rs uses `as i32 as i64` ([`interpreter.rs:389`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) — 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.**
|
||||
|
||||
@@ -65,13 +65,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -79,19 +81,25 @@ RA <- EXTS_32_to_64((RS)[32:63])
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="extswx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:740`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L740)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:62`](../../../crates/sylpheed-ppc/src/opcode.rs#L62)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:967`](../../../crates/sylpheed-ppc/src/decoder.rs#L967)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_extswx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// s <- (RS)[32]
|
||||
// RA[32:63] <- (RS)[32:63]
|
||||
// RA[0:31] <- i32.s
|
||||
Value* rt = f.LoadGPR(i.X.RT);
|
||||
rt = f.SignExtend(f.Truncate(rt, INT32_TYPE), INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, rt);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, rt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -101,7 +109,7 @@ RA <- EXTS_32_to_64((RS)[32:63])
|
||||
|
||||
- **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.
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:399`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.**
|
||||
|
||||
@@ -61,13 +61,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -75,17 +77,18 @@ instruction-stream synchronisation — discards speculative state.
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="isync"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:759`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_memory.cc#L759)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:99`](../../../crates/sylpheed-ppc/src/opcode.rs#L99)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:829`](../../../crates/sylpheed-ppc/src/decoder.rs#L829)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_isync(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// XEINSTRNOTIMPLEMENTED();
|
||||
f.Nop();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -97,7 +100,7 @@ instruction-stream synchronisation — discards speculative state.
|
||||
- **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.
|
||||
- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,23 +83,27 @@ RT <- ((RA) * (RB))[0:63] ; high 64 of unsigned 64×64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhdux"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:311`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L311)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:188`](../../../crates/sylpheed-ppc/src/opcode.rs#L188)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:975`](../../../crates/sylpheed-ppc/src/decoder.rs#L975)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulhdux(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ((RA) × (RB) as 128)[0:63]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
Value* v =
|
||||
f.MulHi(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB), ARITHMETIC_UNSIGNED);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,7 +113,7 @@ RT <- ((RA) * (RB))[0:63] ; high 64 of unsigned 64×64
|
||||
|
||||
- **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.
|
||||
- **Xenia uses native `u128`.** [`interpreter.rs:284`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,23 +83,26 @@ RT <- ((RA) * (RB))[0:63] ; high 64 of signed 64×64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhdx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:297`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L297)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:189`](../../../crates/sylpheed-ppc/src/opcode.rs#L189)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:979`](../../../crates/sylpheed-ppc/src/decoder.rs#L979)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulhdx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ((RA) × (RB) as 128)[0:63]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
Value* v = f.MulHi(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,8 +112,8 @@ RT <- ((RA) * (RB))[0:63] ; high 64 of signed 64×64
|
||||
|
||||
- **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)`).
|
||||
- **Xenia widens to `i128` natively.** [`interpreter.rs:275`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,25 +83,33 @@ RT <- high_32_of_unsigned_multiply((RA)[32:63], (RB)[32:63]) zero-extended to 64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhwux"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:347`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L347)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:190`](../../../crates/sylpheed-ppc/src/opcode.rs#L190)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:977`](../../../crates/sylpheed-ppc/src/decoder.rs#L977)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulhwux(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT[32:64] <- ((RA)[32:63] × (RB)[32:63])[0:31]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
Value* ratrunc =
|
||||
f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* rbtrunc =
|
||||
f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* v = f.Shr(f.Mul(ratrunc, rbtrunc, ARITHMETIC_UNSIGNED), 32);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,10 +117,10 @@ RT <- high_32_of_unsigned_multiply((RA)[32:63], (RB)[32:63]) zero-extended to 64
|
||||
|
||||
## 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)).
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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`.
|
||||
- **`Rc=1` CR0 update.** Uses `as i32 as i64` ([`interpreter.rs:234`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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.
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,25 +83,33 @@ RT <- high_32_of_signed_multiply((RA)[32:63], (RB)[32:63]) sign-extended to 64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulhwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:326`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L326)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:191`](../../../crates/sylpheed-ppc/src/opcode.rs#L191)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:980`](../../../crates/sylpheed-ppc/src/decoder.rs#L980)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulhwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT[32:64] <- ((RA)[32:63] × (RB)[32:63])[0:31]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
Value* ratrunc =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* rbtrunc =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);
|
||||
|
||||
Value* v = f.Sha(f.Mul(ratrunc, rbtrunc), 32);
|
||||
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,11 +117,11 @@ RT <- high_32_of_signed_multiply((RA)[32:63], (RB)[32:63]) sign-extended to 64
|
||||
|
||||
## 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`).
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) `& 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.
|
||||
- **`Rc=1` CR0 update.** [`interpreter.rs:225`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,26 +86,26 @@ RT <- ((RA) * (RB))[64:127] ; low 64 of signed 64×64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulldx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:368`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L368)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:192`](../../../crates/sylpheed-ppc/src/opcode.rs#L192)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:987`](../../../crates/sylpheed-ppc/src/decoder.rs#L987)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulldx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ((RA) × (RB))[64:127]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
Value* v = f.Mul(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -113,8 +115,8 @@ RT <- ((RA) * (RB))[64:127] ; low 64 of signed 64×64
|
||||
|
||||
- **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.
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) has no `oe()` branch).
|
||||
- **`Rc=1` CR0 update is correctly 64-bit.** [`interpreter.rs:269`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,22 +78,20 @@ RT <- ((RA) * EXTS(SIMM))[64:127]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mulli"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:382`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L382)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:193`](../../../crates/sylpheed-ppc/src/opcode.rs#L193)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:447`](../../../crates/sylpheed-ppc/src/decoder.rs#L447)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mulli(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// prod[0:127] <- (RA) × EXTS(SI)
|
||||
// RT <- prod[64:127]
|
||||
Value* v = f.Mul(f.LoadGPR(i.D.RA), f.LoadConstantInt64(XEEXTS16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -99,7 +99,7 @@ RT <- ((RA) * EXTS(SIMM))[64:127]
|
||||
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) — 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.
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,29 +86,28 @@ RT <- ((RA)[32:63]) * ((RB)[32:63]) ; signed 32×32 → 64
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mullwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:390`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L390)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:194`](../../../crates/sylpheed-ppc/src/opcode.rs#L194)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:989`](../../../crates/sylpheed-ppc/src/decoder.rs#L989)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mullwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- (RA)[32:63] × (RB)[32:63]
|
||||
if (i.XO.OE) {
|
||||
// With XER update.
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
}
|
||||
Value* v = f.Mul(
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE),
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,22 +82,22 @@ RA <- ~((RS) & (RB))
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="nandx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:753`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L753)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:196`](../../../crates/sylpheed-ppc/src/opcode.rs#L196)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:927`](../../../crates/sylpheed-ppc/src/decoder.rs#L927)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_nandx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- ¬((RS) & (RB))
|
||||
Value* ra = f.Not(f.And(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,7 +109,7 @@ RA <- ~((RS) & (RB))
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:377`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -69,13 +69,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -83,29 +85,33 @@ RT <- ~(RA) + 1
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="negx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:406`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L406)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:197`](../../../crates/sylpheed-ppc/src/opcode.rs#L197)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:981`](../../../crates/sylpheed-ppc/src/decoder.rs#L981)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_negx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + 1
|
||||
if (i.XO.OE) {
|
||||
// Stub implementation.
|
||||
// TODO: Handle overflow flag for XER.
|
||||
// NOTE: 535507D4 (Raiden Fighters Aces) never seems to rely on this
|
||||
// behavior, despite having OE set.
|
||||
Value* v = f.LoadGPR(i.XO.RA);
|
||||
if (v->AsUint64() == 0x8000000000000000) {
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Value* v = f.Neg(f.LoadGPR(i.XO.RA));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -114,9 +120,9 @@ RT <- ~(RA) + 1
|
||||
## 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).
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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).
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:204`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). 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).
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,22 +82,22 @@ RA <- ~((RS) | (RB))
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="norx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:763`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L763)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:198`](../../../crates/sylpheed-ppc/src/opcode.rs#L198)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:892`](../../../crates/sylpheed-ppc/src/decoder.rs#L892)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_norx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- ¬((RS) | (RB))
|
||||
Value* ra = f.Not(f.Or(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,7 +109,7 @@ RA <- ~((RS) | (RB))
|
||||
- **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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:372`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,22 +82,22 @@ RA <- (RS) | ~(RB)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="orcx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:800`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L800)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:199`](../../../crates/sylpheed-ppc/src/opcode.rs#L199)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:922`](../../../crates/sylpheed-ppc/src/decoder.rs#L922)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_orcx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) | ¬(RB)
|
||||
Value* ra = f.Or(f.LoadGPR(i.X.RT), f.Not(f.LoadGPR(i.X.RB)));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -108,7 +110,7 @@ RA <- (RS) | ~(RB)
|
||||
- **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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:362`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). Because `~RB` typically has high bits set, `orc.` results often appear `LT` in the truncated CR0.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,18 +78,23 @@ RA <- (RS) | (0x0000 || UIMM)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="ori"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:810`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L810)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:200`](../../../crates/sylpheed-ppc/src/opcode.rs#L200)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:462`](../../../crates/sylpheed-ppc/src/decoder.rs#L462)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::ori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_ori(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) | (i48.0 || UI)
|
||||
if (!i.D.RA && !i.D.RT && !i.D.DS) {
|
||||
f.Nop();
|
||||
return 0;
|
||||
}
|
||||
Value* ra = f.Or(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -99,7 +106,7 @@ RA <- (RS) | (0x0000 || UIMM)
|
||||
- **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`.
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:330`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — 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
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,18 +78,20 @@ RA <- (RS) | (UIMM || 0x0000)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="oris"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:821`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L821)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:201`](../../../crates/sylpheed-ppc/src/opcode.rs#L201)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:463`](../../../crates/sylpheed-ppc/src/decoder.rs#L463)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::oris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] | ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_oris(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) | (i32.0 || UI || i16.0)
|
||||
Value* ra =
|
||||
f.Or(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -98,7 +102,7 @@ RA <- (RS) | (UIMM || 0x0000)
|
||||
- **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).
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:334`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **No `XER`, no `CR` effect.** Pure register OR.
|
||||
- **`RA = 0` reads `r0`** (not literal zero); see [`ori`](ori.md).
|
||||
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,20 +82,39 @@ RA <- (RS) | (RB)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="orx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:773`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L773)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:202`](../../../crates/sylpheed-ppc/src/opcode.rs#L202)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:924`](../../../crates/sylpheed-ppc/src/decoder.rs#L924)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_orx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) | (RB)
|
||||
if (i.X.RT == i.X.RB && i.X.RT == i.X.RA && !i.X.Rc) {
|
||||
// chrispy: this special version of orx is db16cyc and is heavily used in
|
||||
// spinlocks. since we do not emit any code for this we end up wasting a ton
|
||||
// of power
|
||||
if (i.code == 0x7FFFFB78) {
|
||||
f.DelayExecution();
|
||||
} else {
|
||||
// Sometimes used as no-op.
|
||||
f.Nop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Value* ra;
|
||||
if (i.X.RT == i.X.RB) {
|
||||
ra = f.LoadGPR(i.X.RT);
|
||||
} else {
|
||||
ra = f.Or(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
|
||||
}
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -105,7 +126,7 @@ RA <- (RS) | (RB)
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:357`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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
|
||||
|
||||
@@ -62,28 +62,26 @@ rldcl[Rc] [RA], [RS], [RB], [MB]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -91,24 +89,37 @@ rldcl[Rc] [RA], [RS], [RB], [MB]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldclx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:856`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L856)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:204`](../../../crates/sylpheed-ppc/src/opcode.rs#L204)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:848`](../../../crates/sylpheed-ppc/src/decoder.rs#L848)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldclx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- rB[58:63]
|
||||
// r <- ROTL[64](rS, n)
|
||||
// b <- mb[5] || mb[0:4]
|
||||
// m <- MASK(b, 63)
|
||||
// rA <- r & m
|
||||
Value* n = f.And(f.Truncate(f.LoadGPR(i.MDS.RB), INT8_TYPE),
|
||||
f.LoadConstantInt8(0x3F));
|
||||
|
||||
uint32_t mb = (i.MDS.MB5 << 5) | i.MDS.MB;
|
||||
uint64_t m = XEMASK(mb, 63);
|
||||
Value* v = f.LoadGPR(i.MDS.RT);
|
||||
|
||||
v = f.RotateLeft(v, n);
|
||||
if (m != 0xFFFFFFFFFFFFFFFF) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
|
||||
f.StoreGPR(i.MDS.RA, v);
|
||||
if (i.MDS.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,9 +129,9 @@ rldcl[Rc] [RA], [RS], [RB], [MB]
|
||||
|
||||
- **`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.
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:592`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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).
|
||||
|
||||
|
||||
@@ -62,28 +62,26 @@ rldcr[Rc] [RA], [RS], [RB], [ME]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -91,24 +89,37 @@ rldcr[Rc] [RA], [RS], [RB], [ME]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldcrx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:881`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L881)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:205`](../../../crates/sylpheed-ppc/src/opcode.rs#L205)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:849`](../../../crates/sylpheed-ppc/src/decoder.rs#L849)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldcrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- rB[58:63]
|
||||
// r <- ROTL[64](rS, n)
|
||||
// b <- mb[5] || mb[0:4]
|
||||
// m <- MASK(0, b)
|
||||
// rA <- r & m
|
||||
Value* n = f.And(f.Truncate(f.LoadGPR(i.MDS.RB), INT8_TYPE),
|
||||
f.LoadConstantInt8(0x3F));
|
||||
|
||||
uint32_t mb = (i.MDS.MB5 << 5) | i.MDS.MB;
|
||||
uint64_t m = XEMASK(0, mb);
|
||||
Value* v = f.LoadGPR(i.MDS.RT);
|
||||
|
||||
v = f.RotateLeft(v, n);
|
||||
if (m != 0xFFFFFFFFFFFFFFFF) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
|
||||
f.StoreGPR(i.MDS.RA, v);
|
||||
if (i.MDS.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,9 +129,9 @@ rldcr[Rc] [RA], [RS], [RB], [ME]
|
||||
|
||||
- **`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.
|
||||
- **`ME` is a split 6-bit field.** Same swap-decoded layout as `MB` in `rldclx`: `(instr.mb() << 1) | ((raw >> 1) & 1)` ([`interpreter.rs:597`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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)).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:602`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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.
|
||||
|
||||
|
||||
@@ -63,28 +63,26 @@ rldicl[Rc] [RA], [RS], [SH], [MB]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,24 +90,40 @@ rldicl[Rc] [RA], [RS], [SH], [MB]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldiclx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:929`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L929)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:206`](../../../crates/sylpheed-ppc/src/opcode.rs#L206)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:843`](../../../crates/sylpheed-ppc/src/decoder.rs#L843)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldiclx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- sh[5] || sh[0:4]
|
||||
// r <- ROTL64((RS), n)
|
||||
// b <- mb[5] || mb[0:4]
|
||||
// m <- MASK(b, 63)
|
||||
// RA <- r & m
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
uint64_t m = XEMASK(mb, 63);
|
||||
Value* v = f.LoadGPR(i.MD.RT);
|
||||
if (sh == 64 - mb) {
|
||||
// srdi == rldicl ra,rs,64-n,n
|
||||
v = f.Shr(v, int8_t(mb));
|
||||
} else {
|
||||
if (sh) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
|
||||
}
|
||||
if (m != 0xFFFFFFFFFFFFFFFF) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
}
|
||||
f.StoreGPR(i.MD.RA, v);
|
||||
if (i.MD.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -124,7 +138,7 @@ rldicl[Rc] [RA], [RS], [SH], [MB]
|
||||
- `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)).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:551`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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.
|
||||
|
||||
|
||||
@@ -63,28 +63,26 @@ rldicr[Rc] [RA], [RS], [SH], [ME]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,24 +90,40 @@ rldicr[Rc] [RA], [RS], [SH], [ME]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldicrx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:957`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L957)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:207`](../../../crates/sylpheed-ppc/src/opcode.rs#L207)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:844`](../../../crates/sylpheed-ppc/src/decoder.rs#L844)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldicrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- sh[5] || sh[0:4]
|
||||
// r <- ROTL64((RS), n)
|
||||
// e <- me[5] || me[0:4]
|
||||
// m <- MASK(0, e)
|
||||
// RA <- r & m
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
uint64_t m = XEMASK(0, mb);
|
||||
Value* v = f.LoadGPR(i.MD.RT);
|
||||
if (mb == 63 - sh) {
|
||||
// sldi == rldicr ra,rs,n,63-n
|
||||
v = f.Shl(v, int8_t(sh));
|
||||
} else {
|
||||
if (sh) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
|
||||
}
|
||||
if (m != 0xFFFFFFFFFFFFFFFF) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
}
|
||||
f.StoreGPR(i.MD.RA, v);
|
||||
if (i.MD.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -124,7 +138,7 @@ rldicr[Rc] [RA], [RS], [SH], [ME]
|
||||
- `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)).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** Uses `as i64` directly ([`interpreter.rs:561`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **No `XER` effect.**
|
||||
- **Heavily emitted by 64-bit code generators** for left-shift-and-clear sequences. Recognising the simplified mnemonics aids disassembly.
|
||||
|
||||
|
||||
@@ -63,28 +63,26 @@ rldic[Rc] [RA], [RS], [SH], [MB]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,24 +90,35 @@ rldic[Rc] [RA], [RS], [SH], [MB]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldicx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:906`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L906)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:208`](../../../crates/sylpheed-ppc/src/opcode.rs#L208)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:845`](../../../crates/sylpheed-ppc/src/decoder.rs#L845)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldicx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- sh[5] || sh[0:4]
|
||||
// r <- ROTL64(RS, n)
|
||||
// b <- mb[5] || mb[0:4]
|
||||
// m <- MASK(b, ~n)
|
||||
// RA <- r & m
|
||||
// TODO: Check if this makes any sense. It works in 535507D4,
|
||||
// but it's the only title I could find that uses this instruction.
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
uint64_t m = XEMASK(mb, ~sh);
|
||||
Value* v = f.LoadGPR(i.MD.RT);
|
||||
if (sh) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
|
||||
}
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
f.StoreGPR(i.MD.RA, v);
|
||||
if (i.MD.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,11 +127,11 @@ rldic[Rc] [RA], [RS], [SH], [MB]
|
||||
## 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.
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) 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.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:571`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) uses `as i64` directly.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
@@ -63,28 +63,26 @@ rldimi[Rc] [RA], [RS], [SH], [MB]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,24 +90,37 @@ rldimi[Rc] [RA], [RS], [SH], [MB]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rldimix"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:985`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L985)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:209`](../../../crates/sylpheed-ppc/src/opcode.rs#L209)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:846`](../../../crates/sylpheed-ppc/src/decoder.rs#L846)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rldimix(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- sh[5] || sh[0:4]
|
||||
// r <- ROTL64((RS), n)
|
||||
// b <- me[5] || me[0:4]
|
||||
// m <- MASK(b, ¬n)
|
||||
// RA <- (r & m) | ((RA)&¬m)
|
||||
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
|
||||
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
|
||||
uint64_t m = XEMASK(mb, ~sh);
|
||||
Value* v = f.LoadGPR(i.MD.RT);
|
||||
if (sh) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
|
||||
}
|
||||
if (m != 0xFFFFFFFFFFFFFFFF) {
|
||||
Value* ra = f.LoadGPR(i.MD.RA);
|
||||
v = f.Or(f.And(v, f.LoadConstantUint64(m)),
|
||||
f.And(ra, f.LoadConstantUint64(~m)));
|
||||
}
|
||||
f.StoreGPR(i.MD.RA, v);
|
||||
if (i.MD.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,7 +129,7 @@ rldimi[Rc] [RA], [RS], [SH], [MB]
|
||||
## 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.
|
||||
- **Mask is `MASK_LEFT(MB) AND MASK_RIGHT(63 - SH)`** ([`interpreter.rs:578`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) — 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.
|
||||
|
||||
@@ -63,28 +63,26 @@ rlwimi[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,27 +90,37 @@ rlwimi[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwimix"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1010`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1010)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:210`](../../../crates/sylpheed-ppc/src/opcode.rs#L210)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:459`](../../../crates/sylpheed-ppc/src/decoder.rs#L459)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rlwimix(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- SH
|
||||
// r <- ROTL32((RS)[32:63], n)
|
||||
// m <- MASK(MB+32, ME+32)
|
||||
// RA <- r&m | (RA)&¬m
|
||||
Value* v = f.LoadGPR(i.M.RT);
|
||||
// (x||x)
|
||||
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
|
||||
if (i.M.SH) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(i.M.SH));
|
||||
}
|
||||
// Compiler sometimes masks with 0xFFFFFFFF (identity) - avoid the work here
|
||||
// as our truncation/zero-extend does it for us.
|
||||
uint64_t m = XEMASK(i.M.MB + 32, i.M.ME + 32);
|
||||
if (m != 0xFFFFFFFFFFFFFFFFull) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
v = f.Or(v, f.And(f.LoadGPR(i.M.RA), f.LoadConstantUint64(~m)));
|
||||
f.StoreGPR(i.M.RA, v);
|
||||
if (i.M.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -120,11 +128,11 @@ rlwimi[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
|
||||
## 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).
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:531`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). 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
|
||||
|
||||
@@ -63,28 +63,26 @@ rlwinm[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,26 +90,67 @@ rlwinm[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwinmx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1046`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1046)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:211`](../../../crates/sylpheed-ppc/src/opcode.rs#L211)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:460`](../../../crates/sylpheed-ppc/src/decoder.rs#L460)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rlwinmx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- SH
|
||||
// r <- ROTL32((RS)[32:63], n)
|
||||
// m <- MASK(MB+32, ME+32)
|
||||
// RA <- r & m
|
||||
Value* v = f.LoadGPR(i.M.RT);
|
||||
|
||||
unsigned rotation = i.M.SH;
|
||||
|
||||
uint64_t m = XEMASK(i.M.MB + 32, i.M.ME + 32);
|
||||
|
||||
// in uint32 range (so no register concat/truncate/zx needed) and no rotation
|
||||
if (m < (1ULL << 32) && (rotation == 0)) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
// masks out all the bits that are rotated in from the right, so just do a
|
||||
// shift + and. the and with 0xFFFFFFFF is done instead of a truncate/zx
|
||||
// because we have a special case for it in the emitters that will just do a
|
||||
// single insn (mov reg32, lowpartofreg64), otherwise we generate
|
||||
// significantly more code from setting up the opnds of the truncate/zx
|
||||
else if (InstrCheck_rlx_only_needs_low(rotation, m)) {
|
||||
// this path is taken for like 90% of all rlwinms
|
||||
v = f.And(f.Shl(v, rotation), f.LoadConstantUint64(0xFFFFFFFF));
|
||||
}
|
||||
|
||||
else {
|
||||
// (x||x)
|
||||
// cs: changed this to mask with UINT32_MAX instead of doing the
|
||||
// truncate/extend, this generates better code in the backend and is easier
|
||||
// to do analysis on
|
||||
v = f.And(v, f.LoadConstantUint64(0xFFFFFFFF));
|
||||
|
||||
v = f.Or(f.Shl(v, 32), v);
|
||||
|
||||
// TODO(benvanik): optimize srwi
|
||||
// TODO(benvanik): optimize slwi
|
||||
// The compiler will generate a bunch of these for the special case of SH=0.
|
||||
// Which seems to just select some bits and set cr0 for use with a branch.
|
||||
// We can detect this and do less work.
|
||||
if (i.M.SH) {
|
||||
v = f.RotateLeft(v, f.LoadConstantInt8(rotation));
|
||||
}
|
||||
// Compiler sometimes masks with 0xFFFFFFFF (identity) - avoid the work here
|
||||
// as our truncation/zero-extend does it for us.
|
||||
if (m != 0xFFFFFFFFFFFFFFFFull) {
|
||||
v = f.And(v, f.LoadConstantUint64(m));
|
||||
}
|
||||
}
|
||||
f.StoreGPR(i.M.RA, v);
|
||||
if (i.M.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -128,7 +167,7 @@ rlwinm[Rc] [RA], [RS], [SH], [MB], [ME]
|
||||
- `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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:518`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). Since the result fits in 32 bits, the truncation matches spec exactly.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
@@ -63,28 +63,26 @@ rlwnm[Rc] [RA], [RS], [RB], [MB], [ME]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -92,26 +90,31 @@ rlwnm[Rc] [RA], [RS], [RB], [MB], [ME]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="rlwnmx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1101`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1101)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:212`](../../../crates/sylpheed-ppc/src/opcode.rs#L212)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:461`](../../../crates/sylpheed-ppc/src/decoder.rs#L461)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_rlwnmx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- (RB)[59:63]
|
||||
// r <- ROTL32((RS)[32:63], n)
|
||||
// m <- MASK(MB+32, ME+32)
|
||||
// RA <- r & m
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.M.SH), INT8_TYPE), f.LoadConstantInt8(0x1F));
|
||||
Value* v = f.LoadGPR(i.M.RT);
|
||||
// (x||x)
|
||||
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
|
||||
v = f.RotateLeft(v, sh);
|
||||
v = f.And(v, f.LoadConstantUint64(XEMASK(i.M.MB + 32, i.M.ME + 32)));
|
||||
f.StoreGPR(i.M.RA, v);
|
||||
if (i.M.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -119,12 +122,12 @@ rlwnm[Rc] [RA], [RS], [RB], [MB], [ME]
|
||||
|
||||
## 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)).
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:540`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — harmless because the result already fits in 32 bits.
|
||||
- **No `XER` effect.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,22 +83,31 @@ RA <- ((RS) << n) if n < 64 else 0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sldx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1122`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1122)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:216`](../../../crates/sylpheed-ppc/src/opcode.rs#L216)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:874`](../../../crates/sylpheed-ppc/src/decoder.rs#L874)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_sldx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- (RB)[58:63]
|
||||
// r <- ROTL64((RS), n)
|
||||
// if (RB)[57] = 0 then
|
||||
// m <- MASK(0, 63-n)
|
||||
// else
|
||||
// m <- i64.0
|
||||
// RA <- r & m
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
|
||||
Value* v = f.Select(f.IsTrue(f.Shr(sh, 6)), f.LoadZeroInt64(),
|
||||
f.Shl(f.LoadGPR(i.X.RT), sh));
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -105,9 +116,9 @@ RA <- ((RS) << n) if n < 64 else 0
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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`).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:467`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.**
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,24 +83,32 @@ RA <- ((RS) << n) & 0x0000_0000_FFFF_FFFF if n < 32 else 0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="slwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1141`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1141)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:217`](../../../crates/sylpheed-ppc/src/opcode.rs#L217)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:872`](../../../crates/sylpheed-ppc/src/decoder.rs#L872)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_slwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- (RB)[59:63]
|
||||
// r <- ROTL32((RS)[32:63], n)
|
||||
// if (RB)[58] = 0 then
|
||||
// m <- MASK(32, 63-n)
|
||||
// else
|
||||
// m <- i64.0
|
||||
// RA <- r & m
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
|
||||
Value* v = f.Select(f.IsTrue(f.Shr(sh, 5)), f.LoadZeroInt32(),
|
||||
f.Shl(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE), sh));
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,10 +117,10 @@ RA <- ((RS) << n) & 0x0000_0000_FFFF_FFFF if n < 32 else 0
|
||||
## 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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits** ([`interpreter.rs:420`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). Since the high 32 bits are zero, this matches spec exactly.
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
@@ -69,13 +69,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -83,28 +85,44 @@ CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sradix"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1230`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1230)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:218`](../../../crates/sylpheed-ppc/src/opcode.rs#L218)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:858`](../../../crates/sylpheed-ppc/src/decoder.rs#L858)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_sradix(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- sh[5] || sh[0-4]
|
||||
// r <- ROTL[64](rS, 64 - n)
|
||||
// m ← MASK(n, 63)
|
||||
// S ← rS[0]
|
||||
// rA <- (r & m) | (((64)S) & ¬ m)
|
||||
// XER[CA] <- S & ((r & ¬ m) ¦ 0)
|
||||
// if n == 0: rA <- rS, XER[CA] = 0
|
||||
// if n >= 64: rA <- 64 sign bits of rS, XER[CA] = sign bit of rS
|
||||
Value* v = f.LoadGPR(i.XS.RT);
|
||||
int8_t sh = (i.XS.SH5 << 5) | i.XS.SH;
|
||||
|
||||
// CA is set if any bits are shifted out of the right and if the result
|
||||
// is negative.
|
||||
if (sh) {
|
||||
uint64_t mask = XEMASK(64 - sh, 63);
|
||||
Value* ca = f.And(f.Truncate(f.Shr(v, 63), INT8_TYPE),
|
||||
f.IsTrue(f.And(v, f.LoadConstantUint64(mask))));
|
||||
f.StoreCA(ca);
|
||||
|
||||
v = f.Sha(v, sh);
|
||||
} else {
|
||||
f.StoreCA(f.LoadZeroInt8());
|
||||
}
|
||||
|
||||
f.StoreGPR(i.XS.RA, v);
|
||||
if (i.XS.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -113,10 +131,10 @@ CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
## 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.
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). Range `0..63`.
|
||||
- **`SH = 0`** is a no-op (sign-extends `RS` to itself), and explicitly clears `XER[CA]` ([`interpreter.rs:498`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:506`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **Idiom: `sradi rA, rS, n; addze rA, rA`** — signed integer divide by `2^n` rounded toward zero (the textbook PPC sequence).
|
||||
- **No `OE` bit.**
|
||||
|
||||
|
||||
@@ -69,13 +69,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -83,31 +85,41 @@ CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sradx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1201`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1201)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:219`](../../../crates/sylpheed-ppc/src/opcode.rs#L219)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:956`](../../../crates/sylpheed-ppc/src/decoder.rs#L956)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_sradx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- rB[58-63]
|
||||
// r <- ROTL[64](rS, 64 - n)
|
||||
// if rB[57] = 0 then m ← MASK(n, 63)
|
||||
// else m ← (64)0
|
||||
// S ← rS[0]
|
||||
// rA <- (r & m) | (((64)S) & ¬ m)
|
||||
// XER[CA] <- S & ((r & ¬ m) ¦ 0)
|
||||
// if n == 0: rA <- rS, XER[CA] = 0
|
||||
// if n >= 64: rA <- 64 sign bits of rS, XER[CA] = sign bit of rS
|
||||
Value* rt = f.LoadGPR(i.X.RT);
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
|
||||
Value* clamp_sh = f.Min(sh, f.LoadConstantInt8(0x3F));
|
||||
Value* v = f.Sha(rt, clamp_sh);
|
||||
|
||||
// CA is set if any bits are shifted out of the right and if the result
|
||||
// is negative.
|
||||
Value* ca =
|
||||
f.And(f.IsTrue(f.Shr(rt, 63)), f.CompareNE(f.Shl(v, clamp_sh), rt));
|
||||
f.StoreCA(ca);
|
||||
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -117,9 +129,9 @@ CA <- (RS signed < 0) && any_bit_shifted_out
|
||||
|
||||
- **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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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).
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:489`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **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 `-∞`.
|
||||
|
||||
|
||||
@@ -68,13 +68,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -82,29 +84,51 @@ CA <- (RS[32] signed) && any_low_bit_shifted_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srawix"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1291`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1291)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:220`](../../../crates/sylpheed-ppc/src/opcode.rs#L220)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:958`](../../../crates/sylpheed-ppc/src/decoder.rs#L958)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_srawix(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- SH
|
||||
// r <- ROTL32((RS)[32:63], 64-n)
|
||||
// m <- MASK(n+32, 63)
|
||||
// s <- (RS)[32]
|
||||
// RA <- r&m | (i64.s)&¬m
|
||||
// CA <- s & ((r&¬m)[32:63]≠0)
|
||||
// if n == 0: rA <- sign_extend(rS), XER[CA] = 0
|
||||
// if n >= 32: rA <- 64 sign bits of rS, XER[CA] = sign bit of lo_32(rS)
|
||||
Value* v = f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE);
|
||||
Value* ca;
|
||||
if (!i.X.RB) {
|
||||
// No shift, just a fancy sign extend and CA clearer.
|
||||
v = f.SignExtend(v, INT64_TYPE);
|
||||
ca = f.LoadZeroInt8();
|
||||
} else {
|
||||
// CA is set if any bits are shifted out of the right and if the result
|
||||
// is negative.
|
||||
uint32_t mask = (uint32_t)XEMASK(64 - i.X.RB, 63);
|
||||
|
||||
if (mask == 1) {
|
||||
ca = f.And(f.CompareSLT(v, f.LoadConstantInt32(0)),
|
||||
f.Truncate(v, INT8_TYPE));
|
||||
|
||||
} else {
|
||||
ca = f.And(f.CompareSLT(v, f.LoadConstantInt32(0)),
|
||||
f.IsTrue(f.And(v, f.LoadConstantUint32(mask))));
|
||||
}
|
||||
|
||||
v = f.Sha(v, (int8_t)i.X.RB), v = f.SignExtend(v, INT64_TYPE);
|
||||
}
|
||||
f.StoreCA(ca);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -117,7 +141,7 @@ CA <- (RS[32] signed) && any_low_bit_shifted_out
|
||||
- **`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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:457`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — matches spec because the sign-extended result has consistent low/high 32-bit signs.
|
||||
- **No `OE` bit.**
|
||||
|
||||
## Related Instructions
|
||||
|
||||
@@ -69,13 +69,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -83,33 +85,41 @@ CA <- 1 if (signed RS < 0) && any_bit_shifted_out else 0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srawx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1262`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1262)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:221`](../../../crates/sylpheed-ppc/src/opcode.rs#L221)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:955`](../../../crates/sylpheed-ppc/src/decoder.rs#L955)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_srawx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- rB[59-63]
|
||||
// r <- ROTL32((RS)[32:63], 64-n)
|
||||
// m <- MASK(n+32, 63)
|
||||
// s <- (RS)[32]
|
||||
// RA <- r&m | (i64.s)&¬m
|
||||
// CA <- s & ((r&¬m)[32:63]≠0)
|
||||
// if n == 0: rA <- sign_extend(rS), XER[CA] = 0
|
||||
// if n >= 32: rA <- 64 sign bits of rS, XER[CA] = sign bit of lo_32(rS)
|
||||
Value* rt = f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE);
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
|
||||
Value* clamp_sh = f.Min(sh, f.LoadConstantInt8(0x1F));
|
||||
Value* v = f.Sha(rt, f.Min(sh, clamp_sh));
|
||||
|
||||
// CA is set if any bits are shifted out of the right and if the result
|
||||
// is negative.
|
||||
Value* ca =
|
||||
f.And(f.IsTrue(f.Shr(rt, 31)), f.CompareNE(f.Shl(v, clamp_sh), rt));
|
||||
f.StoreCA(ca);
|
||||
|
||||
v = f.SignExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -118,10 +128,10 @@ CA <- 1 if (signed RS < 0) && any_bit_shifted_out else 0
|
||||
## 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)).
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **`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.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:443`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — 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.**
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,22 +83,31 @@ RA <- ((RS) >> n) if n < 64 else 0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srdx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1161`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1161)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:222`](../../../crates/sylpheed-ppc/src/opcode.rs#L222)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:936`](../../../crates/sylpheed-ppc/src/decoder.rs#L936)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_srdx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- (RB)[58:63]
|
||||
// r <- ROTL64((RS), 64-n)
|
||||
// if (RB)[57] = 0 then
|
||||
// m <- MASK(n, 63)
|
||||
// else
|
||||
// m <- i64.0
|
||||
// RA <- r & m
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
|
||||
Value* v = f.Select(f.IsTrue(f.And(sh, f.LoadConstantInt8(0x40))),
|
||||
f.LoadZeroInt64(), f.Shr(f.LoadGPR(i.X.RT), sh));
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -105,9 +116,9 @@ RA <- ((RS) >> n) if n < 64 else 0
|
||||
## 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)).
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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`.
|
||||
- **`Rc=1` CR0 is correctly 64-bit.** [`interpreter.rs:475`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). 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.
|
||||
|
||||
|
||||
@@ -67,13 +67,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -81,24 +83,33 @@ RA <- ((RS)[32:63] >> n) zero-extended if n < 32 else 0
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="srwx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:1180`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L1180)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:223`](../../../crates/sylpheed-ppc/src/opcode.rs#L223)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:935`](../../../crates/sylpheed-ppc/src/decoder.rs#L935)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_srwx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- (RB)[59:63]
|
||||
// r <- ROTL32((RS)[32:63], 64-n)
|
||||
// if (RB)[58] = 0 then
|
||||
// m <- MASK(n+32, 63)
|
||||
// else
|
||||
// m <- i64.0
|
||||
// RA <- r & m
|
||||
Value* sh =
|
||||
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
|
||||
Value* v =
|
||||
f.Select(f.IsTrue(f.And(sh, f.LoadConstantInt8(0x20))), f.LoadZeroInt32(),
|
||||
f.Shr(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE), sh));
|
||||
v = f.ZeroExtend(v, INT64_TYPE);
|
||||
f.StoreGPR(i.X.RA, v);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -107,9 +118,9 @@ RA <- ((RS)[32:63] >> n) zero-extended if n < 32 else 0
|
||||
## 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)).
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)).
|
||||
- **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`.
|
||||
- **`Rc=1` CR0 update truncates to 32 bits in xenia-rs.** [`interpreter.rs:428`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). 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.
|
||||
|
||||
|
||||
@@ -71,13 +71,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,32 +87,30 @@ CA <- carry_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfcx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:441`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L441)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:281`](../../../crates/sylpheed-ppc/src/opcode.rs#L281)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:974`](../../../crates/sylpheed-ppc/src/decoder.rs#L974)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subfcx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + (RB) + 1
|
||||
Value* ra = f.LoadGPR(i.XO.RA);
|
||||
Value* rb = f.LoadGPR(i.XO.RB);
|
||||
Value* v = f.Sub(rb, ra);
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow(EFLAGS??);
|
||||
} else {
|
||||
f.StoreCA(SubDidCarry(f, rb, ra));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -119,9 +119,9 @@ CA <- carry_out
|
||||
## 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.
|
||||
- **`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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) 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.`.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:160`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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`.
|
||||
|
||||
|
||||
@@ -71,13 +71,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,33 +87,30 @@ CA <- carry_out
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:468`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L468)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:282`](../../../crates/sylpheed-ppc/src/opcode.rs#L282)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:982`](../../../crates/sylpheed-ppc/src/decoder.rs#L982)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subfex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + (RB) + CA
|
||||
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
|
||||
Value* rb = f.LoadGPR(i.XO.RB);
|
||||
Value* v = f.AddWithCarry(not_ra, rb, f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
|
||||
} else {
|
||||
f.StoreCA(AddWithCarryDidCarry(f, not_ra, rb, f.LoadCA()));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -120,9 +119,9 @@ CA <- carry_out
|
||||
## 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.
|
||||
- **Carry-out predicate handles the boundary case.** Xenia computes `CA' = (rb > ra) || (rb == ra && CA != 0)` ([`interpreter.rs:170`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:173`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **`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).
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ subfic [RD], [RA], [SIMM]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,24 +84,21 @@ subfic [RD], [RA], [SIMM]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subficx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:459`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L459)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:283`](../../../crates/sylpheed-ppc/src/opcode.rs#L283)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:448`](../../../crates/sylpheed-ppc/src/decoder.rs#L448)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subficx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + EXTS(SI) + 1
|
||||
Value* ra = f.LoadGPR(i.D.RA);
|
||||
Value* v = f.Sub(f.LoadConstantInt64(XEEXTS16(i.D.DS)), ra);
|
||||
f.StoreGPR(i.D.RT, v);
|
||||
f.StoreCA(SubDidCarry(f, f.LoadConstantInt64(XEEXTS16(i.D.DS)), ra));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -113,7 +108,7 @@ subfic [RD], [RA], [SIMM]
|
||||
|
||||
- **`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.
|
||||
- **`XER[CA] = 1` when `SIMM >= RA`** (no borrow). Computed in xenia as `if imm >= ra` ([`interpreter.rs:73`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)) — 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.
|
||||
|
||||
@@ -64,28 +64,26 @@ subfme[OE][Rc] [RD], [RA]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -93,31 +91,30 @@ subfme[OE][Rc] [RD], [RA]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfmex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:486`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L486)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:284`](../../../crates/sylpheed-ppc/src/opcode.rs#L284)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:986`](../../../crates/sylpheed-ppc/src/decoder.rs#L986)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subfmex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + CA - 1
|
||||
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
|
||||
Value* v = f.AddWithCarry(not_ra, f.LoadConstantInt64(-1), f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
|
||||
} else {
|
||||
f.StoreCA(
|
||||
AddWithCarryDidCarry(f, not_ra, f.LoadConstantInt64(-1), f.LoadCA()));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -127,9 +124,9 @@ subfme[OE][Rc] [RD], [RA]
|
||||
|
||||
- **`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.
|
||||
- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:191`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:194`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **`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.
|
||||
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,29 +86,26 @@ RT <- ~(RA) + (RB) + 1 ; = (RB) − (RA)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:427`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L427)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:285`](../../../crates/sylpheed-ppc/src/opcode.rs#L285)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:978`](../../../crates/sylpheed-ppc/src/decoder.rs#L978)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subfx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + (RB) + 1
|
||||
Value* v = f.Sub(f.LoadGPR(i.XO.RB), f.LoadGPR(i.XO.RA));
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow(EFLAGS??);
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -64,28 +64,26 @@ subfze[OE][Rc] [RD], [RA]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -93,32 +91,29 @@ subfze[OE][Rc] [RD], [RA]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="subfzex"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:504`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L504)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:286`](../../../crates/sylpheed-ppc/src/opcode.rs#L286)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:984`](../../../crates/sylpheed-ppc/src/decoder.rs#L984)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_subfzex(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RT <- ¬(RA) + CA
|
||||
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
|
||||
Value* v = f.AddWithCarry(not_ra, f.LoadZeroInt64(), f.LoadCA());
|
||||
f.StoreGPR(i.XO.RT, v);
|
||||
if (i.XO.OE) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
|
||||
} else {
|
||||
f.StoreCA(AddWithCarryDidCarry(f, not_ra, f.LoadZeroInt64(), f.LoadCA()));
|
||||
}
|
||||
if (i.XO.Rc) {
|
||||
f.UpdateCR(0, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -128,9 +123,9 @@ subfze[OE][Rc] [RD], [RA]
|
||||
|
||||
- **`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.
|
||||
- **Carry-out predicate.** `CA' = (~RA != 0) || (CA != 0)` ([`interpreter.rs:180`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs)). 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).
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:183`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **`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.
|
||||
|
||||
|
||||
@@ -61,13 +61,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -75,17 +77,17 @@ multi-thread memory barrier (heavy). L=0 full sync; L=1 lightweight sync.
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sync"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_memory.cc:754`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_memory.cc#L754)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:288`](../../../crates/sylpheed-ppc/src/opcode.rs#L288)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:940`](../../../crates/sylpheed-ppc/src/decoder.rs#L940)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_sync(PPCHIRBuilder& f, const InstrData& i) {
|
||||
f.MemoryBarrier();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -97,7 +99,7 @@ multi-thread memory barrier (heavy). L=0 full sync; L=1 lightweight sync.
|
||||
- **`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.
|
||||
- **Xenia-rs is a no-op.** [`interpreter.rs:1267`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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.
|
||||
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,18 +78,19 @@ RA <- (RS) ^ (0x0000 || UIMM)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xori"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:839`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L839)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:497`](../../../crates/sylpheed-ppc/src/opcode.rs#L497)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:464`](../../../crates/sylpheed-ppc/src/decoder.rs#L464)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xori => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ (instr.uimm16() as u64);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_xori(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) XOR (i48.0 || UI)
|
||||
Value* ra = f.Xor(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -98,7 +101,7 @@ RA <- (RS) ^ (0x0000 || UIMM)
|
||||
- **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.
|
||||
- **64-bit operation in xenia-rs.** [`interpreter.rs:338`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) — 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).
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,18 +78,20 @@ RA <- (RS) ^ (UIMM || 0x0000)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xoris"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:846`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L846)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:498`](../../../crates/sylpheed-ppc/src/opcode.rs#L498)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:465`](../../../crates/sylpheed-ppc/src/decoder.rs#L465)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::xoris => {
|
||||
ctx.gpr[instr.ra()] = ctx.gpr[instr.rs()] ^ ((instr.uimm16() as u64) << 16);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_xoris(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) XOR (i32.0 || UI || i16.0)
|
||||
Value* ra =
|
||||
f.Xor(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
|
||||
f.StoreGPR(i.D.RA, ra);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -98,7 +102,7 @@ RA <- (RS) ^ (UIMM || 0x0000)
|
||||
- **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).
|
||||
- **64-bit operation.** [`interpreter.rs:342`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs).
|
||||
- **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.
|
||||
|
||||
@@ -66,13 +66,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -80,20 +82,22 @@ RA <- (RS) ^ (RB)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="xorx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:829`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L829)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:499`](../../../crates/sylpheed-ppc/src/opcode.rs#L499)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:913`](../../../crates/sylpheed-ppc/src/decoder.rs#L913)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_xorx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// RA <- (RS) XOR (RB)
|
||||
Value* ra = f.Xor(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
|
||||
f.StoreGPR(i.X.RA, ra);
|
||||
if (i.X.Rc) {
|
||||
f.UpdateCR(0, ra);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -106,7 +110,7 @@ RA <- (RS) ^ (RB)
|
||||
- **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.
|
||||
- **64-bit CR update on Xenon, 32-bit in xenia-rs.** [`interpreter.rs:367`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs) 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]`.**
|
||||
|
||||
|
||||
@@ -70,13 +70,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -84,34 +86,44 @@ if LK then LR <- CIA + 4
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcctrx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:250`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L250)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:22`](../../../crates/sylpheed-ppc/src/opcode.rs#L22)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:836`](../../../crates/sylpheed-ppc/src/decoder.rs#L836)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bcctrx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
```cpp
|
||||
int InstrEmit_bcctrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if cond_ok then
|
||||
// NIA <- CTR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
let cond_ok = (bo & 0b10000) != 0
|
||||
|| (ctx.get_cr_bit(bi) == ((bo & 0b01000) != 0));
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.XL.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool expect_true = !not_cond_ok;
|
||||
return InstrEmit_branch(f, "bcctrx", i.address, f.LoadCTR(), i.XL.LK, cond_ok,
|
||||
expect_true);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -84,37 +84,80 @@ 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bclrx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:282`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L282)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:23`](../../../crates/sylpheed-ppc/src/opcode.rs#L23)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:826`](../../../crates/sylpheed-ppc/src/decoder.rs#L826)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bclrx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
```cpp
|
||||
int InstrEmit_bclrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// NIA <- LR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
if bo & 0b00100 == 0 {
|
||||
ctx.ctr = ctx.ctr.wrapping_sub(1);
|
||||
}
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
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));
|
||||
Value* ctr_ok = NULL;
|
||||
if (select_bits(i.XL.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = f.LoadCTR();
|
||||
ctr = f.Sub(ctr, f.LoadConstantUint64(1));
|
||||
f.StoreCTR(ctr);
|
||||
// Ctr check.
|
||||
ctr = f.Truncate(ctr, INT32_TYPE);
|
||||
// TODO(benvanik): could do something similar to cond and avoid the
|
||||
// is_true/branch_true pairing.
|
||||
if (select_bits(i.XL.BO, 1, 1)) {
|
||||
ctr_ok = f.IsFalse(ctr);
|
||||
} else {
|
||||
ctr_ok = f.IsTrue(ctr);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.XL.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
bool expect_true = true;
|
||||
if (ctr_ok && cond_ok) {
|
||||
if (not_cond_ok) {
|
||||
cond_ok = f.IsFalse(cond_ok);
|
||||
}
|
||||
ok = f.And(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
expect_true = !not_cond_ok;
|
||||
}
|
||||
|
||||
return InstrEmit_branch(f, "bclrx", i.address, f.LoadLR(), i.XL.LK, ok,
|
||||
expect_true, true);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -75,13 +75,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -89,45 +91,89 @@ if LK then LR <- CIA + 4
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bcx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:173`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L173)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:24`](../../../crates/sylpheed-ppc/src/opcode.rs#L24)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:455`](../../../crates/sylpheed-ppc/src/decoder.rs#L455)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::bcx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
```cpp
|
||||
int InstrEmit_bcx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// if AA then
|
||||
// NIA <- EXTS(BD || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(BD || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// Decrement CTR if needed
|
||||
if bo & 0b00100 == 0 {
|
||||
ctx.ctr = ctx.ctr.wrapping_sub(1);
|
||||
}
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
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));
|
||||
Value* ctr_ok = NULL;
|
||||
if (select_bits(i.B.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = f.LoadCTR();
|
||||
ctr = f.Sub(ctr, f.LoadConstantUint64(1));
|
||||
f.StoreCTR(ctr);
|
||||
// Ctr check.
|
||||
ctr = f.Truncate(ctr, INT32_TYPE);
|
||||
// TODO(benvanik): could do something similar to cond and avoid the
|
||||
// is_true/branch_true pairing.
|
||||
if (select_bits(i.B.BO, 1, 1)) {
|
||||
ctr_ok = f.IsFalse(ctr);
|
||||
} else {
|
||||
ctr_ok = f.IsTrue(ctr);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Value* cond_ok = NULL;
|
||||
bool not_cond_ok = false;
|
||||
if (select_bits(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = f.LoadCRField(i.B.BI >> 2, i.B.BI & 3);
|
||||
cond_ok = cr;
|
||||
if (select_bits(i.B.BO, 3, 3)) {
|
||||
// Expect true.
|
||||
not_cond_ok = false;
|
||||
} else {
|
||||
// Expect false.
|
||||
not_cond_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
bool expect_true = true;
|
||||
if (ctr_ok && cond_ok) {
|
||||
if (not_cond_ok) {
|
||||
cond_ok = f.IsFalse(cond_ok);
|
||||
}
|
||||
ok = f.And(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
expect_true = !not_cond_ok;
|
||||
}
|
||||
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = (uint32_t)XEEXTS16(i.B.BD << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS16(i.B.BD << 2));
|
||||
}
|
||||
return InstrEmit_branch(f, "bcx", i.address, f.LoadConstantUint32(nia),
|
||||
i.B.LK, ok, expect_true);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -77,25 +77,31 @@ 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="bx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:154`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L154)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:25`](../../../crates/sylpheed-ppc/src/opcode.rs#L25)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:457`](../../../crates/sylpheed-ppc/src/decoder.rs#L457)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_bx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// if AA then
|
||||
// NIA <- EXTS(LI || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(LI || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
uint32_t nia;
|
||||
if (i.I.AA) {
|
||||
nia = (uint32_t)XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = (uint32_t)(i.address + XEEXTS26(i.I.LI << 2));
|
||||
}
|
||||
|
||||
return InstrEmit_branch(f, "bx", i.address, f.LoadConstantUint32(nia),
|
||||
i.I.LK);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -62,13 +62,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -76,28 +78,28 @@ system_call_exception(LEV)
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="sc"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:457`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L457)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:214`](../../../crates/sylpheed-ppc/src/opcode.rs#L214)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:456`](../../../crates/sylpheed-ppc/src/decoder.rs#L456)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_sc(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// Game code should only ever use LEV=0.
|
||||
// LEV=2 is to signify 'call import' from Xenia.
|
||||
// TODO(gibbed): syscalls!
|
||||
if (i.SC.LEV == 0) {
|
||||
f.CallExtern(f.builtins()->syscall_handler);
|
||||
return 0;
|
||||
}
|
||||
if (i.SC.LEV == 2) {
|
||||
f.CallExtern(f.function());
|
||||
return 0;
|
||||
}
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,49 +85,28 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="td"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:554`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L554)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:290`](../../../crates/sylpheed-ppc/src/opcode.rs#L290)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:884`](../../../crates/sylpheed-ppc/src/decoder.rs#L884)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_td(PPCHIRBuilder& f, const InstrData& i) {
|
||||
if (cvars::ignore_trap_instructions) {
|
||||
return 0;
|
||||
}
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
Value* ra = f.LoadGPR(i.X.RA);
|
||||
Value* rb = f.LoadGPR(i.X.RB);
|
||||
return InstrEmit_trap(f, i, ra, rb, i.X.RT);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -56,28 +56,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,49 +83,27 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tdi"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:570`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L570)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:291`](../../../crates/sylpheed-ppc/src/opcode.rs#L291)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:442`](../../../crates/sylpheed-ppc/src/decoder.rs#L442)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_tdi(PPCHIRBuilder& f, const InstrData& i) {
|
||||
if (cvars::ignore_trap_instructions) {
|
||||
return 0;
|
||||
}
|
||||
// a <- (RA)
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
Value* ra = f.LoadGPR(i.D.RA);
|
||||
Value* rb = f.LoadConstantInt64(XEEXTS16(i.D.DS));
|
||||
return InstrEmit_trap(f, i, ra, rb, i.D.RT);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,49 +85,30 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="tw"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:585`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L585)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:292`](../../../crates/sylpheed-ppc/src/opcode.rs#L292)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:865`](../../../crates/sylpheed-ppc/src/decoder.rs#L865)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_tw(PPCHIRBuilder& f, const InstrData& i) {
|
||||
if (cvars::ignore_trap_instructions) {
|
||||
return 0;
|
||||
}
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// b <- EXTS((RB)[32:63])
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
Value* ra =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE), INT64_TYPE);
|
||||
Value* rb =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE), INT64_TYPE);
|
||||
return InstrEmit_trap(f, i, ra, rb, i.X.RT);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -56,28 +56,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,49 +83,34 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="twi"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:603`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L603)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:293`](../../../crates/sylpheed-ppc/src/opcode.rs#L293)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:443`](../../../crates/sylpheed-ppc/src/decoder.rs#L443)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_twi(PPCHIRBuilder& f, const InstrData& i) {
|
||||
if (cvars::ignore_trap_instructions) {
|
||||
return 0;
|
||||
}
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
if (i.D.RA == 0 && i.D.RT == 0x1F) {
|
||||
// This is a special trap. Probably.
|
||||
uint16_t type = (uint16_t)XEEXTS16(i.D.DS);
|
||||
f.Trap(type);
|
||||
return 0;
|
||||
}
|
||||
Value* ra =
|
||||
f.SignExtend(f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE), INT64_TYPE);
|
||||
Value* rb = f.LoadConstantInt64(XEEXTS16(i.D.DS));
|
||||
return InstrEmit_trap(f, i, ra, rb, i.D.RT);
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crand"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:352`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L352)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:35`](../../../crates/sylpheed-ppc/src/opcode.rs#L35)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:832`](../../../crates/sylpheed-ppc/src/decoder.rs#L832)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crand(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] & CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
@@ -102,7 +113,7 @@ _No condition-register or status-register effects._
|
||||
- **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.
|
||||
- **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`](https://git.mc02.dev/fabi/xenia-rs/src/commit/8401d4d5112849bf7b0f78f9d620f3620b1ce58d/crates/xenia-cpu/src/interpreter.rs). xenia-canary's `InstrEmit_crand` emits the equivalent host AND of the two CR bits.
|
||||
|
||||
## Related Instructions
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crandc"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:361`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L361)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:36`](../../../crates/sylpheed-ppc/src/opcode.rs#L36)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:828`](../../../crates/sylpheed-ppc/src/decoder.rs#L828)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crandc(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] & ¬CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(ba, f.And(f.Not(bb), f.LoadConstantInt8(0x01)));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="creqv"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:370`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L370)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:37`](../../../crates/sylpheed-ppc/src/opcode.rs#L37)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:833`](../../../crates/sylpheed-ppc/src/decoder.rs#L833)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_creqv(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] == CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.CompareEQ(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crnand"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:379`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L379)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:38`](../../../crates/sylpheed-ppc/src/opcode.rs#L38)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:831`](../../../crates/sylpheed-ppc/src/decoder.rs#L831)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crnand(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- ¬(CR[ba] & CR[bb]) bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(f.Not(f.And(ba, bb)), f.LoadConstantInt8(0x01));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crnor"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:388`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L388)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:39`](../../../crates/sylpheed-ppc/src/opcode.rs#L39)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:827`](../../../crates/sylpheed-ppc/src/decoder.rs#L827)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crnor(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- ¬(CR[ba] | CR[bb]) bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.And(f.Not(f.Or(ba, bb)), f.LoadConstantInt8(0x01));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="cror"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:397`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L397)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:40`](../../../crates/sylpheed-ppc/src/opcode.rs#L40)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:835`](../../../crates/sylpheed-ppc/src/decoder.rs#L835)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_cror(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] | CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Or(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crorc"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:406`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L406)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:41`](../../../crates/sylpheed-ppc/src/opcode.rs#L41)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:834`](../../../crates/sylpheed-ppc/src/decoder.rs#L834)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crorc(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] | ¬CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Or(ba, f.And(f.Not(bb), f.LoadConstantInt8(0x01)));
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,10 +85,23 @@ _No condition-register or status-register effects._
|
||||
## 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)
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="crxor"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:415`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L415)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:42`](../../../crates/sylpheed-ppc/src/opcode.rs#L42)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:830`](../../../crates/sylpheed-ppc/src/decoder.rs#L830)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```cpp
|
||||
int InstrEmit_crxor(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[bt] <- CR[ba] xor CR[bb] bt=bo, ba=bi, bb=bb
|
||||
Value* ba = f.LoadCRField(i.XL.BI >> 2, i.XL.BI & 3);
|
||||
Value* bb = f.LoadCRField(i.XL.BB >> 2, i.XL.BB & 3);
|
||||
Value* bt = f.Xor(ba, bb);
|
||||
f.StoreCRField(i.XL.BO >> 2, i.XL.BO & 3, bt);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<!-- GENERATED: END -->
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,18 +84,23 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrf"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:424`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L424)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:167`](../../../crates/sylpheed-ppc/src/opcode.rs#L167)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:825`](../../../crates/sylpheed-ppc/src/decoder.rs#L825)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mcrf => {
|
||||
ctx.cr[instr.crfd()] = ctx.cr[instr.crfs()];
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mcrf(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[4*bf:4*bf+3] <- CR[4*bfa:4*bfa+3] bf=bo, bfa=bi
|
||||
// LoadCR/StoreCR bake the field position into the word and cannot cross it.
|
||||
uint32_t crfd = i.XL.BO >> 2;
|
||||
uint32_t crfs = i.XL.BI >> 2;
|
||||
for (uint32_t bit = 0; bit < 4; ++bit) {
|
||||
f.StoreCRField(crfd, bit, f.LoadCRField(crfs, bit));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ mcrfs [CRFD], [CRFS]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,44 +85,38 @@ mcrfs [CRFD], [CRFS]
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrfs"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:371`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L371)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:168`](../../../crates/sylpheed-ppc/src/opcode.rs#L168)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:1019`](../../../crates/sylpheed-ppc/src/decoder.rs#L1019)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mcrfs(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// mcrfs CRFD, CRFS
|
||||
// CR[4*CRFD:4*CRFD+3] <- FPSCR[4*CRFS:4*CRFS+3]
|
||||
// FPSCR[4*CRFS:4*CRFS+3] <- 0 (exception bits cleared)
|
||||
|
||||
uint32_t crfd = i.X.RT >> 2; // Destination CR field
|
||||
uint32_t crfs = i.X.RA >> 2; // Source FPSCR field
|
||||
|
||||
Value* fpscr = f.LoadFPSCR();
|
||||
|
||||
// Extract 4-bit field from FPSCR
|
||||
// FPSCR fields are numbered from left to right (0-7), with field 0 being bits
|
||||
// 0-3
|
||||
uint32_t shift = 4 * (7 - crfs);
|
||||
Value* fpscr_field = f.And(f.Shr(fpscr, shift), f.LoadConstantUint32(0xF));
|
||||
|
||||
// Store to CR field (need to shift to proper position in 64-bit CR)
|
||||
f.StoreCR(crfd, f.Shl(f.ZeroExtend(fpscr_field, INT64_TYPE), 4 * (7 - crfd)));
|
||||
|
||||
// Clear the FPSCR field (set to 0)
|
||||
uint32_t mask = ~(0xF << shift);
|
||||
f.StoreFPSCR(f.And(fpscr, f.LoadConstantUint32(mask)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,27 +84,32 @@ _No condition-register or status-register effects._
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mcrxr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:435`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L435)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:169`](../../../crates/sylpheed-ppc/src/opcode.rs#L169)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:929`](../../../crates/sylpheed-ppc/src/decoder.rs#L929)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</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;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mcrxr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// CR[4*CRFD:4*CRFD+3] <- XER[0:3]
|
||||
// XER[0:3] <- 0
|
||||
// XER bits: SO (bit 31), OV (bit 30), CA (bit 29), reserved (bit 28)
|
||||
|
||||
uint32_t crfd = i.X.RT >> 2;
|
||||
Value* xer = f.LoadXER();
|
||||
|
||||
// Extract XER[0:3] which are bits 31-28
|
||||
Value* xer_bits = f.And(f.Shr(xer, 28), f.LoadConstantUint64(0xF));
|
||||
|
||||
// Store to CR field
|
||||
f.StoreCR(crfd, f.Shl(xer_bits, 4 * (7 - crfd)));
|
||||
|
||||
// Clear XER[0:3] (bits 31-28)
|
||||
f.StoreXER(f.And(xer, f.LoadConstantUint64(~(0xFULL << 28))));
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -63,13 +63,15 @@ 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 */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -77,18 +79,53 @@ RT <- 0x00000000 || CR
|
||||
## 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>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfcr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:627`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L627)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:171`](../../../crates/sylpheed-ppc/src/opcode.rs#L171)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:868`](../../../crates/sylpheed-ppc/src/decoder.rs#L868)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfcr => {
|
||||
ctx.gpr[instr.rd()] = ctx.cr() as u64;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mfcr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// mfocrf RT,FXM
|
||||
// RT <- undefined
|
||||
// count <- 0
|
||||
// do i = 0 to 7
|
||||
// if FXMi = 1 then
|
||||
// n <- i
|
||||
// count <- count + 1
|
||||
// if count = 1 then
|
||||
// RT4un + 32:4un + 35 <- CR4un + 32 : 4un + 35
|
||||
|
||||
// TODO(benvanik): optimize mfcr sequences.
|
||||
// Often look something like this:
|
||||
// mfocrf r11, cr6
|
||||
// not r10, r11
|
||||
// extrwi r3, r10, 1, 26
|
||||
// Could recognize this and only load the appropriate CR bit.
|
||||
|
||||
Value* v;
|
||||
if (i.XFX.spr & (1 << 9)) {
|
||||
uint32_t bits = (i.XFX.spr & 0x1FF) >> 1;
|
||||
int count = 0;
|
||||
int cri = 0;
|
||||
for (int b = 0; b <= 7; ++b) {
|
||||
if (bits & (1 << b)) {
|
||||
cri = 7 - b;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count == 1) {
|
||||
v = f.LoadCR(cri);
|
||||
} else {
|
||||
v = f.LoadZeroInt64();
|
||||
}
|
||||
} else {
|
||||
v = f.LoadCR();
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -59,28 +59,26 @@ mffs[Rc] [RD]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -88,20 +86,21 @@ mffs[Rc] [RD]
|
||||
## Implementation References
|
||||
|
||||
**`mffsx`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mffsx"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:397`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L397)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:910`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L910)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3035-3040`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3035-L3040)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mffsx"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:397`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L397)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:172`](../../../crates/sylpheed-ppc/src/opcode.rs#L172)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:1025`](../../../crates/sylpheed-ppc/src/decoder.rs#L1025)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mffsx => {
|
||||
// Move from FPSCR: frD = FPSCR as double (low 32 bits)
|
||||
ctx.fpr[instr.rd()] = f64::from_bits(ctx.fpscr as u64);
|
||||
if instr.rc_bit() { update_cr1_from_fpscr(ctx); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mffsx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* v = f.Cast(f.ZeroExtend(f.LoadFPSCR(), INT64_TYPE), FLOAT64_TYPE);
|
||||
f.StoreFPR(i.X.RT, v);
|
||||
if (i.X.Rc) {
|
||||
f.CopyFPSCRToCR1();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -57,28 +57,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -86,18 +84,20 @@ _No condition-register or status-register effects._
|
||||
## Implementation References
|
||||
|
||||
**`mfmsr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfmsr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:814`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L814)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:771`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L771)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1645-1648`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1645-L1648)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfmsr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:816`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L816)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:173`](../../../crates/sylpheed-ppc/src/opcode.rs#L173)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:886`](../../../crates/sylpheed-ppc/src/decoder.rs#L886)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfmsr => {
|
||||
ctx.gpr[instr.rd()] = ctx.msr;
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// bit 48 = EE; interrupt enabled
|
||||
// bit 62 = RI; recoverable interrupt
|
||||
// return 8000h if unlocked (interrupts enabled), else 0
|
||||
f.StoreGPR(i.X.RT, f.LoadContext(offsetof(PPCContext, msr), INT64_TYPE));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -79,43 +79,65 @@ switch (n) {
|
||||
## Implementation References
|
||||
|
||||
**`mfspr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfspr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:666`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L666)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:799`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L799)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1567-1595`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1567-L1595)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfspr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:668`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L668)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:174`](../../../crates/sylpheed-ppc/src/opcode.rs#L174)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:914`](../../../crates/sylpheed-ppc/src/decoder.rs#L914)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfspr => {
|
||||
let spr = instr.spr();
|
||||
ctx.gpr[instr.rd()] = match spr {
|
||||
crate::context::spr::XER => ctx.xer() as u64,
|
||||
crate::context::spr::LR => ctx.lr,
|
||||
crate::context::spr::CTR => ctx.ctr,
|
||||
crate::context::spr::DEC => ctx.dec as u64,
|
||||
crate::context::spr::TBL => ctx.timebase & 0xFFFF_FFFF,
|
||||
crate::context::spr::TBU => ctx.timebase >> 32,
|
||||
crate::context::spr::VRSAVE => ctx.vrsave as u64,
|
||||
// Xbox 360 Xenon processor signature (from canary).
|
||||
crate::context::spr::PVR => 0x0071_0800,
|
||||
// Benign SPRs — titles read these but we don't model them.
|
||||
crate::context::spr::SPRG0
|
||||
| crate::context::spr::SPRG1
|
||||
| crate::context::spr::SPRG2
|
||||
| crate::context::spr::SPRG3
|
||||
| crate::context::spr::HID0
|
||||
| crate::context::spr::HID1
|
||||
| crate::context::spr::DAR
|
||||
| crate::context::spr::DSISR
|
||||
| crate::context::spr::PIR => 0,
|
||||
_ => {
|
||||
tracing::warn!("mfspr: unimplemented SPR {}", spr);
|
||||
0
|
||||
}
|
||||
};
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mfspr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// RT <- SPR(n)
|
||||
// else
|
||||
// RT <- i32.0 || SPR(n)
|
||||
Value* v;
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = f.LoadXER();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = f.LoadLR();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = f.LoadCTR();
|
||||
break;
|
||||
case 256:
|
||||
// VRSAVE
|
||||
|
||||
v = f.ZeroExtend(f.LoadContext(offsetof(PPCContext, vrsave), INT32_TYPE),
|
||||
INT64_TYPE);
|
||||
break;
|
||||
case 268:
|
||||
// TB
|
||||
v = f.LoadClock();
|
||||
break;
|
||||
case 269:
|
||||
// TBU
|
||||
v = f.Shr(f.LoadClock(), 32);
|
||||
break;
|
||||
case 287:
|
||||
// [ Processor Version Register (PVR) ]
|
||||
// PVR is a 32 bit, read-only register within the supervisor level.
|
||||
// Bits 0 to 15 are the version number.
|
||||
// Bits 16 to 31 are the revision number.
|
||||
// Known Values: 0x710600?, 0x710700, 0x710800 (Corona?);
|
||||
// Note: Some XEXs (such as mfgbootlauncher.xex) may check for a value
|
||||
// that's less than 0x710700.
|
||||
v = f.LoadConstantUint64(cvars::pvr);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, v);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -56,28 +56,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,23 +83,25 @@ _No condition-register or status-register effects._
|
||||
## Implementation References
|
||||
|
||||
**`mftb`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mftb"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:719`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L719)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:803`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L803)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1664-1672`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1664-L1672)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mftb"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:721`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L721)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:175`](../../../crates/sylpheed-ppc/src/opcode.rs#L175)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:918`](../../../crates/sylpheed-ppc/src/decoder.rs#L918)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mftb => {
|
||||
let tbr = instr.spr();
|
||||
ctx.gpr[instr.rd()] = match tbr {
|
||||
268 => ctx.timebase & 0xFFFF_FFFF,
|
||||
269 => ctx.timebase >> 32,
|
||||
_ => 0,
|
||||
};
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mftb(PPCHIRBuilder& f, const InstrData& i) {
|
||||
Value* time = f.LoadClock();
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
if (n == 268) {
|
||||
// TB - full bits.
|
||||
} else {
|
||||
// TBU - upper bits only.
|
||||
time = f.Shr(time, 32);
|
||||
}
|
||||
f.StoreGPR(i.XFX.RT, time);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -56,28 +56,26 @@ _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.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -85,22 +83,19 @@ _No condition-register or status-register effects._
|
||||
## Implementation References
|
||||
|
||||
**`mfvscr`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfvscr"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:303`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L303)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:53`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L53)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:539`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L539)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:2506-2513`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L2506-L2513)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mfvscr"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_altivec.cc:303`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_altivec.cc#L303)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:176`](../../../crates/sylpheed-ppc/src/opcode.rs#L176)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:654`](../../../crates/sylpheed-ppc/src/decoder.rs#L654)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mfvscr => {
|
||||
// PPCBUG-080: ISA places VSCR in the rightmost word of VD with
|
||||
// bytes 0-11 zeroed. Previously the full 128-bit ctx.vscr was
|
||||
// copied (leaking stale upper data to guest).
|
||||
let vscr_word = ctx.vscr.as_u32x4()[3];
|
||||
ctx.vr[instr.rd()] = xenia_types::Vec128::from_u32x4_array([0, 0, 0, vscr_word]);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mfvscr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// mfvscr VD - Move from VSCR to VD
|
||||
f.StoreVR(i.VX.VD,
|
||||
f.LoadContext(offsetof(PPCContext, vscr_vec), VEC128_TYPE));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -63,13 +63,15 @@ for i in 0..7:
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -77,28 +79,51 @@ for i in 0..7:
|
||||
## Implementation References
|
||||
|
||||
**`mtcrf`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mtcrf"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_control.cc:732`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_control.cc#L732)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:779`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L779)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:1631-1644`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L1631-L1644)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mtcrf"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_control.cc:734`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_control.cc#L734)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:178`](../../../crates/sylpheed-ppc/src/opcode.rs#L178)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:894`](../../../crates/sylpheed-ppc/src/decoder.rs#L894)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mtcrf => {
|
||||
let crm = instr.crm();
|
||||
let val = ctx.gpr[instr.rs()] as u32;
|
||||
let old = ctx.cr();
|
||||
let mut new = old;
|
||||
for i in 0..8u32 {
|
||||
if crm & (1 << (7 - i)) != 0 {
|
||||
let mask = 0xF << (28 - i * 4);
|
||||
new = (new & !mask) | (val & mask);
|
||||
}
|
||||
}
|
||||
ctx.set_cr(new);
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mtcrf(PPCHIRBuilder& f, const InstrData& i) {
|
||||
// mtocrf FXM,RS
|
||||
// count <- 0
|
||||
// do i = 0 to 7
|
||||
// if FXMi = 1 then
|
||||
// n <- i
|
||||
// count <- count + 1
|
||||
// if count = 1 then
|
||||
// CR4un + 32 : 4un + 35 <- RS4un + 32:4un + 35
|
||||
|
||||
Value* v = f.LoadGPR(i.XFX.RT);
|
||||
if (i.XFX.spr & (1 << 9)) {
|
||||
uint32_t bits = (i.XFX.spr & 0x1FF) >> 1;
|
||||
int count = 0;
|
||||
int cri = 0;
|
||||
for (int b = 0; b <= 7; ++b) {
|
||||
if (bits & (1 << b)) {
|
||||
cri = 7 - b;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count == 1) {
|
||||
f.StoreCR(cri, v);
|
||||
} else {
|
||||
// Invalid; store zero to CR.
|
||||
f.StoreCR(f.LoadZeroInt64());
|
||||
}
|
||||
} else {
|
||||
uint32_t bits = (i.XFX.spr & 0x1FF) >> 1;
|
||||
for (int b = 0; b <= 7; ++b) {
|
||||
if (bits & (1 << b)) {
|
||||
int cri = 7 - b;
|
||||
f.StoreCR(cri, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
@@ -58,28 +58,26 @@ mtfsb0[Rc] [FPSCRD]
|
||||
## Operation (pseudocode)
|
||||
|
||||
```
|
||||
; Pseudocode derives directly from the xenia-rs interpreter
|
||||
; arm (see Implementation References). Operation semantics:
|
||||
; - Read source operands from the fields listed under Operands.
|
||||
; - Apply the arithmetic / logical / memory action described
|
||||
; in the Description field above.
|
||||
; - Write results to the destination register(s); update any
|
||||
; status bits enumerated under Status-Register Effects.
|
||||
; Consult the IBM AIX reference link under IBM Reference for
|
||||
; canonical PPC-style pseudocode where xenia's expression is
|
||||
; terse.
|
||||
; No hand-written pseudocode for this instruction yet.
|
||||
; The authoritative semantics are the Canary emitter snapshot under
|
||||
; Implementation References; about half of Canary's emitters open
|
||||
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
|
||||
; Every side effect is also enumerated in the Register Effects and
|
||||
; Status-Register Effects tables above.
|
||||
```
|
||||
|
||||
## C Translation Example
|
||||
|
||||
```c
|
||||
/* C translation: the xenia-rs interpreter arm below in */
|
||||
/* Implementation References is the authoritative semantic */
|
||||
/* snapshot. Translate it line-by-line: */
|
||||
/* - ctx.gpr[N] -> r[N] (or f[]/v[] for FPRs/VRs) */
|
||||
/* - mem.read_u*/write_u* -> mem_read_u*_be / mem_write_u*_be */
|
||||
/* - ctx.update_cr_signed(fld, v) -> update_cr_signed(fld, v) */
|
||||
/* - ctx.xer_ca / xer_ov / xer_so -> xer.CA / xer.OV / xer.SO */
|
||||
/* No hand-written C yet. Translate the Canary emitter snapshot */
|
||||
/* under Implementation References; its HIR maps directly: */
|
||||
/* f.LoadGPR(n) / f.StoreGPR(n, v) -> r[n] / r[n] = v */
|
||||
/* f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n] */
|
||||
/* f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters */
|
||||
/* wrap them in f.ByteSwap for the big-endian guest value */
|
||||
/* f.UpdateCR(n, v) -> CR field n from v's LOW 32 BITS vs 0 */
|
||||
/* f.LoadCA / f.StoreCA -> xer.CA; f.StoreSAT -> vscr.SAT */
|
||||
/* i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands */
|
||||
/* The Register Effects and Status-Register Effects tables above */
|
||||
/* enumerate every side effect a faithful translation must emit. */
|
||||
```
|
||||
@@ -87,21 +85,17 @@ mtfsb0[Rc] [FPSCRD]
|
||||
## Implementation References
|
||||
|
||||
**`mtfsb0x`**
|
||||
- xenia-canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mtfsb0x"`](../../xenia-canary/tools/ppc-instructions.xml)
|
||||
- xenia-canary emit: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:406`](../../xenia-canary/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L406)
|
||||
- xenia-rs opcode: [`crates/xenia-cpu/src/opcode.rs:55`](../../xenia-rs/crates/xenia-cpu/src/opcode.rs#L55)
|
||||
- xenia-rs decoder: [`crates/xenia-cpu/src/decoder.rs:905`](../../xenia-rs/crates/xenia-cpu/src/decoder.rs#L905)
|
||||
- xenia-rs interpreter: [`crates/xenia-cpu/src/interpreter.rs:3055-3061`](../../xenia-rs/crates/xenia-cpu/src/interpreter.rs#L3055-L3061)
|
||||
<details><summary>xenia-rs interpreter body (frozen snapshot)</summary>
|
||||
- Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="mtfsb0x"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml)
|
||||
- Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_fpu.cc:406`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_fpu.cc#L406)
|
||||
- Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:179`](../../../crates/sylpheed-ppc/src/opcode.rs#L179)
|
||||
- Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:1020`](../../../crates/sylpheed-ppc/src/decoder.rs#L1020)
|
||||
<details><summary>Canary emitter (frozen snapshot @ <code>f21ebd49e9</code>)</summary>
|
||||
|
||||
```rust
|
||||
PpcOpcode::mtfsb0x => {
|
||||
// Clear FPSCR bit crbd
|
||||
let bit = instr.crbd();
|
||||
ctx.fpscr &= !(1 << (31 - bit));
|
||||
if instr.rc_bit() { update_cr1_from_fpscr(ctx); }
|
||||
ctx.pc += 4;
|
||||
}
|
||||
```cpp
|
||||
int InstrEmit_mtfsb0x(PPCHIRBuilder& f, const InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user