#![allow(dead_code)]
use alloc::format;
use alloc::string::String;
use super::super::{
cpsr::CPSR,
cpu_mode::CpuMode,
gba::{GBA, SWI_HANDLER},
utils::AsBoolSlice,
};
pub type ARMError = String;
pub type ARMResult = Result<(), ARMError>;
pub type ARMInstruction = fn(&mut GBA, u32) -> ARMResult;
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub(crate) enum Cond {
EQ,
NE,
CS_HS,
CC_LO,
MI,
PL,
VS,
VC,
HI,
LS,
GE,
LT,
GT,
LE,
AL,
NV,
}
#[inline]
pub(crate) fn opcode_to_cond(opcode: u32) -> Cond {
match (opcode >> 28) as u8 {
0x0 => Cond::EQ,
0x1 => Cond::NE,
0x2 => Cond::CS_HS,
0x3 => Cond::CC_LO,
0x4 => Cond::MI,
0x5 => Cond::PL,
0x6 => Cond::VS,
0x7 => Cond::VC,
0x8 => Cond::HI,
0x9 => Cond::LS,
0xA => Cond::GE,
0xB => Cond::LT,
0xC => Cond::GT,
0xD => Cond::LE,
0xE => Cond::AL,
0xF => Cond::NV,
_ => panic!("This is impossible"), }
}
#[inline]
pub(crate) fn check_cond(gba: &mut GBA, opcode: u32) -> bool {
let applies = match opcode_to_cond(opcode) {
Cond::EQ => gba.cpsr.zero_flag(),
Cond::NE => !gba.cpsr.zero_flag(),
Cond::CS_HS => gba.cpsr.carry_flag(),
Cond::CC_LO => !gba.cpsr.carry_flag(),
Cond::MI => gba.cpsr.negative_flag(),
Cond::PL => !gba.cpsr.negative_flag(),
Cond::VS => gba.cpsr.overflow_flag(),
Cond::VC => !gba.cpsr.overflow_flag(),
Cond::HI => gba.cpsr.carry_flag() && !gba.cpsr.zero_flag(),
Cond::LS => !gba.cpsr.carry_flag() || gba.cpsr.zero_flag(),
Cond::GE => gba.cpsr.negative_flag() == gba.cpsr.overflow_flag(),
Cond::LT => gba.cpsr.negative_flag() != gba.cpsr.overflow_flag(),
Cond::GT => gba.cpsr.negative_flag() == gba.cpsr.overflow_flag() && !gba.cpsr.zero_flag(),
Cond::LE => gba.cpsr.negative_flag() != gba.cpsr.overflow_flag() && gba.cpsr.zero_flag(),
Cond::AL => true,
Cond::NV => false,
};
if applies {
gba.clocks += 1;
}
!applies
}
#[inline]
#[allow(clippy::identity_op)]
fn as_u8_nibbles(opcode: u32) -> (u8, u8, u8, u8, u8) {
(
((opcode & 0x000F_0000) >> 16) as u8,
((opcode & 0x0000_F000) >> 12) as u8,
((opcode & 0x0000_0F00) >> 8) as u8,
((opcode & 0x0000_00F0) >> 4) as u8,
((opcode & 0x0000_000F) >> 0) as u8,
)
}
#[inline]
fn as_usize_nibbles(opcode: u32) -> (usize, usize, usize, usize, usize) {
let (b0, b1, b2, b3, b4) = as_u8_nibbles(opcode);
(b0 as usize, b1 as usize, b2 as usize, b3 as usize, b4 as usize)
}
#[inline]
fn as_flags(opcode: u32) -> (bool, bool, bool, bool, bool) {
(
(opcode & 0x0100_0000) != 0,
(opcode & 0x0080_0000) != 0,
(opcode & 0x0040_0000) != 0,
(opcode & 0x0020_0000) != 0,
(opcode & 0x0010_0000) != 0,
)
}
#[inline]
fn as_extra_flag(opcode: u32) -> bool {
(opcode & 0x0200_0000) != 0
}
fn branch_and_exchange(gba: &mut GBA, opcode: u32) -> ARMResult {
gba.clocks += 3; let reg_n = opcode & 0x0000_000F;
let mut jmp_addr = gba.regs[reg_n as usize];
let switch_to_thumb = (jmp_addr & 0x0000_0001) != 0;
if switch_to_thumb {
jmp_addr -= 1;
gba.cpsr.set_thumb_state_flag(true);
}
*gba.pc() = jmp_addr;
Ok(())
}
fn branch_or_branch_and_link(gba: &mut GBA, opcode: u32) -> ARMResult {
gba.clocks += 3; let offset = (opcode & 0x007F_FFFF) << 2; let is_positive = (opcode & 0x0080_0000) == 0;
let is_branch_and_link = (opcode & 0x0100_0000) != 0;
let pc = gba.regs[15];
if is_branch_and_link {
*gba.LR() = pc;
}
gba.regs[15] = 4u32
.overflowing_add(if is_positive {
pc.overflowing_add(offset).0
} else {
pc.overflowing_sub(!(offset | 0xFF00_0003) + 4).0
})
.0;
Ok(())
}
pub(crate) fn software_interrupt(gba: &mut GBA, _: u32) -> ARMResult {
gba.set_mode(CpuMode::Supervisor);
*gba.pc() = SWI_HANDLER;
gba.cpsr.set_thumb_state_flag(false); gba.cpsr.set_irq_disabled_flag(true);
gba.clocks += 0; Ok(())
}
fn single_data_swap(gba: &mut GBA, opcode: u32) -> ARMResult {
let (_, _, is_byte_else_word, _, _) = as_flags(opcode);
let (rn, rd, _, _, rm) = as_usize_nibbles(opcode);
if is_byte_else_word {
*gba.reg_mut(rd) = (*gba.reg_mut(rn)) & 0x0000_00FF;
*gba.reg_mut(rn) = (*gba.reg_mut(rm)) & 0x0000_00FF;
} else {
*gba.reg_mut(rd) = *gba.reg_mut(rn);
*gba.reg_mut(rn) = *gba.reg_mut(rm);
}
gba.clocks += 0; Ok(())
}
fn multiply_long(gba: &mut GBA, opcode: u32) -> ARMResult {
let (_, _, signed, accumulate, set_cond_flags) = as_flags(opcode);
let (rdhigh, rdlow, rn, _, rm) = as_usize_nibbles(opcode);
let res: u64 = if signed {
return Err(String::from("No support for signed long multiplication"));
} else {
u64::from(gba.fetch_reg(rn)) * u64::from(gba.fetch_reg(rm))
};
*gba.reg_mut(rdhigh) = (res) as u32;
*gba.reg_mut(rdlow) = (res >> 32) as u32;
if set_cond_flags {
gba.cpsr.set_zero_flag(res == 0);
gba.cpsr.set_negative_flag((res & 0x8000_0000_0000_0000) != 0);
gba.cpsr.set_carry_flag(false); }
gba.clocks += 0; Ok(())
}
fn multiply(gba: &mut GBA, opcode: u32) -> ARMResult {
let (_, _, _, accumulate, set_cond_flags) = as_flags(opcode);
let (rd, rn, rs, _, rm) = as_usize_nibbles(opcode);
let res = if accumulate {
gba.fetch_reg(rn).overflowing_mul(gba.fetch_reg(rm)).0.overflowing_add(gba.fetch_reg(rd)).0
} else {
gba.fetch_reg(rn).overflowing_mul(gba.fetch_reg(rm)).0
};
*gba.reg_mut(rs) = res;
if set_cond_flags {
gba.cpsr.set_all_status_flags(res, Some(false), None);
}
gba.clocks += 0; Ok(())
}
#[allow(unused_variables)]
#[allow(clippy::many_single_char_names)]
fn halfword_data_transfer_register_offset(gba: &mut GBA, opcode: u32) -> ARMResult {
let (p, o, _, w, l) = as_flags(opcode);
let (rn, rd, _, sh, rm) = as_usize_nibbles(opcode);
let s = (sh & 0b0100) != 0;
let h = (sh & 0b0010) != 0;
gba.clocks += 0; unimplemented!()
}
#[allow(unused_variables)]
#[allow(clippy::many_single_char_names)]
fn halfword_data_transfer_immediate_offset(gba: &mut GBA, opcode: u32) -> ARMResult {
let (p, o, _, w, l) = as_flags(opcode);
let (rn, rd, offset, sh, rm) = as_usize_nibbles(opcode);
let s = (sh & 0b0100) != 0;
let h = (sh & 0b0010) != 0;
gba.clocks += 0; unimplemented!()
}
fn single_data_transfer(gba: &mut GBA, opcode: u32) -> ARMResult {
let shifted_register = as_extra_flag(opcode);
let (is_pre_offseted, is_up, is_byte_size, bit_21, is_load) = as_flags(opcode);
let (rn, rd, third_byte, second_byte, first_byte) = as_usize_nibbles(opcode);
let offset = if !shifted_register {
opcode & 0x0000_0FFF
} else {
let shift_amount = (third_byte as u32) * 2 + (((second_byte as u32) & 0x8) >> 3);
let shift_type = second_byte;
let register_value = gba.regs[first_byte];
match shift_type & 6 {
0 => register_value.overflowing_shl(shift_amount).0,
2 => register_value.overflowing_shr(shift_amount).0,
4 => (register_value as i32).overflowing_shr(shift_amount).0 as u32,
6 => register_value.rotate_right(shift_amount),
_ => {
return Err(format!(
"Invalid instruction SDis_thumb_state_flag(Single Data Transfer) shift type {}",
shift_type & 6
));
}
}
};
let mut addr =
(i64::from(gba.regs[rn]) + if is_up { i64::from(offset) } else { -i64::from(offset) }) as u32;
if rn == 15 {
addr += 4;
}
if is_load {
gba.regs[rd] = if is_byte_size {
u32::from(gba.fetch_byte(((addr) / 4u32) * 4u32))
} else {
gba.fetch_u32(addr)
};
} else if is_byte_size {
gba.write_u8(addr, gba.regs[rd] as u8);
} else {
gba.write_u32(addr, gba.regs[rd]);
}
if !is_pre_offseted || bit_21 {
gba.regs[rn] = addr;
}
gba.clocks += 0; Ok(())
}
fn block_data_transfer(gba: &mut GBA, opcode: u32) -> ARMResult {
let (is_pre_offseted, is_up, psr_or_user_mode, write_back, is_load_else_store) = as_flags(opcode);
let (rn, _, _, _, _) = as_usize_nibbles(opcode);
let (rlist2, rlist1) = (((opcode & 0x0000_FF00) >> 8) as u8, (opcode & 0x0000_00FF) as u8);
let rlists = [rlist1, rlist2];
let is_psr = psr_or_user_mode && is_load_else_store && ((rlist2 & 0x80) != 0);
let get_mut_reg: fn(&mut GBA, usize) -> &mut u32 = if psr_or_user_mode && !is_psr {
|gba, x| match x {
0..=7 => &mut gba.regs[x],
8..=12 => {
if gba.cpsr.mode() == CpuMode::FIQ {
&mut gba.fiq_only_banks[0][x - 8]
} else {
&mut gba.regs[x]
}
}
13..=15 => &mut gba.all_modes_banks[CpuMode::User.as_usize()][x - 13],
_ => unimplemented!(
"This should never happen. Attempt to access an invalid register number ({})",
x
),
}
} else {
|gba, x| &mut gba.regs[x]
};
let mut sp = *get_mut_reg(gba, rn);
let operation: fn(&mut u32, u32) = if !is_up {
|sp, offset| {
*sp -= offset;
}
} else {
|sp, offset| {
*sp += offset;
}
};
let is_push = !is_load_else_store;
if is_push {
for (byte_number, byte) in rlists.iter().enumerate().rev() {
for (bit_in_byte, &bit_is_set) in byte.as_bools().iter().rev().enumerate().rev() {
if bit_is_set {
let bit_number = byte_number * 8 + bit_in_byte;
if is_pre_offseted {
operation(&mut sp, 4);
}
let val = *get_mut_reg(gba, bit_number);
gba.write_u32(sp, val);
if !is_pre_offseted {
operation(&mut sp, 4);
}
}
}
}
} else {
for (byte_number, byte) in rlists.iter().enumerate() {
for (bit_in_byte, &bit_is_set) in byte.as_bools().iter().rev().enumerate() {
if bit_is_set {
let bit_number = byte_number * 8 + bit_in_byte;
if is_pre_offseted {
operation(&mut sp, 4);
}
*get_mut_reg(gba, bit_number) = gba.fetch_u32(sp);
if !is_pre_offseted {
operation(&mut sp, 4);
}
}
}
}
}
if write_back {
*get_mut_reg(gba, rn) = sp;
}
if is_psr {
gba.cpsr = *gba
.get_spsr_mut()
.ok_or("Cannot use ^(PSR) on arm BDT while in User or Priviledged(System) mode")?;
}
gba.clocks += 0; Ok(())
}
fn alu_operation(gba: &mut GBA, opcode: u32) -> ARMResult {
let immediate = as_extra_flag(opcode);
let (rn_num, rd, _, _, _) = as_usize_nibbles(opcode);
let (_, _, third_byte, second_byte, lowest_byte) = as_u8_nibbles(opcode);
let set_condition_flags = (opcode & 0x0010_0000) != 0 || rd == 15;
let mut rn = gba.regs[rn_num];
if rn_num == 15 {
rn = rn.overflowing_add(4).0; }
let operation = ((opcode & 0x01E0_0000) >> 21) as u8;
let (op2, shift_carry) = if immediate {
let ror_shift = u32::from(third_byte) << 1;
let val = u32::from(lowest_byte + second_byte * 16);
(val.rotate_right(ror_shift), Some((val & (1u32 << ror_shift)) != 0))
} else {
let register_value: u32 = gba.regs[lowest_byte as usize];
let shift_by_register = (opcode & 0x0000_0010) != 0;
let mut imm_shift_zero = false;
let shift_amount = if shift_by_register {
(gba.regs[third_byte as usize] & 0x0000_000F)
.overflowing_add(if third_byte == 15 { 4 } else { 0 })
.0
} else {
let shift_amount = (opcode & 0x0000_0F80) >> 7;
imm_shift_zero = shift_amount == 0;
shift_amount
};
let shift_type = ((opcode & 0x0000_0060) >> 5) as u8;
let (op2, shift_carry) = if imm_shift_zero {
match shift_type {
0 => (register_value, None),
1 => (0, Some((register_value & 0x8000_0000) != 0)),
2 => (
(register_value as i32).overflowing_shr(32).0 as u32,
Some((register_value & 0x8000_0000) != 0),
),
3 => unimplemented!("TODO: RRX"),
_ => unimplemented!("Impossible. We AND'ed 2 bits, there are 4 posibilities"),
}
} else {
match shift_type {
0 => (
register_value.overflowing_shl(shift_amount).0,
if shift_amount == 0 {
None
} else {
Some(((register_value >> (32 - shift_amount)) & 1) != 0)
},
),
1 => (
register_value.overflowing_shr(shift_amount).0,
if shift_amount != 0 {
Some(((register_value >> (shift_amount - 1)) & 1) != 0)
} else {
None
},
),
2 => (
(register_value as i32).overflowing_shr(shift_amount).0 as u32,
if shift_amount != 0 {
Some(((register_value >> (core::cmp::max(shift_amount, 32) - 1)) & 1) != 0)
} else {
None
},
),
3 => (
register_value.rotate_right(shift_amount),
if shift_amount != 0 {
Some(((register_value >> (shift_amount - 1)) & 1) != 0)
} else {
None
},
),
_ => unimplemented!("Impossible. We AND'ed 2 bits, there are 4 posibilities"),
}
};
(op2, if lowest_byte == 0 { None } else { shift_carry })
};
let res = &mut gba.regs[rd];
let cpsr = gba.cpsr;
let ref_cpsr = &mut gba.cpsr;
let mut set_all_flags = move |res: u32, carry: Option<bool>, overflow: Option<bool>| {
if set_condition_flags {
ref_cpsr.set_all_status_flags(res, carry, overflow);
}
};
match operation as u8 {
0 => {
*res = rn & op2;
set_all_flags(*res, shift_carry, None);
}
1 => {
*res = rn ^ op2;
set_all_flags(*res, shift_carry, None);
}
2 => {
let (result, overflow) = rn.overflowing_sub(op2);
*res = result;
set_all_flags(*res, Some(op2 <= rn), Some(overflow));
}
3 => {
let (result, overflow) = op2.overflowing_sub(rn);
*res = result;
set_all_flags(*res, Some(rn <= op2), Some(overflow));
}
4 => {
let (result, overflow) = op2.overflowing_add(rn);
*res = result;
set_all_flags(result, Some(CPSR::addition_carries(result, op2, rn)), Some(overflow));
}
5 => {
let (result, overflow) = op2.overflowing_add(rn);
let (result, overflow2) = result.overflowing_add(cpsr.carry_flag() as u32);
*res = result;
set_all_flags(
result,
Some(CPSR::addition_carries(result, op2, rn)),
Some(overflow || overflow2),
);
}
6 => {
let (result, overflow) = rn.overflowing_sub(op2);
let (result, overflow2) = result.overflowing_sub(1 - (cpsr.carry_flag() as u32));
*res = result;
set_all_flags(*res, Some(op2 <= rn), Some(overflow || overflow2));
}
7 => {
let (result, overflow) = op2.overflowing_sub(rn);
let (result, overflow2) = result.overflowing_sub(1 - (cpsr.carry_flag() as u32));
*res = result;
set_all_flags(*res, Some(rn <= op2), Some(overflow || overflow2));
}
8 => {
let res = rn & op2;
if rd == 15 {
set_all_flags(res, None, None);
} else {
set_all_flags(res, shift_carry, None);
}
}
9 => {
let res = rn ^ op2;
if rd == 15 {
set_all_flags(res, None, None);
} else {
set_all_flags(res, shift_carry, None);
}
}
10 => {
let (res, _) = rn.overflowing_sub(op2);
set_all_flags(res, Some(op2 <= rn), Some(false));
}
11 => {
let (res, v) = rn.overflowing_add(op2);
set_all_flags(res, Some(CPSR::addition_carries(res, rn, op2)), Some(v));
}
12 => {
*res = rn | op2;
set_all_flags(*res, shift_carry, None);
}
13 => {
*res = op2;
set_all_flags(*res, shift_carry, None);
}
14 => {
*res = rn & (!op2);
set_all_flags(*res, shift_carry, None);
}
15 => {
*res = !op2;
set_all_flags(*res, shift_carry, None);
}
_ => unimplemented!("Impossible. We AND'ed 4 bits, there are 16 posibilities"),
}
gba.clocks += 0; Ok(())
}
fn move_to_status_register(gba: &mut GBA, opcode: u32) -> ARMResult {
const FLAGS_MASK: u32 = 0xFF00_0000;
const STATE_MASK: u32 = 0x0000_0020;
const PRIVACY_MASK: u32 = 0x0000_00CF;
let immediate = as_extra_flag(opcode);
let (_, _, use_spsr, _, _) = as_flags(opcode);
let change_flags_fields = (opcode & 0x0008_0000) != 0; let change_control_fields = (opcode & 0x0001_0000) != 0; let operand = if immediate {
let (_, _, shift, second, first) = as_u8_nibbles(opcode);
(u32::from(first) + u32::from(second) * 16).rotate_right(2 * u32::from(shift))
} else {
gba.regs[(opcode & 0x0000_000F) as usize]
};
let current_mode = gba.cpsr.mode();
if use_spsr {
let spsr = gba.get_spsr_mut().ok_or("")?;
let old = spsr.0;
let mut valid_bits_mask: u32 = (if change_control_fields { STATE_MASK } else { 0 })
| (if change_flags_fields { FLAGS_MASK } else { 0 });
valid_bits_mask &= FLAGS_MASK | STATE_MASK | PRIVACY_MASK;
let invalid_bits_mask = !valid_bits_mask;
*spsr = CPSR((old & invalid_bits_mask) | (operand & valid_bits_mask));
} else {
let old = gba.cpsr.0;
let valid_bits_mask: u32 = (if change_control_fields { STATE_MASK } else { 0 })
| (if change_flags_fields { FLAGS_MASK } else { 0 });
let invalid_bits_mask = !valid_bits_mask;
gba.cpsr = CPSR((old & invalid_bits_mask) | (operand & valid_bits_mask));
if current_mode != CpuMode::User && change_control_fields {
gba.set_mode(CpuMode::from_byte(((operand & 0x0000_000F) | 0x0000_0010) as u8));
gba.cpsr = CPSR((gba.cpsr.0 & (!PRIVACY_MASK)) | (operand & PRIVACY_MASK));
}
};
Ok(())
}
fn move_to_register_from_status_register(gba: &mut GBA, opcode: u32) -> ARMResult {
let (_, _, use_spsr, _, _) = as_flags(opcode);
gba.regs[((opcode & 0x0000_F000) >> 12) as usize] = if use_spsr {
gba
.get_spsr_mut()
.ok_or("Cannot use ^(PSR) on arm MRS while in User or Priviledged(System) mode")?
.0
} else {
gba.cpsr.0
};
Ok(())
}
fn coprocessor_data_transfer(_: &mut GBA, _: u32) -> ARMResult {
unimplemented!("Coprocessor instructions are not supported")
}
fn coprocessor_data_operation(_: &mut GBA, _: u32) -> ARMResult {
unimplemented!("Coprocessor instructions are not supported")
}
fn coprocessor_register_transfer(_: &mut GBA, _: u32) -> ARMResult {
unimplemented!("Coprocessor instructions are not supported")
}
fn decode_arm(opcode: u32) -> Result<ARMInstruction, ARMError> {
let bits27_20 = (opcode >> 20) as u8;
let bits11_4 = (opcode >> 4) as u8;
const T: bool = true;
const F: bool = false;
Ok(match (bits27_20.as_bools(), bits11_4.as_bools()) {
([F, T, T, _, _, _, _, _], [_, _, _, _, _, _, _, T]) => unimplemented!(
"Unimplemented: Undefined instruction {:b} ({:x}) bits 27-20: {:x} bits 11-4: {:x}",
opcode,
opcode,
bits27_20,
bits11_4 ),
([F, T, _, _, _, _, _, _], _) => single_data_transfer,
([F, F, F, F, F, F, _, _], [_, _, _, _, T, F, F, T]) => multiply,
([F, F, F, F, T, _, _, _], [_, _, _, _, T, F, F, T]) => multiply_long,
([F, F, F, T, F, _, F, F], [F, F, F, F, T, F, F, T]) => single_data_swap,
([F, F, F, _, _, F, _, _], [F, F, F, F, T, _, _, T]) => halfword_data_transfer_register_offset,
([F, F, F, _, _, T, _, _], [F, F, F, F, T, _, _, T]) => halfword_data_transfer_immediate_offset,
([T, F, F, _, _, _, _, _], _) => block_data_transfer,
([T, T, F, _, _, _, _, _], _) => coprocessor_data_transfer,
([T, T, T, F, _, _, _, _], [_, _, _, F, _, _, _, _]) => coprocessor_data_operation,
([T, T, T, F, _, _, _, _], [_, _, _, T, _, _, _, _]) => coprocessor_register_transfer,
([T, T, T, T, _, _, _, _], _) => software_interrupt,
([T, F, T, _, _, _, _, _], _) => branch_or_branch_and_link,
([F, F, F, T, F, F, T, F], [T, T, T, T, F, F, F, T])
if (opcode & 0x000F_F000) == 0x000F_F000 =>
{
branch_and_exchange
}
([F, F, _, T, F, _, F, F], [F, F, F, F, F, F, F, F]) => move_to_register_from_status_register,
([F, F, _, T, F, _, T, F], _) => move_to_status_register,
([F, F, _, _, _, _, _, _], _) => alu_operation,
})
}
pub(crate) fn execute_one_instruction(gba: &mut GBA) -> ARMResult {
let pc = *gba.pc();
let opcode = gba.fetch_u32(pc);
*gba.pc() += 4;
(gba.instruction_hook_with_opcode)(gba, opcode);
if check_cond(gba, opcode) {
return Ok(());
}
let instruction = decode_arm(opcode)?;
instruction(gba, opcode)?;
Ok(())
}