92 lines
2.6 KiB
Rust
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,
|
|
}
|