diff --git a/arm7tdmi/src/lib.rs b/arm7tdmi/src/lib.rs index daa61bb..0e69b30 100644 --- a/arm7tdmi/src/lib.rs +++ b/arm7tdmi/src/lib.rs @@ -95,6 +95,7 @@ impl fmt::Display for CpuState { } #[derive(Debug, Primitive, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] pub enum CpuMode { User = 0b10000, Fiq = 0b10001, diff --git a/core/src/gba.rs b/core/src/gba.rs index af6569c..1a5af23 100644 --- a/core/src/gba.rs +++ b/core/src/gba.rs @@ -2,7 +2,6 @@ use std::cell::Cell; use std::rc::Rc; -use arm7tdmi::gdbstub::stub::SingleThreadStopReason; use bincode; use serde::{Deserialize, Serialize}; @@ -251,19 +250,9 @@ impl GameBoyAdvance { /// Recv & handle messages from the debugger, and return if we are stopped or not pub fn debugger_run(&mut self) { - let mut should_interrupt_frame = false; let debugger = self.debugger.take().expect("debugger should be None here"); - self.debugger = debugger.handle_incoming_requests(self, &mut should_interrupt_frame); - - if let Some(debugger) = &mut self.debugger { - if should_interrupt_frame { - debugger.notify_stop_reason(SingleThreadStopReason::DoneStep); - } else { - self.frame_interruptible(); - } - } else { - error!("debugger was disconnected!"); - } + self.debugger = debugger.handle_incoming_requests(self); + self.frame_interruptible(); } #[inline] @@ -272,10 +261,15 @@ impl GameBoyAdvance { } #[inline] - pub(crate) fn cpu_step(&mut self) { + fn cpu_interrupt(&mut self) { + self.cpu.irq(); + self.io_devs.haltcnt = HaltState::Running; // Clear out from low power mode + } + + #[inline] + fn cpu_step(&mut self) { if self.io_devs.intc.irq_pending() { - self.cpu.irq(); - self.io_devs.haltcnt = HaltState::Running; + self.cpu_interrupt(); } self.cpu.step(); } @@ -285,7 +279,29 @@ impl GameBoyAdvance { match (self.io_devs.dmac.is_active(), self.io_devs.haltcnt) { (true, _) => Some(BusMaster::Dma), (false, HaltState::Running) => Some(BusMaster::Cpu), - (false, _) => None, + (false, HaltState::Halt) => None, + } + } + + #[inline] + pub(crate) fn single_step(&mut self) { + // 3 Options: + // 1. DMA is active - thus CPU is blocked + // 2. DMA inactive and halt state is RUN - CPU can run + // 3. DMA inactive and halt state is HALT - CPU is blocked + match self.get_bus_master() { + Some(BusMaster::Dma) => self.dma_step(), + Some(BusMaster::Cpu) => self.cpu_step(), + None => { + // Halt mode - system is in a low-power mode, only (IE and IF) can release CPU from this state. + if self.io_devs.intc.irq_pending() { + self.cpu_interrupt(); + } else { + // Fast-forward to next pending HW event so we don't waste time idle-looping when we know the only way + // To get out of Halt mode is through an interrupt. + self.scheduler.fast_forward_to_next(); + } + } } } @@ -293,60 +309,45 @@ impl GameBoyAdvance { /// @return number of cycle actually ran #[inline] pub(super) fn run(&mut self, cycles_to_run: usize) -> usize { - let run_start_time = self.scheduler.timestamp(); + let start_time = self.scheduler.timestamp(); + let end_time = start_time + cycles_to_run; // Register an event to mark the end of this run self.scheduler - .schedule_at(EventType::RunLimitReached, run_start_time + cycles_to_run); + .schedule_at(EventType::RunLimitReached, end_time); - let mut running = true; - while running { - // The tricky part is to avoid unnecessary calls for Scheduler::process_pending, + 'running: loop { + // The tricky part is to avoid unnecessary calls for Scheduler::handle_events, // performance-wise it would be best to run as many cycles as fast as possible while we know there are no pending events. - // Fast forward emulation until an event occurs - 'run_unitl_next_event: while self.scheduler.timestamp() - <= self.scheduler.timestamp_of_next_event() + // Safety: Since we pushed a RunLimitReached event, we know this check has a hard limit + while self.scheduler.timestamp() + <= unsafe { self.scheduler.timestamp_of_next_event_unchecked() } { - // 3 Options: - // 1. DMA is active - thus CPU is blocked - // 2. DMA inactive and halt state is RUN - CPU can run - // 3. DMA inactive and halt state is HALT - CPU is blocked - match self.get_bus_master() { - Some(BusMaster::Dma) => self.dma_step(), - Some(BusMaster::Cpu) => { - self.cpu_step(); - if CHECK_BREAKPOINTS { - if let Some(bp) = self.cpu.check_breakpoint() { - debug!("Arm7tdmi breakpoint hit 0x{:08x}", bp); - self.scheduler.cancel_pending(EventType::RunLimitReached); - running = false; - - if let Some(debugger) = &mut self.debugger { - debugger.notify_breakpoint(bp); - } - - break 'run_unitl_next_event; - } - } - } - None => { - if self.io_devs.intc.irq_pending() { - self.io_devs.haltcnt = HaltState::Running; - } else { - self.scheduler.fast_forward_to_next(); - self.handle_events(&mut running) + self.single_step(); + if CHECK_BREAKPOINTS { + if let Some(bp) = self.cpu.check_breakpoint() { + debug!("Arm7tdmi breakpoint hit 0x{:08x}", bp); + self.scheduler.cancel_pending(EventType::RunLimitReached); + let _ = self.handle_events(); + if let Some(debugger) = &mut self.debugger { + debugger.notify_breakpoint(bp); } + break 'running; } } } - self.handle_events(&mut running); + if self.handle_events() { + break 'running; + } } - self.scheduler.timestamp() - run_start_time + self.scheduler.timestamp() - start_time } - fn handle_events(&mut self, run_limit_flag: &mut bool) { + /// Handle all pending scheduler events and return if run limit was reached. + #[inline] + pub(super) fn handle_events(&mut self) -> bool { let io = &mut (*self.io_devs); while let Some((event, event_time)) = self.scheduler.pop_pending_event() { // Since we only examine the scheduler queue every so often, most events will be handled late by a few cycles. @@ -354,8 +355,8 @@ impl GameBoyAdvance { // every cpu cycle, where in 99% of cases it will always be empty. let new_event = match event { EventType::RunLimitReached => { - *run_limit_flag = false; - None + // If we have pending events, we handle by the next frame. + return true; } EventType::DmaActivateChannel(channel_id) => { io.dmac.activate_channel(channel_id); @@ -375,6 +376,7 @@ impl GameBoyAdvance { self.scheduler.schedule_at(new_event, event_time + when) } } + false } pub fn skip_bios(&mut self) { @@ -414,26 +416,6 @@ impl GameBoyAdvance { None } - #[cfg(feature = "debugger")] - /// 'step' function that checks for breakpoints - /// TODO avoid code duplication - pub fn step_debugger(&mut self) -> Option { - // clear any pending DMAs - self.dma_step(); - - // Run the CPU - self.cpu_step(); - - let breakpoint = self.check_breakpoint(); - - let mut _running = true; - while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() { - self.handle_event(event, cycles_late, &mut _running); - } - - breakpoint - } - pub fn get_frame_buffer(&self) -> &[u32] { self.sysbus.io.gpu.get_frame_buffer() } diff --git a/core/src/gdb_support.rs b/core/src/gdb_support.rs index a39d986..eaa90ae 100644 --- a/core/src/gdb_support.rs +++ b/core/src/gdb_support.rs @@ -9,13 +9,14 @@ use arm7tdmi::gdbstub::target::TargetError; use arm7tdmi::gdbstub::target::{ext::base::singlethread::SingleThreadBase, Target}; use arm7tdmi::gdbstub_arch::arm::reg::ArmCoreRegs; use arm7tdmi::memory::Addr; -use crossbeam::channel::{Receiver, Sender}; +use crossbeam::channel::Receiver; // mod target; mod event_loop; pub(crate) mod gdb_thread; mod memory_map; mod target; +use target::DebuggerTarget; use crate::GameBoyAdvance; @@ -34,153 +35,119 @@ pub(crate) enum DebuggerRequest { Disconnected(DisconnectReason), } -pub struct DebuggerTarget { - tx: Sender, - request_complete_signal: Arc<(Mutex, Condvar)>, - stop_signal: Arc<(Mutex>, Condvar)>, - memory_map: String, -} - -impl DebuggerTarget { - #[inline] - pub fn wait_for_operation(&mut self) { - let (lock, cvar) = &*self.request_complete_signal; - let mut finished = lock.lock().unwrap(); - while !*finished { - finished = cvar.wait(finished).unwrap(); - } - *finished = false; - } -} - pub(crate) struct DebuggerRequestHandler { rx: Receiver, request_complete_signal: Arc<(Mutex, Condvar)>, - stop_signal: Arc<(Mutex>, Condvar)>, + stop_signal: Arc<(Mutex>>, Condvar)>, thread: JoinHandle<()>, stopped: bool, } -enum DebuggerStatus { - RequestComplete, - ResumeRequested, - StopRequested, - Disconnected(DisconnectReason), -} - impl DebuggerRequestHandler { + pub fn handle_incoming_requests( + mut self, + gba: &mut GameBoyAdvance, + ) -> Option { + if self.thread.is_finished() { + warn!("gdb server thread unexpectdly died"); + return self.terminate(); + } + loop { + // Handle as much as messages as possible + while let Ok(mut req) = self.rx.try_recv() { + match self.handle_request(gba, &mut req) { + Ok(Some(disconnect_reason)) => { + debug!("Debugger disconnected due to {:?}", disconnect_reason); + debug!("closing gdbserver thread"); + return self.terminate(); + } + Ok(None) => {} + Err(_) => { + error!("An error occured while handling debug request {:?}", req); + return self.terminate(); + } + } + } + // If target is in stopped state, stay here. + if !self.stopped { + break; + } + } + Some(self) + } + fn handle_request( &mut self, gba: &mut GameBoyAdvance, req: &mut DebuggerRequest, - ) -> Result::Error>> { + ) -> Result, TargetError<::Error>> { use DebuggerRequest::*; match req { ReadRegs(regs) => { let mut regs = regs.lock().unwrap(); gba.cpu.read_registers(&mut regs)?; - debug!("Debugger requested to read regs: {:?}", regs); - Ok(DebuggerStatus::RequestComplete) + trace!("Debugger requested to read regs: {:?}", regs); + self.complete_request(None) } WriteRegs(regs) => { - debug!("Debugger requested to write regs: {:?}", regs); + trace!("Debugger requested to write regs: {:?}", regs); gba.cpu.write_registers(regs)?; - Ok(DebuggerStatus::RequestComplete) + self.complete_request(None) } ReadAddrs(addr, data) => { let mut data = data.lock().unwrap(); - debug!( + trace!( "Debugger requested to read {} bytes from 0x{:08x}", data.len(), addr ); gba.cpu.read_addrs(*addr, &mut data)?; - Ok(DebuggerStatus::RequestComplete) + self.complete_request(None) } WriteAddrs(addr, data) => { - debug!( + trace!( "Debugger requested to write {} bytes at 0x{:08x}", data.len(), addr ); gba.cpu.write_addrs(*addr, &data)?; - Ok(DebuggerStatus::RequestComplete) + self.complete_request(None) } Interrupt => { - debug!("Debugger requested stopped"); - self.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT)); - Ok(DebuggerStatus::StopRequested) + debug!("Ctrl-C from debugger"); + self.stopped = true; + self.complete_request(Some(SingleThreadStopReason::Signal(Signal::SIGINT))) } Resume => { - debug!("Debugger requested resume"); + debug!("Resume"); self.stopped = false; - Ok(DebuggerStatus::ResumeRequested) + self.complete_request(None) } SingleStep => { debug!("Debugger requested single step"); - gba.cpu_step(); - let stop_reason = SingleThreadStopReason::DoneStep; - self.notify_stop_reason(stop_reason); - Ok(DebuggerStatus::StopRequested) + self.stopped = true; + gba.single_step(); + let _ = gba.handle_events(); + self.complete_request(Some(SingleThreadStopReason::DoneStep)) } AddSwBreakpoint(addr) => { gba.cpu.add_breakpoint(*addr); - Ok(DebuggerStatus::RequestComplete) + self.complete_request(None) } DelSwBreakpoint(addr) => { gba.cpu.del_breakpoint(*addr); - Ok(DebuggerStatus::RequestComplete) + self.complete_request(None) } - Disconnected(reason) => Ok(DebuggerStatus::Disconnected(*reason)), + Disconnected(reason) => Ok(Some(*reason)), } } - fn terminate(mut self, should_interrupt_frame: &mut bool) -> Option { + fn terminate(mut self) -> Option { self.notify_stop_reason(SingleThreadStopReason::Exited(1)); self.thread.join().unwrap(); - self.stopped = true; - *should_interrupt_frame = true; None } - pub fn handle_incoming_requests( - mut self, - gba: &mut GameBoyAdvance, - should_interrupt_frame: &mut bool, - ) -> Option { - if self.thread.is_finished() { - warn!("gdb server thread unexpectdly died"); - return self.terminate(should_interrupt_frame); - } - while let Ok(mut req) = self.rx.try_recv() { - match self.handle_request(gba, &mut req) { - Ok(DebuggerStatus::RequestComplete) => { - self.notify_request_complete(); - } - Ok(DebuggerStatus::StopRequested) => { - self.stopped = true; - self.notify_request_complete(); - } - Ok(DebuggerStatus::ResumeRequested) => { - self.stopped = false; - self.notify_request_complete(); - } - Ok(DebuggerStatus::Disconnected(reason)) => { - debug!("Debugger disconnected due to {:?}", reason); - debug!("closing gdbserver thread"); - return self.terminate(should_interrupt_frame); - } - - Err(_) => { - error!("An error occured while handling debug request {:?}", req); - return self.terminate(should_interrupt_frame); - } - } - } - *should_interrupt_frame = self.stopped; - Some(self) - } - fn notify_request_complete(&mut self) { let (lock, cvar) = &*self.request_complete_signal; let mut finished = lock.lock().unwrap(); @@ -188,15 +155,27 @@ impl DebuggerRequestHandler { cvar.notify_one(); } + fn complete_request( + &mut self, + stop_reason: Option>, + ) -> Result, TargetError<::Error>> { + self.notify_request_complete(); + if let Some(stop_reason) = stop_reason { + self.notify_stop_reason(stop_reason); + } + Ok(None) + } + pub fn notify_stop_reason(&mut self, reason: SingleThreadStopReason) { - self.stopped = true; + debug!("Notifying debugger on stop reason: {:?}", reason); let (lock, cvar) = &*self.stop_signal; let mut stop_reason = lock.lock().unwrap(); - *stop_reason = reason; + *stop_reason = Some(reason); cvar.notify_one(); } pub fn notify_breakpoint(&mut self, _bp: Addr) { + self.stopped = true; self.notify_stop_reason(SingleThreadStopReason::SwBreak(())); } } diff --git a/core/src/gdb_support/event_loop.rs b/core/src/gdb_support/event_loop.rs index c9bb2f1..a248e25 100644 --- a/core/src/gdb_support/event_loop.rs +++ b/core/src/gdb_support/event_loop.rs @@ -6,9 +6,9 @@ use arm7tdmi::gdb::gdbstub::{ target::Target, }; -use super::{DebuggerRequest, DebuggerTarget}; +use super::{target::DebuggerTarget, DebuggerRequest}; -pub struct DebuggerEventLoop {} +pub(crate) struct DebuggerEventLoop {} impl run_blocking::BlockingEventLoop for DebuggerEventLoop { type Target = DebuggerTarget; @@ -35,17 +35,12 @@ impl run_blocking::BlockingEventLoop for DebuggerEventLoop { return Ok(run_blocking::Event::IncomingData(byte)); } else { // try and wait for the stop reason - let (lock, cvar) = &*target.stop_signal; - let stop_reason = lock.lock().unwrap(); - let (stop_reason, timeout_result) = cvar - .wait_timeout(stop_reason, Duration::from_millis(10)) - .unwrap(); - if timeout_result.timed_out() { - // timed-out, try again later - continue; + if let Some(stop_reason) = + target.wait_for_stop_reason_timeout(Duration::from_millis(10)) + { + info!("Target stopped due to {:?}!", stop_reason); + return Ok(run_blocking::Event::TargetStopped(stop_reason)); } - info!("Target stopped due to {:?}!", stop_reason); - return Ok(run_blocking::Event::TargetStopped(*stop_reason)); } } } @@ -54,12 +49,8 @@ impl run_blocking::BlockingEventLoop for DebuggerEventLoop { target: &mut DebuggerTarget, ) -> Result>, ::Error> { info!("on_interrupt: sending stop message"); - target.tx.send(DebuggerRequest::Interrupt).unwrap(); - target.wait_for_operation(); + target.debugger_request(DebuggerRequest::Interrupt); info!("Waiting for target to stop "); - let (lock, cvar) = &*target.stop_signal; - let stop_signal = lock.lock().unwrap(); - let stop_signal = cvar.wait(stop_signal).unwrap(); - Ok(Some(*stop_signal)) + Ok(Some(target.wait_for_stop_reason_blocking())) } } diff --git a/core/src/gdb_support/gdb_thread.rs b/core/src/gdb_support/gdb_thread.rs index 59e9359..c830bd7 100644 --- a/core/src/gdb_support/gdb_thread.rs +++ b/core/src/gdb_support/gdb_thread.rs @@ -11,9 +11,8 @@ use arm7tdmi::{ use crate::{GBAError, GameBoyAdvance}; -use super::{ - event_loop::DebuggerEventLoop, DebuggerRequest, DebuggerRequestHandler, DebuggerTarget, -}; +use super::target::DebuggerTarget; +use super::{event_loop::DebuggerEventLoop, DebuggerRequestHandler}; /// Starts a gdbserver thread pub(crate) fn start_gdb_server_thread( @@ -22,10 +21,7 @@ pub(crate) fn start_gdb_server_thread( ) -> Result { let (tx, rx) = crossbeam::channel::unbounded(); let request_complete_signal = Arc::new((Mutex::new(false), Condvar::new())); - let stop_signal = Arc::new(( - Mutex::new(SingleThreadStopReason::Signal(Signal::SIGINT)), - Condvar::new(), - )); + let stop_signal = Arc::new((Mutex::new(None), Condvar::new())); let stop_signal_2 = stop_signal.clone(); let request_complete_signal_2 = request_complete_signal.clone(); let memory_map = gba.sysbus.generate_memory_map_xml().unwrap(); @@ -35,21 +31,14 @@ pub(crate) fn start_gdb_server_thread( debug!("starting GDB Server thread"); let conn: Box> = Box::new(conn); - let mut target = DebuggerTarget { - tx, - request_complete_signal: request_complete_signal_2, - stop_signal: stop_signal_2, - memory_map, - }; + let mut target = + DebuggerTarget::new(tx, request_complete_signal_2, stop_signal_2, memory_map); let gdbserver = GdbStub::new(conn); let disconnect_reason = gdbserver .run_blocking::(&mut target) .map_err(|e| e.to_string()) .unwrap(); - target - .tx - .send(DebuggerRequest::Disconnected(disconnect_reason)) - .unwrap(); + target.disconnect(disconnect_reason); }); let mut debugger = DebuggerRequestHandler { diff --git a/core/src/gdb_support/target.rs b/core/src/gdb_support/target.rs index 3bdc5fd..b8da327 100644 --- a/core/src/gdb_support/target.rs +++ b/core/src/gdb_support/target.rs @@ -1,8 +1,11 @@ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Condvar, Mutex}; +use std::time::Duration; /// Implementing the Target trait for gdbstub use arm7tdmi::gdb::{copy_range_to_buf, gdbstub, gdbstub_arch}; +use crossbeam::channel::Sender; use gdbstub::common::Signal; +use gdbstub::stub::{DisconnectReason, SingleThreadStopReason}; use gdbstub::target::ext::base::singlethread::{ SingleThreadBase, SingleThreadResume, SingleThreadSingleStep, }; @@ -12,7 +15,78 @@ use gdbstub::target::ext::breakpoints::BreakpointsOps; use gdbstub::target::{self, Target, TargetError, TargetResult}; use gdbstub_arch::arm::reg::ArmCoreRegs; -use super::{DebuggerRequest, DebuggerTarget}; +use super::DebuggerRequest; + +pub(crate) struct DebuggerTarget { + tx: Sender, + request_complete_signal: Arc<(Mutex, Condvar)>, + pub(crate) stop_signal: Arc<(Mutex>>, Condvar)>, + pub(crate) memory_map: String, +} + +impl DebuggerTarget { + pub fn new( + tx: Sender, + request_complete_signal: Arc<(Mutex, Condvar)>, + stop_signal: Arc<(Mutex>>, Condvar)>, + memory_map: String, + ) -> DebuggerTarget { + DebuggerTarget { + tx, + request_complete_signal, + stop_signal, + memory_map, + } + } + + pub fn debugger_request(&mut self, req: DebuggerRequest) { + let (lock, cvar) = &*self.request_complete_signal; + let mut finished = lock.lock().unwrap(); + *finished = false; + + // now send the request + self.tx.send(req).unwrap(); + + // wait for the notification + while !*finished { + finished = cvar.wait(finished).unwrap(); + } + *finished = false; + } + + pub fn wait_for_stop_reason_timeout( + &mut self, + timeout: Duration, + ) -> Option> { + let (lock, cvar) = &*self.stop_signal; + let mut stop_reason = lock.lock().unwrap(); + if let Some(stop_reason) = stop_reason.take() { + return Some(stop_reason); + } + let (mut stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap(); + if timeout_result.timed_out() { + None + } else { + Some(stop_reason.take().expect("None is not expected here")) + } + } + + pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason { + let (lock, cvar) = &*self.stop_signal; + let mut stop_reason = lock.lock().unwrap(); + if let Some(stop_reason) = stop_reason.take() { + return stop_reason; + } + let mut stop_reason = cvar.wait(stop_reason).unwrap(); + stop_reason.take().expect("None is not expected here") + } + + pub fn disconnect(&mut self, disconnect_reason: DisconnectReason) { + self.tx + .send(DebuggerRequest::Disconnected(disconnect_reason)) + .unwrap(); + } +} impl Target for DebuggerTarget { type Error = (); @@ -37,28 +111,19 @@ impl Target for DebuggerTarget { impl SingleThreadBase for DebuggerTarget { fn read_registers(&mut self, regs: &mut ArmCoreRegs) -> TargetResult<(), Self> { let regs_copy = Arc::new(Mutex::new(ArmCoreRegs::default())); - self.tx - .send(DebuggerRequest::ReadRegs(regs_copy.clone())) - .unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::ReadRegs(regs_copy.clone())); regs_copy.lock().unwrap().clone_into(regs); Ok(()) } fn write_registers(&mut self, regs: &ArmCoreRegs) -> TargetResult<(), Self> { - self.tx - .send(DebuggerRequest::WriteRegs(regs.clone())) - .unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::WriteRegs(regs.clone())); Ok(()) } fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> { let buffer = Arc::new(Mutex::new(vec![0; data.len()].into_boxed_slice())); - self.tx - .send(DebuggerRequest::ReadAddrs(start_addr, buffer.clone())) - .unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::ReadAddrs(start_addr, buffer.clone())); data.copy_from_slice(&buffer.lock().unwrap()); Ok(()) } @@ -78,8 +143,7 @@ impl SingleThreadBase for DebuggerTarget { impl SingleThreadResume for DebuggerTarget { fn resume(&mut self, _signal: Option) -> Result<(), Self::Error> { - self.tx.send(DebuggerRequest::Resume).unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::Resume); Ok(()) } @@ -94,8 +158,7 @@ impl SingleThreadResume for DebuggerTarget { impl SingleThreadSingleStep for DebuggerTarget { fn step(&mut self, _signal: Option) -> Result<(), Self::Error> { - self.tx.send(DebuggerRequest::SingleStep).unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::SingleStep); Ok(()) } } @@ -132,10 +195,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget { addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { - self.tx - .send(DebuggerRequest::AddSwBreakpoint(addr)) - .unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::AddSwBreakpoint(addr)); Ok(true) } @@ -144,10 +204,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget { addr: u32, _kind: gdbstub_arch::arm::ArmBreakpointKind, ) -> TargetResult { - self.tx - .send(DebuggerRequest::DelSwBreakpoint(addr)) - .unwrap(); - self.wait_for_operation(); + self.debugger_request(DebuggerRequest::DelSwBreakpoint(addr)); Ok(true) } } diff --git a/core/src/iodev.rs b/core/src/iodev.rs index 84c597b..56e5e21 100644 --- a/core/src/iodev.rs +++ b/core/src/iodev.rs @@ -22,7 +22,7 @@ use self::consts::*; pub enum HaltState { Running, Halt, // In Halt mode, the CPU is paused as long as (IE AND IF)=0, - Stop, // In Stop mode, most of the hardware including sound and video are paused + // Stop, // In Stop mode, most of the hardware including sound and video are paused TODO: handle } #[derive(Clone, Serialize, Deserialize)] @@ -288,7 +288,7 @@ impl BusIO for IoDevices { REG_POSTFLG => io.post_boot_flag = value != 0, REG_HALTCNT => { if value & 0x80 != 0 { - io.haltcnt = HaltState::Stop; + // io.haltcnt = HaltState::Stop; panic!("Can't handle HaltCtrl == Stop yet"); } else { io.haltcnt = HaltState::Halt; diff --git a/core/src/sched.rs b/core/src/sched.rs index 2cbff20..0f734fa 100644 --- a/core/src/sched.rs +++ b/core/src/sched.rs @@ -194,9 +194,12 @@ impl Scheduler { } #[inline] - /// The event queue is assumed to be not empty - pub fn timestamp_of_next_event(&self) -> usize { - self.events.peek().unwrap_or_else(|| unreachable!()).time + /// Safety - Onyl safe to call when we know the event queue is not empty + pub unsafe fn timestamp_of_next_event_unchecked(&self) -> usize { + self.events + .peek() + .unwrap_or_else(|| std::hint::unreachable_unchecked()) + .time } #[inline] diff --git a/platform/rustboyadvance-sdl2/src/main.rs b/platform/rustboyadvance-sdl2/src/main.rs index 13311cd..01d5f7f 100644 --- a/platform/rustboyadvance-sdl2/src/main.rs +++ b/platform/rustboyadvance-sdl2/src/main.rs @@ -107,7 +107,6 @@ fn main() -> Result<(), Box> { // normal_panic(panic_info); // })); - if opts.skip_bios { println!("Skipping bios animation.."); gba.skip_bios();