lemur/src/memory.rs

92 lines
2.6 KiB
Rust

use std::{
collections::HashMap,
sync::{Arc, Mutex, MutexGuard},
};
use bytemuck::BoxBytes;
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
pub struct MemoryMonitor {
client: EmulatorClient,
regions: HashMap<MemoryRegion, Arc<Mutex<BoxBytes>>>,
}
impl MemoryMonitor {
pub fn new(client: EmulatorClient) -> Self {
Self {
client,
regions: HashMap::new(),
}
}
pub fn view(&mut self, sim: SimId, start: u32, length: usize) -> MemoryView {
let region = MemoryRegion { sim, start, length };
let memory = self.regions.entry(region).or_insert_with(|| {
let mut buf = aligned_memory(start, length);
let (tx, rx) = oneshot::channel();
self.client
.send_command(EmulatorCommand::ReadMemory(sim, start, length, vec![], tx));
let bytes = pollster::block_on(rx).unwrap();
buf.copy_from_slice(&bytes);
#[expect(clippy::arc_with_non_send_sync)] // TODO: remove after bytemuck upgrade
Arc::new(Mutex::new(buf))
});
MemoryView {
memory: memory.clone(),
}
}
}
fn aligned_memory(start: u32, length: usize) -> BoxBytes {
if start % 4 == 0 && length % 4 == 0 {
let memory = vec![0u32; length / 4].into_boxed_slice();
return bytemuck::box_bytes_of(memory);
}
if start % 2 == 0 && length % 2 == 0 {
let memory = vec![0u16; length / 2].into_boxed_slice();
return bytemuck::box_bytes_of(memory);
}
let memory = vec![0u8; length].into_boxed_slice();
bytemuck::box_bytes_of(memory)
}
pub struct MemoryView {
memory: Arc<Mutex<BoxBytes>>,
}
// SAFETY: BoxBytes is supposed to be Send+Sync, will be in a new version
unsafe impl Send for MemoryView {}
impl MemoryView {
pub fn borrow(&self) -> MemoryRef<'_> {
MemoryRef {
inner: self.memory.lock().unwrap(),
}
}
}
pub struct MemoryRef<'a> {
inner: MutexGuard<'a, BoxBytes>,
}
impl MemoryRef<'_> {
pub fn read<T: bytemuck::AnyBitPattern>(&self, index: usize) -> T {
let from = index * size_of::<T>();
let to = from + size_of::<T>();
*bytemuck::from_bytes(&self.inner[from..to])
}
pub fn range<T: bytemuck::AnyBitPattern>(&self, start: usize, count: usize) -> &[T] {
let from = start * size_of::<T>();
let to = from + (count * size_of::<T>());
bytemuck::cast_slice(&self.inner[from..to])
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct MemoryRegion {
sim: SimId,
start: u32,
length: usize,
}