[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:
parent
bf601404fa
commit
dc7cd24e8d
|
@ -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, "];")?;
|
||||
|
|
|
@ -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>"),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Reference in a new issue