use crate::processor::exceptions::{MemoryException,ProgramHaltedException};
use std::ops::Range;
use std::any::Any;
pub type MemoryResult<T> = anyhow::Result<T>;
pub(super) fn check_alignment_range<TData>(addr: u64, range: &Range<usize>) -> MemoryResult<()> {
let addr = addr as usize;
let size = std::mem::size_of::<TData>();
let align = size;
if addr % align != 0 {
bail!(MemoryException::AddressMisaligned{addr, expected: align})
} else if !range.contains(&addr) || !range.contains(&(addr + size - 1)) {
bail!(MemoryException::AddressUnmapped{addr})
} else {
Ok(())
}
}
pub trait MemoryOf<TData, TAddr=u64> where TData: Sized {
fn read(&mut self, addr: TAddr) -> MemoryResult<TData>;
fn write(&mut self, addr: TAddr, val: TData) -> MemoryResult<()>;
}
pub trait Memory<TAddr=u64>: MemoryOf<u8, TAddr> + MemoryOf<u16, TAddr> + MemoryOf<u32, TAddr> + MemoryOf<u64, TAddr> {
fn range(&self) -> Range<usize>;
fn load_u8(&mut self, addr: TAddr) -> MemoryResult<u8> {
<Self as MemoryOf<u8, TAddr>>::read(self, addr)
}
fn load_u16(&mut self, addr: TAddr) -> MemoryResult<u16> {
<Self as MemoryOf<u16, TAddr>>::read(self, addr)
}
fn load_u32(&mut self, addr: TAddr) -> MemoryResult<u32> {
<Self as MemoryOf<u32, TAddr>>::read(self, addr)
}
fn load_u64(&mut self, addr: TAddr) -> MemoryResult<u64> {
<Self as MemoryOf<u64, TAddr>>::read(self, addr)
}
fn store_u8(&mut self, addr: TAddr, val: u8) -> MemoryResult<()> {
<Self as MemoryOf<u8, TAddr>>::write(self, addr, val)
}
fn store_u16(&mut self, addr: TAddr, val: u16) -> MemoryResult<()> {
<Self as MemoryOf<u16, TAddr>>::write(self, addr, val)
}
fn store_u32(&mut self, addr: TAddr, val: u32) -> MemoryResult<()> {
<Self as MemoryOf<u32, TAddr>>::write(self, addr, val)
}
fn store_u64(&mut self, addr: TAddr, val: u64) -> MemoryResult<()> {
<Self as MemoryOf<u64, TAddr>>::write(self, addr, val)
}
fn as_any(&self) -> &dyn Any;
}
use std::mem::size_of;
const_assert!(size_of::<usize>() >= size_of::<u64>());
pub struct IOMemory {
range: Range<usize>,
value: Option<u64>,
halt_on_write: bool,
}
impl IOMemory {
pub fn return_address(addr: usize, halt_on_write: bool) -> IOMemory {
IOMemory{
range: Range{ start: addr, end: addr+8 },
halt_on_write,
value: None
}
}
}
impl<TData> MemoryOf<TData> for IOMemory where TData: Into<u64> + Default, dyn Memory: MemoryOf<TData> {
fn read(&mut self, _: u64) -> MemoryResult<TData> { Ok(TData::default()) }
fn write(&mut self, addr: u64, val: TData) -> MemoryResult<()> {
self.value = Some(val.into());
if self.halt_on_write {
bail!(ProgramHaltedException::ResultReturned{
addr: addr as usize
})
} else {
Ok(())
}
}
}
impl Memory for IOMemory {
fn range(&self) -> Range<usize> {
self.range.clone()
}
fn as_any(&self) -> &dyn Any { self }
}
pub struct MemoryBacking {
data: Vec<u8>,
range: Range<usize>
}
impl MemoryBacking {
pub fn zeros(range: Range<usize>) -> MemoryBacking {
assert!(!range.is_empty());
if range.start % 4 != 0 || range.end % 4 != 0 {
panic!("Input range {:x?} for MemoryBacking not aligned", range);
}
MemoryBacking {
data: vec![0; range.end - range.start],
range
}
}
pub fn from_vec(mut vec: Vec<u8>, range: Range<usize>) -> MemoryBacking {
assert!(!range.is_empty());
if range.start % 4 != 0 || range.end % 4 != 0 {
panic!("Input range {:x?} for MemoryBacking not aligned", range);
}
let pad_memory_to = range.end - range.start;
assert_eq!(vec.len() % 4, 0);
assert_eq!(vec.len() as usize <= pad_memory_to, true);
vec.append(&mut vec![0; pad_memory_to - vec.len()]);
MemoryBacking {
data: vec,
range
}
}
pub fn from_file(path_s: &str, range: Range<usize>) -> MemoryBacking {
assert!(!range.is_empty());
if range.start % 4 != 0 || range.end % 4 != 0 {
panic!("Input range {:x?} for MemoryBacking not aligned", range);
}
use std::io::Read;
use std::path::Path;
use std::fs::{File,metadata};
let path = Path::new(path_s);
let pad_memory_to = range.end - range.start;
let mut f = File::open(&path).expect("no file found");
let metadata = metadata(&path).expect("unable to read metadata");
assert_eq!(metadata.len() % 4, 0);
assert_eq!(metadata.len() as usize <= pad_memory_to, true);
let mut buffer = vec![0; pad_memory_to];
f.read(&mut buffer).expect("buffer overflow");
MemoryBacking {
data: buffer,
range
}
}
}
impl MemoryOf<u64> for MemoryBacking {
fn read(&mut self, addr: u64) -> MemoryResult<u64> {
check_alignment_range::<u64>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
Ok(
((self.data[addr+7] as u64) << 56) |
((self.data[addr+6] as u64) << 48) |
((self.data[addr+5] as u64) << 40) |
((self.data[addr+4] as u64) << 32) |
((self.data[addr+3] as u64) << 24) |
((self.data[addr+2] as u64) << 16) |
((self.data[addr+1] as u64) << 8) |
((self.data[addr+0] as u64))
)
}
fn write(&mut self, addr: u64, val: u64) -> MemoryResult<()> {
check_alignment_range::<u64>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
self.data[addr + 7] = (val >> 56) as u8;
self.data[addr + 6] = (val >> 48) as u8;
self.data[addr + 5] = (val >> 40) as u8;
self.data[addr + 4] = (val >> 32) as u8;
self.data[addr + 3] = (val >> 24) as u8;
self.data[addr + 2] = (val >> 16) as u8;
self.data[addr + 1] = (val >> 8) as u8;
self.data[addr + 0] = (val) as u8;
Ok(())
}
}
impl MemoryOf<u32> for MemoryBacking {
fn read(&mut self, addr: u64) -> MemoryResult<u32> {
check_alignment_range::<u32>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
Ok(
((self.data[addr+3] as u32) << 24) |
((self.data[addr+2] as u32) << 16) |
((self.data[addr+1] as u32) << 8) |
((self.data[addr+0] as u32))
)
}
fn write(&mut self, addr: u64, val: u32) -> MemoryResult<()> {
check_alignment_range::<u32>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
self.data[addr + 3] = (val >> 24) as u8;
self.data[addr + 2] = (val >> 16) as u8;
self.data[addr + 1] = (val >> 8) as u8;
self.data[addr + 0] = (val) as u8;
Ok(())
}
}
impl MemoryOf<u16> for MemoryBacking {
fn read(&mut self, addr: u64) -> MemoryResult<u16> {
check_alignment_range::<u16>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
Ok(
((self.data[addr+1] as u16) << 8) |
((self.data[addr+0] as u16))
)
}
fn write(&mut self, addr: u64, val: u16) -> MemoryResult<()> {
check_alignment_range::<u16>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
self.data[addr + 1] = (val >> 8) as u8;
self.data[addr + 0] = (val) as u8;
Ok(())
}
}
impl MemoryOf<u8> for MemoryBacking {
fn read(&mut self, addr: u64) -> MemoryResult<u8> {
check_alignment_range::<u8>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
Ok(
self.data[addr]
)
}
fn write(&mut self, addr: u64, val: u8) -> MemoryResult<()> {
check_alignment_range::<u8>(addr, &self.range)?;
let addr = (addr as usize) - self.range.start;
self.data[addr] = val;
Ok(())
}
}
impl Memory for MemoryBacking {
fn range(&self) -> Range<usize> {
self.range.clone()
}
fn as_any(&self) -> &dyn Any { self }
}
pub struct AggregateMemory {
mappings: Vec<Box<dyn Memory>>,
full_range: Range<usize>
}
impl AggregateMemory {
pub fn from_mappings(mappings: Vec<Box<dyn Memory>>) -> Self {
assert!(mappings.len() >= 1);
let mut full_range = mappings[0].range().clone();
for mapping_a in mappings.iter() {
use std::cmp::{min,max};
let range_a = mapping_a.range();
full_range.start = min(range_a.start, full_range.start);
full_range.end = max(range_a.end, full_range.end);
for mapping_b in mappings.iter() {
let range_b = mapping_b.range();
use std::ptr::eq;
if !eq(mapping_a, mapping_b) &&
(
range_a.contains(&range_b.start) ||
range_a.contains(&(range_b.end - 1))
)
{
panic!("Mappings have overlapping ranges {:x?} and {:x?}", range_a, range_b)
}
}
}
assert!(!full_range.is_empty());
assert!(full_range.start % 4 == 0 && full_range.end % 4 == 0);
AggregateMemory {
mappings,
full_range
}
}
pub fn get_io_values(&self) -> Vec<Option<u64>> {
let mut io_vals = vec![];
for mapping in &self.mappings {
if let Some(io) = mapping.as_ref().as_any().downcast_ref::<IOMemory>() {
io_vals.push(io.value);
}
}
return io_vals;
}
}
impl<TData> MemoryOf<TData> for AggregateMemory where dyn Memory: MemoryOf<TData> {
fn read(&mut self, addr: u64) -> MemoryResult<TData> {
for mapping in self.mappings.iter_mut() {
if mapping.range().contains(&(addr as usize)) {
return mapping.read(addr)
}
}
bail!(MemoryException::AddressUnmapped{addr: addr as usize})
}
fn write(&mut self, addr: u64, val: TData) -> MemoryResult<()> {
for mapping in self.mappings.iter_mut() {
if mapping.range().contains(&(addr as usize)) {
return mapping.write(addr, val)
}
}
bail!(MemoryException::AddressUnmapped{addr: addr as usize})
}
}
impl Memory for AggregateMemory {
fn range(&self) -> Range<usize> {
self.full_range.clone()
}
fn as_any(&self) -> &dyn Any { self }
}
impl From<MemoryBacking> for AggregateMemory {
fn from(backing: MemoryBacking) -> Self {
AggregateMemory {
full_range: backing.range.clone(),
mappings: vec![Box::new(backing)]
}
}
}