debugger: Add Deref expression.
i.e: r5 = *r6 r5 = *(u8*)r6 r5 = *(u16*)0x08000000 Former-commit-id: 962dade8e3c0b9f291115285137cf51b0abde266
This commit is contained in:
parent
645e71ac40
commit
05fb40c17c
|
@ -6,6 +6,7 @@ use arm::{ArmDecodeError, ArmInstruction};
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub use cpu::*;
|
pub use cpu::*;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
|
pub use bus::Bus;
|
||||||
mod exception;
|
mod exception;
|
||||||
mod psr;
|
mod psr;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::arm7tdmi::bus::Bus;
|
use crate::arm7tdmi::bus::Bus;
|
||||||
use crate::arm7tdmi::{reg_string, REG_PC};
|
use crate::arm7tdmi::{reg_string, Addr, REG_PC};
|
||||||
use crate::debugger::Debugger;
|
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
|
|
||||||
|
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
@ -111,3 +112,102 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debugger {
|
||||||
|
pub fn eval_command(&self, command: Value, args: Vec<Value>) -> DebuggerResult<Command> {
|
||||||
|
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 <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]",
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
"bl" => Ok(Command::ListBreakpoints),
|
||||||
|
"q" | "quit" => Ok(Command::Quit),
|
||||||
|
"r" | "reset" => Ok(Command::Reset),
|
||||||
|
_ => Err(DebuggerError::InvalidCommand(command)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ use rustyline::Editor;
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
use super::arm7tdmi;
|
use super::arm7tdmi;
|
||||||
|
use super::arm7tdmi::{Addr, Bus};
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use parser::{parse_expr, Expr, Value};
|
use parser::{parse_expr, DerefType, Expr, Value};
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
use command::Command;
|
use command::Command;
|
||||||
|
@ -99,7 +100,7 @@ impl Debugger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn val_address(&self, arg: &Value) -> DebuggerResult<u32> {
|
fn val_address(&self, arg: &Value) -> DebuggerResult<Addr> {
|
||||||
match arg {
|
match arg {
|
||||||
Value::Num(n) => Ok(*n),
|
Value::Num(n) => Ok(*n),
|
||||||
Value::Name(reg) => {
|
Value::Name(reg) => {
|
||||||
|
@ -113,107 +114,19 @@ impl Debugger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_command(&self, command: Value, args: Vec<Value>) -> DebuggerResult<Command> {
|
|
||||||
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 <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]",
|
|
||||||
))),
|
|
||||||
},
|
|
||||||
"bl" => Ok(Command::ListBreakpoints),
|
|
||||||
"q" | "quit" => Ok(Command::Quit),
|
|
||||||
"r" | "reset" => Ok(Command::Reset),
|
|
||||||
_ => Err(DebuggerError::InvalidCommand(command)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_assignment(&mut self, lvalue: Value, rvalue: Value) -> DebuggerResult<()> {
|
fn eval_assignment(&mut self, lvalue: Value, rvalue: Value) -> DebuggerResult<()> {
|
||||||
let lvalue = self.val_reg(&lvalue)?;
|
let lvalue = self.val_reg(&lvalue)?;
|
||||||
let rvalue = self.val_address(&rvalue)?;
|
let rvalue = match rvalue {
|
||||||
|
Value::Deref(addr_value, deref_type) => {
|
||||||
|
let addr = self.val_address(&addr_value)?;
|
||||||
|
match deref_type {
|
||||||
|
DerefType::Word => self.sysbus.read_32(addr),
|
||||||
|
DerefType::HalfWord => self.sysbus.read_16(addr) as u32,
|
||||||
|
DerefType::Byte => self.sysbus.read_8(addr) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.val_address(&rvalue)?,
|
||||||
|
};
|
||||||
self.cpu.set_reg(lvalue, rvalue);
|
self.cpu.set_reg(lvalue, rvalue);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::arm7tdmi::Addr;
|
||||||
|
|
||||||
use nom;
|
use nom;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::{tag, take_while_m_n};
|
use nom::bytes::complete::{tag, take_while_m_n};
|
||||||
use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1};
|
use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1};
|
||||||
use nom::combinator::{cut, map, map_res};
|
use nom::combinator::{cut, map, map_res, opt};
|
||||||
use nom::error::{context, convert_error, ParseError, VerboseError};
|
use nom::error::{context, convert_error, ParseError, VerboseError};
|
||||||
use nom::multi::separated_list;
|
use nom::multi::separated_list;
|
||||||
use nom::sequence::{preceded, separated_pair, terminated, tuple};
|
use nom::sequence::{delimited, preceded, separated_pair, terminated, tuple};
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
|
||||||
use super::{DebuggerError, DebuggerResult};
|
use super::{DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum DerefType {
|
||||||
|
Word,
|
||||||
|
HalfWord,
|
||||||
|
Byte,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Num(u32),
|
Num(u32),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Name(String),
|
Name(String),
|
||||||
|
Deref(Box<Value>, DerefType),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -57,8 +67,42 @@ fn parse_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value,
|
||||||
map(alphanumeric1, |s: &str| Value::Name(String::from(s)))(i)
|
map(alphanumeric1, |s: &str| Value::Name(String::from(s)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_deref_type<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, DerefType, E> {
|
||||||
|
delimited(
|
||||||
|
char('('),
|
||||||
|
alt((
|
||||||
|
map(tag("u32*"), |_| DerefType::Word),
|
||||||
|
map(tag("u16*"), |_| DerefType::HalfWord),
|
||||||
|
map(tag("u8*"), |_| DerefType::Byte),
|
||||||
|
)),
|
||||||
|
char(')'),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_deref<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
||||||
|
context(
|
||||||
|
"deref",
|
||||||
|
preceded(
|
||||||
|
char('*'),
|
||||||
|
cut(map(
|
||||||
|
tuple((
|
||||||
|
map(opt(parse_deref_type), |t| match t {
|
||||||
|
Some(t) => t,
|
||||||
|
None => DerefType::Word,
|
||||||
|
}),
|
||||||
|
alt((parse_num, parse_name)),
|
||||||
|
)),
|
||||||
|
|(t, v)| Value::Deref(Box::new(v), t),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
fn parse_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
||||||
context("argument", alt((parse_boolean, parse_num, parse_name)))(i)
|
context(
|
||||||
|
"argument",
|
||||||
|
alt((parse_boolean, parse_deref, parse_num, parse_name)),
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> {
|
fn parse_command<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> {
|
||||||
|
@ -165,4 +209,21 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_deref() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_deref::<VerboseError<&str>>("*(u16*)0x1234"),
|
||||||
|
Ok((
|
||||||
|
"",
|
||||||
|
Value::Deref(Box::new(Value::Num(0x1234)), DerefType::HalfWord)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_deref::<VerboseError<&str>>("*r10"),
|
||||||
|
Ok((
|
||||||
|
"",
|
||||||
|
Value::Deref(Box::new(Value::Name("r10".to_string())), DerefType::Word)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue