fixes & improvements
Former-commit-id: 1ddeb07bde78a676201037c0bb269ff8122d29c2 Former-commit-id: f9c0eaa2a1073dba82dfb4d3594fbc0b979ef2e0
This commit is contained in:
parent
838ca43ac4
commit
e4e2c710be
|
@ -13,9 +13,10 @@ pub extern crate gdbstub_arch;
|
||||||
/// Wait for tcp connection on port
|
/// Wait for tcp connection on port
|
||||||
pub fn wait_for_connection(port: u16) -> io::Result<TcpStream> {
|
pub fn wait_for_connection(port: u16) -> io::Result<TcpStream> {
|
||||||
let bind_addr = format!("0.0.0.0:{port}");
|
let bind_addr = format!("0.0.0.0:{port}");
|
||||||
|
info!("waiting for connection on {:?}", bind_addr);
|
||||||
|
|
||||||
let sock = TcpListener::bind(bind_addr)?;
|
let sock = TcpListener::bind(bind_addr)?;
|
||||||
|
|
||||||
info!("waiting for connection");
|
|
||||||
// Blocks until a GDB client connects via TCP.
|
// Blocks until a GDB client connects via TCP.
|
||||||
// i.e: Running `target remote localhost:<port>` from the GDB prompt.
|
// i.e: Running `target remote localhost:<port>` from the GDB prompt.
|
||||||
let (stream, addr) = sock.accept()?;
|
let (stream, addr) = sock.accept()?;
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl<I: MemoryGdbInterface> SingleThreadBase for Arm7tdmiCore<I> {
|
||||||
&mut self,
|
&mut self,
|
||||||
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
|
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
|
||||||
) -> TargetResult<(), Self> {
|
) -> TargetResult<(), Self> {
|
||||||
regs.pc = self.pc;
|
regs.pc = self.get_next_pc();
|
||||||
regs.lr = self.get_reg(REG_LR);
|
regs.lr = self.get_reg(REG_LR);
|
||||||
regs.sp = self.get_reg(REG_SP);
|
regs.sp = self.get_reg(REG_SP);
|
||||||
regs.r[..].copy_from_slice(&self.gpr[..13]);
|
regs.r[..].copy_from_slice(&self.gpr[..13]);
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub(crate) enum DebuggerRequest {
|
||||||
Interrupt,
|
Interrupt,
|
||||||
Resume,
|
Resume,
|
||||||
SingleStep,
|
SingleStep,
|
||||||
|
Reset,
|
||||||
Disconnected(DisconnectReason),
|
Disconnected(DisconnectReason),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ pub(crate) struct DebuggerRequestHandler {
|
||||||
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
|
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
|
||||||
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
|
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
|
||||||
thread: JoinHandle<()>,
|
thread: JoinHandle<()>,
|
||||||
stopped: bool,
|
pub(crate) stopped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebuggerRequestHandler {
|
impl DebuggerRequestHandler {
|
||||||
|
@ -123,6 +124,12 @@ impl DebuggerRequestHandler {
|
||||||
self.stopped = false;
|
self.stopped = false;
|
||||||
self.complete_request(None)
|
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 => {
|
SingleStep => {
|
||||||
debug!("Debugger requested single step");
|
debug!("Debugger requested single step");
|
||||||
self.stopped = true;
|
self.stopped = true;
|
||||||
|
@ -153,6 +160,10 @@ impl DebuggerRequestHandler {
|
||||||
let mut finished = lock.lock().unwrap();
|
let mut finished = lock.lock().unwrap();
|
||||||
*finished = true;
|
*finished = true;
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
|
// wait for the ack
|
||||||
|
while *finished {
|
||||||
|
finished = cvar.wait(finished).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_request(
|
fn complete_request(
|
||||||
|
|
|
@ -3,9 +3,8 @@ use std::sync::{Arc, Condvar, Mutex};
|
||||||
use arm7tdmi::{
|
use arm7tdmi::{
|
||||||
gdb::wait_for_connection,
|
gdb::wait_for_connection,
|
||||||
gdbstub::{
|
gdbstub::{
|
||||||
common::Signal,
|
|
||||||
conn::ConnectionExt,
|
conn::ConnectionExt,
|
||||||
stub::{GdbStub, SingleThreadStopReason},
|
stub::GdbStub,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,14 +40,12 @@ pub(crate) fn start_gdb_server_thread(
|
||||||
target.disconnect(disconnect_reason);
|
target.disconnect(disconnect_reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut debugger = DebuggerRequestHandler {
|
let debugger = DebuggerRequestHandler {
|
||||||
rx,
|
rx,
|
||||||
request_complete_signal,
|
request_complete_signal,
|
||||||
stop_signal,
|
stop_signal,
|
||||||
thread,
|
thread,
|
||||||
stopped: true,
|
stopped: true,
|
||||||
};
|
};
|
||||||
debugger.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT));
|
|
||||||
|
|
||||||
Ok(debugger)
|
Ok(debugger)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::sync::{Arc, Condvar, Mutex, WaitTimeoutResult};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// Implementing the Target trait for gdbstub
|
/// 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::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps};
|
||||||
use gdbstub::target::ext::base::BaseOps;
|
use gdbstub::target::ext::base::BaseOps;
|
||||||
use gdbstub::target::ext::breakpoints::BreakpointsOps;
|
use gdbstub::target::ext::breakpoints::BreakpointsOps;
|
||||||
|
use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput};
|
||||||
use gdbstub::target::{self, Target, TargetError, TargetResult};
|
use gdbstub::target::{self, Target, TargetError, TargetResult};
|
||||||
use gdbstub_arch::arm::reg::ArmCoreRegs;
|
use gdbstub_arch::arm::reg::ArmCoreRegs;
|
||||||
|
|
||||||
|
@ -42,16 +43,15 @@ impl DebuggerTarget {
|
||||||
pub fn debugger_request(&mut self, req: DebuggerRequest) {
|
pub fn debugger_request(&mut self, req: DebuggerRequest) {
|
||||||
let (lock, cvar) = &*self.request_complete_signal;
|
let (lock, cvar) = &*self.request_complete_signal;
|
||||||
let mut finished = lock.lock().unwrap();
|
let mut finished = lock.lock().unwrap();
|
||||||
*finished = false;
|
|
||||||
|
|
||||||
// now send the request
|
// now send the request
|
||||||
self.tx.send(req).unwrap();
|
self.tx.send(req).unwrap();
|
||||||
|
|
||||||
// wait for the notification
|
// wait for the notification
|
||||||
while !*finished {
|
while !*finished {
|
||||||
finished = cvar.wait(finished).unwrap();
|
finished = cvar.wait(finished).unwrap();
|
||||||
}
|
}
|
||||||
|
// ack the other side we got the signal
|
||||||
*finished = false;
|
*finished = false;
|
||||||
|
cvar.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for_stop_reason_timeout(
|
pub fn wait_for_stop_reason_timeout(
|
||||||
|
@ -60,24 +60,22 @@ impl DebuggerTarget {
|
||||||
) -> Option<SingleThreadStopReason<u32>> {
|
) -> Option<SingleThreadStopReason<u32>> {
|
||||||
let (lock, cvar) = &*self.stop_signal;
|
let (lock, cvar) = &*self.stop_signal;
|
||||||
let mut stop_reason = lock.lock().unwrap();
|
let mut stop_reason = lock.lock().unwrap();
|
||||||
if let Some(stop_reason) = stop_reason.take() {
|
let mut timeout_result: WaitTimeoutResult;
|
||||||
return Some(stop_reason);
|
while stop_reason.is_none() {
|
||||||
}
|
(stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
|
||||||
let (mut stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
|
|
||||||
if timeout_result.timed_out() {
|
if timeout_result.timed_out() {
|
||||||
None
|
return None;
|
||||||
} else {
|
|
||||||
Some(stop_reason.take().expect("None is not expected here"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(stop_reason.take().expect("None is not expected here"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason<u32> {
|
pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason<u32> {
|
||||||
let (lock, cvar) = &*self.stop_signal;
|
let (lock, cvar) = &*self.stop_signal;
|
||||||
let mut stop_reason = lock.lock().unwrap();
|
let mut stop_reason = lock.lock().unwrap();
|
||||||
if let Some(stop_reason) = stop_reason.take() {
|
while stop_reason.is_none() {
|
||||||
return stop_reason;
|
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")
|
stop_reason.take().expect("None is not expected here")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +104,10 @@ impl Target for DebuggerTarget {
|
||||||
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
|
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleThreadBase for DebuggerTarget {
|
impl SingleThreadBase for DebuggerTarget {
|
||||||
|
@ -208,3 +210,31 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
|
||||||
Ok(true)
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ use rustboyadvance_core::prelude::*;
|
||||||
use rustboyadvance_utils::FpsCounter;
|
use rustboyadvance_utils::FpsCounter;
|
||||||
|
|
||||||
const LOG_DIR: &str = ".logs";
|
const LOG_DIR: &str = ".logs";
|
||||||
const DEFAULT_GDB_SERVER_PORT: u16 = 1337;
|
|
||||||
|
|
||||||
fn ask_download_bios() {
|
fn ask_download_bios() {
|
||||||
const OPEN_SOURCE_BIOS_URL: &'static str =
|
const OPEN_SOURCE_BIOS_URL: &'static str =
|
||||||
|
@ -113,7 +112,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.gdbserver {
|
if opts.gdbserver {
|
||||||
gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT);
|
gba.start_gdbserver(opts.gdbserver_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vsync = true;
|
let mut vsync = true;
|
||||||
|
@ -144,7 +143,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
info!("ending debugger...")
|
info!("ending debugger...")
|
||||||
}
|
}
|
||||||
Scancode::F2 => gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT),
|
Scancode::F2 => gba.start_gdbserver(opts.gdbserver_port),
|
||||||
Scancode::F5 => {
|
Scancode::F5 => {
|
||||||
info!("Saving state ...");
|
info!("Saving state ...");
|
||||||
let save = gba.save_state()?;
|
let save = gba.save_state()?;
|
||||||
|
@ -159,10 +158,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if opts.savestate_path().is_file() {
|
if opts.savestate_path().is_file() {
|
||||||
let save = read_bin_file(&opts.savestate_path())?;
|
let save = read_bin_file(&opts.savestate_path())?;
|
||||||
info!("Restoring state from {:?}...", 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;
|
_sdl_audio_device = _sdl_audio_device_new;
|
||||||
let rom = opts.read_rom()?.into_boxed_slice();
|
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!");
|
info!("Restored!");
|
||||||
} else {
|
} else {
|
||||||
info!("Savestate not created, please create one by pressing F5");
|
info!("Savestate not created, please create one by pressing F5");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{path::PathBuf, io};
|
use std::{io, path::PathBuf};
|
||||||
|
|
||||||
use rustboyadvance_core::{
|
use rustboyadvance_core::{
|
||||||
cartridge::{BackupType, GamepakBuilder},
|
cartridge::{BackupType, GamepakBuilder},
|
||||||
|
@ -33,6 +33,9 @@ pub struct Options {
|
||||||
#[structopt(short = "d", long)]
|
#[structopt(short = "d", long)]
|
||||||
pub gdbserver: bool,
|
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
|
/// Force emulation of RTC, use for games that have RTC but the emulator fails to detect
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
pub rtc: bool,
|
pub rtc: bool,
|
||||||
|
|
Reference in a new issue