[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:
Michel Heily 2020-04-04 15:33:36 +03:00
parent 8f11bebd2b
commit a523a37d32
13 changed files with 517 additions and 142 deletions

View file

@ -12,6 +12,7 @@ members = [
] ]
[dependencies] [dependencies]
lazy_static = {version="1.4.0", optional = true}
serde = {version = "1.0.104", features = ["derive"] } serde = {version = "1.0.104", features = ["derive"] }
bincode = "1.2.1" bincode = "1.2.1"
byteorder = "1" byteorder = "1"
@ -41,6 +42,10 @@ gdbstub = {git = "https://github.com/daniel5151/gdbstub.git", optional = true, f
[features] [features]
debugger = ["nom", "rustyline"] debugger = ["nom", "rustyline"]
gdb = ["gdbstub"] 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] [profile.dev]
opt-level = 0 opt-level = 0

View file

@ -21,4 +21,5 @@ winres = "0.1"
[features] [features]
debugger = ["rustboyadvance-ng/debugger"] debugger = ["rustboyadvance-ng/debugger"]
gdb = ["rustboyadvance-ng/gdb"] gdb = ["rustboyadvance-ng/gdb"]
arm7tdmi_dispatch_table = ["rustboyadvance-ng/arm7tdmi_dispatch_table"]

View file

