[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> {
|
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, "];")?;
|
||||||
|
|
|
@ -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>"),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,56 +167,98 @@ impl InstructionDecoder for ArmInstruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArmInstruction {
|
pub trait ArmDecodeHelper {
|
||||||
fn make_decode_error(&self, kind: ArmDecodeErrorKind) -> ArmDecodeError {
|
fn cond(&self) -> ArmCond;
|
||||||
ArmDecodeError {
|
|
||||||
kind: kind,
|
fn rm(&self) -> usize;
|
||||||
insn: self.raw,
|
|
||||||
addr: self.pc,
|
fn rs(&self) -> usize;
|
||||||
}
|
|
||||||
|
fn rd_lo(&self) -> usize;
|
||||||
|
|
||||||
|
fn rd_hi(&self) -> usize;
|
||||||
|
|
||||||
|
fn opcode(&self) -> AluOpCode;
|
||||||
|
|
||||||
|
fn branch_offset(&self) -> i32;
|
||||||
|
|
||||||
|
fn load_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn set_cond_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn write_back_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn accumulate_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn u_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType;
|
||||||
|
|
||||||
|
fn transfer_size(&self) -> usize;
|
||||||
|
|
||||||
|
fn psr_and_force_user_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn spsr_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn add_offset_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn pre_index_flag(&self) -> bool;
|
||||||
|
|
||||||
|
fn link_flag(&self) -> bool;
|
||||||
|
|
||||||
|
/// gets offset used by ldr/str instructions
|
||||||
|
fn ldr_str_offset(&self) -> BarrelShifterValue;
|
||||||
|
|
||||||
|
fn get_bs_op(&self) -> BarrelShiftOpCode;
|
||||||
|
|
||||||
|
fn get_shift_reg_by(&self) -> ShiftRegisterBy;
|
||||||
|
|
||||||
|
fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue;
|
||||||
|
|
||||||
|
fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue;
|
||||||
|
|
||||||
|
fn operand2(&self) -> BarrelShifterValue;
|
||||||
|
|
||||||
|
fn register_list(&self) -> u16;
|
||||||
|
|
||||||
|
fn swi_comment(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cond(&self) -> ArmCond {
|
macro_rules! arm_decode_helper_impl {
|
||||||
ArmCond::from_u32(self.raw.bit_range(28..32)).unwrap()
|
($($t:ty),*) => {$(
|
||||||
|
|
||||||
|
impl ArmDecodeHelper for $t {
|
||||||
|
#[inline(always)]
|
||||||
|
fn cond(&self) -> ArmCond {
|
||||||
|
ArmCond::from_u32(self.bit_range(28..32)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rn(&self) -> usize {
|
#[inline(always)]
|
||||||
match self.fmt {
|
fn rm(&self) -> usize {
|
||||||
ArmFormat::Multiply => self.raw.bit_range(12..16) as usize,
|
self.bit_range(0..4) 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 {
|
#[inline(always)]
|
||||||
match self.fmt {
|
fn rs(&self) -> usize {
|
||||||
ArmFormat::Multiply => self.raw.bit_range(16..20) as usize,
|
self.bit_range(8..12) as usize
|
||||||
_ => self.raw.bit_range(12..16) as usize,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rm(&self) -> usize {
|
#[inline(always)]
|
||||||
self.raw.bit_range(0..4) as usize
|
fn rd_lo(&self) -> usize {
|
||||||
|
self.bit_range(12..16) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rs(&self) -> usize {
|
#[inline(always)]
|
||||||
self.raw.bit_range(8..12) as usize
|
fn rd_hi(&self) -> usize {
|
||||||
|
self.bit_range(16..20) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rd_lo(&self) -> usize {
|
#[inline(always)]
|
||||||
self.raw.bit_range(12..16) as usize
|
fn opcode(&self) -> AluOpCode {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rd_hi(&self) -> usize {
|
|
||||||
self.raw.bit_range(16..20) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn opcode(&self) -> AluOpCode {
|
|
||||||
use std::hint::unreachable_unchecked;
|
use std::hint::unreachable_unchecked;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(opc) = AluOpCode::from_u16(self.raw.bit_range(21..25) as u16) {
|
if let Some(opc) = AluOpCode::from_u16(self.bit_range(21..25) as u16) {
|
||||||
opc
|
opc
|
||||||
} else {
|
} else {
|
||||||
unreachable_unchecked()
|
unreachable_unchecked()
|
||||||
|
@ -217,75 +266,86 @@ impl ArmInstruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branch_offset(&self) -> i32 {
|
#[inline(always)]
|
||||||
((self.raw.bit_range(0..24) << 8) as i32) >> 6
|
fn branch_offset(&self) -> i32 {
|
||||||
|
((self.bit_range(0..24) << 8) as i32) >> 6
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(20)
|
fn load_flag(&self) -> bool {
|
||||||
|
self.bit(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cond_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(20)
|
fn set_cond_flag(&self) -> bool {
|
||||||
|
self.bit(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_back_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(21)
|
fn write_back_flag(&self) -> bool {
|
||||||
|
self.bit(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accumulate_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(21)
|
fn accumulate_flag(&self) -> bool {
|
||||||
|
self.bit(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn u_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(22)
|
fn u_flag(&self) -> bool {
|
||||||
|
self.bit(22)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn halfword_data_transfer_type(&self) -> Result<ArmHalfwordTransferType, ArmDecodeError> {
|
#[inline(always)]
|
||||||
let bits = (self.raw & 0b1100000) >> 5;
|
fn halfword_data_transfer_type(&self) -> ArmHalfwordTransferType {
|
||||||
match ArmHalfwordTransferType::from_u32(bits) {
|
let bits = (*self & 0b1100000) >> 5;
|
||||||
Some(x) => Ok(x),
|
ArmHalfwordTransferType::from_u32(bits).unwrap()
|
||||||
None => Err(ArmDecodeError::new(InvalidHSBits(bits), self.raw, self.pc)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer_size(&self) -> usize {
|
#[inline(always)]
|
||||||
if self.raw.bit(22) {
|
fn transfer_size(&self) -> usize {
|
||||||
|
if self.bit(22) {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn psr_and_force_user_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(22)
|
fn psr_and_force_user_flag(&self) -> bool {
|
||||||
|
self.bit(22)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spsr_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(22)
|
fn spsr_flag(&self) -> bool {
|
||||||
|
self.bit(22)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_offset_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(23)
|
fn add_offset_flag(&self) -> bool {
|
||||||
|
self.bit(23)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pre_index_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(24)
|
fn pre_index_flag(&self) -> bool {
|
||||||
|
self.bit(24)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn link_flag(&self) -> bool {
|
#[inline(always)]
|
||||||
self.raw.bit(24)
|
fn link_flag(&self) -> bool {
|
||||||
|
self.bit(24)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// gets offset used by ldr/str instructions
|
/// gets offset used by ldr/str instructions
|
||||||
pub fn ldr_str_offset(&self) -> BarrelShifterValue {
|
#[inline(always)]
|
||||||
let ofs = self.raw.bit_range(0..12);
|
fn ldr_str_offset(&self) -> BarrelShifterValue {
|
||||||
if self.raw.bit(25) {
|
let ofs = self.bit_range(0..12);
|
||||||
|
if self.bit(25) {
|
||||||
let rm = ofs & 0xf;
|
let rm = ofs & 0xf;
|
||||||
BarrelShifterValue::ShiftedRegister(ShiftedRegister {
|
BarrelShifterValue::ShiftedRegister(ShiftedRegister {
|
||||||
reg: rm as usize,
|
reg: rm as usize,
|
||||||
shift_by: self.get_shift_reg_by(ofs),
|
shift_by: self.get_shift_reg_by(),
|
||||||
bs_op: self.get_bs_op(ofs),
|
bs_op: self.get_bs_op(),
|
||||||
added: Some(self.add_offset_flag()),
|
added: Some(self.add_offset_flag()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -299,69 +359,73 @@ impl ArmInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_bs_op(&self, shift_field: u32) -> BarrelShiftOpCode {
|
fn get_bs_op(&self) -> BarrelShiftOpCode {
|
||||||
BarrelShiftOpCode::from_u8(shift_field.bit_range(5..7) as u8).unwrap()
|
BarrelShiftOpCode::from_u8(self.bit_range(5..7) as u8).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_shift_reg_by(&self, shift_field: u32) -> ShiftRegisterBy {
|
fn get_shift_reg_by(&self) -> ShiftRegisterBy {
|
||||||
if shift_field.bit(4) {
|
if self.bit(4) {
|
||||||
let rs = shift_field.bit_range(8..12) as usize;
|
let rs = self.bit_range(8..12) as usize;
|
||||||
ShiftRegisterBy::ByRegister(rs)
|
ShiftRegisterBy::ByRegister(rs)
|
||||||
} else {
|
} else {
|
||||||
let amount = shift_field.bit_range(7..12) as u32;
|
let amount = self.bit_range(7..12) as u32;
|
||||||
ShiftRegisterBy::ByAmount(amount)
|
ShiftRegisterBy::ByAmount(amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ldr_str_hs_offset(&self) -> Result<BarrelShifterValue, ArmDecodeError> {
|
#[inline(always)]
|
||||||
match self.fmt {
|
fn ldr_str_hs_imm_offset(&self) -> BarrelShifterValue {
|
||||||
ArmFormat::HalfwordDataTransferImmediateOffset => {
|
let offset8 = (self.bit_range(8..12) << 4) + self.bit_range(0..4);
|
||||||
let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4);
|
|
||||||
let offset8 = if self.add_offset_flag() {
|
let offset8 = if self.add_offset_flag() {
|
||||||
offset8
|
offset8
|
||||||
} else {
|
} else {
|
||||||
(-(offset8 as i32)) as u32
|
(-(offset8 as i32)) as u32
|
||||||
};
|
};
|
||||||
Ok(BarrelShifterValue::ImmediateValue(offset8))
|
BarrelShifterValue::ImmediateValue(offset8)
|
||||||
}
|
}
|
||||||
ArmFormat::HalfwordDataTransferRegOffset => {
|
|
||||||
Ok(BarrelShifterValue::ShiftedRegister(ShiftedRegister {
|
#[inline(always)]
|
||||||
reg: (self.raw & 0xf) as usize,
|
fn ldr_str_hs_reg_offset(&self) -> BarrelShifterValue {
|
||||||
|
BarrelShifterValue::ShiftedRegister(
|
||||||
|
ShiftedRegister {
|
||||||
|
reg: (self & 0xf) as usize,
|
||||||
shift_by: ShiftRegisterBy::ByAmount(0),
|
shift_by: ShiftRegisterBy::ByAmount(0),
|
||||||
bs_op: BarrelShiftOpCode::LSL,
|
bs_op: BarrelShiftOpCode::LSL,
|
||||||
added: Some(self.add_offset_flag()),
|
added: Some(self.add_offset_flag()),
|
||||||
}))
|
})
|
||||||
}
|
|
||||||
_ => Err(self.make_decode_error(DecodedPartDoesNotBelongToInstruction)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operand2(&self) -> BarrelShifterValue {
|
fn operand2(&self) -> BarrelShifterValue {
|
||||||
if self.raw.bit(25) {
|
if self.bit(25) {
|
||||||
let immediate = self.raw & 0xff;
|
let immediate = self & 0xff;
|
||||||
let rotate = 2 * self.raw.bit_range(8..12);
|
let rotate = 2 * self.bit_range(8..12);
|
||||||
BarrelShifterValue::RotatedImmediate(immediate, rotate)
|
BarrelShifterValue::RotatedImmediate(immediate, rotate)
|
||||||
} else {
|
} else {
|
||||||
let reg = self.raw & 0xf;
|
let reg = self & 0xf;
|
||||||
let shifted_reg = ShiftedRegister {
|
let shifted_reg = ShiftedRegister {
|
||||||
reg: reg as usize,
|
reg: reg as usize,
|
||||||
bs_op: self.get_bs_op(self.raw),
|
bs_op: self.get_bs_op(),
|
||||||
shift_by: self.get_shift_reg_by(self.raw),
|
shift_by: self.get_shift_reg_by(),
|
||||||
added: None,
|
added: None,
|
||||||
}; // TODO error handling
|
}; // TODO error handling
|
||||||
BarrelShifterValue::ShiftedRegister(shifted_reg)
|
BarrelShifterValue::ShiftedRegister(shifted_reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_list(&self) -> u16 {
|
fn register_list(&self) -> u16 {
|
||||||
(self.raw & 0xffff) as u16
|
(self & 0xffff) as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swi_comment(&self) -> u32 {
|
fn swi_comment(&self) -> u32 {
|
||||||
self.raw.bit_range(0..24)
|
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.
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
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::DecodedInstruction;
|
||||||
use super::{arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState};
|
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);
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,112 +208,164 @@ 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 {
|
/// A trait which provides methods to extract thumb instruction fields
|
||||||
self.raw.bit_range(3..6) as usize
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rb(&self) -> usize {
|
macro_rules! thumb_decode_helper_impl {
|
||||||
match self.fmt {
|
($($t:ty),*) => {$(
|
||||||
ThumbFormat::LdmStm => self.raw.bit_range(8..11) as usize,
|
|
||||||
_ => self.raw.bit_range(3..6) as usize,
|
impl ThumbDecodeHelper for $t {
|
||||||
}
|
|
||||||
|
#[inline]
|
||||||
|
fn rs(&self) -> usize {
|
||||||
|
self.bit_range(3..6) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ro(&self) -> usize {
|
#[inline]
|
||||||
self.raw.bit_range(6..9) as usize
|
/// Note: not true for LdmStm
|
||||||
|
fn rb(&self) -> usize {
|
||||||
|
self.bit_range(3..6) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rn(&self) -> usize {
|
#[inline]
|
||||||
self.raw.bit_range(6..9) as usize
|
fn ro(&self) -> usize {
|
||||||
|
self.bit_range(6..9) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format1_op(&self) -> BarrelShiftOpCode {
|
#[inline]
|
||||||
BarrelShiftOpCode::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
|
fn rn(&self) -> usize {
|
||||||
|
self.bit_range(6..9) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format3_op(&self) -> OpFormat3 {
|
#[inline]
|
||||||
OpFormat3::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
|
fn format1_op(&self) -> BarrelShiftOpCode {
|
||||||
|
BarrelShiftOpCode::from_u8(self.bit_range(11..13) as u8).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format5_op(&self) -> OpFormat5 {
|
#[inline]
|
||||||
OpFormat5::from_u8(self.raw.bit_range(8..10) as u8).unwrap()
|
fn format3_op(&self) -> OpFormat3 {
|
||||||
|
OpFormat3::from_u8(self.bit_range(11..13) as u8).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format4_alu_op(&self) -> ThumbAluOps {
|
#[inline]
|
||||||
ThumbAluOps::from_u16(self.raw.bit_range(6..10)).unwrap()
|
fn format5_op(&self) -> OpFormat5 {
|
||||||
|
OpFormat5::from_u8(self.bit_range(8..10) as u8).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset5(&self) -> u8 {
|
#[inline]
|
||||||
self.raw.bit_range(6..11) as u8
|
fn format4_alu_op(&self) -> ThumbAluOps {
|
||||||
|
ThumbAluOps::from_u16(self.bit_range(6..10)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bcond_offset(&self) -> i32 {
|
#[inline]
|
||||||
((((self.raw & 0xff) as u32) << 24) as i32) >> 23
|
fn offset5(&self) -> u8 {
|
||||||
|
self.bit_range(6..11) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset11(&self) -> i32 {
|
#[inline]
|
||||||
(self.raw & 0x7FF) as i32
|
fn bcond_offset(&self) -> i32 {
|
||||||
|
((((*self & 0xff) as u32) << 24) as i32) >> 23
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn word8(&self) -> u16 {
|
#[inline]
|
||||||
(self.raw & 0xff) << 2
|
fn offset11(&self) -> i32 {
|
||||||
|
(*self & 0x7FF) as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_transferring_bytes(&self) -> bool {
|
#[inline]
|
||||||
match self.fmt {
|
fn word8(&self) -> u16 {
|
||||||
ThumbFormat::LdrStrRegOffset => self.raw.bit(10),
|
(*self & 0xff) << 2
|
||||||
ThumbFormat::LdrStrImmOffset => self.raw.bit(12),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_load(&self) -> bool {
|
#[inline]
|
||||||
self.raw.bit(11)
|
fn is_load(&self) -> bool {
|
||||||
|
self.bit(11)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_subtract(&self) -> bool {
|
#[inline]
|
||||||
self.raw.bit(9)
|
fn is_subtract(&self) -> bool {
|
||||||
|
self.bit(9)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_immediate_operand(&self) -> bool {
|
#[inline]
|
||||||
self.raw.bit(10)
|
fn is_immediate_operand(&self) -> bool {
|
||||||
|
self.bit(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cond(&self) -> ArmCond {
|
#[inline]
|
||||||
ArmCond::from_u8(self.raw.bit_range(8..12) as u8).expect("bad condition")
|
fn cond(&self) -> ArmCond {
|
||||||
|
ArmCond::from_u8(self.bit_range(8..12) as u8).expect("bad condition")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flag(&self, bit: usize) -> bool {
|
#[inline]
|
||||||
self.raw.bit(bit)
|
fn flag(self, bit: usize) -> bool {
|
||||||
|
self.bit(bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_list(&self) -> u8 {
|
#[inline]
|
||||||
(self.raw & 0xff) as u8
|
fn register_list(&self) -> u8 {
|
||||||
|
(*self & 0xff) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sword7(&self) -> i32 {
|
#[inline]
|
||||||
let imm7 = self.raw & 0x7f;
|
fn sword7(&self) -> i32 {
|
||||||
if self.flag(ThumbInstruction::FLAG_S) {
|
let imm7 = *self & 0x7f;
|
||||||
|
if self.bit(consts::flags::FLAG_S) {
|
||||||
-((imm7 << 2) as i32)
|
-((imm7 << 2) as i32)
|
||||||
} else {
|
} else {
|
||||||
(imm7 << 2) as i32
|
(imm7 << 2) as i32
|
||||||
|
@ -316,6 +373,11 @@ impl ThumbInstruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Reference in a new issue