diff --git a/src/arm7tdmi/arm/exec.rs b/src/arm7tdmi/arm/exec.rs new file mode 100644 index 0000000..d90a7f0 --- /dev/null +++ b/src/arm7tdmi/arm/exec.rs @@ -0,0 +1,19 @@ +use super::super::cpu::{Core, CpuPipelineAction, CpuError, CpuInstruction, CpuExecResult}; +use super::super::sysbus::SysBus; +use super::{ArmInstruction, ArmInstructionFormat}; + +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))), + } + } +} \ No newline at end of file diff --git a/src/arm7tdmi/arm/mod.rs b/src/arm7tdmi/arm/mod.rs index 4669f2c..0626665 100644 --- a/src/arm7tdmi/arm/mod.rs +++ b/src/arm7tdmi/arm/mod.rs @@ -1,4 +1,5 @@ pub mod display; +pub mod exec; use crate::bit::BitIndex; use crate::num_traits::FromPrimitive; diff --git a/src/arm7tdmi/cpu.rs b/src/arm7tdmi/cpu.rs index ea1b8fe..47ea638 100644 --- a/src/arm7tdmi/cpu.rs +++ b/src/arm7tdmi/cpu.rs @@ -1,14 +1,23 @@ use std::convert::TryFrom; +use std::fmt; use crate::num_traits::FromPrimitive; +use super::arm::exec; use super::arm::*; use super::sysbus::SysBus; +#[derive(Debug, PartialEq)] +pub enum CpuInstruction { + Arm(ArmInstruction), + Thumb, +} + #[derive(Debug, PartialEq)] pub enum CpuError { ArmDecodeError(ArmDecodeError), - IllegalInstruction, + IllegalInstruction(CpuInstruction), + UnimplementedCpuInstruction(CpuInstruction), } impl From for CpuError { @@ -17,6 +26,29 @@ impl From for CpuError { } } +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(CpuInstruction::Arm(insn)) => write!( + f, + "illegal instruction at address @0x{:08x} (0x{:08x})", + insn.pc, insn.raw + ), + e => write!(f, "error: {:#x?}", e) + } + } +} + pub type CpuResult = Result; #[derive(Debug, PartialEq)] @@ -45,15 +77,24 @@ pub struct CpuModeContext { #[derive(Debug)] pub struct Core { - pc: u32, + pub pc: u32, // r0-r7 gpr: [u32; 8], cpsr: u32, mode: CpuMode, state: CpuState, + verbose: bool } +#[derive(Debug, PartialEq)] +pub enum CpuPipelineAction { + AdvancePc, + Branch, +} + +pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>; + impl Core { pub fn new() -> Core { Core { @@ -62,9 +103,14 @@ impl Core { cpsr: 0, mode: CpuMode::System, state: CpuState::ARM, + verbose: false, } } + pub fn set_verbose(&mut self, v: bool) { + self.verbose = v; + } + pub fn get_reg(&self, reg_num: usize) -> u32 { match reg_num { 0...7 => self.gpr[reg_num], @@ -100,22 +146,30 @@ impl Core { self.pc = self.pc.wrapping_add(self.word_size() as u32) } - fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuResult<()> { + 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))?; - - Ok(()) + // exec + self.exec_arm(sysbus, insn) } pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> { - match self.state { - CpuState::ARM => self.step_arm(sysbus)?, + let (executed_insn, pipeline_action) = match self.state { + CpuState::ARM => self.step_arm(sysbus), CpuState::THUMB => unimplemented!("thumb not implemented :("), - }; + }?; - self.advance_pc(); + if self.verbose { + if let CpuInstruction::Arm(insn) = executed_insn { + println!("{:8x}:\t{:08x} \t{}", insn.pc, insn.raw, insn) + } + } + + if CpuPipelineAction::AdvancePc == pipeline_action { + self.advance_pc(); + } Ok(()) } diff --git a/src/debugger.rs b/src/debugger.rs index 0aa26f8..9e303c0 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -19,6 +19,7 @@ use super::sysbus::SysBus; pub struct Debugger { cpu: cpu::Core, sysbus: SysBus, + running: bool, breakpoints: Vec, } @@ -118,49 +119,93 @@ impl 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) + } + } + } + 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(); - loop { + while self.running { let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan())); match readline { Ok(line) => { let command = parse_debugger_command(&line); match command { - Ok(Nop) => (), - Ok(Info) => { - println!("cpu info: {:#?}", self.cpu) - } - Ok(SingleStep) => { - println!("single step:"); - self.cpu.step(&mut self.sysbus)?; - () - }, - Ok(Quit) => { - print!("Quitting!"); - break - }, - Ok(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!") - } - } - Ok(ListBreakpoints) => { - println!("breakpoint list:"); - for (i, b) in self.breakpoints.iter().enumerate() { - println!("[{}] 0x{:08x}", i, b) - } - } - Ok(Reset) => { - println!("resetting cpu..."); - self.cpu.reset(); - println!("cpu is restarted!") + Ok(cmd) => { + self.command(cmd) } Err(DebuggerError::InvalidCommand(command)) => { println!("invalid command: {}", command) @@ -171,7 +216,6 @@ impl Debugger { Err(e) => { return Err(e); } - Ok(command) => println!("got command: {:?}", command), } } Err(ReadlineError::Interrupted) => { diff --git a/src/main.rs b/src/main.rs index 0a3a0ba..d7463bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,6 +108,7 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> { let mut sysbus = SysBus::new(bios_bin); let mut core = cpu::Core::new(); + core.set_verbose(true); let mut debugger = Debugger::new(core, sysbus); println!("starting debugger...");