diff --git a/src/lib.rs b/src/lib.rs index 197d2b4..8750eb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod rom; pub mod suzy; pub mod vectors; pub mod consts; +mod shared_memory; use bus::*; use cartridge::*; @@ -14,6 +15,7 @@ use log::trace; use mikey::{video::{LYNX_SCREEN_HEIGHT, LYNX_SCREEN_WIDTH}, Mikey}; use ram::*; use rom::Rom; +use shared_memory::SharedMemory; use std::io::Error; use suzy::{registers::{joystick_swap, Joystick, Switches}, Suzy}; use vectors::Vectors; @@ -298,6 +300,14 @@ impl Lynx { * 105. // 105 lines ) } + + pub fn ram_size(&self) -> usize { + RAM_MAX as usize + } + + pub fn ram_data(&self) -> &SharedMemory { + self.ram.data() + } } impl Default for Lynx { diff --git a/src/ram.rs b/src/ram.rs index 49f5aa5..057f890 100644 --- a/src/ram.rs +++ b/src/ram.rs @@ -1,15 +1,16 @@ use log::trace; - +use shared_memory::SharedMemory; use crate::*; use serde::{Serialize, Deserialize}; use super::bus::*; use super::MMC_ADDR; -const RAM_MAX: u16 = 0xffff; +pub const RAM_MAX: u16 = 0xffff; + #[derive(Serialize, Deserialize)] pub struct Ram { - data: Vec, + data: SharedMemory, addr_r: u16, data_r: u8, ticks_to_done: i8, @@ -20,7 +21,7 @@ pub struct Ram { impl Ram { pub fn new() -> Ram { let mut r = Ram { - data: vec![0xFF; (RAM_MAX as usize) + 1], + data: SharedMemory::new((RAM_MAX as usize) + 1, 0xFF), ticks_to_done: -1, addr_r: 0, data_r: 0, @@ -45,8 +46,7 @@ impl Ram { pub fn copy(&mut self, dest: u16, buf: &[u8]) { assert!(dest as usize + buf.len() <= RAM_MAX as usize); - let d = dest as usize; - self.data[d..(d + buf.len())].copy_from_slice(buf); + self.data.copy(dest, buf); } pub fn peek(&mut self, bus: &Bus) { @@ -102,6 +102,10 @@ impl Ram { pub fn write(&self) -> bool { self.write } + + pub fn data(&self) -> &SharedMemory { + &self.data + } } impl Default for Ram { diff --git a/src/shared_memory.rs b/src/shared_memory.rs new file mode 100644 index 0000000..9036dbe --- /dev/null +++ b/src/shared_memory.rs @@ -0,0 +1,115 @@ +use std::cell::UnsafeCell; +use std::fmt; +use std::ops::{Index, IndexMut}; +use serde::de::Visitor; +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +pub struct SharedMemory { + data: UnsafeCell> +} + +// To share the RAM with Libretro, shouldn't have concurrent accesses +impl SharedMemory { + pub fn new(len: usize, fill_with: u8) -> Self { + Self { + data: UnsafeCell::new(vec![fill_with; len]) + } + } + + pub fn get_mut(&mut self) -> &mut Self { + self + } + + pub fn fill(&mut self, v: u8) { + let ptr = self.data.get_mut(); + (*ptr).fill(v); + } + + pub fn copy(&mut self, dest: u16, buf: &[u8]) { + let d = dest as usize; + let ptr = self.data.get_mut(); + (*ptr)[d..(d + buf.len())].copy_from_slice(buf); + } + + // Libretro only + #[allow(clippy::mut_from_ref)] + pub unsafe fn as_mut_slice(&self) -> &mut [u8] { + let ptr = self.data.get(); + unsafe { &mut (*ptr) } + } +} + +impl Default for SharedMemory { + fn default() -> Self { + SharedMemory::new(0, 0xFF) + } +} + +impl Index for SharedMemory { + type Output = u8; + fn index(&self, i: usize) -> &u8 { + let ptr = self.data.get(); + unsafe { &(*ptr)[i] } + } +} + +impl IndexMut for SharedMemory { + fn index_mut(&mut self, i: usize) -> &mut u8 { + let ptr = self.data.get_mut(); + &mut (*ptr)[i] + } +} + +impl Serialize for SharedMemory { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let ptr = self.data.get(); + let mut seq = serializer.serialize_seq(Some(unsafe{(*ptr).len()}))?; + unsafe { + for e in (*ptr).iter() { + seq.serialize_element(&e)?; + } + } + seq.end() + } +} + +struct SharedMemoryVisitor; + +impl SharedMemoryVisitor { + fn new() -> Self { + Self {} + } +} + +impl<'de> Visitor<'de> for SharedMemoryVisitor { + type Value = SharedMemory; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct SharedMemory") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut mem = SharedMemory::default(); + let ptr = mem.data.get_mut(); + while let Some(value) = seq.next_element()? { + (*ptr).push(value); + } + Ok(mem) + } +} + +impl<'de> Deserialize<'de> for SharedMemory { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(SharedMemoryVisitor::new()) + } +} \ No newline at end of file