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:
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user