// 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)
#include <algorithm>
#include <cinttypes>
#include <cstdio>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/arm/dyncom/arm_dyncom_dec.h"
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
#include "core/arm/dyncom/arm_dyncom_run.h"
#include "core/arm/dyncom/arm_dyncom_thumb.h"
#include "core/arm/dyncom/arm_dyncom_trans.h"
#include "core/arm/skyeye_common/armstate.h"
#include "core/arm/skyeye_common/armsupp.h"
#include "core/arm/skyeye_common/vfp/vfp.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/svc.h"
#include "core/memory.h"
#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 = 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");
}
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) {
u32 inst_size = 4;
u32 inst = Memory::Read32(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) {
LOG_ERROR(Core_ARM11, "Decode failure.\tPC: [0x%08" PRIX32 "]\tInstruction: %08" PRIX32,
phys_addr, inst);
LOG_ERROR(Core_ARM11, "cpsr=0x%" PRIX32 ", cpu->TFlag=%d, r15=0x%08" PRIX32, 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 = 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 = 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::IsServerEnabled()) { \
if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None && \
PC == breakpoint_data.address)) { \
GDBStub::Break(); \
goto END; \
} \
}
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
// clunky switch statement.
#if defined __GNUC__ || defined __clang__
#define GOTO_NEXT_INST \
GDB_BP_CHECK; \
if (num_instrs >= cpu->NumInstrsToExecute) \
goto END; \
num_instrs++; \
goto* InstLabel[inst_base->idx]
#else
#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; \
}
#endif
#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])
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback
// to a clunky switch statement.
#if defined __GNUC__ || defined __clang__
void* InstLabel[] = {&&VMLA_INST,
&&VMLS_INST,
&&VNMLA_INST,
&&VNMLS_INST,
&&VNMUL_INST,
&&VMUL_INST,
&&VADD_INST,
&&VSUB_INST,
&&VDIV_INST,
&&VMOVI_INST,
&&VMOVR_INST,
&&VABS_INST,
&&VNEG_INST,
&&VSQRT_INST,
&&VCMP_INST,
&&VCMP2_INST,
&&VCVTBDS_INST,
&&VCVTBFF_INST,
&&VCVTBFI_INST,
&&VMOVBRS_INST,
&&VMSR_INST,
&&VMOVBRC_INST,
&&VMRS_INST,
&&VMOVBCR_INST,
&&VMOVBRRSS_INST,
&&VMOVBRRD_INST,
&&VSTR_INST,
&&VPUSH_INST,
&&VSTM_INST,
&&VPOP_INST,
&&VLDR_INST,
&&VLDM_INST,
&&SRS_INST,
&&RFE_INST,
&&BKPT_INST,
&&BLX_INST,
&&CPS_INST,
&&PLD_INST,
&&SETEND_INST,
&&CLREX_INST,
&&REV16_INST,
&&USAD8_INST,
&&SXTB_INST,
&&UXTB_INST,
&&SXTH_INST,
&&SXTB16_INST,
&&UXTH_INST,
&&UXTB16_INST,
&&CPY_INST,
&&UXTAB_INST,
&&SSUB8_INST,
&&SHSUB8_INST,
&&SSUBADDX_INST,
&&STREX_INST,
&&STREXB_INST,
&&SWP_INST,
&&SWPB_INST,
&&SSUB16_INST,
&&SSAT16_INST,
&&SHSUBADDX_INST,
&&QSUBADDX_INST,
&&SHADDSUBX_INST,
&&SHADD8_INST,
&&SHADD16_INST,
&&SEL_INST,
&&SADDSUBX_INST,
&&SADD8_INST,
&&SADD16_INST,
&&SHSUB16_INST,
&&UMAAL_INST,
&&UXTAB16_INST,
&&USUBADDX_INST,
&&USUB8_INST,
&&USUB16_INST,
&&USAT16_INST,
&&USADA8_INST,
&&UQSUBADDX_INST,
&&UQSUB8_INST,
&&UQSUB16_INST,
&&UQADDSUBX_INST,
&&UQADD8_INST,
&&UQADD16_INST,
&&SXTAB_INST,
&&UHSUBADDX_INST,
&&UHSUB8_INST,
&&UHSUB16_INST,
&&UHADDSUBX_INST,
&&UHADD8_INST,
&&UHADD16_INST,
&&UADDSUBX_INST,
&&UADD8_INST,
&&UADD16_INST,
&&SXTAH_INST,
&&SXTAB16_INST,
&&QADD8_INST,
&&BXJ_INST,
&&CLZ_INST,
&&UXTAH_INST,
&&BX_INST,
&&REV_INST,
&&BLX_INST,
&&REVSH_INST,
&&QADD_INST,
&&QADD16_INST,
&&QADDSUBX_INST,
&&LDREX_INST,
&&QDADD_INST,
&&QDSUB_INST,
&&QSUB_INST,
&&LDREXB_INST,
&&QSUB8_INST,
&&QSUB16_INST,
&&SMUAD_INST,
&&SMMUL_INST,
&&SMUSD_INST,
&&SMLSD_INST,
&&SMLSLD_INST,
&&SMMLA_INST,
&&SMMLS_INST,
&&SMLALD_INST,
&&SMLAD_INST,
&&SMLAW_INST,
&&SMULW_INST,
&&PKHTB_INST,
&&PKHBT_INST,
&&SMUL_INST,
&&SMLALXY_INST,
&&SMLA_INST,
&&MCRR_INST,
&&MRRC_INST,
&&CMP_INST,
&&TST_INST,
&&TEQ_INST,
&&CMN_INST,
&&SMULL_INST,
&&UMULL_INST,
&&UMLAL_INST,
&&SMLAL_INST,
&&MUL_INST,
&&MLA_INST,
&&SSAT_INST,
&&USAT_INST,
&&MRS_INST,
&&MSR_INST,
&&AND_INST,
&&BIC_INST,
&&LDM_INST,
&&EOR_INST,
&&ADD_INST,
&&RSB_INST,
&&RSC_INST,
&&SBC_INST,
&&ADC_INST,
&&SUB_INST,
&&ORR_INST,
&&MVN_INST,
&&MOV_INST,
&&STM_INST,
&&LDM_INST,
&&LDRSH_INST,
&&STM_INST,
&&LDM_INST,
&&LDRSB_INST,
&&STRD_INST,
&&LDRH_INST,
&&STRH_INST,
&&LDRD_INST,
&&STRT_INST,
&&STRBT_INST,
&&LDRBT_INST,
&&LDRT_INST,
&&MRC_INST,
&&MCR_INST,
&&MSR_INST,
&&MSR_INST,
&&MSR_INST,
&&MSR_INST,
&&MSR_INST,
&&LDRB_INST,
&&STRB_INST,
&&LDR_INST,
&&LDRCOND_INST,
&&STR_INST,
&&CDP_INST,
&&STC_INST,
&&LDC_INST,
&&LDREXD_INST,
&&STREXD_INST,
&&LDREXH_INST,
&&STREXH_INST,
&&NOP_INST,
&&YIELD_INST,
&&WFE_INST,
&&WFI_INST,
&&SEV_INST,
&&SWI_INST,
&&BBL_INST,
&&B_2_THUMB,
&&B_COND_THUMB,
&&BL_1_THUMB,
&&BL_2_THUMB,
&&BLX_1_THUMB,
&&DISPATCH,
&&INIT_INST_LENGTH,
&&END};
#endif
arm_inst* inst_base;
unsigned int addr;
unsigned int num_instrs = 0;
int ptr;
LOAD_NZCVT;
DISPATCH : {
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::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);
}
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);
}
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 (int 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->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 "core/arm/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;
}
}