Improve the debugger
- Add tracing of opcodes and potentially more stuff - Add option to run a script file at the beginnig (I use it to redirect traces to a file) - Support breakpoints again Former-commit-id: 4e988d6bc1a59456c96547f0320a6d9abedcae00
This commit is contained in:
parent
7e98af80c2
commit
b288625b9a
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -190,6 +190,15 @@ dependencies = [
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
@ -658,6 +667,7 @@ dependencies = [
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"minifb 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"minifb 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -970,6 +980,7 @@ dependencies = [
|
||||||
"checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03"
|
"checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03"
|
||||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||||
|
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
||||||
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||||
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
||||||
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
||||||
|
|
|
@ -28,3 +28,11 @@ args:
|
||||||
- debug:
|
- debug:
|
||||||
long: debug
|
long: debug
|
||||||
help: Start with the debugger attached
|
help: Start with the debugger attached
|
||||||
|
- script_file:
|
||||||
|
long: script-file
|
||||||
|
short: f
|
||||||
|
takes_value: true
|
||||||
|
help: Text file with debugger commands to run
|
||||||
|
required: false
|
||||||
|
requires:
|
||||||
|
debug
|
|
@ -69,7 +69,7 @@ fn run_emulator(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
gba.cpu.set_verbose(true);
|
gba.cpu.set_verbose(true);
|
||||||
let mut debugger = Debugger::new(gba);
|
let mut debugger = Debugger::new(gba);
|
||||||
println!("starting debugger...");
|
println!("starting debugger...");
|
||||||
debugger.repl()?;
|
debugger.repl(matches.value_of("script_file"))?;
|
||||||
println!("ending debugger...");
|
println!("ending debugger...");
|
||||||
} else {
|
} else {
|
||||||
let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60);
|
let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60);
|
||||||
|
|
|
@ -381,6 +381,22 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trace_opcode(&self, insn: u32) {
|
||||||
|
if self.trace_opcodes && self.pipeline_state == PipelineState::Execute {
|
||||||
|
print!("[{:08X}] PC=0x{:08x} | ", insn, self.pc);
|
||||||
|
for r in 0..15 {
|
||||||
|
print!("R{}=0x{:08x} ", r, self.gpr[r]);
|
||||||
|
}
|
||||||
|
print!(
|
||||||
|
" N={} Z={} C={} V={} T={}\n",
|
||||||
|
self.cpsr.N() as u8,
|
||||||
|
self.cpsr.Z() as u8,
|
||||||
|
self.cpsr.C() as u8,
|
||||||
|
self.cpsr.V() as u8,
|
||||||
|
self.cpsr.state() as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Perform a pipeline step
|
/// Perform a pipeline step
|
||||||
/// If an instruction was executed in this step, return it.
|
/// If an instruction was executed in this step, return it.
|
||||||
pub fn step(&mut self, bus: &mut SysBus) -> CpuResult<()> {
|
pub fn step(&mut self, bus: &mut SysBus) -> CpuResult<()> {
|
||||||
|
|
|
@ -40,10 +40,31 @@ impl GameBoyAdvance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_key_state(&mut self) {
|
pub fn update_key_state(&mut self) {
|
||||||
self.sysbus.io.keyinput = self.backend.get_key_state();
|
self.sysbus.io.keyinput = self.backend.get_key_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_breakpoint(&mut self, addr: u32) -> Option<usize> {
|
||||||
|
if !self.cpu.breakpoints.contains(&addr) {
|
||||||
|
let new_index = self.cpu.breakpoints.len();
|
||||||
|
self.cpu.breakpoints.push(addr);
|
||||||
|
Some(new_index)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_breakpoint(&self) -> Option<u32> {
|
||||||
|
let next_pc = self.cpu.get_next_pc();
|
||||||
|
for bp in &self.cpu.breakpoints {
|
||||||
|
if *bp == next_pc {
|
||||||
|
return Some(next_pc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn step_new(&mut self) {
|
pub fn step_new(&mut self) {
|
||||||
let mut irqs = IrqBitmask(0);
|
let mut irqs = IrqBitmask(0);
|
||||||
let previous_cycles = self.cpu.cycles;
|
let previous_cycles = self.cpu.cycles;
|
||||||
|
|
|
@ -31,10 +31,19 @@ pub enum MemWriteCommandSize {
|
||||||
Word,
|
Word,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct TraceFlags: u32 {
|
||||||
|
const TRACE_SYSBUS = 0b00000001;
|
||||||
|
const TRACE_OPCODE = 0b00000010;
|
||||||
|
const TRACE_DMA = 0b00000100;
|
||||||
|
const TRACE_TIMERS = 0b000001000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Info,
|
Info,
|
||||||
DisplayInfo,
|
GpuInfo,
|
||||||
Step(usize),
|
Step(usize),
|
||||||
Continue,
|
Continue,
|
||||||
Frame(usize),
|
Frame(usize),
|
||||||
|
@ -49,25 +58,31 @@ pub enum Command {
|
||||||
ListBreakpoints,
|
ListBreakpoints,
|
||||||
Reset,
|
Reset,
|
||||||
Quit,
|
Quit,
|
||||||
|
TraceToggle(TraceFlags),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Debugger {
|
||||||
pub fn run(&self, debugger: &mut Debugger) {
|
pub fn run_command(&mut self, command: Command) {
|
||||||
use Command::*;
|
use Command::*;
|
||||||
match *self {
|
match command {
|
||||||
Info => println!("{}", debugger.gba.cpu),
|
Info => {
|
||||||
DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ }
|
println!("{}", self.gba.cpu);
|
||||||
|
println!("IME={}", self.gba.sysbus.io.intc.interrupt_master_enable);
|
||||||
|
println!("IE={:#?}", self.gba.sysbus.io.intc.interrupt_enable);
|
||||||
|
println!("IF={:#?}", self.gba.sysbus.io.intc.interrupt_flags);
|
||||||
|
}
|
||||||
|
GpuInfo => println!("GPU: {:#?}", self.gba.sysbus.io.gpu),
|
||||||
Step(count) => {
|
Step(count) => {
|
||||||
debugger.ctrlc_flag.store(true, Ordering::SeqCst);
|
self.ctrlc_flag.store(true, Ordering::SeqCst);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
if !debugger.ctrlc_flag.load(Ordering::SeqCst) {
|
if !self.ctrlc_flag.load(Ordering::SeqCst) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
debugger.gba.step_new();
|
self.gba.step_new();
|
||||||
while debugger.gba.cpu.last_executed.is_none() {
|
while self.gba.cpu.last_executed.is_none() {
|
||||||
debugger.gba.step_new();
|
self.gba.step_new();
|
||||||
}
|
}
|
||||||
let last_executed = debugger.gba.cpu.last_executed.unwrap();
|
let last_executed = self.gba.cpu.last_executed.unwrap();
|
||||||
print!(
|
print!(
|
||||||
"{}\t{}",
|
"{}\t{}",
|
||||||
Colour::Black
|
Colour::Black
|
||||||
|
@ -81,99 +96,48 @@ impl Command {
|
||||||
"{}",
|
"{}",
|
||||||
Colour::Purple.dimmed().italic().paint(format!(
|
Colour::Purple.dimmed().italic().paint(format!(
|
||||||
"\t\t/// Next instruction at @0x{:08x}",
|
"\t\t/// Next instruction at @0x{:08x}",
|
||||||
debugger.gba.cpu.get_next_pc()
|
self.gba.cpu.get_next_pc()
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
println!("{}\n", debugger.gba.cpu);
|
println!("{}\n", self.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 => {
|
Continue => {
|
||||||
let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60);
|
self.ctrlc_flag.store(true, Ordering::SeqCst);
|
||||||
debugger.ctrlc_flag.store(true, Ordering::SeqCst);
|
while self.ctrlc_flag.load(Ordering::SeqCst) {
|
||||||
while debugger.ctrlc_flag.load(Ordering::SeqCst) {
|
|
||||||
let start_time = time::Instant::now();
|
let start_time = time::Instant::now();
|
||||||
debugger.gba.frame();
|
self.gba.update_key_state();
|
||||||
|
match self.gba.check_breakpoint() {
|
||||||
let time_passed = start_time.elapsed();
|
Some(addr) => {
|
||||||
let delay = frame_time.checked_sub(time_passed);
|
println!("Breakpoint reached! @{:x}", addr);
|
||||||
match delay {
|
break;
|
||||||
None => {}
|
|
||||||
Some(delay) => {
|
|
||||||
::std::thread::sleep(delay);
|
|
||||||
}
|
}
|
||||||
};
|
_ => {
|
||||||
|
self.gba.step_new();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 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) => {
|
Frame(count) => {
|
||||||
use super::time::PreciseTime;
|
use super::time::PreciseTime;
|
||||||
let start = PreciseTime::now();
|
let start = PreciseTime::now();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
debugger.gba.frame();
|
self.gba.frame();
|
||||||
}
|
}
|
||||||
let end = PreciseTime::now();
|
let end = PreciseTime::now();
|
||||||
println!("that took {} seconds", start.to(end));
|
println!("that took {} seconds", start.to(end));
|
||||||
}
|
}
|
||||||
HexDump(addr, nbytes) => {
|
HexDump(addr, nbytes) => {
|
||||||
let bytes = debugger.gba.sysbus.get_bytes(addr..addr + nbytes);
|
let bytes = self.gba.sysbus.get_bytes(addr..addr + nbytes);
|
||||||
hexdump::hexdump(&bytes);
|
hexdump::hexdump(&bytes);
|
||||||
}
|
}
|
||||||
MemWrite(size, addr, val) => match size {
|
MemWrite(size, addr, val) => match size {
|
||||||
MemWriteCommandSize::Byte => debugger.gba.sysbus.write_8(addr, val as u8),
|
MemWriteCommandSize::Byte => self.gba.sysbus.write_8(addr, val as u8),
|
||||||
MemWriteCommandSize::Half => debugger.gba.sysbus.write_16(addr, val as u16),
|
MemWriteCommandSize::Half => self.gba.sysbus.write_16(addr, val as u16),
|
||||||
MemWriteCommandSize::Word => debugger.gba.sysbus.write_32(addr, val as u32),
|
MemWriteCommandSize::Word => self.gba.sysbus.write_32(addr, val as u32),
|
||||||
},
|
},
|
||||||
Disass(mode, addr, n) => {
|
Disass(mode, addr, n) => {
|
||||||
let bytes = debugger.gba.sysbus.get_bytes(addr..addr + n);
|
let bytes = self.gba.sysbus.get_bytes(addr..addr + n);
|
||||||
match mode {
|
match mode {
|
||||||
DisassMode::ModeArm => {
|
DisassMode::ModeArm => {
|
||||||
let disass = Disassembler::<ArmInstruction>::new(addr, &bytes);
|
let disass = Disassembler::<ArmInstruction>::new(addr, &bytes);
|
||||||
|
@ -191,37 +155,60 @@ impl Command {
|
||||||
}
|
}
|
||||||
Quit => {
|
Quit => {
|
||||||
print!("Quitting!");
|
print!("Quitting!");
|
||||||
debugger.stop();
|
self.stop();
|
||||||
}
|
}
|
||||||
AddBreakpoint(addr) => {
|
AddBreakpoint(addr) => match self.gba.add_breakpoint(addr) {
|
||||||
if !debugger.gba.cpu.breakpoints.contains(&addr) {
|
Some(index) => println!("Added breakpoint [{}] 0x{:08x}", index, addr),
|
||||||
let new_index = debugger.gba.cpu.breakpoints.len();
|
None => println!("Breakpint already exists."),
|
||||||
debugger.gba.cpu.breakpoints.push(addr);
|
},
|
||||||
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
|
DelBreakpoint(addr) => self.delete_breakpoint(addr),
|
||||||
} else {
|
ClearBreakpoints => self.gba.cpu.breakpoints.clear(),
|
||||||
println!("breakpoint already exists!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelBreakpoint(addr) => debugger.delete_breakpoint(addr),
|
|
||||||
ClearBreakpoints => debugger.gba.cpu.breakpoints.clear(),
|
|
||||||
ListBreakpoints => {
|
ListBreakpoints => {
|
||||||
println!("breakpoint list:");
|
println!("breakpoint list:");
|
||||||
for (i, b) in debugger.gba.cpu.breakpoints.iter().enumerate() {
|
for (i, b) in self.gba.cpu.breakpoints.iter().enumerate() {
|
||||||
println!("[{}] 0x{:08x}", i, b)
|
println!("[{}] 0x{:08x}", i, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaletteView => create_palette_view(&debugger.gba.sysbus.palette_ram.mem),
|
PaletteView => create_palette_view(&self.gba.sysbus.palette_ram.mem),
|
||||||
// TileView(bg) => create_tile_view(bg, &debugger.gba),
|
// TileView(bg) => create_tile_view(bg, &self.gba),
|
||||||
Reset => {
|
Reset => {
|
||||||
println!("resetting cpu...");
|
println!("resetting cpu...");
|
||||||
debugger.gba.cpu.reset(&mut debugger.gba.sysbus);
|
self.gba.cpu.reset(&mut self.gba.sysbus);
|
||||||
println!("cpu is restarted!")
|
println!("cpu is restarted!")
|
||||||
}
|
}
|
||||||
|
TraceToggle(flags) => {
|
||||||
|
if flags.contains(TraceFlags::TRACE_SYSBUS) {
|
||||||
|
self.gba.sysbus.trace_access = !self.gba.sysbus.trace_access;
|
||||||
|
println!(
|
||||||
|
"[*] sysbus tracing {}",
|
||||||
|
if self.gba.sysbus.trace_access {
|
||||||
|
"on"
|
||||||
|
} else {
|
||||||
|
"off"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if flags.contains(TraceFlags::TRACE_OPCODE) {
|
||||||
|
self.gba.cpu.trace_opcodes = !self.gba.cpu.trace_opcodes;
|
||||||
|
println!(
|
||||||
|
"[*] opcode tracing {}",
|
||||||
|
if self.gba.cpu.trace_opcodes {
|
||||||
|
"on"
|
||||||
|
} else {
|
||||||
|
"off"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if flags.contains(TraceFlags::TRACE_DMA) {
|
||||||
|
println!("[*] dma tracing not implemented");
|
||||||
|
}
|
||||||
|
if flags.contains(TraceFlags::TRACE_TIMERS) {
|
||||||
|
println!("[*] timers tracing not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Debugger {
|
|
||||||
fn get_disassembler_args(&self, args: Vec<Value>) -> DebuggerResult<(Addr, u32)> {
|
fn get_disassembler_args(&self, args: Vec<Value>) -> DebuggerResult<(Addr, u32)> {
|
||||||
match args.len() {
|
match args.len() {
|
||||||
2 => {
|
2 => {
|
||||||
|
@ -260,7 +247,7 @@ impl Debugger {
|
||||||
|
|
||||||
match command.as_ref() {
|
match command.as_ref() {
|
||||||
"i" | "info" => Ok(Command::Info),
|
"i" | "info" => Ok(Command::Info),
|
||||||
"dispinfo" => Ok(Command::DisplayInfo),
|
"gpuinfo" => Ok(Command::GpuInfo),
|
||||||
"s" | "step" => {
|
"s" | "step" => {
|
||||||
let count = match args.len() {
|
let count = match args.len() {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
|
@ -424,6 +411,28 @@ impl Debugger {
|
||||||
"bl" => Ok(Command::ListBreakpoints),
|
"bl" => Ok(Command::ListBreakpoints),
|
||||||
"q" | "quit" => Ok(Command::Quit),
|
"q" | "quit" => Ok(Command::Quit),
|
||||||
"r" | "reset" => Ok(Command::Reset),
|
"r" | "reset" => Ok(Command::Reset),
|
||||||
|
"trace" => {
|
||||||
|
let usage = DebuggerError::InvalidCommandFormat(String::from(
|
||||||
|
"trace [sysbus|opcode|dma|all]",
|
||||||
|
));
|
||||||
|
if args.len() != 1 {
|
||||||
|
Err(usage)
|
||||||
|
} else {
|
||||||
|
if let Value::Identifier(flag_str) = &args[0] {
|
||||||
|
let flags = match flag_str.as_ref() {
|
||||||
|
"sysbus" => TraceFlags::TRACE_SYSBUS,
|
||||||
|
"opcode" => TraceFlags::TRACE_OPCODE,
|
||||||
|
"dma" => TraceFlags::TRACE_DMA,
|
||||||
|
"timers" => TraceFlags::TRACE_TIMERS,
|
||||||
|
"all" => TraceFlags::all(),
|
||||||
|
_ => return Err(usage),
|
||||||
|
};
|
||||||
|
Ok(Command::TraceToggle(flags))
|
||||||
|
} else {
|
||||||
|
Err(usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Err(DebuggerError::InvalidCommand(command)),
|
_ => Err(DebuggerError::InvalidCommand(command)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, prelude::*, BufReader};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -20,13 +22,14 @@ mod palette_view;
|
||||||
mod tile_view;
|
mod tile_view;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub enum DebuggerError {
|
pub enum DebuggerError {
|
||||||
ParsingError(String),
|
ParsingError(String),
|
||||||
CpuError(CpuError),
|
CpuError(CpuError),
|
||||||
InvalidCommand(String),
|
InvalidCommand(String),
|
||||||
InvalidArgument(String),
|
InvalidArgument(String),
|
||||||
InvalidCommandFormat(String),
|
InvalidCommandFormat(String),
|
||||||
|
IoError(::std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CpuError> for DebuggerError {
|
impl From<CpuError> for DebuggerError {
|
||||||
|
@ -35,6 +38,12 @@ impl From<CpuError> for DebuggerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<::std::io::Error> for DebuggerError {
|
||||||
|
fn from(e: ::std::io::Error) -> DebuggerError {
|
||||||
|
DebuggerError::IoError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type DebuggerResult<T> = Result<T, DebuggerError>;
|
type DebuggerResult<T> = Result<T, DebuggerError>;
|
||||||
|
|
||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
|
@ -147,7 +156,7 @@ impl Debugger {
|
||||||
Expr::Command(c, a) => match self.eval_command(c, a) {
|
Expr::Command(c, a) => match self.eval_command(c, a) {
|
||||||
Ok(cmd) => {
|
Ok(cmd) => {
|
||||||
self.previous_command = Some(cmd.clone());
|
self.previous_command = Some(cmd.clone());
|
||||||
cmd.run(self)
|
self.run_command(cmd)
|
||||||
}
|
}
|
||||||
Err(DebuggerError::InvalidCommand(c)) => {
|
Err(DebuggerError::InvalidCommand(c)) => {
|
||||||
println!("{}: {:?}", "invalid command".red(), c)
|
println!("{}: {:?}", "invalid command".red(), c)
|
||||||
|
@ -174,11 +183,20 @@ impl Debugger {
|
||||||
self.running = false;
|
self.running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repl(&mut self) -> DebuggerResult<()> {
|
pub fn repl(&mut self, script_file: Option<&str>) -> DebuggerResult<()> {
|
||||||
println!("Welcome to rustboyadvance-NG debugger 😎!\n");
|
println!("Welcome to rustboyadvance-NG debugger 😎!\n");
|
||||||
self.running = true;
|
self.running = true;
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
let _ = rl.load_history(".rustboyadvance_history");
|
let _ = rl.load_history(".rustboyadvance_history");
|
||||||
|
if let Some(path) = script_file {
|
||||||
|
println!("Executing script file");
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
for line in reader.lines() {
|
||||||
|
let expr = parse_expr(&line.unwrap())?;
|
||||||
|
self.eval_expr(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
while self.running {
|
while self.running {
|
||||||
let readline = rl.readline(&format!("({}) ᐅ ", "rustboyadvance-dbg".bold().cyan()));
|
let readline = rl.readline(&format!("({}) ᐅ ", "rustboyadvance-dbg".bold().cyan()));
|
||||||
match readline {
|
match readline {
|
||||||
|
|
Reference in a new issue