// 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 #include #include "common/common_types.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "core/memory.h" #include "core/hle/svc.h" #include "core/arm/disassembler/arm_disasm.h" #include "core/arm/dyncom/arm_dyncom_dec.h" #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_thumb.h" #include "core/arm/dyncom/arm_dyncom_trans.h" #include "core/arm/dyncom/arm_dyncom_run.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" #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(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(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(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(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(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) { unsigned int inst_size = 4; unsigned int 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) { std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, disasm.c_str(), inst); LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); 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::g_server_enabled) { \ 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::g_server_enabled && GDBStub::IsConnected()) { breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute); } inst_base = (arm_inst *)&trans_cache_buf[ptr]; GOTO_NEXT_INST; } ADC_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { adc_inst* const inst_cream = (adc_inst*)inst_base->component; u32 rn_val = RN; if (inst_cream->Rn == 15) rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; RD = AddWithCarry(rn_val, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow); if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); cpu->CFlag = carry; cpu->VFlag = overflow; } if (inst_cream->Rd == 15) { INC_PC(sizeof(adc_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(adc_inst)); FETCH_INST; GOTO_NEXT_INST; } ADD_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { add_inst* const inst_cream = (add_inst*)inst_base->component; u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn); bool carry; bool overflow; RD = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow); if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); cpu->CFlag = carry; cpu->VFlag = overflow; } if (inst_cream->Rd == 15) { INC_PC(sizeof(add_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(add_inst)); FETCH_INST; GOTO_NEXT_INST; } AND_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { and_inst* const inst_cream = (and_inst*)inst_base->component; u32 lop = RN; u32 rop = SHIFTER_OPERAND; if (inst_cream->Rn == 15) lop += 2 * cpu->GetInstructionSize(); RD = lop & rop; if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); UPDATE_CFLAG_WITH_SC; } if (inst_cream->Rd == 15) { INC_PC(sizeof(and_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(and_inst)); FETCH_INST; GOTO_NEXT_INST; } BBL_INST: { if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { bbl_inst *inst_cream = (bbl_inst *)inst_base->component; if (inst_cream->L) { LINK_RTN_ADDR; } SET_PC; INC_PC(sizeof(bbl_inst)); goto DISPATCH; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bbl_inst)); goto DISPATCH; } BIC_INST: { bic_inst *inst_cream = (bic_inst *)inst_base->component; if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { u32 lop = RN; if (inst_cream->Rn == 15) { lop += 2 * cpu->GetInstructionSize(); } u32 rop = SHIFTER_OPERAND; RD = lop & (~rop); if ((inst_cream->S) && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); UPDATE_CFLAG_WITH_SC; } if (inst_cream->Rd == 15) { INC_PC(sizeof(bic_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bic_inst)); FETCH_INST; GOTO_NEXT_INST; } BKPT_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { bkpt_inst* const inst_cream = (bkpt_inst*)inst_base->component; LOG_DEBUG(Core_ARM11, "Breakpoint instruction hit. Immediate: 0x%08X", inst_cream->imm); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bkpt_inst)); FETCH_INST; GOTO_NEXT_INST; } BLX_INST: { blx_inst *inst_cream = (blx_inst *)inst_base->component; if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) { unsigned int inst = inst_cream->inst; if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) { const u32 jump_address = cpu->Reg[inst_cream->val.Rm]; cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); if(cpu->TFlag) cpu->Reg[14] |= 0x1; cpu->Reg[15] = jump_address & 0xfffffffe; cpu->TFlag = jump_address & 0x1; } else { cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize()); cpu->TFlag = 0x1; int signed_int = inst_cream->val.signed_immed_24; signed_int = (signed_int & 0x800000) ? (0x3F000000 | signed_int) : signed_int; signed_int = signed_int << 2; cpu->Reg[15] = cpu->Reg[15] + 8 + signed_int + (BIT(inst, 24) << 1); } INC_PC(sizeof(blx_inst)); goto DISPATCH; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(blx_inst)); goto DISPATCH; } BX_INST: BXJ_INST: { // Note that only the 'fail' case of BXJ is emulated. This is because // the facilities for Jazelle emulation are not implemented. // // According to the ARM documentation on BXJ, if setting the J bit in the APSR // fails, then BXJ functions identically like a regular BX instruction. // // This is sufficient for citra, as the CPU for the 3DS does not implement Jazelle. if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { bx_inst* const inst_cream = (bx_inst*)inst_base->component; u32 address = RM; if (inst_cream->Rm == 15) address += 2 * cpu->GetInstructionSize(); cpu->TFlag = address & 1; cpu->Reg[15] = address & 0xfffffffe; INC_PC(sizeof(bx_inst)); goto DISPATCH; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(bx_inst)); goto DISPATCH; } CDP_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { // Undefined instruction here cpu->NumInstrsToExecute = 0; return num_instrs; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cdp_inst)); FETCH_INST; GOTO_NEXT_INST; } CLREX_INST: { cpu->UnsetExclusiveMemoryAddress(); cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(clrex_inst)); FETCH_INST; GOTO_NEXT_INST; } CLZ_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { clz_inst* inst_cream = (clz_inst*)inst_base->component; RD = clz(RM); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(clz_inst)); FETCH_INST; GOTO_NEXT_INST; } CMN_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { cmn_inst* const inst_cream = (cmn_inst*)inst_base->component; u32 rn_val = RN; if (inst_cream->Rn == 15) rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; u32 result = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow); UPDATE_NFLAG(result); UPDATE_ZFLAG(result); cpu->CFlag = carry; cpu->VFlag = overflow; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cmn_inst)); FETCH_INST; GOTO_NEXT_INST; } CMP_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { cmp_inst* const inst_cream = (cmp_inst*)inst_base->component; u32 rn_val = RN; if (inst_cream->Rn == 15) rn_val += 2 * cpu->GetInstructionSize(); bool carry; bool overflow; u32 result = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow); UPDATE_NFLAG(result); UPDATE_ZFLAG(result); cpu->CFlag = carry; cpu->VFlag = overflow; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cmp_inst)); FETCH_INST; GOTO_NEXT_INST; } CPS_INST: { cps_inst *inst_cream = (cps_inst *)inst_base->component; u32 aif_val = 0; u32 aif_mask = 0; if (cpu->InAPrivilegedMode()) { if (inst_cream->imod1) { if (inst_cream->A) { aif_val |= (inst_cream->imod0 << 8); aif_mask |= 1 << 8; } if (inst_cream->I) { aif_val |= (inst_cream->imod0 << 7); aif_mask |= 1 << 7; } if (inst_cream->F) { aif_val |= (inst_cream->imod0 << 6); aif_mask |= 1 << 6; } aif_mask = ~aif_mask; cpu->Cpsr = (cpu->Cpsr & aif_mask) | aif_val; } if (inst_cream->mmod) { cpu->Cpsr = (cpu->Cpsr & 0xffffffe0) | inst_cream->mode; cpu->ChangePrivilegeMode(inst_cream->mode); } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(cps_inst)); FETCH_INST; GOTO_NEXT_INST; } CPY_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { mov_inst* inst_cream = (mov_inst*)inst_base->component; RD = SHIFTER_OPERAND; if (inst_cream->Rd == 15) { INC_PC(sizeof(mov_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mov_inst)); FETCH_INST; GOTO_NEXT_INST; } EOR_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { eor_inst* inst_cream = (eor_inst*)inst_base->component; u32 lop = RN; if (inst_cream->Rn == 15) { lop += 2 * cpu->GetInstructionSize(); } u32 rop = SHIFTER_OPERAND; RD = lop ^ rop; if (inst_cream->S && (inst_cream->Rd == 15)) { if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Spsr_copy & 0x1F); LOAD_NZCVT; } } else if (inst_cream->S) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); UPDATE_CFLAG_WITH_SC; } if (inst_cream->Rd == 15) { INC_PC(sizeof(eor_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(eor_inst)); FETCH_INST; GOTO_NEXT_INST; } LDC_INST: { // Instruction not implemented //LOG_CRITICAL(Core_ARM11, "unimplemented instruction"); cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldc_inst)); FETCH_INST; GOTO_NEXT_INST; } LDM_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int inst = inst_cream->inst; if (BIT(inst, 22) && !BIT(inst, 15)) { for (int i = 0; i < 13; i++) { if(BIT(inst, i)) { cpu->Reg[i] = cpu->ReadMemory32(addr); addr += 4; } } if (BIT(inst, 13)) { if (cpu->Mode == USER32MODE) cpu->Reg[13] = cpu->ReadMemory32(addr); else cpu->Reg_usr[0] = cpu->ReadMemory32(addr); addr += 4; } if (BIT(inst, 14)) { if (cpu->Mode == USER32MODE) cpu->Reg[14] = cpu->ReadMemory32(addr); else cpu->Reg_usr[1] = cpu->ReadMemory32(addr); addr += 4; } } else if (!BIT(inst, 22)) { for(int i = 0; i < 16; i++ ){ if(BIT(inst, i)){ unsigned int ret = cpu->ReadMemory32(addr); // For armv5t, should enter thumb when bits[0] is non-zero. if(i == 15){ cpu->TFlag = ret & 0x1; ret &= 0xFFFFFFFE; } cpu->Reg[i] = ret; addr += 4; } } } else if (BIT(inst, 22) && BIT(inst, 15)) { for(int i = 0; i < 15; i++ ){ if(BIT(inst, i)){ cpu->Reg[i] = cpu->ReadMemory32(addr); addr += 4; } } if (CurrentModeHasSPSR) { cpu->Cpsr = cpu->Spsr_copy; cpu->ChangePrivilegeMode(cpu->Cpsr & 0x1F); LOAD_NZCVT; } cpu->Reg[15] = cpu->ReadMemory32(addr); } if (BIT(inst, 15)) { INC_PC(sizeof(ldst_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } SXTH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { sxth_inst* inst_cream = (sxth_inst*)inst_base->component; unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate); if (BIT(operand2, 15)) { operand2 |= 0xffff0000; } else { operand2 &= 0xffff; } RD = operand2; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(sxth_inst)); FETCH_INST; GOTO_NEXT_INST; } LDR_INST: { ldst_inst *inst_cream = (ldst_inst *)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { // For armv5t, should enter thumb when bits[0] is non-zero. cpu->TFlag = value & 0x1; cpu->Reg[15] &= 0xFFFFFFFE; INC_PC(sizeof(ldst_inst)); goto DISPATCH; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRCOND_INST: { if (CondPassed(cpu, inst_base->cond)) { ldst_inst *inst_cream = (ldst_inst *)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; if (BITS(inst_cream->inst, 12, 15) == 15) { // For armv5t, should enter thumb when bits[0] is non-zero. cpu->TFlag = value & 0x1; cpu->Reg[15] &= 0xFFFFFFFE; INC_PC(sizeof(ldst_inst)); goto DISPATCH; } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } UXTH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { uxth_inst* inst_cream = (uxth_inst*)inst_base->component; RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxth_inst)); FETCH_INST; GOTO_NEXT_INST; } UXTAH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { uxtah_inst* inst_cream = (uxtah_inst*)inst_base->component; unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff; RD = RN + operand2; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(uxtah_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRB_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRBT_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); const u32 dest_index = BITS(inst_cream->inst, 12, 15); const u32 previous_mode = cpu->Mode; cpu->ChangePrivilegeMode(USER32MODE); const u8 value = cpu->ReadMemory8(addr); cpu->ChangePrivilegeMode(previous_mode); cpu->Reg[dest_index] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRD_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; // Should check if RD is even-numbered, Rd != 14, addr[0:1] == 0, (CP15_reg1_U == 1 || addr[2] == 0) inst_cream->get_addr(cpu, inst_cream->inst, addr); // The 3DS doesn't have LPAE (Large Physical Access Extension), so it // wouldn't do this as a single read. cpu->Reg[BITS(inst_cream->inst, 12, 15) + 0] = cpu->ReadMemory32(addr); cpu->Reg[BITS(inst_cream->inst, 12, 15) + 1] = cpu->ReadMemory32(addr + 4); // No dispatch since this operation should not modify R15 } cpu->Reg[15] += 4; INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDREX_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory32(read_addr); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; } LDREXB_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory8(read_addr); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; } LDREXH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory16(read_addr); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; } LDREXD_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { generic_arm_inst* inst_cream = (generic_arm_inst*)inst_base->component; unsigned int read_addr = RN; cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory32(read_addr); RD2 = cpu->ReadMemory32(read_addr + 4); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRSB_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->ReadMemory8(addr); if (BIT(value, 7)) { value |= 0xffffff00; } cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRSH_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); unsigned int value = cpu->ReadMemory16(addr); if (BIT(value, 15)) { value |= 0xffff0000; } cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } LDRT_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); const u32 dest_index = BITS(inst_cream->inst, 12, 15); const u32 previous_mode = cpu->Mode; cpu->ChangePrivilegeMode(USER32MODE); const u32 value = cpu->ReadMemory32(addr); cpu->ChangePrivilegeMode(previous_mode); cpu->Reg[dest_index] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); FETCH_INST; GOTO_NEXT_INST; } MCR_INST: { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { mcr_inst* inst_cream = (mcr_inst*)inst_base->component; unsigned int inst = inst_cream->inst; if (inst_cream->Rd == 15) { DEBUG_MSG; } else { if (inst_cream->cp_num == 15) cpu->WriteCP15Register(RD, CRn, OPCODE_1, CRm, OPCODE_2); } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mcr_inst)); FETCH_INST; GOTO_NEXT_INST; } MCRR_INST: { // Stubbed, as the MPCore doesn't have any registers that are accessible // through this instruction. if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { mcrr_inst* const inst_cream = (mcrr_inst*)inst_base->component; LOG_ERROR(Core_ARM11, "MCRR executed | Coprocessor: %u, CRm %u, opc1: %u, Rt: %u, Rt2: %u", inst_cream->cp_num, inst_cream->crm, inst_cream->opcode_1, inst_cream->rt, inst_cream->rt2); } 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((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((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(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); RD = operand1 * operand2 + RN; if (AddOverflow(operand1 * operand2, RN, RD)) cpu->Cpsr |= (1 << 27); } 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_usr[1] + 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_usr[1] + 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 | (hi << 16)); } // SXTAB16 else { u32 lo = (rn_val & 0xFFFF) + (u32)(s8)(rm_val & 0xFF); u32 hi = ((rn_val >> 16) & 0xFFFF) + (u32)(s8)((rm_val >> 16) & 0xFF); RD = (lo | (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; } }