Beginnings of rust emulation
This commit is contained in:
+135
-13
@@ -1,37 +1,159 @@
|
||||
use std::{ffi::c_void, ptr};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
#[repr(C)]
|
||||
struct VB { _data: [u8; 0] }
|
||||
struct VB {
|
||||
_data: [u8; 0],
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type c_int = i32;
|
||||
type OnFrame = extern "C" fn(sim: *mut VB) -> c_int;
|
||||
|
||||
#[link(name = "vb")]
|
||||
extern "C" {
|
||||
#[link_name = "vbEmulate"]
|
||||
fn vb_emulate(sim: *mut VB, cycles: *mut u32) -> c_int;
|
||||
#[link_name = "vbGetCartROM"]
|
||||
fn vb_get_cart_rom(sim: *mut VB, size: *mut u32) -> *mut c_void;
|
||||
#[link_name = "vbGetPixels"]
|
||||
fn vb_get_pixels(
|
||||
sim: *mut VB,
|
||||
left: *mut c_void,
|
||||
left_stride_x: c_int,
|
||||
left_stride_y: c_int,
|
||||
right: *mut c_void,
|
||||
right_stride_x: c_int,
|
||||
right_stride_y: c_int,
|
||||
);
|
||||
#[link_name = "vbGetUserData"]
|
||||
fn vb_get_user_data(sim: *mut VB) -> *mut c_void;
|
||||
#[link_name = "vbInit"]
|
||||
fn vb_init(sim: *mut VB) -> *mut VB;
|
||||
|
||||
#[link_name = "vbReset"]
|
||||
fn vb_reset(sim: *mut VB);
|
||||
#[link_name = "vbSetCartROM"]
|
||||
fn vb_set_cart_rom(sim: *mut VB, rom: *mut c_void, size: u32) -> c_int;
|
||||
#[link_name = "vbSetKeys"]
|
||||
fn vb_set_keys(sim: *mut VB, keys: u16) -> u16;
|
||||
#[link_name = "vbSetFrameCallback"]
|
||||
fn vb_set_frame_callback(sim: *mut VB, on_frame: OnFrame);
|
||||
#[link_name = "vbSetUserData"]
|
||||
fn vb_set_user_data(sim: *mut VB, tag: *mut c_void);
|
||||
#[link_name = "vbSizeOf"]
|
||||
fn vb_size_of() -> usize;
|
||||
}
|
||||
|
||||
#[link_name = "vbGetKeys"]
|
||||
fn vb_get_keys(sim: *mut VB) -> u16;
|
||||
extern "C" fn on_frame(sim: *mut VB) -> i32 {
|
||||
// SAFETY: the *mut VB owns its userdata.
|
||||
// There is no way for the userdata to be null or otherwise invalid.
|
||||
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
|
||||
data.frame_seen = true;
|
||||
1
|
||||
}
|
||||
|
||||
struct VBState {
|
||||
frame_seen: bool,
|
||||
}
|
||||
|
||||
pub struct CoreVB {
|
||||
sim: *mut VB,
|
||||
_data: Box<[u8]>,
|
||||
}
|
||||
|
||||
// SAFETY: the memory pointed to by sim is valid
|
||||
unsafe impl Send for CoreVB {}
|
||||
|
||||
impl CoreVB {
|
||||
pub fn new() -> Self {
|
||||
// init the VB instance itself
|
||||
let size = unsafe { vb_size_of() };
|
||||
let mut data = vec![0; size].into_boxed_slice();
|
||||
let sim = data.as_mut_ptr() as *mut VB;
|
||||
// allocate a vec of u64 so that this memory is 8-byte aligned
|
||||
let memory = vec![0u64; size.div_ceil(4)];
|
||||
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
|
||||
unsafe { vb_init(sim) };
|
||||
CoreVB {
|
||||
sim,
|
||||
_data: data
|
||||
unsafe { vb_reset(sim) };
|
||||
|
||||
// set up userdata
|
||||
let state = VBState { frame_seen: false };
|
||||
unsafe { vb_set_user_data(sim, Box::into_raw(Box::new(state)).cast()) };
|
||||
unsafe { vb_set_frame_callback(sim, on_frame) };
|
||||
|
||||
CoreVB { sim }
|
||||
}
|
||||
|
||||
pub fn load_rom(&mut self, rom: Vec<u8>) -> Result<()> {
|
||||
self.unload_rom();
|
||||
|
||||
let size = rom.len() as u32;
|
||||
let rom = Box::into_raw(rom.into_boxed_slice()).cast();
|
||||
let status = unsafe { vb_set_cart_rom(self.sim, rom, size) };
|
||||
if status == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let _: Vec<u8> =
|
||||
unsafe { Vec::from_raw_parts(rom.cast(), size as usize, size as usize) };
|
||||
Err(anyhow!("Invalid ROM size of {} bytes", size))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> u16 {
|
||||
unsafe { vb_get_keys(self.sim) }
|
||||
fn unload_rom(&mut self) -> Option<Vec<u8>> {
|
||||
let mut size = 0;
|
||||
let rom = unsafe { vb_get_cart_rom(self.sim, &mut size) };
|
||||
if rom.is_null() {
|
||||
return None;
|
||||
}
|
||||
unsafe { vb_set_cart_rom(self.sim, ptr::null_mut(), 0) };
|
||||
let vec = unsafe { Vec::from_raw_parts(rom.cast(), size as usize, size as usize) };
|
||||
Some(vec)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emulate_frame(&mut self) {
|
||||
let mut cycles = 20_000_000;
|
||||
unsafe { vb_emulate(self.sim, &mut cycles) };
|
||||
}
|
||||
|
||||
pub fn read_pixels(&mut self, buffers: &mut [Vec<u8>; 2]) -> bool {
|
||||
// SAFETY: the *mut VB owns its userdata.
|
||||
// There is no way for the userdata to be null or otherwise invalid.
|
||||
let data: &mut VBState = unsafe { &mut *vb_get_user_data(self.sim).cast() };
|
||||
if !data.frame_seen {
|
||||
return false;
|
||||
}
|
||||
data.frame_seen = false;
|
||||
|
||||
assert_eq!(buffers[0].len(), 384 * 224);
|
||||
assert_eq!(buffers[1].len(), 384 * 224);
|
||||
unsafe {
|
||||
vb_get_pixels(
|
||||
self.sim,
|
||||
buffers[0].as_mut_ptr().cast(),
|
||||
1,
|
||||
384,
|
||||
buffers[1].as_mut_ptr().cast(),
|
||||
1,
|
||||
384,
|
||||
);
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_keys(&mut self, keys: u16) {
|
||||
unsafe { vb_set_keys(self.sim, keys) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CoreVB {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: the *mut VB owns its userdata.
|
||||
// There is no way for the userdata to be null or otherwise invalid.
|
||||
let ptr: *mut VBState = unsafe { vb_get_user_data(self.sim).cast() };
|
||||
// SAFETY: we made this pointer ourselves, we can for sure free it
|
||||
unsafe { drop(Box::from_raw(ptr)) };
|
||||
|
||||
let len = unsafe { vb_size_of() }.div_ceil(4);
|
||||
// SAFETY: the sim's memory originally came from a Vec<u64>
|
||||
let bytes: Vec<u64> = unsafe { Vec::from_raw_parts(self.sim.cast(), len, len) };
|
||||
drop(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user