diff --git a/arm7tdmi/src/gdb/mod.rs b/arm7tdmi/src/gdb/mod.rs index 8a7ec01b..fd0743d3 100644 --- a/arm7tdmi/src/gdb/mod.rs +++ b/arm7tdmi/src/gdb/mod.rs @@ -13,9 +13,10 @@ pub extern crate gdbstub_arch; /// Wait for tcp connection on port pub fn wait_for_connection(port: u16) -> io::Result { let bind_addr = format!("0.0.0.0:{port}"); + info!("waiting for connection on {:?}", bind_addr); + let sock = TcpListener::bind(bind_addr)?; - info!("waiting for connection"); // Blocks until a GDB client connects via TCP. // i.e: Running `target remote localhost:` from the GDB prompt. let (stream, addr) = sock.accept()?; diff --git a/arm7tdmi/src/gdb/target.rs b/arm7tdmi/src/gdb/target.rs index b36e294d..0b0885cc 100644 --- a/arm7tdmi/src/gdb/target.rs +++ b/arm7tdmi/src/gdb/target.rs @@ -41,7 +41,7 @@ impl SingleThreadBase for Arm7tdmiCore { &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, ) -> TargetResult<(), Self> { - regs.pc = self.pc; + regs.pc = self.get_next_pc(); regs.lr = self.get_reg(REG_LR); regs.sp = self.get_reg(REG_SP); regs.r[..].copy_from_slice(&self.gpr[..13]); diff --git a/core/src/gdb_support.rs b/core/src/gdb_support.rs index eaa90aec..7f117bbd 100644 --- a/core/src/gdb_support.rs +++ b/core/src/gdb_support.rs @@ -32,6 +32,7 @@ pub(crate) enum DebuggerRequest { Interrupt, Resume, SingleStep, + Reset, Disconnected(DisconnectReason), } @@ -40,7 +41,7 @@ pub(crate) struct DebuggerRequestHandler { request_complete_signal: Arc<(Mutex, Condvar)>, stop_signal: Arc<(Mutex>>, Condvar)>, thread: JoinHandle<()>, - stopped: bool, + pub(crate) stopped: bool, } impl DebuggerRequestHandler { @@ -123,6 +124,12 @@ impl DebuggerRequestHandler { self.stopped = false; self.complete_request(None) } + Reset => { + debug!("Sending reset interrupt to gba"); + self.stopped = true; + gba.cpu.reset(); + self.complete_request(Some(SingleThreadStopReason::Signal(Signal::SIGTRAP))) + } SingleStep => { debug!("Debugger requested single step"); self.stopped = true; @@ -153,6 +160,10 @@ impl DebuggerRequestHandler { let mut finished = lock.lock().unwrap(); *finished = true; cvar.notify_one(); + // wait for the ack + while *finished { + finished = cvar.wait(finished).unwrap(); + } } fn complete_request( diff --git a/core/src/gdb_support/gdb_thread.rs b/core/src/gdb_support/gdb_thread.rs index c830bd77..61ff52ec 100644 --- a/core/src/gdb_support/gdb_thread.rs +++ b/core/src/gdb_support/gdb_thread.rs @@ -3,9 +3,8 @@ use std::sync::{Arc, Condvar, Mutex}; use arm7tdmi::{ gdb::wait_for_connection, gdbstub::{ - common::Signal, conn::ConnectionExt, - stub::{GdbStub, SingleThreadStopReason}, + stub::GdbStub, }, }; @@ -41,14 +40,12 @@ pub(crate) fn start_gdb_server_thread( target.disconnect(disconnect_reason); }); - let mut debugger = DebuggerRequestHandler { + let debugger = DebuggerRequestHandler { rx, request_complete_signal, stop_signal, thread, stopped: true, }; - debugger.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT)); - Ok(debugger) } diff --git a/core/src/gdb_support/target.rs b/core/src/gdb_support/target.rs index b8da3276..251f4b78 100644 --- a/core/src/gdb_support/target.rs +++ b/core/src/gdb_support/target.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Condvar, Mutex, WaitTimeoutResult}; use std::time::Duration; /// Implementing the Target trait for gdbstub @@ -12,6 +12,7 @@ use gdbstub::target::ext::base::singlethread::{ use gdbstub::target::ext::base::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps}; use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::breakpoints::BreakpointsOps; +use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput}; use gdbstub::target::{self, Target, TargetError, TargetResult}; use gdbstub_arch::arm::reg::ArmCoreRegs; @@ -42,16 +43,15 @@ impl DebuggerTarget { 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(); } + // ack the other side we got the signal *finished = false; + cvar.notify_one(); } pub fn wait_for_stop_reason_timeout( @@ -60,24 +60,22 @@ impl DebuggerTarget { ) -> 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")) + let mut timeout_result: WaitTimeoutResult; + while stop_reason.is_none() { + (stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap(); + if timeout_result.timed_out() { + return None; + } } + 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; + while stop_reason.is_none() { + stop_reason = cvar.wait(stop_reason).unwrap(); } - let mut stop_reason = cvar.wait(stop_reason).unwrap(); stop_reason.take().expect("None is not expected here") } @@ -106,6 +104,10 @@ impl Target for DebuggerTarget { fn support_memory_map(&mut self) -> Option> { Some(self) } + + fn support_monitor_cmd(&mut self) -> Option> { + Some(self) + } } impl SingleThreadBase for DebuggerTarget { @@ -208,3 +210,31 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget { Ok(true) } } + +impl target::ext::monitor_cmd::MonitorCmd for DebuggerTarget { + fn handle_monitor_cmd( + &mut self, + cmd: &[u8], + mut out: ConsoleOutput<'_>, + ) -> Result<(), Self::Error> { + let cmd = match std::str::from_utf8(cmd) { + Ok(cmd) => cmd, + Err(_) => { + outputln!(out, "command must be valid UTF-8"); + return Ok(()); + } + }; + + match cmd { + "reset" => { + self.debugger_request(DebuggerRequest::Reset); + outputln!(out, "sent reset signal"); + } + unk => { + outputln!(out, "unknown command: {}", unk); + } + } + + Ok(()) + } +} diff --git a/platform/rustboyadvance-sdl2/src/main.rs b/platform/rustboyadvance-sdl2/src/main.rs index 01d5f7f9..88291c21 100644 --- a/platform/rustboyadvance-sdl2/src/main.rs +++ b/platform/rustboyadvance-sdl2/src/main.rs @@ -27,7 +27,6 @@ use rustboyadvance_core::prelude::*; use rustboyadvance_utils::FpsCounter; const LOG_DIR: &str = ".logs"; -const DEFAULT_GDB_SERVER_PORT: u16 = 1337; fn ask_download_bios() { const OPEN_SOURCE_BIOS_URL: &'static str = @@ -113,7 +112,7 @@ fn main() -> Result<(), Box> { } if opts.gdbserver { - gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT); + gba.start_gdbserver(opts.gdbserver_port); } let mut vsync = true; @@ -144,7 +143,7 @@ fn main() -> Result<(), Box> { .unwrap(); info!("ending debugger...") } - Scancode::F2 => gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT), + Scancode::F2 => gba.start_gdbserver(opts.gdbserver_port), Scancode::F5 => { info!("Saving state ..."); let save = gba.save_state()?; @@ -159,10 +158,16 @@ fn main() -> Result<(), Box> { if opts.savestate_path().is_file() { let save = read_bin_file(&opts.savestate_path())?; info!("Restoring state from {:?}...", opts.savestate_path()); - let (audio_interface, _sdl_audio_device_new) = audio::create_audio_player(&sdl_context)?; + let (audio_interface, _sdl_audio_device_new) = + audio::create_audio_player(&sdl_context)?; _sdl_audio_device = _sdl_audio_device_new; let rom = opts.read_rom()?.into_boxed_slice(); - gba = Box::new(GameBoyAdvance::from_saved_state(&save, bios_bin.clone(), rom, audio_interface)?); + gba = Box::new(GameBoyAdvance::from_saved_state( + &save, + bios_bin.clone(), + rom, + audio_interface, + )?); info!("Restored!"); } else { info!("Savestate not created, please create one by pressing F5"); diff --git a/platform/rustboyadvance-sdl2/src/options.rs b/platform/rustboyadvance-sdl2/src/options.rs index 9873e08c..eca65d79 100644 --- a/platform/rustboyadvance-sdl2/src/options.rs +++ b/platform/rustboyadvance-sdl2/src/options.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, io}; +use std::{io, path::PathBuf}; use rustboyadvance_core::{ cartridge::{BackupType, GamepakBuilder}, @@ -33,6 +33,9 @@ pub struct Options { #[structopt(short = "d", long)] pub gdbserver: bool, + #[structopt(long = "port", default_value = "1337")] + pub gdbserver_port: u16, + /// Force emulation of RTC, use for games that have RTC but the emulator fails to detect #[structopt(long)] pub rtc: bool,