@ -113,7 +113,7 @@ impl ArmInstruction {
f, f,
"b{link}{cond}\t{ofs:#x}", "b{link}{cond}\t{ofs:#x}",
link = if self.link_flag() { "l" } else { "" }, link = if self.link_flag() { "l" } else { "" },
cond = self.cond, cond = self.cond(),
ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr) ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr)
) )
} }
@ -152,7 +152,7 @@ impl ArmInstruction {
f, f,
"{opcode}{S}{cond}\t{Rd}, ", "{opcode}{S}{cond}\t{Rd}, ",
opcode = opcode, opcode = opcode,
cond = self.cond, cond = self.cond(),
S = self.set_cond_mark(), S = self.set_cond_mark(),
Rd = reg_string(self.rd()) Rd = reg_string(self.rd())
), ),
@ -160,14 +160,14 @@ impl ArmInstruction {
f, f,
"{opcode}{cond}\t{Rn}, ", "{opcode}{cond}\t{Rn}, ",
opcode = opcode, opcode = opcode,
cond = self.cond, cond = self.cond(),
Rn = reg_string(self.rn()) Rn = reg_string(self.rn())
), ),
_ => write!( _ => write!(
f, f,
"{opcode}{S}{cond}\t{Rd}, {Rn}, ", "{opcode}{S}{cond}\t{Rd}, {Rn}, ",
opcode = opcode, opcode = opcode,
cond = self.cond, cond = self.cond(),
S = self.set_cond_mark(), S = self.set_cond_mark(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
Rn = reg_string(self.rn()) Rn = reg_string(self.rn())
@ -230,7 +230,7 @@ impl ArmInstruction {
"{mnem}{B}{T}{cond}\t{Rd}, ", "{mnem}{B}{T}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" }, mnem = if self.load_flag() { "ldr" } else { "str" },
B = if self.transfer_size() == 1 { "b" } else { "" }, 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 = if !self.pre_index_flag() && self.write_back_flag() {
"t" "t"
} else { } else {
@ -249,7 +249,7 @@ impl ArmInstruction {
mnem = if self.load_flag() { "ldm" } else { "stm" }, mnem = if self.load_flag() { "ldm" } else { "stm" },
inc_dec = if self.add_offset_flag() { 'i' } else { 'd' }, inc_dec = if self.add_offset_flag() { 'i' } else { 'd' },
pre_post = if self.pre_index_flag() { 'b' } else { 'a' }, pre_post = if self.pre_index_flag() { 'b' } else { 'a' },
cond = self.cond, cond = self.cond(),
Rn = reg_string(self.rn()), Rn = reg_string(self.rn()),
auto_inc = if self.write_back_flag() { "!" } else { "" } auto_inc = if self.write_back_flag() { "!" } else { "" }
)?; )?;
@ -283,7 +283,7 @@ impl ArmInstruction {
write!( write!(
f, f,
"mrs{cond}\t{Rd}, {psr}", "mrs{cond}\t{Rd}, {psr}",
cond = self.cond, cond = self.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" } psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }
) )
@ -294,7 +294,7 @@ impl ArmInstruction {
write!( write!(
f, f,
"msr{cond}\t{psr}, {Rm}", "msr{cond}\t{psr}, {Rm}",
cond = self.cond, cond = self.cond(),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, psr = if self.spsr_flag() { "SPSR" } else { "CPSR" },
Rm = reg_string(self.rm()), Rm = reg_string(self.rm()),
) )
@ -304,7 +304,7 @@ impl ArmInstruction {
write!( write!(
f, f,
"msr{cond}\t{psr}, ", "msr{cond}\t{psr}, ",
cond = self.cond, cond = self.cond(),
psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" }, psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" },
)?; )?;
if let Ok(Some(op)) = self.fmt_operand2(f) { if let Ok(Some(op)) = self.fmt_operand2(f) {
@ -327,7 +327,7 @@ impl ArmInstruction {
f, f,
"mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}", "mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}",
S = self.set_cond_mark(), S = self.set_cond_mark(),
cond = self.cond, cond = self.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()), Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()), Rs = reg_string(self.rs()),
@ -338,7 +338,7 @@ impl ArmInstruction {
f, f,
"mul{S}{cond}\t{Rd}, {Rm}, {Rs}", "mul{S}{cond}\t{Rd}, {Rm}, {Rs}",
S = self.set_cond_mark(), S = self.set_cond_mark(),
cond = self.cond, cond = self.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()), Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()), Rs = reg_string(self.rs()),
@ -365,7 +365,7 @@ impl ArmInstruction {
"mull" "mull"
}, },
S = self.set_cond_mark(), S = self.set_cond_mark(),
cond = self.cond, cond = self.cond(),
RdLo = reg_string(self.rd_lo()), RdLo = reg_string(self.rd_lo()),
RdHi = reg_string(self.rd_hi()), RdHi = reg_string(self.rd_hi()),
Rm = reg_string(self.rm()), Rm = reg_string(self.rm()),
@ -379,7 +379,7 @@ impl ArmInstruction {
f, f,
"{mnem}{type}{cond}\t{Rd}, ", "{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" }, mnem = if self.load_flag() { "ldr" } else { "str" },
cond = self.cond, cond = self.cond(),
type = transfer_type, type = transfer_type,
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
)?; )?;
@ -393,7 +393,7 @@ impl ArmInstruction {
write!( write!(
f, f,
"swi{cond}\t#{comm:#x}", "swi{cond}\t#{comm:#x}",
cond = self.cond, cond = self.cond(),
comm = self.swi_comment() comm = self.swi_comment()
) )
} }
@ -403,7 +403,7 @@ impl ArmInstruction {
f, f,
"swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]", "swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]",
B = if self.transfer_size() == 1 { "b" } else { "" }, B = if self.transfer_size() == 1 { "b" } else { "" },
cond = self.cond, cond = self.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()), Rm = reg_string(self.rm()),
Rn = reg_string(self.rn()), Rn = reg_string(self.rn()),
@ -430,6 +430,7 @@ impl fmt::Display for ArmInstruction {
LDR_STR_HS_REG => self.fmt_ldr_str_hs(f), LDR_STR_HS_REG => self.fmt_ldr_str_hs(f),
SWI => self.fmt_swi(f), SWI => self.fmt_swi(f),
SWP => self.fmt_swp(f), SWP => self.fmt_swp(f),
Undefined => write!(f, "<Undefined>"),
} }
} }
} }

View file

