thumb: Finish converting all instructions to use const-generics
Former-commit-id: 671c75a985123a52b312a8ae5495ff545d7678dc Former-commit-id: c0794ce36f410cd8c058cbe087c388a84aae8b85
This commit is contained in:
parent
4a7eb53707
commit
03d28c77b6
|
@ -8,6 +8,8 @@ use bit::BitIndex;
|
|||
|
||||
// copied and slightly adjusted from src/core/arm7tdmi/thumb/mod.rs
|
||||
fn thumb_decode(i: u16) -> (&'static str, String) {
|
||||
let offset5 = i.bit_range(6..11) as u8;
|
||||
let load = i.bit(11);
|
||||
if i & 0xf800 == 0x1800 {
|
||||
(
|
||||
"AddSub",
|
||||
|
@ -22,9 +24,9 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
|
|||
(
|
||||
"MoveShiftedReg",
|
||||
format!(
|
||||
"exec_thumb_move_shifted_reg::<{BS_OP}, {OFFSET5}>",
|
||||
"exec_thumb_move_shifted_reg::<{BS_OP}, {IMM}>",
|
||||
BS_OP = i.bit_range(11..13) as u8,
|
||||
OFFSET5 = i.bit_range(6..11) as u8
|
||||
IMM = i.bit_range(6..11) as u8
|
||||
),
|
||||
)
|
||||
} else if i & 0xe000 == 0x2000 {
|
||||
|
@ -64,16 +66,23 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
|
|||
"LdrStrRegOffset",
|
||||
format!(
|
||||
"exec_thumb_ldr_str_reg_offset::<{LOAD}, {RO}, {BYTE}>",
|
||||
LOAD = i.bit(11),
|
||||
LOAD = load,
|
||||
RO = i.bit_range(6..9) as usize,
|
||||
BYTE = i.bit(10),
|
||||
),
|
||||
)
|
||||
} else if i & 0xf200 == 0x5200 {
|
||||
("LdrStrSHB", String::from("exec_thumb_ldr_str_shb"))
|
||||
(
|
||||
"LdrStrSHB",
|
||||
format!(
|
||||
"exec_thumb_ldr_str_shb::<{RO}, {SIGN_EXTEND}, {HALFWORD}>",
|
||||
RO = i.bit_range(6..9) as usize,
|
||||
SIGN_EXTEND = i.bit(10),
|
||||
HALFWORD = i.bit(11),
|
||||
),
|
||||
)
|
||||
} else if i & 0xe000 == 0x6000 {
|
||||
let is_transferring_bytes = i.bit(12);
|
||||
let offset5 = i.bit_range(6..11) as u8;
|
||||
let offset = if is_transferring_bytes {
|
||||
offset5
|
||||
} else {
|
||||
|
@ -83,7 +92,7 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
|
|||
"LdrStrImmOffset",
|
||||
format!(
|
||||
"exec_thumb_ldr_str_imm_offset::<{LOAD}, {BYTE}, {OFFSET}>",
|
||||
LOAD = i.bit(11),
|
||||
LOAD = load,
|
||||
BYTE = is_transferring_bytes,
|
||||
OFFSET = offset
|
||||
),
|
||||
|
@ -91,31 +100,72 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
|
|||
} else if i & 0xf000 == 0x8000 {
|
||||
(
|
||||
"LdrStrHalfWord",
|
||||
String::from("exec_thumb_ldr_str_halfword"),
|
||||
format!(
|
||||
"exec_thumb_ldr_str_halfword::<{LOAD}, {OFFSET}>",
|
||||
LOAD = load,
|
||||
OFFSET = (offset5 << 1) as i32
|
||||
),
|
||||
)
|
||||
} else if i & 0xf000 == 0x9000 {
|
||||
("LdrStrSp", String::from("exec_thumb_ldr_str_sp"))
|
||||
(
|
||||
"LdrStrSp",
|
||||
format!(
|
||||
"exec_thumb_ldr_str_sp::<{LOAD}, {RD}>",
|
||||
LOAD = load,
|
||||
RD = i.bit_range(8..11)
|
||||
),
|
||||
)
|
||||
} else if i & 0xf000 == 0xa000 {
|
||||
("LoadAddress", String::from("exec_thumb_load_address"))
|
||||
(
|
||||
"LoadAddress",
|
||||
format!(
|
||||
"exec_thumb_load_address::<{SP}, {RD}>",
|
||||
SP = i.bit(11),
|
||||
RD = i.bit_range(8..11)
|
||||
),
|
||||
)
|
||||
} else if i & 0xff00 == 0xb000 {
|
||||
("AddSp", String::from("exec_thumb_add_sp"))
|
||||
(
|
||||
"AddSp",
|
||||
format!("exec_thumb_add_sp::<{FLAG_S}>", FLAG_S = i.bit(7)),
|
||||
)
|
||||
} else if i & 0xf600 == 0xb400 {
|
||||
("PushPop", String::from("exec_thumb_push_pop"))
|
||||
(
|
||||
"PushPop",
|
||||
format!(
|
||||
"exec_thumb_push_pop::<{POP}, {FLAG_R}>",
|
||||
POP = load,
|
||||
FLAG_R = i.bit(8)
|
||||
),
|
||||
)
|
||||
} else if i & 0xf000 == 0xc000 {
|
||||
("LdmStm", String::from("exec_thumb_ldm_stm"))
|
||||
(
|
||||
"LdmStm",
|
||||
format!(
|
||||
"exec_thumb_ldm_stm::<{LOAD}, {RB}>",
|
||||
LOAD = load,
|
||||
RB = i.bit_range(8..11) as usize
|
||||
),
|
||||
)
|
||||
} else if i & 0xff00 == 0xdf00 {
|
||||
("Swi", String::from("exec_thumb_swi"))
|
||||
} else if i & 0xf000 == 0xd000 {
|
||||
(
|
||||
"BranchConditional",
|
||||
String::from("exec_thumb_branch_with_cond"),
|
||||
format!(
|
||||
"exec_thumb_branch_with_cond::<{COND}>",
|
||||
COND = i.bit_range(8..12) as u8
|
||||
),
|
||||
)
|
||||
} else if i & 0xf800 == 0xe000 {
|
||||
("Branch", String::from("exec_thumb_branch"))
|
||||
} else if i & 0xf000 == 0xf000 {
|
||||
(
|
||||
"BranchLongWithLink",
|
||||
String::from("exec_thumb_branch_long_with_link"),
|
||||
format!(
|
||||
"exec_thumb_branch_long_with_link::<{FLAG_LOW_OFFSET}>",
|
||||
FLAG_LOW_OFFSET = i.bit(11),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
("Undefined", String::from("thumb_undefined"))
|
||||
|
|
|
@ -7,6 +7,18 @@ use crate::arm7tdmi::*;
|
|||
|
||||
use super::ThumbDecodeHelper;
|
||||
|
||||
pub(super) mod consts {
|
||||
pub(super) mod flags {
|
||||
pub const FLAG_H1: usize = 7;
|
||||
pub const FLAG_H2: usize = 6;
|
||||
pub const FLAG_R: usize = 8;
|
||||
pub const FLAG_LOW_OFFSET: usize = 11;
|
||||
pub const FLAG_SP: usize = 11;
|
||||
pub const FLAG_SIGN_EXTEND: usize = 10;
|
||||
pub const FLAG_HALFWORD: usize = 11;
|
||||
}
|
||||
}
|
||||
|
||||
impl ThumbInstruction {
|
||||
fn fmt_thumb_move_shifted_reg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
|
|
|
@ -10,14 +10,14 @@ use MemoryAccess::*;
|
|||
impl<I: MemoryInterface> Core<I> {
|
||||
/// Format 1
|
||||
/// Execution Time: 1S
|
||||
pub(in super::super) fn exec_thumb_move_shifted_reg<const BS_OP: u8, const OFFSET5: u8>(
|
||||
pub(in super::super) fn exec_thumb_move_shifted_reg<const BS_OP: u8, const IMM: u8>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let rd = (insn & 0b111) as usize;
|
||||
let rs = insn.bit_range(3..6) as usize;
|
||||
|
||||
let shift_amount = OFFSET5 as u32;
|
||||
let shift_amount = IMM as u32;
|
||||
let mut carry = self.cpsr.C();
|
||||
let bsop = match BS_OP {
|
||||
0 => BarrelShiftOpCode::LSL,
|
||||
|
@ -258,15 +258,19 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 8 load/store sign-extended byte/halfword
|
||||
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR
|
||||
pub(in super::super) fn exec_thumb_ldr_str_shb(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_ldr_str_shb<
|
||||
const RO: usize,
|
||||
const SIGN_EXTEND: bool,
|
||||
const HALFWORD: bool,
|
||||
>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let rb = insn.bit_range(3..6) as usize;
|
||||
let rd = (insn & 0b111) as usize;
|
||||
|
||||
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
|
||||
match (
|
||||
insn.bit(consts::flags::FLAG_SIGN_EXTEND),
|
||||
insn.bit(consts::flags::FLAG_HALFWORD),
|
||||
) {
|
||||
let addr = self.gpr[rb].wrapping_add(self.gpr[RO]);
|
||||
match (SIGN_EXTEND, HALFWORD) {
|
||||
(false, false) =>
|
||||
/* strh */
|
||||
{
|
||||
|
@ -314,12 +318,15 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 10
|
||||
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR
|
||||
pub(in super::super) fn exec_thumb_ldr_str_halfword(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_ldr_str_halfword<const LOAD: bool, const OFFSET: i32>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let rb = insn.bit_range(3..6) as usize;
|
||||
let rd = (insn & 0b111) as usize;
|
||||
let base = self.gpr[rb] as i32;
|
||||
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
||||
if insn.is_load() {
|
||||
let addr = base.wrapping_add(OFFSET) as Addr;
|
||||
if LOAD {
|
||||
let data = self.ldr_half(addr, NonSeq);
|
||||
self.idle_cycle();
|
||||
self.gpr[rd] = data as u32;
|
||||
|
@ -332,26 +339,29 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 11 load/store SP-relative
|
||||
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR
|
||||
pub(in super::super) fn exec_thumb_ldr_str_sp(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_ldr_str_sp<const LOAD: bool, const RD: usize>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let addr = self.gpr[REG_SP] + (insn.word8() as Addr);
|
||||
let rd = insn.bit_range(8..11) as usize;
|
||||
if insn.is_load() {
|
||||
if LOAD {
|
||||
let data = self.ldr_word(addr, NonSeq);
|
||||
self.idle_cycle();
|
||||
self.gpr[rd] = data;
|
||||
self.gpr[RD] = data;
|
||||
CpuAction::AdvancePC(Seq)
|
||||
} else {
|
||||
self.store_aligned_32(addr, self.gpr[rd], NonSeq);
|
||||
self.store_aligned_32(addr, self.gpr[RD], NonSeq);
|
||||
CpuAction::AdvancePC(NonSeq)
|
||||
}
|
||||
}
|
||||
|
||||
/// Format 12
|
||||
/// Execution Time: 1S
|
||||
pub(in super::super) fn exec_thumb_load_address(&mut self, insn: u16) -> CpuAction {
|
||||
let rd = insn.bit_range(8..11) as usize;
|
||||
|
||||
self.gpr[rd] = if insn.bit(consts::flags::FLAG_SP) {
|
||||
pub(in super::super) fn exec_thumb_load_address<const SP: bool, const RD: usize>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
self.gpr[RD] = if SP {
|
||||
self.gpr[REG_SP] + (insn.word8() as Addr)
|
||||
} else {
|
||||
(self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr)
|
||||
|
@ -362,17 +372,26 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 13
|
||||
/// Execution Time: 1S
|
||||
pub(in super::super) fn exec_thumb_add_sp(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_add_sp<const FLAG_S: bool>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let op1 = self.gpr[REG_SP] as i32;
|
||||
let op2 = insn.sword7();
|
||||
|
||||
self.gpr[REG_SP] = op1.wrapping_add(op2) as u32;
|
||||
let offset = ((insn & 0x7f) << 2) as i32;
|
||||
self.gpr[REG_SP] = if FLAG_S {
|
||||
op1.wrapping_sub(offset) as u32
|
||||
} else {
|
||||
op1.wrapping_add(offset) as u32
|
||||
};
|
||||
|
||||
CpuAction::AdvancePC(Seq)
|
||||
}
|
||||
/// Format 14
|
||||
/// Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
|
||||
pub(in super::super) fn exec_thumb_push_pop(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_push_pop<const POP: bool, const FLAG_R: bool>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
macro_rules! push {
|
||||
($r:expr, $access:ident) => {
|
||||
self.gpr[REG_SP] -= 4;
|
||||
|
@ -395,17 +414,15 @@ impl<I: MemoryInterface> Core<I> {
|
|||
};
|
||||
}
|
||||
let mut result = CpuAction::AdvancePC(NonSeq);
|
||||
let is_pop = insn.is_load();
|
||||
let pc_lr_flag = insn.bit(consts::flags::FLAG_R);
|
||||
let rlist = insn.register_list();
|
||||
let mut access = MemoryAccess::NonSeq;
|
||||
if is_pop {
|
||||
if POP {
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
pop!(r, access);
|
||||
}
|
||||
}
|
||||
if pc_lr_flag {
|
||||
if FLAG_R {
|
||||
pop!(REG_PC);
|
||||
self.pc = self.pc & !1;
|
||||
result = CpuAction::PipelineFlushed;
|
||||
|
@ -414,7 +431,7 @@ impl<I: MemoryInterface> Core<I> {
|
|||
// Idle 1 cycle
|
||||
self.idle_cycle();
|
||||
} else {
|
||||
if pc_lr_flag {
|
||||
if FLAG_R {
|
||||
push!(REG_LR, access);
|
||||
}
|
||||
for r in (0..8).rev() {
|
||||
|
@ -429,19 +446,18 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 15
|
||||
/// Execution Time: nS+1N+1I for LDM, or (n-1)S+2N for STM.
|
||||
pub(in super::super) fn exec_thumb_ldm_stm(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_ldm_stm<const LOAD: bool, const RB: usize>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let mut result = CpuAction::AdvancePC(NonSeq);
|
||||
|
||||
let rb = insn.bit_range(8..11) as usize;
|
||||
let base_reg = rb;
|
||||
let is_load = insn.is_load();
|
||||
|
||||
let align_preserve = self.gpr[base_reg] & 3;
|
||||
let mut addr = self.gpr[base_reg] & !3;
|
||||
let align_preserve = self.gpr[RB] & 3;
|
||||
let mut addr = self.gpr[RB] & !3;
|
||||
let rlist = insn.register_list();
|
||||
// let mut first = true;
|
||||
if rlist != 0 {
|
||||
if is_load {
|
||||
if LOAD {
|
||||
let mut access = NonSeq;
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
|
@ -452,15 +468,15 @@ impl<I: MemoryInterface> Core<I> {
|
|||
}
|
||||
}
|
||||
self.idle_cycle();
|
||||
if !rlist.bit(base_reg) {
|
||||
self.gpr[base_reg] = addr + align_preserve;
|
||||
if !rlist.bit(RB) {
|
||||
self.gpr[RB] = addr + align_preserve;
|
||||
}
|
||||
} else {
|
||||
let mut first = true;
|
||||
let mut access = NonSeq;
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
let v = if r != base_reg {
|
||||
let v = if r != RB {
|
||||
self.gpr[r]
|
||||
} else {
|
||||
if first {
|
||||
|
@ -476,12 +492,12 @@ impl<I: MemoryInterface> Core<I> {
|
|||
access = Seq;
|
||||
addr += 4;
|
||||
}
|
||||
self.gpr[base_reg] = addr + align_preserve;
|
||||
self.gpr[RB] = addr + align_preserve;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// From gbatek.htm: Empty Rlist: R15 loaded/stored (ARMv4 only), and Rb=Rb+40h (ARMv4-v5).
|
||||
if is_load {
|
||||
if LOAD {
|
||||
let val = self.load_32(addr, NonSeq);
|
||||
self.pc = val & !1;
|
||||
result = CpuAction::PipelineFlushed;
|
||||
|
@ -490,7 +506,7 @@ impl<I: MemoryInterface> Core<I> {
|
|||
self.store_32(addr, self.pc + 2, NonSeq);
|
||||
}
|
||||
addr += 0x40;
|
||||
self.gpr[base_reg] = addr + align_preserve;
|
||||
self.gpr[RB] = addr + align_preserve;
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -500,8 +516,12 @@ impl<I: MemoryInterface> Core<I> {
|
|||
/// Execution Time:
|
||||
/// 2S+1N if condition true (jump executed)
|
||||
/// 1S if condition false
|
||||
pub(in super::super) fn exec_thumb_branch_with_cond(&mut self, insn: u16) -> CpuAction {
|
||||
if !self.check_arm_cond(insn.cond()) {
|
||||
pub(in super::super) fn exec_thumb_branch_with_cond<const COND: u8>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let cond = ArmCond::from_u8(COND).expect("bad cond");
|
||||
if !self.check_arm_cond(cond) {
|
||||
CpuAction::AdvancePC(Seq)
|
||||
} else {
|
||||
let offset = insn.bcond_offset();
|
||||
|
@ -529,9 +549,12 @@ impl<I: MemoryInterface> Core<I> {
|
|||
|
||||
/// Format 19
|
||||
/// Execution Time: 3S+1N (first opcode 1S, second opcode 2S+1N).
|
||||
pub(in super::super) fn exec_thumb_branch_long_with_link(&mut self, insn: u16) -> CpuAction {
|
||||
pub(in super::super) fn exec_thumb_branch_long_with_link<const FLAG_LOW_OFFSET: bool>(
|
||||
&mut self,
|
||||
insn: u16,
|
||||
) -> CpuAction {
|
||||
let mut off = insn.offset11();
|
||||
if insn.bit(consts::flags::FLAG_LOW_OFFSET) {
|
||||
if FLAG_LOW_OFFSET {
|
||||
off = off << 1;
|
||||
let next_pc = (self.pc - 2) | 1;
|
||||
self.pc = ((self.gpr[REG_LR] & !1) as i32).wrapping_add(off) as u32;
|
||||
|
|
|
@ -208,21 +208,6 @@ impl From<OpFormat5> for AluOpCode {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) mod consts {
|
||||
pub(super) mod flags {
|
||||
#[cfg(feature = "debugger")]
|
||||
pub const FLAG_H1: usize = 7;
|
||||
#[cfg(feature = "debugger")]
|
||||
pub const FLAG_H2: usize = 6;
|
||||
pub const FLAG_R: usize = 8;
|
||||
pub const FLAG_S: usize = 7;
|
||||
pub const FLAG_LOW_OFFSET: usize = 11;
|
||||
pub const FLAG_SP: usize = 11;
|
||||
pub const FLAG_SIGN_EXTEND: usize = 10;
|
||||
pub const FLAG_HALFWORD: usize = 11;
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait which provides methods to extract thumb instruction fields
|
||||
pub trait ThumbDecodeHelper {
|
||||
// Consts
|
||||
|
@ -367,7 +352,7 @@ macro_rules! thumb_decode_helper_impl {
|
|||
#[inline]
|
||||
fn sword7(&self) -> i32 {
|
||||
let imm7 = *self & 0x7f;
|
||||
if self.bit(consts::flags::FLAG_S) {
|
||||
if self.bit(7) {
|
||||
-((imm7 << 2) as i32)
|
||||
} else {
|
||||
(imm7 << 2) as i32
|
||||
|
|
Reference in a new issue