dynarmic/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp

4250 lines
139 KiB
C++

// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#define CITRA_IGNORE_EXIT(x)
#ifdef _MSC_VER
#pragma warning(disable : 4244)
#endif
#include <algorithm>
#include <cstdio>
#include "common/assert.h"
#include "common/common_types.h"
//#include "common/logging/log.h"
//#include "common/microprofile.h"
//#include "core/memory.h"
//#include "core/hle/svc.h"
//#include "A32/skyeye_interpreter/disassembler/arm_disasm.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_run.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
#include "A32/skyeye_interpreter/skyeye_common/armsupp.h"
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h"
//#include "core/gdbstub/gdbstub.h"
#define LOG_INFO(...) do{}while(0)
#define LOG_TRACE(...) do{}while(0)
#define RM BITS(sht_oper, 0, 3)
#define RS BITS(sht_oper, 8, 11)
#define glue(x, y) x ## y
#define DPO(s) glue(DataProcessingOperands, s)
#define ROTATE_RIGHT(n, i, l) ((n << (l - i)) | (n >> i))
#define ROTATE_LEFT(n, i, l) ((n >> (l - i)) | (n << i))
#define ROTATE_RIGHT_32(n, i) ROTATE_RIGHT(n, i, 32)
#define ROTATE_LEFT_32(n, i) ROTATE_LEFT(n, i, 32)
static bool CondPassed(const ARMul_State* cpu, unsigned int cond) {
const bool n_flag = cpu->NFlag != 0;
const bool z_flag = cpu->ZFlag != 0;
const bool c_flag = cpu->CFlag != 0;
const bool v_flag = cpu->VFlag != 0;
switch (cond) {
case ConditionCode::EQ:
return z_flag;
case ConditionCode::NE:
return !z_flag;
case ConditionCode::CS:
return c_flag;
case ConditionCode::CC:
return !c_flag;
case ConditionCode::MI:
return n_flag;
case ConditionCode::PL:
return !n_flag;
case ConditionCode::VS:
return v_flag;
case ConditionCode::VC:
return !v_flag;
case ConditionCode::HI:
return (c_flag && !z_flag);
case ConditionCode::LS:
return (!c_flag || z_flag);
case ConditionCode::GE:
return (n_flag == v_flag);
case ConditionCode::LT:
return (n_flag != v_flag);
case ConditionCode::GT:
return (!z_flag && (n_flag == v_flag));
case ConditionCode::LE:
return (z_flag || (n_flag != v_flag));
case ConditionCode::AL:
case ConditionCode::NV: // Unconditional
return true;
}
return false;
}
static unsigned int DPO(Immediate)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int immed_8 = BITS(sht_oper, 0, 7);
unsigned int rotate_imm = BITS(sht_oper, 8, 11);
unsigned int shifter_operand = ROTATE_RIGHT_32(immed_8, rotate_imm * 2);
if (rotate_imm == 0)
cpu->shifter_carry_out = cpu->CFlag;
else
cpu->shifter_carry_out = BIT(shifter_operand, 31);
return shifter_operand;
}
static unsigned int DPO(Register)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
return shifter_operand;
}
static unsigned int DPO(LogicalShiftLeftByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
int shift_imm = BITS(sht_oper, 7, 11);
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand;
if (shift_imm == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
} else {
shifter_operand = rm << shift_imm;
cpu->shifter_carry_out = BIT(rm, 32 - shift_imm);
}
return shifter_operand;
}
static unsigned int DPO(LogicalShiftLeftByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
int shifter_operand;
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int rs = CHECK_READ_REG15(cpu, RS);
if (BITS(rs, 0, 7) == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
} else if (BITS(rs, 0, 7) < 32) {
shifter_operand = rm << BITS(rs, 0, 7);
cpu->shifter_carry_out = BIT(rm, 32 - BITS(rs, 0, 7));
} else if (BITS(rs, 0, 7) == 32) {
shifter_operand = 0;
cpu->shifter_carry_out = BIT(rm, 0);
} else {
shifter_operand = 0;
cpu->shifter_carry_out = 0;
}
return shifter_operand;
}
static unsigned int DPO(LogicalShiftRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand;
int shift_imm = BITS(sht_oper, 7, 11);
if (shift_imm == 0) {
shifter_operand = 0;
cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = rm >> shift_imm;
cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
}
return shifter_operand;
}
static unsigned int DPO(LogicalShiftRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rs = CHECK_READ_REG15(cpu, RS);
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand;
if (BITS(rs, 0, 7) == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
} else if (BITS(rs, 0, 7) < 32) {
shifter_operand = rm >> BITS(rs, 0, 7);
cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
} else if (BITS(rs, 0, 7) == 32) {
shifter_operand = 0;
cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = 0;
cpu->shifter_carry_out = 0;
}
return shifter_operand;
}
static unsigned int DPO(ArithmeticShiftRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand;
int shift_imm = BITS(sht_oper, 7, 11);
if (shift_imm == 0) {
if (BIT(rm, 31) == 0)
shifter_operand = 0;
else
shifter_operand = 0xFFFFFFFF;
cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = static_cast<int>(rm) >> shift_imm;
cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
}
return shifter_operand;
}
static unsigned int DPO(ArithmeticShiftRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rs = CHECK_READ_REG15(cpu, RS);
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int shifter_operand;
if (BITS(rs, 0, 7) == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
} else if (BITS(rs, 0, 7) < 32) {
shifter_operand = static_cast<int>(rm) >> BITS(rs, 0, 7);
cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
} else {
if (BIT(rm, 31) == 0)
shifter_operand = 0;
else
shifter_operand = 0xffffffff;
cpu->shifter_carry_out = BIT(rm, 31);
}
return shifter_operand;
}
static unsigned int DPO(RotateRightByImmediate)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int shifter_operand;
unsigned int rm = CHECK_READ_REG15(cpu, RM);
int shift_imm = BITS(sht_oper, 7, 11);
if (shift_imm == 0) {
shifter_operand = (cpu->CFlag << 31) | (rm >> 1);
cpu->shifter_carry_out = BIT(rm, 0);
} else {
shifter_operand = ROTATE_RIGHT_32(rm, shift_imm);
cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
}
return shifter_operand;
}
static unsigned int DPO(RotateRightByRegister)(ARMul_State* cpu, unsigned int sht_oper) {
unsigned int rm = CHECK_READ_REG15(cpu, RM);
unsigned int rs = CHECK_READ_REG15(cpu, RS);
unsigned int shifter_operand;
if (BITS(rs, 0, 7) == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = cpu->CFlag;
} else if (BITS(rs, 0, 4) == 0) {
shifter_operand = rm;
cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = ROTATE_RIGHT_32(rm, BITS(rs, 0, 4));
cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 4) - 1);
}
return shifter_operand;
}
#define DEBUG_MSG //LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0)
#define LnSWoUB(s) glue(LnSWoUB, s)
#define MLnS(s) glue(MLnS, s)
#define LdnStM(s) glue(LdnStM, s)
#define W_BIT BIT(inst, 21)
#define U_BIT BIT(inst, 23)
#define I_BIT BIT(inst, 25)
#define P_BIT BIT(inst, 24)
#define OFFSET_12 BITS(inst, 0, 11)
static void LnSWoUB(ImmediateOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int addr;
if (U_BIT)
addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
else
addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;
virt_addr = addr;
}
static void LnSWoUB(RegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
unsigned int addr;
if (U_BIT)
addr = rn + rm;
else
addr = rn - rm;
virt_addr = addr;
}
static void LnSWoUB(ImmediatePostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);
if (U_BIT)
cpu->Reg[Rn] += OFFSET_12;
else
cpu->Reg[Rn] -= OFFSET_12;
virt_addr = addr;
}
static void LnSWoUB(ImmediatePreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int addr;
if (U_BIT)
addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
else
addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31)))
cpu->Reg[Rn] = addr;
}
static void MLnS(RegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int addr;
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
if (U_BIT)
addr = rn + rm;
else
addr = rn - rm;
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31)))
cpu->Reg[Rn] = addr;
}
static void LnSWoUB(RegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
unsigned int addr;
if (U_BIT)
addr = rn + rm;
else
addr = rn - rm;
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31))) {
cpu->Reg[Rn] = addr;
}
}
static void LnSWoUB(ScaledRegisterPreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int shift = BITS(inst, 5, 6);
unsigned int shift_imm = BITS(inst, 7, 11);
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int index = 0;
unsigned int addr;
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
switch (shift) {
case 0:
index = rm << shift_imm;
break;
case 1:
if (shift_imm == 0) {
index = 0;
} else {
index = rm >> shift_imm;
}
break;
case 2:
if (shift_imm == 0) { // ASR #32
if (BIT(rm, 31) == 1)
index = 0xFFFFFFFF;
else
index = 0;
} else {
index = static_cast<int>(rm) >> shift_imm;
}
break;
case 3:
if (shift_imm == 0) {
index = (cpu->CFlag << 31) | (rm >> 1);
} else {
index = ROTATE_RIGHT_32(rm, shift_imm);
}
break;
}
if (U_BIT)
addr = rn + index;
else
addr = rn - index;
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31)))
cpu->Reg[Rn] = addr;
}
static void LnSWoUB(ScaledRegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int shift = BITS(inst, 5, 6);
unsigned int shift_imm = BITS(inst, 7, 11);
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int index = 0;
unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
switch (shift) {
case 0:
index = rm << shift_imm;
break;
case 1:
if (shift_imm == 0) {
index = 0;
} else {
index = rm >> shift_imm;
}
break;
case 2:
if (shift_imm == 0) { // ASR #32
if (BIT(rm, 31) == 1)
index = 0xFFFFFFFF;
else
index = 0;
} else {
index = static_cast<int>(rm) >> shift_imm;
}
break;
case 3:
if (shift_imm == 0) {
index = (cpu->CFlag << 31) | (rm >> 1);
} else {
index = ROTATE_RIGHT_32(rm, shift_imm);
}
break;
}
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31))) {
if (U_BIT)
cpu->Reg[Rn] += index;
else
cpu->Reg[Rn] -= index;
}
}
static void LnSWoUB(RegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
if (CondPassed(cpu, BITS(inst, 28, 31))) {
if (U_BIT) {
cpu->Reg[Rn] += rm;
} else {
cpu->Reg[Rn] -= rm;
}
}
}
static void MLnS(ImmediateOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int immedL = BITS(inst, 0, 3);
unsigned int immedH = BITS(inst, 8, 11);
unsigned int Rn = BITS(inst, 16, 19);
unsigned int addr;
unsigned int offset_8 = (immedH << 4) | immedL;
if (U_BIT)
addr = CHECK_READ_REG15_WA(cpu, Rn) + offset_8;
else
addr = CHECK_READ_REG15_WA(cpu, Rn) - offset_8;
virt_addr = addr;
}
static void MLnS(RegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int addr;
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
if (U_BIT)
addr = rn + rm;
else
addr = rn - rm;
virt_addr = addr;
}
static void MLnS(ImmediatePreIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int immedH = BITS(inst, 8, 11);
unsigned int immedL = BITS(inst, 0, 3);
unsigned int addr;
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int offset_8 = (immedH << 4) | immedL;
if (U_BIT)
addr = rn + offset_8;
else
addr = rn - offset_8;
virt_addr = addr;
if (CondPassed(cpu, BITS(inst, 28, 31)))
cpu->Reg[Rn] = addr;
}
static void MLnS(ImmediatePostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int immedH = BITS(inst, 8, 11);
unsigned int immedL = BITS(inst, 0, 3);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
virt_addr = rn;
if (CondPassed(cpu, BITS(inst, 28, 31))) {
unsigned int offset_8 = (immedH << 4) | immedL;
if (U_BIT)
rn += offset_8;
else
rn -= offset_8;
cpu->Reg[Rn] = rn;
}
}
static void MLnS(RegisterPostIndexed)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
if (CondPassed(cpu, BITS(inst, 28, 31))) {
if (U_BIT)
cpu->Reg[Rn] += rm;
else
cpu->Reg[Rn] -= rm;
}
}
static void LdnStM(DecrementBefore)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int i = BITS(inst, 0, 15);
int count = 0;
while (i) {
if (i & 1) count++;
i = i >> 1;
}
virt_addr = CHECK_READ_REG15_WA(cpu, Rn) - count * 4;
if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
cpu->Reg[Rn] -= count * 4;
}
static void LdnStM(IncrementBefore)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int i = BITS(inst, 0, 15);
int count = 0;
while (i) {
if (i & 1) count++;
i = i >> 1;
}
virt_addr = CHECK_READ_REG15_WA(cpu, Rn) + 4;
if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
cpu->Reg[Rn] += count * 4;
}
static void LdnStM(IncrementAfter)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int i = BITS(inst, 0, 15);
int count = 0;
while(i) {
if (i & 1) count++;
i = i >> 1;
}
virt_addr = CHECK_READ_REG15_WA(cpu, Rn);
if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
cpu->Reg[Rn] += count * 4;
}
static void LdnStM(DecrementAfter)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int Rn = BITS(inst, 16, 19);
unsigned int i = BITS(inst, 0, 15);
int count = 0;
while(i) {
if(i & 1) count++;
i = i >> 1;
}
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
unsigned int start_addr = rn - count * 4 + 4;
virt_addr = start_addr;
if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21)) {
cpu->Reg[Rn] -= count * 4;
}
}
static void LnSWoUB(ScaledRegisterOffset)(ARMul_State* cpu, unsigned int inst, unsigned int& virt_addr) {
unsigned int shift = BITS(inst, 5, 6);
unsigned int shift_imm = BITS(inst, 7, 11);
unsigned int Rn = BITS(inst, 16, 19);
unsigned int Rm = BITS(inst, 0, 3);
unsigned int index = 0;
unsigned int addr;
unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
switch (shift) {
case 0:
index = rm << shift_imm;
break;
case 1:
if (shift_imm == 0) {
index = 0;
} else {
index = rm >> shift_imm;
}
break;
case 2:
if (shift_imm == 0) { // ASR #32
if (BIT(rm, 31) == 1)
index = 0xFFFFFFFF;
else
index = 0;
} else {
index = static_cast<int>(rm) >> shift_imm;
}
break;
case 3:
if (shift_imm == 0) {
index = (cpu->CFlag << 31) | (rm >> 1);
} else {
index = ROTATE_RIGHT_32(rm, shift_imm);
}
break;
}
if (U_BIT) {
addr = rn + index;
} else
addr = rn - index;
virt_addr = addr;
}
shtop_fp_t GetShifterOp(unsigned int inst) {
if (BIT(inst, 25)) {
return DPO(Immediate);
} else if (BITS(inst, 4, 11) == 0) {
return DPO(Register);
} else if (BITS(inst, 4, 6) == 0) {
return DPO(LogicalShiftLeftByImmediate);
} else if (BITS(inst, 4, 7) == 1) {
return DPO(LogicalShiftLeftByRegister);
} else if (BITS(inst, 4, 6) == 2) {
return DPO(LogicalShiftRightByImmediate);
} else if (BITS(inst, 4, 7) == 3) {
return DPO(LogicalShiftRightByRegister);
} else if (BITS(inst, 4, 6) == 4) {
return DPO(ArithmeticShiftRightByImmediate);
} else if (BITS(inst, 4, 7) == 5) {
return DPO(ArithmeticShiftRightByRegister);
} else if (BITS(inst, 4, 6) == 6) {
return DPO(RotateRightByImmediate);
} else if (BITS(inst, 4, 7) == 7) {
return DPO(RotateRightByRegister);
}
return nullptr;
}
get_addr_fp_t GetAddressingOp(unsigned int inst) {
if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 0) {
return LnSWoUB(ImmediateOffset);
} else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
return LnSWoUB(RegisterOffset);
} else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
return LnSWoUB(ScaledRegisterOffset);
} else if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 1) {
return LnSWoUB(ImmediatePreIndexed);
} else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BITS(inst, 4, 11) == 0) {
return LnSWoUB(RegisterPreIndexed);
} else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BIT(inst, 4) == 0) {
return LnSWoUB(ScaledRegisterPreIndexed);
} else if (BITS(inst, 24, 27) == 4 && BIT(inst, 21) == 0) {
return LnSWoUB(ImmediatePostIndexed);
} else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
return LnSWoUB(RegisterPostIndexed);
} else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
return LnSWoUB(ScaledRegisterPostIndexed);
} else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(ImmediateOffset);
} else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(RegisterOffset);
} else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 3 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(ImmediatePreIndexed);
} else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 1 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(RegisterPreIndexed);
} else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(ImmediatePostIndexed);
} else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
return MLnS(RegisterPostIndexed);
} else if (BITS(inst, 23, 27) == 0x11) {
return LdnStM(IncrementAfter);
} else if (BITS(inst, 23, 27) == 0x13) {
return LdnStM(IncrementBefore);
} else if (BITS(inst, 23, 27) == 0x10) {
return LdnStM(DecrementAfter);
} else if (BITS(inst, 23, 27) == 0x12) {
return LdnStM(DecrementBefore);
}
return nullptr;
}
// Specialized for LDRT, LDRBT, STRT, and STRBT, which have specific addressing mode requirements
get_addr_fp_t GetAddressingOpLoadStoreT(unsigned int inst) {
if (BITS(inst, 25, 27) == 2) {
return LnSWoUB(ImmediatePostIndexed);
} else if (BITS(inst, 25, 27) == 3) {
return LnSWoUB(ScaledRegisterPostIndexed);
}
// Reaching this would indicate the thumb version
// of this instruction, however the 3DS CPU doesn't
// support this variant (the 3DS CPU is only ARMv6K,
// while this variant is added in ARMv6T2).
// So it's sufficient for citra to not implement this.
return nullptr;
}
enum {
FETCH_SUCCESS,
FETCH_FAILURE
};
static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_inst, u32* inst_size, ARM_INST_PTR* ptr_inst_base) {
// Check if in Thumb mode
ThumbDecodeStatus ret = TranslateThumbInstruction (addr, inst, arm_inst, inst_size);
if (ret == ThumbDecodeStatus::BRANCH) {
int inst_index;
int table_length = (int)arm_instruction_trans_len;
u32 tinstr = GetThumbInstruction(inst, addr);
switch ((tinstr & 0xF800) >> 11) {
case 26:
case 27:
if (((tinstr & 0x0F00) != 0x0E00) && ((tinstr & 0x0F00) != 0x0F00)){
inst_index = table_length - 4;
*ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index);
} else {
// LOG_ERROR(Core_ARM11, "thumb decoder error");
ASSERT_MSG(false, "thumb decoder error");
}
break;
case 28:
// Branch 2, unconditional branch
inst_index = table_length - 5;
*ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index);
break;
case 8:
case 29:
// For BLX 1 thumb instruction
inst_index = table_length - 1;
*ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index);
break;
case 30:
// For BL 1 thumb instruction
inst_index = table_length - 3;
*ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index);
break;
case 31:
// For BL 2 thumb instruction
inst_index = table_length - 2;
*ptr_inst_base = arm_instruction_trans[inst_index](tinstr, inst_index);
break;
default:
ret = ThumbDecodeStatus::UNDEFINED;
break;
}
}
return ret;
}
enum {
KEEP_GOING,
FETCH_EXCEPTION
};
//MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64));
static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) {
unsigned int inst_size = 4;
unsigned int inst = cpu->user_callbacks->MemoryReadCode(phys_addr & 0xFFFFFFFC);
// If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
if (cpu->TFlag) {
u32 arm_inst;
ThumbDecodeStatus state = DecodeThumbInstruction(inst, phys_addr, &arm_inst, &inst_size, &inst_base);
// We have translated the Thumb branch instruction in the Thumb decoder
if (state == ThumbDecodeStatus::BRANCH) {
return inst_size;
}
inst = arm_inst;
}
int idx;
if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
// std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst);
// LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst);
// LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
ASSERT_MSG(false, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, "", inst);
ASSERT_MSG(false, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
CITRA_IGNORE_EXIT(-1);
}
inst_base = arm_instruction_trans[idx](inst, idx);
return inst_size;
}
static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
// MICROPROFILE_SCOPE(DynCom_Decode);
// Decode instruction, get index
// Allocate memory and init InsCream
// Go on next, until terminal instruction
// Save start addr of basicblock in CreamCache
ARM_INST_PTR inst_base = nullptr;
TransExtData ret = TransExtData::NON_BRANCH;
int size = 0; // instruction size of basic block
bb_start = (int)trans_cache_buf_top;
u32 phys_addr = addr;
u32 pc_start = cpu->Reg[15];
while (ret == TransExtData::NON_BRANCH) {
unsigned int inst_size = InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
size++;
phys_addr += inst_size;
if ((phys_addr & 0xfff) == 0) {
inst_base->br = TransExtData::END_OF_PAGE;
}
ret = inst_base->br;
};
cpu->instruction_cache[pc_start] = bb_start;
return KEEP_GOING;
}
static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) {
// MICROPROFILE_SCOPE(DynCom_Decode);
ARM_INST_PTR inst_base = nullptr;
bb_start = (int)trans_cache_buf_top;
u32 phys_addr = addr;
u32 pc_start = cpu->Reg[15];
InterpreterTranslateInstruction(cpu, phys_addr, inst_base);
if (inst_base->br == TransExtData::NON_BRANCH) {
inst_base->br = TransExtData::SINGLE_STEP;
}
cpu->instruction_cache[pc_start] = bb_start;
return KEEP_GOING;
}
static int clz(unsigned int x) {
int n;
if (x == 0) return (32);
n = 1;
if ((x >> 16) == 0) { n = n + 16; x = x << 16;}
if ((x >> 24) == 0) { n = n + 8; x = x << 8;}
if ((x >> 28) == 0) { n = n + 4; x = x << 4;}
if ((x >> 30) == 0) { n = n + 2; x = x << 2;}
n = n - (x >> 31);
return n;
}
//MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0));
unsigned InterpreterMainLoop(ARMul_State* cpu) {
// MICROPROFILE_SCOPE(DynCom_Execute);
// GDBStub::BreakpointAddress breakpoint_data;
#undef RM
#undef RS
#define CRn inst_cream->crn
#define OPCODE_1 inst_cream->opcode_1
#define OPCODE_2 inst_cream->opcode_2
#define CRm inst_cream->crm
#define RD cpu->Reg[inst_cream->Rd]
#define RD2 cpu->Reg[inst_cream->Rd + 1]
#define RN cpu->Reg[inst_cream->Rn]
#define RM cpu->Reg[inst_cream->Rm]
#define RS cpu->Reg[inst_cream->Rs]
#define RDHI cpu->Reg[inst_cream->RdHi]
#define RDLO cpu->Reg[inst_cream->RdLo]
#define LINK_RTN_ADDR (cpu->Reg[14] = cpu->Reg[15] + 4)
#define SET_PC (cpu->Reg[15] = cpu->Reg[15] + 8 + inst_cream->signed_immed_24)
#define SHIFTER_OPERAND inst_cream->shtop_func(cpu, inst_cream->shifter_operand)
#define FETCH_INST if (inst_base->br != TransExtData::NON_BRANCH) goto DISPATCH; \
inst_base = (arm_inst *)&trans_cache_buf[ptr]
#define INC_PC(l) ptr += sizeof(arm_inst) + l
#define INC_PC_STUB ptr += sizeof(arm_inst)
#define GDB_BP_CHECK \
cpu->Cpsr &= ~(1 << 5); \
cpu->Cpsr |= cpu->TFlag << 5;
// if (GDBStub::g_server_enabled) {
// if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None && PC == breakpoint_data.address)) {
// GDBStub::Break();
// goto END;
// }
// }
#define GOTO_NEXT_INST \
GDB_BP_CHECK; \
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
num_instrs++; \
switch(inst_base->idx) { \
case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \
case 2: goto VNMLA_INST; \
case 3: goto VNMLS_INST; \
case 4: goto VNMUL_INST; \
case 5: goto VMUL_INST; \
case 6: goto VADD_INST; \
case 7: goto VSUB_INST; \
case 8: goto VDIV_INST; \
case 9: goto VMOVI_INST; \
case 10: goto VMOVR_INST; \
case 11: goto VABS_INST; \
case 12: goto VNEG_INST; \
case 13: goto VSQRT_INST; \
case 14: goto VCMP_INST; \
case 15: goto VCMP2_INST; \
case 16: goto VCVTBDS_INST; \
case 17: goto VCVTBFF_INST; \
case 18: goto VCVTBFI_INST; \
case 19: goto VMOVBRS_INST; \
case 20: goto VMSR_INST; \
case 21: goto VMOVBRC_INST; \
case 22: goto VMRS_INST; \
case 23: goto VMOVBCR_INST; \
case 24: goto VMOVBRRSS_INST; \
case 25: goto VMOVBRRD_INST; \
case 26: goto VSTR_INST; \
case 27: goto VPUSH_INST; \
case 28: goto VSTM_INST; \
case 29: goto VPOP_INST; \
case 30: goto VLDR_INST; \
case 31: goto VLDM_INST ; \
case 32: goto SRS_INST; \
case 33: goto RFE_INST; \
case 34: goto BKPT_INST; \
case 35: goto BLX_INST; \
case 36: goto CPS_INST; \
case 37: goto PLD_INST; \
case 38: goto SETEND_INST; \
case 39: goto CLREX_INST; \
case 40: goto REV16_INST; \
case 41: goto USAD8_INST; \
case 42: goto SXTB_INST; \
case 43: goto UXTB_INST; \
case 44: goto SXTH_INST; \
case 45: goto SXTB16_INST; \
case 46: goto UXTH_INST; \
case 47: goto UXTB16_INST; \
case 48: goto CPY_INST; \
case 49: goto UXTAB_INST; \
case 50: goto SSUB8_INST; \
case 51: goto SHSUB8_INST; \
case 52: goto SSUBADDX_INST; \
case 53: goto STREX_INST; \
case 54: goto STREXB_INST; \
case 55: goto SWP_INST; \
case 56: goto SWPB_INST; \
case 57: goto SSUB16_INST; \
case 58: goto SSAT16_INST; \
case 59: goto SHSUBADDX_INST; \
case 60: goto QSUBADDX_INST; \
case 61: goto SHADDSUBX_INST; \
case 62: goto SHADD8_INST; \
case 63: goto SHADD16_INST; \
case 64: goto SEL_INST; \
case 65: goto SADDSUBX_INST; \
case 66: goto SADD8_INST; \
case 67: goto SADD16_INST; \
case 68: goto SHSUB16_INST; \
case 69: goto UMAAL_INST; \
case 70: goto UXTAB16_INST; \
case 71: goto USUBADDX_INST; \
case 72: goto USUB8_INST; \
case 73: goto USUB16_INST; \
case 74: goto USAT16_INST; \
case 75: goto USADA8_INST; \
case 76: goto UQSUBADDX_INST; \
case 77: goto UQSUB8_INST; \
case 78: goto UQSUB16_INST; \
case 79: goto UQADDSUBX_INST; \
case 80: goto UQADD8_INST; \
case 81: goto UQADD16_INST; \
case 82: goto SXTAB_INST; \
case 83: goto UHSUBADDX_INST; \
case 84: goto UHSUB8_INST; \
case 85: goto UHSUB16_INST; \
case 86: goto UHADDSUBX_INST; \
case 87: goto UHADD8_INST; \
case 88: goto UHADD16_INST; \
case 89: goto UADDSUBX_INST; \
case 90: goto UADD8_INST; \
case 91: goto UADD16_INST; \
case 92: goto SXTAH_INST; \
case 93: goto SXTAB16_INST; \
case 94: goto QADD8_INST; \
case 95: goto BXJ_INST; \
case 96: goto CLZ_INST; \
case 97: goto UXTAH_INST; \
case 98: goto BX_INST; \
case 99: goto REV_INST; \
case 100: goto BLX_INST; \
case 101: goto REVSH_INST; \
case 102: goto QADD_INST; \
case 103: goto QADD16_INST; \
case 104: goto QADDSUBX_INST; \
case 105: goto LDREX_INST; \
case 106: goto QDADD_INST; \
case 107: goto QDSUB_INST; \
case 108: goto QSUB_INST; \
case 109: goto LDREXB_INST; \
case 110: goto QSUB8_INST; \
case 111: goto QSUB16_INST; \
case 112: goto SMUAD_INST; \
case 113: goto SMMUL_INST; \
case 114: goto SMUSD_INST; \
case 115: goto SMLSD_INST; \
case 116: goto SMLSLD_INST; \
case 117: goto SMMLA_INST; \
case 118: goto SMMLS_INST; \
case 119: goto SMLALD_INST; \
case 120: goto SMLAD_INST; \
case 121: goto SMLAW_INST; \
case 122: goto SMULW_INST; \
case 123: goto PKHTB_INST; \
case 124: goto PKHBT_INST; \
case 125: goto SMUL_INST; \
case 126: goto SMLALXY_INST; \
case 127: goto SMLA_INST; \
case 128: goto MCRR_INST; \
case 129: goto MRRC_INST; \
case 130: goto CMP_INST; \
case 131: goto TST_INST; \
case 132: goto TEQ_INST; \
case 133: goto CMN_INST; \
case 134: goto SMULL_INST; \
case 135: goto UMULL_INST; \
case 136: goto UMLAL_INST; \
case 137: goto SMLAL_INST; \
case 138: goto MUL_INST; \
case 139: goto MLA_INST; \
case 140: goto SSAT_INST; \
case 141: goto USAT_INST; \
case 142: goto MRS_INST; \
case 143: goto MSR_INST; \
case 144: goto AND_INST; \
case 145: goto BIC_INST; \
case 146: goto LDM_INST; \
case 147: goto EOR_INST; \
case 148: goto ADD_INST; \
case 149: goto RSB_INST; \
case 150: goto RSC_INST; \
case 151: goto SBC_INST; \
case 152: goto ADC_INST; \
case 153: goto SUB_INST; \
case 154: goto ORR_INST; \
case 155: goto MVN_INST; \
case 156: goto MOV_INST; \
case 157: goto STM_INST; \
case 158: goto LDM_INST; \
case 159: goto LDRSH_INST; \
case 160: goto STM_INST; \
case 161: goto LDM_INST; \
case 162: goto LDRSB_INST; \
case 163: goto STRD_INST; \
case 164: goto LDRH_INST; \
case 165: goto STRH_INST; \
case 166: goto LDRD_INST; \
case 167: goto STRT_INST; \
case 168: goto STRBT_INST; \
case 169: goto LDRBT_INST; \
case 170: goto LDRT_INST; \
case 171: goto MRC_INST; \
case 172: goto MCR_INST; \
case 173: goto MSR_INST; \
case 174: goto MSR_INST; \
case 175: goto MSR_INST; \
case 176: goto MSR_INST; \
case 177: goto MSR_INST; \
case 178: goto LDRB_INST; \
case 179: goto STRB_INST; \
case 180: goto LDR_INST; \
case 181: goto LDRCOND_INST ; \
case 182: goto STR_INST; \
case 183: goto CDP_INST; \
case 184: goto STC_INST; \
case 185: goto LDC_INST; \
case 186: goto LDREXD_INST; \
case 187: goto STREXD_INST; \
case 188: goto LDREXH_INST; \
case 189: goto STREXH_INST; \
case 190: goto NOP_INST; \
case 191: goto YIELD_INST; \
case 192: goto WFE_INST; \
case 193: goto WFI_INST; \
case 194: goto SEV_INST; \
case 195: goto SWI_INST; \
case 196: goto BBL_INST; \
case 197: goto B_2_THUMB ; \
case 198: goto B_COND_THUMB ; \
case 199: goto BL_1_THUMB ; \
case 200: goto BL_2_THUMB ; \
case 201: goto BLX_1_THUMB ; \
case 202: goto DISPATCH; \
case 203: goto INIT_INST_LENGTH; \
case 204: goto END; \
}
#define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
#define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
#define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
#define SAVE_NZCVT cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) | \
(cpu->NFlag << 31) | \
(cpu->ZFlag << 30) | \
(cpu->CFlag << 29) | \
(cpu->VFlag << 28) | \
(cpu->TFlag << 5)
#define LOAD_NZCVT cpu->NFlag = (cpu->Cpsr >> 31); \
cpu->ZFlag = (cpu->Cpsr >> 30) & 1; \
cpu->CFlag = (cpu->Cpsr >> 29) & 1; \
cpu->VFlag = (cpu->Cpsr >> 28) & 1; \
cpu->TFlag = (cpu->Cpsr >> 5) & 1;
#define CurrentModeHasSPSR (cpu->Mode != SYSTEM32MODE) && (cpu->Mode != USER32MODE)
#define PC (cpu->Reg[15])
arm_inst* inst_base;
unsigned int addr;
unsigned int num_instrs = 0;
int ptr;
LOAD_NZCVT;
DISPATCH:
{
if (num_instrs >= cpu->NumInstrsToExecute)
goto END;
if (!cpu->NirqSig) {
if (!(cpu->Cpsr & 0x80)) {
goto END;
}
}
if (cpu->TFlag)
cpu->Reg[15] &= 0xfffffffe;
else
cpu->Reg[15] &= 0xfffffffc;
// Find the cached instruction cream, otherwise translate it...
auto itr = cpu->instruction_cache.find(cpu->Reg[15]);
if (itr != cpu->instruction_cache.end()) {
ptr = itr->second;
} else if (cpu->NumInstrsToExecute != 1) {
if (InterpreterTranslateBlock(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
goto END;
} else {
if (InterpreterTranslateSingle(cpu, ptr, cpu->Reg[15]) == FETCH_EXCEPTION)
goto END;
}
// Find breakpoint if one exists within the block
// if (GDBStub::g_server_enabled && GDBStub::IsConnected()) {
// breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute);
// }
inst_base = (arm_inst *)&trans_cache_buf[ptr];
GOTO_NEXT_INST;
}
ADC_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
adc_inst* const inst_cream = (adc_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
RD = AddWithCarry(rn_val, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(adc_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(adc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
ADD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
add_inst* const inst_cream = (add_inst*)inst_base->component;
u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
bool carry;
bool overflow;
RD = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(add_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(add_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
AND_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
and_inst* const inst_cream = (and_inst*)inst_base->component;
u32 lop = RN;
u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += 2 * cpu->GetInstructionSize();
RD = lop & rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(and_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(and_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
BBL_INST:
{
if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) {
bbl_inst *inst_cream = (bbl_inst *)inst_base->component;
if (inst_cream->L) {
LINK_RTN_ADDR;
}
SET_PC;
INC_PC(sizeof(bbl_inst));
goto DISPATCH;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(bbl_inst));
goto DISPATCH;
}
BIC_INST:
{
bic_inst *inst_cream = (bic_inst *)inst_base->component;
if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) {
u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * cpu->GetInstructionSize();
}
u32 rop = SHIFTER_OPERAND;
RD = lop & (~rop);
if ((inst_cream->S) && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(bic_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(bic_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
BKPT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
// bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component;
// LOG_DEBUG(Core_ARM11, "Breakpoint instruction hit. Immediate: 0x%08X", inst_cream->imm);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(bkpt_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
BLX_INST:
{
blx_inst *inst_cream = (blx_inst *)inst_base->component;
if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) {
unsigned int inst = inst_cream->inst;
if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) {
const u32 jump_address = cpu->Reg[inst_cream->val.Rm];
cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
if(cpu->TFlag)
cpu->Reg[14] |= 0x1;
cpu->Reg[15] = jump_address & 0xfffffffe;
cpu->TFlag = jump_address & 0x1;
} else {
cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
cpu->TFlag = 0x1;
int signed_int = inst_cream->val.signed_immed_24;
signed_int = (signed_int & 0x800000) ? (0x3F000000 | signed_int) : signed_int;
signed_int = signed_int << 2;
cpu->Reg[15] = cpu->Reg[15] + 8 + signed_int + (BIT(inst, 24) << 1);
}
INC_PC(sizeof(blx_inst));
goto DISPATCH;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(blx_inst));
goto DISPATCH;
}
BX_INST:
BXJ_INST:
{
// Note that only the 'fail' case of BXJ is emulated. This is because
// the facilities for Jazelle emulation are not implemented.
//
// According to the ARM documentation on BXJ, if setting the J bit in the APSR
// fails, then BXJ functions identically like a regular BX instruction.
//
// This is sufficient for citra, as the CPU for the 3DS does not implement Jazelle.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
bx_inst* const inst_cream = (bx_inst*)inst_base->component;
u32 address = RM;
if (inst_cream->Rm == 15)
address += 2 * cpu->GetInstructionSize();
cpu->TFlag = address & 1;
cpu->Reg[15] = address & 0xfffffffe;
INC_PC(sizeof(bx_inst));
goto DISPATCH;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(bx_inst));
goto DISPATCH;
}
CDP_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
// Undefined instruction here
cpu->NumInstrsToExecute = 0;
return num_instrs;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(cdp_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CLREX_INST:
{
cpu->UnsetExclusiveMemoryAddress();
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(clrex_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CLZ_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
clz_inst* inst_cream = (clz_inst*)inst_base->component;
RD = clz(RM);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(clz_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CMN_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
cmn_inst* const inst_cream = (cmn_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
u32 result = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow);
UPDATE_NFLAG(result);
UPDATE_ZFLAG(result);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(cmn_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CMP_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
cmp_inst* const inst_cream = (cmp_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
u32 result = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
UPDATE_NFLAG(result);
UPDATE_ZFLAG(result);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(cmp_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CPS_INST:
{
cps_inst *inst_cream = (cps_inst *)inst_base->component;
u32 aif_val = 0;
u32 aif_mask = 0;
if (cpu->InAPrivilegedMode()) {
if (inst_cream->imod1) {
if (inst_cream->A) {
aif_val |= (inst_cream->imod0 << 8);
aif_mask |= 1 << 8;
}
if (inst_cream->I) {
aif_val |= (inst_cream->imod0 << 7);
aif_mask |= 1 << 7;
}
if (inst_cream->F) {
aif_val |= (inst_cream->imod0 << 6);
aif_mask |= 1 << 6;
}
aif_mask = ~aif_mask;
cpu->Cpsr = (cpu->Cpsr & aif_mask) | aif_val;
}
if (inst_cream->mmod) {
cpu->Cpsr = (cpu->Cpsr & 0xffffffe0) | inst_cream->mode;
cpu->ChangePrivilegeMode(inst_cream->mode);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(cps_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
CPY_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mov_inst* inst_cream = (mov_inst*)inst_base->component;
RD = SHIFTER_OPERAND;
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mov_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mov_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
EOR_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
eor_inst* inst_cream = (eor_inst*)inst_base->component;
u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * cpu->GetInstructionSize();
}
u32 rop = SHIFTER_OPERAND;
RD = lop ^ rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(eor_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(eor_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDC_INST:
{
// Instruction not implemented
//LOG_CRITICAL(Core_ARM11, "unimplemented instruction");
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDM_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int inst = inst_cream->inst;
if (BIT(inst, 22) && !BIT(inst, 15)) {
for (int i = 0; i < 13; i++) {
if(BIT(inst, i)) {
cpu->Reg[i] = cpu->ReadMemory32(addr);
addr += 4;
}
}
if (BIT(inst, 13)) {
if (cpu->Mode == USER32MODE)
cpu->Reg[13] = cpu->ReadMemory32(addr);
else
cpu->Reg_usr[0] = cpu->ReadMemory32(addr);
addr += 4;
}
if (BIT(inst, 14)) {
if (cpu->Mode == USER32MODE)
cpu->Reg[14] = cpu->ReadMemory32(addr);
else
cpu->Reg_usr[1] = cpu->ReadMemory32(addr);
addr += 4;
}
} else if (!BIT(inst, 22)) {
for(int i = 0; i < 16; i++ ){
if(BIT(inst, i)){
unsigned int ret = cpu->ReadMemory32(addr);
// For armv5t, should enter thumb when bits[0] is non-zero.
if(i == 15){
cpu->TFlag = ret & 0x1;
ret &= 0xFFFFFFFE;
}
cpu->Reg[i] = ret;
addr += 4;
}
}
} else if (BIT(inst, 22) && BIT(inst, 15)) {
for(int i = 0; i < 15; i++ ){
if(BIT(inst, i)){
cpu->Reg[i] = cpu->ReadMemory32(addr);
addr += 4;
}
}
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F);
LOAD_NZCVT;
}
cpu->Reg[15] = cpu->ReadMemory32(addr);
}
if (BIT(inst, 15)) {
INC_PC(sizeof(ldst_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SXTH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sxth_inst* inst_cream = (sxth_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
if (BIT(operand2, 15)) {
operand2 |= 0xffff0000;
} else {
operand2 &= 0xffff;
}
RD = operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sxth_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDR_INST:
{
ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->ReadMemory32(addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
// For armv5t, should enter thumb when bits[0] is non-zero.
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
goto DISPATCH;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRCOND_INST:
{
if (CondPassed(cpu, inst_base->cond)) {
ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->ReadMemory32(addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
// For armv5t, should enter thumb when bits[0] is non-zero.
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UXTH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
uxth_inst* inst_cream = (uxth_inst*)inst_base->component;
RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxth_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UXTAH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
uxtah_inst* inst_cream = (uxtah_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
RD = RN + operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxtah_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRBT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
const u32 dest_index = BITS(inst_cream->inst, 12, 15);
const u32 previous_mode = cpu->Mode;
cpu->ChangePrivilegeMode(USER32MODE);
const u8 value = cpu->ReadMemory8(addr);
cpu->ChangePrivilegeMode(previous_mode);
cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
// Should check if RD is even-numbered, Rd != 14, addr[0:1] == 0, (CP15_reg1_U == 1 || addr[2] == 0)
inst_cream->get_addr(cpu, inst_cream->inst, addr);
// The 3DS doesn't have LPAE (Large Physical Access Extension), so it
// wouldn't do this as a single read.
cpu->Reg[BITS(inst_cream->inst, 12, 15) + 0] = cpu->ReadMemory32(addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1] = cpu->ReadMemory32(addr + 4);
// No dispatch since this operation should not modify R15
}
cpu->Reg[15] += 4;
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDREX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int read_addr = RN;
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory32(read_addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDREXB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int read_addr = RN;
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory8(read_addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDREXH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int read_addr = RN;
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory16(read_addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDREXD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int read_addr = RN;
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory32(read_addr);
RD2 = cpu->ReadMemory32(read_addr + 4);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRSB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->ReadMemory8(addr);
if (BIT(value, 7)) {
value |= 0xffffff00;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRSH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->ReadMemory16(addr);
if (BIT(value, 15)) {
value |= 0xffff0000;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
LDRT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
const u32 dest_index = BITS(inst_cream->inst, 12, 15);
const u32 previous_mode = cpu->Mode;
cpu->ChangePrivilegeMode(USER32MODE);
const u32 value = cpu->ReadMemory32(addr);
cpu->ChangePrivilegeMode(previous_mode);
cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MCR_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mcr_inst* inst_cream = (mcr_inst*)inst_base->component;
// unsigned int inst = inst_cream->inst;
if (inst_cream->Rd == 15) {
DEBUG_MSG;
} else {
if (inst_cream->cp_num == 15)
cpu->WriteCP15Register(RD, CRn, OPCODE_1, CRm, OPCODE_2);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mcr_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MCRR_INST:
{
// Stubbed, as the MPCore doesn't have any registers that are accessible
// through this instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component;
// LOG_ERROR(Core_ARM11, "MCRR executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u",
// inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2);
ASSERT_MSG(false, "MCRR executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u",
inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mcrr_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MLA_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mla_inst* inst_cream = (mla_inst*)inst_base->component;
u64 rm = RM;
u64 rs = RS;
u64 rn = RN;
RD = static_cast<u32>((rm * rs + rn) & 0xffffffff);
if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mla_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MOV_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mov_inst* inst_cream = (mov_inst*)inst_base->component;
RD = SHIFTER_OPERAND;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mov_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mov_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MRC_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mrc_inst* inst_cream = (mrc_inst*)inst_base->component;
if (inst_cream->cp_num == 15) {
const uint32_t value = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2);
if (inst_cream->Rd == 15) {
cpu->Cpsr = (cpu->Cpsr & ~0xF0000000) | (value & 0xF0000000);
LOAD_NZCVT;
} else {
RD = value;
}
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mrc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MRRC_INST:
{
// Stubbed, as the MPCore doesn't have any registers that are accessible
// through this instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component;
// LOG_ERROR(Core_ARM11, "MRRC executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u",
// inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2);
ASSERT_MSG(false, "MRRC executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u",
inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mcrr_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MRS_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mrs_inst* inst_cream = (mrs_inst*)inst_base->component;
if (inst_cream->R) {
RD = cpu->Spsr_copy;
} else {
SAVE_NZCVT;
RD = cpu->Cpsr;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mrs_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MSR_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
msr_inst* inst_cream = (msr_inst*)inst_base->component;
const u32 UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
unsigned int inst = inst_cream->inst;
unsigned int operand;
if (BIT(inst, 25)) {
int rot_imm = BITS(inst, 8, 11) * 2;
operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
} else {
operand = cpu->Reg[BITS(inst, 0, 3)];
}
u32 byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
| (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
u32 mask = 0;
if (!inst_cream->R) {
if (cpu->InAPrivilegedMode()) {
if ((operand & StateMask) != 0) {
/// UNPREDICTABLE
DEBUG_MSG;
} else
mask = byte_mask & (UserMask | PrivMask);
} else {
mask = byte_mask & UserMask;
}
SAVE_NZCVT;
cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F);
LOAD_NZCVT;
} else {
if (CurrentModeHasSPSR) {
mask = byte_mask & (UserMask | PrivMask | StateMask);
cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
}
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(msr_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MUL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mul_inst* inst_cream = (mul_inst*)inst_base->component;
u64 rm = RM;
u64 rs = RS;
RD = static_cast<u32>((rm * rs) & 0xffffffff);
if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mul_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
MVN_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mvn_inst* const inst_cream = (mvn_inst*)inst_base->component;
RD = ~SHIFTER_OPERAND;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mvn_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mvn_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
ORR_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
orr_inst* const inst_cream = (orr_inst*)inst_base->component;
u32 lop = RN;
u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += 2 * cpu->GetInstructionSize();
RD = lop | rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(orr_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(orr_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
NOP_INST:
{
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC_STUB;
FETCH_INST;
GOTO_NEXT_INST;
}
PKHBT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
pkh_inst *inst_cream = (pkh_inst *)inst_base->component;
RD = (RN & 0xFFFF) | ((RM << inst_cream->imm) & 0xFFFF0000);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(pkh_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
PKHTB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
pkh_inst *inst_cream = (pkh_inst *)inst_base->component;
int shift_imm = inst_cream->imm ? inst_cream->imm : 31;
RD = ((static_cast<s32>(RM) >> shift_imm) & 0xFFFF) | (RN & 0xFFFF0000);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(pkh_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
PLD_INST:
{
// Not implemented. PLD is a hint instruction, so it's optional.
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(pld_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
QADD_INST:
QDADD_INST:
QDSUB_INST:
QSUB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u8 op1 = inst_cream->op1;
const u32 rm_val = RM;
const u32 rn_val = RN;
u32 result = 0;
// QADD
if (op1 == 0x00) {
result = rm_val + rn_val;
if (AddOverflow(rm_val, rn_val, result)) {
result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
}
// QSUB
else if (op1 == 0x01) {
result = rm_val - rn_val;
if (SubOverflow(rm_val, rn_val, result)) {
result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
}
// QDADD
else if (op1 == 0x02) {
u32 mul = (rn_val * 2);
if (AddOverflow(rn_val, rn_val, rn_val * 2)) {
mul = POS(mul) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
result = mul + rm_val;
if (AddOverflow(rm_val, mul, result)) {
result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
}
// QDSUB
else if (op1 == 0x03) {
u32 mul = (rn_val * 2);
if (AddOverflow(rn_val, rn_val, mul)) {
mul = POS(mul) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
result = rm_val - mul;
if (SubOverflow(rm_val, mul, result)) {
result = POS(result) ? 0x80000000 : 0x7FFFFFFF;
cpu->Cpsr |= (1 << 27);
}
}
RD = result;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
QADD8_INST:
QADD16_INST:
QADDSUBX_INST:
QSUB8_INST:
QSUB16_INST:
QSUBADDX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u16 rm_lo = (RM & 0xFFFF);
const u16 rm_hi = ((RM >> 16) & 0xFFFF);
const u16 rn_lo = (RN & 0xFFFF);
const u16 rn_hi = ((RN >> 16) & 0xFFFF);
const u8 op2 = inst_cream->op2;
u16 lo_result = 0;
u16 hi_result = 0;
// QADD16
if (op2 == 0x00) {
lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_lo);
hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_hi);
}
// QASX
else if (op2 == 0x01) {
lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_hi);
hi_result = ARMul_SignedSaturatedAdd16(rn_hi, rm_lo);
}
// QSAX
else if (op2 == 0x02) {
lo_result = ARMul_SignedSaturatedAdd16(rn_lo, rm_hi);
hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_lo);
}
// QSUB16
else if (op2 == 0x03) {
lo_result = ARMul_SignedSaturatedSub16(rn_lo, rm_lo);
hi_result = ARMul_SignedSaturatedSub16(rn_hi, rm_hi);
}
// QADD8
else if (op2 == 0x04) {
lo_result = ARMul_SignedSaturatedAdd8(rn_lo & 0xFF, rm_lo & 0xFF) |
ARMul_SignedSaturatedAdd8(rn_lo >> 8, rm_lo >> 8) << 8;
hi_result = ARMul_SignedSaturatedAdd8(rn_hi & 0xFF, rm_hi & 0xFF) |
ARMul_SignedSaturatedAdd8(rn_hi >> 8, rm_hi >> 8) << 8;
}
// QSUB8
else if (op2 == 0x07) {
lo_result = ARMul_SignedSaturatedSub8(rn_lo & 0xFF, rm_lo & 0xFF) |
ARMul_SignedSaturatedSub8(rn_lo >> 8, rm_lo >> 8) << 8;
hi_result = ARMul_SignedSaturatedSub8(rn_hi & 0xFF, rm_hi & 0xFF) |
ARMul_SignedSaturatedSub8(rn_hi >> 8, rm_hi >> 8) << 8;
}
RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
REV_INST:
REV16_INST:
REVSH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
rev_inst* const inst_cream = (rev_inst*)inst_base->component;
const u8 op1 = inst_cream->op1;
const u8 op2 = inst_cream->op2;
// REV
if (op1 == 0x03 && op2 == 0x01) {
RD = ((RM & 0xFF) << 24) | (((RM >> 8) & 0xFF) << 16) | (((RM >> 16) & 0xFF) << 8) | ((RM >> 24) & 0xFF);
}
// REV16
else if (op1 == 0x03 && op2 == 0x05) {
RD = ((RM & 0xFF) << 8) | ((RM & 0xFF00) >> 8) | ((RM & 0xFF0000) << 8) | ((RM & 0xFF000000) >> 8);
}
// REVSH
else if (op1 == 0x07 && op2 == 0x05) {
RD = ((RM & 0xFF) << 8) | ((RM & 0xFF00) >> 8);
if (RD & 0x8000)
RD |= 0xffff0000;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(rev_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
RFE_INST:
{
// RFE is unconditional
ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
u32 address = 0;
inst_cream->get_addr(cpu, inst_cream->inst, address);
cpu->Cpsr = cpu->ReadMemory32(address);
cpu->Reg[15] = cpu->ReadMemory32(address + 4);
INC_PC(sizeof(ldst_inst));
goto DISPATCH;
}
RSB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
rsb_inst* const inst_cream = (rsb_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
RD = AddWithCarry(~rn_val, SHIFTER_OPERAND, 1, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsb_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(rsb_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
RSC_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
rsc_inst* const inst_cream = (rsc_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
RD = AddWithCarry(~rn_val, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsc_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(rsc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SADD8_INST:
SSUB8_INST:
SADD16_INST:
SADDSUBX_INST:
SSUBADDX_INST:
SSUB16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u8 op2 = inst_cream->op2;
if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
const s16 rn_lo = (RN & 0xFFFF);
const s16 rn_hi = ((RN >> 16) & 0xFFFF);
const s16 rm_lo = (RM & 0xFFFF);
const s16 rm_hi = ((RM >> 16) & 0xFFFF);
s32 lo_result = 0;
s32 hi_result = 0;
// SADD16
if (inst_cream->op2 == 0x00) {
lo_result = (rn_lo + rm_lo);
hi_result = (rn_hi + rm_hi);
}
// SASX
else if (op2 == 0x01) {
lo_result = (rn_lo - rm_hi);
hi_result = (rn_hi + rm_lo);
}
// SSAX
else if (op2 == 0x02) {
lo_result = (rn_lo + rm_hi);
hi_result = (rn_hi - rm_lo);
}
// SSUB16
else if (op2 == 0x03) {
lo_result = (rn_lo - rm_lo);
hi_result = (rn_hi - rm_hi);
}
RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
if (lo_result >= 0) {
cpu->Cpsr |= (1 << 16);
cpu->Cpsr |= (1 << 17);
} else {
cpu->Cpsr &= ~(1 << 16);
cpu->Cpsr &= ~(1 << 17);
}
if (hi_result >= 0) {
cpu->Cpsr |= (1 << 18);
cpu->Cpsr |= (1 << 19);
} else {
cpu->Cpsr &= ~(1 << 18);
cpu->Cpsr &= ~(1 << 19);
}
}
else if (op2 == 0x04 || op2 == 0x07) {
s32 lo_val1, lo_val2;
s32 hi_val1, hi_val2;
// SADD8
if (op2 == 0x04) {
lo_val1 = (s32)(s8)(RN & 0xFF) + (s32)(s8)(RM & 0xFF);
lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) + (s32)(s8)((RM >> 8) & 0xFF);
hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) + (s32)(s8)((RM >> 16) & 0xFF);
hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) + (s32)(s8)((RM >> 24) & 0xFF);
}
// SSUB8
else {
lo_val1 = (s32)(s8)(RN & 0xFF) - (s32)(s8)(RM & 0xFF);
lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) - (s32)(s8)((RM >> 8) & 0xFF);
hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) - (s32)(s8)((RM >> 16) & 0xFF);
hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) - (s32)(s8)((RM >> 24) & 0xFF);
}
RD = ((lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24));
if (lo_val1 >= 0)
cpu->Cpsr |= (1 << 16);
else
cpu->Cpsr &= ~(1 << 16);
if (lo_val2 >= 0)
cpu->Cpsr |= (1 << 17);
else
cpu->Cpsr &= ~(1 << 17);
if (hi_val1 >= 0)
cpu->Cpsr |= (1 << 18);
else
cpu->Cpsr &= ~(1 << 18);
if (hi_val2 >= 0)
cpu->Cpsr |= (1 << 19);
else
cpu->Cpsr &= ~(1 << 19);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SBC_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sbc_inst* const inst_cream = (sbc_inst*)inst_base->component;
u32 rn_val = RN;
if (inst_cream->Rn == 15)
rn_val += 2 * cpu->GetInstructionSize();
bool carry;
bool overflow;
RD = AddWithCarry(rn_val, ~SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sbc_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sbc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SEL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u32 to = RM;
const u32 from = RN;
const u32 cpsr = cpu->Cpsr;
u32 result;
if (cpsr & (1 << 16))
result = from & 0xff;
else
result = to & 0xff;
if (cpsr & (1 << 17))
result |= from & 0x0000ff00;
else
result |= to & 0x0000ff00;
if (cpsr & (1 << 18))
result |= from & 0x00ff0000;
else
result |= to & 0x00ff0000;
if (cpsr & (1 << 19))
result |= from & 0xff000000;
else
result |= to & 0xff000000;
RD = result;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SETEND_INST:
{
// SETEND is unconditional
setend_inst* const inst_cream = (setend_inst*)inst_base->component;
const bool big_endian = (inst_cream->set_bigend == 1);
if (big_endian)
cpu->Cpsr |= (1 << 9);
else
cpu->Cpsr &= ~(1 << 9);
// LOG_WARNING(Core_ARM11, "SETEND %s executed", big_endian ? "BE" : "LE");
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(setend_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SEV_INST:
{
// Stubbed, as SEV is a hint instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
LOG_TRACE(Core_ARM11, "SEV executed.");
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC_STUB;
FETCH_INST;
GOTO_NEXT_INST;
}
SHADD8_INST:
SHADD16_INST:
SHADDSUBX_INST:
SHSUB8_INST:
SHSUB16_INST:
SHSUBADDX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u8 op2 = inst_cream->op2;
const u32 rm_val = RM;
const u32 rn_val = RN;
if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
s32 lo_result = 0;
s32 hi_result = 0;
// SHADD16
if (op2 == 0x00) {
lo_result = ((s16)(rn_val & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
}
// SHASX
else if (op2 == 0x01) {
lo_result = ((s16)(rn_val & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
}
// SHSAX
else if (op2 == 0x02) {
lo_result = ((s16)(rn_val & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
}
// SHSUB16
else if (op2 == 0x03) {
lo_result = ((s16)(rn_val & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
}
RD = ((lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16));
}
else if (op2 == 0x04 || op2 == 0x07) {
s16 lo_val1, lo_val2;
s16 hi_val1, hi_val2;
// SHADD8
if (op2 == 0x04) {
lo_val1 = ((s8)(rn_val & 0xFF) + (s8)(rm_val & 0xFF)) >> 1;
lo_val2 = ((s8)((rn_val >> 8) & 0xFF) + (s8)((rm_val >> 8) & 0xFF)) >> 1;
hi_val1 = ((s8)((rn_val >> 16) & 0xFF) + (s8)((rm_val >> 16) & 0xFF)) >> 1;
hi_val2 = ((s8)((rn_val >> 24) & 0xFF) + (s8)((rm_val >> 24) & 0xFF)) >> 1;
}
// SHSUB8
else {
lo_val1 = ((s8)(rn_val & 0xFF) - (s8)(rm_val & 0xFF)) >> 1;
lo_val2 = ((s8)((rn_val >> 8) & 0xFF) - (s8)((rm_val >> 8) & 0xFF)) >> 1;
hi_val1 = ((s8)((rn_val >> 16) & 0xFF) - (s8)((rm_val >> 16) & 0xFF)) >> 1;
hi_val2 = ((s8)((rn_val >> 24) & 0xFF) - (s8)((rm_val >> 24) & 0xFF)) >> 1;
}
RD = (lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLA_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smla_inst* inst_cream = (smla_inst*)inst_base->component;
s32 operand1, operand2;
if (inst_cream->x == 0)
operand1 = (BIT(RM, 15)) ? (BITS(RM, 0, 15) | 0xffff0000) : BITS(RM, 0, 15);
else
operand1 = (BIT(RM, 31)) ? (BITS(RM, 16, 31) | 0xffff0000) : BITS(RM, 16, 31);
if (inst_cream->y == 0)
operand2 = (BIT(RS, 15)) ? (BITS(RS, 0, 15) | 0xffff0000) : BITS(RS, 0, 15);
else
operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31);
u32 product = operand1 * operand2;
u32 result = product + RN;
if (AddOverflow(product, RN, result))
cpu->Cpsr |= (1 << 27);
RD = result;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smla_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLAD_INST:
SMLSD_INST:
SMUAD_INST:
SMUSD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
const u8 op2 = inst_cream->op2;
u32 rm_val = cpu->Reg[inst_cream->Rm];
const u32 rn_val = cpu->Reg[inst_cream->Rn];
if (inst_cream->m)
rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
const s16 rm_lo = (rm_val & 0xFFFF);
const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
const s16 rn_lo = (rn_val & 0xFFFF);
const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
const u32 product1 = (rn_lo * rm_lo);
const u32 product2 = (rn_hi * rm_hi);
// SMUAD and SMLAD
if (BIT(op2, 1) == 0) {
u32 rd_val = (product1 + product2);
if (inst_cream->Ra != 15) {
rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
RD = rd_val;
if (ARMul_AddOverflowQ(product1, product2))
cpu->Cpsr |= (1 << 27);
}
// SMUSD and SMLSD
else {
u32 rd_val = (product1 - product2);
if (inst_cream->Ra != 15) {
rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
RD = rd_val;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlad_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLAL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
umlal_inst* inst_cream = (umlal_inst*)inst_base->component;
long long int rm = RM;
long long int rs = RS;
if (BIT(rm, 31)) {
rm |= 0xffffffff00000000LL;
}
if (BIT(rs, 31)) {
rs |= 0xffffffff00000000LL;
}
long long int rst = rm * rs;
long long int rdhi32 = RDHI;
long long int hilo = (rdhi32 << 32) + RDLO;
rst += hilo;
RDLO = BITS(rst, 0, 31);
RDHI = BITS(rst, 32, 63);
if (inst_cream->S) {
cpu->NFlag = BIT(RDHI, 31);
cpu->ZFlag = (RDHI == 0 && RDLO == 0);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(umlal_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLALXY_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
u64 operand1 = RN;
u64 operand2 = RM;
if (inst_cream->x != 0)
operand1 >>= 16;
if (inst_cream->y != 0)
operand2 >>= 16;
operand1 &= 0xFFFF;
if (operand1 & 0x8000)
operand1 -= 65536;
operand2 &= 0xFFFF;
if (operand2 & 0x8000)
operand2 -= 65536;
u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2);
RDLO = (dest & 0xFFFFFFFF);
RDHI = ((dest >> 32) & 0xFFFFFFFF);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlalxy_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLAW_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
const u32 rm_val = RM;
const u32 rn_val = RN;
const u32 ra_val = cpu->Reg[inst_cream->Ra];
const bool high = (inst_cream->m == 1);
const s16 operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF);
const s64 result = (s64)(s32)rn_val * (s64)(s32)operand2 + ((s64)(s32)ra_val << 16);
RD = BITS(result, 16, 47);
if ((result >> 16) != (s32)RD)
cpu->Cpsr |= (1 << 27);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlad_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMLALD_INST:
SMLSLD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlald_inst* const inst_cream = (smlald_inst*)inst_base->component;
const bool do_swap = (inst_cream->swap == 1);
const u32 rdlo_val = RDLO;
const u32 rdhi_val = RDHI;
const u32 rn_val = RN;
u32 rm_val = RM;
if (do_swap)
rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
const s32 product1 = (s16)(rn_val & 0xFFFF) * (s16)(rm_val & 0xFFFF);
const s32 product2 = (s16)((rn_val >> 16) & 0xFFFF) * (s16)((rm_val >> 16) & 0xFFFF);
s64 result;
// SMLALD
if (BIT(inst_cream->op2, 1) == 0) {
result = (product1 + product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
}
// SMLSLD
else {
result = (product1 - product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32));
}
RDLO = (result & 0xFFFFFFFF);
RDHI = ((result >> 32) & 0xFFFFFFFF);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlald_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMMLA_INST:
SMMLS_INST:
SMMUL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
const u32 rm_val = RM;
const u32 rn_val = RN;
const bool do_round = (inst_cream->m == 1);
// Assume SMMUL by default.
s64 result = (s64)(s32)rn_val * (s64)(s32)rm_val;
if (inst_cream->Ra != 15) {
const u32 ra_val = cpu->Reg[inst_cream->Ra];
// SMMLA, otherwise SMMLS
if (BIT(inst_cream->op2, 1) == 0)
result += ((s64)ra_val << 32);
else
result = ((s64)ra_val << 32) - result;
}
if (do_round)
result += 0x80000000;
RD = ((result >> 32) & 0xFFFFFFFF);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlad_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMUL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smul_inst* inst_cream = (smul_inst*)inst_base->component;
u32 operand1, operand2;
if (inst_cream->x == 0)
operand1 = (BIT(RM, 15)) ? (BITS(RM, 0, 15) | 0xffff0000) : BITS(RM, 0, 15);
else
operand1 = (BIT(RM, 31)) ? (BITS(RM, 16, 31) | 0xffff0000) : BITS(RM, 16, 31);
if (inst_cream->y == 0)
operand2 = (BIT(RS, 15)) ? (BITS(RS, 0, 15) | 0xffff0000) : BITS(RS, 0, 15);
else
operand2 = (BIT(RS, 31)) ? (BITS(RS, 16, 31) | 0xffff0000) : BITS(RS, 16, 31);
RD = operand1 * operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smul_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMULL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
umull_inst* inst_cream = (umull_inst*)inst_base->component;
s64 rm = RM;
s64 rs = RS;
if (BIT(rm, 31)) {
rm |= 0xffffffff00000000LL;
}
if (BIT(rs, 31)) {
rs |= 0xffffffff00000000LL;
}
s64 rst = rm * rs;
RDHI = BITS(rst, 32, 63);
RDLO = BITS(rst, 0, 31);
if (inst_cream->S) {
cpu->NFlag = BIT(RDHI, 31);
cpu->ZFlag = (RDHI == 0 && RDLO == 0);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(umull_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SMULW_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
s64 result = (s64)rm * (s64)(s32)RN;
RD = BITS(result, 16, 47);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(smlad_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SRS_INST:
{
// SRS is unconditional
ldst_inst* const inst_cream = (ldst_inst*)inst_base->component;
u32 address = 0;
inst_cream->get_addr(cpu, inst_cream->inst, address);
cpu->WriteMemory32(address + 0, cpu->Reg[14]);
cpu->WriteMemory32(address + 4, cpu->Spsr_copy);
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SSAT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
u8 shift_type = inst_cream->shift_type;
u8 shift_amount = inst_cream->imm5;
u32 rn_val = RN;
// 32-bit ASR is encoded as an amount of 0.
if (shift_type == 1 && shift_amount == 0)
shift_amount = 31;
if (shift_type == 0)
rn_val <<= shift_amount;
else if (shift_type == 1)
rn_val = ((s32)rn_val >> shift_amount);
bool saturated = false;
rn_val = ARMul_SignedSatQ(rn_val, inst_cream->sat_imm, &saturated);
if (saturated)
cpu->Cpsr |= (1 << 27);
RD = rn_val;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ssat_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SSAT16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
const u8 saturate_to = inst_cream->sat_imm;
bool sat1 = false;
bool sat2 = false;
RD = (ARMul_SignedSatQ((s16)RN, saturate_to, &sat1) & 0xFFFF) |
ARMul_SignedSatQ((s32)RN >> 16, saturate_to, &sat2) << 16;
if (sat1 || sat2)
cpu->Cpsr |= (1 << 27);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ssat_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STC_INST:
{
// Instruction not implemented
//LOG_CRITICAL(Core_ARM11, "unimplemented instruction");
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(stc_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STM_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
unsigned int inst = inst_cream->inst;
unsigned int Rn = BITS(inst, 16, 19);
unsigned int old_RN = cpu->Reg[Rn];
inst_cream->get_addr(cpu, inst_cream->inst, addr);
if (BIT(inst_cream->inst, 22) == 1) {
for (int i = 0; i < 13; i++) {
if (BIT(inst_cream->inst, i)) {
cpu->WriteMemory32(addr, cpu->Reg[i]);
addr += 4;
}
}
if (BIT(inst_cream->inst, 13)) {
if (cpu->Mode == USER32MODE)
cpu->WriteMemory32(addr, cpu->Reg[13]);
else
cpu->WriteMemory32(addr, cpu->Reg_usr[0]);
addr += 4;
}
if (BIT(inst_cream->inst, 14)) {
if (cpu->Mode == USER32MODE)
cpu->WriteMemory32(addr, cpu->Reg[14]);
else
cpu->WriteMemory32(addr, cpu->Reg_usr[1]);
addr += 4;
}
if (BIT(inst_cream->inst, 15)) {
cpu->WriteMemory32(addr, cpu->Reg[15] + 8);
}
} else {
for (size_t i = 0; i < 15; i++) {
if (BIT(inst_cream->inst, i)) {
if (i == Rn)
cpu->WriteMemory32(addr, old_RN);
else
cpu->WriteMemory32(addr, cpu->Reg[i]);
addr += 4;
}
}
// Check PC reg
if (BIT(inst_cream->inst, 15)) {
cpu->WriteMemory32(addr, cpu->Reg[15] + 8);
}
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SXTB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sxtb_inst* inst_cream = (sxtb_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
if (BIT(operand2, 7)) {
operand2 |= 0xffffff00;
} else {
operand2 &= 0xff;
}
RD = operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sxtb_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STR_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int reg = BITS(inst_cream->inst, 12, 15);
unsigned int value = cpu->Reg[reg];
if (reg == 15)
value += 2 * cpu->GetInstructionSize();
cpu->WriteMemory32(addr, value);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UXTB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
uxtb_inst* inst_cream = (uxtb_inst*)inst_base->component;
RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxtb_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UXTAB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
uxtab_inst* inst_cream = (uxtab_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
RD = RN + operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxtab_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STRB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
cpu->WriteMemory8(addr, value);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STRBT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
const u32 previous_mode = cpu->Mode;
const u32 value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
cpu->ChangePrivilegeMode(USER32MODE);
cpu->WriteMemory8(addr, value);
cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STRD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
// The 3DS doesn't have the Large Physical Access Extension (LPAE)
// so STRD wouldn't store these as a single write.
cpu->WriteMemory32(addr + 0, cpu->Reg[BITS(inst_cream->inst, 12, 15)]);
cpu->WriteMemory32(addr + 4, cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1]);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STREX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int write_addr = cpu->Reg[inst_cream->Rn];
if (cpu->IsExclusiveMemoryAccess(write_addr)) {
cpu->UnsetExclusiveMemoryAddress();
cpu->WriteMemory32(write_addr, RM);
RD = 0;
} else {
// Failed to write due to mutex access
RD = 1;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STREXB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int write_addr = cpu->Reg[inst_cream->Rn];
if (cpu->IsExclusiveMemoryAccess(write_addr)) {
cpu->UnsetExclusiveMemoryAddress();
cpu->WriteMemory8(write_addr, cpu->Reg[inst_cream->Rm]);
RD = 0;
} else {
// Failed to write due to mutex access
RD = 1;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STREXD_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int write_addr = cpu->Reg[inst_cream->Rn];
if (cpu->IsExclusiveMemoryAccess(write_addr)) {
cpu->UnsetExclusiveMemoryAddress();
const u32 rt = cpu->Reg[inst_cream->Rm + 0];
const u32 rt2 = cpu->Reg[inst_cream->Rm + 1];
u64 value;
if (cpu->InBigEndianMode())
value = (((u64)rt << 32) | rt2);
else
value = (((u64)rt2 << 32) | rt);
cpu->WriteMemory64(write_addr, value);
RD = 0;
}
else {
// Failed to write due to mutex access
RD = 1;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STREXH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
unsigned int write_addr = cpu->Reg[inst_cream->Rn];
if (cpu->IsExclusiveMemoryAccess(write_addr)) {
cpu->UnsetExclusiveMemoryAddress();
cpu->WriteMemory16(write_addr, RM);
RD = 0;
} else {
// Failed to write due to mutex access
RD = 1;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STRH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xffff;
cpu->WriteMemory16(addr, value);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
STRT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
const u32 previous_mode = cpu->Mode;
const u32 rt_index = BITS(inst_cream->inst, 12, 15);
u32 value = cpu->Reg[rt_index];
if (rt_index == 15)
value += 2 * cpu->GetInstructionSize();
cpu->ChangePrivilegeMode(USER32MODE);
cpu->WriteMemory32(addr, value);
cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SUB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sub_inst* const inst_cream = (sub_inst*)inst_base->component;
u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
bool carry;
bool overflow;
RD = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F);
LOAD_NZCVT;
}
} else if (inst_cream->S) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
cpu->CFlag = carry;
cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sub_inst));
goto DISPATCH;
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sub_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SWI_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swi_inst* const inst_cream = (swi_inst*)inst_base->component;
// SVC::CallSVC(inst_cream->num & 0xFFFF);
cpu->user_callbacks->CallSVC(inst_cream->num & 0xFFFF);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(swi_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SWP_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swp_inst* inst_cream = (swp_inst*)inst_base->component;
addr = RN;
unsigned int value = cpu->ReadMemory32(addr);
cpu->WriteMemory32(addr, RM);
RD = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(swp_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SWPB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swp_inst* inst_cream = (swp_inst*)inst_base->component;
addr = RN;
unsigned int value = cpu->ReadMemory8(addr);
cpu->WriteMemory8(addr, (RM & 0xFF));
RD = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(swp_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SXTAB_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sxtab_inst* inst_cream = (sxtab_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
// Sign extend for byte
operand2 = (0x80 & operand2)? (0xFFFFFF00 | operand2):operand2;
RD = RN + operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxtab_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SXTAB16_INST:
SXTB16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sxtab_inst* const inst_cream = (sxtab_inst*)inst_base->component;
const u8 rotation = inst_cream->rotate * 8;
u32 rm_val = RM;
u32 rn_val = RN;
if (rotation)
rm_val = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
// SXTB16
if (inst_cream->Rn == 15) {
u32 lo = (u32)(s8)rm_val;
u32 hi = (u32)(s8)(rm_val >> 16);
RD = (lo & 0xFFFF) | (hi << 16);
}
// SXTAB16
else {
u32 lo = rn_val + (u32)(s8)(rm_val & 0xFF);
u32 hi = (rn_val >> 16) + (u32)(s8)((rm_val >> 16) & 0xFF);
RD = (lo & 0xFFFF) | (hi << 16);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sxtab_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
SXTAH_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
sxtah_inst* inst_cream = (sxtah_inst*)inst_base->component;
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
// Sign extend for half
operand2 = (0x8000 & operand2) ? (0xFFFF0000 | operand2) : operand2;
RD = RN + operand2;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(sxtah_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
TEQ_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
teq_inst* const inst_cream = (teq_inst*)inst_base->component;
u32 lop = RN;
u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += cpu->GetInstructionSize() * 2;
u32 result = lop ^ rop;
UPDATE_NFLAG(result);
UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(teq_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
TST_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
tst_inst* const inst_cream = (tst_inst*)inst_base->component;
u32 lop = RN;
u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += cpu->GetInstructionSize() * 2;
u32 result = lop & rop;
UPDATE_NFLAG(result);
UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(tst_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UADD8_INST:
UADD16_INST:
UADDSUBX_INST:
USUB8_INST:
USUB16_INST:
USUBADDX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u8 op2 = inst_cream->op2;
const u32 rm_val = RM;
const u32 rn_val = RN;
s32 lo_result = 0;
s32 hi_result = 0;
// UADD16
if (op2 == 0x00) {
lo_result = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
hi_result = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
if (lo_result & 0xFFFF0000) {
cpu->Cpsr |= (1 << 16);
cpu->Cpsr |= (1 << 17);
} else {
cpu->Cpsr &= ~(1 << 16);
cpu->Cpsr &= ~(1 << 17);
}
if (hi_result & 0xFFFF0000) {
cpu->Cpsr |= (1 << 18);
cpu->Cpsr |= (1 << 19);
} else {
cpu->Cpsr &= ~(1 << 18);
cpu->Cpsr &= ~(1 << 19);
}
}
// UASX
else if (op2 == 0x01) {
lo_result = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
hi_result = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
if (lo_result >= 0) {
cpu->Cpsr |= (1 << 16);
cpu->Cpsr |= (1 << 17);
} else {
cpu->Cpsr &= ~(1 << 16);
cpu->Cpsr &= ~(1 << 17);
}
if (hi_result >= 0x10000) {
cpu->Cpsr |= (1 << 18);
cpu->Cpsr |= (1 << 19);
} else {
cpu->Cpsr &= ~(1 << 18);
cpu->Cpsr &= ~(1 << 19);
}
}
// USAX
else if (op2 == 0x02) {
lo_result = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
hi_result = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
if (lo_result >= 0x10000) {
cpu->Cpsr |= (1 << 16);
cpu->Cpsr |= (1 << 17);
} else {
cpu->Cpsr &= ~(1 << 16);
cpu->Cpsr &= ~(1 << 17);
}
if (hi_result >= 0) {
cpu->Cpsr |= (1 << 18);
cpu->Cpsr |= (1 << 19);
} else {
cpu->Cpsr &= ~(1 << 18);
cpu->Cpsr &= ~(1 << 19);
}
}
// USUB16
else if (op2 == 0x03) {
lo_result = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
hi_result = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
if ((lo_result & 0xFFFF0000) == 0) {
cpu->Cpsr |= (1 << 16);
cpu->Cpsr |= (1 << 17);
} else {
cpu->Cpsr &= ~(1 << 16);
cpu->Cpsr &= ~(1 << 17);
}
if ((hi_result & 0xFFFF0000) == 0) {
cpu->Cpsr |= (1 << 18);
cpu->Cpsr |= (1 << 19);
} else {
cpu->Cpsr &= ~(1 << 18);
cpu->Cpsr &= ~(1 << 19);
}
}
// UADD8
else if (op2 == 0x04) {
s16 sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
s16 sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
s16 sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
s16 sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
if (sum1 >= 0x100)
cpu->Cpsr |= (1 << 16);
else
cpu->Cpsr &= ~(1 << 16);
if (sum2 >= 0x100)
cpu->Cpsr |= (1 << 17);
else
cpu->Cpsr &= ~(1 << 17);
if (sum3 >= 0x100)
cpu->Cpsr |= (1 << 18);
else
cpu->Cpsr &= ~(1 << 18);
if (sum4 >= 0x100)
cpu->Cpsr |= (1 << 19);
else
cpu->Cpsr &= ~(1 << 19);
lo_result = ((sum1 & 0xFF) | (sum2 & 0xFF) << 8);
hi_result = ((sum3 & 0xFF) | (sum4 & 0xFF) << 8);
}
// USUB8
else if (op2 == 0x07) {
s16 diff1 = (rn_val & 0xFF) - (rm_val & 0xFF);
s16 diff2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
s16 diff3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
s16 diff4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
if (diff1 >= 0)
cpu->Cpsr |= (1 << 16);
else
cpu->Cpsr &= ~(1 << 16);
if (diff2 >= 0)
cpu->Cpsr |= (1 << 17);
else
cpu->Cpsr &= ~(1 << 17);
if (diff3 >= 0)
cpu->Cpsr |= (1 << 18);
else
cpu->Cpsr &= ~(1 << 18);
if (diff4 >= 0)
cpu->Cpsr |= (1 << 19);
else
cpu->Cpsr &= ~(1 << 19);
lo_result = (diff1 & 0xFF) | ((diff2 & 0xFF) << 8);
hi_result = (diff3 & 0xFF) | ((diff4 & 0xFF) << 8);
}
RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UHADD8_INST:
UHADD16_INST:
UHADDSUBX_INST:
UHSUBADDX_INST:
UHSUB8_INST:
UHSUB16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u32 rm_val = RM;
const u32 rn_val = RN;
const u8 op2 = inst_cream->op2;
if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03)
{
u32 lo_val = 0;
u32 hi_val = 0;
// UHADD16
if (op2 == 0x00) {
lo_val = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
hi_val = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
}
// UHASX
else if (op2 == 0x01) {
lo_val = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
hi_val = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
}
// UHSAX
else if (op2 == 0x02) {
lo_val = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
hi_val = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
}
// UHSUB16
else if (op2 == 0x03) {
lo_val = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
hi_val = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
}
lo_val >>= 1;
hi_val >>= 1;
RD = (lo_val & 0xFFFF) | ((hi_val & 0xFFFF) << 16);
}
else if (op2 == 0x04 || op2 == 0x07) {
u32 sum1;
u32 sum2;
u32 sum3;
u32 sum4;
// UHADD8
if (op2 == 0x04) {
sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
}
// UHSUB8
else {
sum1 = (rn_val & 0xFF) - (rm_val & 0xFF);
sum2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
sum3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
sum4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
}
sum1 >>= 1;
sum2 >>= 1;
sum3 >>= 1;
sum4 >>= 1;
RD = (sum1 & 0xFF) | ((sum2 & 0xFF) << 8) | ((sum3 & 0xFF) << 16) | ((sum4 & 0xFF) << 24);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UMAAL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
const u64 rm = RM;
const u64 rn = RN;
const u64 rd_lo = RDLO;
const u64 rd_hi = RDHI;
const u64 result = (rm * rn) + rd_lo + rd_hi;
RDLO = (result & 0xFFFFFFFF);
RDHI = ((result >> 32) & 0xFFFFFFFF);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(umaal_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UMLAL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
umlal_inst* inst_cream = (umlal_inst*)inst_base->component;
unsigned long long int rm = RM;
unsigned long long int rs = RS;
unsigned long long int rst = rm * rs;
unsigned long long int add = ((unsigned long long) RDHI)<<32;
add += RDLO;
rst += add;
RDLO = BITS(rst, 0, 31);
RDHI = BITS(rst, 32, 63);
if (inst_cream->S) {
cpu->NFlag = BIT(RDHI, 31);
cpu->ZFlag = (RDHI == 0 && RDLO == 0);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(umlal_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UMULL_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
umull_inst* inst_cream = (umull_inst*)inst_base->component;
unsigned long long int rm = RM;
unsigned long long int rs = RS;
unsigned long long int rst = rm * rs;
RDHI = BITS(rst, 32, 63);
RDLO = BITS(rst, 0, 31);
if (inst_cream->S) {
cpu->NFlag = BIT(RDHI, 31);
cpu->ZFlag = (RDHI == 0 && RDLO == 0);
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(umull_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
B_2_THUMB:
{
b_2_thumb* inst_cream = (b_2_thumb*)inst_base->component;
cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
INC_PC(sizeof(b_2_thumb));
goto DISPATCH;
}
B_COND_THUMB:
{
b_cond_thumb* inst_cream = (b_cond_thumb*)inst_base->component;
if(CondPassed(cpu, inst_cream->cond))
cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
else
cpu->Reg[15] += 2;
INC_PC(sizeof(b_cond_thumb));
goto DISPATCH;
}
BL_1_THUMB:
{
bl_1_thumb* inst_cream = (bl_1_thumb*)inst_base->component;
cpu->Reg[14] = cpu->Reg[15] + 4 + inst_cream->imm;
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(bl_1_thumb));
FETCH_INST;
GOTO_NEXT_INST;
}
BL_2_THUMB:
{
bl_2_thumb* inst_cream = (bl_2_thumb*)inst_base->component;
int tmp = ((cpu->Reg[15] + 2) | 1);
cpu->Reg[15] = (cpu->Reg[14] + inst_cream->imm);
cpu->Reg[14] = tmp;
INC_PC(sizeof(bl_2_thumb));
goto DISPATCH;
}
BLX_1_THUMB:
{
// BLX 1 for armv5t and above
u32 tmp = cpu->Reg[15];
blx_1_thumb* inst_cream = (blx_1_thumb*)inst_base->component;
cpu->Reg[15] = (cpu->Reg[14] + inst_cream->imm) & 0xFFFFFFFC;
cpu->Reg[14] = ((tmp + 2) | 1);
cpu->TFlag = 0;
INC_PC(sizeof(blx_1_thumb));
goto DISPATCH;
}
UQADD8_INST:
UQADD16_INST:
UQADDSUBX_INST:
UQSUB8_INST:
UQSUB16_INST:
UQSUBADDX_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
const u8 op2 = inst_cream->op2;
const u32 rm_val = RM;
const u32 rn_val = RN;
u16 lo_val = 0;
u16 hi_val = 0;
// UQADD16
if (op2 == 0x00) {
lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, rm_val & 0xFFFF);
hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
}
// UQASX
else if (op2 == 0x01) {
lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
hi_val = ARMul_UnsignedSaturatedAdd16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
}
// UQSAX
else if (op2 == 0x02) {
lo_val = ARMul_UnsignedSaturatedAdd16(rn_val & 0xFFFF, (rm_val >> 16) & 0xFFFF);
hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, rm_val & 0xFFFF);
}
// UQSUB16
else if (op2 == 0x03) {
lo_val = ARMul_UnsignedSaturatedSub16(rn_val & 0xFFFF, rm_val & 0xFFFF);
hi_val = ARMul_UnsignedSaturatedSub16((rn_val >> 16) & 0xFFFF, (rm_val >> 16) & 0xFFFF);
}
// UQADD8
else if (op2 == 0x04) {
lo_val = ARMul_UnsignedSaturatedAdd8(rn_val, rm_val) |
ARMul_UnsignedSaturatedAdd8(rn_val >> 8, rm_val >> 8) << 8;
hi_val = ARMul_UnsignedSaturatedAdd8(rn_val >> 16, rm_val >> 16) |
ARMul_UnsignedSaturatedAdd8(rn_val >> 24, rm_val >> 24) << 8;
}
// UQSUB8
else {
lo_val = ARMul_UnsignedSaturatedSub8(rn_val, rm_val) |
ARMul_UnsignedSaturatedSub8(rn_val >> 8, rm_val >> 8) << 8;
hi_val = ARMul_UnsignedSaturatedSub8(rn_val >> 16, rm_val >> 16) |
ARMul_UnsignedSaturatedSub8(rn_val >> 24, rm_val >> 24) << 8;
}
RD = ((lo_val & 0xFFFF) | hi_val << 16);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
USAD8_INST:
USADA8_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component;
const u8 ra_idx = inst_cream->Ra;
const u32 rm_val = RM;
const u32 rn_val = RN;
const u8 diff1 = ARMul_UnsignedAbsoluteDifference(rn_val & 0xFF, rm_val & 0xFF);
const u8 diff2 = ARMul_UnsignedAbsoluteDifference((rn_val >> 8) & 0xFF, (rm_val >> 8) & 0xFF);
const u8 diff3 = ARMul_UnsignedAbsoluteDifference((rn_val >> 16) & 0xFF, (rm_val >> 16) & 0xFF);
const u8 diff4 = ARMul_UnsignedAbsoluteDifference((rn_val >> 24) & 0xFF, (rm_val >> 24) & 0xFF);
u32 finalDif = (diff1 + diff2 + diff3 + diff4);
// Op is USADA8 if true.
if (ra_idx != 15)
finalDif += cpu->Reg[ra_idx];
RD = finalDif;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
USAT_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
u8 shift_type = inst_cream->shift_type;
u8 shift_amount = inst_cream->imm5;
u32 rn_val = RN;
// 32-bit ASR is encoded as an amount of 0.
if (shift_type == 1 && shift_amount == 0)
shift_amount = 31;
if (shift_type == 0)
rn_val <<= shift_amount;
else if (shift_type == 1)
rn_val = ((s32)rn_val >> shift_amount);
bool saturated = false;
rn_val = ARMul_UnsignedSatQ(rn_val, inst_cream->sat_imm, &saturated);
if (saturated)
cpu->Cpsr |= (1 << 27);
RD = rn_val;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ssat_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
USAT16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ssat_inst* const inst_cream = (ssat_inst*)inst_base->component;
const u8 saturate_to = inst_cream->sat_imm;
bool sat1 = false;
bool sat2 = false;
RD = (ARMul_UnsignedSatQ((s16)RN, saturate_to, &sat1) & 0xFFFF) |
ARMul_UnsignedSatQ((s32)RN >> 16, saturate_to, &sat2) << 16;
if (sat1 || sat2)
cpu->Cpsr |= (1 << 27);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ssat_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
UXTAB16_INST:
UXTB16_INST:
{
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
uxtab_inst* const inst_cream = (uxtab_inst*)inst_base->component;
const u8 rn_idx = inst_cream->Rn;
const u32 rm_val = RM;
const u32 rotation = inst_cream->rotate * 8;
const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
// UXTB16, otherwise UXTAB16
if (rn_idx == 15) {
RD = rotated_rm & 0x00FF00FF;
} else {
const u32 rn_val = RN;
const u8 lo_rotated = (rotated_rm & 0xFF);
const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
RD = ((hi_result << 16) | (lo_result & 0xFFFF));
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(uxtab_inst));
FETCH_INST;
GOTO_NEXT_INST;
}
WFE_INST:
{
// Stubbed, as WFE is a hint instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
LOG_TRACE(Core_ARM11, "WFE executed.");
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC_STUB;
FETCH_INST;
GOTO_NEXT_INST;
}
WFI_INST:
{
// Stubbed, as WFI is a hint instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
LOG_TRACE(Core_ARM11, "WFI executed.");
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC_STUB;
FETCH_INST;
GOTO_NEXT_INST;
}
YIELD_INST:
{
// Stubbed, as YIELD is a hint instruction.
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
LOG_TRACE(Core_ARM11, "YIELD executed.");
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC_STUB;
FETCH_INST;
GOTO_NEXT_INST;
}
#define VFP_INTERPRETER_IMPL
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_INTERPRETER_IMPL
END:
{
SAVE_NZCVT;
cpu->NumInstrsToExecute = 0;
return num_instrs;
}
INIT_INST_LENGTH:
{
cpu->NumInstrsToExecute = 0;
return num_instrs;
}
}