docs(ppc-manual): quote Canary and our own decoder, not the retired xenia-rs
The generator had not been able to run correctly since the manual moved into
`tools/ppc-manual/`: it computed the repository root as `HERE.parent.parent`,
which now names `tools/`, so the XML, Canary's emitters and xenia-rs all stopped
resolving — silently, because both scrapers skipped what they could not find.
Every page's references had been pointing at paths that exist nowhere.
What each source contributed, measured on the 350 pages before this change:
Operation (pseudocode) 251 pages: fixed boilerplate "derives from the xenia-rs
interpreter"; 99 carry real hand-written seeds
C translation 337 pages: the same kind of boilerplate
xenia-rs snapshot 336 pages: the interpreter arm, pasted in — the only
per-instruction semantics on unseeded pages
links xenia-rs opcode/decoder/interpreter + Canary emitter
Now:
* semantics come from **Xenia Canary**, the reference emulator, read through
`git show` at a pinned upstream commit (`origin/canary_experimental`,
f21ebd49e9). Not our checkout: it carries instrumentation and lacked
upstream's `mcrf` fix, so it would have published probes and a wrong `mcrf`.
Each page embeds the emitter (`InstrEmit_<mnem>`), and for the 128 pure
one-line delegations also the helper that holds the semantics.
* decode references point at `crates/sylpheed-ppc` — the decoder that
produces `sylpheed.db` — as in-repo relative links.
* the boilerplate now says what is true, and the C translation guide maps
Canary's actual HIR calls, checked against `ppc_hir_builder.h` (including
that `UpdateCR(n, v)` truncates to 32 bits).
* `rust_scraper.py` -> `decoder_scraper.py` (interpreter half dropped);
missing sources are now errors, not empty results.
Verified:
consistency checks 455 XML entries, 350 families, 598 index keys
hand-written tails 386/386 byte-identical after regeneration
xenia-rs in generated 0
pages with a snapshot 349/350 (was 336) — `dcbi` has no Canary emitter at all
in-repo decoder links 910/910 resolve to a line holding the identifier
emitter boundaries brace counter == column-0 `}` rule on 521/521;
preprocessor model unit-tested (#if 0/#else/#elif)
idempotency re-run: 0 pages updated, 0 working-tree changes
Hand-written notes (outside the generated regions) are not rewritten here:
* 110 links into `../../xenia-rs/...` were dead; they now point at the file in
the archived repository (git.mc02.dev/fabi/xenia-rs @ 8401d4d). Line anchors
were dropped because the notes predate that commit — 0 of 441 old line
ranges match it — and a precise-looking wrong anchor is worse than none. The
link text, which carries the author's line numbers, is unchanged.
* 140 prose claims about xenia-rs's behaviour remain. 23 are verified to hold
for Canary too (the 32-bit CR0 truncation, OE left unimplemented); the other
114 need checking one by one, and some invert — e.g. `divdx` notes a correct
64-bit CR0 update in xenia-rs where Canary's `UpdateCR` truncates. Left for
a deliberate pass rather than a blind substitution.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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