From fb93ebb4ed2725b17b9d38eb6af9b4727ab2e9ee Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Tue, 7 Apr 2020 02:25:35 +0300 Subject: [PATCH] feat/arm7tdmi_dispatch_table: Done! Moved away from using lazy_static for this one since every access to a lazy_static reference adds a runtime check, Currently using a build.rs script to generate a const table instead. Worked out the ARM decoding issues, seems to run fine now. Though the dynamic decoding for ARM is broken now since I had to change things in Data Processing and MSR instructions TODO use `const fn` when it becomes stable Former-commit-id: ba09748ff74a403c7016adcbe0ca553b591f6855 --- Cargo.toml | 8 +- build.rs | 309 +++++++++++++++++++++++++++++++ src/core/arm7tdmi/arm/display.rs | 13 +- src/core/arm7tdmi/arm/exec.rs | 81 ++++---- src/core/arm7tdmi/arm/lut.rs | 161 ---------------- src/core/arm7tdmi/arm/mod.rs | 5 - src/core/arm7tdmi/cpu.rs | 38 ++-- src/core/arm7tdmi/thumb/lut.rs | 65 ------- src/core/arm7tdmi/thumb/mod.rs | 4 - src/lib.rs | 4 - 10 files changed, 379 insertions(+), 309 deletions(-) create mode 100644 build.rs delete mode 100644 src/core/arm7tdmi/arm/lut.rs delete mode 100644 src/core/arm7tdmi/thumb/lut.rs diff --git a/Cargo.toml b/Cargo.toml index a62aa9b..dc1ab4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ ] [dependencies] -lazy_static = {version="1.4.0", optional = true} serde = {version = "1.0.104", features = ["derive"] } bincode = "1.2.1" byteorder = "1" @@ -39,13 +38,16 @@ rustyline = {version = "6.0.0", optional = true} nom = {version = "5.0.0", optional = true} gdbstub = {git = "https://github.com/daniel5151/gdbstub.git", optional = true, features = ["std"], rev = "9686df6d74c4bf45cbc5746273b82640e8852b6d"} +[build-dependencies] +bit = "^0.1" + [features] +default = ["arm7tdmi_dispatch_table"] 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"] +arm7tdmi_dispatch_table = [] [profile.dev] opt-level = 0 diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..83a74ea --- /dev/null +++ b/build.rs @@ -0,0 +1,309 @@ +use std::env; +use std::fs; +use std::io::Write; +use std::path::Path; + +extern crate bit; +use bit::BitIndex; + +// copied and slightly adjusted from src/core/arm7tdmi/thumb/mod.rs +fn thumb_decode(i: u16) -> &'static str { + if i & 0xf800 == 0x1800 { + "AddSub" + } else if i & 0xe000 == 0x0000 { + "MoveShiftedReg" + } else if i & 0xe000 == 0x2000 { + "DataProcessImm" + } else if i & 0xfc00 == 0x4000 { + "AluOps" + } else if i & 0xfc00 == 0x4400 { + "HiRegOpOrBranchExchange" + } else if i & 0xf800 == 0x4800 { + "LdrPc" + } else if i & 0xf200 == 0x5000 { + "LdrStrRegOffset" + } else if i & 0xf200 == 0x5200 { + "LdrStrSHB" + } else if i & 0xe000 == 0x6000 { + "LdrStrImmOffset" + } else if i & 0xf000 == 0x8000 { + "LdrStrHalfWord" + } else if i & 0xf000 == 0x9000 { + "LdrStrSp" + } else if i & 0xf000 == 0xa000 { + "LoadAddress" + } else if i & 0xff00 == 0xb000 { + "AddSp" + } else if i & 0xf600 == 0xb400 { + "PushPop" + } else if i & 0xf000 == 0xc000 { + "LdmStm" + } else if i & 0xff00 == 0xdf00 { + "Swi" + } else if i & 0xf000 == 0xd000 { + "BranchConditional" + } else if i & 0xf800 == 0xe000 { + "Branch" + } else if i & 0xf000 == 0xf000 { + "BranchLongWithLink" + } else { + "Undefined" + } +} + +trait BitAsInt>: BitIndex { + fn ibit(&self, i: usize) -> T { + self.bit(i).into() + } +} + +impl BitAsInt for u32 {} + +/// Returns a string representation of rustboyadvance_ng::core::arm7tdmi::arm::ArmFormat enum member +/// # Arguments +/// * `i` - A 32bit ARM instruction +/// +/// Decoding is according to this table from http://problemkaputt.de/gbatek.htm#ARMBinaryOpcodeFormat +/// ``` +/// |..3 ..................2 ..................1 ..................0| +/// |1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0| +/// |_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| DataProc +/// |_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Rs___|0|Typ|1|__Rm___| DataProc +/// |_Cond__|0_0_1|___Op__|S|__Rn___|__Rd___|_Shift_|___Immediate___| DataProc +/// |_Cond__|0_0_1_1_0_0_1_0_0_0_0_0_1_1_1_1_0_0_0_0|_____Hint______| ARM11:Hint +/// |_Cond__|0_0_1_1_0|P|1|0|_Field_|__Rd___|_Shift_|___Immediate___| PSR Imm +/// |_Cond__|0_0_0_1_0|P|L|0|_Field_|__Rd___|0_0_0_0|0_0_0_0|__Rm___| PSR Reg +/// |_Cond__|0_0_0_1_0_0_1_0_1_1_1_1_1_1_1_1_1_1_1_1|0_0|L|1|__Rn___| BX,BLX +/// |1_1_1_0|0_0_0_1_0_0_1_0|_____immediate_________|0_1_1_1|_immed_| ARM9:BKPT +/// |_Cond__|0_0_0_1_0_1_1_0_1_1_1_1|__Rd___|1_1_1_1|0_0_0_1|__Rm___| ARM9:CLZ +/// |_Cond__|0_0_0_1_0|Op_|0|__Rn___|__Rd___|0_0_0_0|0_1_0_1|__Rm___| ARM9:QALU +/// |_Cond__|0_0_0_0_0_0|A|S|__Rd___|__Rn___|__Rs___|1_0_0_1|__Rm___| Multiply +/// |_Cond__|0_0_0_0_0_1_0_0|_RdHi__|_RdLo__|__Rs___|1_0_0_1|__Rm___| ARM11:UMAAL +/// |_Cond__|0_0_0_0_1|U|A|S|_RdHi__|_RdLo__|__Rs___|1_0_0_1|__Rm___| MulLong +/// |_Cond__|0_0_0_1_0|Op_|0|Rd/RdHi|Rn/RdLo|__Rs___|1|y|x|0|__Rm___| MulHalfARM9 +/// |_Cond__|0_0_0_1_0|B|0_0|__Rn___|__Rd___|0_0_0_0|1_0_0_1|__Rm___| TransSwp12 +/// |_Cond__|0_0_0_1_1|_Op__|__Rn___|__Rd___|1_1_1_1|1_0_0_1|__Rm___| ARM11:LDREX +/// |_Cond__|0_0_0|P|U|0|W|L|__Rn___|__Rd___|0_0_0_0|1|S|H|1|__Rm___| TransReg10 +/// |_Cond__|0_0_0|P|U|1|W|L|__Rn___|__Rd___|OffsetH|1|S|H|1|OffsetL| TransImm10 +/// |_Cond__|0_1_0|P|U|B|W|L|__Rn___|__Rd___|_________Offset________| TransImm9 +/// |_Cond__|0_1_1|P|U|B|W|L|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| TransReg9 +/// |_Cond__|0_1_1|________________xxx____________________|1|__xxx__| Undefined +/// |_Cond__|0_1_1|Op_|x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x_x|1|x_x_x_x| ARM11:Media +/// |1_1_1_1_0_1_0_1_0_1_1_1_1_1_1_1_1_1_1_1_0_0_0_0_0_0_0_1_1_1_1_1| ARM11:CLREX +/// |_Cond__|1_0_0|P|U|S|W|L|__Rn___|__________Register_List________| BlockTrans +/// |_Cond__|1_0_1|L|___________________Offset______________________| B,BL,BLX +/// |_Cond__|1_1_0|P|U|N|W|L|__Rn___|__CRd__|__CP#__|____Offset_____| CoDataTrans +/// |_Cond__|1_1_0_0_0_1_0|L|__Rn___|__Rd___|__CP#__|_CPopc_|__CRm__| CoRR ARM9 +/// |_Cond__|1_1_1_0|_CPopc_|__CRn__|__CRd__|__CP#__|_CP__|0|__CRm__| CoDataOp +/// |_Cond__|1_1_1_0|CPopc|L|__CRn__|__Rd___|__CP#__|_CP__|1|__CRm__| CoRegTrans +/// |_Cond__|1_1_1_1|_____________Ignored_by_Processor______________| SWI +/// ``` +fn arm_decode(i: u32) -> &'static str { + const T: bool = true; + const F: bool = false; + + // First, decode the the top-most non-condition bits + match i.bit_range(26..28) { + 0b00 => { + /* DataProcessing and friends */ + + let result: Option<&str> = match (i.bit_range(23..26), i.bit_range(4..8)) { + (0b000, 0b1001) => { + if 0b0 == i.ibit(22) { + Some("Multiply") + } else { + None + } + } + (0b001, 0b1001) => Some("MultiplyLong"), + (0b010, 0b1001) => { + if 0b00 == i.bit_range(20..22) { + Some("SingleDataSwap") + } else { + None + } + } + (0b010, 0b0001) => { + if 0b010 == i.bit_range(20..23) { + Some("BranchExchange") + } else { + None + } + } + _ => None, + }; + + result.unwrap_or_else(|| { + match (i.ibit(25), i.ibit(22), i.ibit(7), i.ibit(4)) { + (0, 0, 1, 1) => "HalfwordDataTransferRegOffset", + (0, 1, 1, 1) => "HalfwordDataTransferImmediateOffset", + _ => { + let set_cond_flags = i.bit(20); + // PSR Transfers are encoded as a subset of Data Processing, + // with S bit OFF and the encode opcode is one of TEQ,CMN,TST,CMN + let is_op_not_touching_rd = i.bit_range(21..25) & 0b1100 == 0b1000; + if !set_cond_flags && is_op_not_touching_rd { + if i.bit(21) { + // Since bit-16 is ignored and we can't know statically if this is a MoveToStatus or MoveToFlags + "MoveToStatus" + } else { + "MoveFromStatus" + } + } else { + "DataProcessing" + } + } + } + }) + } + 0b01 => { + match (i.bit(25), i.bit(4)) { + (_, F) | (F, T) => "SingleDataTransfer", + (T, T) => "Undefined", /* Possible ARM11 but we don't implement these */ + } + } + 0b10 => match i.bit(25) { + F => "BlockDataTransfer", + T => "BranchLink", + }, + 0b11 => { + match (i.ibit(25), i.ibit(24), i.ibit(4)) { + (0b0, _, _) => "Undefined", /* CoprocessorDataTransfer not implemented */ + (0b1, 0b0, 0b0) => "Undefined", /* CoprocessorDataOperation not implemented */ + (0b1, 0b0, 0b1) => "Undefined", /* CoprocessorRegisterTransfer not implemented */ + (0b1, 0b1, _) => "SoftwareInterrupt", + _ => "Undefined", + } + } + _ => unreachable!(), + } +} + +fn thumb_format_to_handler(thumb_fmt: &str) -> &'static str { + match thumb_fmt { + "MoveShiftedReg" => "exec_thumb_move_shifted_reg", + "AddSub" => "exec_thumb_add_sub", + "DataProcessImm" => "exec_thumb_data_process_imm", + "AluOps" => "exec_thumb_alu_ops", + "HiRegOpOrBranchExchange" => "exec_thumb_hi_reg_op_or_bx", + "LdrPc" => "exec_thumb_ldr_pc", + "LdrStrRegOffset" => "exec_thumb_ldr_str_reg_offset", + "LdrStrSHB" => "exec_thumb_ldr_str_shb", + "LdrStrImmOffset" => "exec_thumb_ldr_str_imm_offset", + "LdrStrHalfWord" => "exec_thumb_ldr_str_halfword", + "LdrStrSp" => "exec_thumb_ldr_str_sp", + "LoadAddress" => "exec_thumb_load_address", + "AddSp" => "exec_thumb_add_sp", + "PushPop" => "exec_thumb_push_pop", + "LdmStm" => "exec_thumb_ldm_stm", + "BranchConditional" => "exec_thumb_branch_with_cond", + "Swi" => "exec_thumb_swi", + "Branch" => "exec_thumb_branch", + "BranchLongWithLink" => "exec_thumb_branch_long_with_link", + "Undefined" => "thumb_undefined", + _ => unreachable!(), + } +} + +fn arm_format_to_handler(arm_fmt: &str) -> &'static str { + match arm_fmt { + "BranchExchange" => "exec_arm_bx", + "BranchLink" => "exec_arm_b_bl", + "DataProcessing" => "exec_arm_data_processing", + "SoftwareInterrupt" => "exec_arm_swi", + "SingleDataTransfer" => "exec_arm_ldr_str", + "HalfwordDataTransferImmediateOffset" => "exec_arm_ldr_str_hs", + "HalfwordDataTransferRegOffset" => "exec_arm_ldr_str_hs", + "BlockDataTransfer" => "exec_arm_ldm_stm", + "MoveFromStatus" => "exec_arm_mrs", + "MoveToStatus" => "exec_arm_transfer_to_status", + "MoveToFlags" => "exec_arm_transfer_to_status", + "Multiply" => "exec_arm_mul_mla", + "MultiplyLong" => "exec_arm_mull_mlal", + "SingleDataSwap" => "exec_arm_swp", + "Undefined" => "arm_undefined", + _ => unreachable!(), + } +} + +fn generate_thumb_lut(file: &mut fs::File) -> Result<(), std::io::Error> { + writeln!( + file, + "use super::thumb::ThumbFormat; + +pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, &ThumbInstruction) -> CpuAction; + +pub struct ThumbInstructionInfo {{ + pub fmt: ThumbFormat, + pub handler_fn: ThumbInstructionHandler +}} +" + )?; + + writeln!( + file, + "pub const THUMB_LUT: [ThumbInstructionInfo; 1024] = [" + )?; + + for i in 0..1024 { + let thumb_fmt = thumb_decode(i << 6); + let handler_name = thumb_format_to_handler(thumb_fmt); + writeln!( + file, + " /* {:#x} */ ThumbInstructionInfo {{ fmt: ThumbFormat::{}, handler_fn: Core::{} }},", + i, thumb_fmt, handler_name + )?; + } + + writeln!(file, "];")?; + + Ok(()) +} + +fn generate_arm_lut(file: &mut fs::File) -> Result<(), std::io::Error> { + writeln!( + file, + "use super::arm::ArmFormat; + +pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, &ArmInstruction) -> CpuAction; + +pub struct ArmInstructionInfo {{ + pub fmt: ArmFormat, + pub handler_fn: ArmInstructionHandler +}} +" + )?; + + writeln!(file, "pub const ARM_LUT: [ArmInstructionInfo; 4096] = [")?; + for i in 0..4096 { + let arm_fmt = arm_decode(((i & 0xff0) << 16) | ((i & 0x00f) << 4)); + let handler_name = arm_format_to_handler(arm_fmt); + writeln!( + file, + " /* {:#x} */ ArmInstructionInfo {{ fmt: ArmFormat::{}, handler_fn: Core::{} }},", + i, arm_fmt, handler_name + )?; + } + writeln!(file, "];")?; + + Ok(()) +} + +fn main() { + let arm7tdmi_dispatch_table_enabled = + env::var_os("CARGO_FEATURE_ARM7TDMI_DISPATCH_TABLE").is_some(); + + if arm7tdmi_dispatch_table_enabled { + // TODO - don't do this in the build script and use `const fn` instead when it becomes stable + let out_dir = env::var_os("OUT_DIR").unwrap(); + let thumb_lut_path = Path::new(&out_dir).join("thumb_lut.rs"); + let mut thumb_lut_file = fs::File::create(&thumb_lut_path).expect("failed to create file"); + generate_thumb_lut(&mut thumb_lut_file).expect("failed to generate thumb table"); + + let arm_lut_path = Path::new(&out_dir).join("arm_lut.rs"); + let mut arm_lut_file = fs::File::create(&arm_lut_path).expect("failed to create file"); + generate_arm_lut(&mut arm_lut_file).expect("failed to generate arm table"); + + println!("cargo:rerun-if-changed=build.rs"); + } +} diff --git a/src/core/arm7tdmi/arm/display.rs b/src/core/arm7tdmi/arm/display.rs index 711bb90..f2398fe 100644 --- a/src/core/arm7tdmi/arm/display.rs +++ b/src/core/arm7tdmi/arm/display.rs @@ -289,15 +289,16 @@ impl ArmInstruction { ) } - /// MSR - transfer register contents to PSR - fn fmt_msr_reg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// MSR - transfer register/immediate contents to PSR + fn fmt_msr(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "msr{cond}\t{psr}, {Rm}", + "msr{cond}\t{psr}, ", cond = self.cond(), psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, - Rm = reg_string(self.rm()), - ) + )?; + self.fmt_operand2(f).unwrap(); + Ok(()) } fn fmt_msr_flags(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -422,7 +423,7 @@ impl fmt::Display for ArmInstruction { SingleDataTransfer => self.fmt_ldr_str(f), BlockDataTransfer => self.fmt_ldm_stm(f), MoveFromStatus => self.fmt_mrs(f), - MoveToStatus => self.fmt_msr_reg(f), + MoveToStatus => self.fmt_msr(f), MoveToFlags => self.fmt_msr_flags(f), Multiply => self.fmt_mul_mla(f), MultiplyLong => self.fmt_mull_mlal(f), diff --git a/src/core/arm7tdmi/arm/exec.rs b/src/core/arm7tdmi/arm/exec.rs index d284ec5..0c5978b 100644 --- a/src/core/arm7tdmi/arm/exec.rs +++ b/src/core/arm7tdmi/arm/exec.rs @@ -21,8 +21,8 @@ impl Core { ArmFormat::HalfwordDataTransferRegOffset => self.exec_arm_ldr_str_hs(bus, insn), ArmFormat::BlockDataTransfer => self.exec_arm_ldm_stm(bus, insn), ArmFormat::MoveFromStatus => self.exec_arm_mrs(bus, insn), - ArmFormat::MoveToStatus => self.exec_arm_msr_reg(bus, insn), - ArmFormat::MoveToFlags => self.exec_arm_msr_flags(bus, insn), + ArmFormat::MoveToStatus => self.exec_arm_transfer_to_status(bus, insn), + ArmFormat::MoveToFlags => self.exec_arm_transfer_to_status(bus, insn), ArmFormat::Multiply => self.exec_arm_mul_mla(bus, insn), ArmFormat::MultiplyLong => self.exec_arm_mull_mlal(bus, insn), ArmFormat::SingleDataSwap => self.exec_arm_swp(bus, insn), @@ -96,12 +96,46 @@ impl Core { 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())) + #[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); + self.ror(immediate, rotate, self.cpsr.C(), false, true) + } else { + self.get_reg((insn.raw & 0b1111) as usize) + } } - fn write_status_register(&mut self, sb: &mut SysBus, is_spsr: bool, value: u32) -> CpuAction { - let new_status_reg = RegPSR::new(value); + // #[cfg(feature = "arm7tdmi_dispatch_table")] + pub fn exec_arm_transfer_to_status( + &mut self, + sb: &mut SysBus, + insn: &ArmInstruction, + ) -> 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 mut mask = 0; + if f { + mask |= 0xff << 24; + } + if s { + mask |= 0xff << 16; + } + if x { + mask |= 0xff << 8; + } + if c { + mask |= 0xff << 0; + } + + let is_spsr = insn.spsr_flag(); + match self.cpsr.mode() { CpuMode::User => { if is_spsr { @@ -113,16 +147,13 @@ impl Core { if is_spsr { self.spsr.set(value); } else { - let t_bit = self.cpsr.state(); let old_mode = self.cpsr.mode(); - self.cpsr.set(value); - if t_bit != self.cpsr.state() { - panic!("T bit changed from MSR"); - } - let new_mode = new_status_reg.mode(); + let new_psr = RegPSR::new((self.cpsr.get() & !mask) | (value & mask)); + let new_mode = new_psr.mode(); if old_mode != new_mode { self.change_mode(old_mode, new_mode); } + self.cpsr = new_psr; } } } @@ -131,19 +162,6 @@ impl Core { CpuAction::AdvancePC } - 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); - - if insn.spsr_flag() { - self.spsr.set_flag_bits(op); - } else { - self.cpsr.set_flag_bits(op); - } - CpuAction::AdvancePC - } - fn decode_operand2(&mut self, op2: &BarrelShifterValue) -> u32 { match op2 { BarrelShifterValue::RotatedImmediate(val, amount) => { @@ -195,19 +213,6 @@ impl Core { let op2 = self.decode_operand2(&op2); let reg_rd = insn.rd(); - if !s_flag { - match opcode { - TEQ => { - return self.write_status_register(sb, false, op2); - } - CMN => { - return self.write_status_register(sb, true, op2); - } - TST => return self.move_from_status_register(sb, reg_rd, false), - CMP => return self.move_from_status_register(sb, reg_rd, true), - _ => (), - } - } if reg_rd == REG_PC && s_flag { self.transfer_spsr_mode(); diff --git a/src/core/arm7tdmi/arm/lut.rs b/src/core/arm7tdmi/arm/lut.rs deleted file mode 100644 index 1900583..0000000 --- a/src/core/arm7tdmi/arm/lut.rs +++ /dev/null @@ -1,161 +0,0 @@ -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 e0c1f11..5ccc173 100644 --- a/src/core/arm7tdmi/arm/mod.rs +++ b/src/core/arm7tdmi/arm/mod.rs @@ -1,11 +1,6 @@ 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::*; diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 93efadc..3f120c2 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -7,28 +7,30 @@ use serde::{Deserialize, Serialize}; 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")] -use super::arm::{arm_insn_hash, ARM_LUT}; +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; -#[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}; -#[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, @@ -68,13 +70,6 @@ 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 @@ -322,17 +317,14 @@ impl Core { #[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); + 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())); - (handler_fn)(self, sb, &arm_insn) + (arm_info.handler_fn)(self, sb, &arm_insn) } #[cfg(feature = "arm7tdmi_dispatch_table")] diff --git a/src/core/arm7tdmi/thumb/lut.rs b/src/core/arm7tdmi/thumb/lut.rs deleted file mode 100644 index 3b67147..0000000 --- a/src/core/arm7tdmi/thumb/lut.rs +++ /dev/null @@ -1,65 +0,0 @@ -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 3d8051f..a8cb70f 100644 --- a/src/core/arm7tdmi/thumb/mod.rs +++ b/src/core/arm7tdmi/thumb/mod.rs @@ -7,10 +7,6 @@ use crate::num::FromPrimitive; 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 { diff --git a/src/lib.rs b/src/lib.rs index 4116a32..1f9a235 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,3 @@ -#[cfg(feature = "arm7tdmi_dispatch_table")] -#[macro_use] -extern crate lazy_static; - #[macro_use] extern crate serde;