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:
122
crates/xenia-memory/src/page_table.rs
Normal file
122
crates/xenia-memory/src/page_table.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
/// Describes a single page in the page table.
|
||||
/// Mirrors the C++ `PageEntry` union from memory.h:82-99.
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct PageEntry(u64);
|
||||
|
||||
impl PageEntry {
|
||||
/// Base address of the allocated region in 4K pages (20 bits).
|
||||
pub fn base_address(&self) -> u32 {
|
||||
(self.0 & 0xFFFFF) as u32
|
||||
}
|
||||
|
||||
pub fn set_base_address(&mut self, val: u32) {
|
||||
self.0 = (self.0 & !0xFFFFF) | (val as u64 & 0xFFFFF);
|
||||
}
|
||||
|
||||
/// Total number of pages in the allocated region (20 bits).
|
||||
pub fn region_page_count(&self) -> u32 {
|
||||
((self.0 >> 20) & 0xFFFFF) as u32
|
||||
}
|
||||
|
||||
pub fn set_region_page_count(&mut self, val: u32) {
|
||||
self.0 = (self.0 & !(0xFFFFF << 20)) | ((val as u64 & 0xFFFFF) << 20);
|
||||
}
|
||||
|
||||
/// Protection bits specified during region allocation (4 bits).
|
||||
pub fn allocation_protect(&self) -> MemoryProtect {
|
||||
MemoryProtect::from_bits_truncate(((self.0 >> 40) & 0xF) as u32)
|
||||
}
|
||||
|
||||
pub fn set_allocation_protect(&mut self, val: MemoryProtect) {
|
||||
self.0 = (self.0 & !(0xF << 40)) | ((val.bits() as u64 & 0xF) << 40);
|
||||
}
|
||||
|
||||
/// Current protection bits (4 bits).
|
||||
pub fn current_protect(&self) -> MemoryProtect {
|
||||
MemoryProtect::from_bits_truncate(((self.0 >> 44) & 0xF) as u32)
|
||||
}
|
||||
|
||||
pub fn set_current_protect(&mut self, val: MemoryProtect) {
|
||||
self.0 = (self.0 & !(0xF << 44)) | ((val.bits() as u64 & 0xF) << 44);
|
||||
}
|
||||
|
||||
/// Allocation state (2 bits).
|
||||
pub fn state(&self) -> AllocationState {
|
||||
AllocationState::from_bits_truncate(((self.0 >> 48) & 0x3) as u32)
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, val: AllocationState) {
|
||||
self.0 = (self.0 & !(0x3 << 48)) | ((val.bits() as u64 & 0x3) << 48);
|
||||
}
|
||||
|
||||
pub fn is_committed(&self) -> bool {
|
||||
self.state().contains(AllocationState::COMMIT)
|
||||
}
|
||||
|
||||
pub fn is_reserved(&self) -> bool {
|
||||
self.state().contains(AllocationState::RESERVE)
|
||||
}
|
||||
|
||||
pub fn is_free(&self) -> bool {
|
||||
self.state().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct MemoryProtect: u32 {
|
||||
const READ = 1 << 0;
|
||||
const WRITE = 1 << 1;
|
||||
const NO_CACHE = 1 << 2;
|
||||
const WRITE_COMBINE = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct AllocationState: u32 {
|
||||
const RESERVE = 1 << 0;
|
||||
const COMMIT = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PageEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PageEntry")
|
||||
.field("base_address", &format_args!("{:#x}", self.base_address()))
|
||||
.field("region_page_count", &self.region_page_count())
|
||||
.field("allocation_protect", &self.allocation_protect())
|
||||
.field("current_protect", &self.current_protect())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_page_entry_bitfields() {
|
||||
let mut entry = PageEntry::default();
|
||||
assert!(entry.is_free());
|
||||
|
||||
entry.set_base_address(0x100);
|
||||
entry.set_region_page_count(0x10);
|
||||
entry.set_allocation_protect(MemoryProtect::READ | MemoryProtect::WRITE);
|
||||
entry.set_current_protect(MemoryProtect::READ);
|
||||
entry.set_state(AllocationState::RESERVE | AllocationState::COMMIT);
|
||||
|
||||
assert_eq!(entry.base_address(), 0x100);
|
||||
assert_eq!(entry.region_page_count(), 0x10);
|
||||
assert_eq!(
|
||||
entry.allocation_protect(),
|
||||
MemoryProtect::READ | MemoryProtect::WRITE
|
||||
);
|
||||
assert_eq!(entry.current_protect(), MemoryProtect::READ);
|
||||
assert!(entry.is_committed());
|
||||
assert!(entry.is_reserved());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user