From 29077adb3846b4307432496a72c06d32fe47eabd Mon Sep 17 00:00:00 2001 From: zxq5 Date: Wed, 4 Mar 2026 04:38:31 +0000 Subject: [PATCH] idk why these weren't alr committed --- doc/ISA.md | 146 ++++++++++++++++++++++ dsa-emu/src/args.rs | 42 +++++++ dsa-emu/src/common/instructions.rs | 177 +++++++++++++++++++++++++++ dsa-emu/src/common/mod.rs | 1 + dsa-emu/src/memory/ram/arraymap.rs | 105 ++++++++++++++++ dsa-emu/src/memory/ram/bulkalloc.rs | 132 ++++++++++++++++++++ dsa-emu/src/memory/ram/hashmap.rs | 63 ++++++++++ dsa-emu/src/memory/ram/mod.rs | 49 ++++++++ dsa-emu/src/memory/ram/prealloc.rs | 47 +++++++ dsa-emu/src/memory/ram/stackarray.rs | 103 ++++++++++++++++ 10 files changed, 865 insertions(+) create mode 100644 doc/ISA.md create mode 100644 dsa-emu/src/args.rs create mode 100644 dsa-emu/src/common/instructions.rs create mode 100644 dsa-emu/src/common/mod.rs create mode 100644 dsa-emu/src/memory/ram/arraymap.rs create mode 100644 dsa-emu/src/memory/ram/bulkalloc.rs create mode 100644 dsa-emu/src/memory/ram/hashmap.rs create mode 100644 dsa-emu/src/memory/ram/mod.rs create mode 100644 dsa-emu/src/memory/ram/prealloc.rs create mode 100644 dsa-emu/src/memory/ram/stackarray.rs diff --git a/doc/ISA.md b/doc/ISA.md new file mode 100644 index 0000000..164fe72 --- /dev/null +++ b/doc/ISA.md @@ -0,0 +1,146 @@ +## Architecture +32 bit instruction width/word size + +## Instruction Types: + +Type Sizes: + - u16: [16 bits] + - u6: [6 bits] + - Reg: [5 bits] + - Opcode: [6 bits] + +Instruction Types: + - R: [Opcode] [Reg] [Reg] [Reg] [Reg] [u6] + - I: [Opcode] [Reg] [Reg] [u16 ] + +## Instruction Set + +// move +mov [ unused ] +cmov + +// load +ldb +ldbs +ldh +ldhs +ldw + +// store +stb +sth +stw + +// immediate +lli [unused] +lui [unused] + +// comparison +ieq [unused] +ine [unused] +igt [unused] +ige [unused] +ilt [unused] +ile [unused] + +// Jump +jmp [ unused ] // unconditional + +jez // conditional on cmp == 0 +jnz + +jic [ unused ] // conditional on carry +jnc [ unused ] + +// Bitwise +and [unused] // and +nnd [unused] // nand +or [unused] // or +nor [unused] // nor +xor [unused] // xor +xnr [unused] // xnor +not [ unused ] [unused] // not + +// Arithmetic +add +sub +shl // left shift of rshamt+ishamt +shr // right shift + +// Immediate Arithmetic +addi +subi + +// Misc +nop (zero/do nothing) +int +hlt + +// Atomic + +// Atomic Compare and Swap - compares src and dst, if src +acs + +## Assembler Pseudoinstructions +Definitions: + Label - named address, a function or global + +``` +lwi // loads the literal value of Label|Imm into dest + // if src is a Label, take literal value of Label + => lli $src, $dest + => lui $src, $dest +lli // loads the lower 16 bits of the literal value of Label into dest +... -> similarly defined for lui + +ldw // loads the word at the address of Label into dest +... => similarly defined for ldb, ldbs, ldh, ldhs + // load the label value of src into dest with lui/lli + => lli $src, $dest + => lui $src, $dest + => ldw $dest, $dest + +stw // stores the word in src using the address/label that is dest. + // load the literal value of dest into the accumulator with lui/lli + => lli $dest, acc + => lui $dest, acc + => stw $src, acc + +jmp + CASE dest > u16::MAX: + => lli $dest, acc + => lui $dest, acc + => jmp $dest, 0 + CASE dest < u16::MAX: + // assembler inserts the literal value of dest + => jmp $dest, 0 + +jmp + CASE lbl > u16::MAX: + AssemblerError! + CASE lbl < u16::MAX: + => jmp $dest, lbl + +call + => subi spr, 4, spr + => stw pcx, spr, 0 + => jmp $addr + +func + // push bpr + => subi spr, 4, spr + => stw bpr, spr, 0 + // replace bpr with spr + => mov spr, bpr + +return + => mov bpr, spr + // pop bpr + => addi spr, 4, spr + => ldw spr, bpr, 0 + // load return addr + => ldw spr, ret, 0 + => addi, spr, 4, spr + => jmp ret, 4 + +``` diff --git a/dsa-emu/src/args.rs b/dsa-emu/src/args.rs new file mode 100644 index 0000000..4e67f98 --- /dev/null +++ b/dsa-emu/src/args.rs @@ -0,0 +1,42 @@ +use std::{fs, path::PathBuf}; + +use clap::{Parser, ValueEnum}; + +use crate::{MemoryMap, RandomAccessMemory}; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct DsaArgs { + /// Memory map to use on boot. + /// Overrides default value. + #[arg(long = "mmap")] + memory_map: Option, + + #[arg(value_enum, long = "mem", default_value = "bulk-alloc")] + pub memory_bank: MemoryBank, +} + +impl DsaArgs { + pub fn get_memory_map(&self) -> Option { + self.memory_map.as_ref().and_then(|m| { + fs::read_to_string(m) + .ok() + .and_then(|map| ron::from_str(&map).ok()) + }) + } +} + +#[derive(Debug, Clone, ValueEnum, Default)] +pub enum MemoryBank { + #[cfg(feature = "mainstore-bulkalloc")] + #[default] + BulkAlloc, + #[cfg(feature = "mainstore-arraymap")] + ArrayMap, + #[cfg(feature = "mainstore-hashmap")] + HashMap, + #[cfg(feature = "mainstore-prealloc")] + PreAlloc, + #[cfg(feature = "mainstore-stackarray")] + StackArray, +} diff --git a/dsa-emu/src/common/instructions.rs b/dsa-emu/src/common/instructions.rs new file mode 100644 index 0000000..bd7a974 --- /dev/null +++ b/dsa-emu/src/common/instructions.rs @@ -0,0 +1,177 @@ +#[derive(Clone, Copy)] +pub struct InstructionWord(pub u32); + +impl InstructionWord { + #[inline] + pub fn opcode(self) -> u8 { + ((self.0 >> 26) & 0x3F) as u8 + } + + // Src shared between R type and I type + #[inline] + pub fn src1(self) -> Reg { + Reg::from_u8_unchecked(((self.0 >> 21) & 0x1F) as u8) + } + + // Dest shared between R type and I type + #[inline] + pub fn dest(self) -> Reg { + Reg::from_u8_unchecked(((self.0 >> 16) & 0x1F) as u8) + } + + // Secondary Src for R type only + #[inline] + pub fn src2(self) -> Reg { + Reg::from_u8_unchecked(((self.0 >> 11) & 0x1F) as u8) + } + + // Misc/Conditional reg for R type only + #[inline] + pub fn misc(self) -> Reg { + Reg::from_u8_unchecked(((self.0 >> 6) & 0x1F) as u8) + } + + // Shift amount / misc data for R type only + #[inline] + pub fn shamt(self) -> u8 { + (self.0 & 0x3F) as u8 + } + + // 16 Bit immediate for I type only + #[inline] + pub fn imm16(self) -> u16 { + self.0 as u16 + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Opcode { + Nop = 0, + + // move + Mov = 1, + CMov = 2, + + // load + Ldb = 3, + Ldbs = 4, + Ldh = 5, + Ldhs = 6, + Ldw = 7, + + // store + Stb = 8, + Sth = 9, + Stw = 10, + + // load immediate + Lli = 11, + Lui = 12, + + // comparison + Ieq = 13, + Ine = 14, + Ilt = 15, + Ile = 16, + Igt = 17, + Ige = 18, + + // jump + Jmp = 19, + Jez = 20, + Jnz = 21, + Jic = 22, + Jnc = 23, + + // bitwise + And = 24, + Nand = 25, + Or = 26, + Nor = 27, + Xor = 28, + Xnor = 29, + Not = 30, + + // arithmetic + Add = 31, + Sub = 32, + Shl = 33, + Shr = 34, + Addi = 35, + Subi = 36, + + // system + Int = 37, + Hlt = 38, +} + +impl Opcode { + #[inline] + pub fn from_u8(val: u8) -> Option { + if val <= Self::Hlt as u8 { + Some(unsafe { std::mem::transmute(val) }) + } else { + None + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +#[repr(u8)] +#[non_exhaustive] +pub enum Reg { + // general purpose + Rg0 = 0, + Rg1 = 1, + Rg2 = 2, + Rg3 = 3, + Rg4 = 4, + Rg5 = 5, + Rg6 = 6, + Rg7 = 7, + Rg8 = 8, + Rg9 = 9, + Rga = 10, + Rgb = 11, + Rgc = 12, + Rgd = 13, + Rge = 14, + Rgf = 15, + + // special purpose + Zero = 16, + Acc = 17, + Spr = 18, + Bpr = 19, + Ret = 20, + Idr = 21, + Mmr = 22, + + // system - read only + Mar = 23, + Mdr = 24, + Sts = 25, + Cir = 26, + Pcx = 27, + + #[default] + Null = 28, +} + +impl Reg { + #[must_use] + #[inline] + pub fn from_u8_unchecked(idx: u8) -> Self { + debug_assert!(idx <= 28); + unsafe { std::mem::transmute(idx) } + } + + #[inline] + pub fn from_u8(idx: u8) -> Result { + if idx > 28 { + return Err(()); + } + Ok(Self::from_u8_unchecked(idx)) + } +} diff --git a/dsa-emu/src/common/mod.rs b/dsa-emu/src/common/mod.rs new file mode 100644 index 0000000..4f17719 --- /dev/null +++ b/dsa-emu/src/common/mod.rs @@ -0,0 +1 @@ +pub mod instructions; diff --git a/dsa-emu/src/memory/ram/arraymap.rs b/dsa-emu/src/memory/ram/arraymap.rs new file mode 100644 index 0000000..5a2519b --- /dev/null +++ b/dsa-emu/src/memory/ram/arraymap.rs @@ -0,0 +1,105 @@ +use std::{ + alloc::{Layout, alloc_zeroed}, + hint::{likely, unlikely}, +}; + +use crate::memory::{ + PhysAddr, RandomAccessMemory, + ram::{NUM_PAGES, Page, idx}, +}; + +pub struct ArrayStore { + pages: Box<[*mut Page; NUM_PAGES]>, +} + +unsafe impl Send for ArrayStore {} +impl ArrayStore { + pub fn new() -> Self { + let pages = vec![0 as *mut Page; 2 << 20] + .into_boxed_slice() + .try_into() + .unwrap(); + Self { pages } + } + + fn alloc(&mut self) -> *mut Page { + let layout = Layout::from_size_align(4096, 4096).unwrap(); + unsafe { alloc_zeroed(layout) as *mut Page } + } +} + +impl RandomAccessMemory for ArrayStore { + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() }; + } + + // Slow path: MMIO, fault, etc — never inlined + // Since we're only reading, we can safely return 0 + return 0; + } + + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() }; + } + + return 0; + } + + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { &*(self.pages[idx] as *const [u8; 4096]) }; + } + + return &[0; 4096]; + } + + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { + (self.pages[idx] as *mut u8).byte_add(offset).write(value); + } + } + + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) } + } + + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) } + } +} diff --git a/dsa-emu/src/memory/ram/bulkalloc.rs b/dsa-emu/src/memory/ram/bulkalloc.rs new file mode 100644 index 0000000..156c891 --- /dev/null +++ b/dsa-emu/src/memory/ram/bulkalloc.rs @@ -0,0 +1,132 @@ +use std::{ + alloc::{Layout, alloc_zeroed}, + hint::{likely, unlikely}, +}; + +use crate::memory::{ + PhysAddr, RandomAccessMemory, + ram::{NUM_PAGES, Page, idx}, +}; + +pub struct BulkAllocStore { + pages: Box<[*mut Page; NUM_PAGES]>, + pre_allocated: [*mut Page; Self::PREALLOC], + idx: u8, +} + +unsafe impl Send for BulkAllocStore {} +impl BulkAllocStore { + const PREALLOC: usize = u8::MAX as usize; + + pub fn new() -> Self { + let pages = vec![0 as *mut Page; 2 << 20] + .into_boxed_slice() + .try_into() + .unwrap(); + + Self { + pages, + pre_allocated: Self::alloc_set(), + idx: 0, + } + } + + fn alloc_set() -> [*mut Page; Self::PREALLOC] { + let layout = Layout::from_size_align(4096 * Self::PREALLOC, 4096).unwrap(); + let mut region = unsafe { alloc_zeroed(layout) as *mut Page }; + let mut set = [0 as *mut Page; Self::PREALLOC]; + for i in 0..Self::PREALLOC { + set[i] = region; + region = unsafe { region.byte_add(4096) }; + } + set + } + + fn alloc(&mut self) -> *mut Page { + if self.idx < Self::PREALLOC as u8 { + let page = self.pre_allocated[self.idx as usize]; + self.idx += 1; + page + } else { + self.idx = 0; + self.pre_allocated = Self::alloc_set(); + self.pre_allocated[0] + } + } +} + +impl RandomAccessMemory for BulkAllocStore { + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() }; + } + + // Slow path: MMIO, fault, etc — never inlined + // Since we're only reading, we can safely return 0 + return 0; + } + + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() }; + } + + return 0; + } + + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { &*(self.pages[idx] as *const [u8; 4096]) }; + } + + return &[0; 4096]; + } + + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { + (self.pages[idx] as *mut u8).byte_add(offset).write(value); + } + } + + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) } + } + + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) } + } +} diff --git a/dsa-emu/src/memory/ram/hashmap.rs b/dsa-emu/src/memory/ram/hashmap.rs new file mode 100644 index 0000000..4fcafab --- /dev/null +++ b/dsa-emu/src/memory/ram/hashmap.rs @@ -0,0 +1,63 @@ +use fxhash::FxHashMap; + +use crate::memory::{PhysAddr, RandomAccessMemory, ram::Page}; + +pub struct HashStore { + pages: FxHashMap, +} + +impl HashStore { + pub fn new() -> Self { + Self { + pages: FxHashMap::default(), + } + } + + #[inline(always)] + const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) { + (addr & !(0xFFF), (addr & 0xFFF) as usize) + } +} + +impl RandomAccessMemory for HashStore { + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { + let (page, offset) = Self::segment_addr(addr); + self.pages.get(&page).map(|p| p[offset]).unwrap_or(0) + } + + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { + let (page, offset) = Self::segment_addr(addr); + debug_assert_eq!(offset % 4, 0); + let page = self.pages.get(&page).unwrap_or(&[0; 4096]); + u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap()) + } + + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 0x1000, 0); + self.pages.get(&addr).unwrap_or(&[0; 4096]) + } + + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + let (page, offset) = Self::segment_addr(addr); + self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value; + } + + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { + let (page, offset) = Self::segment_addr(addr); + debug_assert_eq!(offset % 4, 0); + let page = self.pages.entry(page).or_insert_with(|| [0; 4096]); + page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes()); + } + + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + debug_assert_eq!(addr % 0x1000, 0); + let page = self.pages.entry(addr).or_insert_with(|| [0; 4096]); + page.copy_from_slice(value); + } +} diff --git a/dsa-emu/src/memory/ram/mod.rs b/dsa-emu/src/memory/ram/mod.rs new file mode 100644 index 0000000..eb6986e --- /dev/null +++ b/dsa-emu/src/memory/ram/mod.rs @@ -0,0 +1,49 @@ +use crate::memory::PhysAddr; + +#[cfg(feature = "mainstore-arraymap")] +pub mod arraymap; +#[cfg(feature = "mainstore-arraymap")] +pub use arraymap::ArrayStore; + +#[cfg(feature = "mainstore-hashmap")] +pub mod hashmap; +#[cfg(feature = "mainstore-hashmap")] +pub use hashmap::HashStore; + +#[cfg(feature = "mainstore-stackarray")] +pub mod stackarray; +#[cfg(feature = "mainstore-stackarray")] +pub use stackarray::StackArrayStore; + +#[cfg(feature = "mainstore-bulkalloc")] +pub mod bulkalloc; +#[cfg(feature = "mainstore-bulkalloc")] +pub use bulkalloc::BulkAllocStore; + +#[cfg(feature = "mainstore-prealloc")] +pub mod prealloc; +#[cfg(feature = "mainstore-prealloc")] +pub use prealloc::PreAllocStore; + +const NUM_PAGES: usize = 2 << 20; + +type Page = [u8; 4096]; + +#[inline(always)] +const fn idx(addr: PhysAddr) -> (usize, usize) { + ((addr >> 12) as usize, (addr & 0xFFF) as usize) +} + +pub trait RandomAccessMemory { + fn read_byte(&self, addr: PhysAddr) -> u8; + + fn write_byte(&mut self, addr: PhysAddr, value: u8); + + fn read_word(&self, addr: PhysAddr) -> u32; + + fn write_word(&mut self, addr: PhysAddr, value: u32); + + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096]; + + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]); +} diff --git a/dsa-emu/src/memory/ram/prealloc.rs b/dsa-emu/src/memory/ram/prealloc.rs new file mode 100644 index 0000000..639dfd9 --- /dev/null +++ b/dsa-emu/src/memory/ram/prealloc.rs @@ -0,0 +1,47 @@ +use crate::memory::{PhysAddr, RandomAccessMemory, ram::NUM_PAGES}; + +pub struct PreAllocStore { + heap: Box<[u8; NUM_PAGES * 4096]>, +} + +unsafe impl Send for PreAllocStore {} +impl PreAllocStore { + pub fn new() -> Self { + Self { + heap: unsafe { Box::new_zeroed().assume_init() }, + } + } +} + +impl RandomAccessMemory for PreAllocStore { + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { + unsafe { (self.heap.as_ptr().add(addr as usize) as *const u32).read() } + } + + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { + self.heap[addr as usize] + } + + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 4096, 0); + unsafe { (self.heap.as_ptr().add(addr as usize) as *const [u8; 4096]).as_ref_unchecked() } + } + + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + self.heap[addr as usize] = value; + } + + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { + unsafe { (self.heap.as_ptr().add(addr as usize) as *mut u32).write(value) }; + } + + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + self.heap[addr as usize..addr as usize + 4096].copy_from_slice(value); + } +} diff --git a/dsa-emu/src/memory/ram/stackarray.rs b/dsa-emu/src/memory/ram/stackarray.rs new file mode 100644 index 0000000..205f10d --- /dev/null +++ b/dsa-emu/src/memory/ram/stackarray.rs @@ -0,0 +1,103 @@ +use std::{ + alloc::{Layout, alloc_zeroed}, + hint::{likely, unlikely}, +}; + +use crate::memory::{ + PhysAddr, RandomAccessMemory, + ram::{NUM_PAGES, Page, idx}, +}; + +pub struct StackArrayStore { + pages: [*mut Page; NUM_PAGES], +} + +unsafe impl Send for StackArrayStore {} +impl StackArrayStore { + pub fn new() -> Self { + Self { + pages: [0 as *mut Page; 2 << 20], + } + } + + fn alloc(&mut self) -> *mut Page { + let layout = Layout::from_size_align(4096, 4096).unwrap(); + unsafe { alloc_zeroed(layout) as *mut Page } + } +} + +impl RandomAccessMemory for StackArrayStore { + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() }; + } + + // Slow path: MMIO, fault, etc — never inlined + // Since we're only reading, we can safely return 0 + return 0; + } + + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { + let (idx, offset) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() }; + } + + return 0; + } + + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if likely(!self.pages[idx].is_null()) { + return unsafe { &*(self.pages[idx] as *const [u8; 4096]) }; + } + + return &[0; 4096]; + } + + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { + (self.pages[idx] as *mut u8).byte_add(offset).write(value); + } + } + + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { + debug_assert_eq!(addr % 4, 0); + let (idx, offset) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) } + } + + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + debug_assert_eq!(addr % 4096, 0); + let (idx, _) = idx(addr); + + if unlikely(self.pages[idx].is_null()) { + self.pages[idx] = self.alloc(); + } + + unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) } + } +}