mod memory_type;
use core::{fmt::Debug, ops::Range};
use crate::interpreter::utils::unlikely;
use super::error::Error;
#[doc(inline)]
pub use memory_type::MemoryType;
pub const RAM_OFFSET: u32 = 0x80000000;
#[inline(always)]
fn checked_slice_range(slice: &[u8], start: usize, len: usize) -> Result<Range<usize>, Error> {
let end = start
.checked_add(len)
.ok_or(Error::InvalidMemoryAccessLength(len))?;
if unlikely(end > slice.len()) {
return Err(Error::InvalidMemoryAddress(end as u32));
}
Ok(start..end)
}
pub trait Memory {
fn load_bytes(&mut self, address: u32, len: usize) -> Result<&[u8], Error>;
fn mut_bytes(&mut self, address: u32, len: usize) -> Result<&mut [u8], Error>;
fn store_bytes(&mut self, address: u32, data: &[u8]) -> Result<(), Error>;
}
#[derive(Debug)]
pub struct SliceMemory<'a> {
code: &'a [u8],
ram: &'a mut [u8],
}
impl<'a> SliceMemory<'a> {
pub fn new(code: &'a [u8], ram: &'a mut [u8]) -> SliceMemory<'a> {
SliceMemory { code, ram }
}
}
impl Memory for SliceMemory<'_> {
#[inline]
fn load_bytes(&mut self, address: u32, len: usize) -> Result<&[u8], Error> {
if address >= RAM_OFFSET {
let ram_address = address.wrapping_sub(RAM_OFFSET) as usize;
checked_slice_range(self.ram, ram_address, len).map(|r| &self.ram[r])
} else {
let code_address = address as usize;
checked_slice_range(self.code, code_address, len).map(|r| &self.code[r])
}
}
#[inline]
fn mut_bytes(&mut self, address: u32, len: usize) -> Result<&mut [u8], Error> {
let ram_address = address.wrapping_sub(RAM_OFFSET) as usize;
checked_slice_range(self.ram, ram_address, len).map(|r| &mut self.ram[r])
}
#[inline]
fn store_bytes(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
let ram_address = address.wrapping_sub(RAM_OFFSET) as usize;
checked_slice_range(self.ram, ram_address, data.len()).map(|r| {
self.ram[r].copy_from_slice(data);
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn load_ram() {
let mut ram = [0x1, 0x2, 0x3, 0x4];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.load_bytes(0x80000000, 4);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &[0x1, 0x2, 0x3, 0x4]);
}
#[test]
pub fn mut_ram() {
let mut ram = [0x1, 0x2, 0x3, 0x4];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.mut_bytes(0x80000000, 4);
assert!(result.is_ok());
let bytes = result.unwrap();
bytes[0] = 0x5;
assert_eq!(bytes, &mut [0x5, 0x2, 0x3, 0x4]);
}
#[test]
pub fn load_out_of_ram() {
let mut ram = [0; 2];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.load_bytes(0x80000000, 4);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
#[test]
pub fn mut_out_of_ram() {
let mut ram = [0; 2];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.mut_bytes(0x80000000, 4);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
#[test]
pub fn store_ram() {
let mut ram = [0; 4];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.store_bytes(0x80000000, &[0x1, 0x2, 0x3, 0x4]);
assert!(result.is_ok());
assert_eq!(ram, [0x1, 0x2, 0x3, 0x4]);
}
#[test]
pub fn store_out_of_ram() {
let mut ram = [0; 2];
let mut memory = SliceMemory::new(&[], &mut ram);
let result = memory.store_bytes(0x80000000, &[0; 4]);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
#[test]
pub fn load_code() {
let code = [0x1, 0x2, 0x3, 0x4];
let mut memory = SliceMemory::new(&code, &mut []);
let result = memory.load_bytes(0x0, 4);
assert!(result.is_ok());
assert_eq!(result.unwrap(), &[0x1, 0x2, 0x3, 0x4]);
}
#[test]
pub fn mut_code() {
let code = [0; 2];
let mut memory = SliceMemory::new(&code, &mut []);
let result = memory.mut_bytes(0x0, 4);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
#[test]
pub fn store_code() {
let code = [0; 4];
let mut memory = SliceMemory::new(&code, &mut []);
let result = memory.store_bytes(0x0, &[0x1, 0x2, 0x3, 0x4]);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
#[test]
pub fn load_out_of_code() {
let code = [0; 2];
let mut memory = SliceMemory::new(&code, &mut []);
let result = memory.load_bytes(0x0, 4);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::InvalidMemoryAddress(_)
));
}
}