diff --git a/Cargo.lock b/Cargo.lock index 5fbe419..f037d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,15 @@ dependencies = [ "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayvec" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "arrayvec" version = "0.4.10" @@ -202,6 +211,20 @@ name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hexdump" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazy_static" version = "1.3.0" @@ -285,6 +308,11 @@ name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "odds" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.30" @@ -390,6 +418,7 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -611,6 +640,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" +"checksum arrayvec 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)" = "06f59fe10306bb78facd90d28c2038ad23ffaaefa85bac43c8a434cde383334f" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" @@ -633,6 +663,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "850f3f2c33d20c0f96c4485e087dd580ff041d720988ebf4c84a42acf739262b" +"checksum itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lexical-core 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d224b31f370c8dfc0dea1932d9e6bd451e65aef6f5f2318846664c04b42a796" "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" @@ -644,6 +676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" diff --git a/Cargo.toml b/Cargo.toml index 85ff2b5..aa12df7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ bit = "^0.1" clap = {version = "2.33", features = ["color", "yaml"]} rustyline = "5.0.0" nom = "5.0.0" -colored = "1.8" \ No newline at end of file +colored = "1.8" +hexdump = "0.1.0" \ No newline at end of file diff --git a/src/arm7tdmi/arm/exec.rs b/src/arm7tdmi/arm/exec.rs index d90a7f0..83ca229 100644 --- a/src/arm7tdmi/arm/exec.rs +++ b/src/arm7tdmi/arm/exec.rs @@ -1,19 +1,35 @@ -use super::super::cpu::{Core, CpuPipelineAction, CpuError, CpuInstruction, CpuExecResult}; +use super::super::cpu::{Core, CpuState, CpuPipelineAction, CpuError, CpuInstruction, CpuResult, CpuExecResult}; use super::super::sysbus::SysBus; use super::{ArmInstruction, ArmInstructionFormat}; +use crate::bit::BitIndex; + impl Core { - pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult { - match insn.fmt { - ArmInstructionFormat::BX => { - self.pc = self.get_reg(insn.rn()); - Ok((CpuInstruction::Arm(insn), CpuPipelineAction::Branch)) - }, - ArmInstructionFormat::B_BL => { - self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32; - Ok((CpuInstruction::Arm(insn), CpuPipelineAction::Branch)) - } - fmt => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn))), + fn exec_b_bl(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuResult { + if insn.link_flag() { + self.set_reg(14, self.pc & !0b1); } + self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32; + Ok(CpuPipelineAction::Branch) + } + + fn exec_bx(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuResult { + let rn = self.get_reg(insn.rn()); + if rn.bit(0) { + self.set_state(CpuState::THUMB); + } else { + self.set_state(CpuState::ARM); + } + + Ok(CpuPipelineAction::Branch) + } + + pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult { + let action = match insn.fmt { + ArmInstructionFormat::BX => self.exec_bx(sysbus, insn), + ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn), + fmt => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn))), + }?; + Ok((CpuInstruction::Arm(insn), action)) } } \ No newline at end of file diff --git a/src/arm7tdmi/cpu.rs b/src/arm7tdmi/cpu.rs index 47ea638..c949d46 100644 --- a/src/arm7tdmi/cpu.rs +++ b/src/arm7tdmi/cpu.rs @@ -127,6 +127,10 @@ impl Core { } } + pub fn set_state(&mut self, s: CpuState) { + self.state = s; + } + /// Resets the cpu pub fn reset(&mut self) { self.pc = 0; diff --git a/src/debugger.rs b/src/debugger.rs index 9e303c0..d413c58 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -1,20 +1,20 @@ -use std::str::FromStr; - use rustyline::error::ReadlineError; use rustyline::Editor; use colored::*; use nom; -use nom::bytes; use nom::IResult; use nom::bytes::complete::{tag, take_till1, take_till, take_while_m_n, take_while1}; use nom::combinator::map_res; -use super::arm7tdmi::arm; +use hexdump; + use super::arm7tdmi::cpu; use super::sysbus::SysBus; +use crate::disass::Disassembler; + #[derive(Debug)] pub struct Debugger { cpu: cpu::Core, @@ -50,6 +50,7 @@ enum DebuggerCommand { Info, SingleStep, Continue, + HexDump(u32, usize), Disass { addr: u32, num_opcodes: usize }, AddBreakpoint(u32), ListBreakpoints, @@ -92,6 +93,14 @@ fn parse_debugger_command(input: &str) -> DebuggerResult { "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)?; @@ -167,6 +176,30 @@ impl Debugger { 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!"); diff --git a/src/disass.rs b/src/disass.rs new file mode 100644 index 0000000..c5dcf69 --- /dev/null +++ b/src/disass.rs @@ -0,0 +1,55 @@ +use std::convert::TryFrom; + +use std::io::ErrorKind; +use std::io::{Cursor, Seek, SeekFrom}; + +use byteorder::{LittleEndian, ReadBytesExt}; + +use super::arm7tdmi::arm; + +pub struct Disassembler<'a> { + base: u32, + rdr: Cursor<&'a [u8]>, +} + +impl<'a> Disassembler<'a> { + pub fn new(base: u32, bin: &'a [u8]) -> Disassembler { + Disassembler { + base: base, + rdr: Cursor::new(bin), + } + } +} + +impl<'a> Seek for Disassembler<'a> { + fn seek(&mut self, pos: SeekFrom) -> std::io::Result { + self.rdr.seek(pos) + } +} + +impl<'a> Iterator for Disassembler<'a> { + type Item = String; + + fn next(&mut self) -> Option { + let mut line = String::new(); + let value: u32 = match self.rdr.read_u32::() { + Ok(value) => value, + Err(e) => match e.kind() { + ErrorKind::UnexpectedEof => { + return None; + } + _ => panic!("unexpected error"), + }, + }; + + let addr = self.base + (self.rdr.position() - 4) as u32; + line.push_str(&format!("{:8x}:\t{:08x} \t", addr, value)); + + match arm::ArmInstruction::try_from((value, addr)) { + Ok(insn) => line.push_str(&format!("{}", insn)), + Err(_) => line.push_str(""), + }; + + Some(line) + } +} diff --git a/src/main.rs b/src/main.rs index d7463bf..d3e76d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,6 @@ -use std::convert::TryFrom; use std::fs::File; use std::io; use std::io::prelude::*; -use std::io::ErrorKind; #[macro_use] extern crate enum_primitive_derive; @@ -11,7 +9,6 @@ extern crate num_traits; extern crate bit; extern crate byteorder; -use byteorder::{LittleEndian, ReadBytesExt}; #[macro_use] extern crate clap; @@ -33,6 +30,9 @@ use arm7tdmi::cpu; mod debugger; use debugger::{Debugger, DebuggerError}; +mod disass; +use disass::Disassembler; + #[derive(Debug)] pub enum GBAError { IO(io::Error), @@ -79,25 +79,10 @@ fn run_disass(matches: &ArgMatches) -> GBAResult<()> { let input = matches.value_of("INPUT").unwrap(); let bin = read_bin_file(&input)?; - let mut rdr = io::Cursor::new(bin); - loop { - let value: u32 = match rdr.read_u32::() { - Ok(value) => Ok(value), - Err(e) => match e.kind() { - ErrorKind::UnexpectedEof => { - break; - } - _ => Err(e), - }, - }?; - let addr = (rdr.position() - 4) as u32; - print!("{:8x}:\t{:08x} \t", addr, value); - match arm::ArmInstruction::try_from((value, addr)) { - Ok(insn) => println!("{}", insn), - Err(_) => println!(""), - }; + let disassembler = Disassembler::new(0, &bin); + for line in disassembler { + println!("{}", line) } - Ok(()) } diff --git a/src/sysbus.rs b/src/sysbus.rs index 82ab4ef..775f6d5 100644 --- a/src/sysbus.rs +++ b/src/sysbus.rs @@ -31,4 +31,13 @@ impl SysBus { .read_u16::() .unwrap() } + + pub fn get_bytes(&self, addr: u32, size: usize) -> Option<&[u8]> { + let addr = addr as usize; + if addr + size > self.bios_rom.len() { + None + } else { + Some(&self.bios_rom[addr..addr + size]) + } + } }