Implement execute breakpoints
This commit is contained in:
@@ -63,6 +63,8 @@ pub enum VBRegister {
|
||||
}
|
||||
|
||||
type OnFrame = extern "C" fn(sim: *mut VB) -> c_int;
|
||||
type OnExecute =
|
||||
extern "C" fn(sim: *mut VB, address: u32, code: *const u16, length: c_int) -> c_int;
|
||||
|
||||
#[link(name = "vb")]
|
||||
extern "C" {
|
||||
@@ -109,8 +111,10 @@ extern "C" {
|
||||
fn vb_set_cart_ram(sim: *mut VB, sram: *mut c_void, size: u32) -> c_int;
|
||||
#[link_name = "vbSetCartROM"]
|
||||
fn vb_set_cart_rom(sim: *mut VB, rom: *mut c_void, size: u32) -> c_int;
|
||||
#[link_name = "vbSetExecuteCallback"]
|
||||
fn vb_set_execute_callback(sim: *mut VB, callback: Option<OnExecute>);
|
||||
#[link_name = "vbSetFrameCallback"]
|
||||
fn vb_set_frame_callback(sim: *mut VB, on_frame: OnFrame);
|
||||
fn vb_set_frame_callback(sim: *mut VB, callback: Option<OnFrame>);
|
||||
#[link_name = "vbSetKeys"]
|
||||
fn vb_set_keys(sim: *mut VB, keys: u16) -> u16;
|
||||
#[link_name = "vbSetOption"]
|
||||
@@ -130,7 +134,7 @@ extern "C" {
|
||||
fn vb_size_of() -> usize;
|
||||
}
|
||||
|
||||
extern "C" fn on_frame(sim: *mut VB) -> i32 {
|
||||
extern "C" fn on_frame(sim: *mut VB) -> c_int {
|
||||
// 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() };
|
||||
@@ -138,12 +142,27 @@ extern "C" fn on_frame(sim: *mut VB) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
extern "C" fn on_execute(sim: *mut VB, address: u32, _code: *const u16, _length: c_int) -> c_int {
|
||||
// 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() };
|
||||
|
||||
if data.breakpoints.binary_search(&address).is_err() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data.stop_reason = Some(StopReason::Breakpoint);
|
||||
1
|
||||
}
|
||||
|
||||
const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
|
||||
const AUDIO_CAPACITY_FLOATS: usize = AUDIO_CAPACITY_SAMPLES * 2;
|
||||
pub const EXPECTED_FRAME_SIZE: usize = 834 * 2;
|
||||
|
||||
struct VBState {
|
||||
frame_seen: bool,
|
||||
stop_reason: Option<StopReason>,
|
||||
breakpoints: Vec<u32>,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
@@ -151,8 +170,9 @@ pub struct Sim {
|
||||
sim: *mut VB,
|
||||
}
|
||||
|
||||
// SAFETY: the memory pointed to by sim is valid
|
||||
unsafe impl Send for Sim {}
|
||||
pub enum StopReason {
|
||||
Breakpoint,
|
||||
}
|
||||
|
||||
impl Sim {
|
||||
pub fn new() -> Self {
|
||||
@@ -166,9 +186,13 @@ impl Sim {
|
||||
unsafe { vb_reset(sim) };
|
||||
|
||||
// set up userdata
|
||||
let state = VBState { frame_seen: false };
|
||||
let state = VBState {
|
||||
frame_seen: false,
|
||||
stop_reason: None,
|
||||
breakpoints: vec![],
|
||||
};
|
||||
unsafe { vb_set_user_data(sim, Box::into_raw(Box::new(state)).cast()) };
|
||||
unsafe { vb_set_frame_callback(sim, on_frame) };
|
||||
unsafe { vb_set_frame_callback(sim, Some(on_frame)) };
|
||||
|
||||
// set up audio buffer
|
||||
let audio_buffer = vec![0.0f32; AUDIO_CAPACITY_FLOATS];
|
||||
@@ -257,9 +281,7 @@ impl Sim {
|
||||
}
|
||||
|
||||
pub fn read_pixels(&mut self, buffers: &mut [u8]) -> 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() };
|
||||
let data = self.get_state();
|
||||
if !data.frame_seen {
|
||||
return false;
|
||||
}
|
||||
@@ -323,6 +345,44 @@ impl Sim {
|
||||
into.push(byte as u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_breakpoint(&mut self, address: u32) {
|
||||
let data = self.get_state();
|
||||
if let Err(index) = data.breakpoints.binary_search(&address) {
|
||||
data.breakpoints.insert(index, address);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
vb_set_execute_callback(self.sim, Some(on_execute));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_breakpoint(&mut self, address: u32) {
|
||||
let data = self.get_state();
|
||||
if let Ok(index) = data.breakpoints.binary_search(&address) {
|
||||
data.breakpoints.remove(index);
|
||||
if data.breakpoints.is_empty() {
|
||||
unsafe { vb_set_execute_callback(self.sim, None) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_breakpoints(&mut self) {
|
||||
let data = self.get_state();
|
||||
data.breakpoints.clear();
|
||||
unsafe { vb_set_execute_callback(self.sim, None) };
|
||||
}
|
||||
|
||||
pub fn stop_reason(&mut self) -> Option<StopReason> {
|
||||
let data = self.get_state();
|
||||
data.stop_reason.take()
|
||||
}
|
||||
|
||||
fn get_state(&mut self) -> &mut VBState {
|
||||
// SAFETY: the *mut VB owns its userdata.
|
||||
// There is no way for the userdata to be null or otherwise invalid.
|
||||
unsafe { &mut *vb_get_user_data(self.sim).cast() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Sim {
|
||||
|
||||
Reference in New Issue
Block a user