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