@ -1,7 +1,6 @@
use crate::bit::BitIndex; use crate::bit::BitIndex;
use super::super::alu::*; use super::super::alu::*;
use super::ArmCond;
use crate::core::arm7tdmi::psr::RegPSR; use crate::core::arm7tdmi::psr::RegPSR;
use crate::core::arm7tdmi::CpuAction; use crate::core::arm7tdmi::CpuAction;
use crate::core::arm7tdmi::{Addr, Core, CpuMode, CpuState, REG_LR, REG_PC}; use crate::core::arm7tdmi::{Addr, Core, CpuMode, CpuState, REG_LR, REG_PC};
@ -12,35 +11,34 @@ use super::*;
impl Core { impl Core {
pub fn exec_arm(&mut self, bus: &mut SysBus, insn: &ArmInstruction) -> CpuAction { 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 { match insn.fmt {
ArmFormat::BX => self.exec_bx(bus, insn), ArmFormat::BX => self.exec_arm_bx(bus, insn),
ArmFormat::B_BL => self.exec_b_bl(bus, insn), ArmFormat::B_BL => self.exec_arm_b_bl(bus, insn),
ArmFormat::DP => self.exec_data_processing(bus, insn), ArmFormat::DP => self.exec_arm_data_processing(bus, insn),
ArmFormat::SWI => { ArmFormat::SWI => self.exec_arm_swi(bus, insn),
self.software_interrupt(bus, self.pc - 4, insn.swi_comment()); ArmFormat::LDR_STR => self.exec_arm_ldr_str(bus, insn),
CpuAction::FlushPipeline 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::LDR_STR => self.exec_ldr_str(bus, insn), ArmFormat::LDM_STM => self.exec_arm_ldm_stm(bus, insn),
ArmFormat::LDR_STR_HS_IMM => self.exec_ldr_str_hs(bus, insn), ArmFormat::MRS => self.exec_arm_mrs(bus, insn),
ArmFormat::LDR_STR_HS_REG => self.exec_ldr_str_hs(bus, insn), ArmFormat::MSR_REG => self.exec_arm_msr_reg(bus, insn),
ArmFormat::LDM_STM => self.exec_ldm_stm(bus, insn), ArmFormat::MSR_FLAGS => self.exec_arm_msr_flags(bus, insn),
ArmFormat::MRS => self.move_from_status_register(bus, insn.rd(), insn.spsr_flag()), ArmFormat::MUL_MLA => self.exec_arm_mul_mla(bus, insn),
ArmFormat::MSR_REG => self.exec_msr_reg(bus, insn), ArmFormat::MULL_MLAL => self.exec_arm_mull_mlal(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::SWP => self.exec_arm_swp(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 /// 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); self.S_cycle32(sb, self.pc);
if insn.link_flag() { if insn.link_flag() {
self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1); self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1);
@ -73,7 +71,7 @@ impl Core {
} }
/// Cycles 2S+1N /// 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())) self.branch_exchange(sb, self.get_reg(insn.rn()))
} }
@ -94,7 +92,11 @@ impl Core {
CpuAction::AdvancePC 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())) self.write_status_register(sb, insn.spsr_flag(), self.get_reg(insn.rm()))
} }
@ -129,7 +131,7 @@ impl Core {
CpuAction::AdvancePC 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); self.S_cycle32(sb, self.pc);
let op = insn.operand2(); let op = insn.operand2();
let op = self.decode_operand2(&op); let op = self.decode_operand2(&op);
@ -164,7 +166,11 @@ impl Core {
/// ///
/// Cycles: 1S+x+y (from GBATEK) /// Cycles: 1S+x+y (from GBATEK)
/// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15. /// 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::*; use AluOpCode::*;
self.S_cycle32(sb, self.pc); self.S_cycle32(sb, self.pc);
@ -275,7 +281,7 @@ impl Core {
/// STR{cond}{B}{T} Rd,<Address> | 2N | ---- | [Rn+/-<offset>]=Rd /// STR{cond}{B}{T} Rd,<Address> | 2N | ---- | [Rn+/-<offset>]=Rd
/// ------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------
/// For LDR, add y=1S+1N if Rd=R15. /// 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 mut result = CpuAction::AdvancePC;
let load = insn.load_flag(); let load = insn.load_flag();
@ -352,7 +358,7 @@ impl Core {
result 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 mut result = CpuAction::AdvancePC;
let load = insn.load_flag(); let load = insn.load_flag();
@ -434,7 +440,7 @@ impl Core {
result 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 result = CpuAction::AdvancePC;
let mut full = insn.pre_index_flag(); let mut full = insn.pre_index_flag();
@ -584,7 +590,7 @@ impl Core {
result 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()); let (rd, rn, rs, rm) = (insn.rd(), insn.rn(), insn.rs(), insn.rm());
// check validity // check validity
@ -619,7 +625,7 @@ impl Core {
CpuAction::AdvancePC 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) = let (rd_hi, rd_lo, rn, rs, rm) =
(insn.rd_hi(), insn.rd_lo(), insn.rn(), insn.rs(), insn.rm()); (insn.rd_hi(), insn.rd_lo(), insn.rn(), insn.rs(), insn.rm());
@ -666,7 +672,7 @@ impl Core {
CpuAction::AdvancePC 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()); let base_addr = self.get_reg(insn.rn());
if insn.transfer_size() == 1 { if insn.transfer_size() == 1 {
let t = sb.read_8(base_addr); let t = sb.read_8(base_addr);
@ -686,4 +692,9 @@ impl Core {
CpuAction::AdvancePC 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
}
} }

View 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) }
};
}

View file

@ -1,6 +1,11 @@
pub mod display; pub mod display;
pub mod exec; pub mod exec;
#[cfg(feature = "arm7tdmi_dispatch_table")]
mod lut;
#[cfg(feature = "arm7tdmi_dispatch_table")]
pub use lut::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::alu::*; use super::alu::*;
@ -90,6 +95,8 @@ pub enum ArmFormat {
MSR_REG, MSR_REG,
/// Tanssfer immediate/register to PSR flags only /// Tanssfer immediate/register to PSR flags only
MSR_FLAGS, MSR_FLAGS,
Undefined,
} }
#[derive(Debug, PartialEq, Primitive)] #[derive(Debug, PartialEq, Primitive)]
@ -101,26 +108,29 @@ pub enum ArmHalfwordTransferType {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ArmInstruction { pub struct ArmInstruction {
pub cond: ArmCond,
pub fmt: ArmFormat, pub fmt: ArmFormat,
pub raw: u32, pub raw: u32,
pub pc: Addr, pub pc: Addr,
} }
impl ArmInstruction {
pub fn new(raw: u32, pc: Addr, fmt: ArmFormat) -> ArmInstruction {
ArmInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ArmInstruction { impl InstructionDecoder for ArmInstruction {
type IntType = u32; type IntType = u32;
fn decode(raw: u32, addr: Addr) -> Self { fn decode(raw: u32, addr: Addr) -> Self {
use ArmFormat::*; 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 { let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 {
BX BX
} else if (0x0e00_0000 & raw) == 0x0a00_0000 { } else if (0x0e00_0000 & raw) == 0x0a00_0000 {
B_BL B_BL
} else if (0xe000_0010 & raw) == 0x0600_0000 { } else if (0xe000_0010 & raw) == 0x0600_0000 {
panic!("unknown instruction {:#x} at @{:#x}", raw, addr); Undefined
} else if (0x0fb0_0ff0 & raw) == 0x0100_0090 { } else if (0x0fb0_0ff0 & raw) == 0x0100_0090 {
SWP SWP
} else if (0x0fc0_00f0 & raw) == 0x0000_0090 { } else if (0x0fc0_00f0 & raw) == 0x0000_0090 {
@ -146,11 +156,10 @@ impl InstructionDecoder for ArmInstruction {
} else if (0x0c00_0000 & raw) == 0x0000_0000 { } else if (0x0c00_0000 & raw) == 0x0000_0000 {
DP DP
} else { } else {
panic!("unknown arm instruction {:#x} at @{:#x}", raw, addr); Undefined
}; };
ArmInstruction { ArmInstruction {
cond: cond,
fmt: fmt, fmt: fmt,
raw: raw, raw: raw,
pc: addr, 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 { pub fn rn(&self) -> usize {
match self.fmt { match self.fmt {
ArmFormat::MUL_MLA => self.raw.bit_range(12..16) as usize, ArmFormat::MUL_MLA => self.raw.bit_range(12..16) as usize,

View file

@ -6,17 +6,29 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
use std::fmt; 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; pub use super::exception::Exception;
#[cfg(feature = "arm7tdmi_dispatch_table")]
use super::thumb::THUMB_LUT;
use super::CpuAction; use super::CpuAction;
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
use super::DecodedInstruction; use super::DecodedInstruction;
use super::{ use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState};
arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState, InstructionDecoder,
}; #[cfg(not(feature = "arm7tdmi_dispatch_table"))]
use super::InstructionDecoder;
use crate::core::bus::Bus; use crate::core::bus::Bus;
use crate::core::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus}; 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)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Core { pub struct Core {
pub pc: u32, pub pc: u32,
@ -56,6 +68,13 @@ pub struct Core {
impl Core { impl Core {
pub fn new() -> 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); let cpsr = RegPSR::new(0x0000_00D3);
Core { Core {
memreq: 0xffff_0000, // set memreq to an invalid addr so the first load cycle will be non-sequential 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn S_cycle32(&mut self, sb: &SysBus, addr: u32) { 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn S_cycle16(&mut self, sb: &SysBus, addr: u32) { 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn S_cycle8(&mut self, sb: &SysBus, addr: u32) { 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn N_cycle32(&mut self, sb: &SysBus, addr: u32) { 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn N_cycle16(&mut self, sb: &SysBus, addr: u32) { 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)] #[allow(non_snake_case)]
#[inline(always)] #[inline(always)]
pub(super) fn N_cycle8(&mut self, sb: &SysBus, addr: u32) { 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] #[inline]
@ -301,32 +314,56 @@ impl Core {
} }
} }
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) { #[cfg(feature = "debugger")]
let decoded_arm = ArmInstruction::decode(insn, self.pc.wrapping_sub(8)); fn debugger_record_step(&mut self, d: DecodedInstruction) {
#[cfg(feature = "debugger")] self.gpr_previous = self.get_registers();
{ self.last_executed = Some(d);
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 => {}
}
} }
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) { #[cfg(feature = "arm7tdmi_dispatch_table")]
let decoded_thumb = ThumbInstruction::decode(insn, self.pc.wrapping_sub(4)); 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")] #[cfg(feature = "debugger")]
{ self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone()));
self.gpr_previous = self.get_registers();
self.last_executed = Some(DecodedInstruction::Thumb(decoded_thumb.clone())); (handler_fn)(self, sb, &arm_insn)
} }
let result = self.exec_thumb(sb, &decoded_thumb);
match result { #[cfg(feature = "arm7tdmi_dispatch_table")]
CpuAction::AdvancePC => self.advance_thumb(), fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction {
CpuAction::FlushPipeline => {} 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)] #[inline(always)]
@ -370,14 +407,29 @@ impl Core {
let insn = self.pipeline[0]; let insn = self.pipeline[0];
self.pipeline[0] = self.pipeline[1]; self.pipeline[0] = self.pipeline[1];
self.pipeline[1] = fetched_now; 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 => { CpuState::THUMB => {
let fetched_now = bus.read_16(pc); let fetched_now = bus.read_16(pc);
let insn = self.pipeline[0]; let insn = self.pipeline[0];
self.pipeline[0] = self.pipeline[1]; self.pipeline[0] = self.pipeline[1];
self.pipeline[1] = fetched_now as u32; 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 => {}
}
} }
} }
} }

View file

@ -294,6 +294,7 @@ impl fmt::Display for ThumbInstruction {
ThumbFormat::Swi => self.fmt_thumb_swi(f), ThumbFormat::Swi => self.fmt_thumb_swi(f),
ThumbFormat::Branch => self.fmt_thumb_branch(f), ThumbFormat::Branch => self.fmt_thumb_branch(f),
ThumbFormat::BranchLongWithLink => self.fmt_thumb_branch_long_with_link(f), ThumbFormat::BranchLongWithLink => self.fmt_thumb_branch_long_with_link(f),
ThumbFormat::Undefined => write!(f, "<Undefined>"),
} }
} }
} }

View file

@ -18,7 +18,7 @@ fn pop(cpu: &mut Core, bus: &mut SysBus, r: usize) {
impl Core { impl Core {
/// Format 1 /// Format 1
fn exec_thumb_move_shifted_reg( pub(in super::super) fn exec_thumb_move_shifted_reg(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -43,7 +43,11 @@ impl Core {
} }
/// Format 2 /// 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 rd = (insn.raw & 0b111) as usize;
let op1 = self.get_reg(insn.rs()); let op1 = self.get_reg(insn.rs());
let op2 = if insn.is_immediate_operand() { let op2 = if insn.is_immediate_operand() {
@ -68,7 +72,7 @@ impl Core {
} }
/// Format 3 /// Format 3
fn exec_thumb_data_process_imm( pub(in super::super) fn exec_thumb_data_process_imm(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -96,7 +100,11 @@ impl Core {
} }
/// Format 4 /// 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 rd = (insn.raw & 0b111) as usize;
let rs = insn.rs(); let rs = insn.rs();
let dst = self.get_reg(rd); let dst = self.get_reg(rd);
@ -153,7 +161,7 @@ impl Core {
} }
/// Format 5 /// Format 5
fn exec_thumb_hi_reg_op_or_bx( pub(in super::super) fn exec_thumb_hi_reg_op_or_bx(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -205,7 +213,11 @@ impl Core {
} }
/// Format 6 /// 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 rd = insn.raw.bit_range(8..11) as usize;
let ofs = insn.word8() as Addr; let ofs = insn.word8() as Addr;
@ -261,7 +273,7 @@ impl Core {
} }
/// Format 7 /// Format 7
fn exec_thumb_ldr_str_reg_offset( pub(in super::super) fn exec_thumb_ldr_str_reg_offset(
&mut self, &mut self,
bus: &mut SysBus, bus: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -272,7 +284,11 @@ impl Core {
} }
/// Format 8 /// 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 rb = insn.raw.bit_range(3..6) as usize;
let rd = (insn.raw & 0b111) as usize; let rd = (insn.raw & 0b111) as usize;
@ -318,7 +334,7 @@ impl Core {
} }
/// Format 9 /// Format 9
fn exec_thumb_ldr_str_imm_offset( pub(in super::super) fn exec_thumb_ldr_str_imm_offset(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -335,7 +351,7 @@ impl Core {
} }
/// Format 10 /// Format 10
fn exec_thumb_ldr_str_halfword( pub(in super::super) fn exec_thumb_ldr_str_halfword(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -359,7 +375,11 @@ impl Core {
} }
/// Format 11 /// 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 addr = self.gpr[REG_SP] + (insn.word8() as Addr);
let rd = insn.raw.bit_range(8..11) as usize; let rd = insn.raw.bit_range(8..11) as usize;
if insn.is_load() { if insn.is_load() {
@ -377,7 +397,11 @@ impl Core {
} }
/// Format 12 /// 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 rd = insn.raw.bit_range(8..11) as usize;
let result = if insn.flag(ThumbInstruction::FLAG_SP) { let result = if insn.flag(ThumbInstruction::FLAG_SP) {
self.gpr[REG_SP] + (insn.word8() as Addr) self.gpr[REG_SP] + (insn.word8() as Addr)
@ -391,7 +415,11 @@ impl Core {
} }
/// Format 13 /// 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 op1 = self.gpr[REG_SP] as i32;
let op2 = insn.sword7(); let op2 = insn.sword7();
@ -402,7 +430,11 @@ impl Core {
} }
/// Format 14 /// 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; 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). // (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 /// 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; 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). // (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 /// Format 16
fn exec_thumb_branch_with_cond( pub(in super::super) fn exec_thumb_branch_with_cond(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: &ThumbInstruction,
@ -545,14 +581,22 @@ impl Core {
} }
/// Format 17 /// 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.N_cycle16(sb, self.pc);
self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2); self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2);
CpuAction::FlushPipeline CpuAction::FlushPipeline
} }
/// Format 18 /// 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; let offset = ((insn.offset11() << 21) >> 20) as i32;
self.pc = (self.pc as i32).wrapping_add(offset) as u32; self.pc = (self.pc as i32).wrapping_add(offset) as u32;
self.S_cycle16(sb, self.pc); self.S_cycle16(sb, self.pc);
@ -561,7 +605,7 @@ impl Core {
} }
/// Format 19 /// Format 19
fn exec_thumb_branch_long_with_link( pub(in super::super) fn exec_thumb_branch_long_with_link(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, 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 { pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
match insn.fmt { match insn.fmt {
ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn), 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::Swi => self.exec_thumb_swi(bus, insn),
ThumbFormat::Branch => self.exec_thumb_branch(bus, insn), ThumbFormat::Branch => self.exec_thumb_branch(bus, insn),
ThumbFormat::BranchLongWithLink => self.exec_thumb_branch_long_with_link(bus, insn), ThumbFormat::BranchLongWithLink => self.exec_thumb_branch_long_with_link(bus, insn),
ThumbFormat::Undefined => self.thumb_undefined(bus, insn),
} }
} }
} }

View 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) }
};
}

