From c117cbe9241b8637e11254bce923545c77f44191 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Sun, 10 Nov 2019 20:00:07 +0200 Subject: [PATCH] Make the debugger work again, but currently breakpoints are not supported. Added memory write command and the ability to pause the debugger with Ctrl-C Former-commit-id: 83d141fa191dadefb84f7c9de163631a69af8324 --- Cargo.toml | 1 + src/core/arm7tdmi/cpu.rs | 4 +- src/core/gba.rs | 6 +- src/debugger/command.rs | 213 ++++++++++++++++++++++++++++++--------- src/debugger/mod.rs | 13 +++ 5 files changed, 186 insertions(+), 51 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9d5eb5..4024f7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ time = "0.1.42" bitfield = "0.13.1" bitflags = "1.1.0" zip = "0.5.3" +ctrlc = "3.1.3" [profile.dev] opt-level = 0 diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 4b91e73..aeeabde 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -46,7 +46,7 @@ pub struct Core { decoded_arm: u32, fetched_thumb: u16, decoded_thumb: u16, - last_executed: Option, + pub last_executed: Option, pub cycles: usize, @@ -325,10 +325,12 @@ impl Core { PipelineState::Refill1 => { self.pc = pc.wrapping_add(4); self.pipeline_state = PipelineState::Refill2; + self.last_executed = None; } PipelineState::Refill2 => { self.pc = pc.wrapping_add(4); self.pipeline_state = PipelineState::Execute; + self.last_executed = None; } PipelineState::Execute => { let insn = ArmInstruction::decode(insn, self.pc.wrapping_sub(8))?; diff --git a/src/core/gba.rs b/src/core/gba.rs index 12e4a52..02fde23 100644 --- a/src/core/gba.rs +++ b/src/core/gba.rs @@ -36,7 +36,6 @@ impl GameBoyAdvance { while self.sysbus.io.gpu.state != GpuState::VBlank { self.step_new(); } - self.backend.render(self.sysbus.io.gpu.get_framebuffer()); while self.sysbus.io.gpu.state == GpuState::VBlank { self.step_new(); } @@ -73,7 +72,10 @@ impl GameBoyAdvance { io.timers.step(cycles, &mut self.sysbus, &mut irqs); if let Some(new_gpu_state) = io.gpu.step(cycles, &mut self.sysbus, &mut irqs) { match new_gpu_state { - GpuState::VBlank => io.dmac.notify_vblank(), + GpuState::VBlank => { + self.backend.render(io.gpu.get_framebuffer()); + io.dmac.notify_vblank(); + } GpuState::HBlank => io.dmac.notify_hblank(), _ => {} } diff --git a/src/debugger/command.rs b/src/debugger/command.rs index f0fedb5..6a2c28e 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -1,3 +1,7 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time; + use crate::core::arm7tdmi::arm::ArmInstruction; use crate::core::arm7tdmi::bus::Bus; use crate::core::arm7tdmi::thumb::ThumbInstruction; @@ -20,6 +24,13 @@ pub enum DisassMode { ModeThumb, } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum MemWriteCommandSize { + Byte, + Half, + Word, +} + #[derive(Debug, PartialEq, Clone)] pub enum Command { Info, @@ -28,6 +39,7 @@ pub enum Command { Continue, Frame(usize), HexDump(Addr, u32), + MemWrite(MemWriteCommandSize, Addr, u32), Disass(DisassMode, Addr, u32), AddBreakpoint(Addr), DelBreakpoint(Addr), @@ -46,61 +58,101 @@ impl Command { Info => println!("{}", debugger.gba.cpu), DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ } Step(count) => { + debugger.ctrlc_flag.store(true, Ordering::SeqCst); for _ in 0..count { - match debugger.gba.step() { - Ok(insn) => { - print!( - "{}\t{}", - Colour::Black - .bold() - .italic() - .on(Colour::White) - .paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)), - insn - ); - println!( - "{}", - Colour::Purple.dimmed().italic().paint(format!( - "\t\t/// Next instruction at @0x{:08x}", - debugger.gba.cpu.get_next_pc() - )) - ) - } - Err(GBAError::CpuError(e)) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:x?}", debugger.gba.cpu) - } - _ => unreachable!(), + if !debugger.ctrlc_flag.load(Ordering::SeqCst) { + break; } + debugger.gba.step_new(); + while debugger.gba.cpu.last_executed.is_none() { + debugger.gba.step_new(); + } + let last_executed = debugger.gba.cpu.last_executed.unwrap(); + print!( + "{}\t{}", + Colour::Black + .bold() + .italic() + .on(Colour::White) + .paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)), + last_executed + ); + println!( + "{}", + Colour::Purple.dimmed().italic().paint(format!( + "\t\t/// Next instruction at @0x{:08x}", + debugger.gba.cpu.get_next_pc() + )) + ); } println!("{}\n", debugger.gba.cpu); + // match debugger.gba.step() { + // Ok(insn) => { + // print!( + // "{}\t{}", + // Colour::Black + // .bold() + // .italic() + // .on(Colour::White) + // .paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)), + // insn + // ); + // println!( + // "{}", + // Colour::Purple.dimmed().italic().paint(format!( + // "\t\t/// Next instruction at @0x{:08x}", + // debugger.gba.cpu.get_next_pc() + // )) + // ) + // } + // Err(GBAError::CpuError(e)) => { + // println!("{}: {}", "cpu encountered an error".red(), e); + // println!("cpu: {:x?}", debugger.gba.cpu) + // } + // _ => unreachable!(), + // } } Continue => { - let start_cycles = debugger.gba.cpu.cycles(); - loop { - if let Some(bp) = debugger.check_breakpoint() { - match debugger.gba.step() { - Err(GBAError::CpuError(e)) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:x?}", debugger.gba.cpu); - break; - } - _ => (), - }; - let num_cycles = debugger.gba.cpu.cycles() - start_cycles; - println!("hit breakpoint #0x{:08x} after {} cycles !", bp, num_cycles); - break; - } else { - match debugger.gba.step() { - Err(GBAError::CpuError(e)) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:x?}", debugger.gba.cpu); - break; - } - _ => (), - }; - } + let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60); + debugger.ctrlc_flag.store(true, Ordering::SeqCst); + while debugger.ctrlc_flag.load(Ordering::SeqCst) { + let start_time = time::Instant::now(); + debugger.gba.frame(); + + let time_passed = start_time.elapsed(); + let delay = frame_time.checked_sub(time_passed); + match delay { + None => {} + Some(delay) => { + ::std::thread::sleep(delay); + } + }; } + // let start_cycles = debugger.gba.cpu.cycles(); + // loop { + // if let Some(bp) = debugger.check_breakpoint() { + // match debugger.gba.step() { + // Err(GBAError::CpuError(e)) => { + // println!("{}: {}", "cpu encountered an error".red(), e); + // println!("cpu: {:x?}", debugger.gba.cpu); + // break; + // } + // _ => (), + // }; + // let num_cycles = debugger.gba.cpu.cycles() - start_cycles; + // println!("hit breakpoint #0x{:08x} after {} cycles !", bp, num_cycles); + // break; + // } else { + // match debugger.gba.step() { + // Err(GBAError::CpuError(e)) => { + // println!("{}: {}", "cpu encountered an error".red(), e); + // println!("cpu: {:x?}", debugger.gba.cpu); + // break; + // } + // _ => (), + // }; + // } + // } } Frame(count) => { use super::time::PreciseTime; @@ -115,6 +167,11 @@ impl Command { let bytes = debugger.gba.sysbus.get_bytes(addr..addr + nbytes); hexdump::hexdump(&bytes); } + MemWrite(size, addr, val) => match size { + MemWriteCommandSize::Byte => debugger.gba.sysbus.write_8(addr, val as u8), + MemWriteCommandSize::Half => debugger.gba.sysbus.write_16(addr, val as u16), + MemWriteCommandSize::Word => debugger.gba.sysbus.write_32(addr, val as u32), + }, Disass(mode, addr, n) => { let bytes = debugger.gba.sysbus.get_bytes(addr..addr + n); match mode { @@ -257,6 +314,66 @@ impl Debugger { }; Ok(Command::HexDump(addr, n)) } + "mwb" => { + let (addr, val) = match args.len() { + 2 => { + let addr = self.val_address(&args[0])?; + let val = self.val_number(&args[1])? as u8; + + (addr, val) + } + _ => { + return Err(DebuggerError::InvalidCommandFormat( + "mwb [addr] [n]".to_string(), + )) + } + }; + Ok(Command::MemWrite( + MemWriteCommandSize::Byte, + addr, + val as u32, + )) + } + "mwh" => { + let (addr, val) = match args.len() { + 2 => { + let addr = self.val_address(&args[0])?; + let val = self.val_number(&args[1])? as u16; + + (addr, val) + } + _ => { + return Err(DebuggerError::InvalidCommandFormat( + "mwb [addr] [n]".to_string(), + )) + } + }; + Ok(Command::MemWrite( + MemWriteCommandSize::Half, + addr, + val as u32, + )) + } + "mww" => { + let (addr, val) = match args.len() { + 2 => { + let addr = self.val_address(&args[0])?; + let val = self.val_number(&args[1])? as u32; + + (addr, val) + } + _ => { + return Err(DebuggerError::InvalidCommandFormat( + "mwb [addr] [n]".to_string(), + )) + } + }; + Ok(Command::MemWrite( + MemWriteCommandSize::Half, + addr, + val as u32, + )) + } "d" | "disass" => { let (addr, n) = self.get_disassembler_args(args)?; diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index d073a87..008f97e 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -1,3 +1,7 @@ +extern crate ctrlc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + use rustyline::error::ReadlineError; use rustyline::Editor; @@ -36,14 +40,23 @@ type DebuggerResult = Result; pub struct Debugger { pub gba: GameBoyAdvance, running: bool, + pub ctrlc_flag: Arc, pub previous_command: Option, } impl Debugger { pub fn new(gba: GameBoyAdvance) -> Debugger { + let ctrlc_flag = Arc::new(AtomicBool::new(true)); + let r = ctrlc_flag.clone(); + ctrlc::set_handler(move || { + println!("Stopping, Ctrl-C detected!"); + r.store(false, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); Debugger { gba: gba, running: false, + ctrlc_flag: ctrlc_flag, previous_command: None, } }