From 967ccca8ddf9ab8c8e337b53affd86b4befb77a0 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Sat, 29 Jun 2019 01:52:10 +0300 Subject: [PATCH] Mega commit - model CPU pipelining. I except many bugs to arise.. Former-commit-id: bcc6ea57af803f783b0dd548b50956b3ccda2b1a --- Cargo.lock | 1 + Cargo.toml | 6 +- src/arm7tdmi/arm/display.rs | 2 +- src/arm7tdmi/arm/exec.rs | 80 +++++------ src/arm7tdmi/arm/mod.rs | 8 +- src/arm7tdmi/cpu.rs | 280 ++++++++++++++++++------------------ src/arm7tdmi/exception.rs | 2 +- src/arm7tdmi/mod.rs | 119 +++++++++++++++ src/arm7tdmi/psr.rs | 2 +- src/debugger/command.rs | 27 ++-- src/debugger/mod.rs | 23 +-- src/debugger/parser.rs | 2 +- src/disass.rs | 4 +- src/main.rs | 19 ++- 14 files changed, 351 insertions(+), 224 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c359ce..7f19d35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,6 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "rustboyadvance-ng" version = "0.1.0" dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 943de4a..e40ed74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,8 @@ clap = {version = "2.33", features = ["color", "yaml"]} rustyline = "5.0.0" nom = "5.0.0" colored = "1.8" -hexdump = "0.1.0" \ No newline at end of file +ansi_term = "0.11.0" +hexdump = "0.1.0" + +[profile.dev] +opt-level = 0 \ No newline at end of file diff --git a/src/arm7tdmi/arm/display.rs b/src/arm7tdmi/arm/display.rs index b2db557..31f8c68 100644 --- a/src/arm7tdmi/arm/display.rs +++ b/src/arm7tdmi/arm/display.rs @@ -108,7 +108,7 @@ impl ArmInstruction { "b{link}{cond}\t{ofs:#x}", link = if self.link_flag() { "l" } else { "" }, cond = self.cond, - ofs = self.pc.wrapping_add(self.branch_offset() as u32) as u32 + ofs = 8 + self.pc.wrapping_add(self.branch_offset() as u32) as u32 ) } diff --git a/src/arm7tdmi/arm/exec.rs b/src/arm7tdmi/arm/exec.rs index acdcba4..36a4b82 100644 --- a/src/arm7tdmi/arm/exec.rs +++ b/src/arm7tdmi/arm/exec.rs @@ -1,54 +1,32 @@ use crate::bit::BitIndex; -use crate::arm7tdmi; -use arm7tdmi::cpu::{ - Core, CpuError, CpuExecResult, CpuInstruction, CpuPipelineAction, CpuResult, CpuState, - Exception, +use super::super::{ + cpu::{Core, CpuExecResult, CpuPipelineAction}, + exception::Exception, + sysbus::SysBus, + CpuError, CpuInstruction, CpuResult, CpuState, REG_PC, }; -use super::super::sysbus::SysBus; use super::{ ArmCond, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmRegisterShift, ArmShiftType, ArmShiftedValue, }; impl Core { - fn check_arm_cond(&self, cond: ArmCond) -> bool { - use ArmCond::*; - match cond { - Equal => self.cpsr.Z(), - NotEqual => !self.cpsr.Z(), - UnsignedHigherOrSame => self.cpsr.C(), - UnsignedLower => !self.cpsr.C(), - Negative => self.cpsr.N(), - PositiveOrZero => !self.cpsr.N(), - Overflow => self.cpsr.V(), - NoOverflow => !self.cpsr.V(), - UnsignedHigher => self.cpsr.C() && !self.cpsr.Z(), - UnsignedLowerOrSame => !self.cpsr.C() && self.cpsr.Z(), - GreaterOrEqual => self.cpsr.N() == self.cpsr.V(), - LessThan => self.cpsr.N() != self.cpsr.V(), - GreaterThan => !self.cpsr.Z() && (self.cpsr.N() == self.cpsr.V()), - LessThanOrEqual => self.cpsr.Z() || (self.cpsr.N() != self.cpsr.V()), - Always => true, - } - } - pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult { - let action = if self.check_arm_cond(insn.cond) { - match insn.fmt { - ArmInstructionFormat::BX => self.exec_bx(sysbus, insn), - ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn), - ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn), - ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn), - _ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm( - insn, - ))), - } - } else { - Ok(CpuPipelineAction::AdvanceProgramCounter) - }?; - Ok((CpuInstruction::Arm(insn), action)) + if !self.check_arm_cond(insn.cond) { + return Ok(CpuPipelineAction::IncPC); + } + match insn.fmt { + ArmInstructionFormat::BX => self.exec_bx(sysbus, insn), + ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn), + ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn), + ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn), + ArmInstructionFormat::LDR_STR => self.exec_ldr_str(sysbus, insn), + _ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm( + insn, + ))), + } } fn exec_b_bl( @@ -57,13 +35,12 @@ impl Core { insn: ArmInstruction, ) -> CpuResult { if self.verbose && insn.cond != ArmCond::Always { - println!("branch taken!") } 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) + Ok(CpuPipelineAction::Flush) } fn exec_bx( @@ -78,7 +55,8 @@ impl Core { self.cpsr.set_state(CpuState::ARM); } - Ok(CpuPipelineAction::Branch) + self.pc = rn & !1; + Ok(CpuPipelineAction::Flush) } fn exec_swi( @@ -87,7 +65,7 @@ impl Core { _insn: ArmInstruction, ) -> CpuResult { self.exception(Exception::SoftwareInterrupt); - Ok(CpuPipelineAction::Branch) + Ok(CpuPipelineAction::Flush) } fn barrel_shift(val: i32, amount: u32, shift: ArmShiftType) -> i32 { @@ -106,7 +84,7 @@ impl Core { Ok(Core::barrel_shift(val, amount, shift)) } ArmRegisterShift::ShiftRegister(reg, shift) => { - if reg != arm7tdmi::REG_PC { + if reg != REG_PC { Ok(Core::barrel_shift(val, self.get_reg(reg) & 0xff, shift)) } else { Err(CpuError::IllegalInstruction) @@ -189,6 +167,16 @@ impl Core { self.set_reg(insn.rd(), result as u32) } - Ok(CpuPipelineAction::AdvanceProgramCounter) + Ok(CpuPipelineAction::IncPC) + } + + fn exec_ldr_str( + &mut self, + _sysbus: &mut SysBus, + insn: ArmInstruction, + ) -> CpuResult { + let rn = self.get_reg(insn.rn()); + let rd = self.get_reg(insn.rd()); + Ok(CpuPipelineAction::IncPC) } } diff --git a/src/arm7tdmi/arm/mod.rs b/src/arm7tdmi/arm/mod.rs index 32338fc..517d00a 100644 --- a/src/arm7tdmi/arm/mod.rs +++ b/src/arm7tdmi/arm/mod.rs @@ -295,7 +295,7 @@ impl ArmInstruction { } pub fn branch_offset(&self) -> i32 { - (((self.raw.bit_range(0..24) << 8) as i32) >> 6).wrapping_add(8) + (((self.raw.bit_range(0..24) << 8) as i32) >> 6) } pub fn load_flag(&self) -> bool { @@ -451,20 +451,20 @@ mod tests { assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL); assert_eq!(decoded.link_flag(), false); assert_eq!( - (decoded.pc as i32).wrapping_add(decoded.branch_offset()), + (decoded.pc as i32).wrapping_add(decoded.branch_offset()) + 8, 0x30 ); assert_eq!(format!("{}", decoded), "b\t0x30"); } #[test] - fn test_decode_branch_backwards() { + fn test_decode_branch_link_backwards() { // 0x20: bl 0x10 let decoded = ArmInstruction::try_from((0xeb_ff_ff_fa, 0x20)).unwrap(); assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL); assert_eq!(decoded.link_flag(), true); assert_eq!( - (decoded.pc as i32).wrapping_add(decoded.branch_offset()), + (decoded.pc as i32).wrapping_add(decoded.branch_offset()) + 8, 0x10 ); assert_eq!(format!("{}", decoded), "bl\t0x10"); diff --git a/src/arm7tdmi/cpu.rs b/src/arm7tdmi/cpu.rs index 5305b57..da74469 100644 --- a/src/arm7tdmi/cpu.rs +++ b/src/arm7tdmi/cpu.rs @@ -1,135 +1,40 @@ use std::convert::TryFrom; use std::fmt; -use crate::num_traits::FromPrimitive; -use colored::*; +use ansi_term::{Colour, Style}; + +use super::*; -use super::arm::*; pub use super::exception::Exception; use super::psr::RegPSR; use super::reg_string; use super::sysbus::SysBus; -#[derive(Debug, PartialEq)] -pub enum CpuInstruction { - Arm(ArmInstruction), - Thumb, +type Addr = u32; + +#[derive(Debug, Default)] +pub struct PipelineContext { + fetched: Option<(Addr, u32)>, + decoded: Option, } -#[derive(Debug, PartialEq, Primitive, Copy, Clone)] -#[repr(u8)] -pub enum CpuState { - ARM = 0, - THUMB = 1, -} - -impl fmt::Display for CpuState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use CpuState::*; - match self { - ARM => write!(f, "ARM"), - THUMB => write!(f, "THUMB"), - } - } -} - -#[derive(Debug, Primitive, Copy, Clone, PartialEq)] -pub enum CpuMode { - User = 0b10000, - Fiq = 0b10001, - Irq = 0b10010, - Supervisor = 0b10011, - Abort = 0b10111, - Undefined = 0b11011, - System = 0b11111, -} - -impl CpuMode { - pub fn spsr_index(&self) -> Option { - match self { - CpuMode::Fiq => Some(0), - CpuMode::Irq => Some(1), - CpuMode::Supervisor => Some(2), - CpuMode::Abort => Some(3), - CpuMode::Undefined => Some(4), - _ => None, - } +impl PipelineContext { + fn is_flushed(&self) -> bool { + self.fetched.is_none() && self.decoded.is_none() } - pub fn bank_index(&self) -> usize { - match self { - CpuMode::User | CpuMode::System => 0, - CpuMode::Fiq => 1, - CpuMode::Irq => 2, - CpuMode::Supervisor => 3, - CpuMode::Abort => 4, - CpuMode::Undefined => 5, - } + fn is_only_fetched(&self) -> bool { + self.fetched.is_some() && self.decoded.is_none() } -} -impl fmt::Display for CpuMode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use CpuMode::*; - match self { - User => write!(f, "USR"), - Fiq => write!(f, "FIQ"), - Irq => write!(f, "IRQ"), - Supervisor => write!(f, "SVC"), - Abort => write!(f, "ABT"), - Undefined => write!(f, "UND"), - System => write!(f, "SYS"), - } + fn is_ready_to_execute(&self) -> bool { + self.fetched.is_some() && self.decoded.is_some() } } -#[derive(Debug, PartialEq)] -pub enum CpuError { - ArmDecodeError(ArmDecodeError), - IllegalInstruction, - UnimplementedCpuInstruction(CpuInstruction), -} - -impl From for CpuError { - fn from(e: ArmDecodeError) -> CpuError { - CpuError::ArmDecodeError(e) - } -} - -impl fmt::Display for CpuError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - CpuError::ArmDecodeError(e) => write!( - f, - "arm decoding error at address @0x{:08x} (instruction 0x{:08x}): {:?}", - e.addr, e.insn, e.kind - ), - CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn)) => write!( - f, - "unimplemented instruction: 0x{:08x}:\t0x{:08x}\t{}", - insn.pc, insn.raw, insn - ), - CpuError::IllegalInstruction => write!( - f, - "illegal instruction" - ), - e => write!(f, "error: {:#x?}", e), - } - } -} - -pub type CpuResult = Result; - -pub struct CpuModeContext { - // r8-r14 - banked_gpr: [u32; 7], - spsr: u32, -} - #[derive(Debug, Default)] pub struct Core { pub pc: u32, - // r0-r7 pub gpr: [u32; 15], // r13 and r14 are banked for all modes. System&User mode share them pub gpr_banked_r13: [u32; 6], @@ -141,16 +46,22 @@ pub struct Core { pub cpsr: RegPSR, pub spsr: [RegPSR; 5], + pub pipeline: PipelineContext, + cycles: usize, + + // store the gpr before executing an instruction to show diff in the Display impl + gpr_previous: [u32; 15], + pub verbose: bool, } #[derive(Debug, PartialEq)] pub enum CpuPipelineAction { - AdvanceProgramCounter, - Branch, + IncPC, + Flush, } -pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>; +pub type CpuExecResult = CpuResult; impl Core { pub fn new() -> Core { @@ -179,6 +90,10 @@ impl Core { } } + pub fn get_registers(&self) -> [u32; 15] { + self.gpr.clone() + } + fn map_banked_registers(&mut self, curr_mode: CpuMode, new_mode: CpuMode) { let next_index = new_mode.bank_index(); let curr_index = curr_mode.bank_index(); @@ -217,7 +132,7 @@ impl Core { self.exception(Exception::Reset); } - fn word_size(&self) -> usize { + pub fn word_size(&self) -> usize { match self.cpsr.state() { CpuState::ARM => 4, CpuState::THUMB => 2, @@ -228,44 +143,133 @@ impl Core { self.pc = self.pc.wrapping_add(self.word_size() as u32) } - fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuExecResult { - // fetch - let insn = sysbus.read_32(self.pc); - // decode - let insn = ArmInstruction::try_from((insn, self.pc))?; - // exec - self.exec_arm(sysbus, insn) + pub fn check_arm_cond(&self, cond: ArmCond) -> bool { + use ArmCond::*; + match cond { + Equal => self.cpsr.Z(), + NotEqual => !self.cpsr.Z(), + UnsignedHigherOrSame => self.cpsr.C(), + UnsignedLower => !self.cpsr.C(), + Negative => self.cpsr.N(), + PositiveOrZero => !self.cpsr.N(), + Overflow => self.cpsr.V(), + NoOverflow => !self.cpsr.V(), + UnsignedHigher => self.cpsr.C() && !self.cpsr.Z(), + UnsignedLowerOrSame => !self.cpsr.C() && self.cpsr.Z(), + GreaterOrEqual => self.cpsr.N() == self.cpsr.V(), + LessThan => self.cpsr.N() != self.cpsr.V(), + GreaterThan => !self.cpsr.Z() && (self.cpsr.N() == self.cpsr.V()), + LessThanOrEqual => self.cpsr.Z() || (self.cpsr.N() != self.cpsr.V()), + Always => true, + } } - pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> { - let (executed_insn, pipeline_action) = match self.cpsr.state() { + fn step_arm( + &mut self, + sysbus: &mut SysBus, + ) -> CpuResult<(Option, CpuPipelineAction)> { + // fetch + let new_fetched = sysbus.read_32(self.pc); + + // decode + let new_decoded = match self.pipeline.fetched { + Some((addr, i)) => Some(ArmInstruction::try_from((i, addr)).unwrap()), + None => None, + }; + // exec + let result = match self.pipeline.decoded { + Some(d) => { + self.gpr_previous = self.get_registers(); + let action = self.exec_arm(sysbus, d)?; + Ok((Some(d), action)) + } + None => Ok((None, CpuPipelineAction::IncPC)), + }; + + self.pipeline.fetched = Some((self.pc, new_fetched)); + if let Some(d) = new_decoded { + self.pipeline.decoded = Some(d); + } + + result + } + + /// Perform a pipeline step + /// If an instruction was executed in this step, return it. + pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult> { + if self.cycles > 0 { + self.cycles -= 1; + return Ok(None); + } + let (executed_instruction, pipeline_action) = match self.cpsr.state() { CpuState::ARM => self.step_arm(sysbus), CpuState::THUMB => unimplemented!("thumb not implemented :("), }?; - if self.verbose { - if let CpuInstruction::Arm(insn) = executed_insn { - println!("{:8x}:\t{:08x} \t{}", insn.pc, insn.raw, insn) + match pipeline_action { + CpuPipelineAction::IncPC => self.advance_pc(), + CpuPipelineAction::Flush => { + self.pipeline.fetched = None; + self.pipeline.decoded = None; } } - if CpuPipelineAction::AdvanceProgramCounter == pipeline_action { - self.advance_pc(); - } + Ok(executed_instruction) + } - Ok(()) + /// Get's the address of the next instruction that is going to be executed + pub fn get_next_pc(&self) -> Addr { + if self.pipeline.is_flushed() { + self.pc + } else if self.pipeline.is_only_fetched() { + self.pipeline.fetched.unwrap().0 + } else if self.pipeline.is_ready_to_execute() { + self.pipeline.decoded.unwrap().pc + } else { + unreachable!() + } + } + + /// A step that returns only once an instruction was executed. + /// Returns the address of PC before executing an instruction, + /// and the address of the next instruction to be executed; + pub fn step_debugger(&mut self, sysbus: &mut SysBus) -> CpuResult { + loop { + if let Some(i) = self.step(sysbus)? { + return Ok(i); + } + } } } impl fmt::Display for Core { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "ARM7TDMI Core")?; - writeln!(f, "REGISTERS:")?; - for i in 0..16 { - let mut reg = reg_string(i).to_string(); - reg.make_ascii_uppercase(); - writeln!(f, "\t{}\t= 0x{:08x}", reg.bright_yellow(), self.get_reg(i))?; + writeln!(f, "ARM7TDMI Core Status:")?; + writeln!(f, "\tCPSR: {}", self.cpsr)?; + writeln!(f, "\tGeneral Purpose Registers:")?; + let reg_normal_style = Style::new().bold(); + let reg_dirty_style = Colour::Green.bold().on(Colour::Yellow); + let gpr = self.get_registers(); + for i in 0..15 { + let mut reg_name = reg_string(i).to_string(); + reg_name.make_ascii_uppercase(); + + let style = if gpr[i] != self.gpr_previous[i] { + ®_dirty_style + } else { + ®_normal_style + }; + + let entry = format!("\t{}\t= 0x{:08x}", reg_name, gpr[i]); + + write!( + f, + "{}{}", + style.paint(entry), + if (i + 1) % 4 == 0 { "\n" } else { "" } + )?; } - write!(f, "CPSR: {}", self.cpsr) + let pc = format!("\tPC\t= 0x{:08x}", self.pc); + writeln!(f, "{}", reg_normal_style.paint(pc)) } } diff --git a/src/arm7tdmi/exception.rs b/src/arm7tdmi/exception.rs index 4c913a0..29e0ed8 100644 --- a/src/arm7tdmi/exception.rs +++ b/src/arm7tdmi/exception.rs @@ -1,4 +1,4 @@ -use super::cpu::{Core, CpuMode, CpuState}; +use super::{cpu::Core, CpuMode, CpuState}; use colored::*; diff --git a/src/arm7tdmi/mod.rs b/src/arm7tdmi/mod.rs index cb8e9f5..2019f68 100644 --- a/src/arm7tdmi/mod.rs +++ b/src/arm7tdmi/mod.rs @@ -1,4 +1,10 @@ +use std::fmt; + +use crate::num_traits::FromPrimitive; + pub mod arm; +use arm::*; + pub mod cpu; mod exception; mod psr; @@ -17,6 +23,119 @@ pub fn reg_string(reg: usize) -> &'static str { reg_names[reg] } +#[derive(Debug, PartialEq)] +pub enum CpuInstruction { + Arm(ArmInstruction), + Thumb, +} + +#[derive(Debug, PartialEq, Primitive, Copy, Clone)] +#[repr(u8)] +pub enum CpuState { + ARM = 0, + THUMB = 1, +} + +impl fmt::Display for CpuState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use CpuState::*; + match self { + ARM => write!(f, "ARM"), + THUMB => write!(f, "THUMB"), + } + } +} + +#[derive(Debug, Primitive, Copy, Clone, PartialEq)] +pub enum CpuMode { + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, + Supervisor = 0b10011, + Abort = 0b10111, + Undefined = 0b11011, + System = 0b11111, +} + +impl CpuMode { + pub fn spsr_index(&self) -> Option { + match self { + CpuMode::Fiq => Some(0), + CpuMode::Irq => Some(1), + CpuMode::Supervisor => Some(2), + CpuMode::Abort => Some(3), + CpuMode::Undefined => Some(4), + _ => None, + } + } + + pub fn bank_index(&self) -> usize { + match self { + CpuMode::User | CpuMode::System => 0, + CpuMode::Fiq => 1, + CpuMode::Irq => 2, + CpuMode::Supervisor => 3, + CpuMode::Abort => 4, + CpuMode::Undefined => 5, + } + } +} + +impl fmt::Display for CpuMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use CpuMode::*; + match self { + User => write!(f, "USR"), + Fiq => write!(f, "FIQ"), + Irq => write!(f, "IRQ"), + Supervisor => write!(f, "SVC"), + Abort => write!(f, "ABT"), + Undefined => write!(f, "UND"), + System => write!(f, "SYS"), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum CpuError { + ArmDecodeError(ArmDecodeError), + IllegalInstruction, + UnimplementedCpuInstruction(CpuInstruction), +} + +impl From for CpuError { + fn from(e: ArmDecodeError) -> CpuError { + CpuError::ArmDecodeError(e) + } +} + +impl fmt::Display for CpuError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + CpuError::ArmDecodeError(e) => write!( + f, + "arm decoding error at address @0x{:08x} (instruction 0x{:08x}): {:?}", + e.addr, e.insn, e.kind + ), + CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn)) => write!( + f, + "unimplemented instruction: 0x{:08x}:\t0x{:08x}\t{}", + insn.pc, insn.raw, insn + ), + CpuError::IllegalInstruction => write!(f, "illegal instruction"), + e => write!(f, "error: {:#x?}", e), + } + } +} + +pub type CpuResult = Result; + +pub struct CpuModeContext { + // r8-r14 + banked_gpr: [u32; 7], + spsr: u32, +} + #[cfg(test)] mod tests { #[test] diff --git a/src/arm7tdmi/psr.rs b/src/arm7tdmi/psr.rs index 6d7b79a..64abdcc 100644 --- a/src/arm7tdmi/psr.rs +++ b/src/arm7tdmi/psr.rs @@ -4,7 +4,7 @@ use std::fmt; use crate::bit::BitIndex; use crate::num_traits::FromPrimitive; -use super::cpu::{CpuMode, CpuState}; +use super::{CpuMode, CpuState}; use colored::*; diff --git a/src/debugger/command.rs b/src/debugger/command.rs index e370086..6969f43 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -1,13 +1,16 @@ +use crate::arm7tdmi::{reg_string, REG_PC}; use crate::debugger::Debugger; use crate::disass::Disassembler; +use ansi_term::Colour; + use colored::*; use hexdump; #[derive(Debug, PartialEq, Clone)] pub enum Command { Info, - SingleStep, + SingleStep(bool), Continue, HexDump(u32, usize), Disass(u32, usize), @@ -24,16 +27,24 @@ impl Command { use Command::*; match *self { Info => println!("{}", debugger.cpu), - SingleStep => { + SingleStep(_cycle) => { if let Some(bp) = debugger.check_breakpoint() { println!("hit breakpoint #0x{:08x}!", bp); debugger.delete_breakpoint(bp); } else { - match debugger.cpu.step(&mut debugger.sysbus) { - Ok(_) => (), + 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) + println!("cpu: {:x?}", debugger.cpu) } } } @@ -44,11 +55,11 @@ impl Command { debugger.delete_breakpoint(bp); break; } - match debugger.cpu.step(&mut debugger.sysbus) { + match debugger.cpu.step_debugger(&mut debugger.sysbus) { Ok(_) => (), Err(e) => { println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:#x?}", debugger.cpu); + println!("cpu: {:x?}", debugger.cpu); break; } }; @@ -72,7 +83,7 @@ impl Command { } }; let disass = Disassembler::new(addr, bytes); - for line in disass { + for (_, line) in disass { println!("{}", line) } } diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index 2f9e616..29f19b2 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -3,7 +3,7 @@ use rustyline::Editor; use colored::*; -use super::arm7tdmi::cpu; +use super::arm7tdmi; use super::sysbus::SysBus; mod parser; @@ -15,14 +15,14 @@ use command::Command; #[derive(Debug, PartialEq)] pub enum DebuggerError { ParsingError(String), - CpuError(cpu::CpuError), + CpuError(arm7tdmi::CpuError), InvalidCommand(String), InvalidArgument(String), InvalidCommandFormat(String), } -impl From for DebuggerError { - fn from(e: cpu::CpuError) -> DebuggerError { +impl From for DebuggerError { + fn from(e: arm7tdmi::CpuError) -> DebuggerError { DebuggerError::CpuError(e) } } @@ -31,7 +31,7 @@ type DebuggerResult = Result; #[derive(Debug)] pub struct Debugger { - pub cpu: cpu::Core, + pub cpu: arm7tdmi::cpu::Core, pub sysbus: SysBus, running: bool, breakpoints: Vec, @@ -39,7 +39,7 @@ pub struct Debugger { } impl Debugger { - pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger { + pub fn new(cpu: arm7tdmi::cpu::Core, sysbus: SysBus) -> Debugger { Debugger { cpu: cpu, sysbus: sysbus, @@ -50,10 +50,10 @@ impl Debugger { } pub fn check_breakpoint(&self) -> Option { - let pc = self.cpu.get_reg(15); + let next_pc = self.cpu.get_next_pc(); for bp in &self.breakpoints { - if *bp == pc { - return Some(pc); + if *bp == next_pc { + return Some(next_pc); } } @@ -123,7 +123,8 @@ impl Debugger { match command.as_ref() { "i" | "info" => Ok(Command::Info), - "s" | "step" => Ok(Command::SingleStep), + "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() { @@ -255,7 +256,7 @@ impl Debugger { let mut rl = Editor::<()>::new(); rl.load_history(".rustboyadvance_history"); while self.running { - let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".bold().cyan())); + let readline = rl.readline(&format!("({}) ᐅ ", "rustboyadvance-dbg".bold().cyan())); match readline { Ok(line) => { if line.is_empty() { diff --git a/src/debugger/parser.rs b/src/debugger/parser.rs index 3a028b5..bf02853 100644 --- a/src/debugger/parser.rs +++ b/src/debugger/parser.rs @@ -4,7 +4,7 @@ use nom; use nom::branch::alt; use nom::bytes::complete::{tag, take_while_m_n}; use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1}; -use nom::combinator::{map, map_res, cut}; +use nom::combinator::{cut, map, map_res}; use nom::error::{context, convert_error, ParseError, VerboseError}; use nom::multi::separated_list; use nom::sequence::{preceded, separated_pair, terminated, tuple}; diff --git a/src/disass.rs b/src/disass.rs index c5dcf69..c3107dd 100644 --- a/src/disass.rs +++ b/src/disass.rs @@ -28,7 +28,7 @@ impl<'a> Seek for Disassembler<'a> { } impl<'a> Iterator for Disassembler<'a> { - type Item = String; + type Item = (u32, String); fn next(&mut self) -> Option { let mut line = String::new(); @@ -50,6 +50,6 @@ impl<'a> Iterator for Disassembler<'a> { Err(_) => line.push_str(""), }; - Some(line) + Some((addr, line)) } } diff --git a/src/main.rs b/src/main.rs index f0c3e5b..5d6edc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,13 +19,12 @@ extern crate rustyline; extern crate nom; extern crate colored; // not needed in Rust 2018 +extern crate ansi_term; pub mod sysbus; use sysbus::SysBus; mod arm7tdmi; -use arm7tdmi::arm; -use arm7tdmi::cpu; mod debugger; use debugger::{Debugger, DebuggerError}; @@ -36,8 +35,8 @@ use disass::Disassembler; #[derive(Debug)] pub enum GBAError { IO(io::Error), - ArmDecodeError(arm::ArmDecodeError), - CpuError(cpu::CpuError), + ArmDecodeError(arm7tdmi::arm::ArmDecodeError), + CpuError(arm7tdmi::CpuError), DebuggerError(DebuggerError) } @@ -49,14 +48,14 @@ impl From for GBAError { } } -impl From for GBAError { - fn from(err: arm::ArmDecodeError) -> GBAError { +impl From for GBAError { + fn from(err: arm7tdmi::arm::ArmDecodeError) -> GBAError { GBAError::ArmDecodeError(err) } } -impl From for GBAError { - fn from(err: cpu::CpuError) -> GBAError { +impl From for GBAError { + fn from(err: arm7tdmi::CpuError) -> GBAError { GBAError::CpuError(err) } } @@ -80,7 +79,7 @@ fn run_disass(matches: &ArgMatches) -> GBAResult<()> { let bin = read_bin_file(&input)?; let disassembler = Disassembler::new(0, &bin); - for line in disassembler { + for (_, line) in disassembler { println!("{}", line) } Ok(()) @@ -92,7 +91,7 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> { let bios_bin = read_bin_file(gba_bios_path)?; let sysbus = SysBus::new(bios_bin); - let mut core = cpu::Core::new(); + let mut core = arm7tdmi::cpu::Core::new(); core.reset(); core.set_verbose(true); let mut debugger = Debugger::new(core, sysbus);