fix(cpu): PPCBUG-040 PPCBUG-560 fix sh64() bit order and rldicl test helper

PPCBUG-040: decoder.rs sh64() assembled the XS-form shift amount as
(SH[4:0] << 1) | SH[5] instead of (SH[5] << 5) | SH[4:0]. Every
`sradi` with shift N ∈ 1..=62 executed with a completely wrong shift
count (e.g. shift=32 executed as shift=1).

PPCBUG-560: disasm_goldens.rs rldicl() test helper was encoding sh[5:1]
at PPC bits 16-20 and sh[0] at PPC bit 30 — exactly backwards. The wrong
encoder and wrong decoder cancelled out, hiding PPCBUG-040 from tests.
Fix both together so tests validate ISA-correct encodings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-01 20:54:40 +02:00
parent ca5b90b700
commit 147daa0721
3 changed files with 33 additions and 10 deletions

View File

@@ -89,7 +89,7 @@ impl DecodedInstr {
/// SH field for 64-bit shifts (bits 16-20 + bit 30) /// SH field for 64-bit shifts (bits 16-20 + bit 30)
#[inline] pub fn sh64(&self) -> u32 { #[inline] pub fn sh64(&self) -> u32 {
(extract_bits(self.raw, 16, 20) << 1) | extract_bits(self.raw, 30, 30) (extract_bits(self.raw, 30, 30) << 5) | extract_bits(self.raw, 16, 20)
} }
/// SPR field (bits 11-20, swapped halves) /// SPR field (bits 11-20, swapped halves)

View File

@@ -20,7 +20,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use xenia_cpu::decoder::decode; use xenia_cpu::decoder::{DecodedInstr, decode};
use xenia_cpu::disasm::format; use xenia_cpu::disasm::format;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -158,20 +158,20 @@ fn rlwinm(rs: u32, ra: u32, sh: u32, mb: u32, me: u32, rc: u32) -> u32 {
} }
fn rldicl(rs: u32, ra: u32, sh: u32, mb: u32, rc: u32) -> u32 { fn rldicl(rs: u32, ra: u32, sh: u32, mb: u32, rc: u32) -> u32 {
// MD-form, op30 xo=0. sh split: bits 16-20 (high 5) + bit 30 (low bit). // MD-form: sh[4:0] at PPC bits 16-20 (host bits 11-15); sh[5] at PPC bit 30 (host bit 1).
// mb split: bits 21-25 (low 5) + bit 26 (high bit). // mb[4:0] at PPC bits 21-25 (host bits 6-10); mb[5] at PPC bit 26 (host bit 5).
let sh_hi = (sh >> 1) & 0x1F; let sh_lo = sh & 0x1F;
let sh_lo = sh & 1; let sh_hi = (sh >> 5) & 1;
let mb_lo = mb & 0x1F; let mb_lo = mb & 0x1F;
let mb_hi = (mb >> 5) & 1; let mb_hi = (mb >> 5) & 1;
(30 << 26) (30 << 26)
| (rs << 21) | (rs << 21)
| (ra << 16) | (ra << 16)
| (sh_hi << 11) | (sh_lo << 11)
| (mb_lo << 6) | (mb_lo << 6)
| (mb_hi << 5) | (mb_hi << 5)
| (0 << 2) | (0 << 2)
| (sh_lo << 1) | (sh_hi << 1)
| rc | rc
} }
@@ -529,3 +529,26 @@ fn vmx128_registers() {
all.extend_from_slice(&vmx128_4op); all.extend_from_slice(&vmx128_4op);
assert_or_regen("vmx128_registers.json", &all); assert_or_regen("vmx128_registers.json", &all);
} }
#[test]
fn sradi_shift_32_decodes_to_32() {
// sradi rA, rS, 32: sh=32 → sh[4:0]=0, sh[5]=1
// After PPCBUG-040 fix, sh64() must return 32, not 1.
let instr: DecodedInstr = decode(rldicl(3, 4, 32, 63, 0), 0);
// rldicl with mb=63 is not sradi, but tests sh64() extraction.
assert_eq!(instr.sh64(), 32, "sh64 must return 32 for sh=32 (sh5=1, sh_lo=0)");
}
#[test]
fn sh64_shift_1_decodes_correctly() {
// sh=1: sh[4:0]=1, sh[5]=0 → sh64() must return 1
let instr: DecodedInstr = decode(rldicl(3, 4, 1, 0, 0), 0);
assert_eq!(instr.sh64(), 1, "sh64 must return 1 for sh=1");
}
#[test]
fn sh64_shift_63_decodes_correctly() {
// sh=63: sh[4:0]=31=0x1F, sh[5]=1 → sh64() must return 63
let instr: DecodedInstr = decode(rldicl(3, 4, 63, 0, 0), 0);
assert_eq!(instr.sh64(), 63, "sh64 must return 63 for sh=63");
}

View File

@@ -182,7 +182,7 @@
}, },
{ {
"label": "srdi r3, r4, 8", "label": "srdi r3, r4, 8",
"raw": "0x7883E200", "raw": "0x7883C202",
"addr": "0x82000000", "addr": "0x82000000",
"mnemonic": "rldicl", "mnemonic": "rldicl",
"operands": "r3, r4, 56, 8", "operands": "r3, r4, 56, 8",
@@ -191,7 +191,7 @@
}, },
{ {
"label": "rotldi r3, r4, 8", "label": "rotldi r3, r4, 8",
"raw": "0x78832000", "raw": "0x78834000",
"addr": "0x82000000", "addr": "0x82000000",
"mnemonic": "rldicl", "mnemonic": "rldicl",
"operands": "r3, r4, 8, 0", "operands": "r3, r4, 8, 0",