diff --git a/src/debugger/command.rs b/src/debugger/command.rs new file mode 100644 index 0000000..3dca54d --- /dev/null +++ b/src/debugger/command.rs @@ -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!") + } + } + } +} diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index 1f04599..8eeb684 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -3,16 +3,15 @@ use rustyline::Editor; use colored::*; -use hexdump; - use super::arm7tdmi::cpu; use super::sysbus::SysBus; -use crate::disass::Disassembler; - mod parser; use parser::{parse_expr, Expr, Value}; +mod command; +use command::Command; + #[derive(Debug, PartialEq)] pub enum DebuggerError { ParsingError(String), @@ -30,107 +29,13 @@ impl From for DebuggerError { type DebuggerResult = Result; -#[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)] pub struct Debugger { - cpu: cpu::Core, - sysbus: SysBus, + pub cpu: cpu::Core, + pub sysbus: SysBus, running: bool, breakpoints: Vec, - previous_command: Option, + pub previous_command: Option, } impl Debugger { @@ -140,46 +45,57 @@ impl Debugger { sysbus: sysbus, breakpoints: Vec::new(), running: false, - previous_command: None + previous_command: None, } } - fn is_breakpoint_reached(&self) -> bool { - let pc = self.cpu.pc; - for b in &self.breakpoints { - if *b == pc { - return true; + pub fn check_breakpoint(&self) -> Option { + let pc = self.cpu.get_reg(15); + for bp in &self.breakpoints { + if *bp == pc { + 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 { let reg_names = vec![ - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", - "pc"]; + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", + "lr", "pc", + ]; match reg_names.into_iter().position(|r| r == s) { 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 { match arg { - Value::Name(reg) => { - self.decode_reg(®) - }, - v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v))) + Value::Name(reg) => self.decode_reg(®), + v => Err(DebuggerError::InvalidArgument(format!( + "expected a number, got {:?}", + v + ))), } } fn val_number(&self, arg: &Value) -> DebuggerResult { match arg { 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(®)?; 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), "s" | "step" => Ok(Command::SingleStep), "c" | "continue" => Ok(Command::Continue), - "xxd" => { - let addr = self.val_address(&args[0])?; - let nbytes = self.val_number(&args[1])?; - Ok(Command::HexDump(addr, nbytes as usize)) + "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 { + (self.cpu.get_reg(15), 0x100) + } + } + _ => { + return Err(DebuggerError::InvalidCommandFormat( + "xxd [addr] [n]".to_string(), + )) + } + }; + Ok(Command::HexDump(addr, n)) } "d" | "disass" => { let (addr, n) = match args.len() { @@ -243,13 +185,23 @@ impl Debugger { "b" | "break" => { if args.len() != 1 { Err(DebuggerError::InvalidCommandFormat( - "disass ".to_string(), + "break ".to_string(), )) } else { - let addr = self.val_address(&args[0])?; + 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]", + ))), + }, "bl" => Ok(Command::ListBreakpoints), "q" | "quit" => Ok(Command::Quit), "r" | "reset" => Ok(Command::Reset), @@ -271,7 +223,7 @@ impl Debugger { Ok(cmd) => { self.previous_command = Some(cmd.clone()); cmd.run(self) - }, + } Err(DebuggerError::InvalidCommand(c)) => { println!("{}: {:?}", "invalid command".red(), c) } @@ -284,9 +236,11 @@ impl Debugger { Err(e) => println!("{} {:?}", "failed to build command".red(), e), }, 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"), } } @@ -296,6 +250,7 @@ impl Debugger { } pub fn repl(&mut self) -> DebuggerResult<()> { + println!("Welcome to rustboyadvance-NG debugger 😎!\n"); self.running = true; let mut rl = Editor::<()>::new(); rl.load_history(".rustboyadvance_history");