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:
parent
bfee970592
commit
c117cbe924
|
@ -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
|
||||||
|
|
|
@ -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))?;
|
||||||
|
|
|
@ -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(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,61 +58,101 @@ 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;
|
||||||
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!(),
|
|
||||||
}
|
}
|
||||||
|
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);
|
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) => {
|
||||||
let num_cycles = debugger.gba.cpu.cycles() - start_cycles;
|
::std::thread::sleep(delay);
|
||||||
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)?;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue