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
This commit is contained in:
Michel Heily 2019-11-10 20:00:07 +02:00
parent bfee970592
commit c117cbe924
5 changed files with 186 additions and 51 deletions

View file

@ -22,6 +22,7 @@ time = "0.1.42"
bitfield = "0.13.1" bitfield = "0.13.1"
bitflags = "1.1.0" bitflags = "1.1.0"
zip = "0.5.3" zip = "0.5.3"
ctrlc = "3.1.3"
[profile.dev] [profile.dev]
opt-level = 0 opt-level = 0

View file

@ -46,7 +46,7 @@ pub struct Core {
decoded_arm: u32, decoded_arm: u32,
fetched_thumb: u16, fetched_thumb: u16,
decoded_thumb: u16, decoded_thumb: u16,
last_executed: Option<DecodedInstruction>, pub last_executed: Option<DecodedInstruction>,
pub cycles: usize, pub cycles: usize,
@ -325,10 +325,12 @@ impl Core {
PipelineState::Refill1 => { PipelineState::Refill1 => {
self.pc = pc.wrapping_add(4); self.pc = pc.wrapping_add(4);
self.pipeline_state = PipelineState::Refill2; self.pipeline_state = PipelineState::Refill2;
self.last_executed = None;
} }
PipelineState::Refill2 => { PipelineState::Refill2 => {
self.pc = pc.wrapping_add(4); self.pc = pc.wrapping_add(4);
self.pipeline_state = PipelineState::Execute; self.pipeline_state = PipelineState::Execute;
self.last_executed = None;
} }
PipelineState::Execute => { PipelineState::Execute => {
let insn = ArmInstruction::decode(insn, self.pc.wrapping_sub(8))?; let insn = ArmInstruction::decode(insn, self.pc.wrapping_sub(8))?;

View file

@ -36,7 +36,6 @@ impl GameBoyAdvance {
while self.sysbus.io.gpu.state != GpuState::VBlank { while self.sysbus.io.gpu.state != GpuState::VBlank {
self.step_new(); self.step_new();
} }
self.backend.render(self.sysbus.io.gpu.get_framebuffer());
while self.sysbus.io.gpu.state == GpuState::VBlank { while self.sysbus.io.gpu.state == GpuState::VBlank {
self.step_new(); self.step_new();
} }
@ -73,7 +72,10 @@ impl GameBoyAdvance {
io.timers.step(cycles, &mut self.sysbus, &mut irqs); io.timers.step(cycles, &mut self.sysbus, &mut irqs);
if let Some(new_gpu_state) = io.gpu.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 { 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(), GpuState::HBlank => io.dmac.notify_hblank(),
_ => {} _ => {}
} }

View file

@ -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::arm::ArmInstruction;
use crate::core::arm7tdmi::bus::Bus; use crate::core::arm7tdmi::bus::Bus;
use crate::core::arm7tdmi::thumb::ThumbInstruction; use crate::core::arm7tdmi::thumb::ThumbInstruction;
@ -20,6 +24,13 @@ pub enum DisassMode {
ModeThumb, ModeThumb,
} }
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum MemWriteCommandSize {
Byte,
Half,
Word,
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Command { pub enum Command {
Info, Info,
@ -28,6 +39,7 @@ pub enum Command {
Continue, Continue,
Frame(usize), Frame(usize),
HexDump(Addr, u32), HexDump(Addr, u32),
MemWrite(MemWriteCommandSize, Addr, u32),
Disass(DisassMode, Addr, u32), Disass(DisassMode, Addr, u32),
AddBreakpoint(Addr), AddBreakpoint(Addr),
DelBreakpoint(Addr), DelBreakpoint(Addr),
@ -46,17 +58,24 @@ impl Command {
Info => println!("{}", debugger.gba.cpu), Info => println!("{}", debugger.gba.cpu),
DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ } DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ }
Step(count) => { Step(count) => {
debugger.ctrlc_flag.store(true, Ordering::SeqCst);
for _ in 0..count { for _ in 0..count {
match debugger.gba.step() { if !debugger.ctrlc_flag.load(Ordering::SeqCst) {
Ok(insn) => { 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!( print!(
"{}\t{}", "{}\t{}",
Colour::Black Colour::Black
.bold() .bold()
.italic() .italic()
.on(Colour::White) .on(Colour::White)
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)), .paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)),
insn last_executed
); );
println!( println!(
"{}", "{}",
@ -64,43 +83,76 @@ impl Command {
"\t\t/// Next instruction at @0x{:08x}", "\t\t/// Next instruction at @0x{:08x}",
debugger.gba.cpu.get_next_pc() debugger.gba.cpu.get_next_pc()
)) ))
) );
}
Err(GBAError::CpuError(e)) => {
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:x?}", debugger.gba.cpu)
}
_ => unreachable!(),
}
} }
println!("{}\n", debugger.gba.cpu); 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 => { Continue => {
let start_cycles = debugger.gba.cpu.cycles(); let frame_time = time::Duration::new(0, 1_000_000_000u32 / 60);
loop { debugger.ctrlc_flag.store(true, Ordering::SeqCst);
if let Some(bp) = debugger.check_breakpoint() { while debugger.ctrlc_flag.load(Ordering::SeqCst) {
match debugger.gba.step() { let start_time = time::Instant::now();
Err(GBAError::CpuError(e)) => { debugger.gba.frame();
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:x?}", debugger.gba.cpu); let time_passed = start_time.elapsed();
break; let delay = frame_time.checked_sub(time_passed);
match delay {
None => {}
Some(delay) => {
::std::thread::sleep(delay);
} }
_ => (),
};
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 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;
@ -115,6 +167,11 @@ impl Command {
let bytes = debugger.gba.sysbus.get_bytes(addr..addr + nbytes); let bytes = debugger.gba.sysbus.get_bytes(addr..addr + nbytes);
hexdump::hexdump(&bytes); 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) => { Disass(mode, addr, n) => {
let bytes = debugger.gba.sysbus.get_bytes(addr..addr + n); let bytes = debugger.gba.sysbus.get_bytes(addr..addr + n);
match mode { match mode {
@ -257,6 +314,66 @@ impl Debugger {
}; };
Ok(Command::HexDump(addr, n)) 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" => { "d" | "disass" => {
let (addr, n) = self.get_disassembler_args(args)?; let (addr, n) = self.get_disassembler_args(args)?;

View file

@ -1,3 +1,7 @@
extern crate ctrlc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::Editor; use rustyline::Editor;
@ -36,14 +40,23 @@ type DebuggerResult<T> = Result<T, DebuggerError>;
pub struct Debugger { pub struct Debugger {
pub gba: GameBoyAdvance, pub gba: GameBoyAdvance,
running: bool, running: bool,
pub ctrlc_flag: Arc<AtomicBool>,
pub previous_command: Option<Command>, pub previous_command: Option<Command>,
} }
impl Debugger { impl Debugger {
pub fn new(gba: GameBoyAdvance) -> 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 { Debugger {
gba: gba, gba: gba,
running: false, running: false,
ctrlc_flag: ctrlc_flag,
previous_command: None, previous_command: None,
} }
} }