use crate::arm7tdmi::bus::Bus; use crate::arm7tdmi::{reg_string, Addr, REG_PC}; use crate::disass::Disassembler; use super::{parser::Value, Debugger, DebuggerError, DebuggerResult}; use ansi_term::Colour; use colored::*; use hexdump; #[derive(Debug, PartialEq, Clone)] pub enum Command { Info, SingleStep(bool), 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(_cycle) => { if let Some(bp) = debugger.check_breakpoint() { println!("hit breakpoint #0x{:08x}!", bp); debugger.delete_breakpoint(bp); } else { match debugger.cpu.step_debugger(&mut debugger.sysbus) { Ok(insn) => { println!("{}\n", debugger.cpu); println!( "Executed at @0x{:08x}:\n\t{}", insn.pc, Colour::Yellow.italic().paint(format!("{} ", insn)) ); println!("Next instruction at @0x{:08x}", debugger.cpu.get_next_pc()) } Err(e) => { println!("{}: {}", "cpu encountered an error".red(), e); println!("cpu: {:x?}", debugger.cpu) } } } } Continue => loop { if let Some(bp) = debugger.check_breakpoint() { println!("hit breakpoint #0x{:08x}!", bp); debugger.delete_breakpoint(bp); break; } match debugger.cpu.step_debugger(&mut debugger.sysbus) { Ok(insn) => { println!( "@0x{:08x}:\n\t{}", insn.pc, Colour::Yellow.italic().paint(format!("{} ", insn)) ); } Err(e) => { println!("{}: {}", "cpu encountered an error".red(), e); println!("cpu: {:x?}", debugger.cpu); break; } }; }, HexDump(addr, nbytes) => { let bytes = debugger.sysbus.get_bytes(addr, nbytes); hexdump::hexdump(bytes); } Disass(addr, n) => { let bytes = debugger.sysbus.get_bytes(addr, 4 * n); 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!") } } } } impl Debugger { pub fn eval_command(&self, command: Value, args: Vec) -> DebuggerResult { let command = match command { Value::Name(command) => command, _ => { return Err(DebuggerError::InvalidCommand("expected a name".to_string())); } }; match command.as_ref() { "i" | "info" => Ok(Command::Info), "s" | "step" => Ok(Command::SingleStep(false)), "sc" | "stepcycle" => Ok(Command::SingleStep(true)), "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 { (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() { 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, 10) } 0 => { if let Some(Command::Disass(addr, n)) = self.previous_command { (addr + (4 * n as u32), 10) } else { (self.cpu.get_next_pc(), 10) } } _ => { return Err(DebuggerError::InvalidCommandFormat( "disass [addr] [n]".to_string(), )) } }; Ok(Command::Disass(addr, n)) } "b" | "break" => { if args.len() != 1 { Err(DebuggerError::InvalidCommandFormat( "break ".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]", ))), }, "bl" => Ok(Command::ListBreakpoints), "q" | "quit" => Ok(Command::Quit), "r" | "reset" => Ok(Command::Reset), _ => Err(DebuggerError::InvalidCommand(command)), } } }