[perf] Refactor&Optimize inner core::arm7tdmi APIs.

Avoid passing ArmInstruction struct to handlers, as accesses to its fields can result in memory operations.


Former-commit-id: 6ea1719e36a0fefa1b30bdae4d6e8ab4dbf3af1a
Former-commit-id: e5855b8258f98d3f4c0819f3aec2fd0f47fef545
This commit is contained in:
Michel Heily 2020-10-05 23:46:14 -07:00 committed by MishMish
parent bf601404fa
commit dc7cd24e8d
10 changed files with 883 additions and 697 deletions

View file

@ -229,13 +229,18 @@ fn arm_format_to_handler(arm_fmt: &str) -> &'static str {
fn generate_thumb_lut(file: &mut fs::File) -> Result<(), std::io::Error> {
writeln!(
file,
"use super::thumb::ThumbFormat;
"/// This file is auto-generated from the build script
pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, &ThumbInstruction) -> CpuAction;
#[cfg(feature = \"debugger\")]
use super::thumb::ThumbFormat;
pub type ThumbInstructionHandler = fn(&mut Core, &mut SysBus, insn: u16) -> CpuAction;
#[cfg_attr(not(feature = \"debugger\"), repr(transparent))]
pub struct ThumbInstructionInfo {{
pub handler_fn: ThumbInstructionHandler,
#[cfg(feature = \"debugger\")]
pub fmt: ThumbFormat,
pub handler_fn: ThumbInstructionHandler
}}
"
)?;
@ -250,8 +255,13 @@ pub struct ThumbInstructionInfo {{
let handler_name = thumb_format_to_handler(thumb_fmt);
writeln!(
file,
" /* {:#x} */ ThumbInstructionInfo {{ fmt: ThumbFormat::{}, handler_fn: Core::{} }},",
i, thumb_fmt, handler_name
" /* {:#x} */
ThumbInstructionInfo {{
handler_fn: Core::{},
#[cfg(feature = \"debugger\")]
fmt: ThumbFormat::{},
}},",
i, handler_name, thumb_fmt
)?;
}
@ -263,13 +273,18 @@ pub struct ThumbInstructionInfo {{
fn generate_arm_lut(file: &mut fs::File) -> Result<(), std::io::Error> {
writeln!(
file,
"use super::arm::ArmFormat;
"/// This file is auto-generated from the build script
pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, &ArmInstruction) -> CpuAction;
#[cfg(feature = \"debugger\")]
use super::arm::ArmFormat;
pub type ArmInstructionHandler = fn(&mut Core, &mut SysBus, insn: u32) -> CpuAction;
#[cfg_attr(not(feature = \"debugger\"), repr(transparent))]
pub struct ArmInstructionInfo {{
pub handler_fn: ArmInstructionHandler,
#[cfg(feature = \"debugger\")]
pub fmt: ArmFormat,
pub handler_fn: ArmInstructionHandler
}}
"
)?;
@ -280,8 +295,13 @@ pub struct ArmInstructionInfo {{
let handler_name = arm_format_to_handler(arm_fmt);
writeln!(
file,
" /* {:#x} */ ArmInstructionInfo {{ fmt: ArmFormat::{}, handler_fn: Core::{} }},",
i, arm_fmt, handler_name
" /* {:#x} */
ArmInstructionInfo {{
handler_fn: Core::{},
#[cfg(feature = \"debugger\")]
fmt: ArmFormat::{},
}} ,",
i, handler_name, arm_fmt
)?;
}
writeln!(file, "];")?;

View file

@ -1,10 +1,8 @@
use std::fmt;
#[cfg(feature = "debugger")]
use crate::bit::BitIndex;
#[cfg(feature = "debugger")]
use super::{ArmFormat, ArmInstruction};
use super::{ArmDecodeHelper, ArmFormat, ArmInstruction};
use super::{AluOpCode, ArmCond, ArmHalfwordTransferType};
use crate::arm7tdmi::*;
@ -13,6 +11,7 @@ impl fmt::Display for ArmCond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmCond::*;
match self {
Invalid => panic!("Invalid condition code"),
EQ => write!(f, "eq"),
NE => write!(f, "ne"),
HS => write!(f, "cs"),
@ -102,24 +101,27 @@ impl fmt::Display for ShiftedRegister {
}
}
#[cfg(feature = "debugger")]
impl ArmInstruction {
fn fmt_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "bx\t{Rn}", Rn = reg_string(self.rn()))
write!(
f,
"bx\t{Rn}",
Rn = reg_string(self.raw.bit_range(0..4) as usize)
)
}
fn fmt_branch(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"b{link}{cond}\t{ofs:#x}",
link = if self.link_flag() { "l" } else { "" },
cond = self.cond(),
ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr)
link = if self.raw.link_flag() { "l" } else { "" },
cond = self.raw.cond(),
ofs = 8 + self.pc.wrapping_add(self.raw.branch_offset() as Addr)
)
}
fn set_cond_mark(&self) -> &str {
if self.set_cond_flag() {
if self.raw.set_cond_flag() {
"s"
} else {
""
@ -127,7 +129,7 @@ impl ArmInstruction {
}
fn fmt_operand2(&self, f: &mut fmt::Formatter<'_>) -> Result<Option<u32>, fmt::Error> {
let operand2 = self.operand2();
let operand2 = self.raw.operand2();
match operand2 {
BarrelShifterValue::RotatedImmediate(_, _) => {
let value = operand2.decode_rotated_immediate().unwrap();
@ -145,32 +147,35 @@ impl ArmInstruction {
fn fmt_data_processing(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AluOpCode::*;
let opcode = self.opcode();
let opcode = self.raw.opcode();
let rd = self.raw.bit_range(16..20) as usize;
let rn = self.raw.bit_range(16..20) as usize;
match opcode {
MOV | MVN => write!(
f,
"{opcode}{S}{cond}\t{Rd}, ",
opcode = opcode,
cond = self.cond(),
cond = self.raw.cond(),
S = self.set_cond_mark(),
Rd = reg_string(self.rd())
Rd = reg_string(rd)
),
CMP | CMN | TEQ | TST => write!(
f,
"{opcode}{cond}\t{Rn}, ",
opcode = opcode,
cond = self.cond(),
Rn = reg_string(self.rn())
cond = self.raw.cond(),
Rn = reg_string(rn)
),
_ => write!(
f,
"{opcode}{S}{cond}\t{Rd}, {Rn}, ",
opcode = opcode,
cond = self.cond(),
cond = self.raw.cond(),
S = self.set_cond_mark(),
Rd = reg_string(self.rd()),
Rn = reg_string(self.rn())
Rd = reg_string(rd),
Rn = reg_string(rn)
),
}?;
@ -179,18 +184,23 @@ impl ArmInstruction {
}
fn auto_incremenet_mark(&self) -> &str {
if self.write_back_flag() {
if self.raw.write_back_flag() {
"!"
} else {
""
}
}
fn fmt_rn_offset(&self, f: &mut fmt::Formatter<'_>, offset: BarrelShifterValue) -> fmt::Result {
write!(f, "[{Rn}", Rn = reg_string(self.rn()))?;
fn fmt_rn_offset(
&self,
f: &mut fmt::Formatter<'_>,
offset: BarrelShifterValue,
rn: usize,
) -> fmt::Result {
write!(f, "[{Rn}", Rn = reg_string(rn))?;
let (ofs_string, comment) = match offset {
BarrelShifterValue::ImmediateValue(value) => {
let value_for_commnet = if self.rn() == REG_PC {
let value_for_commnet = if rn == REG_PC {
value + self.pc + 8 // account for pipelining
} else {
value
@ -211,7 +221,7 @@ impl ArmInstruction {
_ => panic!("bad barrel shifter"),
};
if self.pre_index_flag() {
if self.raw.pre_index_flag() {
write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?;
} else {
write!(f, "], {}", ofs_string)?;
@ -228,33 +238,41 @@ impl ArmInstruction {
write!(
f,
"{mnem}{B}{T}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" },
B = if self.transfer_size() == 1 { "b" } else { "" },
cond = self.cond(),
T = if !self.pre_index_flag() && self.write_back_flag() {
mnem = if self.raw.load_flag() { "ldr" } else { "str" },
B = if self.raw.transfer_size() == 1 {
"b"
} else {
""
},
cond = self.raw.cond(),
T = if !self.raw.pre_index_flag() && self.raw.write_back_flag() {
"t"
} else {
""
},
Rd = reg_string(self.rd()),
Rd = reg_string(self.raw.bit_range(12..16) as usize),
)?;
self.fmt_rn_offset(f, self.ldr_str_offset())
self.fmt_rn_offset(
f,
self.raw.ldr_str_offset(),
self.raw.bit_range(16..20) as usize,
)
}
fn fmt_ldm_stm(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{",
mnem = if self.load_flag() { "ldm" } else { "stm" },
inc_dec = if self.add_offset_flag() { 'i' } else { 'd' },
pre_post = if self.pre_index_flag() { 'b' } else { 'a' },
cond = self.cond(),
Rn = reg_string(self.rn()),
auto_inc = if self.write_back_flag() { "!" } else { "" }
mnem = if self.raw.load_flag() { "ldm" } else { "stm" },
inc_dec = if self.raw.add_offset_flag() { 'i' } else { 'd' },
pre_post = if self.raw.pre_index_flag() { 'b' } else { 'a' },
cond = self.raw.cond(),
Rn = reg_string(self.raw.bit_range(16..20) as usize),
auto_inc = if self.raw.write_back_flag() { "!" } else { "" }
)?;
let register_list = self.register_list();
let register_list = self.raw.register_list();
let mut has_first = false;
for i in 0..16 {
if register_list.bit(i) {
@ -270,7 +288,7 @@ impl ArmInstruction {
write!(
f,
"}}{}",
if self.psr_and_force_user_flag() {
if self.raw.psr_and_force_user_flag() {
"^"
} else {
""
@ -283,9 +301,9 @@ impl ArmInstruction {
write!(
f,
"mrs{cond}\t{Rd}, {psr}",
cond = self.cond(),
Rd = reg_string(self.rd()),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }
cond = self.raw.cond(),
Rd = reg_string(self.raw.bit_range(12..16) as usize),
psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" }
)
}
@ -294,8 +312,8 @@ impl ArmInstruction {
write!(
f,
"msr{cond}\t{psr}, ",
cond = self.cond(),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" },
cond = self.raw.cond(),
psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" },
)?;
self.fmt_operand2(f).unwrap();
Ok(())
@ -305,8 +323,12 @@ impl ArmInstruction {
write!(
f,
"msr{cond}\t{psr}, ",
cond = self.cond(),
psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" },
cond = self.raw.cond(),
psr = if self.raw.spsr_flag() {
"SPSR_f"
} else {
"CPSR_f"
},
)?;
if let Ok(Some(op)) = self.fmt_operand2(f) {
let psr = RegPSR::new(op & 0xf000_0000);
@ -323,32 +345,33 @@ impl ArmInstruction {
}
fn fmt_mul_mla(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.accumulate_flag() {
let rd = self.raw.bit_range(16..20) as usize;
if self.raw.accumulate_flag() {
write!(
f,
"mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}",
S = self.set_cond_mark(),
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
Rn = reg_string(self.rn()),
cond = self.raw.cond(),
Rd = reg_string(rd),
Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.raw.rs()),
Rn = reg_string(self.raw.bit_range(12..16) as usize),
)
} else {
write!(
f,
"mul{S}{cond}\t{Rd}, {Rm}, {Rs}",
S = self.set_cond_mark(),
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
cond = self.raw.cond(),
Rd = reg_string(rd),
Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.raw.rs()),
)
}
}
fn sign_mark(&self) -> &str {
if self.u_flag() {
if self.raw.u_flag() {
"s"
} else {
"u"
@ -360,42 +383,60 @@ impl ArmInstruction {
f,
"{sign}{mnem}{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}",
sign = self.sign_mark(),
mnem = if self.accumulate_flag() {
mnem = if self.raw.accumulate_flag() {
"mlal"
} else {
"mull"
},
S = self.set_cond_mark(),
cond = self.cond(),
RdLo = reg_string(self.rd_lo()),
RdHi = reg_string(self.rd_hi()),
Rm = reg_string(self.rm()),
Rs = reg_string(self.rs()),
cond = self.raw.cond(),
RdLo = reg_string(self.raw.rd_lo()),
RdHi = reg_string(self.raw.rd_hi()),
Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.raw.rs()),
)
}
fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(transfer_type) = self.halfword_data_transfer_type() {
write!(
f,
"{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" },
cond = self.cond(),
type = transfer_type,
Rd = reg_string(self.rd()),
)?;
self.fmt_rn_offset(f, self.ldr_str_hs_offset().unwrap())
} else {
write!(f, "<undefined>")
}
fn fmt_ldr_str_hs_imm_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let transfer_type = self.raw.halfword_data_transfer_type();
write!(
f,
"{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.raw.load_flag() { "ldr" } else { "str" },
cond = self.raw.cond(),
type = transfer_type,
Rd = reg_string(self.raw.bit_range(12..16) as usize),
)?;
self.fmt_rn_offset(
f,
self.raw.ldr_str_hs_imm_offset(),
self.raw.bit_range(16..20) as usize,
)
}
fn fmt_ldr_str_hs_reg_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let transfer_type = self.raw.halfword_data_transfer_type();
write!(
f,
"{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.raw.load_flag() { "ldr" } else { "str" },
cond = self.raw.cond(),
type = transfer_type,
Rd = reg_string(self.raw.bit_range(12..16) as usize),
)?;
self.fmt_rn_offset(
f,
self.raw.ldr_str_hs_reg_offset(),
self.raw.bit_range(16..20) as usize,
)
}
fn fmt_swi(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"swi{cond}\t#{comm:#x}",
cond = self.cond(),
comm = self.swi_comment()
cond = self.raw.cond(),
comm = self.raw.swi_comment()
)
}
@ -403,16 +444,19 @@ impl ArmInstruction {
write!(
f,
"swp{B}{cond}\t{Rd}, {Rm}, [{Rn}]",
B = if self.transfer_size() == 1 { "b" } else { "" },
cond = self.cond(),
Rd = reg_string(self.rd()),
Rm = reg_string(self.rm()),
Rn = reg_string(self.rn()),
B = if self.raw.transfer_size() == 1 {
"b"
} else {
""
},
cond = self.raw.cond(),
Rd = reg_string(self.raw.bit_range(12..16) as usize),
Rm = reg_string(self.raw.rm()),
Rn = reg_string(self.raw.bit_range(16..20) as usize),
)
}
}
#[cfg(feature = "debugger")]
impl fmt::Display for ArmInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmFormat::*;
@ -427,8 +471,8 @@ impl fmt::Display for ArmInstruction {
MoveToFlags => self.fmt_msr_flags(f),
Multiply => self.fmt_mul_mla(f),
MultiplyLong => self.fmt_mull_mlal(f),
HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs(f),
HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs(f),
HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs_imm_offset(f),
HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs_reg_offset(f),
SoftwareInterrupt => self.fmt_swi(f),
SingleDataSwap => self.fmt_swp(f),
Undefined => write!(f, "<Undefined>"),

View file

@ -7,16 +7,13 @@ use crate::arm7tdmi::{Addr, Core, CpuMode, CpuState, REG_LR, REG_PC};
use crate::sysbus::SysBus;
use crate::Bus;
use super::ArmDecodeHelper;
use super::*;
#[inline(always)]
fn get_bs_op(shift_field: u32) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap()
}
impl Core {
pub fn exec_arm(&mut self, bus: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
match insn.fmt {
#[cfg(not(feature = "arm7tdmi_dispatch_table"))]
pub fn exec_arm(&mut self, bus: &mut SysBus, insn: u32, fmt: ArmFormat) -> CpuAction {
match fmt {
ArmFormat::BranchExchange => self.exec_arm_bx(bus, insn),
ArmFormat::BranchLink => self.exec_arm_b_bl(bus, insn),
ArmFormat::DataProcessing => self.exec_arm_data_processing(bus, insn),
@ -37,18 +34,19 @@ impl Core {
}
}
pub fn arm_undefined(&mut self, _: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn arm_undefined(&mut self, _: &mut SysBus, insn: u32) -> CpuAction {
panic!(
"executing undefined arm instruction {:08x} at @{:08x}",
insn.raw, insn.pc
insn,
self.pc_arm()
)
}
/// Cycles 2S+1N
pub fn exec_arm_b_bl(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_b_bl(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.S_cycle32(sb, self.pc);
if insn.link_flag() {
self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1);
self.set_reg(REG_LR, (self.pc_arm() + (self.word_size() as u32)) & !0b1);
}
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1;
@ -78,8 +76,8 @@ impl Core {
}
/// Cycles 2S+1N
pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
self.branch_exchange(sb, self.get_reg(insn.raw.bit_range(0..4) as usize))
pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.branch_exchange(sb, self.get_reg(insn.bit_range(0..4) as usize))
}
fn move_from_status_register(
@ -99,33 +97,29 @@ impl Core {
CpuAction::AdvancePC
}
pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
self.move_from_status_register(sb, insn.raw.bit_range(12..16) as usize, insn.spsr_flag())
pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.move_from_status_register(sb, insn.bit_range(12..16) as usize, insn.spsr_flag())
}
#[inline(always)]
fn decode_msr_param(&mut self, insn: &ArmInstruction) -> u32 {
if insn.raw.bit(25) {
let immediate = insn.raw & 0xff;
let rotate = 2 * insn.raw.bit_range(8..12);
fn decode_msr_param(&mut self, insn: u32) -> u32 {
if insn.bit(25) {
let immediate = insn & 0xff;
let rotate = 2 * insn.bit_range(8..12);
self.ror(immediate, rotate, self.cpsr.C(), false, true)
} else {
self.get_reg((insn.raw & 0b1111) as usize)
self.get_reg((insn & 0b1111) as usize)
}
}
// #[cfg(feature = "arm7tdmi_dispatch_table")]
pub fn exec_arm_transfer_to_status(
&mut self,
sb: &mut SysBus,
insn: &ArmInstruction,
) -> CpuAction {
pub fn exec_arm_transfer_to_status(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let value = self.decode_msr_param(insn);
let f = insn.raw.bit(19);
let s = insn.raw.bit(18);
let x = insn.raw.bit(17);
let c = insn.raw.bit(16);
let f = insn.bit(19);
let s = insn.bit(18);
let x = insn.bit(17);
let c = insn.bit(16);
let mut mask = 0;
if f {
@ -181,51 +175,46 @@ impl Core {
///
/// Cycles: 1S+x+y (from GBATEK)
/// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15.
pub fn exec_arm_data_processing(
&mut self,
sb: &mut SysBus,
insn: &ArmInstruction,
) -> CpuAction {
pub fn exec_arm_data_processing(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
use AluOpCode::*;
let raw_insn = insn.raw;
self.S_cycle32(sb, self.pc);
let rn = raw_insn.bit_range(16..20) as usize;
let rd = raw_insn.bit_range(12..16) as usize;
let rn = insn.bit_range(16..20) as usize;
let rd = insn.bit_range(12..16) as usize;
let mut op1 = if rn == REG_PC {
insn.pc + 8
self.pc_arm() + 8
} else {
self.get_reg(rn)
};
let mut s_flag = insn.set_cond_flag();
let opcode = insn.opcode();
let op2 = if raw_insn.bit(25) {
let immediate = raw_insn & 0xff;
let rotate = 2 * raw_insn.bit_range(8..12);
let op2 = if insn.bit(25) {
let immediate = insn & 0xff;
let rotate = 2 * insn.bit_range(8..12);
// TODO refactor out
let bs_carry_in = self.cpsr.C();
self.bs_carry_out = bs_carry_in;
self.ror(immediate, rotate, self.cpsr.C(), false, true)
} else {
let reg = raw_insn & 0xf;
let reg = insn & 0xf;
let shift_by = if raw_insn.bit(4) {
let shift_by = if insn.bit(4) {
if rn == REG_PC {
op1 += 4;
}
let rs = raw_insn.bit_range(8..12) as usize;
let rs = insn.bit_range(8..12) as usize;
ShiftRegisterBy::ByRegister(rs)
} else {
let amount = raw_insn.bit_range(7..12) as u32;
let amount = insn.bit_range(7..12) as u32;
ShiftRegisterBy::ByAmount(amount)
};
let shifted_reg = ShiftedRegister {
reg: reg as usize,
bs_op: get_bs_op(raw_insn),
bs_op: insn.get_bs_op(),
shift_by: shift_by,
added: None,
};
@ -277,7 +266,7 @@ impl Core {
MOV => op2,
BIC => op1 & (!op2),
MVN => !op2,
_ => panic!("{} should be a PSR transfer", opcode),
_ => panic!("DataProcessing should be a PSR transfer"),
})
};
@ -304,17 +293,17 @@ impl Core {
/// STR{cond}{B}{T} Rd,<Address> | 2N | ---- | [Rn+/-<offset>]=Rd
/// ------------------------------------------------------------------------------
/// For LDR, add y=1S+1N if Rd=R15.
pub fn exec_arm_ldr_str(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_ldr_str(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let mut result = CpuAction::AdvancePC;
let load = insn.load_flag();
let pre_index = insn.pre_index_flag();
let writeback = insn.write_back_flag();
let base_reg = insn.raw.bit_range(16..20) as usize;
let dest_reg = insn.raw.bit_range(12..16) as usize;
let base_reg = insn.bit_range(16..20) as usize;
let dest_reg = insn.bit_range(12..16) as usize;
let mut addr = self.get_reg(base_reg);
if base_reg == REG_PC {
addr = insn.pc + 8; // prefetching
addr = self.pc_arm() + 8; // prefetching
}
let offset = self.get_barrel_shifted_value(&insn.ldr_str_offset());
let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr;
@ -352,7 +341,7 @@ impl Core {
}
} else {
let value = if dest_reg == REG_PC {
insn.pc + 12
self.pc_arm() + 12
} else {
self.get_reg(dest_reg)
};
@ -381,12 +370,12 @@ impl Core {
result
}
pub fn exec_arm_ldr_str_hs_reg(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_ldr_str_hs_reg(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.ldr_str_hs(
sb,
insn,
BarrelShifterValue::ShiftedRegister(ShiftedRegister {
reg: (insn.raw & 0xf) as usize,
reg: (insn & 0xf) as usize,
shift_by: ShiftRegisterBy::ByAmount(0),
bs_op: BarrelShiftOpCode::LSL,
added: Some(insn.add_offset_flag()),
@ -394,8 +383,8 @@ impl Core {
)
}
pub fn exec_arm_ldr_str_hs_imm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
let offset8 = (insn.raw.bit_range(8..12) << 4) + insn.raw.bit_range(0..4);
pub fn exec_arm_ldr_str_hs_imm(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let offset8 = (insn.bit_range(8..12) << 4) + insn.bit_range(0..4);
let offset8 = if insn.add_offset_flag() {
offset8
} else {
@ -408,7 +397,7 @@ impl Core {
pub fn ldr_str_hs(
&mut self,
sb: &mut SysBus,
insn: &ArmInstruction,
insn: u32,
offset: BarrelShifterValue,
) -> CpuAction {
let mut result = CpuAction::AdvancePC;
@ -416,11 +405,11 @@ impl Core {
let load = insn.load_flag();
let pre_index = insn.pre_index_flag();
let writeback = insn.write_back_flag();
let base_reg = insn.raw.bit_range(16..20) as usize;
let dest_reg = insn.raw.bit_range(12..16) as usize;
let base_reg = insn.bit_range(16..20) as usize;
let dest_reg = insn.bit_range(12..16) as usize;
let mut addr = self.get_reg(base_reg);
if base_reg == REG_PC {
addr = insn.pc + 8; // prefetching
addr = self.pc_arm() + 8; // prefetching
}
let offset = self.get_barrel_shifted_value(&offset);
@ -440,7 +429,7 @@ impl Core {
if load {
self.S_cycle32(sb, self.pc);
let data = match insn.halfword_data_transfer_type().unwrap() {
let data = match insn.halfword_data_transfer_type() {
ArmHalfwordTransferType::SignedByte => {
self.N_cycle8(sb, addr);
sb.read_8(addr) as u8 as i8 as u32
@ -466,12 +455,12 @@ impl Core {
}
} else {
let value = if dest_reg == REG_PC {
insn.pc + 12
self.pc_arm() + 12
} else {
self.get_reg(dest_reg)
};
match insn.halfword_data_transfer_type().unwrap() {
match insn.halfword_data_transfer_type() {
ArmHalfwordTransferType::UnsignedHalfwords => {
self.N_cycle32(sb, addr);
self.write_16(addr, value as u16, sb);
@ -492,15 +481,15 @@ impl Core {
result
}
pub fn exec_arm_ldm_stm(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_ldm_stm(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let mut result = CpuAction::AdvancePC;
let mut full = insn.pre_index_flag();
let ascending = insn.add_offset_flag();
let s_flag = insn.raw.bit(22);
let s_flag = insn.bit(22);
let is_load = insn.load_flag();
let mut writeback = insn.write_back_flag();
let base_reg = insn.raw.bit_range(16..20) as usize;
let base_reg = insn.bit_range(16..20) as usize;
let mut base_addr = self.get_reg(base_reg);
let rlist = insn.register_list();
@ -583,7 +572,7 @@ impl Core {
if rlist.bit(r) {
let val = if r != base_reg {
if r == REG_PC {
insn.pc + 12
self.pc_arm() + 12
} else {
self.get_reg(r)
}
@ -653,9 +642,9 @@ impl Core {
result
}
pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
let rd = insn.raw.bit_range(16..20) as usize;
let rn = insn.raw.bit_range(12..16) as usize;
pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let rd = insn.bit_range(16..20) as usize;
let rn = insn.bit_range(12..16) as usize;
let rs = insn.rs();
let rm = insn.rm();
@ -691,7 +680,7 @@ impl Core {
CpuAction::AdvancePC
}
pub fn exec_arm_mull_mlal(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_mull_mlal(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let rd_hi = insn.rd_hi();
let rd_lo = insn.rd_lo();
let rs = insn.rs();
@ -734,9 +723,9 @@ impl Core {
CpuAction::AdvancePC
}
pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
let base_addr = self.get_reg(insn.raw.bit_range(16..20) as usize);
let rd = insn.raw.bit_range(12..16) as usize;
pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let base_addr = self.get_reg(insn.bit_range(16..20) as usize);
let rd = insn.bit_range(12..16) as usize;
if insn.transfer_size() == 1 {
let t = sb.read_8(base_addr);
self.N_cycle8(sb, base_addr);
@ -756,7 +745,7 @@ impl Core {
CpuAction::AdvancePC
}
pub fn exec_arm_swi(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction {
pub fn exec_arm_swi(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.software_interrupt(sb, self.pc - 4, insn.swi_comment());
CpuAction::FlushPipeline
}

View file

@ -1,3 +1,4 @@
#[cfg(feature = "debugger")]
pub mod display;
pub mod exec;
@ -21,7 +22,6 @@ pub enum ArmDecodeErrorKind {
InvalidHSBits(u32),
IoError(io::ErrorKind),
}
use ArmDecodeErrorKind::*;
#[derive(Debug, PartialEq)]
pub struct ArmDecodeError {
@ -30,6 +30,7 @@ pub struct ArmDecodeError {
pub addr: Addr,
}
#[allow(dead_code)]
impl ArmDecodeError {
fn new(kind: ArmDecodeErrorKind, insn: u32, addr: Addr) -> ArmDecodeError {
ArmDecodeError {
@ -57,6 +58,7 @@ pub enum ArmCond {
GT = 0b1100,
LE = 0b1101,
AL = 0b1110,
Invalid = 0b1111,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
@ -82,33 +84,10 @@ pub enum ArmFormat {
Undefined,
}
#[derive(Debug, PartialEq, Primitive)]
pub enum ArmHalfwordTransferType {
UnsignedHalfwords = 0b01,
SignedByte = 0b10,
SignedHalfwords = 0b11,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ArmInstruction {
pub fmt: ArmFormat,
pub raw: u32,
pub pc: Addr,
}
impl ArmInstruction {
pub fn new(raw: u32, pc: Addr, fmt: ArmFormat) -> ArmInstruction {
ArmInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ArmInstruction {
type IntType = u32;
fn decode(raw: u32, addr: Addr) -> Self {
impl From<u32> for ArmFormat {
fn from(raw: u32) -> ArmFormat {
use ArmFormat::*;
let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 {
if (0x0fff_fff0 & raw) == 0x012f_ff10 {
BranchExchange
} else if (0x0e00_0000 & raw) == 0x0a00_0000 {
BranchLink
@ -140,7 +119,35 @@ impl InstructionDecoder for ArmInstruction {
DataProcessing
} else {
Undefined
};
}
}
}
#[derive(Debug, PartialEq, Primitive)]
pub enum ArmHalfwordTransferType {
UnsignedHalfwords = 0b01,
SignedByte = 0b10,
SignedHalfwords = 0b11,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ArmInstruction {
pub fmt: ArmFormat,
pub raw: u32,
pub pc: Addr,
}
impl ArmInstruction {
pub fn new(raw: u32, pc: Addr, fmt: ArmFormat) -> ArmInstruction {
ArmInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ArmInstruction {
type IntType = u32;
fn decode(raw: u32, addr: Addr) -> Self {
let fmt = ArmFormat::from(raw);
ArmInstruction {
fmt: fmt,
@ -160,209 +167,266 @@ impl InstructionDecoder for ArmInstruction {
}
}
impl ArmInstruction {
fn make_decode_error(&self, kind: ArmDecodeErrorKind) -> ArmDecodeError {
ArmDecodeError {
kind: kind,
insn: self.raw,
addr: self.pc,
}
}
pub trait ArmDecodeHelper {
fn cond(&self) -> ArmCond;
pub fn cond(&self) -> ArmCond {
ArmCond::from_u32(self.raw.bit_range(28..32)).unwrap()
}
fn rm(&self) -> usize;
pub fn rn(&self) -> usize {
match self.fmt {
ArmFormat::Multiply => self.raw.bit_range(12..16) as usize,
ArmFormat::MultiplyLong => self.raw.bit_range(8..12) as usize,
ArmFormat::BranchExchange => self.raw.bit_range(0..4) as usize,
_ => self.raw.bit_range(16..20) as usize,
}
}
fn rs(&self) -> usize;
pub fn rd(&self) -> usize {
match self.fmt {
ArmFormat::Multiply => self.raw.bit_range(16..20) as usize,
_ => self.raw.bit_range(12..16) as usize,
}
}
fn rd_lo(&self) -> usize;
pub fn rm(&self) -> usize {
self.raw.bit_range(0..4) as usize
}
fn rd_hi(&self) -> usize;
pub fn rs(&self) -> usize {
self.raw.bit_range(8..12) as usize
}
fn opcode(&self) -> AluOpCode;
pub fn rd_lo(&self) -> usize {
self.raw.bit_range(12..16) as usize
}
fn branch_offset(&self) -> i32;
pub fn rd_hi(&self) -> usize {
self.raw.bit_range(16..20) as usize
}
fn load_flag(&self) -> bool;
pub fn opcode(&self) -> AluOpCode {
use std::hint::unreachable_unchecked;
fn set_cond_flag(&self) -> bool;
unsafe {
if let Some(opc) = AluOpCode::from_u16(self.raw.bit_range(21..25) as u16) {
opc
} else {
unreachable_unchecked()
}
}
}
fn write_back_flag(&self) -> bool;
pub fn branch_offset(&self) -> i32 {
((self.raw.bit_range(0..24) << 8) as i32) >> 6
}
fn accumulate_flag(&self) -> bool;
pub fn load_flag(&self) -> bool {
self.raw.bit(20)
}
fn u_flag(&self) -> bool;
pub fn set_cond_flag(&self) -> bool {
self.raw.bit(20)
}
fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType;
pub fn write_back_flag(&self) -> bool {
self.raw.bit(21)
}
fn transfer_size(&self) -> usize;
pub fn accumulate_flag(&self) -> bool {
self.raw.bit(21)
}
fn psr_and_force_user_flag(&self) -> bool;
pub fn u_flag(&self) -> bool {
self.raw.bit(22)
}
fn spsr_flag(&self) -> bool;
pub fn halfword_data_transfer_type(&self) -> Result<ArmHalfwordTransferType, ArmDecodeError> {
let bits = (self.raw & 0b1100000) >> 5;
match ArmHalfwordTransferType::from_u32(bits) {
Some(x) => Ok(x),
None => Err(ArmDecodeError::new(InvalidHSBits(bits), self.raw, self.pc)),
}
}
fn add_offset_flag(&self) -> bool;
pub fn transfer_size(&self) -> usize {
if self.raw.bit(22) {
1
} else {
4
}
}
fn pre_index_flag(&self) -> bool;
pub fn psr_and_force_user_flag(&self) -> bool {
self.raw.bit(22)
}
pub fn spsr_flag(&self) -> bool {
self.raw.bit(22)
}
pub fn add_offset_flag(&self) -> bool {
self.raw.bit(23)
}
pub fn pre_index_flag(&self) -> bool {
self.raw.bit(24)
}
pub fn link_flag(&self) -> bool {
self.raw.bit(24)
}
fn link_flag(&self) -> bool;
/// gets offset used by ldr/str instructions
pub fn ldr_str_offset(&self) -> BarrelShifterValue {
let ofs = self.raw.bit_range(0..12);
if self.raw.bit(25) {
let rm = ofs & 0xf;
BarrelShifterValue::ShiftedRegister(ShiftedRegister {
reg: rm as usize,
shift_by: self.get_shift_reg_by(ofs),
bs_op: self.get_bs_op(ofs),
added: Some(self.add_offset_flag()),
})
} else {
let ofs = if self.add_offset_flag() {
ofs as u32
} else {
-(ofs as i32) as u32
};
BarrelShifterValue::ImmediateValue(ofs)
}
}
fn ldr_str_offset(&self) -> BarrelShifterValue;
#[inline(always)]
fn get_bs_op(&self, shift_field: u32) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap()
}
fn get_bs_op(&self) -> BarrelShiftOpCode;
#[inline(always)]
fn get_shift_reg_by(&self, shift_field: u32) -> ShiftRegisterBy {
if shift_field.bit(4) {
let rs = shift_field.bit_range(8..12) as usize;
ShiftRegisterBy::ByRegister(rs)
} else {
let amount = shift_field.bit_range(7..12) as u32;
ShiftRegisterBy::ByAmount(amount)
}
}
fn get_shift_reg_by(&self) -> ShiftRegisterBy;
pub fn ldr_str_hs_offset(&self) -> Result<BarrelShifterValue, ArmDecodeError> {
match self.fmt {
ArmFormat::HalfwordDataTransferImmediateOffset => {
let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4);
let offset8 = if self.add_offset_flag() {
offset8
} else {
(-(offset8 as i32)) as u32
};
Ok(BarrelShifterValue::ImmediateValue(offset8))
}
ArmFormat::HalfwordDataTransferRegOffset => {
Ok(BarrelShifterValue::ShiftedRegister(ShiftedRegister {
reg: (self.raw & 0xf) as usize,
shift_by: ShiftRegisterBy::ByAmount(0),
bs_op: BarrelShiftOpCode::LSL,
added: Some(self.add_offset_flag()),
}))
}
_ => Err(self.make_decode_error(DecodedPartDoesNotBelongToInstruction)),
}
}
fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue;
pub fn operand2(&self) -> BarrelShifterValue {
if self.raw.bit(25) {
let immediate = self.raw & 0xff;
let rotate = 2 * self.raw.bit_range(8..12);
BarrelShifterValue::RotatedImmediate(immediate, rotate)
} else {
let reg = self.raw & 0xf;
let shifted_reg = ShiftedRegister {
reg: reg as usize,
bs_op: self.get_bs_op(self.raw),
shift_by: self.get_shift_reg_by(self.raw),
added: None,
}; // TODO error handling
BarrelShifterValue::ShiftedRegister(shifted_reg)
}
}
fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue;
pub fn register_list(&self) -> u16 {
(self.raw & 0xffff) as u16
}
fn operand2(&self) -> BarrelShifterValue;
pub fn swi_comment(&self) -> u32 {
self.raw.bit_range(0..24)
}
fn register_list(&self) -> u16;
fn swi_comment(&self) -> u32;
}
macro_rules! arm_decode_helper_impl {
($($t:ty),*) => {$(
impl ArmDecodeHelper for $t {
#[inline(always)]
fn cond(&self) -> ArmCond {
ArmCond::from_u32(self.bit_range(28..32)).unwrap()
}
#[inline(always)]
fn rm(&self) -> usize {
self.bit_range(0..4) as usize
}
#[inline(always)]
fn rs(&self) -> usize {
self.bit_range(8..12) as usize
}
#[inline(always)]
fn rd_lo(&self) -> usize {
self.bit_range(12..16) as usize
}
#[inline(always)]
fn rd_hi(&self) -> usize {
self.bit_range(16..20) as usize
}
#[inline(always)]
fn opcode(&self) -> AluOpCode {
use std::hint::unreachable_unchecked;
unsafe {
if let Some(opc) = AluOpCode::from_u16(self.bit_range(21..25) as u16) {
opc
} else {
unreachable_unchecked()
}
}
}
#[inline(always)]
fn branch_offset(&self) -> i32 {
((self.bit_range(0..24) << 8) as i32) >> 6
}
#[inline(always)]
fn load_flag(&self) -> bool {
self.bit(20)
}
#[inline(always)]
fn set_cond_flag(&self) -> bool {
self.bit(20)
}
#[inline(always)]
fn write_back_flag(&self) -> bool {
self.bit(21)
}
#[inline(always)]
fn accumulate_flag(&self) -> bool {
self.bit(21)
}
#[inline(always)]
fn u_flag(&self) -> bool {
self.bit(22)
}
#[inline(always)]
fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType {
let bits = (*self & 0b1100000) >> 5;
ArmHalfwordTransferType::from_u32(bits).unwrap()
}
#[inline(always)]
fn transfer_size(&self) -> usize {
if self.bit(22) {
1
} else {
4
}
}
#[inline(always)]
fn psr_and_force_user_flag(&self) -> bool {
self.bit(22)
}
#[inline(always)]
fn spsr_flag(&self) -> bool {
self.bit(22)
}
#[inline(always)]
fn add_offset_flag(&self) -> bool {
self.bit(23)
}
#[inline(always)]
fn pre_index_flag(&self) -> bool {
self.bit(24)
}
#[inline(always)]
fn link_flag(&self) -> bool {
self.bit(24)
}
/// gets offset used by ldr/str instructions
#[inline(always)]
fn ldr_str_offset(&self) -> BarrelShifterValue {
let ofs = self.bit_range(0..12);
if self.bit(25) {
let rm = ofs & 0xf;
BarrelShifterValue::ShiftedRegister(ShiftedRegister {
reg: rm as usize,
shift_by: self.get_shift_reg_by(),
bs_op: self.get_bs_op(),
added: Some(self.add_offset_flag()),
})
} else {
let ofs = if self.add_offset_flag() {
ofs as u32
} else {
-(ofs as i32) as u32
};
BarrelShifterValue::ImmediateValue(ofs)
}
}
#[inline(always)]
fn get_bs_op(&self) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(self.bit_range(5..7) as u8).unwrap()
}
#[inline(always)]
fn get_shift_reg_by(&self) -> ShiftRegisterBy {
if self.bit(4) {
let rs = self.bit_range(8..12) as usize;
ShiftRegisterBy::ByRegister(rs)
} else {
let amount = self.bit_range(7..12) as u32;
ShiftRegisterBy::ByAmount(amount)
}
}
#[inline(always)]
fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue {
let offset8 = (self.bit_range(8..12) << 4) + self.bit_range(0..4);
let offset8 = if self.add_offset_flag() {
offset8
} else {
(-(offset8 as i32)) as u32
};
BarrelShifterValue::ImmediateValue(offset8)
}
#[inline(always)]
fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue {
BarrelShifterValue::ShiftedRegister(
ShiftedRegister {
reg: (self & 0xf) as usize,
shift_by: ShiftRegisterBy::ByAmount(0),
bs_op: BarrelShiftOpCode::LSL,
added: Some(self.add_offset_flag()),
})
}
fn operand2(&self) -> BarrelShifterValue {
if self.bit(25) {
let immediate = self & 0xff;
let rotate = 2 * self.bit_range(8..12);
BarrelShifterValue::RotatedImmediate(immediate, rotate)
} else {
let reg = self & 0xf;
let shifted_reg = ShiftedRegister {
reg: reg as usize,
bs_op: self.get_bs_op(),
shift_by: self.get_shift_reg_by(),
added: None,
}; // TODO error handling
BarrelShifterValue::ShiftedRegister(shifted_reg)
}
}
fn register_list(&self) -> u16 {
(self & 0xffff) as u16
}
fn swi_comment(&self) -> u32 {
self.bit_range(0..24)
}
}
)*}
}
arm_decode_helper_impl!(u32);
// #[cfg(test)]
// /// All instructions constants were generated using an ARM assembler.
// mod tests {

View file

@ -1,29 +1,35 @@
#[cfg(feature = "debugger")]
use super::reg_string;
#[cfg(feature = "debugger")]
use ansi_term::{Colour, Style};
use serde::{Deserialize, Serialize};
#[cfg(feature = "debugger")]
use std::fmt;
use super::arm::ArmCond;
// Include files that are auto-generated by the build script
// See `build.rs`
#[cfg(feature = "arm7tdmi_dispatch_table")]
include!(concat!(env!("OUT_DIR"), "/arm_lut.rs"));
#[cfg(feature = "arm7tdmi_dispatch_table")]
include!(concat!(env!("OUT_DIR"), "/thumb_lut.rs"));
#[cfg(not(feature = "arm7tdmi_dispatch_table"))]
use super::InstructionDecoder;
pub use super::exception::Exception;
use super::CpuAction;
#[cfg(feature = "debugger")]
use super::DecodedInstruction;
use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState};
use super::{psr::RegPSR, Addr, CpuMode, CpuState, arm::ArmCond};
cfg_if! {
if #[cfg(feature = "arm7tdmi_dispatch_table")] {
// Include files that are auto-generated by the build script
// See `build.rs`
include!(concat!(env!("OUT_DIR"), "/arm_lut.rs"));
include!(concat!(env!("OUT_DIR"), "/thumb_lut.rs"));
} else {
use super::arm::ArmFormat;
use super::thumb::ThumbFormat;
}
}
cfg_if! {
if #[cfg(feature = "debugger")] {
use super::DecodedInstruction;
use super::arm::ArmInstruction;
use super::thumb::ThumbInstruction;
use super::reg_string;
use std::fmt;
use ansi_term::{Colour, Style};
} else {
}
}
use crate::bus::Bus;
use crate::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus};
@ -90,6 +96,18 @@ impl Core {
}
}
#[inline]
/// Gets PC of the currently executed instruction in arm mode
pub fn pc_arm(&self) -> u32 {
self.pc.wrapping_sub(8)
}
#[inline]
/// Gets PC of the currently executed instruction in thumb mode
pub fn pc_thumb(&self) -> u32 {
self.pc.wrapping_sub(4)
}
pub fn get_reg_user(&mut self, r: usize) -> u32 {
match r {
0..=7 => self.gpr[r],
@ -291,6 +309,10 @@ impl Core {
pub(super) fn check_arm_cond(&self, cond: ArmCond) -> bool {
use ArmCond::*;
match cond {
Invalid => {
// TODO - we would normally want to panic here
false
}
EQ => self.cpsr.Z(),
NE => !self.cpsr.Z(),
HS => self.cpsr.C(),
@ -319,43 +341,56 @@ impl Core {
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction {
let hash = (((insn >> 16) & 0xff0) | ((insn >> 4) & 0x00f)) as usize;
let arm_info = &ARM_LUT[hash];
let arm_insn = ArmInstruction::new(insn, self.pc.wrapping_sub(8), arm_info.fmt);
#[cfg(feature = "debugger")]
self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone()));
self.debugger_record_step(DecodedInstruction::Arm(ArmInstruction::new(
insn,
self.pc.wrapping_sub(8),
arm_info.fmt,
)));
(arm_info.handler_fn)(self, sb, &arm_insn)
(arm_info.handler_fn)(self, sb, insn)
}
#[cfg(feature = "arm7tdmi_dispatch_table")]
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction {
let thumb_info = &THUMB_LUT[(insn >> 6) as usize];
let thumb_insn = ThumbInstruction::new(insn, self.pc.wrapping_sub(4), thumb_info.fmt);
#[cfg(feature = "debugger")]
self.debugger_record_step(DecodedInstruction::Thumb(thumb_insn.clone()));
self.debugger_record_step(DecodedInstruction::Thumb(ThumbInstruction::new(
insn,
self.pc.wrapping_sub(4),
thumb_info.fmt,
)));
(thumb_info.handler_fn)(self, sb, &thumb_insn)
(thumb_info.handler_fn)(self, sb, 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));
let arm_fmt = ArmFormat::from(insn);
#[cfg(feature = "debugger")]
self.debugger_record_step(DecodedInstruction::Arm(arm_insn.clone()));
self.debugger_record_step(DecodedInstruction::Arm(ArmInstruction::new(
insn,
self.pc.wrapping_sub(8),
arm_fmt,
)));
self.exec_arm(sb, &arm_insn)
self.exec_arm(sb, insn, arm_fmt)
}
#[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));
let thumb_fmt = ThumbFormat::from(insn);
#[cfg(feature = "debugger")]
self.debugger_record_step(DecodedInstruction::Thumb(thumb_insn.clone()));
self.debugger_record_step(DecodedInstruction::Thumb(ThumbInstruction::new(
insn,
self.pc.wrapping_sub(4),
thumb_fmt,
)));
self.exec_thumb(sb, &thumb_insn)
self.exec_thumb(sb, insn, thumb_fmt)
}
#[inline(always)]
@ -399,8 +434,8 @@ impl Core {
let insn = self.pipeline[0];
self.pipeline[0] = self.pipeline[1];
self.pipeline[1] = fetched_now;
let cond =
ArmCond::from_u32(insn.bit_range(28..32)).expect("invalid arm condition");
let cond = ArmCond::from_u8(insn.bit_range(28..32) as u8)
.unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() });
if cond != ArmCond::AL {
if !self.check_arm_cond(cond) {
self.S_cycle32(bus, self.pc);

View file

@ -62,12 +62,12 @@ pub trait InstructionDecoder: Sized {
fn get_raw(&self) -> Self::IntType;
}
pub fn reg_string(reg: usize) -> &'static str {
pub fn reg_string<T: Into<usize>>(reg: T) -> &'static str {
let reg_names = &[
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr",
"pc",
];
reg_names[reg]
reg_names[reg.into()]
}
#[derive(Debug, PartialEq, Primitive, Copy, Clone)]

View file

@ -1,22 +1,21 @@
use std::fmt;
#[cfg(feature = "debugger")]
use crate::bit::BitIndex;
use super::*;
#[cfg(feature = "debugger")]
use crate::arm7tdmi::*;
#[cfg(feature = "debugger")]
use super::ThumbDecodeHelper;
impl ThumbInstruction {
fn fmt_thumb_move_shifted_reg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{op}\t{Rd}, {Rs}, #{Offset5}",
op = self.format1_op(),
Rd = reg_string(self.rd()),
Rs = reg_string(self.rs()),
Offset5 = self.offset5()
op = self.raw.format1_op(),
Rd = reg_string(self.raw & 0b111),
Rs = reg_string(self.raw.rs()),
Offset5 = self.raw.offset5()
)
}
@ -24,8 +23,8 @@ impl ThumbInstruction {
write!(
f,
"{op}\t{Rd}, #{Offset8:#x}",
op = self.format3_op(),
Rd = reg_string(self.rd()),
op = self.raw.format3_op(),
Rd = reg_string(self.raw.bit_range(8..11)),
Offset8 = self.raw & 0xff
)
}
@ -34,23 +33,23 @@ impl ThumbInstruction {
write!(
f,
"{op}\t{Rd}, {Rs}",
op = self.format4_alu_op(),
Rd = reg_string(self.rd()),
Rs = reg_string(self.rs())
op = self.raw.format4_alu_op(),
Rd = reg_string(self.raw & 0b111),
Rs = reg_string(self.raw.rs())
)
}
fn fmt_thumb_high_reg_op_or_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let op = self.format5_op();
let dst_reg = if self.flag(ThumbInstruction::FLAG_H1) {
self.rd() + 8
let op = self.raw.format5_op();
let dst_reg = if self.raw.flag(consts::flags::FLAG_H1) {
self.raw & 0b111 + 8
} else {
self.rd()
self.raw & 0b111
};
let src_reg = if self.flag(ThumbInstruction::FLAG_H2) {
self.rs() + 8
let src_reg = if self.raw.flag(consts::flags::FLAG_H2) {
self.raw.rs() + 8
} else {
self.rs()
self.raw.rs()
};
write!(f, "{}\t", op)?;
@ -69,9 +68,9 @@ impl ThumbInstruction {
write!(
f,
"ldr\t{Rd}, [pc, #{Imm:#x}] ; = #{effective:#x}",
Rd = reg_string(self.rd()),
Imm = self.word8(),
effective = (self.pc + 4 & !0b10) + (self.word8() as Addr)
Rd = reg_string(self.raw.bit_range(8..11)),
Imm = self.raw.word8(),
effective = (self.pc + 4 & !0b10) + (self.raw.word8() as Addr)
)
}
@ -79,15 +78,11 @@ impl ThumbInstruction {
write!(
f,
"{op}{b}\t{Rd}, [{Rb}, {Ro}]",
op = if self.is_load() { "ldr" } else { "str" },
b = if self.is_transferring_bytes() {
"b"
} else {
""
},
Rd = reg_string(self.rd()),
Rb = reg_string(self.rb()),
Ro = reg_string(self.ro()),
op = if self.raw.is_load() { "ldr" } else { "str" },
b = if self.raw.bit(10) { "b" } else { "" },
Rd = reg_string(self.raw & 0b111),
Rb = reg_string(self.raw.rb()),
Ro = reg_string(self.raw.ro()),
)
}
@ -97,8 +92,8 @@ impl ThumbInstruction {
"{op}\t{Rd}, [{Rb}, {Ro}]",
op = {
match (
self.flag(ThumbInstruction::FLAG_SIGN_EXTEND),
self.flag(ThumbInstruction::FLAG_HALFWORD),
self.raw.flag(consts::flags::FLAG_SIGN_EXTEND),
self.raw.flag(consts::flags::FLAG_HALFWORD),
) {
(false, false) => "strh",
(false, true) => "ldrh",
@ -106,27 +101,24 @@ impl ThumbInstruction {
(true, true) => "ldsh",
}
},
Rd = reg_string(self.rd()),
Rb = reg_string(self.rb()),
Ro = reg_string(self.ro()),
Rd = reg_string(self.raw & 0b111),
Rb = reg_string(self.raw.rb()),
Ro = reg_string(self.raw.ro()),
)
}
fn fmt_thumb_ldr_str_imm_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let is_transferring_bytes = self.raw.bit(12);
write!(
f,
"{op}{b}\t{Rd}, [{Rb}, #{imm:#x}]",
op = if self.is_load() { "ldr" } else { "str" },
b = if self.is_transferring_bytes() {
"b"
} else {
""
},
Rd = reg_string(self.rd()),
Rb = reg_string(self.rb()),
op = if self.raw.is_load() { "ldr" } else { "str" },
b = if is_transferring_bytes { "b" } else { "" },
Rd = reg_string(self.raw & 0b111),
Rb = reg_string(self.raw.rb()),
imm = {
let offset5 = self.offset5();
if self.is_transferring_bytes() {
let offset5 = self.raw.offset5();
if is_transferring_bytes {
offset5
} else {
(offset5 << 3) >> 1
@ -139,10 +131,10 @@ impl ThumbInstruction {
write!(
f,
"{op}\t{Rd}, [{Rb}, #{imm:#x}]",
op = if self.is_load() { "ldrh" } else { "strh" },
Rd = reg_string(self.rd()),
Rb = reg_string(self.rb()),
imm = self.offset5() << 1
op = if self.raw.is_load() { "ldrh" } else { "strh" },
Rd = reg_string(self.raw & 0b111),
Rb = reg_string(self.raw.rb()),
imm = self.raw.offset5() << 1
)
}
@ -150,9 +142,9 @@ impl ThumbInstruction {
write!(
f,
"{op}\t{Rd}, [sp, #{Imm:#x}]",
op = if self.is_load() { "ldr" } else { "str" },
Rd = reg_string(self.rd()),
Imm = self.word8(),
op = if self.raw.is_load() { "ldr" } else { "str" },
Rd = reg_string(self.raw.bit_range(8..11)),
Imm = self.raw.word8(),
)
}
@ -160,35 +152,35 @@ impl ThumbInstruction {
write!(
f,
"add\t{Rd}, {r}, #{Imm:#x}",
Rd = reg_string(self.rd()),
r = if self.flag(ThumbInstruction::FLAG_SP) {
Rd = reg_string(self.raw.bit_range(8..11)),
r = if self.raw.flag(consts::flags::FLAG_SP) {
"sp"
} else {
"pc"
},
Imm = self.word8(),
Imm = self.raw.word8(),
)
}
fn fmt_thumb_add_sub(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let operand = if self.is_immediate_operand() {
let operand = if self.raw.is_immediate_operand() {
format!("#{:x}", self.raw.bit_range(6..9))
} else {
String::from(reg_string(self.rn()))
String::from(reg_string(self.raw.rn()))
};
write!(
f,
"{op}\t{Rd}, {Rs}, {operand}",
op = if self.is_subtract() { "sub" } else { "add" },
Rd = reg_string(self.rd()),
Rs = reg_string(self.rs()),
op = if self.raw.is_subtract() { "sub" } else { "add" },
Rd = reg_string(self.raw & 0b111),
Rs = reg_string(self.raw.rs()),
operand = operand
)
}
fn fmt_thumb_add_sp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "add\tsp, #{imm:x}", imm = self.sword7())
write!(f, "add\tsp, #{imm:x}", imm = self.raw.sword7())
}
fn fmt_register_list(&self, f: &mut fmt::Formatter<'_>, rlist: u8) -> fmt::Result {
@ -207,11 +199,11 @@ impl ThumbInstruction {
}
fn fmt_thumb_push_pop(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\t{{", if self.is_load() { "pop" } else { "push" })?;
let rlist = self.register_list();
write!(f, "{}\t{{", if self.raw.is_load() { "pop" } else { "push" })?;
let rlist = self.raw.register_list();
self.fmt_register_list(f, rlist)?;
if self.flag(ThumbInstruction::FLAG_R) {
let r = if self.is_load() { "pc" } else { "lr" };
if self.raw.flag(consts::flags::FLAG_R) {
let r = if self.raw.is_load() { "pc" } else { "lr" };
if rlist != 0 {
write!(f, ", {}", r)?;
} else {
@ -225,10 +217,10 @@ impl ThumbInstruction {
write!(
f,
"{op}\t{Rb}!, {{",
op = if self.is_load() { "ldm" } else { "stm" },
Rb = reg_string(self.rb()),
op = if self.raw.is_load() { "ldm" } else { "stm" },
Rb = reg_string(self.raw.rb()),
)?;
self.fmt_register_list(f, self.register_list())?;
self.fmt_register_list(f, self.raw.register_list())?;
write!(f, "}}")
}
@ -236,9 +228,9 @@ impl ThumbInstruction {
write!(
f,
"b{cond}\t{addr:#x}",
cond = self.cond(),
cond = self.raw.cond(),
addr = {
let offset = self.bcond_offset();
let offset = self.raw.bcond_offset();
(self.pc as i32 + 4).wrapping_add(offset) as Addr
}
)
@ -253,7 +245,7 @@ impl ThumbInstruction {
f,
"b\t{addr:#x}",
addr = {
let offset = (self.offset11() << 21) >> 20;
let offset = (self.raw.offset11() << 21) >> 20;
(self.pc as i32 + 4).wrapping_add(offset) as Addr
}
)
@ -261,8 +253,8 @@ impl ThumbInstruction {
fn fmt_thumb_branch_long_with_link(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "bl\t#0x{:08x}", {
let offset11 = self.offset11();
if self.flag(ThumbInstruction::FLAG_LOW_OFFSET) {
let offset11 = self.raw.offset11();
if self.raw.flag(consts::flags::FLAG_LOW_OFFSET) {
(offset11 << 1) as i32
} else {
((offset11 << 21) >> 9) as i32

View file

@ -4,7 +4,9 @@ use crate::Bus;
use crate::bit::BitIndex;
use super::ThumbDecodeHelper;
use super::*;
fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) {
cpu.gpr[REG_SP] -= 4;
let stack_addr = cpu.gpr[REG_SP] & !3;
@ -21,10 +23,10 @@ impl Core {
pub(in super::super) fn exec_thumb_move_shifted_reg(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rd = (insn.raw & 0b111) as usize;
let rs = insn.raw.bit_range(3..6) as usize;
let rd = (insn & 0b111) as usize;
let rs = insn.bit_range(3..6) as usize;
let shift_amount = insn.offset5() as u8 as u32;
let op2 = self.barrel_shift_op(
@ -43,12 +45,8 @@ impl Core {
}
/// Format 2
pub(in super::super) fn exec_thumb_add_sub(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
) -> CpuAction {
let rd = (insn.raw & 0b111) as usize;
pub(in super::super) fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
let rd = (insn & 0b111) as usize;
let op1 = self.get_reg(insn.rs());
let op2 = if insn.is_immediate_operand() {
insn.rn() as u32
@ -75,13 +73,13 @@ impl Core {
pub(in super::super) fn exec_thumb_data_process_imm(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
use OpFormat3::*;
let op = insn.format3_op();
let rd = insn.raw.bit_range(8..11) as usize;
let rd = insn.bit_range(8..11) as usize;
let op1 = self.gpr[rd];
let op2_imm = (insn.raw & 0xff) as u32;
let op2_imm = (insn & 0xff) as u32;
let mut carry = self.cpsr.C();
let mut overflow = self.cpsr.V();
let result = match op {
@ -100,12 +98,8 @@ impl Core {
}
/// Format 4
pub(in super::super) fn exec_thumb_alu_ops(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
) -> CpuAction {
let rd = (insn.raw & 0b111) as usize;
pub(in super::super) fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
let rd = (insn & 0b111) as usize;
let rs = insn.rs();
let dst = self.get_reg(rd);
let src = self.get_reg(rs);
@ -164,16 +158,16 @@ impl Core {
pub(in super::super) fn exec_thumb_hi_reg_op_or_bx(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let op = insn.format5_op();
let rd = (insn.raw & 0b111) as usize;
let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) {
let rd = (insn & 0b111) as usize;
let dst_reg = if insn.bit(consts::flags::FLAG_H1) {
rd + 8
} else {
rd
};
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
let src_reg = if insn.bit(consts::flags::FLAG_H2) {
insn.rs() + 8
} else {
insn.rs()
@ -213,12 +207,8 @@ impl Core {
}
/// Format 6
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;
pub(in super::super) fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
let rd = insn.bit_range(8..11) as usize;
let ofs = insn.word8() as Addr;
let addr = (self.pc & !3) + ofs;
@ -238,11 +228,12 @@ impl Core {
fn do_exec_thumb_ldr_str(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
addr: Addr,
is_transferring_bytes: bool,
) -> CpuAction {
let rd = (insn.raw & 0b111) as usize;
let rd = (insn & 0b111) as usize;
if insn.is_load() {
let data = if is_transferring_bytes {
self.S_cycle8(sb, addr);
@ -276,26 +267,26 @@ impl Core {
pub(in super::super) fn exec_thumb_ldr_str_reg_offset(
&mut self,
bus: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize;
let rb = insn.bit_range(3..6) as usize;
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
self.do_exec_thumb_ldr_str(bus, insn, addr, insn.raw.bit(10))
self.do_exec_thumb_ldr_str(bus, insn, addr, insn.bit(10))
}
/// Format 8
pub(in super::super) fn exec_thumb_ldr_str_shb(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize;
let rd = (insn.raw & 0b111) as usize;
let rb = insn.bit_range(3..6) as usize;
let rd = (insn & 0b111) as usize;
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
match (
insn.flag(ThumbInstruction::FLAG_SIGN_EXTEND),
insn.flag(ThumbInstruction::FLAG_HALFWORD),
insn.bit(consts::flags::FLAG_SIGN_EXTEND),
insn.bit(consts::flags::FLAG_HALFWORD),
) {
(false, false) =>
/* strh */
@ -337,27 +328,27 @@ impl Core {
pub(in super::super) fn exec_thumb_ldr_str_imm_offset(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize;
let rb = insn.bit_range(3..6) as usize;
let offset = if insn.raw.bit(12) {
let offset = if insn.bit(12) {
insn.offset5()
} else {
(insn.offset5() << 3) >> 1
};
let addr = self.gpr[rb].wrapping_add(offset as u32);
self.do_exec_thumb_ldr_str(sb, insn, addr, insn.raw.bit(12))
self.do_exec_thumb_ldr_str(sb, insn, addr, insn.bit(12))
}
/// Format 10
pub(in super::super) fn exec_thumb_ldr_str_halfword(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize;
let rd = (insn.raw & 0b111) as usize;
let rb = insn.bit_range(3..6) as usize;
let rd = (insn & 0b111) as usize;
let base = self.gpr[rb] as i32;
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
if insn.is_load() {
@ -378,10 +369,10 @@ impl Core {
pub(in super::super) fn exec_thumb_ldr_str_sp(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let addr = self.gpr[REG_SP] + (insn.word8() as Addr);
let rd = insn.raw.bit_range(8..11) as usize;
let rd = insn.bit_range(8..11) as usize;
if insn.is_load() {
let data = self.ldr_word(addr, sb);
self.S_cycle16(sb, addr);
@ -400,13 +391,13 @@ impl Core {
pub(in super::super) fn exec_thumb_load_address(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let rd = insn.raw.bit_range(8..11) as usize;
let result = if insn.flag(ThumbInstruction::FLAG_SP) {
let rd = insn.bit_range(8..11) as usize;
let result = if insn.bit(consts::flags::FLAG_SP) {
self.gpr[REG_SP] + (insn.word8() as Addr)
} else {
(insn.pc & !0b10) + 4 + (insn.word8() as Addr)
(self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr)
};
self.gpr[rd] = result;
self.S_cycle16(sb, self.pc + 2);
@ -415,11 +406,7 @@ impl Core {
}
/// Format 13
pub(in super::super) 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: u16) -> CpuAction {
let op1 = self.gpr[REG_SP] as i32;
let op2 = insn.sword7();
@ -433,13 +420,13 @@ impl Core {
pub(in super::super) fn exec_thumb_push_pop(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let mut result = CpuAction::AdvancePC;
// (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
let is_pop = insn.is_load();
let pc_lr_flag = insn.flag(ThumbInstruction::FLAG_R);
let pc_lr_flag = insn.bit(consts::flags::FLAG_R);
let rlist = insn.register_list();
self.N_cycle16(sb, self.pc);
let mut first = true;
@ -482,16 +469,12 @@ impl Core {
}
/// Format 15
pub(in super::super) 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: u16) -> CpuAction {
let mut result = CpuAction::AdvancePC;
// (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
let rb = insn.raw.bit_range(8..11) as usize;
let rb = insn.bit_range(8..11) as usize;
let base_reg = rb;
let is_load = insn.is_load();
@ -566,7 +549,7 @@ impl Core {
pub(in super::super) fn exec_thumb_branch_with_cond(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
if !self.check_arm_cond(insn.cond()) {
self.S_cycle16(sb, self.pc + 2);
@ -581,22 +564,14 @@ impl Core {
}
/// Format 17
pub(in super::super) 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: u16) -> CpuAction {
self.N_cycle16(sb, self.pc);
self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2);
CpuAction::FlushPipeline
}
/// Format 18
pub(in super::super) 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: u16) -> CpuAction {
let offset = ((insn.offset11() << 21) >> 20) as i32;
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
self.S_cycle16(sb, self.pc);
@ -608,10 +583,10 @@ impl Core {
pub(in super::super) fn exec_thumb_branch_long_with_link(
&mut self,
sb: &mut SysBus,
insn: &ThumbInstruction,
insn: u16,
) -> CpuAction {
let mut off = insn.offset11();
if insn.flag(ThumbInstruction::FLAG_LOW_OFFSET) {
if insn.bit(consts::flags::FLAG_LOW_OFFSET) {
self.S_cycle16(sb, self.pc);
off = off << 1;
let next_pc = (self.pc - 2) | 1;
@ -628,15 +603,17 @@ impl Core {
}
}
pub fn thumb_undefined(&mut self, _: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
pub fn thumb_undefined(&mut self, _: &mut SysBus, insn: u16) -> CpuAction {
panic!(
"executing undefind thumb instruction {:04x} at @{:08x}",
insn.raw, insn.pc
insn,
self.pc_thumb()
)
}
pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
match insn.fmt {
#[cfg(not(feature = "arm7tdmi_dispatch_table"))]
pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: u16, fmt: ThumbFormat) -> CpuAction {
match fmt {
ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn),
ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn),
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(bus, insn),

View file

@ -5,6 +5,7 @@ use crate::bit::BitIndex;
use crate::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive;
#[cfg(feature = "debugger")]
pub mod display;
pub mod exec;
@ -53,26 +54,10 @@ pub enum ThumbFormat {
Undefined,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ThumbInstruction {
pub fmt: ThumbFormat,
pub raw: u16,
pub pc: Addr,
}
impl ThumbInstruction {
pub fn new(raw: u16, pc: Addr, fmt: ThumbFormat) -> ThumbInstruction {
ThumbInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ThumbInstruction {
type IntType = u16;
fn decode(raw: u16, addr: Addr) -> Self {
use self::ThumbFormat::*;
let fmt = if raw & 0xf800 == 0x1800 {
impl From<u16> for ThumbFormat {
fn from(raw: u16) -> ThumbFormat {
use ThumbFormat::*;
if raw & 0xf800 == 0x1800 {
AddSub
} else if raw & 0xe000 == 0x0000 {
MoveShiftedReg
@ -112,8 +97,28 @@ impl InstructionDecoder for ThumbInstruction {
BranchLongWithLink
} else {
Undefined
};
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ThumbInstruction {
pub fmt: ThumbFormat,
pub raw: u16,
pub pc: Addr,
}
impl ThumbInstruction {
pub fn new(raw: u16, pc: Addr, fmt: ThumbFormat) -> ThumbInstruction {
ThumbInstruction { fmt, raw, pc }
}
}
impl InstructionDecoder for ThumbInstruction {
type IntType = u16;
fn decode(raw: u16, addr: Addr) -> Self {
let fmt = ThumbFormat::from(raw);
ThumbInstruction::new(raw, addr, fmt)
}
@ -203,119 +208,176 @@ impl From<OpFormat5> for AluOpCode {
}
}
impl ThumbInstruction {
const FLAG_H1: usize = 7;
const FLAG_H2: usize = 6;
const FLAG_R: usize = 8;
const FLAG_S: usize = 7;
const FLAG_LOW_OFFSET: usize = 11;
const FLAG_SP: usize = 11;
const FLAG_SIGN_EXTEND: usize = 10;
const FLAG_HALFWORD: usize = 11;
pub fn rd(&self) -> usize {
match self.fmt {
ThumbFormat::DataProcessImm
| ThumbFormat::LdrPc
| ThumbFormat::LdrStrSp
| ThumbFormat::LoadAddress => self.raw.bit_range(8..11) as usize,
_ => (self.raw & 0b111) as usize,
}
}
pub fn rs(&self) -> usize {
self.raw.bit_range(3..6) as usize
}
pub fn rb(&self) -> usize {
match self.fmt {
ThumbFormat::LdmStm => self.raw.bit_range(8..11) as usize,
_ => self.raw.bit_range(3..6) as usize,
}
}
pub fn ro(&self) -> usize {
self.raw.bit_range(6..9) as usize
}
pub fn rn(&self) -> usize {
self.raw.bit_range(6..9) as usize
}
pub fn format1_op(&self) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
}
pub fn format3_op(&self) -> OpFormat3 {
OpFormat3::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
}
pub fn format5_op(&self) -> OpFormat5 {
OpFormat5::from_u8(self.raw.bit_range(8..10) as u8).unwrap()
}
pub fn format4_alu_op(&self) -> ThumbAluOps {
ThumbAluOps::from_u16(self.raw.bit_range(6..10)).unwrap()
}
pub fn offset5(&self) -> u8 {
self.raw.bit_range(6..11) as u8
}
pub fn bcond_offset(&self) -> i32 {
((((self.raw & 0xff) as u32) << 24) as i32) >> 23
}
pub fn offset11(&self) -> i32 {
(self.raw & 0x7FF) as i32
}
pub fn word8(&self) -> u16 {
(self.raw & 0xff) << 2
}
pub fn is_transferring_bytes(&self) -> bool {
match self.fmt {
ThumbFormat::LdrStrRegOffset => self.raw.bit(10),
ThumbFormat::LdrStrImmOffset => self.raw.bit(12),
_ => unreachable!(),
}
}
pub fn is_load(&self) -> bool {
self.raw.bit(11)
}
pub fn is_subtract(&self) -> bool {
self.raw.bit(9)
}
pub fn is_immediate_operand(&self) -> bool {
self.raw.bit(10)
}
pub fn cond(&self) -> ArmCond {
ArmCond::from_u8(self.raw.bit_range(8..12) as u8).expect("bad condition")
}
pub fn flag(&self, bit: usize) -> bool {
self.raw.bit(bit)
}
pub fn register_list(&self) -> u8 {
(self.raw & 0xff) as u8
}
pub fn sword7(&self) -> i32 {
let imm7 = self.raw & 0x7f;
if self.flag(ThumbInstruction::FLAG_S) {
-((imm7 << 2) as i32)
} else {
(imm7 << 2) as i32
}
pub(super) mod consts {
pub(super) mod flags {
pub const FLAG_H1: usize = 7;
pub const FLAG_H2: usize = 6;
pub const FLAG_R: usize = 8;
pub const FLAG_S: usize = 7;
pub const FLAG_LOW_OFFSET: usize = 11;
pub const FLAG_SP: usize = 11;
pub const FLAG_SIGN_EXTEND: usize = 10;
pub const FLAG_HALFWORD: usize = 11;
}
}
/// A trait which provides methods to extract thumb instruction fields
pub trait ThumbDecodeHelper {
// Consts
// Methods
fn rs(&self) -> usize;
fn rb(&self) -> usize;
fn ro(&self) -> usize;
fn rn(&self) -> usize;
fn format1_op(&self) -> BarrelShiftOpCode;
fn format3_op(&self) -> OpFormat3;
fn format5_op(&self) -> OpFormat5;
fn format4_alu_op(&self) -> ThumbAluOps;
fn offset5(&self) -> u8;
fn bcond_offset(&self) -> i32;
fn offset11(&self) -> i32;
fn word8(&self) -> u16;
fn is_load(&self) -> bool;
fn is_subtract(&self) -> bool;
fn is_immediate_operand(&self) -> bool;
fn cond(&self) -> ArmCond;
fn flag(self, bit: usize) -> bool;
fn register_list(&self) -> u8;
fn sword7(&self) -> i32;
}
macro_rules! thumb_decode_helper_impl {
($($t:ty),*) => {$(
impl ThumbDecodeHelper for $t {
#[inline]
fn rs(&self) -> usize {
self.bit_range(3..6) as usize
}
#[inline]
/// Note: not true for LdmStm
fn rb(&self) -> usize {
self.bit_range(3..6) as usize
}
#[inline]
fn ro(&self) -> usize {
self.bit_range(6..9) as usize
}
#[inline]
fn rn(&self) -> usize {
self.bit_range(6..9) as usize
}
#[inline]
fn format1_op(&self) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(self.bit_range(11..13) as u8).unwrap()
}
#[inline]
fn format3_op(&self) -> OpFormat3 {
OpFormat3::from_u8(self.bit_range(11..13) as u8).unwrap()
}
#[inline]
fn format5_op(&self) -> OpFormat5 {
OpFormat5::from_u8(self.bit_range(8..10) as u8).unwrap()
}
#[inline]
fn format4_alu_op(&self) -> ThumbAluOps {
ThumbAluOps::from_u16(self.bit_range(6..10)).unwrap()
}
#[inline]
fn offset5(&self) -> u8 {
self.bit_range(6..11) as u8
}
#[inline]
fn bcond_offset(&self) -> i32 {
((((*self & 0xff) as u32) << 24) as i32) >> 23
}
#[inline]
fn offset11(&self) -> i32 {
(*self & 0x7FF) as i32
}
#[inline]
fn word8(&self) -> u16 {
(*self & 0xff) << 2
}
#[inline]
fn is_load(&self) -> bool {
self.bit(11)
}
#[inline]
fn is_subtract(&self) -> bool {
self.bit(9)
}
#[inline]
fn is_immediate_operand(&self) -> bool {
self.bit(10)
}
#[inline]
fn cond(&self) -> ArmCond {
ArmCond::from_u8(self.bit_range(8..12) as u8).expect("bad condition")
}
#[inline]
fn flag(self, bit: usize) -> bool {
self.bit(bit)
}
#[inline]
fn register_list(&self) -> u8 {
(*self & 0xff) as u8
}
#[inline]
fn sword7(&self) -> i32 {
let imm7 = *self & 0x7f;
if self.bit(consts::flags::FLAG_S) {
-((imm7 << 2) as i32)
} else {
(imm7 << 2) as i32
}
}
}
)*}
}
thumb_decode_helper_impl!(u16);
// #[cfg(test)]
// /// All instructions constants were generated using an ARM assembler.
// mod tests {

View file

@ -26,6 +26,9 @@ extern crate log;
#[macro_use]
extern crate hex_literal;
#[macro_use]
extern crate cfg_if;
use zip;
use std::error::Error;