[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]
|
[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
|
||||||
|
|
|
@ -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"]
|
|
@ -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>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
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 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,
|
||||||
|
|
|
@ -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 => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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::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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Reference in a new issue