Implement stepping
This commit is contained in:
		
							parent
							
								
									6fd8d8f5cf
								
							
						
					
					
						commit
						84f2cf7ece
					
				| 
						 | 
				
			
			@ -315,7 +315,7 @@ impl Emulator {
 | 
			
		|||
 | 
			
		||||
    fn stop_debugging(&mut self, sim_id: SimId) {
 | 
			
		||||
        if let Some(sim) = self.sims.get_mut(sim_id.to_index()) {
 | 
			
		||||
            sim.clear_breakpoints();
 | 
			
		||||
            sim.clear_debug_state();
 | 
			
		||||
        }
 | 
			
		||||
        self.debuggers.remove(&sim_id);
 | 
			
		||||
        if self.debuggers.is_empty() {
 | 
			
		||||
| 
						 | 
				
			
			@ -345,12 +345,21 @@ impl Emulator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn debug_continue(&mut self, sim_id: SimId) {
 | 
			
		||||
    fn debug_continue(&mut self, sim_id: SimId) -> bool {
 | 
			
		||||
        let Some(debugger) = self.debuggers.get_mut(&sim_id) else {
 | 
			
		||||
            self.stop_debugging(sim_id);
 | 
			
		||||
            return;
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
        debugger.stop_reason = None;
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
    fn debug_step(&mut self, sim_id: SimId) {
 | 
			
		||||
        if self.debug_continue(sim_id) {
 | 
			
		||||
            let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
 | 
			
		||||
                return;
 | 
			
		||||
            };
 | 
			
		||||
            sim.step();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn run(&mut self) {
 | 
			
		||||
| 
						 | 
				
			
			@ -412,6 +421,7 @@ impl Emulator {
 | 
			
		|||
                };
 | 
			
		||||
                if let Some(reason) = sim.stop_reason() {
 | 
			
		||||
                    let stop_reason = match reason {
 | 
			
		||||
                        StopReason::Stepped => DebugStopReason::Trace,
 | 
			
		||||
                        StopReason::Breakpoint => DebugStopReason::Breakpoint,
 | 
			
		||||
                    };
 | 
			
		||||
                    self.debug_stop(sim_id, stop_reason);
 | 
			
		||||
| 
						 | 
				
			
			@ -509,6 +519,9 @@ impl Emulator {
 | 
			
		|||
            EmulatorCommand::DebugContinue(sim_id) => {
 | 
			
		||||
                self.debug_continue(sim_id);
 | 
			
		||||
            }
 | 
			
		||||
            EmulatorCommand::DebugStep(sim_id) => {
 | 
			
		||||
                self.debug_step(sim_id);
 | 
			
		||||
            }
 | 
			
		||||
            EmulatorCommand::ReadRegister(sim_id, register, done) => {
 | 
			
		||||
                let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
 | 
			
		||||
                    return;
 | 
			
		||||
| 
						 | 
				
			
			@ -596,6 +609,7 @@ pub enum EmulatorCommand {
 | 
			
		|||
    StopDebugging(SimId),
 | 
			
		||||
    DebugInterrupt(SimId),
 | 
			
		||||
    DebugContinue(SimId),
 | 
			
		||||
    DebugStep(SimId),
 | 
			
		||||
    ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
 | 
			
		||||
    ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
 | 
			
		||||
    AddBreakpoint(SimId, u32),
 | 
			
		||||
| 
						 | 
				
			
			@ -628,6 +642,8 @@ type DebugSender = tokio::sync::mpsc::UnboundedSender<DebugEvent>;
 | 
			
		|||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum DebugStopReason {
 | 
			
		||||
    // We are stepping
 | 
			
		||||
    Trace,
 | 
			
		||||
    // We hit a breakpoint
 | 
			
		||||
    Breakpoint,
 | 
			
		||||
    // The debugger told us to pause
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -147,12 +147,18 @@ extern "C" fn on_execute(sim: *mut VB, address: u32, _code: *const u16, _length:
 | 
			
		|||
    // 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;
 | 
			
		||||
    let mut stopped = 0;
 | 
			
		||||
    if data.step_from.is_some_and(|s| s != address) {
 | 
			
		||||
        data.step_from = None;
 | 
			
		||||
        data.stop_reason = Some(StopReason::Stepped);
 | 
			
		||||
        stopped = 1;
 | 
			
		||||
    }
 | 
			
		||||
    if data.breakpoints.binary_search(&address).is_ok() {
 | 
			
		||||
        data.stop_reason = Some(StopReason::Breakpoint);
 | 
			
		||||
        stopped = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data.stop_reason = Some(StopReason::Breakpoint);
 | 
			
		||||
    1
 | 
			
		||||
    stopped
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +168,7 @@ pub const EXPECTED_FRAME_SIZE: usize = 834 * 2;
 | 
			
		|||
struct VBState {
 | 
			
		||||
    frame_seen: bool,
 | 
			
		||||
    stop_reason: Option<StopReason>,
 | 
			
		||||
    step_from: Option<u32>,
 | 
			
		||||
    breakpoints: Vec<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +179,7 @@ pub struct Sim {
 | 
			
		|||
 | 
			
		||||
pub enum StopReason {
 | 
			
		||||
    Breakpoint,
 | 
			
		||||
    Stepped,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Sim {
 | 
			
		||||
| 
						 | 
				
			
			@ -183,12 +191,14 @@ impl Sim {
 | 
			
		|||
        let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
 | 
			
		||||
        unsafe { vb_init(sim) };
 | 
			
		||||
        unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) };
 | 
			
		||||
        unsafe { vb_set_keys(sim, VBKey::SGN.bits()) };
 | 
			
		||||
        unsafe { vb_reset(sim) };
 | 
			
		||||
 | 
			
		||||
        // set up userdata
 | 
			
		||||
        let state = VBState {
 | 
			
		||||
            frame_seen: false,
 | 
			
		||||
            stop_reason: None,
 | 
			
		||||
            step_from: None,
 | 
			
		||||
            breakpoints: vec![],
 | 
			
		||||
        };
 | 
			
		||||
        unsafe { vb_set_user_data(sim, Box::into_raw(Box::new(state)).cast()) };
 | 
			
		||||
| 
						 | 
				
			
			@ -361,21 +371,35 @@ impl Sim {
 | 
			
		|||
        let data = self.get_state();
 | 
			
		||||
        if let Ok(index) = data.breakpoints.binary_search(&address) {
 | 
			
		||||
            data.breakpoints.remove(index);
 | 
			
		||||
            if data.breakpoints.is_empty() {
 | 
			
		||||
            if data.step_from.is_none() && data.breakpoints.is_empty() {
 | 
			
		||||
                unsafe { vb_set_execute_callback(self.sim, None) };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_breakpoints(&mut self) {
 | 
			
		||||
    pub fn step(&mut self) {
 | 
			
		||||
        let current_pc = unsafe { vb_get_program_counter(self.sim) };
 | 
			
		||||
        let data = self.get_state();
 | 
			
		||||
        data.step_from = Some(current_pc);
 | 
			
		||||
        unsafe {
 | 
			
		||||
            vb_set_execute_callback(self.sim, Some(on_execute));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_debug_state(&mut self) {
 | 
			
		||||
        let data = self.get_state();
 | 
			
		||||
        data.step_from = None;
 | 
			
		||||
        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()
 | 
			
		||||
        let reason = data.stop_reason.take();
 | 
			
		||||
        if data.step_from.is_none() && data.breakpoints.is_empty() {
 | 
			
		||||
            unsafe { vb_set_execute_callback(self.sim, None) };
 | 
			
		||||
        }
 | 
			
		||||
        reason
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_state(&mut self) -> &mut VBState {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,6 +258,12 @@ impl GdbConnection {
 | 
			
		|||
            self.stop_reason = None;
 | 
			
		||||
            // Don't send a response until we hit a breakpoint or get interrupted
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        } else if req.match_str("s") {
 | 
			
		||||
            self.client
 | 
			
		||||
                .send_command(EmulatorCommand::DebugStep(self.sim_id));
 | 
			
		||||
            self.stop_reason = None;
 | 
			
		||||
            // Don't send a response until we hit a breakpoint or get interrupted
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        } else if req.match_str("p") {
 | 
			
		||||
            let mut read_register = || {
 | 
			
		||||
                let register_index = req.match_hex::<usize>()?;
 | 
			
		||||
| 
						 | 
				
			
			@ -348,7 +354,8 @@ impl Drop for GdbConnection {
 | 
			
		|||
 | 
			
		||||
fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> &'static str {
 | 
			
		||||
    match reason {
 | 
			
		||||
        Some(DebugStopReason::Breakpoint) => "T05;thread:p1.t1;threads;p1.t1;reason:breakpoint",
 | 
			
		||||
        Some(DebugStopReason::Trace) => "T05;thread:p1.t1;threads:p1.t1;reason:trace;",
 | 
			
		||||
        Some(DebugStopReason::Breakpoint) => "T05;thread:p1.t1;threads:p1.t1;reason:breakpoint;",
 | 
			
		||||
        Some(DebugStopReason::Trapped) => "T05;swbreak;thread:p1.t1;threads:p1.t1;reason:trap;",
 | 
			
		||||
        None => "T00;thread:p1.t1;threads:p1.t1;",
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue