Initial commit: xenia-rs workspace for Xbox 360 RE
Rust reimplementation of the xenia Xbox 360 emulator targeting reverse- engineering and preservation, initially scoped to Project Sylpheed. Includes: - XEX2 loader (LZX decompression, AES decryption, PE parsing) - XISO / XGD2 disc image VFS - PPC interpreter with 200+ opcodes and VMX128 decoding - Static analyzer: functions, cross-references, labels, asm + SQLite output - HLE kernel covering the xboxkrnl/xam subset used by Sylpheed init - Debugger with in-memory and SQLite-backed execution tracing - `xenia-rs` CLI with extract/dis/exec commands that produce cumulative, superset SQLite databases and opt-in instruction/import/branch traces Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
12
crates/xenia-cpu/Cargo.toml
Normal file
12
crates/xenia-cpu/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "xenia-cpu"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
xenia-types = { workspace = true }
|
||||
xenia-memory = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
191
crates/xenia-cpu/src/context.rs
Normal file
191
crates/xenia-cpu/src/context.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use xenia_types::Vec128;
|
||||
|
||||
/// Condition register field (one of CR0-CR7).
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct CrField {
|
||||
pub lt: bool,
|
||||
pub gt: bool,
|
||||
pub eq: bool,
|
||||
pub so: bool,
|
||||
}
|
||||
|
||||
impl CrField {
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
((self.lt as u8) << 3) | ((self.gt as u8) << 2) | ((self.eq as u8) << 1) | (self.so as u8)
|
||||
}
|
||||
|
||||
pub fn from_u8(val: u8) -> Self {
|
||||
Self {
|
||||
lt: val & 8 != 0,
|
||||
gt: val & 4 != 0,
|
||||
eq: val & 2 != 0,
|
||||
so: val & 1 != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SPR (Special Purpose Register) numbers used by mfspr/mtspr.
|
||||
pub mod spr {
|
||||
pub const XER: u32 = 1;
|
||||
pub const LR: u32 = 8;
|
||||
pub const CTR: u32 = 9;
|
||||
pub const TBL: u32 = 268;
|
||||
pub const TBU: u32 = 269;
|
||||
pub const SPRG0: u32 = 272;
|
||||
pub const SPRG1: u32 = 273;
|
||||
pub const SPRG2: u32 = 274;
|
||||
pub const SPRG3: u32 = 275;
|
||||
pub const PVR: u32 = 287;
|
||||
pub const PIR: u32 = 1023;
|
||||
}
|
||||
|
||||
/// PowerPC processor context. Holds all register state for one guest thread.
|
||||
/// Mirrors PPCContext from ppc_context.h, minus JIT-specific fields.
|
||||
#[repr(C, align(64))]
|
||||
pub struct PpcContext {
|
||||
// General purpose registers (R0-R31)
|
||||
pub gpr: [u64; 32],
|
||||
// Count register
|
||||
pub ctr: u64,
|
||||
// Link register
|
||||
pub lr: u64,
|
||||
// Machine state register
|
||||
pub msr: u64,
|
||||
// Floating-point registers (F0-F31)
|
||||
pub fpr: [f64; 32],
|
||||
// VMX128 vector registers (V0-V127, Xbox 360 extended set)
|
||||
pub vr: [Vec128; 128],
|
||||
|
||||
// Condition register fields (CR0-CR7)
|
||||
pub cr: [CrField; 8],
|
||||
// Floating-point status and control register
|
||||
pub fpscr: u32,
|
||||
// XER register (split for easy individual updates)
|
||||
pub xer_ca: u8,
|
||||
pub xer_ov: u8,
|
||||
pub xer_so: u8,
|
||||
// Altivec VSCR saturation bit
|
||||
pub vscr_sat: u8,
|
||||
|
||||
// Program counter
|
||||
pub pc: u32,
|
||||
// Reservation address/value for lwarx/stwcx
|
||||
pub reserved_addr: u32,
|
||||
pub reserved_val: u64,
|
||||
pub has_reservation: bool,
|
||||
|
||||
// Thread ID (for kernel use)
|
||||
pub thread_id: u32,
|
||||
|
||||
// Cycle counter for timing
|
||||
pub cycle_count: u64,
|
||||
|
||||
// Time base (incremented each instruction for debugging)
|
||||
pub timebase: u64,
|
||||
}
|
||||
|
||||
impl PpcContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
gpr: [0; 32],
|
||||
ctr: 0,
|
||||
lr: 0,
|
||||
msr: 0,
|
||||
fpr: [0.0; 32],
|
||||
vr: [Vec128::ZERO; 128],
|
||||
cr: [CrField::default(); 8],
|
||||
fpscr: 0,
|
||||
xer_ca: 0,
|
||||
xer_ov: 0,
|
||||
xer_so: 0,
|
||||
vscr_sat: 0,
|
||||
pc: 0,
|
||||
reserved_addr: 0,
|
||||
reserved_val: 0,
|
||||
has_reservation: false,
|
||||
thread_id: 0,
|
||||
cycle_count: 0,
|
||||
timebase: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the full 32-bit condition register.
|
||||
pub fn cr(&self) -> u32 {
|
||||
let mut val = 0u32;
|
||||
for (i, field) in self.cr.iter().enumerate() {
|
||||
val |= (field.as_u8() as u32) << (28 - i * 4);
|
||||
}
|
||||
val
|
||||
}
|
||||
|
||||
/// Set the full 32-bit condition register.
|
||||
pub fn set_cr(&mut self, val: u32) {
|
||||
for i in 0..8 {
|
||||
self.cr[i] = CrField::from_u8(((val >> (28 - i * 4)) & 0xF) as u8);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a single CR bit by absolute bit number (0-31).
|
||||
pub fn get_cr_bit(&self, bit: u32) -> bool {
|
||||
let field = (bit / 4) as usize;
|
||||
let sub = bit % 4;
|
||||
match sub {
|
||||
0 => self.cr[field].lt,
|
||||
1 => self.cr[field].gt,
|
||||
2 => self.cr[field].eq,
|
||||
3 => self.cr[field].so,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a single CR bit by absolute bit number (0-31).
|
||||
pub fn set_cr_bit(&mut self, bit: u32, val: bool) {
|
||||
let field = (bit / 4) as usize;
|
||||
let sub = bit % 4;
|
||||
match sub {
|
||||
0 => self.cr[field].lt = val,
|
||||
1 => self.cr[field].gt = val,
|
||||
2 => self.cr[field].eq = val,
|
||||
3 => self.cr[field].so = val,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a condition register field based on a comparison result (signed).
|
||||
pub fn update_cr_signed(&mut self, field: usize, val: i64) {
|
||||
self.cr[field] = CrField {
|
||||
lt: val < 0,
|
||||
gt: val > 0,
|
||||
eq: val == 0,
|
||||
so: self.xer_so != 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Update a condition register field based on a comparison result (unsigned).
|
||||
pub fn update_cr_unsigned(&mut self, field: usize, a: u64, b: u64) {
|
||||
self.cr[field] = CrField {
|
||||
lt: a < b,
|
||||
gt: a > b,
|
||||
eq: a == b,
|
||||
so: self.xer_so != 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Get the full XER register value.
|
||||
pub fn xer(&self) -> u32 {
|
||||
((self.xer_so as u32) << 31) | ((self.xer_ov as u32) << 30) | ((self.xer_ca as u32) << 29)
|
||||
}
|
||||
|
||||
/// Set XER from a full 32-bit value.
|
||||
pub fn set_xer(&mut self, val: u32) {
|
||||
self.xer_so = ((val >> 31) & 1) as u8;
|
||||
self.xer_ov = ((val >> 30) & 1) as u8;
|
||||
self.xer_ca = ((val >> 29) & 1) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PpcContext {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
819
crates/xenia-cpu/src/decoder.rs
Normal file
819
crates/xenia-cpu/src/decoder.rs
Normal file
@@ -0,0 +1,819 @@
|
||||
use crate::opcode::PpcOpcode;
|
||||
|
||||
/// Extract bits [a..=b] from a 32-bit value (PPC bit numbering: 0 = MSB).
|
||||
#[inline(always)]
|
||||
const fn extract_bits(v: u32, a: u32, b: u32) -> u32 {
|
||||
(v >> (32 - 1 - b)) & ((1 << (b - a + 1)) - 1)
|
||||
}
|
||||
|
||||
/// Decoded PPC instruction with extracted operand fields.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DecodedInstr {
|
||||
pub opcode: PpcOpcode,
|
||||
pub raw: u32,
|
||||
pub addr: u32,
|
||||
}
|
||||
|
||||
impl DecodedInstr {
|
||||
// Common field extractors (PPC bit numbering)
|
||||
|
||||
/// Primary opcode (bits 0-5)
|
||||
#[inline] pub fn op(&self) -> u32 { extract_bits(self.raw, 0, 5) }
|
||||
|
||||
/// rD/rS/rT (bits 6-10) - destination/source register
|
||||
#[inline] pub fn rd(&self) -> usize { extract_bits(self.raw, 6, 10) as usize }
|
||||
#[inline] pub fn rs(&self) -> usize { self.rd() }
|
||||
#[inline] pub fn rt(&self) -> usize { self.rd() }
|
||||
|
||||
/// rA (bits 11-15)
|
||||
#[inline] pub fn ra(&self) -> usize { extract_bits(self.raw, 11, 15) as usize }
|
||||
|
||||
/// rB (bits 16-20)
|
||||
#[inline] pub fn rb(&self) -> usize { extract_bits(self.raw, 16, 20) as usize }
|
||||
|
||||
/// rC (bits 21-25) - for 4-operand instructions
|
||||
#[inline] pub fn rc(&self) -> usize { extract_bits(self.raw, 21, 25) as usize }
|
||||
|
||||
/// SIMM/UIMM (bits 16-31) - signed/unsigned immediate
|
||||
#[inline] pub fn simm16(&self) -> i16 { (self.raw & 0xFFFF) as i16 }
|
||||
#[inline] pub fn uimm16(&self) -> u16 { (self.raw & 0xFFFF) as u16 }
|
||||
|
||||
/// D-form displacement (signed, bits 16-31)
|
||||
#[inline] pub fn d(&self) -> i32 { self.simm16() as i32 }
|
||||
|
||||
/// DS-form displacement (signed, bits 16-29, shifted left 2)
|
||||
#[inline] pub fn ds(&self) -> i32 { (self.raw & 0xFFFC) as i16 as i32 }
|
||||
|
||||
/// LI field for branch (bits 6-29, sign-extended, shifted left 2)
|
||||
#[inline] pub fn li(&self) -> i32 {
|
||||
let li = extract_bits(self.raw, 6, 29);
|
||||
// Sign-extend from 24 bits, then shift left 2
|
||||
let sign_extended = ((li as i32) << 8) >> 8;
|
||||
sign_extended << 2
|
||||
}
|
||||
|
||||
/// BD field for conditional branch (bits 16-29, sign-extended, shifted left 2)
|
||||
#[inline] pub fn bd(&self) -> i32 {
|
||||
let bd = extract_bits(self.raw, 16, 29);
|
||||
let sign_extended = ((bd as i32) << 18) >> 18;
|
||||
sign_extended << 2
|
||||
}
|
||||
|
||||
/// BO field (bits 6-10) - branch options
|
||||
#[inline] pub fn bo(&self) -> u32 { extract_bits(self.raw, 6, 10) }
|
||||
|
||||
/// BI field (bits 11-15) - branch condition
|
||||
#[inline] pub fn bi(&self) -> u32 { extract_bits(self.raw, 11, 15) }
|
||||
|
||||
/// AA bit (bit 30) - absolute address
|
||||
#[inline] pub fn aa(&self) -> bool { (self.raw >> 1) & 1 != 0 }
|
||||
|
||||
/// LK bit (bit 31) - link (update LR)
|
||||
#[inline] pub fn lk(&self) -> bool { self.raw & 1 != 0 }
|
||||
|
||||
/// Rc bit (bit 31) - record CR0
|
||||
#[inline] pub fn rc_bit(&self) -> bool { self.raw & 1 != 0 }
|
||||
|
||||
/// OE bit (bit 21) - overflow enable
|
||||
#[inline] pub fn oe(&self) -> bool { extract_bits(self.raw, 21, 21) != 0 }
|
||||
|
||||
/// MB, ME fields for rotate instructions
|
||||
#[inline] pub fn mb(&self) -> u32 { extract_bits(self.raw, 21, 25) }
|
||||
#[inline] pub fn me(&self) -> u32 { extract_bits(self.raw, 26, 30) }
|
||||
|
||||
/// SH field (bits 16-20) for shift instructions
|
||||
#[inline] pub fn sh(&self) -> u32 { extract_bits(self.raw, 16, 20) }
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// SPR field (bits 11-20, swapped halves)
|
||||
#[inline] pub fn spr(&self) -> u32 {
|
||||
let spr_raw = extract_bits(self.raw, 11, 20);
|
||||
((spr_raw & 0x1F) << 5) | ((spr_raw >> 5) & 0x1F)
|
||||
}
|
||||
|
||||
/// CRM field (bits 12-19) for mtcrf
|
||||
#[inline] pub fn crm(&self) -> u32 { extract_bits(self.raw, 12, 19) }
|
||||
|
||||
/// crfD (bits 6-8) - condition register field destination
|
||||
#[inline] pub fn crfd(&self) -> usize { extract_bits(self.raw, 6, 8) as usize }
|
||||
|
||||
/// crfS (bits 11-13)
|
||||
#[inline] pub fn crfs(&self) -> usize { extract_bits(self.raw, 11, 13) as usize }
|
||||
|
||||
/// L bit (bit 10) - 64-bit compare
|
||||
#[inline] pub fn l(&self) -> bool { extract_bits(self.raw, 10, 10) != 0 }
|
||||
|
||||
/// crbD (bits 6-10)
|
||||
#[inline] pub fn crbd(&self) -> u32 { extract_bits(self.raw, 6, 10) }
|
||||
/// crbA (bits 11-15)
|
||||
#[inline] pub fn crba(&self) -> u32 { extract_bits(self.raw, 11, 15) }
|
||||
/// crbB (bits 16-20)
|
||||
#[inline] pub fn crbb(&self) -> u32 { extract_bits(self.raw, 16, 20) }
|
||||
|
||||
// VMX128 field extractors
|
||||
|
||||
/// VA128 (bits 6-10, plus bit from 29)
|
||||
#[inline] pub fn va128(&self) -> usize {
|
||||
(extract_bits(self.raw, 6, 10) | (extract_bits(self.raw, 29, 29) << 5)) as usize
|
||||
}
|
||||
|
||||
/// VB128 (bits 16-20, plus bits from 28, 30)
|
||||
#[inline] pub fn vb128(&self) -> usize {
|
||||
(extract_bits(self.raw, 16, 20)
|
||||
| (extract_bits(self.raw, 28, 28) << 5)
|
||||
| (extract_bits(self.raw, 30, 30) << 6)) as usize
|
||||
}
|
||||
|
||||
/// VD128 (bits 6-10, plus bits from 21, 22)
|
||||
#[inline] pub fn vd128(&self) -> usize {
|
||||
(extract_bits(self.raw, 6, 10)
|
||||
| (extract_bits(self.raw, 21, 21) << 5)
|
||||
| (extract_bits(self.raw, 22, 22) << 6)) as usize
|
||||
}
|
||||
|
||||
/// VS128 - same encoding as VD128
|
||||
#[inline] pub fn vs128(&self) -> usize { self.vd128() }
|
||||
|
||||
/// NB field (bits 16-20) for lswi/stswi
|
||||
#[inline] pub fn nb(&self) -> u32 { extract_bits(self.raw, 16, 20) }
|
||||
}
|
||||
|
||||
/// Decode a 32-bit PPC instruction into its opcode.
|
||||
/// Direct translation of the C++ LookupOpcode from ppc_opcode_lookup_gen.cc.
|
||||
pub fn decode(raw: u32, addr: u32) -> DecodedInstr {
|
||||
let opcode = lookup_opcode(raw);
|
||||
DecodedInstr { opcode, raw, addr }
|
||||
}
|
||||
|
||||
fn lookup_opcode(code: u32) -> PpcOpcode {
|
||||
match extract_bits(code, 0, 5) {
|
||||
2 => PpcOpcode::tdi,
|
||||
3 => PpcOpcode::twi,
|
||||
4 => decode_op4(code),
|
||||
5 => decode_op5(code),
|
||||
6 => decode_op6(code),
|
||||
7 => PpcOpcode::mulli,
|
||||
8 => PpcOpcode::subficx,
|
||||
10 => PpcOpcode::cmpli,
|
||||
11 => PpcOpcode::cmpi,
|
||||
12 => PpcOpcode::addic,
|
||||
13 => PpcOpcode::addicx,
|
||||
14 => PpcOpcode::addi,
|
||||
15 => PpcOpcode::addis,
|
||||
16 => PpcOpcode::bcx,
|
||||
17 => PpcOpcode::sc,
|
||||
18 => PpcOpcode::bx,
|
||||
19 => decode_op19(code),
|
||||
20 => PpcOpcode::rlwimix,
|
||||
21 => PpcOpcode::rlwinmx,
|
||||
23 => PpcOpcode::rlwnmx,
|
||||
24 => PpcOpcode::ori,
|
||||
25 => PpcOpcode::oris,
|
||||
26 => PpcOpcode::xori,
|
||||
27 => PpcOpcode::xoris,
|
||||
28 => PpcOpcode::andix,
|
||||
29 => PpcOpcode::andisx,
|
||||
30 => decode_op30(code),
|
||||
31 => decode_op31(code),
|
||||
32 => PpcOpcode::lwz,
|
||||
33 => PpcOpcode::lwzu,
|
||||
34 => PpcOpcode::lbz,
|
||||
35 => PpcOpcode::lbzu,
|
||||
36 => PpcOpcode::stw,
|
||||
37 => PpcOpcode::stwu,
|
||||
38 => PpcOpcode::stb,
|
||||
39 => PpcOpcode::stbu,
|
||||
40 => PpcOpcode::lhz,
|
||||
41 => PpcOpcode::lhzu,
|
||||
42 => PpcOpcode::lha,
|
||||
43 => PpcOpcode::lhau,
|
||||
44 => PpcOpcode::sth,
|
||||
45 => PpcOpcode::sthu,
|
||||
46 => PpcOpcode::lmw,
|
||||
47 => PpcOpcode::stmw,
|
||||
48 => PpcOpcode::lfs,
|
||||
49 => PpcOpcode::lfsu,
|
||||
50 => PpcOpcode::lfd,
|
||||
51 => PpcOpcode::lfdu,
|
||||
52 => PpcOpcode::stfs,
|
||||
53 => PpcOpcode::stfsu,
|
||||
54 => PpcOpcode::stfd,
|
||||
55 => PpcOpcode::stfdu,
|
||||
58 => match extract_bits(code, 30, 31) {
|
||||
0b00 => PpcOpcode::ld,
|
||||
0b01 => PpcOpcode::ldu,
|
||||
0b10 => PpcOpcode::lwa,
|
||||
_ => PpcOpcode::Invalid,
|
||||
},
|
||||
59 => match extract_bits(code, 26, 30) {
|
||||
0b10010 => PpcOpcode::fdivsx,
|
||||
0b10100 => PpcOpcode::fsubsx,
|
||||
0b10101 => PpcOpcode::faddsx,
|
||||
0b10110 => PpcOpcode::fsqrtsx,
|
||||
0b11000 => PpcOpcode::fresx,
|
||||
0b11001 => PpcOpcode::fmulsx,
|
||||
0b11100 => PpcOpcode::fmsubsx,
|
||||
0b11101 => PpcOpcode::fmaddsx,
|
||||
0b11110 => PpcOpcode::fnmsubsx,
|
||||
0b11111 => PpcOpcode::fnmaddsx,
|
||||
_ => PpcOpcode::Invalid,
|
||||
},
|
||||
62 => match extract_bits(code, 30, 31) {
|
||||
0b00 => PpcOpcode::std,
|
||||
0b01 => PpcOpcode::stdu,
|
||||
_ => PpcOpcode::Invalid,
|
||||
},
|
||||
63 => decode_op63(code),
|
||||
_ => PpcOpcode::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_op4(code: u32) -> PpcOpcode {
|
||||
// VMX128 load/store (op=4, bits 21-27 << 4 | bits 30-31)
|
||||
let key1 = (extract_bits(code, 21, 27) << 4) | extract_bits(code, 30, 31);
|
||||
match key1 {
|
||||
0b00000000011 => return PpcOpcode::lvsl128,
|
||||
0b00001000011 => return PpcOpcode::lvsr128,
|
||||
0b00010000011 => return PpcOpcode::lvewx128,
|
||||
0b00011000011 => return PpcOpcode::lvx128,
|
||||
0b00110000011 => return PpcOpcode::stvewx128,
|
||||
0b00111000011 => return PpcOpcode::stvx128,
|
||||
0b01011000011 => return PpcOpcode::lvxl128,
|
||||
0b01111000011 => return PpcOpcode::stvxl128,
|
||||
0b10000000011 => return PpcOpcode::lvlx128,
|
||||
0b10001000011 => return PpcOpcode::lvrx128,
|
||||
0b10100000011 => return PpcOpcode::stvlx128,
|
||||
0b10101000011 => return PpcOpcode::stvrx128,
|
||||
0b11000000011 => return PpcOpcode::lvlxl128,
|
||||
0b11001000011 => return PpcOpcode::lvrxl128,
|
||||
0b11100000011 => return PpcOpcode::stvlxl128,
|
||||
0b11101000011 => return PpcOpcode::stvrxl128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Standard VMX (op=4, bits 21-31)
|
||||
let key2 = extract_bits(code, 21, 31);
|
||||
match key2 {
|
||||
0b00000000000 => return PpcOpcode::vaddubm,
|
||||
0b00000000010 => return PpcOpcode::vmaxub,
|
||||
0b00000000100 => return PpcOpcode::vrlb,
|
||||
0b00000001000 => return PpcOpcode::vmuloub,
|
||||
0b00000001010 => return PpcOpcode::vaddfp,
|
||||
0b00000001100 => return PpcOpcode::vmrghb,
|
||||
0b00000001110 => return PpcOpcode::vpkuhum,
|
||||
0b00001000000 => return PpcOpcode::vadduhm,
|
||||
0b00001000010 => return PpcOpcode::vmaxuh,
|
||||
0b00001000100 => return PpcOpcode::vrlh,
|
||||
0b00001001000 => return PpcOpcode::vmulouh,
|
||||
0b00001001010 => return PpcOpcode::vsubfp,
|
||||
0b00001001100 => return PpcOpcode::vmrghh,
|
||||
0b00001001110 => return PpcOpcode::vpkuwum,
|
||||
0b00010000000 => return PpcOpcode::vadduwm,
|
||||
0b00010000010 => return PpcOpcode::vmaxuw,
|
||||
0b00010000100 => return PpcOpcode::vrlw,
|
||||
0b00010001100 => return PpcOpcode::vmrghw,
|
||||
0b00010001110 => return PpcOpcode::vpkuhus,
|
||||
0b00011001110 => return PpcOpcode::vpkuwus,
|
||||
0b00100000010 => return PpcOpcode::vmaxsb,
|
||||
0b00100000100 => return PpcOpcode::vslb,
|
||||
0b00100001000 => return PpcOpcode::vmulosb,
|
||||
0b00100001010 => return PpcOpcode::vrefp,
|
||||
0b00100001100 => return PpcOpcode::vmrglb,
|
||||
0b00100001110 => return PpcOpcode::vpkshus,
|
||||
0b00101000010 => return PpcOpcode::vmaxsh,
|
||||
0b00101000100 => return PpcOpcode::vslh,
|
||||
0b00101001000 => return PpcOpcode::vmulosh,
|
||||
0b00101001010 => return PpcOpcode::vrsqrtefp,
|
||||
0b00101001100 => return PpcOpcode::vmrglh,
|
||||
0b00101001110 => return PpcOpcode::vpkswus,
|
||||
0b00110000000 => return PpcOpcode::vaddcuw,
|
||||
0b00110000010 => return PpcOpcode::vmaxsw,
|
||||
0b00110000100 => return PpcOpcode::vslw,
|
||||
0b00110001010 => return PpcOpcode::vexptefp,
|
||||
0b00110001100 => return PpcOpcode::vmrglw,
|
||||
0b00110001110 => return PpcOpcode::vpkshss,
|
||||
0b00111000100 => return PpcOpcode::vsl,
|
||||
0b00111001010 => return PpcOpcode::vlogefp,
|
||||
0b00111001110 => return PpcOpcode::vpkswss,
|
||||
0b01000000000 => return PpcOpcode::vaddubs,
|
||||
0b01000000010 => return PpcOpcode::vminub,
|
||||
0b01000000100 => return PpcOpcode::vsrb,
|
||||
0b01000001000 => return PpcOpcode::vmuleub,
|
||||
0b01000001010 => return PpcOpcode::vrfin,
|
||||
0b01000001100 => return PpcOpcode::vspltb,
|
||||
0b01000001110 => return PpcOpcode::vupkhsb,
|
||||
0b01001000000 => return PpcOpcode::vadduhs,
|
||||
0b01001000010 => return PpcOpcode::vminuh,
|
||||
0b01001000100 => return PpcOpcode::vsrh,
|
||||
0b01001001000 => return PpcOpcode::vmuleuh,
|
||||
0b01001001010 => return PpcOpcode::vrfiz,
|
||||
0b01001001100 => return PpcOpcode::vsplth,
|
||||
0b01001001110 => return PpcOpcode::vupkhsh,
|
||||
0b01010000000 => return PpcOpcode::vadduws,
|
||||
0b01010000010 => return PpcOpcode::vminuw,
|
||||
0b01010000100 => return PpcOpcode::vsrw,
|
||||
0b01010001010 => return PpcOpcode::vrfip,
|
||||
0b01010001100 => return PpcOpcode::vspltw,
|
||||
0b01010001110 => return PpcOpcode::vupklsb,
|
||||
0b01011000100 => return PpcOpcode::vsr,
|
||||
0b01011001010 => return PpcOpcode::vrfim,
|
||||
0b01011001110 => return PpcOpcode::vupklsh,
|
||||
0b01100000000 => return PpcOpcode::vaddsbs,
|
||||
0b01100000010 => return PpcOpcode::vminsb,
|
||||
0b01100000100 => return PpcOpcode::vsrab,
|
||||
0b01100001000 => return PpcOpcode::vmulesb,
|
||||
0b01100001010 => return PpcOpcode::vcfux,
|
||||
0b01100001100 => return PpcOpcode::vspltisb,
|
||||
0b01100001110 => return PpcOpcode::vpkpx,
|
||||
0b01101000000 => return PpcOpcode::vaddshs,
|
||||
0b01101000010 => return PpcOpcode::vminsh,
|
||||
0b01101000100 => return PpcOpcode::vsrah,
|
||||
0b01101001000 => return PpcOpcode::vmulesh,
|
||||
0b01101001010 => return PpcOpcode::vcfsx,
|
||||
0b01101001100 => return PpcOpcode::vspltish,
|
||||
0b01101001110 => return PpcOpcode::vupkhpx,
|
||||
0b01110000000 => return PpcOpcode::vaddsws,
|
||||
0b01110000010 => return PpcOpcode::vminsw,
|
||||
0b01110000100 => return PpcOpcode::vsraw,
|
||||
0b01110001010 => return PpcOpcode::vctuxs,
|
||||
0b01110001100 => return PpcOpcode::vspltisw,
|
||||
0b01111001010 => return PpcOpcode::vctsxs,
|
||||
0b01111001110 => return PpcOpcode::vupklpx,
|
||||
0b10000000000 => return PpcOpcode::vsububm,
|
||||
0b10000000010 => return PpcOpcode::vavgub,
|
||||
0b10000000100 => return PpcOpcode::vand,
|
||||
0b10000001010 => return PpcOpcode::vmaxfp,
|
||||
0b10000001100 => return PpcOpcode::vslo,
|
||||
0b10001000000 => return PpcOpcode::vsubuhm,
|
||||
0b10001000010 => return PpcOpcode::vavguh,
|
||||
0b10001000100 => return PpcOpcode::vandc,
|
||||
0b10001001010 => return PpcOpcode::vminfp,
|
||||
0b10001001100 => return PpcOpcode::vsro,
|
||||
0b10010000000 => return PpcOpcode::vsubuwm,
|
||||
0b10010000010 => return PpcOpcode::vavguw,
|
||||
0b10010000100 => return PpcOpcode::vor,
|
||||
0b10011000100 => return PpcOpcode::vxor,
|
||||
0b10100000010 => return PpcOpcode::vavgsb,
|
||||
0b10100000100 => return PpcOpcode::vnor,
|
||||
0b10101000010 => return PpcOpcode::vavgsh,
|
||||
0b10110000000 => return PpcOpcode::vsubcuw,
|
||||
0b10110000010 => return PpcOpcode::vavgsw,
|
||||
0b11000000000 => return PpcOpcode::vsububs,
|
||||
0b11000000100 => return PpcOpcode::mfvscr,
|
||||
0b11000001000 => return PpcOpcode::vsum4ubs,
|
||||
0b11001000000 => return PpcOpcode::vsubuhs,
|
||||
0b11001000100 => return PpcOpcode::mtvscr,
|
||||
0b11001001000 => return PpcOpcode::vsum4shs,
|
||||
0b11010000000 => return PpcOpcode::vsubuws,
|
||||
0b11010001000 => return PpcOpcode::vsum2sws,
|
||||
0b11100000000 => return PpcOpcode::vsubsbs,
|
||||
0b11100001000 => return PpcOpcode::vsum4sbs,
|
||||
0b11101000000 => return PpcOpcode::vsubshs,
|
||||
0b11110000000 => return PpcOpcode::vsubsws,
|
||||
0b11110001000 => return PpcOpcode::vsumsws,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// VMX compare (op=4, bits 22-31)
|
||||
let key3 = extract_bits(code, 22, 31);
|
||||
match key3 {
|
||||
0b0000000110 => return PpcOpcode::vcmpequb,
|
||||
0b0001000110 => return PpcOpcode::vcmpequh,
|
||||
0b0010000110 => return PpcOpcode::vcmpequw,
|
||||
0b0011000110 => return PpcOpcode::vcmpeqfp,
|
||||
0b0111000110 => return PpcOpcode::vcmpgefp,
|
||||
0b1000000110 => return PpcOpcode::vcmpgtub,
|
||||
0b1001000110 => return PpcOpcode::vcmpgtuh,
|
||||
0b1010000110 => return PpcOpcode::vcmpgtuw,
|
||||
0b1011000110 => return PpcOpcode::vcmpgtfp,
|
||||
0b1100000110 => return PpcOpcode::vcmpgtsb,
|
||||
0b1101000110 => return PpcOpcode::vcmpgtsh,
|
||||
0b1110000110 => return PpcOpcode::vcmpgtsw,
|
||||
0b1111000110 => return PpcOpcode::vcmpbfp,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// VMX 4-operand (op=4, bits 26-31)
|
||||
let key4 = extract_bits(code, 26, 31);
|
||||
match key4 {
|
||||
0b100000 => return PpcOpcode::vmhaddshs,
|
||||
0b100001 => return PpcOpcode::vmhraddshs,
|
||||
0b100010 => return PpcOpcode::vmladduhm,
|
||||
0b100100 => return PpcOpcode::vmsumubm,
|
||||
0b100101 => return PpcOpcode::vmsummbm,
|
||||
0b100110 => return PpcOpcode::vmsumuhm,
|
||||
0b100111 => return PpcOpcode::vmsumuhs,
|
||||
0b101000 => return PpcOpcode::vmsumshm,
|
||||
0b101001 => return PpcOpcode::vmsumshs,
|
||||
0b101010 => return PpcOpcode::vsel,
|
||||
0b101011 => return PpcOpcode::vperm,
|
||||
0b101100 => return PpcOpcode::vsldoi,
|
||||
0b101110 => return PpcOpcode::vmaddfp,
|
||||
0b101111 => return PpcOpcode::vnmsubfp,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// vsldoi128 (op=4, bit 27)
|
||||
if extract_bits(code, 27, 27) == 1 {
|
||||
return PpcOpcode::vsldoi128;
|
||||
}
|
||||
|
||||
PpcOpcode::Invalid
|
||||
}
|
||||
|
||||
fn decode_op5(code: u32) -> PpcOpcode {
|
||||
// vperm128 (op=5, bits 22,27)
|
||||
let key1 = (extract_bits(code, 22, 22) << 5) | extract_bits(code, 27, 27);
|
||||
if key1 == 0b000000 {
|
||||
return PpcOpcode::vperm128;
|
||||
}
|
||||
|
||||
let key2 = (extract_bits(code, 22, 25) << 2) | extract_bits(code, 27, 27);
|
||||
match key2 {
|
||||
0b000001 => PpcOpcode::vaddfp128,
|
||||
0b000101 => PpcOpcode::vsubfp128,
|
||||
0b001001 => PpcOpcode::vmulfp128,
|
||||
0b001101 => PpcOpcode::vmaddfp128,
|
||||
0b010001 => PpcOpcode::vmaddcfp128,
|
||||
0b010101 => PpcOpcode::vnmsubfp128,
|
||||
0b011001 => PpcOpcode::vmsum3fp128,
|
||||
0b011101 => PpcOpcode::vmsum4fp128,
|
||||
0b100000 => PpcOpcode::vpkshss128,
|
||||
0b100001 => PpcOpcode::vand128,
|
||||
0b100100 => PpcOpcode::vpkshus128,
|
||||
0b100101 => PpcOpcode::vandc128,
|
||||
0b101000 => PpcOpcode::vpkswss128,
|
||||
0b101001 => PpcOpcode::vnor128,
|
||||
0b101100 => PpcOpcode::vpkswus128,
|
||||
0b101101 => PpcOpcode::vor128,
|
||||
0b110000 => PpcOpcode::vpkuhum128,
|
||||
0b110001 => PpcOpcode::vxor128,
|
||||
0b110100 => PpcOpcode::vpkuhus128,
|
||||
0b110101 => PpcOpcode::vsel128,
|
||||
0b111000 => PpcOpcode::vpkuwum128,
|
||||
0b111001 => PpcOpcode::vslo128,
|
||||
0b111100 => PpcOpcode::vpkuwus128,
|
||||
0b111101 => PpcOpcode::vsro128,
|
||||
_ => PpcOpcode::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_op6(code: u32) -> PpcOpcode {
|
||||
// vpermwi128
|
||||
let key1 = (extract_bits(code, 21, 22) << 5) | extract_bits(code, 26, 27);
|
||||
if key1 == 0b0100001 {
|
||||
return PpcOpcode::vpermwi128;
|
||||
}
|
||||
|
||||
// vpkd3d128, vrlimi128
|
||||
let key2 = (extract_bits(code, 21, 23) << 4) | extract_bits(code, 26, 27);
|
||||
match key2 {
|
||||
0b1100001 => return PpcOpcode::vpkd3d128,
|
||||
0b1110001 => return PpcOpcode::vrlimi128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Unary VMX128 ops
|
||||
let key3 = extract_bits(code, 21, 27);
|
||||
match key3 {
|
||||
0b0100011 => return PpcOpcode::vcfpsxws128,
|
||||
0b0100111 => return PpcOpcode::vcfpuxws128,
|
||||
0b0101011 => return PpcOpcode::vcsxwfp128,
|
||||
0b0101111 => return PpcOpcode::vcuxwfp128,
|
||||
0b0110011 => return PpcOpcode::vrfim128,
|
||||
0b0110111 => return PpcOpcode::vrfin128,
|
||||
0b0111011 => return PpcOpcode::vrfip128,
|
||||
0b0111111 => return PpcOpcode::vrfiz128,
|
||||
0b1100011 => return PpcOpcode::vrefp128,
|
||||
0b1100111 => return PpcOpcode::vrsqrtefp128,
|
||||
0b1101011 => return PpcOpcode::vexptefp128,
|
||||
0b1101111 => return PpcOpcode::vlogefp128,
|
||||
0b1110011 => return PpcOpcode::vspltw128,
|
||||
0b1110111 => return PpcOpcode::vspltisw128,
|
||||
0b1111111 => return PpcOpcode::vupkd3d128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// VMX128 compare
|
||||
let key4 = (extract_bits(code, 22, 24) << 3) | extract_bits(code, 27, 27);
|
||||
match key4 {
|
||||
0b000000 => return PpcOpcode::vcmpeqfp128,
|
||||
0b001000 => return PpcOpcode::vcmpgefp128,
|
||||
0b010000 => return PpcOpcode::vcmpgtfp128,
|
||||
0b011000 => return PpcOpcode::vcmpbfp128,
|
||||
0b100000 => return PpcOpcode::vcmpequw128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// VMX128 shift/merge
|
||||
let key5 = (extract_bits(code, 22, 25) << 2) | extract_bits(code, 27, 27);
|
||||
match key5 {
|
||||
0b000101 => return PpcOpcode::vrlw128,
|
||||
0b001101 => return PpcOpcode::vslw128,
|
||||
0b010101 => return PpcOpcode::vsraw128,
|
||||
0b011101 => return PpcOpcode::vsrw128,
|
||||
0b101000 => return PpcOpcode::vmaxfp128,
|
||||
0b101100 => return PpcOpcode::vminfp128,
|
||||
0b110000 => return PpcOpcode::vmrghw128,
|
||||
0b110100 => return PpcOpcode::vmrglw128,
|
||||
0b111000 => return PpcOpcode::vupkhsb128,
|
||||
0b111100 => return PpcOpcode::vupklsb128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
PpcOpcode::Invalid
|
||||
}
|
||||
|
||||
fn decode_op19(code: u32) -> PpcOpcode {
|
||||
match extract_bits(code, 21, 30) {
|
||||
0b0000000000 => PpcOpcode::mcrf,
|
||||
0b0000010000 => PpcOpcode::bclrx,
|
||||
0b0000100001 => PpcOpcode::crnor,
|
||||
0b0010000001 => PpcOpcode::crandc,
|
||||
0b0010010110 => PpcOpcode::isync,
|
||||
0b0011000001 => PpcOpcode::crxor,
|
||||
0b0011100001 => PpcOpcode::crnand,
|
||||
0b0100000001 => PpcOpcode::crand,
|
||||
0b0100100001 => PpcOpcode::creqv,
|
||||
0b0110100001 => PpcOpcode::crorc,
|
||||
0b0111000001 => PpcOpcode::cror,
|
||||
0b1000010000 => PpcOpcode::bcctrx,
|
||||
_ => PpcOpcode::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_op30(code: u32) -> PpcOpcode {
|
||||
match extract_bits(code, 27, 29) {
|
||||
0b000 => PpcOpcode::rldiclx,
|
||||
0b001 => PpcOpcode::rldicrx,
|
||||
0b010 => PpcOpcode::rldicx,
|
||||
0b011 => PpcOpcode::rldimix,
|
||||
_ => match extract_bits(code, 27, 30) {
|
||||
0b1000 => PpcOpcode::rldclx,
|
||||
0b1001 => PpcOpcode::rldcrx,
|
||||
_ => PpcOpcode::Invalid,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_op31(code: u32) -> PpcOpcode {
|
||||
// sradix has a unique 10-bit key (bits 21-29)
|
||||
if extract_bits(code, 21, 29) == 0b110011101 {
|
||||
return PpcOpcode::sradix;
|
||||
}
|
||||
|
||||
// Main op31 table (bits 21-30)
|
||||
let key = extract_bits(code, 21, 30);
|
||||
match key {
|
||||
0b0000000000 => return PpcOpcode::cmp,
|
||||
0b0000000100 => return PpcOpcode::tw,
|
||||
0b0000000110 => return PpcOpcode::lvsl,
|
||||
0b0000000111 => return PpcOpcode::lvebx,
|
||||
0b0000010011 => return PpcOpcode::mfcr,
|
||||
0b0000010100 => return PpcOpcode::lwarx,
|
||||
0b0000010101 => return PpcOpcode::ldx,
|
||||
0b0000010111 => return PpcOpcode::lwzx,
|
||||
0b0000011000 => return PpcOpcode::slwx,
|
||||
0b0000011010 => return PpcOpcode::cntlzwx,
|
||||
0b0000011011 => return PpcOpcode::sldx,
|
||||
0b0000011100 => return PpcOpcode::andx,
|
||||
0b0000100000 => return PpcOpcode::cmpl,
|
||||
0b0000100110 => return PpcOpcode::lvsr,
|
||||
0b0000100111 => return PpcOpcode::lvehx,
|
||||
0b0000110101 => return PpcOpcode::ldux,
|
||||
0b0000110110 => return PpcOpcode::dcbst,
|
||||
0b0000110111 => return PpcOpcode::lwzux,
|
||||
0b0000111010 => return PpcOpcode::cntlzdx,
|
||||
0b0000111100 => return PpcOpcode::andcx,
|
||||
0b0001000100 => return PpcOpcode::td,
|
||||
0b0001000111 => return PpcOpcode::lvewx,
|
||||
0b0001010011 => return PpcOpcode::mfmsr,
|
||||
0b0001010100 => return PpcOpcode::ldarx,
|
||||
0b0001010110 => return PpcOpcode::dcbf,
|
||||
0b0001010111 => return PpcOpcode::lbzx,
|
||||
0b0001100111 => return PpcOpcode::lvx,
|
||||
0b0001110111 => return PpcOpcode::lbzux,
|
||||
0b0001111100 => return PpcOpcode::norx,
|
||||
0b0010000111 => return PpcOpcode::stvebx,
|
||||
0b0010010000 => return PpcOpcode::mtcrf,
|
||||
0b0010010010 => return PpcOpcode::mtmsr,
|
||||
0b0010010101 => return PpcOpcode::stdx,
|
||||
0b0010010110 => return PpcOpcode::stwcx,
|
||||
0b0010010111 => return PpcOpcode::stwx,
|
||||
0b0010100111 => return PpcOpcode::stvehx,
|
||||
0b0010110010 => return PpcOpcode::mtmsrd,
|
||||
0b0010110101 => return PpcOpcode::stdux,
|
||||
0b0010110111 => return PpcOpcode::stwux,
|
||||
0b0011000111 => return PpcOpcode::stvewx,
|
||||
0b0011010110 => return PpcOpcode::stdcx,
|
||||
0b0011010111 => return PpcOpcode::stbx,
|
||||
0b0011100111 => return PpcOpcode::stvx,
|
||||
0b0011110110 => return PpcOpcode::dcbtst,
|
||||
0b0011110111 => return PpcOpcode::stbux,
|
||||
0b0100010110 => return PpcOpcode::dcbt,
|
||||
0b0100010111 => return PpcOpcode::lhzx,
|
||||
0b0100011100 => return PpcOpcode::eqvx,
|
||||
0b0100110111 => return PpcOpcode::lhzux,
|
||||
0b0100111100 => return PpcOpcode::xorx,
|
||||
0b0101010011 => return PpcOpcode::mfspr,
|
||||
0b0101010101 => return PpcOpcode::lwax,
|
||||
0b0101010111 => return PpcOpcode::lhax,
|
||||
0b0101100111 => return PpcOpcode::lvxl,
|
||||
0b0101110011 => return PpcOpcode::mftb,
|
||||
0b0101110101 => return PpcOpcode::lwaux,
|
||||
0b0101110111 => return PpcOpcode::lhaux,
|
||||
0b0110010111 => return PpcOpcode::sthx,
|
||||
0b0110011100 => return PpcOpcode::orcx,
|
||||
0b0110110111 => return PpcOpcode::sthux,
|
||||
0b0110111100 => return PpcOpcode::orx,
|
||||
0b0111010011 => return PpcOpcode::mtspr,
|
||||
0b0111010110 => return PpcOpcode::dcbi,
|
||||
0b0111011100 => return PpcOpcode::nandx,
|
||||
0b0111100111 => return PpcOpcode::stvxl,
|
||||
0b1000000000 => return PpcOpcode::mcrxr,
|
||||
0b1000000111 => return PpcOpcode::lvlx,
|
||||
0b1000010100 => return PpcOpcode::ldbrx,
|
||||
0b1000010101 => return PpcOpcode::lswx,
|
||||
0b1000010110 => return PpcOpcode::lwbrx,
|
||||
0b1000010111 => return PpcOpcode::lfsx,
|
||||
0b1000011000 => return PpcOpcode::srwx,
|
||||
0b1000011011 => return PpcOpcode::srdx,
|
||||
0b1000100111 => return PpcOpcode::lvrx,
|
||||
0b1000110111 => return PpcOpcode::lfsux,
|
||||
0b1001010101 => return PpcOpcode::lswi,
|
||||
0b1001010110 => return PpcOpcode::sync,
|
||||
0b1001010111 => return PpcOpcode::lfdx,
|
||||
0b1001110111 => return PpcOpcode::lfdux,
|
||||
0b1010000111 => return PpcOpcode::stvlx,
|
||||
0b1010010100 => return PpcOpcode::stdbrx,
|
||||
0b1010010101 => return PpcOpcode::stswx,
|
||||
0b1010010110 => return PpcOpcode::stwbrx,
|
||||
0b1010010111 => return PpcOpcode::stfsx,
|
||||
0b1010100111 => return PpcOpcode::stvrx,
|
||||
0b1010110111 => return PpcOpcode::stfsux,
|
||||
0b1011010101 => return PpcOpcode::stswi,
|
||||
0b1011010111 => return PpcOpcode::stfdx,
|
||||
0b1011110111 => return PpcOpcode::stfdux,
|
||||
0b1100000111 => return PpcOpcode::lvlxl,
|
||||
0b1100010110 => return PpcOpcode::lhbrx,
|
||||
0b1100011000 => return PpcOpcode::srawx,
|
||||
0b1100011010 => return PpcOpcode::sradx,
|
||||
0b1100100111 => return PpcOpcode::lvrxl,
|
||||
0b1100111000 => return PpcOpcode::srawix,
|
||||
0b1101010110 => return PpcOpcode::eieio,
|
||||
0b1110000111 => return PpcOpcode::stvlxl,
|
||||
0b1110010110 => return PpcOpcode::sthbrx,
|
||||
0b1110011010 => return PpcOpcode::extshx,
|
||||
0b1110100111 => return PpcOpcode::stvrxl,
|
||||
0b1110111010 => return PpcOpcode::extsbx,
|
||||
0b1111010110 => return PpcOpcode::icbi,
|
||||
0b1111010111 => return PpcOpcode::stfiwx,
|
||||
0b1111011010 => return PpcOpcode::extswx,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Arithmetic op31 (bits 22-30)
|
||||
let key2 = extract_bits(code, 22, 30);
|
||||
match key2 {
|
||||
0b000001000 => return PpcOpcode::subfcx,
|
||||
0b000001001 => return PpcOpcode::mulhdux,
|
||||
0b000001010 => return PpcOpcode::addcx,
|
||||
0b000001011 => return PpcOpcode::mulhwux,
|
||||
0b000101000 => return PpcOpcode::subfx,
|
||||
0b001001001 => return PpcOpcode::mulhdx,
|
||||
0b001001011 => return PpcOpcode::mulhwx,
|
||||
0b001101000 => return PpcOpcode::negx,
|
||||
0b010001000 => return PpcOpcode::subfex,
|
||||
0b010001010 => return PpcOpcode::addex,
|
||||
0b011001000 => return PpcOpcode::subfzex,
|
||||
0b011001010 => return PpcOpcode::addzex,
|
||||
0b011101000 => return PpcOpcode::subfmex,
|
||||
0b011101001 => return PpcOpcode::mulldx,
|
||||
0b011101010 => return PpcOpcode::addmex,
|
||||
0b011101011 => return PpcOpcode::mullwx,
|
||||
0b100001010 => return PpcOpcode::addx,
|
||||
0b111001001 => return PpcOpcode::divdux,
|
||||
0b111001011 => return PpcOpcode::divwux,
|
||||
0b111101001 => return PpcOpcode::divdx,
|
||||
0b111101011 => return PpcOpcode::divwx,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// dcbz/dcbz128 special case
|
||||
let key3 = (extract_bits(code, 6, 10) << 20) | (extract_bits(code, 21, 30));
|
||||
match key3 {
|
||||
0b0000000000000001111110110 => return PpcOpcode::dcbz,
|
||||
0b0000100000000001111110110 => return PpcOpcode::dcbz128,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
PpcOpcode::Invalid
|
||||
}
|
||||
|
||||
fn decode_op63(code: u32) -> PpcOpcode {
|
||||
// Primary op63 table (bits 21-30)
|
||||
match extract_bits(code, 21, 30) {
|
||||
0b0000000000 => return PpcOpcode::fcmpu,
|
||||
0b0000001100 => return PpcOpcode::frspx,
|
||||
0b0000001110 => return PpcOpcode::fctiwx,
|
||||
0b0000001111 => return PpcOpcode::fctiwzx,
|
||||
0b0000100000 => return PpcOpcode::fcmpo,
|
||||
0b0000100110 => return PpcOpcode::mtfsb1x,
|
||||
0b0000101000 => return PpcOpcode::fnegx,
|
||||
0b0001000000 => return PpcOpcode::mcrfs,
|
||||
0b0001000110 => return PpcOpcode::mtfsb0x,
|
||||
0b0001001000 => return PpcOpcode::fmrx,
|
||||
0b0010000110 => return PpcOpcode::mtfsfix,
|
||||
0b0010001000 => return PpcOpcode::fnabsx,
|
||||
0b0100001000 => return PpcOpcode::fabsx,
|
||||
0b1001000111 => return PpcOpcode::mffsx,
|
||||
0b1011000111 => return PpcOpcode::mtfsfx,
|
||||
0b1100101110 => return PpcOpcode::fctidx,
|
||||
0b1100101111 => return PpcOpcode::fctidzx,
|
||||
0b1101001110 => return PpcOpcode::fcfidx,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// FPU arithmetic (bits 26-30)
|
||||
match extract_bits(code, 26, 30) {
|
||||
0b10010 => PpcOpcode::fdivx,
|
||||
0b10100 => PpcOpcode::fsubx,
|
||||
0b10101 => PpcOpcode::faddx,
|
||||
0b10110 => PpcOpcode::fsqrtx,
|
||||
0b10111 => PpcOpcode::fselx,
|
||||
0b11001 => PpcOpcode::fmulx,
|
||||
0b11010 => PpcOpcode::frsqrtex,
|
||||
0b11100 => PpcOpcode::fmsubx,
|
||||
0b11101 => PpcOpcode::fmaddx,
|
||||
0b11110 => PpcOpcode::fnmsubx,
|
||||
0b11111 => PpcOpcode::fnmaddx,
|
||||
_ => PpcOpcode::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_decode_addi() {
|
||||
// addi r3, r1, 0x10 => opcode 14, rD=3, rA=1, SIMM=0x10
|
||||
let raw: u32 = (14 << 26) | (3 << 21) | (1 << 16) | 0x10;
|
||||
let instr = decode(raw, 0);
|
||||
assert_eq!(instr.opcode, PpcOpcode::addi);
|
||||
assert_eq!(instr.rd(), 3);
|
||||
assert_eq!(instr.ra(), 1);
|
||||
assert_eq!(instr.simm16(), 0x10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_lwz() {
|
||||
// lwz r5, 0x20(r1) => opcode 32
|
||||
let raw: u32 = (32 << 26) | (5 << 21) | (1 << 16) | 0x20;
|
||||
let instr = decode(raw, 0);
|
||||
assert_eq!(instr.opcode, PpcOpcode::lwz);
|
||||
assert_eq!(instr.rd(), 5);
|
||||
assert_eq!(instr.ra(), 1);
|
||||
assert_eq!(instr.d(), 0x20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_branch() {
|
||||
// b +0x100 => opcode 18, LI=0x40 (shifted left 2 = 0x100), AA=0, LK=0
|
||||
let raw: u32 = (18 << 26) | (0x40 << 2);
|
||||
let instr = decode(raw, 0);
|
||||
assert_eq!(instr.opcode, PpcOpcode::bx);
|
||||
assert_eq!(instr.li(), 0x100);
|
||||
assert!(!instr.aa());
|
||||
assert!(!instr.lk());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_stw() {
|
||||
// stw r7, 0x8(r2)
|
||||
let raw: u32 = (36 << 26) | (7 << 21) | (2 << 16) | 0x8;
|
||||
let instr = decode(raw, 0);
|
||||
assert_eq!(instr.opcode, PpcOpcode::stw);
|
||||
assert_eq!(instr.rs(), 7);
|
||||
assert_eq!(instr.ra(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_ori_nop() {
|
||||
// ori r0, r0, 0 = NOP
|
||||
let raw: u32 = 24 << 26;
|
||||
let instr = decode(raw, 0);
|
||||
assert_eq!(instr.opcode, PpcOpcode::ori);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bits() {
|
||||
assert_eq!(extract_bits(0xFFFF_FFFF, 0, 5), 0x3F);
|
||||
assert_eq!(extract_bits(0x8000_0000, 0, 0), 1);
|
||||
assert_eq!(extract_bits(0x0000_0001, 31, 31), 1);
|
||||
}
|
||||
}
|
||||
276
crates/xenia-cpu/src/disasm.rs
Normal file
276
crates/xenia-cpu/src/disasm.rs
Normal file
@@ -0,0 +1,276 @@
|
||||
use crate::decoder::DecodedInstr;
|
||||
use crate::opcode::PpcOpcode;
|
||||
use std::fmt::Write;
|
||||
|
||||
/// Disassemble a decoded instruction into PPC assembly text.
|
||||
pub fn disassemble(instr: &DecodedInstr) -> String {
|
||||
let mut out = String::new();
|
||||
match instr.opcode {
|
||||
// Branch instructions
|
||||
PpcOpcode::bx => {
|
||||
let target = if instr.aa() {
|
||||
instr.li() as u32
|
||||
} else {
|
||||
instr.addr.wrapping_add(instr.li() as u32)
|
||||
};
|
||||
let mnemonic = if instr.lk() { "bl" } else { "b" };
|
||||
write!(out, "{} 0x{:08X}", mnemonic, target).unwrap();
|
||||
}
|
||||
PpcOpcode::bcx => {
|
||||
let bo = instr.bo();
|
||||
let bi = instr.bi();
|
||||
let target = if instr.aa() {
|
||||
instr.bd() as u32
|
||||
} else {
|
||||
instr.addr.wrapping_add(instr.bd() as u32)
|
||||
};
|
||||
let mnemonic = if instr.lk() { "bcl" } else { "bc" };
|
||||
write!(out, "{} {},{},0x{:08X}", mnemonic, bo, bi, target).unwrap();
|
||||
}
|
||||
PpcOpcode::bclrx => {
|
||||
let mnemonic = if instr.lk() { "bclrl" } else { "bclr" };
|
||||
write!(out, "{} {},{}", mnemonic, instr.bo(), instr.bi()).unwrap();
|
||||
}
|
||||
PpcOpcode::bcctrx => {
|
||||
let mnemonic = if instr.lk() { "bcctrl" } else { "bcctr" };
|
||||
write!(out, "{} {},{}", mnemonic, instr.bo(), instr.bi()).unwrap();
|
||||
}
|
||||
|
||||
// System call
|
||||
PpcOpcode::sc => {
|
||||
write!(out, "sc").unwrap();
|
||||
}
|
||||
|
||||
// D-form load/store
|
||||
PpcOpcode::lwz | PpcOpcode::lwzu | PpcOpcode::lbz | PpcOpcode::lbzu |
|
||||
PpcOpcode::lhz | PpcOpcode::lhzu | PpcOpcode::lha | PpcOpcode::lhau |
|
||||
PpcOpcode::lfs | PpcOpcode::lfsu | PpcOpcode::lfd | PpcOpcode::lfdu => {
|
||||
write!(out, "{:?} r{},{}(r{})", instr.opcode, instr.rd(), instr.d(), instr.ra()).unwrap();
|
||||
}
|
||||
PpcOpcode::stw | PpcOpcode::stwu | PpcOpcode::stb | PpcOpcode::stbu |
|
||||
PpcOpcode::sth | PpcOpcode::sthu |
|
||||
PpcOpcode::stfs | PpcOpcode::stfsu | PpcOpcode::stfd | PpcOpcode::stfdu => {
|
||||
write!(out, "{:?} r{},{}(r{})", instr.opcode, instr.rs(), instr.d(), instr.ra()).unwrap();
|
||||
}
|
||||
|
||||
// D-form immediate ALU
|
||||
PpcOpcode::addi | PpcOpcode::addis | PpcOpcode::addic | PpcOpcode::addicx |
|
||||
PpcOpcode::subficx | PpcOpcode::mulli => {
|
||||
write!(out, "{:?} r{},r{},{}", instr.opcode, instr.rd(), instr.ra(), instr.simm16()).unwrap();
|
||||
}
|
||||
|
||||
// D-form immediate logical
|
||||
PpcOpcode::ori | PpcOpcode::oris | PpcOpcode::xori | PpcOpcode::xoris |
|
||||
PpcOpcode::andix | PpcOpcode::andisx => {
|
||||
write!(out, "{:?} r{},r{},0x{:04X}", instr.opcode, instr.ra(), instr.rs(), instr.uimm16()).unwrap();
|
||||
}
|
||||
|
||||
// Compare
|
||||
PpcOpcode::cmpi => {
|
||||
write!(out, "cmp{}i cr{},r{},{}", if instr.l() { "d" } else { "w" },
|
||||
instr.crfd(), instr.ra(), instr.simm16()).unwrap();
|
||||
}
|
||||
PpcOpcode::cmpli => {
|
||||
write!(out, "cmpl{}i cr{},r{},0x{:04X}", if instr.l() { "d" } else { "w" },
|
||||
instr.crfd(), instr.ra(), instr.uimm16()).unwrap();
|
||||
}
|
||||
PpcOpcode::cmp => {
|
||||
write!(out, "cmp{} cr{},r{},r{}", if instr.l() { "d" } else { "w" },
|
||||
instr.crfd(), instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
PpcOpcode::cmpl => {
|
||||
write!(out, "cmpl{} cr{},r{},r{}", if instr.l() { "d" } else { "w" },
|
||||
instr.crfd(), instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
|
||||
// X-form ALU (3-register)
|
||||
PpcOpcode::addx | PpcOpcode::addcx | PpcOpcode::addex | PpcOpcode::addzex |
|
||||
PpcOpcode::addmex | PpcOpcode::subfx | PpcOpcode::subfcx | PpcOpcode::subfex |
|
||||
PpcOpcode::subfzex | PpcOpcode::subfmex | PpcOpcode::negx |
|
||||
PpcOpcode::mullwx | PpcOpcode::mulhwx | PpcOpcode::mulhwux |
|
||||
PpcOpcode::divwx | PpcOpcode::divwux |
|
||||
PpcOpcode::mulldx | PpcOpcode::mulhdx | PpcOpcode::mulhdux |
|
||||
PpcOpcode::divdx | PpcOpcode::divdux => {
|
||||
write!(out, "{:?} r{},r{},r{}", instr.opcode, instr.rd(), instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
|
||||
// X-form logical
|
||||
PpcOpcode::andx | PpcOpcode::andcx | PpcOpcode::orx | PpcOpcode::orcx |
|
||||
PpcOpcode::xorx | PpcOpcode::norx | PpcOpcode::nandx | PpcOpcode::eqvx => {
|
||||
write!(out, "{:?} r{},r{},r{}", instr.opcode, instr.ra(), instr.rs(), instr.rb()).unwrap();
|
||||
}
|
||||
|
||||
// Shift/rotate
|
||||
PpcOpcode::slwx | PpcOpcode::srwx | PpcOpcode::srawx | PpcOpcode::sldx |
|
||||
PpcOpcode::srdx | PpcOpcode::sradx => {
|
||||
write!(out, "{:?} r{},r{},r{}", instr.opcode, instr.ra(), instr.rs(), instr.rb()).unwrap();
|
||||
}
|
||||
PpcOpcode::srawix => {
|
||||
write!(out, "srawi r{},r{},{}", instr.ra(), instr.rs(), instr.sh()).unwrap();
|
||||
}
|
||||
PpcOpcode::sradix => {
|
||||
write!(out, "sradi r{},r{},{}", instr.ra(), instr.rs(), instr.sh64()).unwrap();
|
||||
}
|
||||
|
||||
// Rotate
|
||||
PpcOpcode::rlwinmx => {
|
||||
write!(out, "rlwinm r{},r{},{},{},{}", instr.ra(), instr.rs(), instr.sh(), instr.mb(), instr.me()).unwrap();
|
||||
}
|
||||
PpcOpcode::rlwimix => {
|
||||
write!(out, "rlwimi r{},r{},{},{},{}", instr.ra(), instr.rs(), instr.sh(), instr.mb(), instr.me()).unwrap();
|
||||
}
|
||||
PpcOpcode::rlwnmx => {
|
||||
write!(out, "rlwnm r{},r{},r{},{},{}", instr.ra(), instr.rs(), instr.rb(), instr.mb(), instr.me()).unwrap();
|
||||
}
|
||||
|
||||
// Special register moves
|
||||
PpcOpcode::mfspr => {
|
||||
let spr_name = match instr.spr() {
|
||||
1 => "xer",
|
||||
8 => "lr",
|
||||
9 => "ctr",
|
||||
268 => "tbl",
|
||||
269 => "tbu",
|
||||
_ => "",
|
||||
};
|
||||
if spr_name.is_empty() {
|
||||
write!(out, "mfspr r{},{}", instr.rd(), instr.spr()).unwrap();
|
||||
} else {
|
||||
write!(out, "mf{} r{}", spr_name, instr.rd()).unwrap();
|
||||
}
|
||||
}
|
||||
PpcOpcode::mtspr => {
|
||||
let spr_name = match instr.spr() {
|
||||
1 => "xer",
|
||||
8 => "lr",
|
||||
9 => "ctr",
|
||||
_ => "",
|
||||
};
|
||||
if spr_name.is_empty() {
|
||||
write!(out, "mtspr {},r{}", instr.spr(), instr.rs()).unwrap();
|
||||
} else {
|
||||
write!(out, "mt{} r{}", spr_name, instr.rs()).unwrap();
|
||||
}
|
||||
}
|
||||
PpcOpcode::mfcr => {
|
||||
write!(out, "mfcr r{}", instr.rd()).unwrap();
|
||||
}
|
||||
PpcOpcode::mtcrf => {
|
||||
write!(out, "mtcrf 0x{:02X},r{}", instr.crm(), instr.rs()).unwrap();
|
||||
}
|
||||
|
||||
// Extend
|
||||
PpcOpcode::extsbx => write!(out, "extsb r{},r{}", instr.ra(), instr.rs()).unwrap(),
|
||||
PpcOpcode::extshx => write!(out, "extsh r{},r{}", instr.ra(), instr.rs()).unwrap(),
|
||||
PpcOpcode::extswx => write!(out, "extsw r{},r{}", instr.ra(), instr.rs()).unwrap(),
|
||||
PpcOpcode::cntlzwx => write!(out, "cntlzw r{},r{}", instr.ra(), instr.rs()).unwrap(),
|
||||
PpcOpcode::cntlzdx => write!(out, "cntlzd r{},r{}", instr.ra(), instr.rs()).unwrap(),
|
||||
|
||||
// X-form load/store
|
||||
PpcOpcode::lwzx | PpcOpcode::lwzux | PpcOpcode::lbzx | PpcOpcode::lbzux |
|
||||
PpcOpcode::lhzx | PpcOpcode::lhzux | PpcOpcode::lhax | PpcOpcode::lhaux |
|
||||
PpcOpcode::lwax | PpcOpcode::lwaux | PpcOpcode::ldx | PpcOpcode::ldux |
|
||||
PpcOpcode::lfsx | PpcOpcode::lfsux | PpcOpcode::lfdx | PpcOpcode::lfdux |
|
||||
PpcOpcode::lwbrx | PpcOpcode::lhbrx | PpcOpcode::ldbrx |
|
||||
PpcOpcode::lwarx | PpcOpcode::ldarx => {
|
||||
write!(out, "{:?} r{},r{},r{}", instr.opcode, instr.rd(), instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
PpcOpcode::stwx | PpcOpcode::stwux | PpcOpcode::stbx | PpcOpcode::stbux |
|
||||
PpcOpcode::sthx | PpcOpcode::sthux | PpcOpcode::stdx | PpcOpcode::stdux |
|
||||
PpcOpcode::stfsx | PpcOpcode::stfsux | PpcOpcode::stfdx | PpcOpcode::stfdux |
|
||||
PpcOpcode::stwbrx | PpcOpcode::sthbrx | PpcOpcode::stdbrx |
|
||||
PpcOpcode::stwcx | PpcOpcode::stdcx | PpcOpcode::stfiwx => {
|
||||
write!(out, "{:?} r{},r{},r{}", instr.opcode, instr.rs(), instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
|
||||
// Cache/sync ops (no-ops for interpreter)
|
||||
PpcOpcode::dcbf | PpcOpcode::dcbi | PpcOpcode::dcbst |
|
||||
PpcOpcode::dcbt | PpcOpcode::dcbtst | PpcOpcode::icbi => {
|
||||
write!(out, "{:?} r{},r{}", instr.opcode, instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
PpcOpcode::dcbz | PpcOpcode::dcbz128 => {
|
||||
write!(out, "{:?} r{},r{}", instr.opcode, instr.ra(), instr.rb()).unwrap();
|
||||
}
|
||||
PpcOpcode::sync | PpcOpcode::eieio | PpcOpcode::isync => {
|
||||
write!(out, "{:?}", instr.opcode).unwrap();
|
||||
}
|
||||
|
||||
// Load/store multiple
|
||||
PpcOpcode::lmw => write!(out, "lmw r{},{}(r{})", instr.rd(), instr.d(), instr.ra()).unwrap(),
|
||||
PpcOpcode::stmw => write!(out, "stmw r{},{}(r{})", instr.rs(), instr.d(), instr.ra()).unwrap(),
|
||||
|
||||
// DS-form loads/stores
|
||||
PpcOpcode::ld | PpcOpcode::ldu | PpcOpcode::lwa => {
|
||||
write!(out, "{:?} r{},{}(r{})", instr.opcode, instr.rd(), instr.ds(), instr.ra()).unwrap();
|
||||
}
|
||||
PpcOpcode::std | PpcOpcode::stdu => {
|
||||
write!(out, "{:?} r{},{}(r{})", instr.opcode, instr.rs(), instr.ds(), instr.ra()).unwrap();
|
||||
}
|
||||
|
||||
// CR logical ops
|
||||
PpcOpcode::crand | PpcOpcode::crandc | PpcOpcode::creqv | PpcOpcode::crnand |
|
||||
PpcOpcode::crnor | PpcOpcode::cror | PpcOpcode::crorc | PpcOpcode::crxor => {
|
||||
write!(out, "{:?} {},{},{}", instr.opcode, instr.crbd(), instr.crba(), instr.crbb()).unwrap();
|
||||
}
|
||||
PpcOpcode::mcrf => {
|
||||
write!(out, "mcrf cr{},cr{}", instr.crfd(), instr.crfs()).unwrap();
|
||||
}
|
||||
|
||||
// Trap
|
||||
PpcOpcode::tdi => write!(out, "tdi {},r{},{}", instr.rd(), instr.ra(), instr.simm16()).unwrap(),
|
||||
PpcOpcode::twi => write!(out, "twi {},r{},{}", instr.rd(), instr.ra(), instr.simm16()).unwrap(),
|
||||
PpcOpcode::td => write!(out, "td {},r{},r{}", instr.rd(), instr.ra(), instr.rb()).unwrap(),
|
||||
PpcOpcode::tw => write!(out, "tw {},r{},r{}", instr.rd(), instr.ra(), instr.rb()).unwrap(),
|
||||
|
||||
// Default: just print opcode and raw hex
|
||||
_ => {
|
||||
write!(out, "{:?} [{:08X}]", instr.opcode, instr.raw).unwrap();
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Disassemble a range of instructions from a byte slice.
|
||||
pub fn disassemble_block(data: &[u8], base_addr: u32, count: usize) -> Vec<(u32, String)> {
|
||||
let mut result = Vec::new();
|
||||
for i in 0..count {
|
||||
let offset = i * 4;
|
||||
if offset + 4 > data.len() {
|
||||
break;
|
||||
}
|
||||
let raw = u32::from_be_bytes([
|
||||
data[offset],
|
||||
data[offset + 1],
|
||||
data[offset + 2],
|
||||
data[offset + 3],
|
||||
]);
|
||||
let addr = base_addr + offset as u32;
|
||||
let instr = crate::decode(raw, addr);
|
||||
let text = disassemble(&instr);
|
||||
result.push((addr, text));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::decoder::decode;
|
||||
|
||||
#[test]
|
||||
fn test_disasm_nop() {
|
||||
// ori r0, r0, 0 = NOP
|
||||
let instr = decode(0x60000000, 0);
|
||||
let text = disassemble(&instr);
|
||||
assert!(text.contains("ori"), "Expected 'ori', got: {}", text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disasm_addi() {
|
||||
let raw = (14u32 << 26) | (3 << 21) | (1 << 16) | 16;
|
||||
let instr = decode(raw, 0);
|
||||
let text = disassemble(&instr);
|
||||
assert!(text.contains("addi"), "Got: {}", text);
|
||||
assert!(text.contains("r3"), "Got: {}", text);
|
||||
}
|
||||
}
|
||||
2529
crates/xenia-cpu/src/interpreter.rs
Normal file
2529
crates/xenia-cpu/src/interpreter.rs
Normal file
File diff suppressed because it is too large
Load Diff
9
crates/xenia-cpu/src/lib.rs
Normal file
9
crates/xenia-cpu/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod context;
|
||||
pub mod decoder;
|
||||
pub mod disasm;
|
||||
pub mod interpreter;
|
||||
pub mod opcode;
|
||||
|
||||
pub use context::PpcContext;
|
||||
pub use decoder::decode;
|
||||
pub use opcode::PpcOpcode;
|
||||
196
crates/xenia-cpu/src/opcode.rs
Normal file
196
crates/xenia-cpu/src/opcode.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
/// All PPC opcodes supported by the Xbox 360, including VMX128 extensions.
|
||||
/// Directly mirrors the C++ PPCOpcode enum from ppc_opcode.h.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(u32)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum PpcOpcode {
|
||||
// ALU
|
||||
addcx, addex, addi, addic, addicx, addis, addmex, addx, addzex,
|
||||
andcx, andisx, andix, andx,
|
||||
// Branch
|
||||
bcctrx, bclrx, bcx, bx,
|
||||
// Compare
|
||||
cmp, cmpi, cmpl, cmpli,
|
||||
// Count leading zeros
|
||||
cntlzdx, cntlzwx,
|
||||
// Condition register
|
||||
crand, crandc, creqv, crnand, crnor, cror, crorc, crxor,
|
||||
// Data cache
|
||||
dcbf, dcbi, dcbst, dcbt, dcbtst, dcbz, dcbz128,
|
||||
// Division
|
||||
divdux, divdx, divwux, divwx,
|
||||
// Sync/barrier
|
||||
eieio,
|
||||
// Logical
|
||||
eqvx, extsbx, extshx, extswx,
|
||||
// FPU
|
||||
fabsx, faddsx, faddx, fcfidx, fcmpo, fcmpu, fctidx, fctidzx, fctiwx, fctiwzx,
|
||||
fdivsx, fdivx, fmaddsx, fmaddx, fmrx, fmsubsx, fmsubx, fmulsx, fmulx,
|
||||
fnabsx, fnegx, fnmaddsx, fnmaddx, fnmsubsx, fnmsubx, fresx, frspx, frsqrtex,
|
||||
fselx, fsqrtsx, fsqrtx, fsubsx, fsubx,
|
||||
// Instruction cache
|
||||
icbi, isync,
|
||||
// Load byte
|
||||
lbz, lbzu, lbzux, lbzx,
|
||||
// Load doubleword
|
||||
ld, ldarx, ldbrx, ldu, ldux, ldx,
|
||||
// Load float
|
||||
lfd, lfdu, lfdux, lfdx, lfs, lfsu, lfsux, lfsx,
|
||||
// Load halfword
|
||||
lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx,
|
||||
// Load multiple/string
|
||||
lmw, lswi, lswx,
|
||||
// Load vector
|
||||
lvebx, lvehx, lvewx, lvewx128, lvlx, lvlx128, lvlxl, lvlxl128,
|
||||
lvrx, lvrx128, lvrxl, lvrxl128,
|
||||
lvsl, lvsl128, lvsr, lvsr128,
|
||||
lvx, lvx128, lvxl, lvxl128,
|
||||
// Load word
|
||||
lwa, lwarx, lwaux, lwax, lwbrx, lwz, lwzu, lwzux, lwzx,
|
||||
// Move CR
|
||||
mcrf, mcrfs, mcrxr,
|
||||
// Move from special
|
||||
mfcr, mffsx, mfmsr, mfspr, mftb, mfvscr,
|
||||
// Move to special
|
||||
mtcrf, mtfsb0x, mtfsb1x, mtfsfix, mtfsfx, mtmsr, mtmsrd, mtspr, mtvscr,
|
||||
// Multiply
|
||||
mulhdux, mulhdx, mulhwux, mulhwx, mulldx, mulli, mullwx,
|
||||
// Logical
|
||||
nandx, negx, norx, orcx, ori, oris, orx,
|
||||
// Rotate
|
||||
rldclx, rldcrx, rldiclx, rldicrx, rldicx, rldimix, rlwimix, rlwinmx, rlwnmx,
|
||||
// System call
|
||||
sc,
|
||||
// Shift
|
||||
sldx, slwx, sradix, sradx, srawix, srawx, srdx, srwx,
|
||||
// Store byte
|
||||
stb, stbu, stbux, stbx,
|
||||
// Store doubleword
|
||||
std, stdbrx, stdcx, stdu, stdux, stdx,
|
||||
// Store float
|
||||
stfd, stfdu, stfdux, stfdx, stfiwx, stfs, stfsu, stfsux, stfsx,
|
||||
// Store halfword
|
||||
sth, sthbrx, sthu, sthux, sthx,
|
||||
// Store multiple/string
|
||||
stmw, stswi, stswx,
|
||||
// Store vector
|
||||
stvebx, stvehx, stvewx, stvewx128, stvlx, stvlx128, stvlxl, stvlxl128,
|
||||
stvrx, stvrx128, stvrxl, stvrxl128,
|
||||
stvx, stvx128, stvxl, stvxl128,
|
||||
// Store word
|
||||
stw, stwbrx, stwcx, stwu, stwux, stwx,
|
||||
// Subtract
|
||||
subfcx, subfex, subficx, subfmex, subfx, subfzex,
|
||||
// Sync
|
||||
sync,
|
||||
// Trap
|
||||
td, tdi, tw, twi,
|
||||
// VMX integer
|
||||
vaddcuw, vaddfp, vaddfp128, vaddsbs, vaddshs, vaddsws,
|
||||
vaddubm, vaddubs, vadduhm, vadduhs, vadduwm, vadduws,
|
||||
vand, vand128, vandc, vandc128,
|
||||
vavgsb, vavgsh, vavgsw, vavgub, vavguh, vavguw,
|
||||
vcfpsxws128, vcfpuxws128, vcfsx, vcfux,
|
||||
vcmpbfp, vcmpbfp128, vcmpeqfp, vcmpeqfp128,
|
||||
vcmpequb, vcmpequh, vcmpequw, vcmpequw128,
|
||||
vcmpgefp, vcmpgefp128, vcmpgtfp, vcmpgtfp128,
|
||||
vcmpgtsb, vcmpgtsh, vcmpgtsw, vcmpgtub, vcmpgtuh, vcmpgtuw,
|
||||
vcsxwfp128, vctsxs, vctuxs, vcuxwfp128,
|
||||
vexptefp, vexptefp128, vlogefp, vlogefp128,
|
||||
vmaddcfp128, vmaddfp, vmaddfp128,
|
||||
vmaxfp, vmaxfp128, vmaxsb, vmaxsh, vmaxsw, vmaxub, vmaxuh, vmaxuw,
|
||||
vmhaddshs, vmhraddshs,
|
||||
vminfp, vminfp128, vminsb, vminsh, vminsw, vminub, vminuh, vminuw,
|
||||
vmladduhm,
|
||||
vmrghb, vmrghh, vmrghw, vmrghw128, vmrglb, vmrglh, vmrglw, vmrglw128,
|
||||
vmsum3fp128, vmsum4fp128,
|
||||
vmsummbm, vmsumshm, vmsumshs, vmsumubm, vmsumuhm, vmsumuhs,
|
||||
vmulesb, vmulesh, vmuleub, vmuleuh, vmulfp128,
|
||||
vmulosb, vmulosh, vmuloub, vmulouh,
|
||||
vnmsubfp, vnmsubfp128, vnor, vnor128,
|
||||
vor, vor128,
|
||||
vperm, vperm128, vpermwi128, vpkd3d128,
|
||||
vpkpx, vpkshss, vpkshss128, vpkshus, vpkshus128,
|
||||
vpkswss, vpkswss128, vpkswus, vpkswus128,
|
||||
vpkuhum, vpkuhum128, vpkuhus, vpkuhus128,
|
||||
vpkuwum, vpkuwum128, vpkuwus, vpkuwus128,
|
||||
vrefp, vrefp128,
|
||||
vrfim, vrfim128, vrfin, vrfin128, vrfip, vrfip128, vrfiz, vrfiz128,
|
||||
vrlb, vrlh, vrlimi128, vrlw, vrlw128,
|
||||
vrsqrtefp, vrsqrtefp128,
|
||||
vsel, vsel128,
|
||||
vsl, vslb, vsldoi, vsldoi128, vslh, vslo, vslo128, vslw, vslw128,
|
||||
vspltb, vsplth, vspltisb, vspltish, vspltisw, vspltisw128, vspltw, vspltw128,
|
||||
vsr, vsrab, vsrah, vsraw, vsraw128, vsrb, vsrh, vsro, vsro128, vsrw, vsrw128,
|
||||
vsubcuw, vsubfp, vsubfp128, vsubsbs, vsubshs, vsubsws,
|
||||
vsububm, vsububs, vsubuhm, vsubuhs, vsubuwm, vsubuws,
|
||||
vsum2sws, vsum4sbs, vsum4shs, vsum4ubs, vsumsws,
|
||||
vupkd3d128, vupkhpx, vupkhsb, vupkhsb128, vupkhsh,
|
||||
vupklpx, vupklsb, vupklsb128, vupklsh,
|
||||
vxor, vxor128,
|
||||
// XOR immediate
|
||||
xori, xoris, xorx,
|
||||
// Invalid
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl PpcOpcode {
|
||||
/// Returns true if this opcode is a branch instruction.
|
||||
pub fn is_branch(&self) -> bool {
|
||||
matches!(self, Self::bx | Self::bcx | Self::bclrx | Self::bcctrx)
|
||||
}
|
||||
|
||||
/// Returns true if this opcode is a system call.
|
||||
pub fn is_syscall(&self) -> bool {
|
||||
matches!(self, Self::sc)
|
||||
}
|
||||
|
||||
/// Returns true if this is a load instruction.
|
||||
pub fn is_load(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::lbz | Self::lbzu | Self::lbzux | Self::lbzx |
|
||||
Self::lhz | Self::lhzu | Self::lhzux | Self::lhzx |
|
||||
Self::lha | Self::lhau | Self::lhaux | Self::lhax |
|
||||
Self::lwz | Self::lwzu | Self::lwzux | Self::lwzx |
|
||||
Self::lwa | Self::lwax | Self::lwaux |
|
||||
Self::ld | Self::ldu | Self::ldux | Self::ldx |
|
||||
Self::lfs | Self::lfsu | Self::lfsux | Self::lfsx |
|
||||
Self::lfd | Self::lfdu | Self::lfdux | Self::lfdx |
|
||||
Self::lhbrx | Self::lwbrx | Self::ldbrx |
|
||||
Self::lmw | Self::lswi | Self::lswx |
|
||||
Self::lwarx | Self::ldarx
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this is a store instruction.
|
||||
pub fn is_store(&self) -> bool {
|
||||
matches!(self,
|
||||
Self::stb | Self::stbu | Self::stbux | Self::stbx |
|
||||
Self::sth | Self::sthu | Self::sthux | Self::sthx |
|
||||
Self::stw | Self::stwu | Self::stwux | Self::stwx |
|
||||
Self::std | Self::stdu | Self::stdux | Self::stdx |
|
||||
Self::stfs | Self::stfsu | Self::stfsux | Self::stfsx |
|
||||
Self::stfd | Self::stfdu | Self::stfdux | Self::stfdx |
|
||||
Self::sthbrx | Self::stwbrx | Self::stdbrx |
|
||||
Self::stmw | Self::stswi | Self::stswx |
|
||||
Self::stwcx | Self::stdcx | Self::stfiwx
|
||||
)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Invalid => "invalid",
|
||||
_ => {
|
||||
// Use debug formatting to get the variant name
|
||||
// This is a placeholder - in practice we'd have a lookup table
|
||||
"?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PpcOpcode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user