From dc7cd24e8d985dc687ca04e7c9273c1cf1700aab Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Mon, 5 Oct 2020 23:46:14 -0700 Subject: [PATCH] [perf] Refactor&Optimize inner core::arm7tdmi APIs. Avoid passing ArmInstruction struct to handlers, as accesses to its fields can result in memory operations. Former-commit-id: 6ea1719e36a0fefa1b30bdae4d6e8ab4dbf3af1a Former-commit-id: e5855b8258f98d3f4c0819f3aec2fd0f47fef545 --- core/build.rs | 40 ++- core/src/arm7tdmi/arm/display.rs | 214 +++++++------ core/src/arm7tdmi/arm/exec.rs | 139 ++++----- core/src/arm7tdmi/arm/mod.rs | 470 ++++++++++++++++------------- core/src/arm7tdmi/cpu.rs | 107 ++++--- core/src/arm7tdmi/mod.rs | 4 +- core/src/arm7tdmi/thumb/display.rs | 140 ++++----- core/src/arm7tdmi/thumb/exec.rs | 139 ++++----- core/src/arm7tdmi/thumb/mod.rs | 324 ++++++++++++-------- core/src/lib.rs | 3 + 10 files changed, 883 insertions(+), 697 deletions(-) diff --git a/core/build.rs b/core/build.rs index 835aca8..e38193a 100644 --- a/core/build.rs +++ b/core/build.rs @@ -229,13 +229,18 @@ fn arm_format_to_handler(arm_fmt: &str) -> &'static str { fn generate_thumb_lut(file: &mut fs::File) -> Result<(), std::io::Error> { writeln!( file, - "use super::thumb::ThumbFormat; + "/// This file is auto-generated from the build script -pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, &ThumbInstruction) -> CpuAction; +#[cfg(feature = \"debugger\")] +use super::thumb::ThumbFormat; +pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, insn: u16) -> CpuAction; + +#[cfg_attr(not(feature = \"debugger\"), repr(transparent))] pub struct ThumbInstructionInfo {{ + pub handler_fn: ThumbInstructionHandler, + #[cfg(feature = \"debugger\")] pub fmt: ThumbFormat, - pub handler_fn: ThumbInstructionHandler }} " )?; @@ -250,8 +255,13 @@ pub struct ThumbInstructionInfo {{ let handler_name = thumb_format_to_handler(thumb_fmt); writeln!( file, - " /* {:#x} */ ThumbInstructionInfo {{ fmt: ThumbFormat::{}, handler_fn: Core::{} }},", - i, thumb_fmt, handler_name + " /* {:#x} */ + ThumbInstructionInfo {{ + handler_fn: Core::{}, + #[cfg(feature = \"debugger\")] + fmt: ThumbFormat::{}, + }},", + i, handler_name, thumb_fmt )?; } @@ -263,13 +273,18 @@ pub struct ThumbInstructionInfo {{ fn generate_arm_lut(file: &mut fs::File) -> Result<(), std::io::Error> { writeln!( file, - "use super::arm::ArmFormat; + "/// This file is auto-generated from the build script -pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, &ArmInstruction) -> CpuAction; +#[cfg(feature = \"debugger\")] +use super::arm::ArmFormat; +pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, insn: u32) -> CpuAction; + +#[cfg_attr(not(feature = \"debugger\"), repr(transparent))] pub struct ArmInstructionInfo {{ + pub handler_fn: ArmInstructionHandler, + #[cfg(feature = \"debugger\")] pub fmt: ArmFormat, - pub handler_fn: ArmInstructionHandler }} " )?; @@ -280,8 +295,13 @@ pub struct ArmInstructionInfo {{ let handler_name = arm_format_to_handler(arm_fmt); writeln!( file, - " /* {:#x} */ ArmInstructionInfo {{ fmt: ArmFormat::{}, handler_fn: Core::{} }},", - i, arm_fmt, handler_name + " /* {:#x} */ + ArmInstructionInfo {{ + handler_fn: Core::{}, + #[cfg(feature = \"debugger\")] + fmt: ArmFormat::{}, + }} ,", + i, handler_name, arm_fmt )?; } writeln!(file, "];")?; diff --git a/core/src/arm7tdmi/arm/display.rs b/core/src/arm7tdmi/arm/display.rs index 89bfa98..04bd400 100644 --- a/core/src/arm7tdmi/arm/display.rs +++ b/core/src/arm7tdmi/arm/display.rs @@ -1,10 +1,8 @@ use std::fmt; -#[cfg(feature = "debugger")] use crate::bit::BitIndex; -#[cfg(feature = "debugger")] -use super::{ArmFormat, ArmInstruction}; +use super::{ArmDecodeHelper, ArmFormat, ArmInstruction}; use super::{AluOpCode, ArmCond, ArmHalfwordTransferType}; use crate::arm7tdmi::*; @@ -13,6 +11,7 @@ impl fmt::Display for ArmCond { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ArmCond::*; match self { + Invalid => panic!("Invalid condition code"), EQ => write!(f, "eq"), NE => write!(f, "ne"), HS => write!(f, "cs"), @@ -102,24 +101,27 @@ impl fmt::Display for ShiftedRegister { } } -#[cfg(feature = "debugger")] impl ArmInstruction { fn fmt_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "bx\t{Rn}", Rn = reg_string(self.rn())) + write!( + f, + "bx\t{Rn}", + Rn = reg_string(self.raw.bit_range(0..4) as usize) + ) } fn fmt_branch(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "b{link}{cond}\t{ofs:#x}", - link = if self.link_flag() { "l" } else { "" }, - cond = self.cond(), - ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr) + link = if self.raw.link_flag() { "l" } else { "" }, + cond = self.raw.cond(), + ofs = 8 + self.pc.wrapping_add(self.raw.branch_offset() as Addr) ) } fn set_cond_mark(&self) -> &str { - if self.set_cond_flag() { + if self.raw.set_cond_flag() { "s" } else { "" @@ -127,7 +129,7 @@ impl ArmInstruction { } fn fmt_operand2(&self, f: &mut fmt::Formatter<'_>) -> Result, fmt::Error> { - let operand2 = self.operand2(); + let operand2 = self.raw.operand2(); match operand2 { BarrelShifterValue::RotatedImmediate(_, _) => { let value = operand2.decode_rotated_immediate().unwrap(); @@ -145,32 +147,35 @@ impl ArmInstruction { fn fmt_data_processing(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use AluOpCode::*; - let opcode = self.opcode(); + let opcode = self.raw.opcode(); + + let rd = self.raw.bit_range(16..20) as usize; + let rn = self.raw.bit_range(16..20) as usize; match opcode { MOV | MVN => write!( f, "{opcode}{S}{cond}\t{Rd}, ", opcode = opcode, - cond = self.cond(), + cond = self.raw.cond(), S = self.set_cond_mark(), - Rd = reg_string(self.rd()) + Rd = reg_string(rd) ), CMP | CMN | TEQ | TST => write!( f, "{opcode}{cond}\t{Rn}, ", opcode = opcode, - cond = self.cond(), - Rn = reg_string(self.rn()) + cond = self.raw.cond(), + Rn = reg_string(rn) ), _ => write!( f, "{opcode}{S}{cond}\t{Rd}, {Rn}, ", opcode = opcode, - cond = self.cond(), + cond = self.raw.cond(), S = self.set_cond_mark(), - Rd = reg_string(self.rd()), - Rn = reg_string(self.rn()) + Rd = reg_string(rd), + Rn = reg_string(rn) ), }?; @@ -179,18 +184,23 @@ impl ArmInstruction { } fn auto_incremenet_mark(&self) -> &str { - if self.write_back_flag() { + if self.raw.write_back_flag() { "!" } else { "" } } - fn fmt_rn_offset(&self, f: &mut fmt::Formatter<'_>, offset: BarrelShifterValue) -> fmt::Result { - write!(f, "[{Rn}", Rn = reg_string(self.rn()))?; + fn fmt_rn_offset( + &self, + f: &mut fmt::Formatter<'_>, + offset: BarrelShifterValue, + rn: usize, + ) -> fmt::Result { + write!(f, "[{Rn}", Rn = reg_string(rn))?; let (ofs_string, comment) = match offset { BarrelShifterValue::ImmediateValue(value) => { - let value_for_commnet = if self.rn() == REG_PC { + let value_for_commnet = if rn == REG_PC { value + self.pc + 8 // account for pipelining } else { value @@ -211,7 +221,7 @@ impl ArmInstruction { _ => panic!("bad barrel shifter"), }; - if self.pre_index_flag() { + if self.raw.pre_index_flag() { write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?; } else { write!(f, "], {}", ofs_string)?; @@ -228,33 +238,41 @@ impl ArmInstruction { write!( f, "{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(), - T = if !self.pre_index_flag() && self.write_back_flag() { + mnem = if self.raw.load_flag() { "ldr" } else { "str" }, + B = if self.raw.transfer_size() == 1 { + "b" + } else { + "" + }, + cond = self.raw.cond(), + T = if !self.raw.pre_index_flag() && self.raw.write_back_flag() { "t" } else { "" }, - Rd = reg_string(self.rd()), + Rd = reg_string(self.raw.bit_range(12..16) as usize), )?; - self.fmt_rn_offset(f, self.ldr_str_offset()) + self.fmt_rn_offset( + f, + self.raw.ldr_str_offset(), + self.raw.bit_range(16..20) as usize, + ) } fn fmt_ldm_stm(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{", - 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(), - Rn = reg_string(self.rn()), - auto_inc = if self.write_back_flag() { "!" } else { "" } + mnem = if self.raw.load_flag() { "ldm" } else { "stm" }, + inc_dec = if self.raw.add_offset_flag() { 'i' } else { 'd' }, + pre_post = if self.raw.pre_index_flag() { 'b' } else { 'a' }, + cond = self.raw.cond(), + Rn = reg_string(self.raw.bit_range(16..20) as usize), + auto_inc = if self.raw.write_back_flag() { "!" } else { "" } )?; - let register_list = self.register_list(); + let register_list = self.raw.register_list(); let mut has_first = false; for i in 0..16 { if register_list.bit(i) { @@ -270,7 +288,7 @@ impl ArmInstruction { write!( f, "}}{}", - if self.psr_and_force_user_flag() { + if self.raw.psr_and_force_user_flag() { "^" } else { "" @@ -283,9 +301,9 @@ impl ArmInstruction { write!( f, "mrs{cond}\t{Rd}, {psr}", - cond = self.cond(), - Rd = reg_string(self.rd()), - psr = if self.spsr_flag() { "SPSR" } else { "CPSR" } + cond = self.raw.cond(), + Rd = reg_string(self.raw.bit_range(12..16) as usize), + psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" } ) } @@ -294,8 +312,8 @@ impl ArmInstruction { write!( f, "msr{cond}\t{psr}, ", - cond = self.cond(), - psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, + cond = self.raw.cond(), + psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" }, )?; self.fmt_operand2(f).unwrap(); Ok(()) @@ -305,8 +323,12 @@ impl ArmInstruction { write!( f, "msr{cond}\t{psr}, ", - cond = self.cond(), - psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" }, + cond = self.raw.cond(), + psr = if self.raw.spsr_flag() { + "SPSR_f" + } else { + "CPSR_f" + }, )?; if let Ok(Some(op)) = self.fmt_operand2(f) { let psr = RegPSR::new(op & 0xf000_0000); @@ -323,32 +345,33 @@ impl ArmInstruction { } fn fmt_mul_mla(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.accumulate_flag() { + let rd = self.raw.bit_range(16..20) as usize; + if self.raw.accumulate_flag() { write!( f, "mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}", S = self.set_cond_mark(), - cond = self.cond(), - Rd = reg_string(self.rd()), - Rm = reg_string(self.rm()), - Rs = reg_string(self.rs()), - Rn = reg_string(self.rn()), + cond = self.raw.cond(), + Rd = reg_string(rd), + Rm = reg_string(self.raw.rm()), + Rs = reg_string(self.raw.rs()), + Rn = reg_string(self.raw.bit_range(12..16) as usize), ) } else { write!( f, "mul{S}{cond}\t{Rd}, {Rm}, {Rs}", S = self.set_cond_mark(), - cond = self.cond(), - Rd = reg_string(self.rd()), - Rm = reg_string(self.rm()), - Rs = reg_string(self.rs()), + cond = self.raw.cond(), + Rd = reg_string(rd), + Rm = reg_string(self.raw.rm()), + Rs = reg_string(self.raw.rs()), ) } } fn sign_mark(&self) -> &str { - if self.u_flag() { + if self.raw.u_flag() { "s" } else { "u" @@ -360,42 +383,60 @@ impl ArmInstruction { f, "{sign}{mnem}{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}", sign = self.sign_mark(), - mnem = if self.accumulate_flag() { + mnem = if self.raw.accumulate_flag() { "mlal" } else { "mull" }, S = self.set_cond_mark(), - cond = self.cond(), - RdLo = reg_string(self.rd_lo()), - RdHi = reg_string(self.rd_hi()), - Rm = reg_string(self.rm()), - Rs = reg_string(self.rs()), + cond = self.raw.cond(), + RdLo = reg_string(self.raw.rd_lo()), + RdHi = reg_string(self.raw.rd_hi()), + Rm = reg_string(self.raw.rm()), + Rs = reg_string(self.raw.rs()), ) } - fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Ok(transfer_type) = self.halfword_data_transfer_type() { - write!( - f, - "{mnem}{type}{cond}\t{Rd}, ", - mnem = if self.load_flag() { "ldr" } else { "str" }, - cond = self.cond(), - type = transfer_type, - Rd = reg_string(self.rd()), - )?; - self.fmt_rn_offset(f, self.ldr_str_hs_offset().unwrap()) - } else { - write!(f, "") - } + fn fmt_ldr_str_hs_imm_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let transfer_type = self.raw.halfword_data_transfer_type(); + write!( + f, + "{mnem}{type}{cond}\t{Rd}, ", + mnem = if self.raw.load_flag() { "ldr" } else { "str" }, + cond = self.raw.cond(), + type = transfer_type, + Rd = reg_string(self.raw.bit_range(12..16) as usize), + )?; + self.fmt_rn_offset( + f, + self.raw.ldr_str_hs_imm_offset(), + self.raw.bit_range(16..20) as usize, + ) + } + + fn fmt_ldr_str_hs_reg_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let transfer_type = self.raw.halfword_data_transfer_type(); + write!( + f, + "{mnem}{type}{cond}\t{Rd}, ", + mnem = if self.raw.load_flag() { "ldr" } else { "str" }, + cond = self.raw.cond(), + type = transfer_type, + Rd = reg_string(self.raw.bit_range(12..16) as usize), + )?; + self.fmt_rn_offset( + f, + self.raw.ldr_str_hs_reg_offset(), + self.raw.bit_range(16..20) as usize, + ) } fn fmt_swi(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "swi{cond}\t#{comm:#x}", - cond = self.cond(), - comm = self.swi_comment() + cond = self.raw.cond(), + comm = self.raw.swi_comment() ) } @@ -403,16 +444,19 @@ impl ArmInstruction { write!( f, "swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]", - B = if self.transfer_size() == 1 { "b" } else { "" }, - cond = self.cond(), - Rd = reg_string(self.rd()), - Rm = reg_string(self.rm()), - Rn = reg_string(self.rn()), + B = if self.raw.transfer_size() == 1 { + "b" + } else { + "" + }, + cond = self.raw.cond(), + Rd = reg_string(self.raw.bit_range(12..16) as usize), + Rm = reg_string(self.raw.rm()), + Rn = reg_string(self.raw.bit_range(16..20) as usize), ) } } -#[cfg(feature = "debugger")] impl fmt::Display for ArmInstruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ArmFormat::*; @@ -427,8 +471,8 @@ impl fmt::Display for ArmInstruction { MoveToFlags => self.fmt_msr_flags(f), Multiply => self.fmt_mul_mla(f), MultiplyLong => self.fmt_mull_mlal(f), - HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs(f), - HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs(f), + HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs_imm_offset(f), + HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs_reg_offset(f), SoftwareInterrupt => self.fmt_swi(f), SingleDataSwap => self.fmt_swp(f), Undefined => write!(f, ""), diff --git a/core/src/arm7tdmi/arm/exec.rs b/core/src/arm7tdmi/arm/exec.rs index e00e282..2f5e0a6 100644 --- a/core/src/arm7tdmi/arm/exec.rs +++ b/core/src/arm7tdmi/arm/exec.rs @@ -7,16 +7,13 @@ use crate::arm7tdmi::{Addr, Core, CpuMode, CpuState, REG_LR, REG_PC}; use crate::sysbus::SysBus; use crate::Bus; +use super::ArmDecodeHelper; use super::*; -#[inline(always)] -fn get_bs_op(shift_field: u32) -> BarrelShiftOpCode { - BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap() -} - impl Core { - pub fn exec_arm(&mut self, bus: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - match insn.fmt { + #[cfg(not(feature = "arm7tdmi_dispatch_table"))] + pub fn exec_arm(&mut self, bus: &mut SysBus, insn: u32, fmt: ArmFormat) -> CpuAction { + match fmt { ArmFormat::BranchExchange => self.exec_arm_bx(bus, insn), ArmFormat::BranchLink => self.exec_arm_b_bl(bus, insn), ArmFormat::DataProcessing => self.exec_arm_data_processing(bus, insn), @@ -37,18 +34,19 @@ impl Core { } } - pub fn arm_undefined(&mut self, _: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn arm_undefined(&mut self, _: &mut SysBus, insn: u32) -> CpuAction { panic!( "executing undefined arm instruction {:08x} at @{:08x}", - insn.raw, insn.pc + insn, + self.pc_arm() ) } /// Cycles 2S+1N - pub fn exec_arm_b_bl(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_b_bl(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { self.S_cycle32(sb, self.pc); if insn.link_flag() { - self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1); + self.set_reg(REG_LR, (self.pc_arm() + (self.word_size() as u32)) & !0b1); } self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1; @@ -78,8 +76,8 @@ impl Core { } /// Cycles 2S+1N - pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - self.branch_exchange(sb, self.get_reg(insn.raw.bit_range(0..4) as usize)) + pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { + self.branch_exchange(sb, self.get_reg(insn.bit_range(0..4) as usize)) } fn move_from_status_register( @@ -99,33 +97,29 @@ impl Core { CpuAction::AdvancePC } - pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - self.move_from_status_register(sb, insn.raw.bit_range(12..16) as usize, insn.spsr_flag()) + pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { + self.move_from_status_register(sb, insn.bit_range(12..16) as usize, insn.spsr_flag()) } #[inline(always)] - fn decode_msr_param(&mut self, insn: &ArmInstruction) -> u32 { - if insn.raw.bit(25) { - let immediate = insn.raw & 0xff; - let rotate = 2 * insn.raw.bit_range(8..12); + fn decode_msr_param(&mut self, insn: u32) -> u32 { + if insn.bit(25) { + let immediate = insn & 0xff; + let rotate = 2 * insn.bit_range(8..12); self.ror(immediate, rotate, self.cpsr.C(), false, true) } else { - self.get_reg((insn.raw & 0b1111) as usize) + self.get_reg((insn & 0b1111) as usize) } } // #[cfg(feature = "arm7tdmi_dispatch_table")] - pub fn exec_arm_transfer_to_status( - &mut self, - sb: &mut SysBus, - insn: &ArmInstruction, - ) -> CpuAction { + pub fn exec_arm_transfer_to_status(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { let value = self.decode_msr_param(insn); - let f = insn.raw.bit(19); - let s = insn.raw.bit(18); - let x = insn.raw.bit(17); - let c = insn.raw.bit(16); + let f = insn.bit(19); + let s = insn.bit(18); + let x = insn.bit(17); + let c = insn.bit(16); let mut mask = 0; if f { @@ -181,51 +175,46 @@ 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. - pub fn exec_arm_data_processing( - &mut self, - sb: &mut SysBus, - insn: &ArmInstruction, - ) -> CpuAction { + pub fn exec_arm_data_processing(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { use AluOpCode::*; - let raw_insn = insn.raw; self.S_cycle32(sb, self.pc); - let rn = raw_insn.bit_range(16..20) as usize; - let rd = raw_insn.bit_range(12..16) as usize; + let rn = insn.bit_range(16..20) as usize; + let rd = insn.bit_range(12..16) as usize; let mut op1 = if rn == REG_PC { - insn.pc + 8 + self.pc_arm() + 8 } else { self.get_reg(rn) }; let mut s_flag = insn.set_cond_flag(); let opcode = insn.opcode(); - let op2 = if raw_insn.bit(25) { - let immediate = raw_insn & 0xff; - let rotate = 2 * raw_insn.bit_range(8..12); + let op2 = if insn.bit(25) { + let immediate = insn & 0xff; + let rotate = 2 * insn.bit_range(8..12); // TODO refactor out let bs_carry_in = self.cpsr.C(); self.bs_carry_out = bs_carry_in; self.ror(immediate, rotate, self.cpsr.C(), false, true) } else { - let reg = raw_insn & 0xf; + let reg = insn & 0xf; - let shift_by = if raw_insn.bit(4) { + let shift_by = if insn.bit(4) { if rn == REG_PC { op1 += 4; } - let rs = raw_insn.bit_range(8..12) as usize; + let rs = insn.bit_range(8..12) as usize; ShiftRegisterBy::ByRegister(rs) } else { - let amount = raw_insn.bit_range(7..12) as u32; + let amount = insn.bit_range(7..12) as u32; ShiftRegisterBy::ByAmount(amount) }; let shifted_reg = ShiftedRegister { reg: reg as usize, - bs_op: get_bs_op(raw_insn), + bs_op: insn.get_bs_op(), shift_by: shift_by, added: None, }; @@ -277,7 +266,7 @@ impl Core { MOV => op2, BIC => op1 & (!op2), MVN => !op2, - _ => panic!("{} should be a PSR transfer", opcode), + _ => panic!("DataProcessing should be a PSR transfer"), }) }; @@ -304,17 +293,17 @@ impl Core { /// STR{cond}{B}{T} Rd,
| 2N | ---- | [Rn+/-]=Rd /// ------------------------------------------------------------------------------ /// For LDR, add y=1S+1N if Rd=R15. - pub fn exec_arm_ldr_str(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldr_str(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { let mut result = CpuAction::AdvancePC; let load = insn.load_flag(); let pre_index = insn.pre_index_flag(); let writeback = insn.write_back_flag(); - let base_reg = insn.raw.bit_range(16..20) as usize; - let dest_reg = insn.raw.bit_range(12..16) as usize; + let base_reg = insn.bit_range(16..20) as usize; + let dest_reg = insn.bit_range(12..16) as usize; let mut addr = self.get_reg(base_reg); if base_reg == REG_PC { - addr = insn.pc + 8; // prefetching + addr = self.pc_arm() + 8; // prefetching } let offset = self.get_barrel_shifted_value(&insn.ldr_str_offset()); let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr; @@ -352,7 +341,7 @@ impl Core { } } else { let value = if dest_reg == REG_PC { - insn.pc + 12 + self.pc_arm() + 12 } else { self.get_reg(dest_reg) }; @@ -381,12 +370,12 @@ impl Core { result } - pub fn exec_arm_ldr_str_hs_reg(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldr_str_hs_reg(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { self.ldr_str_hs( sb, insn, BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: (insn.raw & 0xf) as usize, + reg: (insn & 0xf) as usize, shift_by: ShiftRegisterBy::ByAmount(0), bs_op: BarrelShiftOpCode::LSL, added: Some(insn.add_offset_flag()), @@ -394,8 +383,8 @@ impl Core { ) } - pub fn exec_arm_ldr_str_hs_imm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - let offset8 = (insn.raw.bit_range(8..12) << 4) + insn.raw.bit_range(0..4); + pub fn exec_arm_ldr_str_hs_imm(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { + let offset8 = (insn.bit_range(8..12) << 4) + insn.bit_range(0..4); let offset8 = if insn.add_offset_flag() { offset8 } else { @@ -408,7 +397,7 @@ impl Core { pub fn ldr_str_hs( &mut self, sb: &mut SysBus, - insn: &ArmInstruction, + insn: u32, offset: BarrelShifterValue, ) -> CpuAction { let mut result = CpuAction::AdvancePC; @@ -416,11 +405,11 @@ impl Core { let load = insn.load_flag(); let pre_index = insn.pre_index_flag(); let writeback = insn.write_back_flag(); - let base_reg = insn.raw.bit_range(16..20) as usize; - let dest_reg = insn.raw.bit_range(12..16) as usize; + let base_reg = insn.bit_range(16..20) as usize; + let dest_reg = insn.bit_range(12..16) as usize; let mut addr = self.get_reg(base_reg); if base_reg == REG_PC { - addr = insn.pc + 8; // prefetching + addr = self.pc_arm() + 8; // prefetching } let offset = self.get_barrel_shifted_value(&offset); @@ -440,7 +429,7 @@ impl Core { if load { self.S_cycle32(sb, self.pc); - let data = match insn.halfword_data_transfer_type().unwrap() { + let data = match insn.halfword_data_transfer_type() { ArmHalfwordTransferType::SignedByte => { self.N_cycle8(sb, addr); sb.read_8(addr) as u8 as i8 as u32 @@ -466,12 +455,12 @@ impl Core { } } else { let value = if dest_reg == REG_PC { - insn.pc + 12 + self.pc_arm() + 12 } else { self.get_reg(dest_reg) }; - match insn.halfword_data_transfer_type().unwrap() { + match insn.halfword_data_transfer_type() { ArmHalfwordTransferType::UnsignedHalfwords => { self.N_cycle32(sb, addr); self.write_16(addr, value as u16, sb); @@ -492,15 +481,15 @@ impl Core { result } - pub fn exec_arm_ldm_stm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_ldm_stm(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { let mut result = CpuAction::AdvancePC; let mut full = insn.pre_index_flag(); let ascending = insn.add_offset_flag(); - let s_flag = insn.raw.bit(22); + let s_flag = insn.bit(22); let is_load = insn.load_flag(); let mut writeback = insn.write_back_flag(); - let base_reg = insn.raw.bit_range(16..20) as usize; + let base_reg = insn.bit_range(16..20) as usize; let mut base_addr = self.get_reg(base_reg); let rlist = insn.register_list(); @@ -583,7 +572,7 @@ impl Core { if rlist.bit(r) { let val = if r != base_reg { if r == REG_PC { - insn.pc + 12 + self.pc_arm() + 12 } else { self.get_reg(r) } @@ -653,9 +642,9 @@ impl Core { result } - pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - let rd = insn.raw.bit_range(16..20) as usize; - let rn = insn.raw.bit_range(12..16) as usize; + pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { + let rd = insn.bit_range(16..20) as usize; + let rn = insn.bit_range(12..16) as usize; let rs = insn.rs(); let rm = insn.rm(); @@ -691,7 +680,7 @@ impl Core { CpuAction::AdvancePC } - pub fn exec_arm_mull_mlal(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_mull_mlal(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { let rd_hi = insn.rd_hi(); let rd_lo = insn.rd_lo(); let rs = insn.rs(); @@ -734,9 +723,9 @@ impl Core { CpuAction::AdvancePC } - pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { - let base_addr = self.get_reg(insn.raw.bit_range(16..20) as usize); - let rd = insn.raw.bit_range(12..16) as usize; + pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { + let base_addr = self.get_reg(insn.bit_range(16..20) as usize); + let rd = insn.bit_range(12..16) as usize; if insn.transfer_size() == 1 { let t = sb.read_8(base_addr); self.N_cycle8(sb, base_addr); @@ -756,7 +745,7 @@ impl Core { CpuAction::AdvancePC } - pub fn exec_arm_swi(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { + pub fn exec_arm_swi(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction { self.software_interrupt(sb, self.pc - 4, insn.swi_comment()); CpuAction::FlushPipeline } diff --git a/core/src/arm7tdmi/arm/mod.rs b/core/src/arm7tdmi/arm/mod.rs index 34e4d38..4ccd384 100644 --- a/core/src/arm7tdmi/arm/mod.rs +++ b/core/src/arm7tdmi/arm/mod.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "debugger")] pub mod display; pub mod exec; @@ -21,7 +22,6 @@ pub enum ArmDecodeErrorKind { InvalidHSBits(u32), IoError(io::ErrorKind), } -use ArmDecodeErrorKind::*; #[derive(Debug, PartialEq)] pub struct ArmDecodeError { @@ -30,6 +30,7 @@ pub struct ArmDecodeError { pub addr: Addr, } +#[allow(dead_code)] impl ArmDecodeError { fn new(kind: ArmDecodeErrorKind, insn: u32, addr: Addr) -> ArmDecodeError { ArmDecodeError { @@ -57,6 +58,7 @@ pub enum ArmCond { GT = 0b1100, LE = 0b1101, AL = 0b1110, + Invalid = 0b1111, } #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] @@ -82,33 +84,10 @@ pub enum ArmFormat { Undefined, } -#[derive(Debug, PartialEq, Primitive)] -pub enum ArmHalfwordTransferType { - UnsignedHalfwords = 0b01, - SignedByte = 0b10, - SignedHalfwords = 0b11, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct ArmInstruction { - 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 { +impl From for ArmFormat { + fn from(raw: u32) -> ArmFormat { use ArmFormat::*; - - let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 { + if (0x0fff_fff0 & raw) == 0x012f_ff10 { BranchExchange } else if (0x0e00_0000 & raw) == 0x0a00_0000 { BranchLink @@ -140,7 +119,35 @@ impl InstructionDecoder for ArmInstruction { DataProcessing } else { Undefined - }; + } + } +} + +#[derive(Debug, PartialEq, Primitive)] +pub enum ArmHalfwordTransferType { + UnsignedHalfwords = 0b01, + SignedByte = 0b10, + SignedHalfwords = 0b11, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct ArmInstruction { + 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 { + let fmt = ArmFormat::from(raw); ArmInstruction { fmt: fmt, @@ -160,209 +167,266 @@ impl InstructionDecoder for ArmInstruction { } } -impl ArmInstruction { - fn make_decode_error(&self, kind: ArmDecodeErrorKind) -> ArmDecodeError { - ArmDecodeError { - kind: kind, - insn: self.raw, - addr: self.pc, - } - } +pub trait ArmDecodeHelper { + fn cond(&self) -> ArmCond; - pub fn cond(&self) -> ArmCond { - ArmCond::from_u32(self.raw.bit_range(28..32)).unwrap() - } + fn rm(&self) -> usize; - pub fn rn(&self) -> usize { - match self.fmt { - ArmFormat::Multiply => self.raw.bit_range(12..16) as usize, - ArmFormat::MultiplyLong => self.raw.bit_range(8..12) as usize, - ArmFormat::BranchExchange => self.raw.bit_range(0..4) as usize, - _ => self.raw.bit_range(16..20) as usize, - } - } + fn rs(&self) -> usize; - pub fn rd(&self) -> usize { - match self.fmt { - ArmFormat::Multiply => self.raw.bit_range(16..20) as usize, - _ => self.raw.bit_range(12..16) as usize, - } - } + fn rd_lo(&self) -> usize; - pub fn rm(&self) -> usize { - self.raw.bit_range(0..4) as usize - } + fn rd_hi(&self) -> usize; - pub fn rs(&self) -> usize { - self.raw.bit_range(8..12) as usize - } + fn opcode(&self) -> AluOpCode; - pub fn rd_lo(&self) -> usize { - self.raw.bit_range(12..16) as usize - } + fn branch_offset(&self) -> i32; - pub fn rd_hi(&self) -> usize { - self.raw.bit_range(16..20) as usize - } + fn load_flag(&self) -> bool; - pub fn opcode(&self) -> AluOpCode { - use std::hint::unreachable_unchecked; + fn set_cond_flag(&self) -> bool; - unsafe { - if let Some(opc) = AluOpCode::from_u16(self.raw.bit_range(21..25) as u16) { - opc - } else { - unreachable_unchecked() - } - } - } + fn write_back_flag(&self) -> bool; - pub fn branch_offset(&self) -> i32 { - ((self.raw.bit_range(0..24) << 8) as i32) >> 6 - } + fn accumulate_flag(&self) -> bool; - pub fn load_flag(&self) -> bool { - self.raw.bit(20) - } + fn u_flag(&self) -> bool; - pub fn set_cond_flag(&self) -> bool { - self.raw.bit(20) - } + fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType; - pub fn write_back_flag(&self) -> bool { - self.raw.bit(21) - } + fn transfer_size(&self) -> usize; - pub fn accumulate_flag(&self) -> bool { - self.raw.bit(21) - } + fn psr_and_force_user_flag(&self) -> bool; - pub fn u_flag(&self) -> bool { - self.raw.bit(22) - } + fn spsr_flag(&self) -> bool; - pub fn halfword_data_transfer_type(&self) -> Result { - let bits = (self.raw & 0b1100000) >> 5; - match ArmHalfwordTransferType::from_u32(bits) { - Some(x) => Ok(x), - None => Err(ArmDecodeError::new(InvalidHSBits(bits), self.raw, self.pc)), - } - } + fn add_offset_flag(&self) -> bool; - pub fn transfer_size(&self) -> usize { - if self.raw.bit(22) { - 1 - } else { - 4 - } - } + fn pre_index_flag(&self) -> bool; - pub fn psr_and_force_user_flag(&self) -> bool { - self.raw.bit(22) - } - - pub fn spsr_flag(&self) -> bool { - self.raw.bit(22) - } - - pub fn add_offset_flag(&self) -> bool { - self.raw.bit(23) - } - - pub fn pre_index_flag(&self) -> bool { - self.raw.bit(24) - } - - pub fn link_flag(&self) -> bool { - self.raw.bit(24) - } + fn link_flag(&self) -> bool; /// gets offset used by ldr/str instructions - pub fn ldr_str_offset(&self) -> BarrelShifterValue { - let ofs = self.raw.bit_range(0..12); - if self.raw.bit(25) { - let rm = ofs & 0xf; - BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: rm as usize, - shift_by: self.get_shift_reg_by(ofs), - bs_op: self.get_bs_op(ofs), - added: Some(self.add_offset_flag()), - }) - } else { - let ofs = if self.add_offset_flag() { - ofs as u32 - } else { - -(ofs as i32) as u32 - }; - BarrelShifterValue::ImmediateValue(ofs) - } - } + fn ldr_str_offset(&self) -> BarrelShifterValue; - #[inline(always)] - fn get_bs_op(&self, shift_field: u32) -> BarrelShiftOpCode { - BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap() - } + fn get_bs_op(&self) -> BarrelShiftOpCode; - #[inline(always)] - fn get_shift_reg_by(&self, shift_field: u32) -> ShiftRegisterBy { - if shift_field.bit(4) { - let rs = shift_field.bit_range(8..12) as usize; - ShiftRegisterBy::ByRegister(rs) - } else { - let amount = shift_field.bit_range(7..12) as u32; - ShiftRegisterBy::ByAmount(amount) - } - } + fn get_shift_reg_by(&self) -> ShiftRegisterBy; - pub fn ldr_str_hs_offset(&self) -> Result { - match self.fmt { - ArmFormat::HalfwordDataTransferImmediateOffset => { - let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4); - let offset8 = if self.add_offset_flag() { - offset8 - } else { - (-(offset8 as i32)) as u32 - }; - Ok(BarrelShifterValue::ImmediateValue(offset8)) - } - ArmFormat::HalfwordDataTransferRegOffset => { - Ok(BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: (self.raw & 0xf) as usize, - shift_by: ShiftRegisterBy::ByAmount(0), - bs_op: BarrelShiftOpCode::LSL, - added: Some(self.add_offset_flag()), - })) - } - _ => Err(self.make_decode_error(DecodedPartDoesNotBelongToInstruction)), - } - } + fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue; - pub fn operand2(&self) -> BarrelShifterValue { - if self.raw.bit(25) { - let immediate = self.raw & 0xff; - let rotate = 2 * self.raw.bit_range(8..12); - BarrelShifterValue::RotatedImmediate(immediate, rotate) - } else { - let reg = self.raw & 0xf; - let shifted_reg = ShiftedRegister { - reg: reg as usize, - bs_op: self.get_bs_op(self.raw), - shift_by: self.get_shift_reg_by(self.raw), - added: None, - }; // TODO error handling - BarrelShifterValue::ShiftedRegister(shifted_reg) - } - } + fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue; - pub fn register_list(&self) -> u16 { - (self.raw & 0xffff) as u16 - } + fn operand2(&self) -> BarrelShifterValue; - pub fn swi_comment(&self) -> u32 { - self.raw.bit_range(0..24) - } + fn register_list(&self) -> u16; + + fn swi_comment(&self) -> u32; } +macro_rules! arm_decode_helper_impl { + ($($t:ty),*) => {$( + + impl ArmDecodeHelper for $t { + #[inline(always)] + fn cond(&self) -> ArmCond { + ArmCond::from_u32(self.bit_range(28..32)).unwrap() + } + + #[inline(always)] + fn rm(&self) -> usize { + self.bit_range(0..4) as usize + } + + #[inline(always)] + fn rs(&self) -> usize { + self.bit_range(8..12) as usize + } + + #[inline(always)] + fn rd_lo(&self) -> usize { + self.bit_range(12..16) as usize + } + + #[inline(always)] + fn rd_hi(&self) -> usize { + self.bit_range(16..20) as usize + } + + #[inline(always)] + fn opcode(&self) -> AluOpCode { + use std::hint::unreachable_unchecked; + + unsafe { + if let Some(opc) = AluOpCode::from_u16(self.bit_range(21..25) as u16) { + opc + } else { + unreachable_unchecked() + } + } + } + + #[inline(always)] + fn branch_offset(&self) -> i32 { + ((self.bit_range(0..24) << 8) as i32) >> 6 + } + + #[inline(always)] + fn load_flag(&self) -> bool { + self.bit(20) + } + + #[inline(always)] + fn set_cond_flag(&self) -> bool { + self.bit(20) + } + + #[inline(always)] + fn write_back_flag(&self) -> bool { + self.bit(21) + } + + #[inline(always)] + fn accumulate_flag(&self) -> bool { + self.bit(21) + } + + #[inline(always)] + fn u_flag(&self) -> bool { + self.bit(22) + } + + #[inline(always)] + fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType { + let bits = (*self & 0b1100000) >> 5; + ArmHalfwordTransferType::from_u32(bits).unwrap() + } + + #[inline(always)] + fn transfer_size(&self) -> usize { + if self.bit(22) { + 1 + } else { + 4 + } + } + + #[inline(always)] + fn psr_and_force_user_flag(&self) -> bool { + self.bit(22) + } + + #[inline(always)] + fn spsr_flag(&self) -> bool { + self.bit(22) + } + + #[inline(always)] + fn add_offset_flag(&self) -> bool { + self.bit(23) + } + + #[inline(always)] + fn pre_index_flag(&self) -> bool { + self.bit(24) + } + + #[inline(always)] + fn link_flag(&self) -> bool { + self.bit(24) + } + + /// gets offset used by ldr/str instructions + #[inline(always)] + fn ldr_str_offset(&self) -> BarrelShifterValue { + let ofs = self.bit_range(0..12); + if self.bit(25) { + let rm = ofs & 0xf; + BarrelShifterValue::ShiftedRegister(ShiftedRegister { + reg: rm as usize, + shift_by: self.get_shift_reg_by(), + bs_op: self.get_bs_op(), + added: Some(self.add_offset_flag()), + }) + } else { + let ofs = if self.add_offset_flag() { + ofs as u32 + } else { + -(ofs as i32) as u32 + }; + BarrelShifterValue::ImmediateValue(ofs) + } + } + + #[inline(always)] + fn get_bs_op(&self) -> BarrelShiftOpCode { + BarrelShiftOpCode::from_u8(self.bit_range(5..7) as u8).unwrap() + } + + #[inline(always)] + fn get_shift_reg_by(&self) -> ShiftRegisterBy { + if self.bit(4) { + let rs = self.bit_range(8..12) as usize; + ShiftRegisterBy::ByRegister(rs) + } else { + let amount = self.bit_range(7..12) as u32; + ShiftRegisterBy::ByAmount(amount) + } + } + + #[inline(always)] + fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue { + let offset8 = (self.bit_range(8..12) << 4) + self.bit_range(0..4); + let offset8 = if self.add_offset_flag() { + offset8 + } else { + (-(offset8 as i32)) as u32 + }; + BarrelShifterValue::ImmediateValue(offset8) + } + + #[inline(always)] + fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue { + BarrelShifterValue::ShiftedRegister( + ShiftedRegister { + reg: (self & 0xf) as usize, + shift_by: ShiftRegisterBy::ByAmount(0), + bs_op: BarrelShiftOpCode::LSL, + added: Some(self.add_offset_flag()), + }) + } + + fn operand2(&self) -> BarrelShifterValue { + if self.bit(25) { + let immediate = self & 0xff; + let rotate = 2 * self.bit_range(8..12); + BarrelShifterValue::RotatedImmediate(immediate, rotate) + } else { + let reg = self & 0xf; + let shifted_reg = ShiftedRegister { + reg: reg as usize, + bs_op: self.get_bs_op(), + shift_by: self.get_shift_reg_by(), + added: None, + }; // TODO error handling + BarrelShifterValue::ShiftedRegister(shifted_reg) + } + } + + fn register_list(&self) -> u16 { + (self & 0xffff) as u16 + } + + fn swi_comment(&self) -> u32 { + self.bit_range(0..24) + } + } + )*} + +} + +arm_decode_helper_impl!(u32); + // #[cfg(test)] // /// All instructions constants were generated using an ARM assembler. // mod tests { diff --git a/core/src/arm7tdmi/cpu.rs b/core/src/arm7tdmi/cpu.rs index a6883ce..26de447 100644 --- a/core/src/arm7tdmi/cpu.rs +++ b/core/src/arm7tdmi/cpu.rs @@ -1,29 +1,35 @@ -#[cfg(feature = "debugger")] -use super::reg_string; -#[cfg(feature = "debugger")] -use ansi_term::{Colour, Style}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "debugger")] -use std::fmt; - -use super::arm::ArmCond; - -// Include files that are auto-generated by the build script -// See `build.rs` -#[cfg(feature = "arm7tdmi_dispatch_table")] -include!(concat!(env!("OUT_DIR"), "/arm_lut.rs")); -#[cfg(feature = "arm7tdmi_dispatch_table")] -include!(concat!(env!("OUT_DIR"), "/thumb_lut.rs")); - -#[cfg(not(feature = "arm7tdmi_dispatch_table"))] -use super::InstructionDecoder; pub use super::exception::Exception; use super::CpuAction; -#[cfg(feature = "debugger")] -use super::DecodedInstruction; -use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState}; +use super::{psr::RegPSR, Addr, CpuMode, CpuState, arm::ArmCond}; + +cfg_if! { + if #[cfg(feature = "arm7tdmi_dispatch_table")] { + // Include files that are auto-generated by the build script + // See `build.rs` + include!(concat!(env!("OUT_DIR"), "/arm_lut.rs")); + include!(concat!(env!("OUT_DIR"), "/thumb_lut.rs")); + } else { + use super::arm::ArmFormat; + use super::thumb::ThumbFormat; + } +} + +cfg_if! { + if #[cfg(feature = "debugger")] { + use super::DecodedInstruction; + use super::arm::ArmInstruction; + use super::thumb::ThumbInstruction; + use super::reg_string; + use std::fmt; + + use ansi_term::{Colour, Style}; + } else { + + } +} use crate::bus::Bus; use crate::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus}; @@ -90,6 +96,18 @@ impl Core { } } + #[inline] + /// Gets PC of the currently executed instruction in arm mode + pub fn pc_arm(&self) -> u32 { + self.pc.wrapping_sub(8) + } + + #[inline] + /// Gets PC of the currently executed instruction in thumb mode + pub fn pc_thumb(&self) -> u32 { + self.pc.wrapping_sub(4) + } + pub fn get_reg_user(&mut self, r: usize) -> u32 { match r { 0..=7 => self.gpr[r], @@ -291,6 +309,10 @@ impl Core { pub(super) fn check_arm_cond(&self, cond: ArmCond) -> bool { use ArmCond::*; match cond { + Invalid => { + // TODO - we would normally want to panic here + false + } EQ => self.cpsr.Z(), NE => !self.cpsr.Z(), HS => self.cpsr.C(), @@ -319,43 +341,56 @@ impl Core { fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction { let hash = (((insn >> 16) & 0xff0) | ((insn >> 4) & 0x00f)) as usize; let arm_info = &ARM_LUT[hash]; - let arm_insn = ArmInstruction::new(insn, self.pc.wrapping_sub(8), arm_info.fmt); #[cfg(feature = "debugger")] - self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone())); + self.debugger_record_step(DecodedInstruction::Arm(ArmInstruction::new( + insn, + self.pc.wrapping_sub(8), + arm_info.fmt, + ))); - (arm_info.handler_fn)(self, sb, &arm_insn) + (arm_info.handler_fn)(self, sb, 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())); + self.debugger_record_step(DecodedInstruction::Thumb(ThumbInstruction::new( + insn, + self.pc.wrapping_sub(4), + thumb_info.fmt, + ))); - (thumb_info.handler_fn)(self, sb, &thumb_insn) + (thumb_info.handler_fn)(self, sb, 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)); - + let arm_fmt = ArmFormat::from(insn); #[cfg(feature = "debugger")] - self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone())); + self.debugger_record_step(DecodedInstruction::Arm(ArmInstruction::new( + insn, + self.pc.wrapping_sub(8), + arm_fmt, + ))); - self.exec_arm(sb, &arm_insn) + self.exec_arm(sb, insn, arm_fmt) } #[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)); + let thumb_fmt = ThumbFormat::from(insn); #[cfg(feature = "debugger")] - self.debugger_record_step(DecodedInstruction::Thumb(thumb_insn.clone())); + self.debugger_record_step(DecodedInstruction::Thumb(ThumbInstruction::new( + insn, + self.pc.wrapping_sub(4), + thumb_fmt, + ))); - self.exec_thumb(sb, &thumb_insn) + self.exec_thumb(sb, insn, thumb_fmt) } #[inline(always)] @@ -399,8 +434,8 @@ impl Core { let insn = self.pipeline[0]; self.pipeline[0] = self.pipeline[1]; self.pipeline[1] = fetched_now; - let cond = - ArmCond::from_u32(insn.bit_range(28..32)).expect("invalid arm condition"); + let cond = ArmCond::from_u8(insn.bit_range(28..32) as u8) + .unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() }); if cond != ArmCond::AL { if !self.check_arm_cond(cond) { self.S_cycle32(bus, self.pc); diff --git a/core/src/arm7tdmi/mod.rs b/core/src/arm7tdmi/mod.rs index 2a33af6..c06cfcc 100644 --- a/core/src/arm7tdmi/mod.rs +++ b/core/src/arm7tdmi/mod.rs @@ -62,12 +62,12 @@ pub trait InstructionDecoder: Sized { fn get_raw(&self) -> Self::IntType; } -pub fn reg_string(reg: usize) -> &'static str { +pub fn reg_string>(reg: T) -> &'static str { let reg_names = &[ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc", ]; - reg_names[reg] + reg_names[reg.into()] } #[derive(Debug, PartialEq, Primitive, Copy, Clone)] diff --git a/core/src/arm7tdmi/thumb/display.rs b/core/src/arm7tdmi/thumb/display.rs index c167e28..3cd7e51 100644 --- a/core/src/arm7tdmi/thumb/display.rs +++ b/core/src/arm7tdmi/thumb/display.rs @@ -1,22 +1,21 @@ use std::fmt; -#[cfg(feature = "debugger")] use crate::bit::BitIndex; use super::*; -#[cfg(feature = "debugger")] use crate::arm7tdmi::*; -#[cfg(feature = "debugger")] +use super::ThumbDecodeHelper; + impl ThumbInstruction { fn fmt_thumb_move_shifted_reg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{op}\t{Rd}, {Rs}, #{Offset5}", - op = self.format1_op(), - Rd = reg_string(self.rd()), - Rs = reg_string(self.rs()), - Offset5 = self.offset5() + op = self.raw.format1_op(), + Rd = reg_string(self.raw & 0b111), + Rs = reg_string(self.raw.rs()), + Offset5 = self.raw.offset5() ) } @@ -24,8 +23,8 @@ impl ThumbInstruction { write!( f, "{op}\t{Rd}, #{Offset8:#x}", - op = self.format3_op(), - Rd = reg_string(self.rd()), + op = self.raw.format3_op(), + Rd = reg_string(self.raw.bit_range(8..11)), Offset8 = self.raw & 0xff ) } @@ -34,23 +33,23 @@ impl ThumbInstruction { write!( f, "{op}\t{Rd}, {Rs}", - op = self.format4_alu_op(), - Rd = reg_string(self.rd()), - Rs = reg_string(self.rs()) + op = self.raw.format4_alu_op(), + Rd = reg_string(self.raw & 0b111), + Rs = reg_string(self.raw.rs()) ) } fn fmt_thumb_high_reg_op_or_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let op = self.format5_op(); - let dst_reg = if self.flag(ThumbInstruction::FLAG_H1) { - self.rd() + 8 + let op = self.raw.format5_op(); + let dst_reg = if self.raw.flag(consts::flags::FLAG_H1) { + self.raw & 0b111 + 8 } else { - self.rd() + self.raw & 0b111 }; - let src_reg = if self.flag(ThumbInstruction::FLAG_H2) { - self.rs() + 8 + let src_reg = if self.raw.flag(consts::flags::FLAG_H2) { + self.raw.rs() + 8 } else { - self.rs() + self.raw.rs() }; write!(f, "{}\t", op)?; @@ -69,9 +68,9 @@ impl ThumbInstruction { write!( f, "ldr\t{Rd}, [pc, #{Imm:#x}] ; = #{effective:#x}", - Rd = reg_string(self.rd()), - Imm = self.word8(), - effective = (self.pc + 4 & !0b10) + (self.word8() as Addr) + Rd = reg_string(self.raw.bit_range(8..11)), + Imm = self.raw.word8(), + effective = (self.pc + 4 & !0b10) + (self.raw.word8() as Addr) ) } @@ -79,15 +78,11 @@ impl ThumbInstruction { write!( f, "{op}{b}\t{Rd}, [{Rb}, {Ro}]", - op = if self.is_load() { "ldr" } else { "str" }, - b = if self.is_transferring_bytes() { - "b" - } else { - "" - }, - Rd = reg_string(self.rd()), - Rb = reg_string(self.rb()), - Ro = reg_string(self.ro()), + op = if self.raw.is_load() { "ldr" } else { "str" }, + b = if self.raw.bit(10) { "b" } else { "" }, + Rd = reg_string(self.raw & 0b111), + Rb = reg_string(self.raw.rb()), + Ro = reg_string(self.raw.ro()), ) } @@ -97,8 +92,8 @@ impl ThumbInstruction { "{op}\t{Rd}, [{Rb}, {Ro}]", op = { match ( - self.flag(ThumbInstruction::FLAG_SIGN_EXTEND), - self.flag(ThumbInstruction::FLAG_HALFWORD), + self.raw.flag(consts::flags::FLAG_SIGN_EXTEND), + self.raw.flag(consts::flags::FLAG_HALFWORD), ) { (false, false) => "strh", (false, true) => "ldrh", @@ -106,27 +101,24 @@ impl ThumbInstruction { (true, true) => "ldsh", } }, - Rd = reg_string(self.rd()), - Rb = reg_string(self.rb()), - Ro = reg_string(self.ro()), + Rd = reg_string(self.raw & 0b111), + Rb = reg_string(self.raw.rb()), + Ro = reg_string(self.raw.ro()), ) } fn fmt_thumb_ldr_str_imm_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_transferring_bytes = self.raw.bit(12); write!( f, "{op}{b}\t{Rd}, [{Rb}, #{imm:#x}]", - op = if self.is_load() { "ldr" } else { "str" }, - b = if self.is_transferring_bytes() { - "b" - } else { - "" - }, - Rd = reg_string(self.rd()), - Rb = reg_string(self.rb()), + op = if self.raw.is_load() { "ldr" } else { "str" }, + b = if is_transferring_bytes { "b" } else { "" }, + Rd = reg_string(self.raw & 0b111), + Rb = reg_string(self.raw.rb()), imm = { - let offset5 = self.offset5(); - if self.is_transferring_bytes() { + let offset5 = self.raw.offset5(); + if is_transferring_bytes { offset5 } else { (offset5 << 3) >> 1 @@ -139,10 +131,10 @@ impl ThumbInstruction { write!( f, "{op}\t{Rd}, [{Rb}, #{imm:#x}]", - op = if self.is_load() { "ldrh" } else { "strh" }, - Rd = reg_string(self.rd()), - Rb = reg_string(self.rb()), - imm = self.offset5() << 1 + op = if self.raw.is_load() { "ldrh" } else { "strh" }, + Rd = reg_string(self.raw & 0b111), + Rb = reg_string(self.raw.rb()), + imm = self.raw.offset5() << 1 ) } @@ -150,9 +142,9 @@ impl ThumbInstruction { write!( f, "{op}\t{Rd}, [sp, #{Imm:#x}]", - op = if self.is_load() { "ldr" } else { "str" }, - Rd = reg_string(self.rd()), - Imm = self.word8(), + op = if self.raw.is_load() { "ldr" } else { "str" }, + Rd = reg_string(self.raw.bit_range(8..11)), + Imm = self.raw.word8(), ) } @@ -160,35 +152,35 @@ impl ThumbInstruction { write!( f, "add\t{Rd}, {r}, #{Imm:#x}", - Rd = reg_string(self.rd()), - r = if self.flag(ThumbInstruction::FLAG_SP) { + Rd = reg_string(self.raw.bit_range(8..11)), + r = if self.raw.flag(consts::flags::FLAG_SP) { "sp" } else { "pc" }, - Imm = self.word8(), + Imm = self.raw.word8(), ) } fn fmt_thumb_add_sub(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let operand = if self.is_immediate_operand() { + let operand = if self.raw.is_immediate_operand() { format!("#{:x}", self.raw.bit_range(6..9)) } else { - String::from(reg_string(self.rn())) + String::from(reg_string(self.raw.rn())) }; write!( f, "{op}\t{Rd}, {Rs}, {operand}", - op = if self.is_subtract() { "sub" } else { "add" }, - Rd = reg_string(self.rd()), - Rs = reg_string(self.rs()), + op = if self.raw.is_subtract() { "sub" } else { "add" }, + Rd = reg_string(self.raw & 0b111), + Rs = reg_string(self.raw.rs()), operand = operand ) } fn fmt_thumb_add_sp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "add\tsp, #{imm:x}", imm = self.sword7()) + write!(f, "add\tsp, #{imm:x}", imm = self.raw.sword7()) } fn fmt_register_list(&self, f: &mut fmt::Formatter<'_>, rlist: u8) -> fmt::Result { @@ -207,11 +199,11 @@ impl ThumbInstruction { } fn fmt_thumb_push_pop(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}\t{{", if self.is_load() { "pop" } else { "push" })?; - let rlist = self.register_list(); + write!(f, "{}\t{{", if self.raw.is_load() { "pop" } else { "push" })?; + let rlist = self.raw.register_list(); self.fmt_register_list(f, rlist)?; - if self.flag(ThumbInstruction::FLAG_R) { - let r = if self.is_load() { "pc" } else { "lr" }; + if self.raw.flag(consts::flags::FLAG_R) { + let r = if self.raw.is_load() { "pc" } else { "lr" }; if rlist != 0 { write!(f, ", {}", r)?; } else { @@ -225,10 +217,10 @@ impl ThumbInstruction { write!( f, "{op}\t{Rb}!, {{", - op = if self.is_load() { "ldm" } else { "stm" }, - Rb = reg_string(self.rb()), + op = if self.raw.is_load() { "ldm" } else { "stm" }, + Rb = reg_string(self.raw.rb()), )?; - self.fmt_register_list(f, self.register_list())?; + self.fmt_register_list(f, self.raw.register_list())?; write!(f, "}}") } @@ -236,9 +228,9 @@ impl ThumbInstruction { write!( f, "b{cond}\t{addr:#x}", - cond = self.cond(), + cond = self.raw.cond(), addr = { - let offset = self.bcond_offset(); + let offset = self.raw.bcond_offset(); (self.pc as i32 + 4).wrapping_add(offset) as Addr } ) @@ -253,7 +245,7 @@ impl ThumbInstruction { f, "b\t{addr:#x}", addr = { - let offset = (self.offset11() << 21) >> 20; + let offset = (self.raw.offset11() << 21) >> 20; (self.pc as i32 + 4).wrapping_add(offset) as Addr } ) @@ -261,8 +253,8 @@ impl ThumbInstruction { fn fmt_thumb_branch_long_with_link(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "bl\t#0x{:08x}", { - let offset11 = self.offset11(); - if self.flag(ThumbInstruction::FLAG_LOW_OFFSET) { + let offset11 = self.raw.offset11(); + if self.raw.flag(consts::flags::FLAG_LOW_OFFSET) { (offset11 << 1) as i32 } else { ((offset11 << 21) >> 9) as i32 diff --git a/core/src/arm7tdmi/thumb/exec.rs b/core/src/arm7tdmi/thumb/exec.rs index bf8859a..1b6b27c 100644 --- a/core/src/arm7tdmi/thumb/exec.rs +++ b/core/src/arm7tdmi/thumb/exec.rs @@ -4,7 +4,9 @@ use crate::Bus; use crate::bit::BitIndex; +use super::ThumbDecodeHelper; use super::*; + fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) { cpu.gpr[REG_SP] -= 4; let stack_addr = cpu.gpr[REG_SP] & !3; @@ -21,10 +23,10 @@ impl Core { pub(in super::super) fn exec_thumb_move_shifted_reg( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rd = (insn.raw & 0b111) as usize; - let rs = insn.raw.bit_range(3..6) as usize; + let rd = (insn & 0b111) as usize; + let rs = insn.bit_range(3..6) as usize; let shift_amount = insn.offset5() as u8 as u32; let op2 = self.barrel_shift_op( @@ -43,12 +45,8 @@ impl Core { } /// Format 2 - pub(in super::super) fn exec_thumb_add_sub( - &mut self, - sb: &mut SysBus, - insn: &ThumbInstruction, - ) -> CpuAction { - let rd = (insn.raw & 0b111) as usize; + pub(in super::super) fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction { + let rd = (insn & 0b111) as usize; let op1 = self.get_reg(insn.rs()); let op2 = if insn.is_immediate_operand() { insn.rn() as u32 @@ -75,13 +73,13 @@ impl Core { pub(in super::super) fn exec_thumb_data_process_imm( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { use OpFormat3::*; let op = insn.format3_op(); - let rd = insn.raw.bit_range(8..11) as usize; + let rd = insn.bit_range(8..11) as usize; let op1 = self.gpr[rd]; - let op2_imm = (insn.raw & 0xff) as u32; + let op2_imm = (insn & 0xff) as u32; let mut carry = self.cpsr.C(); let mut overflow = self.cpsr.V(); let result = match op { @@ -100,12 +98,8 @@ impl Core { } /// Format 4 - pub(in super::super) fn exec_thumb_alu_ops( - &mut self, - sb: &mut SysBus, - insn: &ThumbInstruction, - ) -> CpuAction { - let rd = (insn.raw & 0b111) as usize; + pub(in super::super) fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction { + let rd = (insn & 0b111) as usize; let rs = insn.rs(); let dst = self.get_reg(rd); let src = self.get_reg(rs); @@ -164,16 +158,16 @@ impl Core { pub(in super::super) fn exec_thumb_hi_reg_op_or_bx( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { let op = insn.format5_op(); - let rd = (insn.raw & 0b111) as usize; - let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) { + let rd = (insn & 0b111) as usize; + let dst_reg = if insn.bit(consts::flags::FLAG_H1) { rd + 8 } else { rd }; - let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) { + let src_reg = if insn.bit(consts::flags::FLAG_H2) { insn.rs() + 8 } else { insn.rs() @@ -213,12 +207,8 @@ impl Core { } /// Format 6 - 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; + pub(in super::super) fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction { + let rd = insn.bit_range(8..11) as usize; let ofs = insn.word8() as Addr; let addr = (self.pc & !3) + ofs; @@ -238,11 +228,12 @@ impl Core { fn do_exec_thumb_ldr_str( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, + addr: Addr, is_transferring_bytes: bool, ) -> CpuAction { - let rd = (insn.raw & 0b111) as usize; + let rd = (insn & 0b111) as usize; if insn.is_load() { let data = if is_transferring_bytes { self.S_cycle8(sb, addr); @@ -276,26 +267,26 @@ impl Core { pub(in super::super) fn exec_thumb_ldr_str_reg_offset( &mut self, bus: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rb = insn.raw.bit_range(3..6) as usize; + let rb = insn.bit_range(3..6) as usize; let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]); - self.do_exec_thumb_ldr_str(bus, insn, addr, insn.raw.bit(10)) + self.do_exec_thumb_ldr_str(bus, insn, addr, insn.bit(10)) } /// Format 8 pub(in super::super) fn exec_thumb_ldr_str_shb( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rb = insn.raw.bit_range(3..6) as usize; - let rd = (insn.raw & 0b111) as usize; + let rb = insn.bit_range(3..6) as usize; + let rd = (insn & 0b111) as usize; let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]); match ( - insn.flag(ThumbInstruction::FLAG_SIGN_EXTEND), - insn.flag(ThumbInstruction::FLAG_HALFWORD), + insn.bit(consts::flags::FLAG_SIGN_EXTEND), + insn.bit(consts::flags::FLAG_HALFWORD), ) { (false, false) => /* strh */ @@ -337,27 +328,27 @@ impl Core { pub(in super::super) fn exec_thumb_ldr_str_imm_offset( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rb = insn.raw.bit_range(3..6) as usize; + let rb = insn.bit_range(3..6) as usize; - let offset = if insn.raw.bit(12) { + let offset = if insn.bit(12) { insn.offset5() } else { (insn.offset5() << 3) >> 1 }; let addr = self.gpr[rb].wrapping_add(offset as u32); - self.do_exec_thumb_ldr_str(sb, insn, addr, insn.raw.bit(12)) + self.do_exec_thumb_ldr_str(sb, insn, addr, insn.bit(12)) } /// Format 10 pub(in super::super) fn exec_thumb_ldr_str_halfword( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rb = insn.raw.bit_range(3..6) as usize; - let rd = (insn.raw & 0b111) as usize; + let rb = insn.bit_range(3..6) as usize; + let rd = (insn & 0b111) as usize; let base = self.gpr[rb] as i32; let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr; if insn.is_load() { @@ -378,10 +369,10 @@ impl Core { pub(in super::super) fn exec_thumb_ldr_str_sp( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { let addr = self.gpr[REG_SP] + (insn.word8() as Addr); - let rd = insn.raw.bit_range(8..11) as usize; + let rd = insn.bit_range(8..11) as usize; if insn.is_load() { let data = self.ldr_word(addr, sb); self.S_cycle16(sb, addr); @@ -400,13 +391,13 @@ impl Core { pub(in super::super) fn exec_thumb_load_address( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { - let rd = insn.raw.bit_range(8..11) as usize; - let result = if insn.flag(ThumbInstruction::FLAG_SP) { + let rd = insn.bit_range(8..11) as usize; + let result = if insn.bit(consts::flags::FLAG_SP) { self.gpr[REG_SP] + (insn.word8() as Addr) } else { - (insn.pc & !0b10) + 4 + (insn.word8() as Addr) + (self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr) }; self.gpr[rd] = result; self.S_cycle16(sb, self.pc + 2); @@ -415,11 +406,7 @@ impl Core { } /// Format 13 - pub(in super::super) 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: u16) -> CpuAction { let op1 = self.gpr[REG_SP] as i32; let op2 = insn.sword7(); @@ -433,13 +420,13 @@ impl Core { pub(in super::super) fn exec_thumb_push_pop( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> 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). let is_pop = insn.is_load(); - let pc_lr_flag = insn.flag(ThumbInstruction::FLAG_R); + let pc_lr_flag = insn.bit(consts::flags::FLAG_R); let rlist = insn.register_list(); self.N_cycle16(sb, self.pc); let mut first = true; @@ -482,16 +469,12 @@ impl Core { } /// Format 15 - pub(in super::super) 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: u16) -> 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). - let rb = insn.raw.bit_range(8..11) as usize; + let rb = insn.bit_range(8..11) as usize; let base_reg = rb; let is_load = insn.is_load(); @@ -566,7 +549,7 @@ impl Core { pub(in super::super) fn exec_thumb_branch_with_cond( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { if !self.check_arm_cond(insn.cond()) { self.S_cycle16(sb, self.pc + 2); @@ -581,22 +564,14 @@ impl Core { } /// Format 17 - pub(in super::super) 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: u16) -> CpuAction { self.N_cycle16(sb, self.pc); self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2); CpuAction::FlushPipeline } /// Format 18 - pub(in super::super) 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: u16) -> 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); @@ -608,10 +583,10 @@ impl Core { pub(in super::super) fn exec_thumb_branch_long_with_link( &mut self, sb: &mut SysBus, - insn: &ThumbInstruction, + insn: u16, ) -> CpuAction { let mut off = insn.offset11(); - if insn.flag(ThumbInstruction::FLAG_LOW_OFFSET) { + if insn.bit(consts::flags::FLAG_LOW_OFFSET) { self.S_cycle16(sb, self.pc); off = off << 1; let next_pc = (self.pc - 2) | 1; @@ -628,15 +603,17 @@ impl Core { } } - pub fn thumb_undefined(&mut self, _: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { + pub fn thumb_undefined(&mut self, _: &mut SysBus, insn: u16) -> CpuAction { panic!( "executing undefind thumb instruction {:04x} at @{:08x}", - insn.raw, insn.pc + insn, + self.pc_thumb() ) } - pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: &ThumbInstruction) -> CpuAction { - match insn.fmt { + #[cfg(not(feature = "arm7tdmi_dispatch_table"))] + pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: u16, fmt: ThumbFormat) -> CpuAction { + match fmt { ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn), ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn), ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(bus, insn), diff --git a/core/src/arm7tdmi/thumb/mod.rs b/core/src/arm7tdmi/thumb/mod.rs index d006cd8..7878b0e 100644 --- a/core/src/arm7tdmi/thumb/mod.rs +++ b/core/src/arm7tdmi/thumb/mod.rs @@ -5,6 +5,7 @@ use crate::bit::BitIndex; use crate::byteorder::{LittleEndian, ReadBytesExt}; use crate::num::FromPrimitive; +#[cfg(feature = "debugger")] pub mod display; pub mod exec; @@ -53,26 +54,10 @@ pub enum ThumbFormat { Undefined, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct ThumbInstruction { - pub fmt: ThumbFormat, - pub raw: u16, - 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; - - fn decode(raw: u16, addr: Addr) -> Self { - use self::ThumbFormat::*; - - let fmt = if raw & 0xf800 == 0x1800 { +impl From for ThumbFormat { + fn from(raw: u16) -> ThumbFormat { + use ThumbFormat::*; + if raw & 0xf800 == 0x1800 { AddSub } else if raw & 0xe000 == 0x0000 { MoveShiftedReg @@ -112,8 +97,28 @@ impl InstructionDecoder for ThumbInstruction { BranchLongWithLink } else { Undefined - }; + } + } +} +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct ThumbInstruction { + pub fmt: ThumbFormat, + pub raw: u16, + 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; + + fn decode(raw: u16, addr: Addr) -> Self { + let fmt = ThumbFormat::from(raw); ThumbInstruction::new(raw, addr, fmt) } @@ -203,119 +208,176 @@ impl From for AluOpCode { } } -impl ThumbInstruction { - const FLAG_H1: usize = 7; - const FLAG_H2: usize = 6; - const FLAG_R: usize = 8; - const FLAG_S: usize = 7; - const FLAG_LOW_OFFSET: usize = 11; - const FLAG_SP: usize = 11; - const FLAG_SIGN_EXTEND: usize = 10; - const FLAG_HALFWORD: usize = 11; - - pub fn rd(&self) -> usize { - match self.fmt { - ThumbFormat::DataProcessImm - | ThumbFormat::LdrPc - | ThumbFormat::LdrStrSp - | ThumbFormat::LoadAddress => self.raw.bit_range(8..11) as usize, - _ => (self.raw & 0b111) as usize, - } - } - - pub fn rs(&self) -> usize { - self.raw.bit_range(3..6) as usize - } - - pub fn rb(&self) -> usize { - match self.fmt { - ThumbFormat::LdmStm => self.raw.bit_range(8..11) as usize, - _ => self.raw.bit_range(3..6) as usize, - } - } - - pub fn ro(&self) -> usize { - self.raw.bit_range(6..9) as usize - } - - pub fn rn(&self) -> usize { - self.raw.bit_range(6..9) as usize - } - - pub fn format1_op(&self) -> BarrelShiftOpCode { - BarrelShiftOpCode::from_u8(self.raw.bit_range(11..13) as u8).unwrap() - } - - pub fn format3_op(&self) -> OpFormat3 { - OpFormat3::from_u8(self.raw.bit_range(11..13) as u8).unwrap() - } - - pub fn format5_op(&self) -> OpFormat5 { - OpFormat5::from_u8(self.raw.bit_range(8..10) as u8).unwrap() - } - - pub fn format4_alu_op(&self) -> ThumbAluOps { - ThumbAluOps::from_u16(self.raw.bit_range(6..10)).unwrap() - } - - pub fn offset5(&self) -> u8 { - self.raw.bit_range(6..11) as u8 - } - - pub fn bcond_offset(&self) -> i32 { - ((((self.raw & 0xff) as u32) << 24) as i32) >> 23 - } - - pub fn offset11(&self) -> i32 { - (self.raw & 0x7FF) as i32 - } - - pub fn word8(&self) -> u16 { - (self.raw & 0xff) << 2 - } - - pub fn is_transferring_bytes(&self) -> bool { - match self.fmt { - ThumbFormat::LdrStrRegOffset => self.raw.bit(10), - ThumbFormat::LdrStrImmOffset => self.raw.bit(12), - _ => unreachable!(), - } - } - - pub fn is_load(&self) -> bool { - self.raw.bit(11) - } - - pub fn is_subtract(&self) -> bool { - self.raw.bit(9) - } - - pub fn is_immediate_operand(&self) -> bool { - self.raw.bit(10) - } - - pub fn cond(&self) -> ArmCond { - ArmCond::from_u8(self.raw.bit_range(8..12) as u8).expect("bad condition") - } - - pub fn flag(&self, bit: usize) -> bool { - self.raw.bit(bit) - } - - pub fn register_list(&self) -> u8 { - (self.raw & 0xff) as u8 - } - - pub fn sword7(&self) -> i32 { - let imm7 = self.raw & 0x7f; - if self.flag(ThumbInstruction::FLAG_S) { - -((imm7 << 2) as i32) - } else { - (imm7 << 2) as i32 - } +pub(super) mod consts { + pub(super) mod flags { + pub const FLAG_H1: usize = 7; + pub const FLAG_H2: usize = 6; + pub const FLAG_R: usize = 8; + pub const FLAG_S: usize = 7; + pub const FLAG_LOW_OFFSET: usize = 11; + pub const FLAG_SP: usize = 11; + pub const FLAG_SIGN_EXTEND: usize = 10; + pub const FLAG_HALFWORD: usize = 11; } } +/// A trait which provides methods to extract thumb instruction fields +pub trait ThumbDecodeHelper { + // Consts + + // Methods + + fn rs(&self) -> usize; + + fn rb(&self) -> usize; + + fn ro(&self) -> usize; + + fn rn(&self) -> usize; + + fn format1_op(&self) -> BarrelShiftOpCode; + + fn format3_op(&self) -> OpFormat3; + + fn format5_op(&self) -> OpFormat5; + + fn format4_alu_op(&self) -> ThumbAluOps; + + fn offset5(&self) -> u8; + + fn bcond_offset(&self) -> i32; + + fn offset11(&self) -> i32; + + fn word8(&self) -> u16; + + fn is_load(&self) -> bool; + + fn is_subtract(&self) -> bool; + + fn is_immediate_operand(&self) -> bool; + + fn cond(&self) -> ArmCond; + + fn flag(self, bit: usize) -> bool; + + fn register_list(&self) -> u8; + + fn sword7(&self) -> i32; +} + +macro_rules! thumb_decode_helper_impl { + ($($t:ty),*) => {$( + + impl ThumbDecodeHelper for $t { + + #[inline] + fn rs(&self) -> usize { + self.bit_range(3..6) as usize + } + + #[inline] + /// Note: not true for LdmStm + fn rb(&self) -> usize { + self.bit_range(3..6) as usize + } + + #[inline] + fn ro(&self) -> usize { + self.bit_range(6..9) as usize + } + + #[inline] + fn rn(&self) -> usize { + self.bit_range(6..9) as usize + } + + #[inline] + fn format1_op(&self) -> BarrelShiftOpCode { + BarrelShiftOpCode::from_u8(self.bit_range(11..13) as u8).unwrap() + } + + #[inline] + fn format3_op(&self) -> OpFormat3 { + OpFormat3::from_u8(self.bit_range(11..13) as u8).unwrap() + } + + #[inline] + fn format5_op(&self) -> OpFormat5 { + OpFormat5::from_u8(self.bit_range(8..10) as u8).unwrap() + } + + #[inline] + fn format4_alu_op(&self) -> ThumbAluOps { + ThumbAluOps::from_u16(self.bit_range(6..10)).unwrap() + } + + #[inline] + fn offset5(&self) -> u8 { + self.bit_range(6..11) as u8 + } + + #[inline] + fn bcond_offset(&self) -> i32 { + ((((*self & 0xff) as u32) << 24) as i32) >> 23 + } + + #[inline] + fn offset11(&self) -> i32 { + (*self & 0x7FF) as i32 + } + + #[inline] + fn word8(&self) -> u16 { + (*self & 0xff) << 2 + } + + #[inline] + fn is_load(&self) -> bool { + self.bit(11) + } + + #[inline] + fn is_subtract(&self) -> bool { + self.bit(9) + } + + #[inline] + fn is_immediate_operand(&self) -> bool { + self.bit(10) + } + + #[inline] + fn cond(&self) -> ArmCond { + ArmCond::from_u8(self.bit_range(8..12) as u8).expect("bad condition") + } + + #[inline] + fn flag(self, bit: usize) -> bool { + self.bit(bit) + } + + #[inline] + fn register_list(&self) -> u8 { + (*self & 0xff) as u8 + } + + #[inline] + fn sword7(&self) -> i32 { + let imm7 = *self & 0x7f; + if self.bit(consts::flags::FLAG_S) { + -((imm7 << 2) as i32) + } else { + (imm7 << 2) as i32 + } + } + } + + )*} +} + +thumb_decode_helper_impl!(u16); + // #[cfg(test)] // /// All instructions constants were generated using an ARM assembler. // mod tests { diff --git a/core/src/lib.rs b/core/src/lib.rs index e89a4a6..c35a956 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,6 +26,9 @@ extern crate log; #[macro_use] extern crate hex_literal; +#[macro_use] +extern crate cfg_if; + use zip; use std::error::Error;