[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> { fn generate_thumb_lut(file: &mut fs::File) -> Result<(), std::io::Error> {
writeln!( writeln!(
file, 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 struct ThumbInstructionInfo {{
pub handler_fn: ThumbInstructionHandler,
#[cfg(feature = \"debugger\")]
pub fmt: ThumbFormat, pub fmt: ThumbFormat,
pub handler_fn: ThumbInstructionHandler
}} }}
" "
)?; )?;
@ -250,8 +255,13 @@ pub struct ThumbInstructionInfo {{
let handler_name = thumb_format_to_handler(thumb_fmt); let handler_name = thumb_format_to_handler(thumb_fmt);
writeln!( writeln!(
file, file,
" /* {:#x} */ ThumbInstructionInfo {{ fmt: ThumbFormat::{}, handler_fn: Core::{} }},", " /* {:#x} */
i, thumb_fmt, handler_name 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> { fn generate_arm_lut(file: &mut fs::File) -> Result<(), std::io::Error> {
writeln!( writeln!(
file, 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 struct ArmInstructionInfo {{
pub handler_fn: ArmInstructionHandler,
#[cfg(feature = \"debugger\")]
pub fmt: ArmFormat, pub fmt: ArmFormat,
pub handler_fn: ArmInstructionHandler
}} }}
" "
)?; )?;
@ -280,8 +295,13 @@ pub struct ArmInstructionInfo {{
let handler_name = arm_format_to_handler(arm_fmt); let handler_name = arm_format_to_handler(arm_fmt);
writeln!( writeln!(
file, file,
" /* {:#x} */ ArmInstructionInfo {{ fmt: ArmFormat::{}, handler_fn: Core::{} }},", " /* {:#x} */
i, arm_fmt, handler_name ArmInstructionInfo {{
handler_fn: Core::{},
#[cfg(feature = \"debugger\")]
fmt: ArmFormat::{},
}} ,",
i, handler_name, arm_fmt
)?; )?;
} }
writeln!(file, "];")?; writeln!(file, "];")?;

View file

@ -1,10 +1,8 @@
use std::fmt; use std::fmt;
#[cfg(feature = "debugger")]
use crate::bit::BitIndex; use crate::bit::BitIndex;
#[cfg(feature = "debugger")] use super::{ArmDecodeHelper, ArmFormat, ArmInstruction};
use super::{ArmFormat, ArmInstruction};
use super::{AluOpCode, ArmCond, ArmHalfwordTransferType}; use super::{AluOpCode, ArmCond, ArmHalfwordTransferType};
use crate::arm7tdmi::*; use crate::arm7tdmi::*;
@ -13,6 +11,7 @@ impl fmt::Display for ArmCond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmCond::*; use ArmCond::*;
match self { match self {
Invalid => panic!("Invalid condition code"),
EQ => write!(f, "eq"), EQ => write!(f, "eq"),
NE => write!(f, "ne"), NE => write!(f, "ne"),
HS => write!(f, "cs"), HS => write!(f, "cs"),
@ -102,24 +101,27 @@ impl fmt::Display for ShiftedRegister {
} }
} }
#[cfg(feature = "debugger")]
impl ArmInstruction { impl ArmInstruction {
fn fmt_bx(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 { fn fmt_branch(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"b{link}{cond}\t{ofs:#x}", "b{link}{cond}\t{ofs:#x}",
link = if self.link_flag() { "l" } else { "" }, link = if self.raw.link_flag() { "l" } else { "" },
cond = self.cond(), cond = self.raw.cond(),
ofs = 8 + self.pc.wrapping_add(self.branch_offset() as Addr) ofs = 8 + self.pc.wrapping_add(self.raw.branch_offset() as Addr)
) )
} }
fn set_cond_mark(&self) -> &str { fn set_cond_mark(&self) -> &str {
if self.set_cond_flag() { if self.raw.set_cond_flag() {
"s" "s"
} else { } else {
"" ""
@ -127,7 +129,7 @@ impl ArmInstruction {
} }
fn fmt_operand2(&self, f: &mut fmt::Formatter<'_>) -> Result<Option<u32>, fmt::Error> { fn fmt_operand2(&self, f: &mut fmt::Formatter<'_>) -> Result<Option<u32>, fmt::Error> {
let operand2 = self.operand2(); let operand2 = self.raw.operand2();
match operand2 { match operand2 {
BarrelShifterValue::RotatedImmediate(_, _) => { BarrelShifterValue::RotatedImmediate(_, _) => {
let value = operand2.decode_rotated_immediate().unwrap(); let value = operand2.decode_rotated_immediate().unwrap();
@ -145,32 +147,35 @@ impl ArmInstruction {
fn fmt_data_processing(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt_data_processing(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AluOpCode::*; 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 { match opcode {
MOV | MVN => write!( MOV | MVN => write!(
f, f,
"{opcode}{S}{cond}\t{Rd}, ", "{opcode}{S}{cond}\t{Rd}, ",
opcode = opcode, opcode = opcode,
cond = self.cond(), cond = self.raw.cond(),
S = self.set_cond_mark(), S = self.set_cond_mark(),
Rd = reg_string(self.rd()) Rd = reg_string(rd)
), ),
CMP | CMN | TEQ | TST => write!( CMP | CMN | TEQ | TST => write!(
f, f,
"{opcode}{cond}\t{Rn}, ", "{opcode}{cond}\t{Rn}, ",
opcode = opcode, opcode = opcode,
cond = self.cond(), cond = self.raw.cond(),
Rn = reg_string(self.rn()) Rn = reg_string(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.raw.cond(),
S = self.set_cond_mark(), S = self.set_cond_mark(),
Rd = reg_string(self.rd()), Rd = reg_string(rd),
Rn = reg_string(self.rn()) Rn = reg_string(rn)
), ),
}?; }?;
@ -179,18 +184,23 @@ impl ArmInstruction {
} }
fn auto_incremenet_mark(&self) -> &str { fn auto_incremenet_mark(&self) -> &str {
if self.write_back_flag() { if self.raw.write_back_flag() {
"!" "!"
} else { } else {
"" ""
} }
} }
fn fmt_rn_offset(&self, f: &mut fmt::Formatter<'_>, offset: BarrelShifterValue) -> fmt::Result { fn fmt_rn_offset(
write!(f, "[{Rn}", Rn = reg_string(self.rn()))?; &self,
f: &mut fmt::Formatter<'_>,
offset: BarrelShifterValue,
rn: usize,
) -> fmt::Result {
write!(f, "[{Rn}", Rn = reg_string(rn))?;
let (ofs_string, comment) = match offset { let (ofs_string, comment) = match offset {
BarrelShifterValue::ImmediateValue(value) => { 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 value + self.pc + 8 // account for pipelining
} else { } else {
value value
@ -211,7 +221,7 @@ impl ArmInstruction {
_ => panic!("bad barrel shifter"), _ => panic!("bad barrel shifter"),
}; };
if self.pre_index_flag() { if self.raw.pre_index_flag() {
write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?; write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?;
} else { } else {
write!(f, "], {}", ofs_string)?; write!(f, "], {}", ofs_string)?;
@ -228,33 +238,41 @@ impl ArmInstruction {
write!( write!(
f, f,
"{mnem}{B}{T}{cond}\t{Rd}, ", "{mnem}{B}{T}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" }, mnem = if self.raw.load_flag() { "ldr" } else { "str" },
B = if self.transfer_size() == 1 { "b" } else { "" }, B = if self.raw.transfer_size() == 1 {
cond = self.cond(), "b"
T = if !self.pre_index_flag() && self.write_back_flag() { } else {
""
},
cond = self.raw.cond(),
T = if !self.raw.pre_index_flag() && self.raw.write_back_flag() {
"t" "t"
} else { } 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 { fn fmt_ldm_stm(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{", "{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{",
mnem = if self.load_flag() { "ldm" } else { "stm" }, mnem = if self.raw.load_flag() { "ldm" } else { "stm" },
inc_dec = if self.add_offset_flag() { 'i' } else { 'd' }, inc_dec = if self.raw.add_offset_flag() { 'i' } else { 'd' },
pre_post = if self.pre_index_flag() { 'b' } else { 'a' }, pre_post = if self.raw.pre_index_flag() { 'b' } else { 'a' },
cond = self.cond(), cond = self.raw.cond(),
Rn = reg_string(self.rn()), Rn = reg_string(self.raw.bit_range(16..20) as usize),
auto_inc = if self.write_back_flag() { "!" } else { "" } 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; let mut has_first = false;
for i in 0..16 { for i in 0..16 {
if register_list.bit(i) { if register_list.bit(i) {
@ -270,7 +288,7 @@ impl ArmInstruction {
write!( write!(
f, f,
"}}{}", "}}{}",
if self.psr_and_force_user_flag() { if self.raw.psr_and_force_user_flag() {
"^" "^"
} else { } else {
"" ""
@ -283,9 +301,9 @@ impl ArmInstruction {
write!( write!(
f, f,
"mrs{cond}\t{Rd}, {psr}", "mrs{cond}\t{Rd}, {psr}",
cond = self.cond(), cond = self.raw.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(self.raw.bit_range(12..16) as usize),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" } psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" }
) )
} }
@ -294,8 +312,8 @@ impl ArmInstruction {
write!( write!(
f, f,
"msr{cond}\t{psr}, ", "msr{cond}\t{psr}, ",
cond = self.cond(), cond = self.raw.cond(),
psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, psr = if self.raw.spsr_flag() { "SPSR" } else { "CPSR" },
)?; )?;
self.fmt_operand2(f).unwrap(); self.fmt_operand2(f).unwrap();
Ok(()) Ok(())
@ -305,8 +323,12 @@ impl ArmInstruction {
write!( write!(
f, f,
"msr{cond}\t{psr}, ", "msr{cond}\t{psr}, ",
cond = self.cond(), cond = self.raw.cond(),
psr = if self.spsr_flag() { "SPSR_f" } else { "CPSR_f" }, psr = if self.raw.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) {
let psr = RegPSR::new(op & 0xf000_0000); let psr = RegPSR::new(op & 0xf000_0000);
@ -323,32 +345,33 @@ impl ArmInstruction {
} }
fn fmt_mul_mla(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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!( write!(
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.raw.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(rd),
Rm = reg_string(self.rm()), Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.rs()), Rs = reg_string(self.raw.rs()),
Rn = reg_string(self.rn()), Rn = reg_string(self.raw.bit_range(12..16) as usize),
) )
} else { } else {
write!( write!(
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.raw.cond(),
Rd = reg_string(self.rd()), Rd = reg_string(rd),
Rm = reg_string(self.rm()), Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.rs()), Rs = reg_string(self.raw.rs()),
) )
} }
} }
fn sign_mark(&self) -> &str { fn sign_mark(&self) -> &str {
if self.u_flag() { if self.raw.u_flag() {
"s" "s"
} else { } else {
"u" "u"
@ -360,42 +383,60 @@ impl ArmInstruction {
f, f,
"{sign}{mnem}{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}", "{sign}{mnem}{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}",
sign = self.sign_mark(), sign = self.sign_mark(),
mnem = if self.accumulate_flag() { mnem = if self.raw.accumulate_flag() {
"mlal" "mlal"
} else { } else {
"mull" "mull"
}, },
S = self.set_cond_mark(), S = self.set_cond_mark(),
cond = self.cond(), cond = self.raw.cond(),
RdLo = reg_string(self.rd_lo()), RdLo = reg_string(self.raw.rd_lo()),
RdHi = reg_string(self.rd_hi()), RdHi = reg_string(self.raw.rd_hi()),
Rm = reg_string(self.rm()), Rm = reg_string(self.raw.rm()),
Rs = reg_string(self.rs()), Rs = reg_string(self.raw.rs()),
) )
} }
fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt_ldr_str_hs_imm_offset(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(transfer_type) = self.halfword_data_transfer_type() { let transfer_type = self.raw.halfword_data_transfer_type();
write!( write!(
f, f,
"{mnem}{type}{cond}\t{Rd}, ", "{mnem}{type}{cond}\t{Rd}, ",
mnem = if self.load_flag() { "ldr" } else { "str" }, mnem = if self.raw.load_flag() { "ldr" } else { "str" },
cond = self.cond(), cond = self.raw.cond(),
type = transfer_type, type = transfer_type,
Rd = reg_string(self.rd()), Rd = reg_string(self.raw.bit_range(12..16) as usize),
)?; )?;
self.fmt_rn_offset(f, self.ldr_str_hs_offset().unwrap()) self.fmt_rn_offset(
} else { f,
write!(f, "<undefined>") 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 { fn fmt_swi(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"swi{cond}\t#{comm:#x}", "swi{cond}\t#{comm:#x}",
cond = self.cond(), cond = self.raw.cond(),
comm = self.swi_comment() comm = self.raw.swi_comment()
) )
} }
@ -403,16 +444,19 @@ impl ArmInstruction {
write!( write!(
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.raw.transfer_size() == 1 {
cond = self.cond(), "b"
Rd = reg_string(self.rd()), } else {
Rm = reg_string(self.rm()), ""
Rn = reg_string(self.rn()), },
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 { impl fmt::Display for ArmInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArmFormat::*; use ArmFormat::*;
@ -427,8 +471,8 @@ impl fmt::Display for ArmInstruction {
MoveToFlags => self.fmt_msr_flags(f), MoveToFlags => self.fmt_msr_flags(f),
Multiply => self.fmt_mul_mla(f), Multiply => self.fmt_mul_mla(f),
MultiplyLong => self.fmt_mull_mlal(f), MultiplyLong => self.fmt_mull_mlal(f),
HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs(f), HalfwordDataTransferImmediateOffset => self.fmt_ldr_str_hs_imm_offset(f),
HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs(f), HalfwordDataTransferRegOffset => self.fmt_ldr_str_hs_reg_offset(f),
SoftwareInterrupt => self.fmt_swi(f), SoftwareInterrupt => self.fmt_swi(f),
SingleDataSwap => self.fmt_swp(f), SingleDataSwap => self.fmt_swp(f),
Undefined => write!(f, "<Undefined>"), 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::sysbus::SysBus;
use crate::Bus; use crate::Bus;
use super::ArmDecodeHelper;
use super::*; 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 { impl Core {
pub fn exec_arm(&mut self, bus: &mut SysBus, insn: &ArmInstruction) -> CpuAction { #[cfg(not(feature = "arm7tdmi_dispatch_table"))]
match insn.fmt { 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::BranchExchange => self.exec_arm_bx(bus, insn),
ArmFormat::BranchLink => self.exec_arm_b_bl(bus, insn), ArmFormat::BranchLink => self.exec_arm_b_bl(bus, insn),
ArmFormat::DataProcessing => self.exec_arm_data_processing(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!( panic!(
"executing undefined arm instruction {:08x} at @{:08x}", "executing undefined arm instruction {:08x} at @{:08x}",
insn.raw, insn.pc insn,
self.pc_arm()
) )
} }
/// Cycles 2S+1N /// 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); 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, (self.pc_arm() + (self.word_size() as u32)) & !0b1);
} }
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1; self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1;
@ -78,8 +76,8 @@ impl Core {
} }
/// Cycles 2S+1N /// Cycles 2S+1N
pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { pub fn exec_arm_bx(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.branch_exchange(sb, self.get_reg(insn.raw.bit_range(0..4) as usize)) self.branch_exchange(sb, self.get_reg(insn.bit_range(0..4) as usize))
} }
fn move_from_status_register( fn move_from_status_register(
@ -99,33 +97,29 @@ impl Core {
CpuAction::AdvancePC CpuAction::AdvancePC
} }
pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { pub fn exec_arm_mrs(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
self.move_from_status_register(sb, insn.raw.bit_range(12..16) as usize, insn.spsr_flag()) self.move_from_status_register(sb, insn.bit_range(12..16) as usize, insn.spsr_flag())
} }
#[inline(always)] #[inline(always)]
fn decode_msr_param(&mut self, insn: &ArmInstruction) -> u32 { fn decode_msr_param(&mut self, insn: u32) -> u32 {
if insn.raw.bit(25) { if insn.bit(25) {
let immediate = insn.raw & 0xff; let immediate = insn & 0xff;
let rotate = 2 * insn.raw.bit_range(8..12); let rotate = 2 * insn.bit_range(8..12);
self.ror(immediate, rotate, self.cpsr.C(), false, true) self.ror(immediate, rotate, self.cpsr.C(), false, true)
} else { } else {
self.get_reg((insn.raw & 0b1111) as usize) self.get_reg((insn & 0b1111) as usize)
} }
} }
// #[cfg(feature = "arm7tdmi_dispatch_table")] // #[cfg(feature = "arm7tdmi_dispatch_table")]
pub fn exec_arm_transfer_to_status( pub fn exec_arm_transfer_to_status(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
&mut self,
sb: &mut SysBus,
insn: &ArmInstruction,
) -> CpuAction {
let value = self.decode_msr_param(insn); let value = self.decode_msr_param(insn);
let f = insn.raw.bit(19); let f = insn.bit(19);
let s = insn.raw.bit(18); let s = insn.bit(18);
let x = insn.raw.bit(17); let x = insn.bit(17);
let c = insn.raw.bit(16); let c = insn.bit(16);
let mut mask = 0; let mut mask = 0;
if f { if f {
@ -181,51 +175,46 @@ 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.
pub fn exec_arm_data_processing( pub fn exec_arm_data_processing(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
&mut self,
sb: &mut SysBus,
insn: &ArmInstruction,
) -> CpuAction {
use AluOpCode::*; use AluOpCode::*;
let raw_insn = insn.raw;
self.S_cycle32(sb, self.pc); self.S_cycle32(sb, self.pc);
let rn = raw_insn.bit_range(16..20) as usize; let rn = insn.bit_range(16..20) as usize;
let rd = raw_insn.bit_range(12..16) as usize; let rd = insn.bit_range(12..16) as usize;
let mut op1 = if rn == REG_PC { let mut op1 = if rn == REG_PC {
insn.pc + 8 self.pc_arm() + 8
} else { } else {
self.get_reg(rn) self.get_reg(rn)
}; };
let mut s_flag = insn.set_cond_flag(); let mut s_flag = insn.set_cond_flag();
let opcode = insn.opcode(); let opcode = insn.opcode();
let op2 = if raw_insn.bit(25) { let op2 = if insn.bit(25) {
let immediate = raw_insn & 0xff; let immediate = insn & 0xff;
let rotate = 2 * raw_insn.bit_range(8..12); let rotate = 2 * insn.bit_range(8..12);
// TODO refactor out // TODO refactor out
let bs_carry_in = self.cpsr.C(); let bs_carry_in = self.cpsr.C();
self.bs_carry_out = bs_carry_in; self.bs_carry_out = bs_carry_in;
self.ror(immediate, rotate, self.cpsr.C(), false, true) self.ror(immediate, rotate, self.cpsr.C(), false, true)
} else { } 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 { if rn == REG_PC {
op1 += 4; op1 += 4;
} }
let rs = raw_insn.bit_range(8..12) as usize; let rs = insn.bit_range(8..12) as usize;
ShiftRegisterBy::ByRegister(rs) ShiftRegisterBy::ByRegister(rs)
} else { } else {
let amount = raw_insn.bit_range(7..12) as u32; let amount = insn.bit_range(7..12) as u32;
ShiftRegisterBy::ByAmount(amount) ShiftRegisterBy::ByAmount(amount)
}; };
let shifted_reg = ShiftedRegister { let shifted_reg = ShiftedRegister {
reg: reg as usize, reg: reg as usize,
bs_op: get_bs_op(raw_insn), bs_op: insn.get_bs_op(),
shift_by: shift_by, shift_by: shift_by,
added: None, added: None,
}; };
@ -277,7 +266,7 @@ impl Core {
MOV => op2, MOV => op2,
BIC => op1 & (!op2), BIC => op1 & (!op2),
MVN => !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 /// 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.
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 mut result = CpuAction::AdvancePC;
let load = insn.load_flag(); let load = insn.load_flag();
let pre_index = insn.pre_index_flag(); let pre_index = insn.pre_index_flag();
let writeback = insn.write_back_flag(); let 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 dest_reg = insn.raw.bit_range(12..16) as usize; let dest_reg = insn.bit_range(12..16) as usize;
let mut addr = self.get_reg(base_reg); let mut addr = self.get_reg(base_reg);
if base_reg == REG_PC { 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 offset = self.get_barrel_shifted_value(&insn.ldr_str_offset());
let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr; let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr;
@ -352,7 +341,7 @@ impl Core {
} }
} else { } else {
let value = if dest_reg == REG_PC { let value = if dest_reg == REG_PC {
insn.pc + 12 self.pc_arm() + 12
} else { } else {
self.get_reg(dest_reg) self.get_reg(dest_reg)
}; };
@ -381,12 +370,12 @@ impl Core {
result 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( self.ldr_str_hs(
sb, sb,
insn, insn,
BarrelShifterValue::ShiftedRegister(ShiftedRegister { BarrelShifterValue::ShiftedRegister(ShiftedRegister {
reg: (insn.raw & 0xf) as usize, reg: (insn & 0xf) as usize,
shift_by: ShiftRegisterBy::ByAmount(0), shift_by: ShiftRegisterBy::ByAmount(0),
bs_op: BarrelShiftOpCode::LSL, bs_op: BarrelShiftOpCode::LSL,
added: Some(insn.add_offset_flag()), 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 { pub fn exec_arm_ldr_str_hs_imm(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let offset8 = (insn.raw.bit_range(8..12) << 4) + insn.raw.bit_range(0..4); let offset8 = (insn.bit_range(8..12) << 4) + insn.bit_range(0..4);
let offset8 = if insn.add_offset_flag() { let offset8 = if insn.add_offset_flag() {
offset8 offset8
} else { } else {
@ -408,7 +397,7 @@ impl Core {
pub fn ldr_str_hs( pub fn ldr_str_hs(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ArmInstruction, insn: u32,
offset: BarrelShifterValue, offset: BarrelShifterValue,
) -> CpuAction { ) -> CpuAction {
let mut result = CpuAction::AdvancePC; let mut result = CpuAction::AdvancePC;
@ -416,11 +405,11 @@ impl Core {
let load = insn.load_flag(); let load = insn.load_flag();
let pre_index = insn.pre_index_flag(); let pre_index = insn.pre_index_flag();
let writeback = insn.write_back_flag(); let 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 dest_reg = insn.raw.bit_range(12..16) as usize; let dest_reg = insn.bit_range(12..16) as usize;
let mut addr = self.get_reg(base_reg); let mut addr = self.get_reg(base_reg);
if base_reg == REG_PC { if base_reg == REG_PC {
addr = insn.pc + 8; // prefetching addr = self.pc_arm() + 8; // prefetching
} }
let offset = self.get_barrel_shifted_value(&offset); let offset = self.get_barrel_shifted_value(&offset);
@ -440,7 +429,7 @@ impl Core {
if load { if load {
self.S_cycle32(sb, self.pc); 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 => { ArmHalfwordTransferType::SignedByte => {
self.N_cycle8(sb, addr); self.N_cycle8(sb, addr);
sb.read_8(addr) as u8 as i8 as u32 sb.read_8(addr) as u8 as i8 as u32
@ -466,12 +455,12 @@ impl Core {
} }
} else { } else {
let value = if dest_reg == REG_PC { let value = if dest_reg == REG_PC {
insn.pc + 12 self.pc_arm() + 12
} else { } else {
self.get_reg(dest_reg) self.get_reg(dest_reg)
}; };
match insn.halfword_data_transfer_type().unwrap() { match insn.halfword_data_transfer_type() {
ArmHalfwordTransferType::UnsignedHalfwords => { ArmHalfwordTransferType::UnsignedHalfwords => {
self.N_cycle32(sb, addr); self.N_cycle32(sb, addr);
self.write_16(addr, value as u16, sb); self.write_16(addr, value as u16, sb);
@ -492,15 +481,15 @@ impl Core {
result 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 result = CpuAction::AdvancePC;
let mut full = insn.pre_index_flag(); let mut full = insn.pre_index_flag();
let ascending = insn.add_offset_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 is_load = insn.load_flag();
let mut writeback = insn.write_back_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 mut base_addr = self.get_reg(base_reg);
let rlist = insn.register_list(); let rlist = insn.register_list();
@ -583,7 +572,7 @@ impl Core {
if rlist.bit(r) { if rlist.bit(r) {
let val = if r != base_reg { let val = if r != base_reg {
if r == REG_PC { if r == REG_PC {
insn.pc + 12 self.pc_arm() + 12
} else { } else {
self.get_reg(r) self.get_reg(r)
} }
@ -653,9 +642,9 @@ impl Core {
result result
} }
pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { pub fn exec_arm_mul_mla(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let rd = insn.raw.bit_range(16..20) as usize; let rd = insn.bit_range(16..20) as usize;
let rn = insn.raw.bit_range(12..16) as usize; let rn = insn.bit_range(12..16) as usize;
let rs = insn.rs(); let rs = insn.rs();
let rm = insn.rm(); let rm = insn.rm();
@ -691,7 +680,7 @@ impl Core {
CpuAction::AdvancePC 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_hi = insn.rd_hi();
let rd_lo = insn.rd_lo(); let rd_lo = insn.rd_lo();
let rs = insn.rs(); let rs = insn.rs();
@ -734,9 +723,9 @@ impl Core {
CpuAction::AdvancePC CpuAction::AdvancePC
} }
pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: &ArmInstruction) -> CpuAction { pub fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: u32) -> CpuAction {
let base_addr = self.get_reg(insn.raw.bit_range(16..20) as usize); let base_addr = self.get_reg(insn.bit_range(16..20) as usize);
let rd = insn.raw.bit_range(12..16) as usize; let rd = insn.bit_range(12..16) as usize;
if insn.transfer_size() == 1 { if insn.transfer_size() == 1 {
let t = sb.read_8(base_addr); let t = sb.read_8(base_addr);
self.N_cycle8(sb, base_addr); self.N_cycle8(sb, base_addr);
@ -756,7 +745,7 @@ impl Core {
CpuAction::AdvancePC 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()); self.software_interrupt(sb, self.pc - 4, insn.swi_comment());
CpuAction::FlushPipeline CpuAction::FlushPipeline
} }

View file

@ -1,3 +1,4 @@
#[cfg(feature = "debugger")]
pub mod display; pub mod display;
pub mod exec; pub mod exec;
@ -21,7 +22,6 @@ pub enum ArmDecodeErrorKind {
InvalidHSBits(u32), InvalidHSBits(u32),
IoError(io::ErrorKind), IoError(io::ErrorKind),
} }
use ArmDecodeErrorKind::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ArmDecodeError { pub struct ArmDecodeError {
@ -30,6 +30,7 @@ pub struct ArmDecodeError {
pub addr: Addr, pub addr: Addr,
} }
#[allow(dead_code)]
impl ArmDecodeError { impl ArmDecodeError {
fn new(kind: ArmDecodeErrorKind, insn: u32, addr: Addr) -> ArmDecodeError { fn new(kind: ArmDecodeErrorKind, insn: u32, addr: Addr) -> ArmDecodeError {
ArmDecodeError { ArmDecodeError {
@ -57,6 +58,7 @@ pub enum ArmCond {
GT = 0b1100, GT = 0b1100,
LE = 0b1101, LE = 0b1101,
AL = 0b1110, AL = 0b1110,
Invalid = 0b1111,
} }
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
@ -82,33 +84,10 @@ pub enum ArmFormat {
Undefined, Undefined,
} }
#[derive(Debug, PartialEq, Primitive)] impl From<u32> for ArmFormat {
pub enum ArmHalfwordTransferType { fn from(raw: u32) -> ArmFormat {
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 {
use ArmFormat::*; use ArmFormat::*;
if (0x0fff_fff0 & raw) == 0x012f_ff10 {
let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 {
BranchExchange BranchExchange
} else if (0x0e00_0000 & raw) == 0x0a00_0000 { } else if (0x0e00_0000 & raw) == 0x0a00_0000 {
BranchLink BranchLink
@ -140,7 +119,35 @@ impl InstructionDecoder for ArmInstruction {
DataProcessing DataProcessing
} else { } else {
Undefined 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 { ArmInstruction {
fmt: fmt, fmt: fmt,
@ -160,209 +167,266 @@ impl InstructionDecoder for ArmInstruction {
} }
} }
impl ArmInstruction { pub trait ArmDecodeHelper {
fn make_decode_error(&self, kind: ArmDecodeErrorKind) -> ArmDecodeError { fn cond(&self) -> ArmCond;
ArmDecodeError {
kind: kind,
insn: self.raw,
addr: self.pc,
}
}
pub fn cond(&self) -> ArmCond { fn rm(&self) -> usize;
ArmCond::from_u32(self.raw.bit_range(28..32)).unwrap()
}
pub fn rn(&self) -> usize { fn rs(&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,
}
}
pub fn rd(&self) -> usize { fn rd_lo(&self) -> usize;
match self.fmt {
ArmFormat::Multiply => self.raw.bit_range(16..20) as usize,
_ => self.raw.bit_range(12..16) as usize,
}
}
pub fn rm(&self) -> usize { fn rd_hi(&self) -> usize;
self.raw.bit_range(0..4) as usize
}
pub fn rs(&self) -> usize { fn opcode(&self) -> AluOpCode;
self.raw.bit_range(8..12) as usize
}
pub fn rd_lo(&self) -> usize { fn branch_offset(&self) -> i32;
self.raw.bit_range(12..16) as usize
}
pub fn rd_hi(&self) -> usize { fn load_flag(&self) -> bool;
self.raw.bit_range(16..20) as usize
}
pub fn opcode(&self) -> AluOpCode { fn set_cond_flag(&self) -> bool;
use std::hint::unreachable_unchecked;
unsafe { fn write_back_flag(&self) -> bool;
if let Some(opc) = AluOpCode::from_u16(self.raw.bit_range(21..25) as u16) {
opc
} else {
unreachable_unchecked()
}
}
}
pub fn branch_offset(&self) -> i32 { fn accumulate_flag(&self) -> bool;
((self.raw.bit_range(0..24) << 8) as i32) >> 6
}
pub fn load_flag(&self) -> bool { fn u_flag(&self) -> bool;
self.raw.bit(20)
}
pub fn set_cond_flag(&self) -> bool { fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType;
self.raw.bit(20)
}
pub fn write_back_flag(&self) -> bool { fn transfer_size(&self) -> usize;
self.raw.bit(21)
}
pub fn accumulate_flag(&self) -> bool { fn psr_and_force_user_flag(&self) -> bool;
self.raw.bit(21)
}
pub fn u_flag(&self) -> bool { fn spsr_flag(&self) -> bool;
self.raw.bit(22)
}
pub fn halfword_data_transfer_type(&self) -> Result<ArmHalfwordTransferType, ArmDecodeError> { fn add_offset_flag(&self) -> bool;
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)),
}
}
pub fn transfer_size(&self) -> usize { fn pre_index_flag(&self) -> bool;
if self.raw.bit(22) {
1
} else {
4
}
}
pub fn psr_and_force_user_flag(&self) -> bool { fn link_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)
}
/// gets offset used by ldr/str instructions /// gets offset used by ldr/str instructions
pub fn ldr_str_offset(&self) -> BarrelShifterValue { 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)
}
}
#[inline(always)] fn get_bs_op(&self) -> BarrelShiftOpCode;
fn get_bs_op(&self, shift_field: u32) -> BarrelShiftOpCode {
BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap()
}
#[inline(always)] fn get_shift_reg_by(&self) -> ShiftRegisterBy;
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)
}
}
pub fn ldr_str_hs_offset(&self) -> Result<BarrelShifterValue, ArmDecodeError> { fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue;
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)),
}
}
pub fn operand2(&self) -> BarrelShifterValue { fn ldr_str_hs_reg_offset(&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)
}
}
pub fn register_list(&self) -> u16 { fn operand2(&self) -> BarrelShifterValue;
(self.raw & 0xffff) as u16
}
pub fn swi_comment(&self) -> u32 { fn register_list(&self) -> u16;
self.raw.bit_range(0..24)
} 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)] // #[cfg(test)]
// /// All instructions constants were generated using an ARM assembler. // /// All instructions constants were generated using an ARM assembler.
// mod tests { // 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}; 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; pub use super::exception::Exception;
use super::CpuAction; use super::CpuAction;
#[cfg(feature = "debugger")] use super::{psr::RegPSR, Addr, CpuMode, CpuState, arm::ArmCond};
use super::DecodedInstruction;
use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState}; 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::bus::Bus;
use crate::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus}; 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 { pub fn get_reg_user(&mut self, r: usize) -> u32 {
match r { match r {
0..=7 => self.gpr[r], 0..=7 => self.gpr[r],
@ -291,6 +309,10 @@ impl Core {
pub(super) fn check_arm_cond(&self, cond: ArmCond) -> bool { pub(super) fn check_arm_cond(&self, cond: ArmCond) -> bool {
use ArmCond::*; use ArmCond::*;
match cond { match cond {
Invalid => {
// TODO - we would normally want to panic here
false
}
EQ => self.cpsr.Z(), EQ => self.cpsr.Z(),
NE => !self.cpsr.Z(), NE => !self.cpsr.Z(),
HS => self.cpsr.C(), HS => self.cpsr.C(),
@ -319,43 +341,56 @@ impl Core {
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction { fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction {
let hash = (((insn >> 16) & 0xff0) | ((insn >> 4) & 0x00f)) as usize; let hash = (((insn >> 16) & 0xff0) | ((insn >> 4) & 0x00f)) as usize;
let arm_info = &ARM_LUT[hash]; let arm_info = &ARM_LUT[hash];
let arm_insn = ArmInstruction::new(insn, self.pc.wrapping_sub(8), arm_info.fmt);
#[cfg(feature = "debugger")] #[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")] #[cfg(feature = "arm7tdmi_dispatch_table")]
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction { fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction {
let thumb_info = &THUMB_LUT[(insn >> 6) as usize]; 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")] #[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"))] #[cfg(not(feature = "arm7tdmi_dispatch_table"))]
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuAction { 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")] #[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"))] #[cfg(not(feature = "arm7tdmi_dispatch_table"))]
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuAction { 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")] #[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)] #[inline(always)]
@ -399,8 +434,8 @@ 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;
let cond = let cond = ArmCond::from_u8(insn.bit_range(28..32) as u8)
ArmCond::from_u32(insn.bit_range(28..32)).expect("invalid arm condition"); .unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() });
if cond != ArmCond::AL { if cond != ArmCond::AL {
if !self.check_arm_cond(cond) { if !self.check_arm_cond(cond) {
self.S_cycle32(bus, self.pc); self.S_cycle32(bus, self.pc);

View file

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

View file

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

View file

@ -4,7 +4,9 @@ use crate::Bus;
use crate::bit::BitIndex; use crate::bit::BitIndex;
use super::ThumbDecodeHelper;
use super::*; use super::*;
fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) { fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) {
cpu.gpr[REG_SP] -= 4; cpu.gpr[REG_SP] -= 4;
let stack_addr = cpu.gpr[REG_SP] & !3; let stack_addr = cpu.gpr[REG_SP] & !3;
@ -21,10 +23,10 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
let rd = (insn.raw & 0b111) as usize; let rd = (insn & 0b111) as usize;
let rs = insn.raw.bit_range(3..6) as usize; let rs = insn.bit_range(3..6) as usize;
let shift_amount = insn.offset5() as u8 as u32; let shift_amount = insn.offset5() as u8 as u32;
let op2 = self.barrel_shift_op( let op2 = self.barrel_shift_op(
@ -43,12 +45,8 @@ impl Core {
} }
/// Format 2 /// Format 2
pub(in super::super) fn exec_thumb_add_sub( pub(in super::super) fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&mut self, let rd = (insn & 0b111) as usize;
sb: &mut SysBus,
insn: &ThumbInstruction,
) -> CpuAction {
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() {
insn.rn() as u32 insn.rn() as u32
@ -75,13 +73,13 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
use OpFormat3::*; use OpFormat3::*;
let op = insn.format3_op(); 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 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 carry = self.cpsr.C();
let mut overflow = self.cpsr.V(); let mut overflow = self.cpsr.V();
let result = match op { let result = match op {
@ -100,12 +98,8 @@ impl Core {
} }
/// Format 4 /// Format 4
pub(in super::super) fn exec_thumb_alu_ops( pub(in super::super) fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&mut self, let rd = (insn & 0b111) as usize;
sb: &mut SysBus,
insn: &ThumbInstruction,
) -> CpuAction {
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);
let src = self.get_reg(rs); let src = self.get_reg(rs);
@ -164,16 +158,16 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
let op = insn.format5_op(); let op = insn.format5_op();
let rd = (insn.raw & 0b111) as usize; let rd = (insn & 0b111) as usize;
let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) { let dst_reg = if insn.bit(consts::flags::FLAG_H1) {
rd + 8 rd + 8
} else { } else {
rd rd
}; };
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) { let src_reg = if insn.bit(consts::flags::FLAG_H2) {
insn.rs() + 8 insn.rs() + 8
} else { } else {
insn.rs() insn.rs()
@ -213,12 +207,8 @@ impl Core {
} }
/// Format 6 /// Format 6
pub(in super::super) fn exec_thumb_ldr_pc( pub(in super::super) fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&mut self, let rd = insn.bit_range(8..11) as usize;
sb: &mut SysBus,
insn: &ThumbInstruction,
) -> CpuAction {
let rd = insn.raw.bit_range(8..11) as usize;
let ofs = insn.word8() as Addr; let ofs = insn.word8() as Addr;
let addr = (self.pc & !3) + ofs; let addr = (self.pc & !3) + ofs;
@ -238,11 +228,12 @@ impl Core {
fn do_exec_thumb_ldr_str( fn do_exec_thumb_ldr_str(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: u16,
addr: Addr, addr: Addr,
is_transferring_bytes: bool, is_transferring_bytes: bool,
) -> CpuAction { ) -> CpuAction {
let rd = (insn.raw & 0b111) as usize; let rd = (insn & 0b111) as usize;
if insn.is_load() { if insn.is_load() {
let data = if is_transferring_bytes { let data = if is_transferring_bytes {
self.S_cycle8(sb, addr); self.S_cycle8(sb, addr);
@ -276,26 +267,26 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> 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()]); 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 /// Format 8
pub(in super::super) fn exec_thumb_ldr_str_shb( pub(in super::super) fn exec_thumb_ldr_str_shb(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: u16,
) -> CpuAction { ) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize; let rb = insn.bit_range(3..6) as usize;
let rd = (insn.raw & 0b111) as usize; let rd = (insn & 0b111) as usize;
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]); let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
match ( match (
insn.flag(ThumbInstruction::FLAG_SIGN_EXTEND), insn.bit(consts::flags::FLAG_SIGN_EXTEND),
insn.flag(ThumbInstruction::FLAG_HALFWORD), insn.bit(consts::flags::FLAG_HALFWORD),
) { ) {
(false, false) => (false, false) =>
/* strh */ /* strh */
@ -337,27 +328,27 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> 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() insn.offset5()
} else { } else {
(insn.offset5() << 3) >> 1 (insn.offset5() << 3) >> 1
}; };
let addr = self.gpr[rb].wrapping_add(offset as u32); 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 /// Format 10
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
let rb = insn.raw.bit_range(3..6) as usize; let rb = insn.bit_range(3..6) as usize;
let rd = (insn.raw & 0b111) as usize; let rd = (insn & 0b111) as usize;
let base = self.gpr[rb] as i32; let base = self.gpr[rb] as i32;
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr; let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
if insn.is_load() { if insn.is_load() {
@ -378,10 +369,10 @@ impl Core {
pub(in super::super) fn exec_thumb_ldr_str_sp( pub(in super::super) fn exec_thumb_ldr_str_sp(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: u16,
) -> CpuAction { ) -> 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.bit_range(8..11) as usize;
if insn.is_load() { if insn.is_load() {
let data = self.ldr_word(addr, sb); let data = self.ldr_word(addr, sb);
self.S_cycle16(sb, addr); self.S_cycle16(sb, addr);
@ -400,13 +391,13 @@ impl Core {
pub(in super::super) fn exec_thumb_load_address( pub(in super::super) fn exec_thumb_load_address(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: u16,
) -> CpuAction { ) -> CpuAction {
let rd = insn.raw.bit_range(8..11) as usize; let rd = insn.bit_range(8..11) as usize;
let result = if insn.flag(ThumbInstruction::FLAG_SP) { let result = if insn.bit(consts::flags::FLAG_SP) {
self.gpr[REG_SP] + (insn.word8() as Addr) self.gpr[REG_SP] + (insn.word8() as Addr)
} else { } else {
(insn.pc & !0b10) + 4 + (insn.word8() as Addr) (self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr)
}; };
self.gpr[rd] = result; self.gpr[rd] = result;
self.S_cycle16(sb, self.pc + 2); self.S_cycle16(sb, self.pc + 2);
@ -415,11 +406,7 @@ impl Core {
} }
/// Format 13 /// Format 13
pub(in super::super) fn exec_thumb_add_sp( pub(in super::super) fn exec_thumb_add_sp(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&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();
@ -433,13 +420,13 @@ impl Core {
pub(in super::super) fn exec_thumb_push_pop( pub(in super::super) fn exec_thumb_push_pop(
&mut self, &mut self,
sb: &mut SysBus, sb: &mut SysBus,
insn: &ThumbInstruction, insn: u16,
) -> CpuAction { ) -> 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).
let is_pop = insn.is_load(); 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(); let rlist = insn.register_list();
self.N_cycle16(sb, self.pc); self.N_cycle16(sb, self.pc);
let mut first = true; let mut first = true;
@ -482,16 +469,12 @@ impl Core {
} }
/// Format 15 /// Format 15
pub(in super::super) fn exec_thumb_ldm_stm( pub(in super::super) fn exec_thumb_ldm_stm(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&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).
let rb = insn.raw.bit_range(8..11) as usize; let rb = insn.bit_range(8..11) as usize;
let base_reg = rb; let base_reg = rb;
let is_load = insn.is_load(); let is_load = insn.is_load();
@ -566,7 +549,7 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
if !self.check_arm_cond(insn.cond()) { if !self.check_arm_cond(insn.cond()) {
self.S_cycle16(sb, self.pc + 2); self.S_cycle16(sb, self.pc + 2);
@ -581,22 +564,14 @@ impl Core {
} }
/// Format 17 /// Format 17
pub(in super::super) fn exec_thumb_swi( pub(in super::super) fn exec_thumb_swi(&mut self, sb: &mut SysBus, _insn: u16) -> CpuAction {
&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
pub(in super::super) fn exec_thumb_branch( pub(in super::super) fn exec_thumb_branch(&mut self, sb: &mut SysBus, insn: u16) -> CpuAction {
&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);
@ -608,10 +583,10 @@ impl Core {
pub(in super::super) 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: u16,
) -> CpuAction { ) -> CpuAction {
let mut off = insn.offset11(); 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); self.S_cycle16(sb, self.pc);
off = off << 1; off = off << 1;
let next_pc = (self.pc - 2) | 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!( panic!(
"executing undefind thumb instruction {:04x} at @{:08x}", "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 { #[cfg(not(feature = "arm7tdmi_dispatch_table"))]
match insn.fmt { 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::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn),
ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn), ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn),
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(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::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive; use crate::num::FromPrimitive;
#[cfg(feature = "debugger")]
pub mod display; pub mod display;
pub mod exec; pub mod exec;
@ -53,26 +54,10 @@ pub enum ThumbFormat {
Undefined, Undefined,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] impl From<u16> for ThumbFormat {
pub struct ThumbInstruction { fn from(raw: u16) -> ThumbFormat {
pub fmt: ThumbFormat, use ThumbFormat::*;
pub raw: u16, if raw & 0xf800 == 0x1800 {
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 {
AddSub AddSub
} else if raw & 0xe000 == 0x0000 { } else if raw & 0xe000 == 0x0000 {
MoveShiftedReg MoveShiftedReg
@ -112,8 +97,28 @@ impl InstructionDecoder for ThumbInstruction {
BranchLongWithLink BranchLongWithLink
} else { } else {
Undefined 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) ThumbInstruction::new(raw, addr, fmt)
} }
@ -203,119 +208,176 @@ impl From<OpFormat5> for AluOpCode {
} }
} }
impl ThumbInstruction { pub(super) mod consts {
const FLAG_H1: usize = 7; pub(super) mod flags {
const FLAG_H2: usize = 6; pub const FLAG_H1: usize = 7;
const FLAG_R: usize = 8; pub const FLAG_H2: usize = 6;
const FLAG_S: usize = 7; pub const FLAG_R: usize = 8;
const FLAG_LOW_OFFSET: usize = 11; pub const FLAG_S: usize = 7;
const FLAG_SP: usize = 11; pub const FLAG_LOW_OFFSET: usize = 11;
const FLAG_SIGN_EXTEND: usize = 10; pub const FLAG_SP: usize = 11;
const FLAG_HALFWORD: usize = 11; pub const FLAG_SIGN_EXTEND: usize = 10;
pub 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
}
} }
} }
/// 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)] // #[cfg(test)]
// /// All instructions constants were generated using an ARM assembler. // /// All instructions constants were generated using an ARM assembler.
// mod tests { // mod tests {

View file

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