#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use super::super::{
cpsr::{borrow_from, carry_from},
gba::GBA,
utils::AsBoolSlice,
};
use alloc::format;
use alloc::string::String;
pub type ThumbError = String;
pub type ThumbResult = Result<(), ThumbError>;
pub type ThumbInstruction = fn(&mut GBA, u16) -> ThumbResult;
#[inline]
fn as_11th_bit(opcode: u16) -> bool {
(opcode & 0x0800) != 0
}
#[inline]
fn as_low_11bits(opcode: u16) -> u16 {
opcode & 0x07FF
}
#[inline]
fn as_bits_8_to_10(opcode: u16) -> usize {
((opcode >> 8) & 0x0007) as usize
}
#[inline]
fn as_bits_6_to_10(opcode: u16) -> u16 {
(opcode >> 6) & 0x001F
}
#[inline]
#[allow(clippy::identity_op)]
fn as_lower_3bit_values(opcode: u16) -> (usize, usize, usize, usize) {
(
((opcode & 0x0E00) >> 9) as usize,
((opcode & 0x01C0) >> 6) as usize,
((opcode & 0x0038) >> 3) as usize,
((opcode & 0x0007) >> 0) as usize,
)
}
#[inline]
fn as_low_byte(opcode: u16) -> u8 {
opcode as u8
}
#[inline]
fn as_bits_11_and_12(opcode: u16) -> u16 {
(opcode >> 11) & 0x3
}
#[inline]
fn sign_flag(N: u32) -> bool {
(N >> 31) == 1
}
fn move_shifted_register(gba: &mut GBA, opcode: u16) -> ThumbResult {
let operation = as_bits_11_and_12(opcode);
let (_, _, rs, rd) = as_lower_3bit_values(opcode);
let offset = as_bits_6_to_10(opcode);
let operand = gba.regs[rs];
let res = match operation {
0 => operand << offset,
1 => operand >> offset,
2 => ((operand as i32) >> offset) as u32,
3 => unimplemented!("reserved"),
_ => unimplemented!("Impossible?"), };
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(
res,
if offset == 0 && operation == 0 { None } else { Some((operand >> (32 - offset)) & 1 != 0) },
None,
);
gba.clocks += 0; Ok(())
}
fn add_or_sub(gba: &mut GBA, opcode: u16) -> ThumbResult {
let (_, rn, rs, rd) = as_lower_3bit_values(opcode);
let immediate = (opcode & 0x0400) != 0;
let is_substraction = (opcode & 0x0200) != 0;
let first_operand = gba.regs[rs];
let second_operand = if immediate { rn as u32 } else { gba.regs[rn] };
gba.regs[rd] = if is_substraction {
let (result, overflow) = first_operand.overflowing_sub(second_operand);
gba.cpsr.set_all_status_flags(
result,
Some(borrow_from(first_operand, second_operand)),
Some(overflow),
);
result
} else {
let (result, overflow) = first_operand.overflowing_add(second_operand);
gba.cpsr.set_all_status_flags(result, Some(overflow), Some(false));
result
};
gba.clocks += 0; Ok(())
}
fn immediate_operation(gba: &mut GBA, opcode: u16) -> ThumbResult {
let rd = as_bits_8_to_10(opcode);
let offset = u32::from(as_low_byte(opcode));
let operation = as_bits_11_and_12(opcode);
let rd_val = gba.regs[rd];
if operation == 1 {
let (res, overflow) = rd_val.overflowing_sub(offset);
gba.cpsr.set_all_status_flags(res, Some(offset <= rd_val), Some(overflow));
gba.clocks += 0; return Ok(());
}
gba.regs[rd] = match operation {
0 => {
gba.cpsr.set_all_status_flags(offset, None, None);
offset
}
2 => {
let (res, overflow) = rd_val.overflowing_add(offset);
gba.cpsr.set_all_status_flags_for_addition(res, rd_val, offset, Some(overflow));
res
}
3 => {
let (res, overflow) = rd_val.overflowing_sub(offset);
gba.cpsr.set_all_status_flags(res, Some(offset <= rd_val), Some(overflow));
res
}
_ => unimplemented!(), };
gba.clocks += 0; Ok(())
}
fn alu_operation(gba: &mut GBA, opcode: u16) -> ThumbResult {
let (_, _, rs, rd) = as_lower_3bit_values(opcode);
let operation = ((opcode >> 6) & 0x000F) as u8;
let (rs_val, rd_val) = (gba.regs[rs], gba.regs[rd]);
match operation {
0x0 => {
let res = rd_val & rs_val;
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, None, None);
}
0x1 => {
let res = rd_val ^ rs_val;
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, None, None);
}
0x2 => {
let rs_val = rs_val & 0x00FF;
let (result, _) = rd_val.overflowing_shl(rs_val);
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(
result,
if rs_val == 0 { None } else { Some((rd_val >> (32 - rs_val)) & 1 != 0) },
None,
);
}
0x3 => {
let rs_val = rs_val & 0x00FF;
let result = rd_val >> rs_val;
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(
result,
if rs_val == 0 { None } else { Some((rd_val >> (rs_val - 1)) & 1 != 0) },
None,
);
}
0x4 => {
let rs_val = rs_val & 0x00FF;
let result = ((rd_val as i32) << rs_val) as u32;
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(
result,
if rs_val == 0 { None } else { Some((rd_val >> (rs_val - 1)) & 1 != 0) },
None,
);
}
0x5 => {
let (result, overflow) = rd_val.overflowing_add(rs_val);
let (result, overflow2) = result.overflowing_add(gba.cpsr.carry_flag() as u32);
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(
result,
Some(carry_from(rd_val, rs_val + (gba.cpsr.carry_flag() as u32), result)), Some(overflow || overflow2),
);
}
0x6 => {
let (result, overflow) = rd_val.overflowing_sub(rs_val);
let (result, overflow2) = result.overflowing_sub((!gba.cpsr.carry_flag()) as u32);
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(
result,
Some(
borrow_from(rd_val, rs_val.overflowing_add((!gba.cpsr.carry_flag()) as u32).0)
|| overflow2,
),
Some(overflow || overflow2),
);
}
0x7 => {
let rs_val = rs_val & 0x00FF;
if rs_val == 0 {
gba.cpsr.set_neutral_flags(rd_val);
} else {
let r4 = rs_val & 0x1F;
if r4 > 0 {
let result = rd_val.rotate_right(r4);
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(result, Some(((rd_val >> (r4 - 1)) & 1) != 0), None);
} else {
gba.cpsr.set_neutral_flags(rd_val);
gba.cpsr.set_carry_flag((rd_val >> 31) != 0);
}
}
}
0x8 => {
gba.cpsr.set_all_status_flags(rd_val & rs_val, None, None);
}
0x9 => {
let (result, overflow) = 0u32.overflowing_sub(rs_val);
gba.regs[rd] = result;
gba.cpsr.set_all_status_flags(result, Some(borrow_from(0, rs_val)), Some(overflow));
}
0xA => {
let (res, _) = rd_val.overflowing_sub(rs_val);
gba.cpsr.set_all_status_flags(
res,
Some(borrow_from(rd_val, rs_val)),
Some(((rd_val ^ rs_val) as i32) < 0 && ((rs_val ^ res) as i32) >= 0),
);
}
0xB => {
let (res, overflow) = rd_val.overflowing_add(rs_val);
gba.cpsr.set_all_status_flags(res, Some(carry_from(rd_val, rs_val, res)), Some(overflow));
}
0xC => {
let res = rd_val | rs_val;
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, None, None);
}
0xD => {
let res = rd_val.overflowing_mul(rs_val).0;
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, Some(false), None);
}
0xE => {
let res = rd_val & (!rs_val);
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, None, None);
}
0xF => {
let res = !rs_val;
gba.regs[rd] = res;
gba.cpsr.set_all_status_flags(res, None, None);
}
_ => unimplemented!("Impossible"),
}
gba.clocks += 0; Ok(())
}
fn high_register_operations_or_bx(gba: &mut GBA, opcode: u16) -> ThumbResult {
let (_, _, rs, rd) = as_lower_3bit_values(opcode);
let rs_most_significant_bit = (opcode & 0x0040) >> 6;
let rd_most_significant_bit = (opcode & 0x0080) >> 7;
let operation = (opcode & 0x0300) >> 8;
let rs = rs + ((rs_most_significant_bit as usize) << 3);
let rd = rd + ((rd_most_significant_bit as usize) << 3);
match operation {
0 => {
let (res, _) = gba.regs[rd].overflowing_add(gba.regs[rs]);
gba.regs[rd] = res;
}
1 => {
let (op1, op2) = (gba.regs[rd], gba.regs[rs]);
let (res, overflow) = op1.overflowing_sub(op2);
gba.cpsr.set_all_status_flags(res, Some(borrow_from(op1, op2)), Some(overflow));
}
2 => {
gba.regs[rd] = if rs == 15 { gba.regs[15].overflowing_add(2).0 } else { gba.regs[rs] };
}
3 => {
let rs_val = gba.regs[rs];
gba.cpsr.set_thumb_state_flag((rs_val & 0x0000_0001) != 0);
if rs == 15 {
gba.regs[15] = rs_val & 0xFFFF_FFFC;
} else {
gba.regs[15] = rs_val & 0xFFFF_FFFE;
}
}
_ => unimplemented!("Impossible"), }
gba.clocks += 0; Ok(())
}
fn pc_relative_load(gba: &mut GBA, opcode: u16) -> ThumbResult {
let rd = as_bits_8_to_10(opcode);
let byte = u32::from(as_low_byte(opcode));
let offset = byte << 2; gba.regs[rd] =
gba.fetch_u32(((gba.regs[15].overflowing_add(2).0) & 0xFFFF_FFFC).overflowing_add(offset).0);
gba.clocks += 0; Ok(())
}
fn load_or_store_with_relative_offset(gba: &mut GBA, opcode: u16) -> ThumbResult {
let (_, ro, rb, rd) = as_lower_3bit_values(opcode);
let is_load = as_11th_bit(opcode);
let is_byte_else_word = (opcode & 0x0400) != 0;
let (addr, _) = gba.regs[ro].overflowing_add(gba.regs[rb]);
if is_load {
if is_byte_else_word {
gba.write_u8(addr, gba.regs[rd] as u8);
} else {
gba.write_u32(addr, gba.regs[rd]);
}
} else {
gba.regs[rd] =
if is_byte_else_word { u32::from(gba.fetch_byte(addr)) } else { gba.fetch_u32(addr) };
}
gba.clocks += 0; Ok(())
}
fn load_or_store_sign_extended_byte_or_halfword(gba: &mut GBA, opcode: u16) -> ThumbResult {
let H = as_11th_bit(opcode);
let (_, ro, rb, rd) = as_lower_3bit_values(opcode);
let sign_extend = (opcode & 0x0400) != 0;
let addr = gba.regs[rb].overflowing_add(gba.regs[ro]).0;
if !H && !sign_extend {
gba.write_u16(addr, gba.regs[rd] as u16);
gba.clocks += 0; return Ok(());
}
gba.regs[rd] = if H {
if sign_extend {
(i32::from(gba.fetch_u16(addr) as i16) as u32) } else {
u32::from(gba.fetch_u16(addr))
}
} else {
(i32::from(gba.fetch_byte(addr) as i8) as u32) };
gba.clocks += 0; Ok(())
}
fn load_or_store_with_immediate_offset(gba: &mut GBA, opcode: u16) -> ThumbResult {
let is_byte_else_word = (opcode & 0x1000) != 0;
let is_load_else_store = as_11th_bit(opcode);
let (_, _, rb, rd) = as_lower_3bit_values(opcode);
let offset = u32::from(as_bits_6_to_10(opcode));
let rb_val = gba.regs[rb];
if is_byte_else_word {
let addr = offset.overflowing_add(rb_val).0;
if is_load_else_store {
gba.regs[rd] = u32::from(gba.fetch_byte(addr));
} else {
gba.write_u8(addr, gba.regs[rd] as u8);
}
} else {
let offset = offset << 2;
let addr = offset.overflowing_add(rb_val).0;
if is_load_else_store {
gba.regs[rd] = gba.fetch_u32(addr);
} else {
gba.write_u32(addr, gba.regs[rd]);
}
}
gba.clocks += 0; Ok(())
}
fn load_or_store_halfword(gba: &mut GBA, opcode: u16) -> ThumbResult {
let is_load_else_store = as_11th_bit(opcode);
let (_, _, rb, rd) = as_lower_3bit_values(opcode);
let offset = as_bits_6_to_10(opcode);
let offset = u32::from(offset) << 1;
let addr = offset.overflowing_add(gba.regs[rb]).0;
if is_load_else_store {
gba.regs[rd] = u32::from(gba.fetch_u16(addr));
gba.write_u16(addr, gba.regs[rd] as u16);
}
gba.clocks += 0; Ok(())
}
fn stack_pointer_relative_load_or_store(gba: &mut GBA, opcode: u16) -> ThumbResult {
let is_load_else_store = as_11th_bit(opcode);
let byte = as_low_byte(opcode);
let offset = u32::from(byte) << 2;
let rd = as_bits_8_to_10(opcode);
let addr = gba.regs[13].overflowing_add(offset).0;
if is_load_else_store {
gba.regs[rd] = gba.fetch_u32(addr);
} else {
gba.write_u32(addr, gba.regs[rd]);
}
gba.clocks += 0; Ok(())
}
fn load_address(gba: &mut GBA, opcode: u16) -> ThumbResult {
let load_sp_else_pc = as_11th_bit(opcode);
let byte = as_low_byte(opcode);
let nn = u32::from(byte) << 2;
let rd = as_bits_8_to_10(opcode);
gba.regs[rd] = if load_sp_else_pc {
gba.regs[13].overflowing_add(nn).0
} else {
((gba.regs[15].overflowing_add(4).0) & !2).overflowing_add(nn).0
};
gba.clocks += 0; Ok(())
}
fn add_or_sub_offset_to_stack_pointer(gba: &mut GBA, opcode: u16) -> ThumbResult {
let byte = as_low_byte(opcode);
let substract_else_add = (byte & 0x80) != 0;
let byte7 = byte & 0x0000_007F;
let offset = u32::from(byte7) << 2;
let sp = gba.regs[13];
gba.regs[13] =
if substract_else_add { sp.overflowing_sub(offset).0 } else { sp.overflowing_add(offset).0 };
gba.clocks += 0; Ok(())
}
fn push_or_pop(gba: &mut GBA, opcode: u16) -> ThumbResult {
let is_pop_else_push = as_11th_bit(opcode);
let pc_or_lr_flag = (opcode & 0x0100) != 0;
let rlist = as_low_byte(opcode);
let mut sp = gba.regs[13];
if is_pop_else_push {
for (idx, _) in rlist.as_bools().iter().rev().enumerate().filter(|(_, &boolean)| boolean) {
sp = sp.overflowing_add(4).0;
gba.regs[idx] = gba.fetch_u32(sp);
}
if pc_or_lr_flag {
sp = sp.overflowing_add(4).0;
gba.regs[15] = gba.fetch_u32(sp) & (!0x0000_0001); }
} else {
if pc_or_lr_flag {
gba.write_u32(sp, gba.regs[14]); sp = sp.overflowing_sub(4).0;
}
for (idx, _) in rlist.as_bools().iter().rev().enumerate().filter(|(_, &boolean)| boolean).rev()
{
gba.write_u32(sp, gba.regs[idx]);
sp = sp.overflowing_sub(4).0;
}
}
gba.regs[13] = sp;
gba.clocks += 0; Ok(())
}
fn multiple_loads_or_stores(gba: &mut GBA, opcode: u16) -> ThumbResult {
let rb = as_bits_8_to_10(opcode);
let is_load_else_store = as_11th_bit(opcode);
let rlist = as_low_byte(opcode);
let mut addr = gba.regs[rb];
let operation: fn(&mut GBA, u32, usize) = if is_load_else_store {
|gba: &mut GBA, addr, reg| {
gba.regs[reg] = gba.fetch_u32(addr);
}
} else {
|gba: &mut GBA, addr, reg| {
gba.write_u32(addr, gba.regs[reg]);
}
};
for (idx, _) in rlist.as_bools().iter().rev().enumerate().filter(|(_, &boolean)| boolean) {
operation(gba, addr, idx);
addr = addr.overflowing_add(4).0;
}
gba.regs[rb] = addr;
gba.clocks += 0; Ok(())
}
fn conditional_branch(gba: &mut GBA, opcode: u16) -> ThumbResult {
let offset = u32::from(as_low_byte(opcode)) << 1;
let (is_negative, offset) = ((offset & 0x0000_0100) != 0, offset & 0x0000_00FF);
let cond = (opcode >> 8) & 0x000F;
let should_not_jump = super::arm::check_cond(gba, u32::from(cond) << 28);
if should_not_jump {
gba.clocks += 0; return Ok(());
}
gba.regs[15] = if is_negative {
gba.regs[15].overflowing_sub((!offset) & 0x0000_00FE).0
} else {
gba.regs[15].overflowing_add(offset.overflowing_add(2).0).0
};
gba.clocks += 0; Ok(())
}
fn software_interrupt(gba: &mut GBA, opcode: u16) -> ThumbResult {
super::arm::software_interrupt(gba, u32::from(opcode) << 16)?;
gba.clocks += 0; Ok(())
}
fn branch(gba: &mut GBA, opcode: u16) -> ThumbResult {
let is_negative = (opcode & 0x0000_0400) != 0; let absolute_offset = ((opcode & 0x0000_03FF) as u32) << 1; let pc = gba.regs[15];
gba.regs[15] = if is_negative {
pc.overflowing_sub((!absolute_offset) & 0x0000_07FE).0
} else {
pc.overflowing_add(absolute_offset).0.overflowing_add(2).0
};
gba.clocks += 0; Ok(())
}
fn branch_and_link_or_link_and_exchange_first_opcode(gba: &mut GBA, opcode: u16) -> ThumbResult {
let upper_offset = (i32::from((as_low_11bits(opcode) as i16) << 5) << 7) as u32;
let pc = gba.regs[15];
gba.regs[14] = pc.overflowing_add(upper_offset).0.overflowing_add(2).0;
gba.clocks += 0; Ok(())
}
fn branch_and_link_or_link_and_exchange_second_opcode(gba: &mut GBA, opcode: u16) -> ThumbResult {
let lower_offset = u32::from(as_low_11bits(opcode) << 1);
let pc = gba.regs[15].overflowing_add(2).0;
gba.regs[15] = gba.regs[14].overflowing_add(lower_offset).0;
gba.regs[14] = pc.overflowing_sub(1).0;
gba.clocks += 0; Ok(())
}
fn decode_thumb(opcode: u16) -> Result<ThumbInstruction, ThumbError> {
let bits15_8 = (opcode >> 8) as u8;
const T: bool = true;
const F: bool = false;
Ok(match bits15_8.as_bools() {
[F, F, F, T, T, _, _, _] => add_or_sub,
[F, F, F, _, _, _, _, _] => move_shifted_register,
[F, F, T, _, _, _, _, _] => immediate_operation,
[F, T, F, F, F, F, _, _] => alu_operation,
[F, T, F, F, F, T, _, _] => high_register_operations_or_bx,
[F, T, F, F, T, _, _, _] => pc_relative_load,
[F, T, F, T, _, _, F, _] => load_or_store_with_relative_offset,
[F, T, F, T, _, _, T, _] => load_or_store_sign_extended_byte_or_halfword,
[F, T, T, _, _, _, _, _] => load_or_store_with_immediate_offset,
[T, F, F, F, _, _, _, _] => load_or_store_halfword,
[T, F, F, T, _, _, _, _] => stack_pointer_relative_load_or_store,
[T, F, T, F, _, _, _, _] => load_address,
[T, F, T, T, F, F, F, F] => add_or_sub_offset_to_stack_pointer,
[T, F, T, T, _, T, F, _] => push_or_pop,
[T, T, F, F, _, _, _, _] => multiple_loads_or_stores,
[T, T, F, T, T, T, T, T] => software_interrupt,
[T, T, F, T, _, _, _, _] => conditional_branch,
[T, T, T, F, F, _, _, _] => branch,
[T, T, T, T, F, _, _, _] => branch_and_link_or_link_and_exchange_first_opcode,
[T, T, T, T, T, _, _, _] => branch_and_link_or_link_and_exchange_second_opcode,
_ => return Err(format!("Invalid thumb opcode: {:x}", opcode)),
})
}
pub(crate) fn execute_one_instruction(gba: &mut GBA) -> ThumbResult {
let pc = *gba.pc();
let opcode = gba.fetch_u16(pc);
*gba.pc() = pc.overflowing_add(2).0;
(gba.instruction_hook_with_opcode)(gba, u32::from(opcode));
let instruction = decode_thumb(opcode)?;
instruction(gba, opcode)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::{AsBoolSlice, *};
extern crate test;
use test::Bencher;
fn none(_: &mut GBA, _: u16) -> ThumbResult {
Ok(())
}
fn decode_thumb(opcode: u16) -> ThumbInstruction {
let bits15_8 = (opcode >> 8) as u8;
const T: bool = true;
const F: bool = false;
match bits15_8.as_bools() {
[F, F, F, T, T, T, _, _] => add_or_sub,
[F, F, F, _, _, _, _, _] => move_shifted_register,
[F, F, T, _, _, _, _, _] => immediate_operation,
[F, T, F, F, F, F, _, _] => alu_operation,
[F, T, F, F, F, T, _, _] => high_register_operations_or_bx,
[F, T, F, F, T, _, _, _] => pc_relative_load,
[F, T, F, T, _, _, F, _] => load_or_store_with_relative_offset,
[F, T, F, T, _, _, T, _] => load_or_store_sign_extended_byte_or_halfword,
[F, T, T, _, _, _, _, _] => load_or_store_with_immediate_offset,
[T, F, F, F, _, _, _, _] => load_or_store_halfword,
[T, F, F, T, _, _, _, _] => stack_pointer_relative_load_or_store,
[T, F, T, F, _, _, _, _] => load_address,
[T, F, T, T, F, F, F, F] => add_or_sub_offset_to_stack_pointer,
[T, F, T, T, _, T, F, _] => push_or_pop,
[T, T, F, F, _, _, _, _] => multiple_loads_or_stores,
[T, T, F, T, T, T, T, T] => software_interrupt,
[T, T, F, T, _, _, _, _] => conditional_branch,
[T, T, T, F, F, _, _, _] => branch,
[T, T, T, T, _, _, _, _] => branch_and_link_or_link_and_exchange_first_opcode,
_ => none,
}
}
#[allow(clippy::identity_op)]
fn decode_thumb_raw(opcode: u16) -> ThumbInstruction {
let bits15_8 = (opcode >> 8) as u8;
const T: bool = true;
const F: bool = false;
match bits15_8 {
x if (x & 0b1111_1100) == 0b0001_1100 => add_or_sub,
x if (x & 0b1110_0000) == 0b0000_0000 => move_shifted_register,
x if (x & 0b1110_0000) == 0b0010_0000 => immediate_operation,
x if (x & 0b1111_1100) == 0b0100_0000 => alu_operation,
x if (x & 0b1111_1100) == 0b0100_0100 => high_register_operations_or_bx,
x if (x & 0b1111_1000) == 0b0100_1000 => pc_relative_load,
x if (x & 0b1111_0010) == 0b0101_0000 => load_or_store_with_relative_offset,
x if (x & 0b1111_0010) == 0b0101_0010 => load_or_store_sign_extended_byte_or_halfword,
x if (x & 0b1110_0000) == 0b0110_0000 => load_or_store_with_immediate_offset,
x if (x & 0b1111_0000) == 0b1000_0000 => load_or_store_halfword,
x if (x & 0b1111_0000) == 0b1001_0000 => stack_pointer_relative_load_or_store,
x if (x & 0b1111_0000) == 0b1010_0000 => load_address,
x if (x & 0b1111_1111) == 0b1011_0000 => add_or_sub_offset_to_stack_pointer,
x if (x & 0b1011_0110) == 0b1011_0100 => push_or_pop,
x if (x & 0b1111_0000) == 0b1100_0000 => multiple_loads_or_stores,
x if (x & 0b1111_1111) == 0b0110_1111 => software_interrupt,
x if (x & 0b1111_0000) == 0b1101_0000 => conditional_branch,
x if (x & 0b1111_1000) == 0b1110_0000 => branch,
x if (x & 0b1111_0000) == 0b1111_0000 => branch_and_link_or_link_and_exchange_first_opcode,
_ => none,
}
}
#[bench]
fn bench_decode_thumb(b: &mut Bencher) {
b.iter(|| {
test::black_box((0..100_000u32).fold(0u32, |old, x| {
test::black_box(old);
test::black_box(decode_thumb(x as u16));
0u32
}))
});
}
#[bench]
fn bench_decode_thumb_raw(b: &mut Bencher) {
b.iter(|| {
test::black_box((0..100_000u32).fold(0u32, |old, x| {
test::black_box(old);
test::black_box(decode_thumb_raw(x as u16));
0u32
}))
});
}
}