From a523a37d328ecf1e3fe0651f1f5c2314c6763e9f Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Sat, 4 Apr 2020 15:33:36 +0300 Subject: [PATCH] [WIP] Start working on arm7tdmi dispatch table. Lookup tables are generally faster than matching in most architectures. There is some trouble in decoding some arm data processing instructions so this is non-default feature for now * Crashes on armwrestler but many games seem to work Former-commit-id: 3c06ea344ae0097947d8cd5bd05b1f882c7f743a --- Cargo.toml | 5 + rustboyadvance-sdl2/Cargo.toml | 3 +- src/core/arm7tdmi/arm/display.rs | 31 +++--- src/core/arm7tdmi/arm/exec.rs | 79 ++++++++------ src/core/arm7tdmi/arm/lut.rs | 161 +++++++++++++++++++++++++++++ src/core/arm7tdmi/arm/mod.rs | 25 +++-- src/core/arm7tdmi/cpu.rs | 132 ++++++++++++++++------- src/core/arm7tdmi/thumb/display.rs | 1 + src/core/arm7tdmi/thumb/exec.rs | 90 ++++++++++++---- src/core/arm7tdmi/thumb/lut.rs | 65 ++++++++++++ src/core/arm7tdmi/thumb/mod.rs | 28 +++-- src/debugger/command.rs | 35 ++++--- src/lib.rs | 4 + 13 files changed, 517 insertions(+), 142 deletions(-) create mode 100644 src/core/arm7tdmi/arm/lut.rs create mode 100644 src/core/arm7tdmi/thumb/lut.rs diff --git a/Cargo.toml b/Cargo.toml index 425955b..a62aa9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ ] [dependencies] +lazy_static = {version="1.4.0", optional = true} serde = {version = "1.0.104", features = ["derive"] } bincode = "1.2.1" byteorder = "1" @@ -41,6 +42,10 @@ gdbstub = {git = "https://github.com/daniel5151/gdbstub.git", optional = true, f [features] debugger = ["nom", "rustyline"] gdb = ["gdbstub"] +# Uses lookup tables when executing instructions instead of `match` statements. +# Faster, but consumes more memory. +# Experimental and not thoroughly tested yet +arm7tdmi_dispatch_table = ["lazy_static"] [profile.dev] opt-level = 0 diff --git a/rustboyadvance-sdl2/Cargo.toml b/rustboyadvance-sdl2/Cargo.toml index 6d2d3ee..2619c99 100644 --- a/rustboyadvance-sdl2/Cargo.toml +++ b/rustboyadvance-sdl2/Cargo.toml @@ -21,4 +21,5 @@ winres = "0.1" [features] debugger = ["rustboyadvance-ng/debugger"] -gdb = ["rustboyadvance-ng/gdb"] \ No newline at end of file +gdb = ["rustboyadvance-ng/gdb"] +arm7tdmi_dispatch_table = ["rustboyadvance-ng/arm7tdmi_dispatch_table"] \ No newline at end of file diff --git a/src/core/arm7tdmi/arm/display.rs b/src/core/arm7tdmi/arm/display.rs index 1ee4654..2d60a1f 100644 --- a/src/core/arm7tdmi/arm/display.rs +++ b/src/core/arm7tdmi/arm/display.rs @@ -113,7 +113,7 @@ impl ArmInstruction { f, "b{link}{cond}\t{ofs:#x}", link = if self.link_flag() { "l" } else { "" }, - cond = self.cond, + cond = self.cond(), ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr) ) } @@ -152,7 +152,7 @@ impl ArmInstruction { f, "{opcode}{S}{cond}\t{Rd}, ", opcode = opcode, - cond = self.cond, + cond = self.cond(), S = self.set_cond_mark(), Rd = reg_string(self.rd()) ), @@ -160,14 +160,14 @@ impl ArmInstruction { f, "{opcode}{cond}\t{Rn}, ", opcode = opcode, - cond = self.cond, + cond = self.cond(), Rn = reg_string(self.rn()) ), _ => write!( f, "{opcode}{S}{cond}\t{Rd}, {Rn}, ", opcode = opcode, - cond = self.cond, + cond = self.cond(), S = self.set_cond_mark(), Rd = reg_string(self.rd()), Rn = reg_string(self.rn()) @@ -230,7 +230,7 @@ impl ArmInstruction { "{mnem}{B}{T}{cond}\t{Rd}, ", mnem = if self.load_flag() { "ldr" } else { "str" }, B = if self.transfer_size() == 1 { "b" } else { "" }, - cond = self.cond, + cond = self.cond(), T = if !self.pre_index_flag() && self.write_back_flag() { "t" } else { @@ -249,7 +249,7 @@ impl ArmInstruction { mnem = if self.load_flag() { "ldm" } else { "stm" }, inc_dec = if self.add_offset_flag() { 'i' } else { 'd' }, pre_post = if self.pre_index_flag() { 'b' } else { 'a' }, - cond = self.cond, + cond = self.cond(), Rn = reg_string(self.rn()), auto_inc = if self.write_back_flag() { "!" } else { "" } )?; @@ -283,7 +283,7 @@ impl ArmInstruction { write!( f, "mrs{cond}\t{Rd}, {psr}", - cond = self.cond, + cond = self.cond(), Rd = reg_string(self.rd()), psr = if self.spsr_flag() { "SPSR" } else { "CPSR" } ) @@ -294,7 +294,7 @@ impl ArmInstruction { write!( f, "msr{cond}\t{psr}, {Rm}", - cond = self.cond, + cond = self.cond(), psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, Rm = reg_string(self.rm()), ) @@ -304,7 +304,7 @@ impl ArmInstruction { write!( f, "msr{cond}\t{psr}, ", - cond = self.cond, + cond = self.cond(), psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" }, )?; if let Ok(Some(op)) = self.fmt_operand2(f) { @@ -327,7 +327,7 @@ impl ArmInstruction { f, "mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}", S = self.set_cond_mark(), - cond = self.cond, + cond = self.cond(), Rd = reg_string(self.rd()), Rm = reg_string(self.rm()), Rs = reg_string(self.rs()), @@ -338,7 +338,7 @@ impl ArmInstruction { f, "mul{S}{cond}\t{Rd}, {Rm}, {Rs}", S = self.set_cond_mark(), - cond = self.cond, + cond = self.cond(), Rd = reg_string(self.rd()), Rm = reg_string(self.rm()), Rs = reg_string(self.rs()), @@ -365,7 +365,7 @@ impl ArmInstruction { "mull" }, S = self.set_cond_mark(), - cond = self.cond, + cond = self.cond(), RdLo = reg_string(self.rd_lo()), RdHi = reg_string(self.rd_hi()), Rm = reg_string(self.rm()), @@ -379,7 +379,7 @@ impl ArmInstruction { f, "{mnem}{type}{cond}\t{Rd}, ", mnem = if self.load_flag() { "ldr" } else { "str" }, - cond = self.cond, + cond = self.cond(), type = transfer_type, Rd = reg_string(self.rd()), )?; @@ -393,7 +393,7 @@ impl ArmInstruction { write!( f, "swi{cond}\t#{comm:#x}", - cond = self.cond, + cond = self.cond(), comm = self.swi_comment() ) } @@ -403,7 +403,7 @@ impl ArmInstruction { f, "swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]", B = if self.transfer_size() == 1 { "b" } else { "" }, - cond = self.cond, + cond = self.cond(), Rd = reg_string(self.rd()), Rm = reg_string(self.rm()), Rn = reg_string(self.rn()), @@ -430,6 +430,7 @@ impl fmt::Display for ArmInstruction { LDR_STR_HS_REG => self.fmt_ldr_str_hs(f), SWI => self.fmt_swi(f), SWP => self.fmt_swp(f), + Undefined => write!(f, ""), } } } diff --git a/src/core/arm7tdmi/arm/exec.rs b/src/core/arm7tdmi/arm/exec.rs index b76c09c..08396ec 100644 --- a/src/core/arm7tdmi/arm/exec.rs +++ b/src/core/arm7tdmi/arm/exec.rs @@ -1,7 +1,6 @@ use crate::bit::BitIndex; use super::super::alu::*; -use super::ArmCond; use crate::core::arm7tdmi::psr::RegPSR; use crate::core::arm7tdmi::CpuAction; use crate::core::arm7tdmi::{Addr, Core, CpuMode, CpuState, REG_LR, REG_PC}; @@ -12,35 +11,34 @@ use super::*; impl Core { pub fn exec_arm(&mut self, bus: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - if insn.cond != ArmCond::AL { - if !self.check_arm_cond(insn.cond) { - self.S_cycle32(bus, self.pc); - return CpuAction::AdvancePC; - } - } match insn.fmt { - ArmFormat::BX => self.exec_bx(bus, insn), - ArmFormat::B_BL => self.exec_b_bl(bus, insn), - ArmFormat::DP => self.exec_data_processing(bus, insn), - ArmFormat::SWI => { - self.software_interrupt(bus, self.pc - 4, insn.swi_comment()); - CpuAction::FlushPipeline - } - ArmFormat::LDR_STR => self.exec_ldr_str(bus, insn), - ArmFormat::LDR_STR_HS_IMM => self.exec_ldr_str_hs(bus, insn), - ArmFormat::LDR_STR_HS_REG => self.exec_ldr_str_hs(bus, insn), - ArmFormat::LDM_STM => self.exec_ldm_stm(bus, insn), - ArmFormat::MRS => self.move_from_status_register(bus, insn.rd(), insn.spsr_flag()), - ArmFormat::MSR_REG => self.exec_msr_reg(bus, insn), - ArmFormat::MSR_FLAGS => self.exec_msr_flags(bus, insn), - ArmFormat::MUL_MLA => self.exec_mul_mla(bus, insn), - ArmFormat::MULL_MLAL => self.exec_mull_mlal(bus, insn), + ArmFormat::BX => self.exec_arm_bx(bus, insn), + ArmFormat::B_BL => self.exec_arm_b_bl(bus, insn), + ArmFormat::DP => self.exec_arm_data_processing(bus, insn), + ArmFormat::SWI => self.exec_arm_swi(bus, insn), + ArmFormat::LDR_STR => self.exec_arm_ldr_str(bus, insn), + ArmFormat::LDR_STR_HS_IMM => self.exec_arm_ldr_str_hs(bus, insn), + ArmFormat::LDR_STR_HS_REG => self.exec_arm_ldr_str_hs(bus, insn), + ArmFormat::LDM_STM => self.exec_arm_ldm_stm(bus, insn), + ArmFormat::MRS => self.exec_arm_mrs(bus, insn), + ArmFormat::MSR_REG => self.exec_arm_msr_reg(bus, insn), + ArmFormat::MSR_FLAGS => self.exec_arm_msr_flags(bus, insn), + ArmFormat::MUL_MLA => self.exec_arm_mul_mla(bus, insn), + ArmFormat::MULL_MLAL => self.exec_arm_mull_mlal(bus, insn), ArmFormat::SWP => self.exec_arm_swp(bus, insn), + ArmFormat::Undefined => panic!("Undefined instruction "), } } + pub fn arm_undefined(&mut self, _: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + panic!( + "executing undefind arm instruction {:08x} at @{:08x}", + insn.raw, insn.pc + ) + } + /// Cycles 2S+1N - fn exec_b_bl(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_b_bl(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { self.S_cycle32(sb, self.pc); if insn.link_flag() { self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1); @@ -73,7 +71,7 @@ impl Core { } /// Cycles 2S+1N - fn exec_bx(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { self.branch_exchange(sb, self.get_reg(insn.rn())) } @@ -94,7 +92,11 @@ impl Core { CpuAction::AdvancePC } - fn exec_msr_reg(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + self.move_from_status_register(sb, insn.rd(), insn.spsr_flag()) + } + + pub fn exec_arm_msr_reg(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { self.write_status_register(sb, insn.spsr_flag(), self.get_reg(insn.rm())) } @@ -129,7 +131,7 @@ impl Core { CpuAction::AdvancePC } - fn exec_msr_flags(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_msr_flags(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { self.S_cycle32(sb, self.pc); let op = insn.operand2(); let op = self.decode_operand2(&op); @@ -164,7 +166,11 @@ impl Core { /// /// Cycles: 1S+x+y (from GBATEK) /// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15. - fn exec_data_processing(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_data_processing( + &mut self, + sb: &mut SysBus, + insn: &ArmInstruction, + ) -> CpuAction { use AluOpCode::*; self.S_cycle32(sb, self.pc); @@ -275,7 +281,7 @@ impl Core { /// STR{cond}{B}{T} Rd,
| 2N | ---- | [Rn+/-]=Rd /// ------------------------------------------------------------------------------ /// For LDR, add y=1S+1N if Rd=R15. - fn exec_ldr_str(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldr_str(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let mut result = CpuAction::AdvancePC; let load = insn.load_flag(); @@ -352,7 +358,7 @@ impl Core { result } - fn exec_ldr_str_hs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldr_str_hs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let mut result = CpuAction::AdvancePC; let load = insn.load_flag(); @@ -434,7 +440,7 @@ impl Core { result } - fn exec_ldm_stm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldm_stm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let mut result = CpuAction::AdvancePC; let mut full = insn.pre_index_flag(); @@ -584,7 +590,7 @@ impl Core { result } - fn exec_mul_mla(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let (rd, rn, rs, rm) = (insn.rd(), insn.rn(), insn.rs(), insn.rm()); // check validity @@ -619,7 +625,7 @@ impl Core { CpuAction::AdvancePC } - fn exec_mull_mlal(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_mull_mlal(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let (rd_hi, rd_lo, rn, rs, rm) = (insn.rd_hi(), insn.rd_lo(), insn.rn(), insn.rs(), insn.rm()); @@ -666,7 +672,7 @@ impl Core { CpuAction::AdvancePC } - fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { let base_addr = self.get_reg(insn.rn()); if insn.transfer_size() == 1 { let t = sb.read_8(base_addr); @@ -686,4 +692,9 @@ impl Core { CpuAction::AdvancePC } + + pub fn exec_arm_swi(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + self.software_interrupt(sb, self.pc - 4, insn.swi_comment()); + CpuAction::FlushPipeline + } } diff --git a/src/core/arm7tdmi/arm/lut.rs b/src/core/arm7tdmi/arm/lut.rs new file mode 100644 index 0000000..1900583 --- /dev/null +++ b/src/core/arm7tdmi/arm/lut.rs @@ -0,0 +1,161 @@ +use super::super::super::SysBus; +use super::super::Core; +use super::super::CpuAction; +use super::{ArmFormat, ArmInstruction}; + +use bit::BitIndex; + +pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, &ArmInstruction) -> CpuAction; + +impl From for ArmInstructionHandler { + fn from(arm_fmt: ArmFormat) -> ArmInstructionHandler { + match arm_fmt { + ArmFormat::BranchExchange => Core::exec_arm_bx, + ArmFormat::BranchLink => Core::exec_arm_b_bl, + ArmFormat::DataProcessing => Core::exec_arm_data_processing, + ArmFormat::SoftwareInterrupt => Core::exec_arm_swi, + ArmFormat::SingleDataTransfer => Core::exec_arm_ldr_str, + ArmFormat::HalfwordDataTransferImmediateOffset => Core::exec_arm_ldr_str_hs, + ArmFormat::HalfwordDataTransferRegOffset => Core::exec_arm_ldr_str_hs, + ArmFormat::BlockDataTransfer => Core::exec_arm_ldm_stm, + ArmFormat::Multiply => Core::exec_arm_mul_mla, + ArmFormat::MultiplyLong => Core::exec_arm_mull_mlal, + ArmFormat::SingleDataSwap => Core::exec_arm_swp, + _ => Core::arm_undefined, + } + } +} + +pub struct ArmInstructionInfo { + pub fmt: ArmFormat, + pub handler_fn: ArmInstructionHandler, +} + +impl ArmInstructionInfo { + fn new(fmt: ArmFormat, handler_fn: ArmInstructionHandler) -> ArmInstructionInfo { + ArmInstructionInfo { fmt, handler_fn } + } +} + +#[inline(always)] +pub fn arm_insn_hash(insn: u32) -> usize { + (((insn >> 16) & 0xff0) | ((insn >> 4) & 0x00f)) as usize +} + +impl From for ArmFormat { + fn from(i: u32) -> ArmFormat { + use ArmFormat::*; + + // match i.bit_range(26..28) { + // 0b00 => { + // match (i.bit_range(23..26), i.bit(22) as u32, i.bit_range(20..22), i.bit_range(4..8)) { + // (0b000, 0b0, , _, 0b1001) => Multiply, + // (0b001, _, _, 0b1001) => MultiplyLong, + // (0b010, _, 0b00, 0b1001) => SingleDataSwap, + // (0b010, 0b0, 0b10, 0b0001) => BranchExchange, + + // _ => DataProcessing + // } + // } + // 0b01 => { + // if i.bit(4) { + // Undefined + // } else { + // SingleDataTransfer + // } + // } + // 0b10 => { + + // } + // 0b11 { + + // } + // } + + if (0x0ff0_00f0 & i) == 0x0120_0010 { + BranchExchange + } else if (0x0e00_0000 & i) == 0x0a00_0000 { + BranchLink + } else if (0xe000_0010 & i) == 0x0600_0000 { + Undefined + } else if (0x0fb0_0ff0 & i) == 0x0100_0090 { + SingleDataSwap + } else if (0x0fc0_00f0 & i) == 0x0000_0090 { + Multiply + } else if (0x0f80_00f0 & i) == 0x0080_0090 { + MultiplyLong + } else if (0x0c00_0000 & i) == 0x0400_0000 { + SingleDataTransfer + } else if (0x0e40_0F90 & i) == 0x0000_0090 { + HalfwordDataTransferRegOffset + } else if (0x0e40_0090 & i) == 0x0040_0090 { + HalfwordDataTransferImmediateOffset + } else if (0x0e00_0000 & i) == 0x0800_0000 { + BlockDataTransfer + } else if (0x0f00_0000 & i) == 0x0f00_0000 { + SoftwareInterrupt + } else if (0x0c00_0000 & i) == 0x0000_0000 { + DataProcessing + } else { + Undefined + } + } +} + +lazy_static! { + + pub static ref ARM_FN_LUT: [ArmInstructionHandler; 256] = { + + use std::mem::{self, MaybeUninit}; + + let mut lut: [MaybeUninit; 256] = unsafe { + MaybeUninit::uninit().assume_init() + }; + + for i in 0..256 { + lut[i] = MaybeUninit::new(Core::arm_undefined); + } + + lut[ArmFormat::BranchExchange as usize] = MaybeUninit::new(Core::exec_arm_bx); + lut[ArmFormat::BranchLink as usize] = MaybeUninit::new(Core::exec_arm_b_bl); + lut[ArmFormat::DataProcessing as usize] = MaybeUninit::new(Core::exec_arm_data_processing); + lut[ArmFormat::SoftwareInterrupt as usize] = MaybeUninit::new(Core::exec_arm_swi); + lut[ArmFormat::SingleDataTransfer as usize] = MaybeUninit::new(Core::exec_arm_ldr_str); + lut[ArmFormat::HalfwordDataTransferImmediateOffset as usize] = MaybeUninit::new(Core::exec_arm_ldr_str_hs); + lut[ArmFormat::HalfwordDataTransferRegOffset as usize] = MaybeUninit::new(Core::exec_arm_ldr_str_hs); + lut[ArmFormat::BlockDataTransfer as usize] = MaybeUninit::new(Core::exec_arm_ldm_stm); + lut[ArmFormat::MoveFromStatus as usize] = MaybeUninit::new(Core::exec_arm_mrs); + lut[ArmFormat::MoveToStatus as usize] = MaybeUninit::new(Core::exec_arm_msr_reg); + lut[ArmFormat::MoveToFlags as usize] = MaybeUninit::new(Core::exec_arm_msr_flags); + lut[ArmFormat::Multiply as usize] = MaybeUninit::new(Core::exec_arm_mul_mla); + lut[ArmFormat::MultiplyLong as usize] = MaybeUninit::new(Core::exec_arm_mull_mlal); + lut[ArmFormat::SingleDataSwap as usize] = MaybeUninit::new(Core::exec_arm_swp); + lut[ArmFormat::Undefined as usize] = MaybeUninit::new(Core::arm_undefined); + + // Everything is initialized. Transmute the array to the + // initialized type. + unsafe { mem::transmute::<_, [ArmInstructionHandler; 256]>(lut) } + }; + + // there are 0xfff different hashes + pub static ref ARM_LUT: [u8; 4096] = { + + debug!("generating ARM lookup table"); + + use std::mem::{self, MaybeUninit}; + + let mut lut: [MaybeUninit; 4096] = unsafe { + MaybeUninit::uninit().assume_init() + }; + + for i in 0..4096 { + let x = ((i & 0xff0) << 16) | ((i & 0x00f) << 4); + let fmt = ArmFormat::from(x); + lut[i as usize] = MaybeUninit::new(fmt as u8); + } + + // Everything is initialized. Transmute the array to the + // initialized type. + unsafe { mem::transmute::<_, [u8; 4096]>(lut) } + }; +} diff --git a/src/core/arm7tdmi/arm/mod.rs b/src/core/arm7tdmi/arm/mod.rs index 0d2e0cc..c7dfa02 100644 --- a/src/core/arm7tdmi/arm/mod.rs +++ b/src/core/arm7tdmi/arm/mod.rs @@ -1,6 +1,11 @@ pub mod display; pub mod exec; +#[cfg(feature = "arm7tdmi_dispatch_table")] +mod lut; +#[cfg(feature = "arm7tdmi_dispatch_table")] +pub use lut::*; + use serde::{Deserialize, Serialize}; use super::alu::*; @@ -90,6 +95,8 @@ pub enum ArmFormat { MSR_REG, /// Tanssfer immediate/register to PSR flags only MSR_FLAGS, + + Undefined, } #[derive(Debug, PartialEq, Primitive)] @@ -101,26 +108,29 @@ pub enum ArmHalfwordTransferType { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct ArmInstruction { - pub cond: ArmCond, pub fmt: ArmFormat, pub raw: u32, pub pc: Addr, } +impl ArmInstruction { + pub fn new(raw: u32, pc: Addr, fmt: ArmFormat) -> ArmInstruction { + ArmInstruction { fmt, raw, pc } + } +} + impl InstructionDecoder for ArmInstruction { type IntType = u32; fn decode(raw: u32, addr: Addr) -> Self { use ArmFormat::*; - let cond_code = raw.bit_range(28..32) as u8; - let cond = ArmCond::from_u8(cond_code).expect("invalid arm condition"); let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 { BX } else if (0x0e00_0000 & raw) == 0x0a00_0000 { B_BL } else if (0xe000_0010 & raw) == 0x0600_0000 { - panic!("unknown instruction {:#x} at @{:#x}", raw, addr); + Undefined } else if (0x0fb0_0ff0 & raw) == 0x0100_0090 { SWP } else if (0x0fc0_00f0 & raw) == 0x0000_0090 { @@ -146,11 +156,10 @@ impl InstructionDecoder for ArmInstruction { } else if (0x0c00_0000 & raw) == 0x0000_0000 { DP } else { - panic!("unknown arm instruction {:#x} at @{:#x}", raw, addr); + Undefined }; ArmInstruction { - cond: cond, fmt: fmt, raw: raw, pc: addr, @@ -177,6 +186,10 @@ impl ArmInstruction { } } + pub fn cond(&self) -> ArmCond { + ArmCond::from_u32(self.raw.bit_range(28..32)).unwrap() + } + pub fn rn(&self) -> usize { match self.fmt { ArmFormat::MUL_MLA => self.raw.bit_range(12..16) as usize, diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 7466dd9..93efadc 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -6,17 +6,29 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "debugger")] use std::fmt; +use super::arm::ArmCond; +#[cfg(feature = "arm7tdmi_dispatch_table")] +use super::arm::{arm_insn_hash, ARM_LUT}; pub use super::exception::Exception; +#[cfg(feature = "arm7tdmi_dispatch_table")] +use super::thumb::THUMB_LUT; use super::CpuAction; #[cfg(feature = "debugger")] use super::DecodedInstruction; -use super::{ - arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState, InstructionDecoder, -}; +use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState}; + +#[cfg(not(feature = "arm7tdmi_dispatch_table"))] +use super::InstructionDecoder; use crate::core::bus::Bus; use crate::core::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus}; +use bit::BitIndex; +use num::FromPrimitive; + +#[cfg(feature = "arm7tdmi_dispatch_table")] +use lazy_static; + #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct Core { pub pc: u32, @@ -56,6 +68,13 @@ pub struct Core { impl Core { pub fn new() -> Core { + #[cfg(feature = "arm7tdmi_dispatch_table")] + { + lazy_static::initialize(&ARM_LUT); + lazy_static::initialize(&ARM_FN_LUT); + lazy_static::initialize(&THUMB_LUT); + } + let cpsr = RegPSR::new(0x0000_00D3); Core { memreq: 0xffff_0000, // set memreq to an invalid addr so the first load cycle will be non-sequential @@ -240,43 +259,37 @@ impl Core { #[allow(non_snake_case)] #[inline(always)] pub(super) fn S_cycle32(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, Seq + MemoryAccess32); + self.cycles += sb.get_cycles(addr, Seq, MemoryAccess32); } #[allow(non_snake_case)] #[inline(always)] pub(super) fn S_cycle16(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, Seq + MemoryAccess16); + self.cycles += sb.get_cycles(addr, Seq, MemoryAccess16); } #[allow(non_snake_case)] #[inline(always)] pub(super) fn S_cycle8(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, Seq + MemoryAccess8); + self.cycles += sb.get_cycles(addr, Seq, MemoryAccess8); } #[allow(non_snake_case)] #[inline(always)] pub(super) fn N_cycle32(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess32); + self.cycles += sb.get_cycles(addr, NonSeq, MemoryAccess32); } #[allow(non_snake_case)] #[inline(always)] pub(super) fn N_cycle16(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess16); + self.cycles += sb.get_cycles(addr, NonSeq, MemoryAccess16); } #[allow(non_snake_case)] #[inline(always)] pub(super) fn N_cycle8(&mut self, sb: &SysBus, addr: u32) { - self.cycles += 1; - self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess8); + self.cycles += sb.get_cycles(addr, NonSeq, MemoryAccess8); } #[inline] @@ -301,32 +314,56 @@ impl Core { } } - fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) { - let decoded_arm = ArmInstruction::decode(insn, self.pc.wrapping_sub(8)); - #[cfg(feature = "debugger")] - { - self.gpr_previous = self.get_registers(); - self.last_executed = Some(DecodedInstruction::Arm(decoded_arm.clone())); - } - let result = self.exec_arm(sb, &decoded_arm); - match result { - CpuAction::AdvancePC => self.advance_arm(), - CpuAction::FlushPipeline => {} - } + #[cfg(feature = "debugger")] + fn debugger_record_step(&mut self, d: DecodedInstruction) { + self.gpr_previous = self.get_registers(); + self.last_executed = Some(d); } - fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) { - let decoded_thumb = ThumbInstruction::decode(insn, self.pc.wrapping_sub(4)); + #[cfg(feature = "arm7tdmi_dispatch_table")] + fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction { + let l1_index = ARM_LUT[arm_insn_hash(insn)] as usize; + let handler_fn = ARM_FN_LUT[l1_index]; + + // This is safe because the table can't hold invalid indices + let arm_format: ArmFormat = unsafe { std::mem::transmute(l1_index as u8) }; + let arm_insn = ArmInstruction::new(insn, self.pc.wrapping_sub(8), arm_format); + #[cfg(feature = "debugger")] - { - self.gpr_previous = self.get_registers(); - self.last_executed = Some(DecodedInstruction::Thumb(decoded_thumb.clone())); - } - let result = self.exec_thumb(sb, &decoded_thumb); - match result { - CpuAction::AdvancePC => self.advance_thumb(), - CpuAction::FlushPipeline => {} - } + self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone())); + + (handler_fn)(self, sb, &arm_insn) + } + + #[cfg(feature = "arm7tdmi_dispatch_table")] + fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction { + let thumb_info = &THUMB_LUT[(insn >> 6) as usize]; + let thumb_insn = ThumbInstruction::new(insn, self.pc.wrapping_sub(4), thumb_info.fmt); + + #[cfg(feature = "debugger")] + self.debugger_record_step(DecodedInstruction::Thumb(thumb_insn.clone())); + + (thumb_info.handler_fn)(self, sb, &thumb_insn) + } + + #[cfg(not(feature = "arm7tdmi_dispatch_table"))] + fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction { + let arm_insn = ArmInstruction::decode(insn, self.pc.wrapping_sub(8)); + + #[cfg(feature = "debugger")] + self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone())); + + self.exec_arm(sb, &arm_insn) + } + + #[cfg(not(feature = "arm7tdmi_dispatch_table"))] + fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction { + let thumb_insn = ThumbInstruction::decode(insn, self.pc.wrapping_sub(4)); + + #[cfg(feature = "debugger")] + self.debugger_record_step(DecodedInstruction::Thumb(thumb_insn.clone())); + + self.exec_thumb(sb, &thumb_insn) } #[inline(always)] @@ -370,14 +407,29 @@ impl Core { let insn = self.pipeline[0]; self.pipeline[0] = self.pipeline[1]; self.pipeline[1] = fetched_now; - self.step_arm_exec(insn, bus) + let cond = + ArmCond::from_u32(insn.bit_range(28..32)).expect("invalid arm condition"); + if cond != ArmCond::AL { + if !self.check_arm_cond(cond) { + self.S_cycle32(bus, self.pc); + self.advance_arm(); + return; + } + } + match self.step_arm_exec(insn, bus) { + CpuAction::AdvancePC => self.advance_arm(), + CpuAction::FlushPipeline => {} + } } CpuState::THUMB => { let fetched_now = bus.read_16(pc); let insn = self.pipeline[0]; self.pipeline[0] = self.pipeline[1]; self.pipeline[1] = fetched_now as u32; - self.step_thumb_exec(insn as u16, bus) + match self.step_thumb_exec(insn as u16, bus) { + CpuAction::AdvancePC => self.advance_thumb(), + CpuAction::FlushPipeline => {} + } } } } diff --git a/src/core/arm7tdmi/thumb/display.rs b/src/core/arm7tdmi/thumb/display.rs index f30e265..f4f37f7 100644 --- a/src/core/arm7tdmi/thumb/display.rs +++ b/src/core/arm7tdmi/thumb/display.rs @@ -294,6 +294,7 @@ impl fmt::Display for ThumbInstruction { ThumbFormat::Swi => self.fmt_thumb_swi(f), ThumbFormat::Branch => self.fmt_thumb_branch(f), ThumbFormat::BranchLongWithLink => self.fmt_thumb_branch_long_with_link(f), + ThumbFormat::Undefined => write!(f, ""), } } } diff --git a/src/core/arm7tdmi/thumb/exec.rs b/src/core/arm7tdmi/thumb/exec.rs index 8c5929e..8ab2d54 100644 --- a/src/core/arm7tdmi/thumb/exec.rs +++ b/src/core/arm7tdmi/thumb/exec.rs @@ -18,7 +18,7 @@ fn pop(cpu: &mut Core, bus: &mut SysBus, r: usize) { impl Core { /// Format 1 - fn exec_thumb_move_shifted_reg( + pub(in super::super) fn exec_thumb_move_shifted_reg( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -43,7 +43,11 @@ impl Core { } /// Format 2 - fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_add_sub( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let rd = (insn.raw & 0b111) as usize; let op1 = self.get_reg(insn.rs()); let op2 = if insn.is_immediate_operand() { @@ -68,7 +72,7 @@ impl Core { } /// Format 3 - fn exec_thumb_data_process_imm( + pub(in super::super) fn exec_thumb_data_process_imm( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -96,7 +100,11 @@ impl Core { } /// Format 4 - fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_alu_ops( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let rd = (insn.raw & 0b111) as usize; let rs = insn.rs(); let dst = self.get_reg(rd); @@ -153,7 +161,7 @@ impl Core { } /// Format 5 - fn exec_thumb_hi_reg_op_or_bx( + pub(in super::super) fn exec_thumb_hi_reg_op_or_bx( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -205,7 +213,11 @@ impl Core { } /// Format 6 - fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_ldr_pc( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let rd = insn.raw.bit_range(8..11) as usize; let ofs = insn.word8() as Addr; @@ -261,7 +273,7 @@ impl Core { } /// Format 7 - fn exec_thumb_ldr_str_reg_offset( + pub(in super::super) fn exec_thumb_ldr_str_reg_offset( &mut self, bus: &mut SysBus, insn: &ThumbInstruction, @@ -272,7 +284,11 @@ impl Core { } /// Format 8 - fn exec_thumb_ldr_str_shb(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_ldr_str_shb( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let rb = insn.raw.bit_range(3..6) as usize; let rd = (insn.raw & 0b111) as usize; @@ -318,7 +334,7 @@ impl Core { } /// Format 9 - fn exec_thumb_ldr_str_imm_offset( + pub(in super::super) fn exec_thumb_ldr_str_imm_offset( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -335,7 +351,7 @@ impl Core { } /// Format 10 - fn exec_thumb_ldr_str_halfword( + pub(in super::super) fn exec_thumb_ldr_str_halfword( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -359,7 +375,11 @@ impl Core { } /// Format 11 - fn exec_thumb_ldr_str_sp(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_ldr_str_sp( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let addr = self.gpr[REG_SP] + (insn.word8() as Addr); let rd = insn.raw.bit_range(8..11) as usize; if insn.is_load() { @@ -377,7 +397,11 @@ impl Core { } /// Format 12 - fn exec_thumb_load_address(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_load_address( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let rd = insn.raw.bit_range(8..11) as usize; let result = if insn.flag(ThumbInstruction::FLAG_SP) { self.gpr[REG_SP] + (insn.word8() as Addr) @@ -391,7 +415,11 @@ impl Core { } /// Format 13 - fn exec_thumb_add_sp(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_add_sp( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let op1 = self.gpr[REG_SP] as i32; let op2 = insn.sword7(); @@ -402,7 +430,11 @@ impl Core { } /// Format 14 - fn exec_thumb_push_pop(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_push_pop( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let mut result = CpuAction::AdvancePC; // (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH). @@ -450,7 +482,11 @@ impl Core { } /// Format 15 - fn exec_thumb_ldm_stm(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_ldm_stm( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let mut result = CpuAction::AdvancePC; // (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH). @@ -527,7 +563,7 @@ impl Core { } /// Format 16 - fn exec_thumb_branch_with_cond( + pub(in super::super) fn exec_thumb_branch_with_cond( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -545,14 +581,22 @@ impl Core { } /// Format 17 - fn exec_thumb_swi(&mut self, sb: &mut SysBus, _insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_swi( + &mut self, + sb: &mut SysBus, + _insn: &ThumbInstruction, + ) -> CpuAction { self.N_cycle16(sb, self.pc); self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2); CpuAction::FlushPipeline } /// Format 18 - fn exec_thumb_branch(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub(in super::super) fn exec_thumb_branch( + &mut self, + sb: &mut SysBus, + insn: &ThumbInstruction, + ) -> CpuAction { let offset = ((insn.offset11() << 21) >> 20) as i32; self.pc = (self.pc as i32).wrapping_add(offset) as u32; self.S_cycle16(sb, self.pc); @@ -561,7 +605,7 @@ impl Core { } /// Format 19 - fn exec_thumb_branch_long_with_link( + pub(in super::super) fn exec_thumb_branch_long_with_link( &mut self, sb: &mut SysBus, insn: &ThumbInstruction, @@ -584,6 +628,13 @@ impl Core { } } + pub fn thumb_undefined(&mut self, _: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + panic!( + "executing undefind thumb instruction {:04x} at @{:08x}", + insn.raw, insn.pc + ) + } + pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { match insn.fmt { ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn), @@ -605,6 +656,7 @@ impl Core { ThumbFormat::Swi => self.exec_thumb_swi(bus, insn), ThumbFormat::Branch => self.exec_thumb_branch(bus, insn), ThumbFormat::BranchLongWithLink => self.exec_thumb_branch_long_with_link(bus, insn), + ThumbFormat::Undefined => self.thumb_undefined(bus, insn), } } } diff --git a/src/core/arm7tdmi/thumb/lut.rs b/src/core/arm7tdmi/thumb/lut.rs new file mode 100644 index 0000000..3b67147 --- /dev/null +++ b/src/core/arm7tdmi/thumb/lut.rs @@ -0,0 +1,65 @@ +use super::super::super::SysBus; +use super::super::Core; +use super::super::CpuAction; +use super::super::InstructionDecoder; +use super::{ThumbFormat, ThumbInstruction}; + +pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, &ThumbInstruction) -> CpuAction; + +impl From for ThumbInstructionHandler { + fn from(thumb_fmt: ThumbFormat) -> ThumbInstructionHandler { + match thumb_fmt { + ThumbFormat::MoveShiftedReg => Core::exec_thumb_move_shifted_reg, + ThumbFormat::AddSub => Core::exec_thumb_add_sub, + ThumbFormat::DataProcessImm => Core::exec_thumb_data_process_imm, + ThumbFormat::AluOps => Core::exec_thumb_alu_ops, + ThumbFormat::HiRegOpOrBranchExchange => Core::exec_thumb_hi_reg_op_or_bx, + ThumbFormat::LdrPc => Core::exec_thumb_ldr_pc, + ThumbFormat::LdrStrRegOffset => Core::exec_thumb_ldr_str_reg_offset, + ThumbFormat::LdrStrSHB => Core::exec_thumb_ldr_str_shb, + ThumbFormat::LdrStrImmOffset => Core::exec_thumb_ldr_str_imm_offset, + ThumbFormat::LdrStrHalfWord => Core::exec_thumb_ldr_str_halfword, + ThumbFormat::LdrStrSp => Core::exec_thumb_ldr_str_sp, + ThumbFormat::LoadAddress => Core::exec_thumb_load_address, + ThumbFormat::AddSp => Core::exec_thumb_add_sp, + ThumbFormat::PushPop => Core::exec_thumb_push_pop, + ThumbFormat::LdmStm => Core::exec_thumb_ldm_stm, + ThumbFormat::BranchConditional => Core::exec_thumb_branch_with_cond, + ThumbFormat::Swi => Core::exec_thumb_swi, + ThumbFormat::Branch => Core::exec_thumb_branch, + ThumbFormat::BranchLongWithLink => Core::exec_thumb_branch_long_with_link, + ThumbFormat::Undefined => Core::thumb_undefined, + } + } +} + +pub struct ThumbInstructionInfo { + pub fmt: ThumbFormat, + pub handler_fn: ThumbInstructionHandler, +} + +lazy_static! { + pub static ref THUMB_LUT: [ThumbInstructionInfo; 1024] = { + + debug!("generating THUMB lookup table"); + + use std::mem::{self, MaybeUninit}; + + let mut lut: [MaybeUninit; 1024] = unsafe { + MaybeUninit::uninit().assume_init() + }; + + for i in 0..1024 { + let insn = ThumbInstruction::decode(i << 6, 0); + let info = ThumbInstructionInfo { + fmt: insn.fmt, + handler_fn: insn.fmt.into() + }; + lut[i as usize] = MaybeUninit::new(info); + } + + // Everything is initialized. Transmute the array to the + // initialized type. + unsafe { mem::transmute::<_, [ThumbInstructionInfo; 1024]>(lut) } + }; +} diff --git a/src/core/arm7tdmi/thumb/mod.rs b/src/core/arm7tdmi/thumb/mod.rs index 0298ec1..3d8051f 100644 --- a/src/core/arm7tdmi/thumb/mod.rs +++ b/src/core/arm7tdmi/thumb/mod.rs @@ -1,13 +1,16 @@ +use super::alu::*; +use super::arm::*; +use super::{Addr, InstructionDecoder}; use crate::bit::BitIndex; use crate::byteorder::{LittleEndian, ReadBytesExt}; use crate::num::FromPrimitive; -use super::alu::*; -use super::arm::*; -use super::{Addr, InstructionDecoder}; - pub mod display; pub mod exec; +#[cfg(feature = "arm7tdmi_dispatch_table")] +mod lut; +#[cfg(feature = "arm7tdmi_dispatch_table")] +pub use lut::*; #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub enum ThumbFormat { @@ -49,6 +52,9 @@ pub enum ThumbFormat { Branch, /// Format 19 BranchLongWithLink, + + /// Not an actual thumb format + Undefined, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] @@ -58,6 +64,12 @@ pub struct ThumbInstruction { pub pc: Addr, } +impl ThumbInstruction { + pub fn new(raw: u16, pc: Addr, fmt: ThumbFormat) -> ThumbInstruction { + ThumbInstruction { fmt, raw, pc } + } +} + impl InstructionDecoder for ThumbInstruction { type IntType = u16; @@ -103,14 +115,10 @@ impl InstructionDecoder for ThumbInstruction { } else if raw & 0xf000 == 0xf000 { BranchLongWithLink } else { - panic!("unknown thumb instruction {:#x} at @{:#x}", raw, addr); + Undefined }; - ThumbInstruction { - fmt: fmt, - raw: raw, - pc: addr, - } + ThumbInstruction::new(raw, addr, fmt) } fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Self { diff --git a/src/debugger/command.rs b/src/debugger/command.rs index f3b4a09..02a39fb 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -85,23 +85,24 @@ impl Debugger { while self.gba.cpu.last_executed.is_none() { self.gba.cpu.step(&mut self.gba.sysbus); } - let last_executed = self.gba.cpu.last_executed.unwrap(); - print!( - "{}\t{}", - Colour::Black - .bold() - .italic() - .on(Colour::White) - .paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)), - last_executed - ); - println!( - "{}", - Colour::Purple.dimmed().italic().paint(format!( - "\t\t/// Next instruction at @0x{:08x}", - self.gba.cpu.get_next_pc() - )) - ); + if let Some(last_executed) = &self.gba.cpu.last_executed { + print!( + "{}\t{}", + Colour::Black + .bold() + .italic() + .on(Colour::White) + .paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)), + last_executed + ); + println!( + "{}", + Colour::Purple.dimmed().italic().paint(format!( + "\t\t/// Next instruction at @0x{:08x}", + self.gba.cpu.get_next_pc() + )) + ); + } } println!("{}\n", self.gba.cpu); } diff --git a/src/lib.rs b/src/lib.rs index 1f9a235..4116a32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "arm7tdmi_dispatch_table")] +#[macro_use] +extern crate lazy_static; + #[macro_use] extern crate serde;