From fc400ace5fb06ccc73baec1656572284c5db30b1 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Tue, 25 Jun 2019 21:31:45 +0300 Subject: [PATCH] Improve debug repl parsing :) --- src/debugger.rs | 270 ----------------------------------------- src/debugger/mod.rs | 260 +++++++++++++++++++++++++++++++++++++++ src/debugger/parser.rs | 101 +++++++++++++++ 3 files changed, 361 insertions(+), 270 deletions(-) delete mode 100644 src/debugger.rs create mode 100644 src/debugger/mod.rs create mode 100644 src/debugger/parser.rs diff --git a/src/debugger.rs b/src/debugger.rs deleted file mode 100644 index d413c58..0000000 --- a/src/debugger.rs +++ /dev/null @@ -1,270 +0,0 @@ -use rustyline::error::ReadlineError; -use rustyline::Editor; - -use colored::*; - -use nom; -use nom::IResult; -use nom::bytes::complete::{tag, take_till1, take_till, take_while_m_n, take_while1}; -use nom::combinator::map_res; - -use hexdump; - -use super::arm7tdmi::cpu; -use super::sysbus::SysBus; - -use crate::disass::Disassembler; - -#[derive(Debug)] -pub struct Debugger { - cpu: cpu::Core, - sysbus: SysBus, - running: bool, - breakpoints: Vec, -} - - -#[derive(Debug, PartialEq)] -pub enum DebuggerError { - ParsingError(String), - CpuError(cpu::CpuError), - InvalidCommand(String) -} - -impl From for DebuggerError { - fn from(e: cpu::CpuError) -> DebuggerError { - DebuggerError::CpuError(e) - } -} - -impl From> for DebuggerError { - fn from(e: nom::Err<(&str, nom::error::ErrorKind)>) -> DebuggerError { - DebuggerError::ParsingError("parsing of command failed".to_string()) - } -} - -type DebuggerResult = Result; - -#[derive(Debug, PartialEq)] -enum DebuggerCommand { - Info, - SingleStep, - Continue, - HexDump(u32, usize), - Disass { addr: u32, num_opcodes: usize }, - AddBreakpoint(u32), - ListBreakpoints, - Reset, - Quit, - Nop, -} -use DebuggerCommand::*; - -fn from_hex(input: &str) -> Result { - u32::from_str_radix(input, 16) -} - -fn from_dec(input: &str) -> Result { - u32::from_str_radix(input, 10) -} - -fn whitespace(input: &str) -> IResult<&str, ()> { - let (input, _) = take_while1(char::is_whitespace)(input)?; - Ok((input, ())) -} - -fn parse_hex_num(input: &str) -> IResult<&str, u32> { - let (input, _) = tag("0x")(input)?; - map_res(take_while_m_n(1, 8, |c: char| c.is_digit(16)), from_hex)(input) -} - -fn parse_num(input: &str) -> IResult<&str, u32> { - map_res(take_while1(|c: char| c.is_digit(10)), from_dec)(input) -} - -fn parse_word(input: &str) -> IResult<&str, &str> { - take_till(char::is_whitespace)(input) -} - -fn parse_debugger_command(input: &str) -> DebuggerResult { - // TODO this code is shit! - let (input, command_name) = parse_word(input)?; - match command_name { - "i" | "info" => Ok(Info), - "s" | "step" => Ok(SingleStep), - "c" | "continue" => Ok(Continue), - "xxd" => { - let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?; - let (input, addr) = parse_hex_num(input)?; - let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?; - let (_, nbytes) = parse_num(input)?; - let nbytes = nbytes as usize; - Ok(HexDump(addr, nbytes)) - } - "d" | "disass" => { - let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?; - let (input, addr) = parse_hex_num(input)?; - let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?; - let (_, num_opcodes) = parse_num(input)?; - let num_opcodes = num_opcodes as usize; - Ok(Disass{ addr, num_opcodes }) - } - "b" | "break" => { - let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?; - let (_, addr) = parse_hex_num(input)?; - Ok(AddBreakpoint(addr)) - } - "bl" => Ok(ListBreakpoints), - "q" | "quit" => Ok(Quit), - "r" | "reset" => Ok(Reset), - "" => Ok(Nop), - _ => Err(DebuggerError::InvalidCommand(command_name.to_string())) - } -} - -impl Debugger { - pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger { - Debugger { - cpu: cpu, - sysbus: sysbus, - breakpoints: Vec::new(), - running: false, - } - } - - fn is_breakpoint_reached(&self) -> bool { - let pc = self.cpu.pc; - for b in &self.breakpoints { - if *b == pc { - return true; - } - } - - false - } - - fn command(&mut self, cmd: DebuggerCommand) { - match cmd { - Nop => (), - Info => { - println!("cpu info: {:#x?}", self.cpu) - } - SingleStep => { - ; - match self.cpu.step(&mut self.sysbus) { - Ok(_) => (), - Err(e) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:#x?}", self.cpu); - } - }; - if self.is_breakpoint_reached() { - println!("breakpoint 0x{:08x} reached!", self.cpu.pc) - } - }, - Continue => { - loop { - match self.cpu.step(&mut self.sysbus) { - Ok(_) => (), - Err(e) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:#x?}", self.cpu); - break - } - }; - if self.is_breakpoint_reached() { - println!("breakpoint 0x{:08x} reached!", self.cpu.pc) - } - } - }, - HexDump(addr, nbytes) => { - let bytes = match self.sysbus.get_bytes(addr, nbytes) { - Some(bytes) => bytes, - None => { - println!("requested content out of bounds"); - return - } - }; - hexdump::hexdump(bytes); - } - Disass{ addr, num_opcodes } => { - let bytes = match self.sysbus.get_bytes(addr, 4*num_opcodes) { - 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!"); - self.running = false; - }, - AddBreakpoint(addr) => { - if !self.breakpoints.contains(&addr) { - let new_index = self.breakpoints.len(); - self.breakpoints.push(addr); - println!("added breakpoint [{}] 0x{:08x}", new_index, addr); - } else { - println!("breakpoint already exists!") - } - } - ListBreakpoints => { - println!("breakpoint list:"); - for (i, b) in self.breakpoints.iter().enumerate() { - println!("[{}] 0x{:08x}", i, b) - } - } - Reset => { - println!("resetting cpu..."); - self.cpu.reset(); - println!("cpu is restarted!") - }, - _ => panic!("command {:?} not implemented", cmd) - } - } - pub fn repl(&mut self) -> DebuggerResult<()> { - self.running = true; - let mut rl = Editor::<()>::new(); - while self.running { - let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan())); - match readline { - Ok(line) => { - let command = parse_debugger_command(&line); - match command { - Ok(cmd) => { - self.command(cmd) - } - Err(DebuggerError::InvalidCommand(command)) => { - println!("invalid command: {}", command) - } - Err(DebuggerError::ParsingError(msg)) => { - println!("Parsing error: {:?}", msg) - } - Err(e) => { - return Err(e); - } - } - } - Err(ReadlineError::Interrupted) => { - println!("CTRL-C"); - break; - } - Err(ReadlineError::Eof) => { - println!("CTRL-D"); - break; - } - Err(err) => { - println!("Error: {:?}", err); - break; - } - } - } - Ok(()) - } -} diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs new file mode 100644 index 0000000..d31a27e --- /dev/null +++ b/src/debugger/mod.rs @@ -0,0 +1,260 @@ +use std::convert::TryFrom; + +use rustyline::error::ReadlineError; +use rustyline::Editor; + +use colored::*; + +use hexdump; + +use super::arm7tdmi::cpu; +use super::sysbus::SysBus; + +use crate::disass::Disassembler; + +mod parser; +use parser::{parse_command_line, ParsedLine, Argument}; + +#[derive(Debug)] +pub struct Debugger { + cpu: cpu::Core, + sysbus: SysBus, + running: bool, + breakpoints: Vec, +} + +#[derive(Debug, PartialEq)] +pub enum DebuggerError { + ParsingError(String), + CpuError(cpu::CpuError), + InvalidCommand(String), + InvalidArgument(String) +} + +impl From for DebuggerError { + fn from(e: cpu::CpuError) -> DebuggerError { + DebuggerError::CpuError(e) + } +} + +type DebuggerResult = Result; + +#[derive(Debug, PartialEq)] +pub enum DebuggerCommand { + Info, + SingleStep, + Continue, + HexDump(u32, usize), + Disass { addr: u32, num_opcodes: usize }, + AddBreakpoint(u32), + ListBreakpoints, + Reset, + Quit, +} +use DebuggerCommand::*; + +impl TryFrom for DebuggerCommand { + type Error = DebuggerError; + + fn try_from(value: ParsedLine) -> Result { + match value.command.as_ref() { + "i" | "info" => { + Ok(Info) + }, + "s" | "step" => Ok(SingleStep), + "c" | "continue" => Ok(Continue), + "xxd" => { + let mut args = value.args.into_iter(); + let addr = match args.next() { + Some(Argument::Num(n)) => n, + _ => { + return Err(DebuggerError::InvalidArgument(format!("expected a number"))); + } + }; + let nbytes = match args.next() { + Some(Argument::Num(n)) => n, + _ => { + return Err(DebuggerError::InvalidArgument(format!("expected a number"))); + } + }; + let nbytes = nbytes as usize; + Ok(HexDump(addr, nbytes)) + } + "d" | "disass" => { + let mut args = value.args.into_iter(); + let addr = match args.next() { + Some(Argument::Num(n)) => n, + _ => { + return Err(DebuggerError::InvalidArgument(format!("expected a number"))); + } + }; + let num_opcodes = match args.next() { + Some(Argument::Num(n)) => n, + _ => { + return Err(DebuggerError::InvalidArgument(format!("expected a number"))); + } + }; + let num_opcodes = num_opcodes as usize; + Ok(Disass { addr, num_opcodes }) + } + "b" | "break" => { + let mut args = value.args.into_iter(); + let addr = match args.next() { + Some(Argument::Num(n)) => n, + _ => { + return Err(DebuggerError::InvalidArgument(format!("expected a number"))); + } + }; + Ok(AddBreakpoint(addr)) + } + "bl" => Ok(ListBreakpoints), + "q" | "quit" => Ok(Quit), + "r" | "reset" => Ok(Reset), + _ => Err(DebuggerError::InvalidCommand(value.command)), + } + } +} + +impl Debugger { + pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger { + Debugger { + cpu: cpu, + sysbus: sysbus, + breakpoints: Vec::new(), + running: false, + } + } + + fn is_breakpoint_reached(&self) -> bool { + let pc = self.cpu.pc; + for b in &self.breakpoints { + if *b == pc { + return true; + } + } + + false + } + + fn command(&mut self, cmd: DebuggerCommand) { + match cmd { + Info => println!("cpu info: {:#x?}", self.cpu), + SingleStep => { + match self.cpu.step(&mut self.sysbus) { + Ok(_) => (), + Err(e) => { + println!("{}: {}", "cpu encountered an error".red(), e); + println!("cpu: {:#x?}", self.cpu); + } + }; + if self.is_breakpoint_reached() { + println!("breakpoint 0x{:08x} reached!", self.cpu.pc) + } + } + Continue => loop { + match self.cpu.step(&mut self.sysbus) { + Ok(_) => (), + Err(e) => { + println!("{}: {}", "cpu encountered an error".red(), e); + println!("cpu: {:#x?}", self.cpu); + break; + } + }; + if self.is_breakpoint_reached() { + println!("breakpoint 0x{:08x} reached!", self.cpu.pc) + } + }, + HexDump(addr, nbytes) => { + let bytes = match self.sysbus.get_bytes(addr, nbytes) { + Some(bytes) => bytes, + None => { + println!("requested content out of bounds"); + return; + } + }; + hexdump::hexdump(bytes); + } + Disass { addr, num_opcodes } => { + let bytes = match self.sysbus.get_bytes(addr, 4 * num_opcodes) { + 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!"); + self.running = false; + } + AddBreakpoint(addr) => { + if !self.breakpoints.contains(&addr) { + let new_index = self.breakpoints.len(); + self.breakpoints.push(addr); + println!("added breakpoint [{}] 0x{:08x}", new_index, addr); + } else { + println!("breakpoint already exists!") + } + } + ListBreakpoints => { + println!("breakpoint list:"); + for (i, b) in self.breakpoints.iter().enumerate() { + println!("[{}] 0x{:08x}", i, b) + } + } + Reset => { + println!("resetting cpu..."); + self.cpu.reset(); + println!("cpu is restarted!") + } + _ => panic!("command {:?} not implemented", cmd), + } + } + pub fn repl(&mut self) -> DebuggerResult<()> { + self.running = true; + let mut rl = Editor::<()>::new(); + while self.running { + let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan())); + match readline { + Ok(line) => { + if line.is_empty() { + continue + } + let line = parse_command_line(&line); + match line { + Ok(line) => { + match DebuggerCommand::try_from(line) { + Ok(cmd) => self.command(cmd), + Err(DebuggerError::InvalidCommand(c)) => println!("{}: {:?}", "invalid command".red(), c), + Err(DebuggerError::InvalidArgument(m)) => println!("{}: {}", "invalid argument".red(), m), + _ => () + } + } + Err(DebuggerError::ParsingError(msg)) => { + println!("Parsing error: {}", msg) + } + _ => () + } + } + Err(ReadlineError::Interrupted) => { + println!("CTRL-C"); + break; + } + Err(ReadlineError::Eof) => { + println!("CTRL-D"); + break; + } + Err(err) => { + println!("Error: {:?}", err); + break; + } + } + } + Ok(()) + } +} diff --git a/src/debugger/parser.rs b/src/debugger/parser.rs new file mode 100644 index 0000000..794d3fa --- /dev/null +++ b/src/debugger/parser.rs @@ -0,0 +1,101 @@ +use std::fmt; + +use nom; +use nom::branch::alt; +use nom::bytes::complete::{tag, take_while_m_n}; +use nom::character::complete::{alphanumeric0, digit1, multispace0, multispace1}; +use nom::combinator::{cut, map, map_res}; +use nom::error::{context, convert_error, ParseError, VerboseError}; +use nom::multi::separated_list; +use nom::sequence::{preceded, terminated}; +use nom::IResult; + +use super::{DebuggerError, DebuggerResult}; + +#[derive(Debug, PartialEq, Clone)] +pub enum Argument { + Num(u32), + Boolean(bool), +} + +impl fmt::Display for Argument { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Argument::Num(_) => write!(f, "number"), + Argument::Boolean(_) => write!(f, "boolean"), + } + } +} + +#[derive(Debug)] +pub struct ParsedLine { + pub command: String, + pub args: Vec, +} + +fn parse_u32_hex<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, u32, E> { + let (i, _) = context("hex", tag("0x"))(i)?; + map_res(take_while_m_n(1, 8, |c: char| c.is_digit(16)), |s| { + u32::from_str_radix(s, 16) + })(i) +} + +fn parse_u32<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, u32, E> { + context("u32", map_res(digit1, |s| u32::from_str_radix(s, 10)))(i) +} + +fn parse_num<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { + map(alt((parse_u32_hex, parse_u32)), |n| Argument::Num(n))(i) +} + +fn parse_boolean<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { + context( + "bool", + alt(( + map(tag("true"), |_| Argument::Boolean(true)), + map(tag("false"), |_| Argument::Boolean(false)), + )), + )(i) +} + +fn parse_argument<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { + context("argument", alt((parse_num, parse_boolean)))(i) +} + +fn parse_argument_list<'a, E: ParseError<&'a str>>( + i: &'a str, +) -> IResult<&'a str, Vec, E> { + context( + "argument list", + preceded(multispace0, terminated(separated_list(multispace1, parse_argument), multispace0)) + )(i) +} + +fn parse_command_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { + context("command", preceded(multispace0, cut(alphanumeric0)))(i) +} + +fn parse_line<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, ParsedLine, E> { + let (i, command_name) = parse_command_name(i)?; + println!("command name = {:?}, remaining {:?}", command_name, i); + let (i, arguments) = parse_argument_list(i)?; + println!("command: {} arguments: {:?}", command_name, arguments); + + Ok(( + i, + ParsedLine { + command: command_name.to_string(), + args: arguments, + }, + )) +} + +pub fn parse_command_line(input: &str) -> DebuggerResult { + match parse_line::>(input) { + Ok((_, line)) => Ok(line), + Err(nom::Err::Failure(e)) | Err(nom::Err::Error(e)) => { + Err(DebuggerError::ParsingError(convert_error(input, e))) + } + _ => panic!("unhandled parser error"), + } +} \ No newline at end of file