2019-06-30 14:59:19 +01:00
|
|
|
use crate::arm7tdmi::bus::Bus;
|
2019-07-02 14:57:35 +01:00
|
|
|
use crate::arm7tdmi::{Addr, CpuState};
|
2019-06-27 10:55:28 +01:00
|
|
|
use crate::disass::Disassembler;
|
2019-07-06 13:53:36 +01:00
|
|
|
use crate::GBAError;
|
2019-06-27 10:55:28 +01:00
|
|
|
|
2019-07-06 14:04:43 +01:00
|
|
|
use super::palette_view::create_palette_view;
|
2019-07-06 21:38:08 +01:00
|
|
|
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
2019-07-02 11:31:02 +01:00
|
|
|
|
2019-06-28 23:52:10 +01:00
|
|
|
use ansi_term::Colour;
|
|
|
|
|
2019-06-27 10:55:28 +01:00
|
|
|
use colored::*;
|
|
|
|
use hexdump;
|
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
|
|
|
pub enum DisassMode {
|
|
|
|
ModeArm,
|
|
|
|
ModeThumb,
|
|
|
|
}
|
|
|
|
|
2019-06-27 10:55:28 +01:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub enum Command {
|
|
|
|
Info,
|
2019-07-03 23:37:47 +01:00
|
|
|
Step(usize),
|
2019-06-27 10:55:28 +01:00
|
|
|
Continue,
|
2019-07-02 14:57:35 +01:00
|
|
|
HexDump(Addr, usize),
|
|
|
|
Disass(DisassMode, Addr, usize),
|
|
|
|
AddBreakpoint(Addr),
|
|
|
|
DelBreakpoint(Addr),
|
2019-07-06 14:04:43 +01:00
|
|
|
PaletteView,
|
2019-06-27 10:55:28 +01:00
|
|
|
ClearBreakpoints,
|
|
|
|
ListBreakpoints,
|
|
|
|
Reset,
|
|
|
|
Quit,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
pub fn run(&self, debugger: &mut Debugger) {
|
|
|
|
use Command::*;
|
|
|
|
match *self {
|
2019-07-06 13:53:36 +01:00
|
|
|
Info => println!("{}", debugger.gba.cpu),
|
2019-07-03 23:37:47 +01:00
|
|
|
Step(count) => {
|
|
|
|
for _ in 0..count {
|
|
|
|
if let Some(bp) = debugger.check_breakpoint() {
|
|
|
|
println!("hit breakpoint #0x{:08x}!", bp);
|
|
|
|
debugger.delete_breakpoint(bp);
|
|
|
|
} else {
|
2019-07-06 13:53:36 +01:00
|
|
|
match debugger.gba.step() {
|
2019-07-03 23:37:47 +01:00
|
|
|
Ok(insn) => {
|
|
|
|
print!(
|
|
|
|
"{}\t{}",
|
|
|
|
Colour::Black
|
|
|
|
.bold()
|
|
|
|
.italic()
|
|
|
|
.on(Colour::White)
|
|
|
|
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)),
|
|
|
|
insn
|
|
|
|
);
|
2019-07-06 13:53:36 +01:00
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
Colour::Purple.dimmed().italic().paint(format!(
|
|
|
|
"\t\t/// Next instruction at @0x{:08x}",
|
|
|
|
debugger.gba.cpu.get_next_pc()
|
|
|
|
))
|
|
|
|
)
|
2019-07-03 23:37:47 +01:00
|
|
|
}
|
2019-07-06 13:53:36 +01:00
|
|
|
Err(GBAError::CpuError(e)) => {
|
2019-07-03 23:37:47 +01:00
|
|
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
2019-07-06 13:53:36 +01:00
|
|
|
println!("cpu: {:x?}", debugger.gba.cpu)
|
2019-07-03 23:37:47 +01:00
|
|
|
}
|
2019-07-06 13:53:36 +01:00
|
|
|
_ => unreachable!(),
|
2019-06-27 10:55:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-06 13:53:36 +01:00
|
|
|
println!("{}\n", debugger.gba.cpu);
|
2019-06-27 10:55:28 +01:00
|
|
|
}
|
|
|
|
Continue => loop {
|
|
|
|
if let Some(bp) = debugger.check_breakpoint() {
|
2019-06-28 13:06:38 +01:00
|
|
|
println!("hit breakpoint #0x{:08x}!", bp);
|
2019-06-27 10:55:28 +01:00
|
|
|
debugger.delete_breakpoint(bp);
|
|
|
|
break;
|
|
|
|
}
|
2019-07-06 13:53:36 +01:00
|
|
|
match debugger.gba.step() {
|
2019-07-06 21:38:08 +01:00
|
|
|
// Ok(insn) => {
|
|
|
|
// println!(
|
|
|
|
// "@0x{:08x}:\t{}",
|
|
|
|
// insn.get_pc(),
|
|
|
|
// Colour::Yellow.italic().paint(format!("{} ", insn))
|
|
|
|
// );
|
|
|
|
// }
|
2019-07-06 13:53:36 +01:00
|
|
|
Err(GBAError::CpuError(e)) => {
|
2019-06-27 10:55:28 +01:00
|
|
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
2019-07-06 13:53:36 +01:00
|
|
|
println!("cpu: {:x?}", debugger.gba.cpu);
|
2019-06-27 10:55:28 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-07-06 21:38:08 +01:00
|
|
|
_ => (),
|
2019-06-27 10:55:28 +01:00
|
|
|
};
|
|
|
|
},
|
|
|
|
HexDump(addr, nbytes) => {
|
2019-07-06 13:53:36 +01:00
|
|
|
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
2019-07-02 14:57:35 +01:00
|
|
|
hexdump::hexdump(&bytes[0..nbytes]);
|
2019-06-27 10:55:28 +01:00
|
|
|
}
|
2019-07-02 14:57:35 +01:00
|
|
|
Disass(mode, addr, n) => {
|
|
|
|
use crate::arm7tdmi::arm::ArmInstruction;
|
|
|
|
use crate::arm7tdmi::thumb::ThumbInstruction;
|
|
|
|
|
2019-07-06 13:53:36 +01:00
|
|
|
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
2019-07-02 14:57:35 +01:00
|
|
|
match mode {
|
|
|
|
DisassMode::ModeArm => {
|
|
|
|
let disass = Disassembler::<ArmInstruction>::new(addr, bytes);
|
|
|
|
for (_, line) in disass.take(n) {
|
|
|
|
println!("{}", line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DisassMode::ModeThumb => {
|
|
|
|
let disass = Disassembler::<ThumbInstruction>::new(addr, bytes);
|
|
|
|
for (_, line) in disass.take(n) {
|
|
|
|
println!("{}", line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-06-27 10:55:28 +01:00
|
|
|
}
|
|
|
|
Quit => {
|
|
|
|
print!("Quitting!");
|
|
|
|
debugger.stop();
|
|
|
|
}
|
|
|
|
AddBreakpoint(addr) => {
|
|
|
|
if !debugger.breakpoints.contains(&addr) {
|
|
|
|
let new_index = debugger.breakpoints.len();
|
|
|
|
debugger.breakpoints.push(addr);
|
|
|
|
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
|
|
|
|
} else {
|
|
|
|
println!("breakpoint already exists!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DelBreakpoint(addr) => debugger.delete_breakpoint(addr),
|
|
|
|
ClearBreakpoints => debugger.breakpoints.clear(),
|
|
|
|
ListBreakpoints => {
|
|
|
|
println!("breakpoint list:");
|
|
|
|
for (i, b) in debugger.breakpoints.iter().enumerate() {
|
|
|
|
println!("[{}] 0x{:08x}", i, b)
|
|
|
|
}
|
|
|
|
}
|
2019-07-06 14:04:43 +01:00
|
|
|
PaletteView => create_palette_view(debugger.gba.sysbus.get_bytes(0x0500_0000)),
|
2019-06-27 10:55:28 +01:00
|
|
|
Reset => {
|
|
|
|
println!("resetting cpu...");
|
2019-07-06 13:53:36 +01:00
|
|
|
debugger.gba.cpu.reset();
|
2019-06-27 10:55:28 +01:00
|
|
|
println!("cpu is restarted!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-02 11:31:02 +01:00
|
|
|
|
|
|
|
impl Debugger {
|
2019-07-02 14:57:35 +01:00
|
|
|
fn get_disassembler_args(&self, args: Vec<Value>) -> DebuggerResult<(Addr, usize)> {
|
|
|
|
match args.len() {
|
|
|
|
2 => {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
let n = self.val_number(&args[1])?;
|
|
|
|
|
|
|
|
Ok((addr, n as usize))
|
|
|
|
}
|
|
|
|
1 => {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
|
|
|
|
Ok((addr, 10))
|
|
|
|
}
|
|
|
|
0 => {
|
|
|
|
if let Some(Command::Disass(_mode, addr, n)) = &self.previous_command {
|
|
|
|
Ok((*addr + (4 * (*n as u32)), 10))
|
|
|
|
} else {
|
2019-07-06 13:53:36 +01:00
|
|
|
Ok((self.gba.cpu.get_next_pc(), 10))
|
2019-07-02 14:57:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(DebuggerError::InvalidCommandFormat(
|
|
|
|
"disass [addr] [n]".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-02 11:31:02 +01:00
|
|
|
pub fn eval_command(&self, command: Value, args: Vec<Value>) -> DebuggerResult<Command> {
|
|
|
|
let command = match command {
|
2019-07-02 14:57:35 +01:00
|
|
|
Value::Identifier(command) => command,
|
2019-07-02 11:31:02 +01:00
|
|
|
_ => {
|
|
|
|
return Err(DebuggerError::InvalidCommand("expected a name".to_string()));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match command.as_ref() {
|
|
|
|
"i" | "info" => Ok(Command::Info),
|
2019-07-03 23:37:47 +01:00
|
|
|
"s" | "step" => {
|
|
|
|
let count = match args.len() {
|
|
|
|
0 => 1,
|
|
|
|
1 => self.val_number(&args[0])?,
|
|
|
|
_ => {
|
|
|
|
return Err(DebuggerError::InvalidCommandFormat(
|
|
|
|
"step [count]".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Command::Step(count as usize))
|
|
|
|
}
|
2019-07-02 11:31:02 +01:00
|
|
|
"c" | "continue" => Ok(Command::Continue),
|
|
|
|
"x" | "hexdump" => {
|
|
|
|
let (addr, n) = match args.len() {
|
|
|
|
2 => {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
let n = self.val_number(&args[1])?;
|
|
|
|
|
|
|
|
(addr, n as usize)
|
|
|
|
}
|
|
|
|
1 => {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
|
|
|
|
(addr, 0x100)
|
|
|
|
}
|
|
|
|
0 => {
|
|
|
|
if let Some(Command::HexDump(addr, n)) = self.previous_command {
|
|
|
|
(addr + (4 * n as u32), 0x100)
|
|
|
|
} else {
|
2019-07-06 13:53:36 +01:00
|
|
|
(self.gba.cpu.get_reg(15), 0x100)
|
2019-07-02 11:31:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(DebuggerError::InvalidCommandFormat(
|
|
|
|
"xxd [addr] [n]".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Command::HexDump(addr, n))
|
|
|
|
}
|
|
|
|
"d" | "disass" => {
|
2019-07-02 14:57:35 +01:00
|
|
|
let (addr, n) = self.get_disassembler_args(args)?;
|
2019-07-02 11:31:02 +01:00
|
|
|
|
2019-07-06 13:53:36 +01:00
|
|
|
let m = match self.gba.cpu.cpsr.state() {
|
2019-07-02 14:57:35 +01:00
|
|
|
CpuState::ARM => DisassMode::ModeArm,
|
|
|
|
CpuState::THUMB => DisassMode::ModeThumb,
|
2019-07-02 11:31:02 +01:00
|
|
|
};
|
2019-07-02 14:57:35 +01:00
|
|
|
Ok(Command::Disass(m, addr, n))
|
|
|
|
}
|
|
|
|
"da" | "disass-arm" => {
|
|
|
|
let (addr, n) = self.get_disassembler_args(args)?;
|
|
|
|
|
|
|
|
Ok(Command::Disass(DisassMode::ModeArm, addr, n))
|
|
|
|
}
|
|
|
|
"dt" | "disass-thumb" => {
|
|
|
|
let (addr, n) = self.get_disassembler_args(args)?;
|
2019-07-02 11:31:02 +01:00
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
Ok(Command::Disass(DisassMode::ModeThumb, addr, n))
|
2019-07-02 11:31:02 +01:00
|
|
|
}
|
|
|
|
"b" | "break" => {
|
|
|
|
if args.len() != 1 {
|
|
|
|
Err(DebuggerError::InvalidCommandFormat(
|
|
|
|
"break <addr>".to_string(),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
Ok(Command::AddBreakpoint(addr))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"bd" | "breakdel" => match args.len() {
|
|
|
|
0 => Ok(Command::ClearBreakpoints),
|
|
|
|
1 => {
|
|
|
|
let addr = self.val_address(&args[0])?;
|
|
|
|
Ok(Command::DelBreakpoint(addr))
|
|
|
|
}
|
|
|
|
_ => Err(DebuggerError::InvalidCommandFormat(String::from(
|
|
|
|
"breakdel [addr]",
|
|
|
|
))),
|
|
|
|
},
|
2019-07-06 14:04:43 +01:00
|
|
|
"palette-view" => Ok(Command::PaletteView),
|
2019-07-02 11:31:02 +01:00
|
|
|
"bl" => Ok(Command::ListBreakpoints),
|
|
|
|
"q" | "quit" => Ok(Command::Quit),
|
|
|
|
"r" | "reset" => Ok(Command::Reset),
|
|
|
|
_ => Err(DebuggerError::InvalidCommand(command)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|