diff --git a/crates/xenia-cpu/src/decoder.rs b/crates/xenia-cpu/src/decoder.rs index 136d3e3..28e55dc 100644 --- a/crates/xenia-cpu/src/decoder.rs +++ b/crates/xenia-cpu/src/decoder.rs @@ -89,7 +89,7 @@ impl DecodedInstr { /// SH field for 64-bit shifts (bits 16-20 + bit 30) #[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) diff --git a/crates/xenia-cpu/tests/disasm_goldens.rs b/crates/xenia-cpu/tests/disasm_goldens.rs index 6c39d54..97b3825 100644 --- a/crates/xenia-cpu/tests/disasm_goldens.rs +++ b/crates/xenia-cpu/tests/disasm_goldens.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; -use xenia_cpu::decoder::decode; +use xenia_cpu::decoder::{DecodedInstr, decode}; use xenia_cpu::disasm::format; #[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 { - // MD-form, op30 xo=0. sh split: bits 16-20 (high 5) + bit 30 (low bit). - // mb split: bits 21-25 (low 5) + bit 26 (high bit). - let sh_hi = (sh >> 1) & 0x1F; - let sh_lo = sh & 1; + // MD-form: sh[4:0] at PPC bits 16-20 (host bits 11-15); sh[5] at PPC bit 30 (host bit 1). + // mb[4:0] at PPC bits 21-25 (host bits 6-10); mb[5] at PPC bit 26 (host bit 5). + let sh_lo = sh & 0x1F; + let sh_hi = (sh >> 5) & 1; let mb_lo = mb & 0x1F; let mb_hi = (mb >> 5) & 1; (30 << 26) | (rs << 21) | (ra << 16) - | (sh_hi << 11) + | (sh_lo << 11) | (mb_lo << 6) | (mb_hi << 5) | (0 << 2) - | (sh_lo << 1) + | (sh_hi << 1) | rc } @@ -529,3 +529,26 @@ fn vmx128_registers() { all.extend_from_slice(&vmx128_4op); 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"); +} diff --git a/crates/xenia-cpu/tests/golden/extended_mnemonics.json b/crates/xenia-cpu/tests/golden/extended_mnemonics.json index d869109..efb251b 100644 --- a/crates/xenia-cpu/tests/golden/extended_mnemonics.json +++ b/crates/xenia-cpu/tests/golden/extended_mnemonics.json @@ -182,7 +182,7 @@ }, { "label": "srdi r3, r4, 8", - "raw": "0x7883E200", + "raw": "0x7883C202", "addr": "0x82000000", "mnemonic": "rldicl", "operands": "r3, r4, 56, 8", @@ -191,7 +191,7 @@ }, { "label": "rotldi r3, r4, 8", - "raw": "0x78832000", + "raw": "0x78834000", "addr": "0x82000000", "mnemonic": "rldicl", "operands": "r3, r4, 8, 0",