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
This commit is contained in:
parent
ad232227c1
commit
fb93ebb4ed
|
@ -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
|
||||
|
|
309
build.rs
Normal file
309
build.rs
Normal file
|
@ -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<T: From<bool>>: BitIndex {
|
||||
fn ibit(&self, i: usize) -> T {
|
||||
self.bit(i).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAsInt<u32> 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");
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<ArmFormat> 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<u32> 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<ArmInstructionHandler>; 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<u8>; 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) }
|
||||
};
|
||||
}
|
|
@ -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::*;
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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<ThumbFormat> 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<ThumbInstructionInfo>; 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) }
|
||||
};
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
#[cfg(feature = "arm7tdmi_dispatch_table")]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
|
|
Reference in a new issue