Gdb fixes

Former-commit-id: f8507472a94386902962d1ffead7dd6f85624d82
Former-commit-id: 15446ef199288e887b7bc02fec08f9798c4c6ea2
This commit is contained in:
Michel Heily 2022-09-20 23:58:54 +03:00
parent c8c1cdd57b
commit 838ca43ac4
9 changed files with 238 additions and 237 deletions

View file

@ -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,

View file

@ -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<const CHECK_BREAKPOINTS: bool>(&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<u32> {
// 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()
}

View file

@ -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<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, 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<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, Condvar)>,
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, 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<DebuggerRequestHandler> {
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<DebuggerStatus, TargetError<<DebuggerTarget as Target>::Error>> {
) -> Result<Option<DisconnectReason>, TargetError<<DebuggerTarget as Target>::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<DebuggerRequestHandler> {
fn terminate(mut self) -> Option<DebuggerRequestHandler> {
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<DebuggerRequestHandler> {
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<SingleThreadStopReason<u32>>,
) -> Result<Option<DisconnectReason>, TargetError<<DebuggerTarget as Target>::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<u32>) {
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(()));
}
}

View file

@ -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<Option<SingleThreadStopReason<u32>>, <DebuggerTarget as Target>::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 <blocking>");
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()))
}
}

View file

@ -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<DebuggerRequestHandler, GBAError> {
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<dyn ConnectionExt<Error = std::io::Error>> = 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::<DebuggerEventLoop>(&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 {

View file

@ -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<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
pub(crate) stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
pub(crate) memory_map: String,
}
impl DebuggerTarget {
pub fn new(
tx: Sender<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, 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<SingleThreadStopReason<u32>> {
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<u32> {
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<Signal>) -> 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<Signal>) -> 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<bool, Self> {
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<bool, Self> {
self.tx
.send(DebuggerRequest::DelSwBreakpoint(addr))
.unwrap();
self.wait_for_operation();
self.debugger_request(DebuggerRequest::DelSwBreakpoint(addr));
Ok(true)
}
}

View file

@ -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;

View file

@ -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]

View file

@ -107,7 +107,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// normal_panic(panic_info);
// }));
if opts.skip_bios {
println!("Skipping bios animation..");
gba.skip_bios();