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;