1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
mod naive;
mod paged;
pub use naive::NaiveMemory;
pub use paged::PagedMemory;
use crate::addr::Addr;
/// A trait representing a generic interface to a device that can be accessed through
/// memory operations.
///
/// This can be used to implement several virtual hardware devices including RAMs, ROMs,
/// and memory-mapped devices, like uarts or video buffers.
///
/// It is mandatory to implement `load_byte` and `store_byte`. All other operations are
/// optional and will default to being defined in terms of these two.
///
/// Several hardware-level notions are ignored, including whether the memory is
/// readable, writable, or executable, or whether it is a memory or a device. There is no notion
/// of atomic access.
///
/// There are two implementations provided in this library:
///
/// [`NaiveMemory`](crate::memory::NaiveMemory) is an incredibly simple, but inefficient
/// implementation that uses a hashmap to assign each memory addresses to its current value.
/// It is here only as an illustration of the `Memory` interface.
///
/// [`PagedMemory`](crate::memory::PagedMemory) is a *slightly* less naive implementation.
/// Whenever an address is first accessed, it will allocate a 4KiB page containing that address.
/// The pages are then at least contiguous in the host machine's memory.
pub trait Memory {
fn load_byte(&self, addr: Addr) -> u8;
fn load_halfword(&self, addr: Addr) -> u16 {
let mut val = self.load_byte(addr) as u16;
val |= (self.load_byte(addr + 1) as u16) << 8;
val
}
fn load_word(&self, addr: Addr) -> u32 {
let mut val = self.load_byte(addr) as u32;
val |= (self.load_byte(addr + 1) as u32) << 8;
val |= (self.load_byte(addr + 2) as u32) << 16;
val |= (self.load_byte(addr + 3) as u32) << 24;
val
}
fn store_byte(&mut self, addr: Addr, value: u8);
fn store_halfword(&mut self, addr: Addr, value: u16) {
self.store_byte(addr, (value & 0x000000ff) as u8);
self.store_byte(addr + 1, ((value & 0x0000ff00) >> 8) as u8);
}
fn store_word(&mut self, addr: Addr, value: u32) {
self.store_byte(addr, (value & 0x000000ff) as u8);
self.store_byte(addr + 1, ((value & 0x0000ff00) >> 8) as u8);
self.store_byte(addr + 2, ((value & 0x00ff0000) >> 16) as u8);
self.store_byte(addr + 3, ((value & 0xff000000) >> 24) as u8);
}
fn memcpy(&mut self, base_addr: Addr, buf: &[u8]) {
for (i, byte) in buf.iter().enumerate() {
self.store_byte(base_addr + i, *byte);
}
}
}