shrooms-vb-native/src/shrooms_vb_core.rs

160 lines
4.9 KiB
Rust
Raw Normal View History

2024-11-03 16:32:53 +00:00
use std::{ffi::c_void, ptr};
use anyhow::{anyhow, Result};
2024-11-02 20:18:41 +00:00
#[repr(C)]
2024-11-03 16:32:53 +00:00
struct VB {
_data: [u8; 0],
}
#[allow(non_camel_case_types)]
type c_int = i32;
type OnFrame = extern "C" fn(sim: *mut VB) -> c_int;
2024-11-02 20:18:41 +00:00
#[link(name = "vb")]
extern "C" {
2024-11-03 16:32:53 +00:00
#[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;
2024-11-02 20:18:41 +00:00
#[link_name = "vbInit"]
fn vb_init(sim: *mut VB) -> *mut VB;
2024-11-03 16:32:53 +00:00
#[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);
2024-11-02 20:18:41 +00:00
#[link_name = "vbSizeOf"]
fn vb_size_of() -> usize;
2024-11-03 16:32:53 +00:00
}
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
}
2024-11-02 20:18:41 +00:00
2024-11-03 16:32:53 +00:00
struct VBState {
frame_seen: bool,
2024-11-02 20:18:41 +00:00
}
pub struct CoreVB {
sim: *mut VB,
}
2024-11-03 16:32:53 +00:00
// SAFETY: the memory pointed to by sim is valid
unsafe impl Send for CoreVB {}
2024-11-02 20:18:41 +00:00
impl CoreVB {
pub fn new() -> Self {
2024-11-03 16:32:53 +00:00
// init the VB instance itself
2024-11-02 20:18:41 +00:00
let size = unsafe { vb_size_of() };
2024-11-03 16:32:53 +00:00
// 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();
2024-11-02 20:18:41 +00:00
unsafe { vb_init(sim) };
2024-11-03 16:32:53 +00:00
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))
2024-11-02 20:18:41 +00:00
}
}
2024-11-03 16:32:53 +00:00
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)
2024-11-02 20:18:41 +00:00
}
2024-11-03 16:32:53 +00:00
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);
}
}