debugger: Few improvements
This commit is contained in:
parent
6552329310
commit
b9d1d38c2d
107
src/debugger/command.rs
Normal file
107
src/debugger/command.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use crate::debugger::Debugger;
|
||||||
|
use crate::disass::Disassembler;
|
||||||
|
|
||||||
|
use colored::*;
|
||||||
|
use hexdump;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
Info,
|
||||||
|
SingleStep,
|
||||||
|
Continue,
|
||||||
|
HexDump(u32, usize),
|
||||||
|
Disass(u32, usize),
|
||||||
|
AddBreakpoint(u32),
|
||||||
|
DelBreakpoint(u32),
|
||||||
|
ClearBreakpoints,
|
||||||
|
ListBreakpoints,
|
||||||
|
Reset,
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn run(&self, debugger: &mut Debugger) {
|
||||||
|
use Command::*;
|
||||||
|
match *self {
|
||||||
|
Info => println!("{}", debugger.cpu),
|
||||||
|
SingleStep => {
|
||||||
|
if let Some(bp) = debugger.check_breakpoint() {
|
||||||
|
println!("breakpoint #{} reached!", bp);
|
||||||
|
debugger.delete_breakpoint(bp);
|
||||||
|
} else {
|
||||||
|
match debugger.cpu.step(&mut debugger.sysbus) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
|
println!("cpu: {:#x?}", debugger.cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Continue => loop {
|
||||||
|
if let Some(bp) = debugger.check_breakpoint() {
|
||||||
|
println!("breakpoint #{} reached!", bp);
|
||||||
|
debugger.delete_breakpoint(bp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
match debugger.cpu.step(&mut debugger.sysbus) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
|
println!("cpu: {:#x?}", debugger.cpu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
HexDump(addr, nbytes) => {
|
||||||
|
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
|
||||||
|
Some(bytes) => bytes,
|
||||||
|
None => {
|
||||||
|
println!("requested content out of bounds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hexdump::hexdump(bytes);
|
||||||
|
}
|
||||||
|
Disass(addr, n) => {
|
||||||
|
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
|
||||||
|
Some(bytes) => bytes,
|
||||||
|
None => {
|
||||||
|
println!("requested content out of bounds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let disass = Disassembler::new(addr, bytes);
|
||||||
|
for line in disass {
|
||||||
|
println!("{}", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reset => {
|
||||||
|
println!("resetting cpu...");
|
||||||
|
debugger.cpu.reset();
|
||||||
|
println!("cpu is restarted!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,16 +3,15 @@ use rustyline::Editor;
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
use hexdump;
|
|
||||||
|
|
||||||
use super::arm7tdmi::cpu;
|
use super::arm7tdmi::cpu;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
use crate::disass::Disassembler;
|
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use parser::{parse_expr, Expr, Value};
|
use parser::{parse_expr, Expr, Value};
|
||||||
|
|
||||||
|
mod command;
|
||||||
|
use command::Command;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebuggerError {
|
pub enum DebuggerError {
|
||||||
ParsingError(String),
|
ParsingError(String),
|
||||||
|
@ -30,107 +29,13 @@ impl From<cpu::CpuError> for DebuggerError {
|
||||||
|
|
||||||
type DebuggerResult<T> = Result<T, DebuggerError>;
|
type DebuggerResult<T> = Result<T, DebuggerError>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Command {
|
|
||||||
Info,
|
|
||||||
SingleStep,
|
|
||||||
Continue,
|
|
||||||
HexDump(u32, usize),
|
|
||||||
Disass(u32, usize),
|
|
||||||
AddBreakpoint(u32),
|
|
||||||
ListBreakpoints,
|
|
||||||
Reset,
|
|
||||||
Quit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command {
|
|
||||||
fn run(&self, debugger: &mut Debugger) {
|
|
||||||
use Command::*;
|
|
||||||
match *self {
|
|
||||||
Info => println!("{}", debugger.cpu),
|
|
||||||
SingleStep => {
|
|
||||||
match debugger.cpu.step(&mut debugger.sysbus) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => {
|
|
||||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
|
||||||
println!("cpu: {:#x?}", debugger.cpu);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if debugger.is_breakpoint_reached() {
|
|
||||||
println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Continue => loop {
|
|
||||||
match debugger.cpu.step(&mut debugger.sysbus) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => {
|
|
||||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
|
||||||
println!("cpu: {:#x?}", debugger.cpu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if debugger.is_breakpoint_reached() {
|
|
||||||
println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
HexDump(addr, nbytes) => {
|
|
||||||
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
|
|
||||||
Some(bytes) => bytes,
|
|
||||||
None => {
|
|
||||||
println!("requested content out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
hexdump::hexdump(bytes);
|
|
||||||
}
|
|
||||||
Disass(addr, n) => {
|
|
||||||
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
|
|
||||||
Some(bytes) => bytes,
|
|
||||||
None => {
|
|
||||||
println!("requested content out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let disass = Disassembler::new(addr, bytes);
|
|
||||||
for line in disass {
|
|
||||||
println!("{}", line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListBreakpoints => {
|
|
||||||
println!("breakpoint list:");
|
|
||||||
for (i, b) in debugger.breakpoints.iter().enumerate() {
|
|
||||||
println!("[{}] 0x{:08x}", i, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reset => {
|
|
||||||
println!("resetting cpu...");
|
|
||||||
debugger.cpu.reset();
|
|
||||||
println!("cpu is restarted!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
cpu: cpu::Core,
|
pub cpu: cpu::Core,
|
||||||
sysbus: SysBus,
|
pub sysbus: SysBus,
|
||||||
running: bool,
|
running: bool,
|
||||||
breakpoints: Vec<u32>,
|
breakpoints: Vec<u32>,
|
||||||
previous_command: Option<Command>,
|
pub previous_command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debugger {
|
impl Debugger {
|
||||||
|
@ -140,46 +45,57 @@ impl Debugger {
|
||||||
sysbus: sysbus,
|
sysbus: sysbus,
|
||||||
breakpoints: Vec::new(),
|
breakpoints: Vec::new(),
|
||||||
running: false,
|
running: false,
|
||||||
previous_command: None
|
previous_command: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_breakpoint_reached(&self) -> bool {
|
pub fn check_breakpoint(&self) -> Option<u32> {
|
||||||
let pc = self.cpu.pc;
|
let pc = self.cpu.get_reg(15);
|
||||||
for b in &self.breakpoints {
|
for bp in &self.breakpoints {
|
||||||
if *b == pc {
|
if *bp == pc {
|
||||||
return true;
|
return Some(pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete_breakpoint(&mut self, addr: u32) {
|
||||||
|
self.breakpoints.retain(|&a| a != addr);
|
||||||
|
}
|
||||||
|
|
||||||
fn decode_reg(&self, s: &str) -> DebuggerResult<usize> {
|
fn decode_reg(&self, s: &str) -> DebuggerResult<usize> {
|
||||||
let reg_names = vec![
|
let reg_names = vec![
|
||||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr",
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp",
|
||||||
"pc"];
|
"lr", "pc",
|
||||||
|
];
|
||||||
|
|
||||||
match reg_names.into_iter().position(|r| r == s) {
|
match reg_names.into_iter().position(|r| r == s) {
|
||||||
Some(index) => Ok(index),
|
Some(index) => Ok(index),
|
||||||
None => Err(DebuggerError::InvalidArgument(format!("{:?} is not a register name", s)))
|
None => Err(DebuggerError::InvalidArgument(format!(
|
||||||
|
"{:?} is not a register name",
|
||||||
|
s
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> {
|
fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> {
|
||||||
match arg {
|
match arg {
|
||||||
Value::Name(reg) => {
|
Value::Name(reg) => self.decode_reg(®),
|
||||||
self.decode_reg(®)
|
v => Err(DebuggerError::InvalidArgument(format!(
|
||||||
},
|
"expected a number, got {:?}",
|
||||||
v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v)))
|
v
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn val_number(&self, arg: &Value) -> DebuggerResult<u32> {
|
fn val_number(&self, arg: &Value) -> DebuggerResult<u32> {
|
||||||
match arg {
|
match arg {
|
||||||
Value::Num(n) => Ok(*n),
|
Value::Num(n) => Ok(*n),
|
||||||
v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v)))
|
v => Err(DebuggerError::InvalidArgument(format!(
|
||||||
|
"expected a number, got {:?}",
|
||||||
|
v
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +106,10 @@ impl Debugger {
|
||||||
let reg = self.decode_reg(®)?;
|
let reg = self.decode_reg(®)?;
|
||||||
Ok(self.cpu.get_reg(reg))
|
Ok(self.cpu.get_reg(reg))
|
||||||
}
|
}
|
||||||
v => Err(DebuggerError::InvalidArgument(format!("addr: expected a number or register, got {:?}", v)))
|
v => Err(DebuggerError::InvalidArgument(format!(
|
||||||
|
"addr: expected a number or register, got {:?}",
|
||||||
|
v
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +125,33 @@ impl Debugger {
|
||||||
"i" | "info" => Ok(Command::Info),
|
"i" | "info" => Ok(Command::Info),
|
||||||
"s" | "step" => Ok(Command::SingleStep),
|
"s" | "step" => Ok(Command::SingleStep),
|
||||||
"c" | "continue" => Ok(Command::Continue),
|
"c" | "continue" => Ok(Command::Continue),
|
||||||
"xxd" => {
|
"x" | "hexdump" => {
|
||||||
let addr = self.val_address(&args[0])?;
|
let (addr, n) = match args.len() {
|
||||||
let nbytes = self.val_number(&args[1])?;
|
2 => {
|
||||||
Ok(Command::HexDump(addr, nbytes as usize))
|
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 {
|
||||||
|
(self.cpu.get_reg(15), 0x100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(DebuggerError::InvalidCommandFormat(
|
||||||
|
"xxd [addr] [n]".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Command::HexDump(addr, n))
|
||||||
}
|
}
|
||||||
"d" | "disass" => {
|
"d" | "disass" => {
|
||||||
let (addr, n) = match args.len() {
|
let (addr, n) = match args.len() {
|
||||||
|
@ -243,13 +185,23 @@ impl Debugger {
|
||||||
"b" | "break" => {
|
"b" | "break" => {
|
||||||
if args.len() != 1 {
|
if args.len() != 1 {
|
||||||
Err(DebuggerError::InvalidCommandFormat(
|
Err(DebuggerError::InvalidCommandFormat(
|
||||||
"disass <addr>".to_string(),
|
"break <addr>".to_string(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let addr = self.val_address(&args[0])?;
|
let addr = self.val_address(&args[0])?;
|
||||||
Ok(Command::AddBreakpoint(addr))
|
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]",
|
||||||
|
))),
|
||||||
|
},
|
||||||
"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),
|
||||||
|
@ -271,7 +223,7 @@ impl Debugger {
|
||||||
Ok(cmd) => {
|
Ok(cmd) => {
|
||||||
self.previous_command = Some(cmd.clone());
|
self.previous_command = Some(cmd.clone());
|
||||||
cmd.run(self)
|
cmd.run(self)
|
||||||
},
|
}
|
||||||
Err(DebuggerError::InvalidCommand(c)) => {
|
Err(DebuggerError::InvalidCommand(c)) => {
|
||||||
println!("{}: {:?}", "invalid command".red(), c)
|
println!("{}: {:?}", "invalid command".red(), c)
|
||||||
}
|
}
|
||||||
|
@ -284,9 +236,11 @@ impl Debugger {
|
||||||
Err(e) => println!("{} {:?}", "failed to build command".red(), e),
|
Err(e) => println!("{} {:?}", "failed to build command".red(), e),
|
||||||
},
|
},
|
||||||
Expr::Assignment(lvalue, rvalue) => match self.eval_assignment(lvalue, rvalue) {
|
Expr::Assignment(lvalue, rvalue) => match self.eval_assignment(lvalue, rvalue) {
|
||||||
Err(DebuggerError::InvalidArgument(m)) => println!("{}: {}", "assignment error".red(), m),
|
Err(DebuggerError::InvalidArgument(m)) => {
|
||||||
_ => ()
|
println!("{}: {}", "assignment error".red(), m)
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
Expr::Empty => println!("Got empty expr"),
|
Expr::Empty => println!("Got empty expr"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,6 +250,7 @@ impl Debugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repl(&mut self) -> DebuggerResult<()> {
|
pub fn repl(&mut self) -> DebuggerResult<()> {
|
||||||
|
println!("Welcome to rustboyadvance-NG debugger 😎!\n");
|
||||||
self.running = true;
|
self.running = true;
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
rl.load_history(".rustboyadvance_history");
|
rl.load_history(".rustboyadvance_history");
|
||||||
|
|
Reference in a new issue