thumb: Finish converting all instructions to use const-generics

Former-commit-id: 671c75a985123a52b312a8ae5495ff545d7678dc
Former-commit-id: c0794ce36f410cd8c058cbe087c388a84aae8b85
This commit is contained in:
Michel Heily 2021-07-02 13:53:00 +03:00 committed by MichelOS
parent 4a7eb53707
commit 03d28c77b6
4 changed files with 148 additions and 78 deletions

View file

@ -8,6 +8,8 @@ use bit::BitIndex;
// copied and slightly adjusted from src/core/arm7tdmi/thumb/mod.rs // copied and slightly adjusted from src/core/arm7tdmi/thumb/mod.rs
fn thumb_decode(i: u16) -> (&'static str, String) { 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 { if i & 0xf800 == 0x1800 {
( (
"AddSub", "AddSub",
@ -22,9 +24,9 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
( (
"MoveShiftedReg", "MoveShiftedReg",
format!( 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, 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 { } else if i & 0xe000 == 0x2000 {
@ -64,16 +66,23 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
"LdrStrRegOffset", "LdrStrRegOffset",
format!( format!(
"exec_thumb_ldr_str_reg_offset::<{LOAD}, {RO}, {BYTE}>", "exec_thumb_ldr_str_reg_offset::<{LOAD}, {RO}, {BYTE}>",
LOAD = i.bit(11), LOAD = load,
RO = i.bit_range(6..9) as usize, RO = i.bit_range(6..9) as usize,
BYTE = i.bit(10), BYTE = i.bit(10),
), ),
) )
} else if i & 0xf200 == 0x5200 { } 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 { } else if i & 0xe000 == 0x6000 {
let is_transferring_bytes = i.bit(12); let is_transferring_bytes = i.bit(12);
let offset5 = i.bit_range(6..11) as u8;
let offset = if is_transferring_bytes { let offset = if is_transferring_bytes {
offset5 offset5
} else { } else {
@ -83,7 +92,7 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
"LdrStrImmOffset", "LdrStrImmOffset",
format!( format!(
"exec_thumb_ldr_str_imm_offset::<{LOAD}, {BYTE}, {OFFSET}>", "exec_thumb_ldr_str_imm_offset::<{LOAD}, {BYTE}, {OFFSET}>",
LOAD = i.bit(11), LOAD = load,
BYTE = is_transferring_bytes, BYTE = is_transferring_bytes,
OFFSET = offset OFFSET = offset
), ),
@ -91,31 +100,72 @@ fn thumb_decode(i: u16) -> (&'static str, String) {
} else if i & 0xf000 == 0x8000 { } else if i & 0xf000 == 0x8000 {
( (
"LdrStrHalfWord", "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 { } 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 { } 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 { } 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 { } 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 { } 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 { } else if i & 0xff00 == 0xdf00 {
("Swi", String::from("exec_thumb_swi")) ("Swi", String::from("exec_thumb_swi"))
} else if i & 0xf000 == 0xd000 { } else if i & 0xf000 == 0xd000 {
( (
"BranchConditional", "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 { } else if i & 0xf800 == 0xe000 {
("Branch", String::from("exec_thumb_branch")) ("Branch", String::from("exec_thumb_branch"))
} else if i & 0xf000 == 0xf000 { } else if i & 0xf000 == 0xf000 {
( (
"BranchLongWithLink", "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 { } else {
("Undefined", String::from("thumb_undefined")) ("Undefined", String::from("thumb_undefined"))

View file

@ -7,6 +7,18 @@ use crate::arm7tdmi::*;
use super::ThumbDecodeHelper; 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 { 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!(

View file

@ -10,14 +10,14 @@ use MemoryAccess::*;
impl<I: MemoryInterface> Core<I> { impl<I: MemoryInterface> Core<I> {
/// Format 1 /// Format 1
/// Execution Time: 1S /// 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, &mut self,
insn: u16, insn: u16,
) -> CpuAction { ) -> CpuAction {
let rd = (insn & 0b111) as usize; let rd = (insn & 0b111) as usize;
let rs = insn.bit_range(3..6) 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 mut carry = self.cpsr.C();
let bsop = match BS_OP { let bsop = match BS_OP {
0 => BarrelShiftOpCode::LSL, 0 => BarrelShiftOpCode::LSL,
@ -258,15 +258,19 @@ impl<I: MemoryInterface> Core<I> {
/// Format 8 load/store sign-extended byte/halfword /// Format 8 load/store sign-extended byte/halfword
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR /// 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 rb = insn.bit_range(3..6) as usize;
let rd = (insn & 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[RO]);
match ( match (SIGN_EXTEND, HALFWORD) {
insn.bit(consts::flags::FLAG_SIGN_EXTEND),
insn.bit(consts::flags::FLAG_HALFWORD),
) {
(false, false) => (false, false) =>
/* strh */ /* strh */
{ {
@ -314,12 +318,15 @@ impl<I: MemoryInterface> Core<I> {
/// Format 10 /// Format 10
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR /// 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 rb = insn.bit_range(3..6) as usize;
let rd = (insn & 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(OFFSET) as Addr;
if insn.is_load() { if LOAD {
let data = self.ldr_half(addr, NonSeq); let data = self.ldr_half(addr, NonSeq);
self.idle_cycle(); self.idle_cycle();
self.gpr[rd] = data as u32; self.gpr[rd] = data as u32;
@ -332,26 +339,29 @@ impl<I: MemoryInterface> Core<I> {
/// Format 11 load/store SP-relative /// Format 11 load/store SP-relative
/// Execution Time: 1S+1N+1I for LDR, or 2N for STR /// 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 addr = self.gpr[REG_SP] + (insn.word8() as Addr);
let rd = insn.bit_range(8..11) as usize; if LOAD {
if insn.is_load() {
let data = self.ldr_word(addr, NonSeq); let data = self.ldr_word(addr, NonSeq);
self.idle_cycle(); self.idle_cycle();
self.gpr[rd] = data; self.gpr[RD] = data;
CpuAction::AdvancePC(Seq) CpuAction::AdvancePC(Seq)
} else { } else {
self.store_aligned_32(addr, self.gpr[rd], NonSeq); self.store_aligned_32(addr, self.gpr[RD], NonSeq);
CpuAction::AdvancePC(NonSeq) CpuAction::AdvancePC(NonSeq)
} }
} }
/// Format 12 /// Format 12
/// Execution Time: 1S /// Execution Time: 1S
pub(in super::super) fn exec_thumb_load_address(&mut self, insn: u16) -> CpuAction { pub(in super::super) fn exec_thumb_load_address<const SP: bool, const RD: usize>(
let rd = insn.bit_range(8..11) as usize; &mut self,
insn: u16,
self.gpr[rd] = if insn.bit(consts::flags::FLAG_SP) { ) -> CpuAction {
self.gpr[RD] = if SP {
self.gpr[REG_SP] + (insn.word8() as Addr) self.gpr[REG_SP] + (insn.word8() as Addr)
} else { } else {
(self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr) (self.pc_thumb() & !0b10) + 4 + (insn.word8() as Addr)
@ -362,17 +372,26 @@ impl<I: MemoryInterface> Core<I> {
/// Format 13 /// Format 13
/// Execution Time: 1S /// 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 op1 = self.gpr[REG_SP] as i32;
let op2 = insn.sword7(); let offset = ((insn & 0x7f) << 2) as i32;
self.gpr[REG_SP] = if FLAG_S {
self.gpr[REG_SP] = op1.wrapping_add(op2) as u32; op1.wrapping_sub(offset) as u32
} else {
op1.wrapping_add(offset) as u32
};
CpuAction::AdvancePC(Seq) CpuAction::AdvancePC(Seq)
} }
/// Format 14 /// Format 14
/// Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH). /// 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 { macro_rules! push {
($r:expr, $access:ident) => { ($r:expr, $access:ident) => {
self.gpr[REG_SP] -= 4; self.gpr[REG_SP] -= 4;
@ -395,17 +414,15 @@ impl<I: MemoryInterface> Core<I> {
}; };
} }
let mut result = CpuAction::AdvancePC(NonSeq); 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 rlist = insn.register_list();
let mut access = MemoryAccess::NonSeq; let mut access = MemoryAccess::NonSeq;
if is_pop { if POP {
for r in 0..8 { for r in 0..8 {
if rlist.bit(r) { if rlist.bit(r) {
pop!(r, access); pop!(r, access);
} }
} }
if pc_lr_flag { if FLAG_R {
pop!(REG_PC); pop!(REG_PC);
self.pc = self.pc & !1; self.pc = self.pc & !1;
result = CpuAction::PipelineFlushed; result = CpuAction::PipelineFlushed;
@ -414,7 +431,7 @@ impl<I: MemoryInterface> Core<I> {
// Idle 1 cycle // Idle 1 cycle
self.idle_cycle(); self.idle_cycle();
} else { } else {
if pc_lr_flag { if FLAG_R {
push!(REG_LR, access); push!(REG_LR, access);
} }
for r in (0..8).rev() { for r in (0..8).rev() {
@ -429,19 +446,18 @@ impl<I: MemoryInterface> Core<I> {
/// Format 15 /// Format 15
/// Execution Time: nS+1N+1I for LDM, or (n-1)S+2N for STM. /// 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 mut result = CpuAction::AdvancePC(NonSeq);
let rb = insn.bit_range(8..11) as usize; let align_preserve = self.gpr[RB] & 3;
let base_reg = rb; let mut addr = self.gpr[RB] & !3;
let is_load = insn.is_load();
let align_preserve = self.gpr[base_reg] & 3;
let mut addr = self.gpr[base_reg] & !3;
let rlist = insn.register_list(); let rlist = insn.register_list();
// let mut first = true; // let mut first = true;
if rlist != 0 { if rlist != 0 {
if is_load { if LOAD {
let mut access = NonSeq; let mut access = NonSeq;
for r in 0..8 { for r in 0..8 {
if rlist.bit(r) { if rlist.bit(r) {
@ -452,15 +468,15 @@ impl<I: MemoryInterface> Core<I> {
} }
} }
self.idle_cycle(); self.idle_cycle();
if !rlist.bit(base_reg) { if !rlist.bit(RB) {
self.gpr[base_reg] = addr + align_preserve; self.gpr[RB] = addr + align_preserve;
} }
} else { } else {
let mut first = true; let mut first = true;
let mut access = NonSeq; let mut access = NonSeq;
for r in 0..8 { for r in 0..8 {
if rlist.bit(r) { if rlist.bit(r) {
let v = if r != base_reg { let v = if r != RB {
self.gpr[r] self.gpr[r]
} else { } else {
if first { if first {
@ -476,12 +492,12 @@ impl<I: MemoryInterface> Core<I> {
access = Seq; access = Seq;
addr += 4; addr += 4;
} }
self.gpr[base_reg] = addr + align_preserve; self.gpr[RB] = addr + align_preserve;
} }
} }
} else { } else {
// From gbatek.htm: Empty Rlist: R15 loaded/stored (ARMv4 only), and Rb=Rb+40h (ARMv4-v5). // 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); let val = self.load_32(addr, NonSeq);
self.pc = val & !1; self.pc = val & !1;
result = CpuAction::PipelineFlushed; result = CpuAction::PipelineFlushed;
@ -490,7 +506,7 @@ impl<I: MemoryInterface> Core<I> {
self.store_32(addr, self.pc + 2, NonSeq); self.store_32(addr, self.pc + 2, NonSeq);
} }
addr += 0x40; addr += 0x40;
self.gpr[base_reg] = addr + align_preserve; self.gpr[RB] = addr + align_preserve;
} }
result result
@ -500,8 +516,12 @@ impl<I: MemoryInterface> Core<I> {
/// Execution Time: /// Execution Time:
/// 2S+1N if condition true (jump executed) /// 2S+1N if condition true (jump executed)
/// 1S if condition false /// 1S if condition false
pub(in super::super) fn exec_thumb_branch_with_cond(&mut self, insn: u16) -> CpuAction { pub(in super::super) fn exec_thumb_branch_with_cond<const COND: u8>(
if !self.check_arm_cond(insn.cond()) { &mut self,
insn: u16,
) -> CpuAction {
let cond = ArmCond::from_u8(COND).expect("bad cond");
if !self.check_arm_cond(cond) {
CpuAction::AdvancePC(Seq) CpuAction::AdvancePC(Seq)
} else { } else {
let offset = insn.bcond_offset(); let offset = insn.bcond_offset();
@ -529,9 +549,12 @@ impl<I: MemoryInterface> Core<I> {
/// Format 19 /// Format 19
/// Execution Time: 3S+1N (first opcode 1S, second opcode 2S+1N). /// 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(); let mut off = insn.offset11();
if insn.bit(consts::flags::FLAG_LOW_OFFSET) { if FLAG_LOW_OFFSET {
off = off << 1; off = off << 1;
let next_pc = (self.pc - 2) | 1; let next_pc = (self.pc - 2) | 1;
self.pc = ((self.gpr[REG_LR] & !1) as i32).wrapping_add(off) as u32; self.pc = ((self.gpr[REG_LR] & !1) as i32).wrapping_add(off) as u32;

View file

@ -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 /// A trait which provides methods to extract thumb instruction fields
pub trait ThumbDecodeHelper { pub trait ThumbDecodeHelper {
// Consts // Consts
@ -367,7 +352,7 @@ macro_rules! thumb_decode_helper_impl {
#[inline] #[inline]
fn sword7(&self) -> i32 { fn sword7(&self) -> i32 {
let imm7 = *self & 0x7f; let imm7 = *self & 0x7f;
if self.bit(consts::flags::FLAG_S) { if self.bit(7) {
-((imm7 << 2) as i32) -((imm7 << 2) as i32)
} else { } else {
(imm7 << 2) as i32 (imm7 << 2) as i32