emulator for a made-up cpu architecture. learning experience :)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.2 KiB

use std::fmt::{Debug, Display};
use bytes::BytesMut;
use tracing::{trace, warn};
pub trait Memory {
fn size(&self) -> u32;
fn read(&self, addr: u32) -> u8;
fn write(&mut self, addr: u32, value: u8);
fn read_u16(&self, addr: u32) -> u16 {
let b1 = self.read(addr);
let b2 = self.read(addr + 1);
u16::from_le_bytes([b1, b2])
}
fn read_u32(&self, addr: u32) -> u32 {
let b1 = self.read(addr);
let b2 = self.read(addr + 1);
let b3 = self.read(addr + 2);
let b4 = self.read(addr + 3);
u32::from_le_bytes([b1, b2, b3, b4])
}
fn write_u16(&mut self, addr: u32, value: u16) {
let [b1, b2] = value.to_le_bytes();
self.write(addr, b1);
self.write(addr + 1, b2);
}
fn write_u32(&mut self, addr: u32, value: u32) {
let [b1, b2, b3, b4] = value.to_le_bytes();
self.write(addr, b1);
self.write(addr + 1, b2);
self.write(addr + 2, b3);
self.write(addr + 3, b4);
}
fn pre_step(&mut self) {}
fn post_step(&mut self) {
warn!("mem-trait default: post-step");
}
}
type BoxedMem = Box<dyn Memory>;
impl Memory for BytesMut {
fn size(&self) -> u32 {
self.len() as u32
}
fn read(&self, addr: u32) -> u8 {
self[addr as usize]
}
fn write(&mut self, addr: u32, value: u8) {
self.get_mut(addr as usize).map(|v| *v = value);
}
}
#[derive(Default)]
pub struct MemoryMap {
pub mems: Vec<MappedMemory>,
}
impl MemoryMap {
fn pos_resp(&self, addr: u32) -> Option<usize> {
self.mems
.iter()
.position(|mem| mem.start <= addr && mem.end >= addr)
}
fn get_responsible(&self, addr: u32) -> Option<&MappedMemory> {
self.pos_resp(addr).map(|idx| &self.mems[idx])
}
fn get_responsible_mut(&mut self, addr: u32) -> Option<&mut MappedMemory> {
self.pos_resp(addr).map(|idx| &mut self.mems[idx])
}
fn collides(&self, b: &MappedMemory) -> bool {
self.mems.iter().any(|a| {
if a.start == b.start || a.end == b.end {
false
} else if a.start < b.start {
a.end > b.start
} else {
a.start < b.end
}
})
}
pub fn add(&mut self, mem: impl Memory + 'static, at: u32) -> Result<(), ()> {
let mapped = MappedMemory::new(Box::new(mem), at);
if self.collides(&mapped) {
return Err(());
}
self.mems.push(mapped);
Ok(())
}
}
impl Display for MemoryMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MemoryMap({:?})", self.mems)
}
}
impl Memory for MemoryMap {
fn size(&self) -> u32 {
u32::MAX
}
fn read(&self, addr: u32) -> u8 {
match self.get_responsible(addr) {
None => {
warn!("out of bounds read at {:#010x}", addr);
0
}
Some(mem) => {
trace!("mmap: read of {:#x} delegated to {:?}", addr, mem);
mem.read(addr)
}
}
}
fn write(&mut self, addr: u32, value: u8) {
self.get_responsible_mut(addr)
.map(|mem| mem.write(addr, value));
}
}
pub struct MappedMemory {
pub start: u32,
pub end: u32,
pub inner: BoxedMem,
}
impl MappedMemory {
pub fn new(mem: BoxedMem, start: u32) -> MappedMemory {
let size = mem.size();
let end = start + size - 1;
Self {
start,
end,
inner: mem,
}
}
}
impl Debug for MappedMemory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MappedMem({:#x} .. {:#x})", self.start, self.end)
}
}
impl Memory for MappedMemory {
fn size(&self) -> u32 {
self.inner.size()
}
fn read(&self, addr: u32) -> u8 {
let internal = addr - self.start;
assert!(internal < self.size());
self.inner.read(internal)
}
fn write(&mut self, addr: u32, value: u8) {
let internal = addr - self.start;
assert!(internal < self.size());
self.inner.write(internal, value);
}
}