View file

@ -1,13 +1,16 @@
use super::alu::*;
use super::arm::*;
use super::{Addr, InstructionDecoder};
use crate::bit::BitIndex; use crate::bit::BitIndex;
use crate::byteorder::{LittleEndian, ReadBytesExt}; use crate::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive; use crate::num::FromPrimitive;
use super::alu::*;
use super::arm::*;
use super::{Addr, InstructionDecoder};
pub mod display; pub mod display;
pub mod exec; 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)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub enum ThumbFormat { pub enum ThumbFormat {
@ -49,6 +52,9 @@ pub enum ThumbFormat {
Branch, Branch,
/// Format 19 /// Format 19
BranchLongWithLink, BranchLongWithLink,
/// Not an actual thumb format
Undefined,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
@ -58,6 +64,12 @@ pub struct ThumbInstruction {
pub pc: Addr, pub pc: Addr,
} }
impl ThumbInstruction {
pub fn new(raw: u16, pc: Addr, fmt: ThumbFormat) -> ThumbInstruction {
ThumbInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ThumbInstruction { impl InstructionDecoder for ThumbInstruction {
type IntType = u16; type IntType = u16;
@ -103,14 +115,10 @@ impl InstructionDecoder for ThumbInstruction {
} else if raw & 0xf000 == 0xf000 { } else if raw & 0xf000 == 0xf000 {
BranchLongWithLink BranchLongWithLink
} else { } else {
panic!("unknown thumb instruction {:#x} at @{:#x}", raw, addr); Undefined
}; };
ThumbInstruction { ThumbInstruction::new(raw, addr, fmt)
fmt: fmt,
raw: raw,
pc: addr,
}
} }
fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Self { fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Self {

View file

@ -85,23 +85,24 @@ impl Debugger {
while self.gba.cpu.last_executed.is_none() { while self.gba.cpu.last_executed.is_none() {
self.gba.cpu.step(&mut self.gba.sysbus); self.gba.cpu.step(&mut self.gba.sysbus);
} }
let last_executed = self.gba.cpu.last_executed.unwrap(); if let Some(last_executed) = &self.gba.cpu.last_executed {
print!( print!(
"{}\t{}", "{}\t{}",
Colour::Black Colour::Black
.bold() .bold()
.italic() .italic()
.on(Colour::White) .on(Colour::White)
.paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)), .paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)),
last_executed last_executed
); );
println!( println!(
"{}", "{}",
Colour::Purple.dimmed().italic().paint(format!( Colour::Purple.dimmed().italic().paint(format!(
"\t\t/// Next instruction at @0x{:08x}", "\t\t/// Next instruction at @0x{:08x}",
self.gba.cpu.get_next_pc() self.gba.cpu.get_next_pc()
)) ))
); );
}
} }
println!("{}\n", self.gba.cpu); println!("{}\n", self.gba.cpu);
} }

View file

@ -1,3 +1,7 @@
#[cfg(feature = "arm7tdmi_dispatch_table")]
#[macro_use]
extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate serde; extern crate serde;