// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#define CITRA_IGNORE_EXIT(x)

#include <algorithm>
#include <cinttypes>
#include <common/log.h>
#include <common/types.h>
#include <cpu/dyncom/arm_dyncom_dec.h>
#include <cpu/dyncom/arm_dyncom_interpreter.h>
#include <cpu/dyncom/arm_dyncom_run.h>
#include <cpu/dyncom/arm_dyncom_thumb.h>
#include <cpu/dyncom/arm_dyncom_trans.h>
#include <cpu/dyncom/armstate.h>
#include <cpu/dyncom/armsupp.h>
#include <cpu/dyncom/vfp/vfp.h>
#include <cstdio>

#include <cpu/arm_interface.h>

#define RM BITS(sht_oper, 0, 3)
#define RS BITS(sht_oper, 8, 11)

#define glue(x, y) x##y
#define DPO(s) glue(DataProcessingOperands, s)
#define ROTATE_RIGHT(n, i, l) ((n << (l - i)) | (n >> i))
#define ROTATE_LEFT(n, i, l) ((n >> (l - i)) | (n << i))
#define ROTATE_RIGHT_32(n, i) ROTATE_RIGHT(n, i, 32)
#define ROTATE_LEFT_32(n, i) ROTATE_LEFT(n, i, 32)

static bool CondPassed(const ARMul_State *cpu, unsigned int cond) {
    const bool n_flag = cpu->NFlag != 0;
    const bool z_flag = cpu->ZFlag != 0;
    const bool c_flag = cpu->CFlag != 0;
    const bool v_flag = cpu->VFlag != 0;

    switch (cond) {
    case ConditionCode::EQ:
        return z_flag;
    case ConditionCode::NE:
        return !z_flag;
    case ConditionCode::CS:
        return c_flag;
    case ConditionCode::CC:
        return !c_flag;
    case ConditionCode::MI:
        return n_flag;
    case ConditionCode::PL:
        return !n_flag;
    case ConditionCode::VS:
        return v_flag;
    case ConditionCode::VC:
        return !v_flag;
    case ConditionCode::HI:
        return (c_flag && !z_flag);
    case ConditionCode::LS:
        return (!c_flag || z_flag);
    case ConditionCode::GE:
        return (n_flag == v_flag);
    case ConditionCode::LT:
        return (n_flag != v_flag);
    case ConditionCode::GT:
        return (!z_flag && (n_flag == v_flag));
    case ConditionCode::LE:
        return (z_flag || (n_flag != v_flag));
    case ConditionCode::AL:
    case ConditionCode::NV: // Unconditional
        return true;
    }

    return false;
}

static unsigned int DPO(Immediate)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int immed_8 = BITS(sht_oper, 0, 7);
    unsigned int rotate_imm = BITS(sht_oper, 8, 11);
    unsigned int shifter_operand = ROTATE_RIGHT_32(immed_8, rotate_imm * 2);
    if (rotate_imm == 0)
        cpu->shifter_carry_out = cpu->CFlag;
    else
        cpu->shifter_carry_out = BIT(shifter_operand, 31);
    return shifter_operand;
}

static unsigned int DPO(Register)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand = rm;
    cpu->shifter_carry_out = cpu->CFlag;
    return shifter_operand;
}

static unsigned int DPO(LogicalShiftLeftByImmediate)(ARMul_State *cpu, unsigned int sht_oper) {
    int shift_imm = BITS(sht_oper, 7, 11);
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand;
    if (shift_imm == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = cpu->CFlag;
    } else {
        shifter_operand = rm << shift_imm;
        cpu->shifter_carry_out = BIT(rm, 32 - shift_imm);
    }
    return shifter_operand;
}

static unsigned int DPO(LogicalShiftLeftByRegister)(ARMul_State *cpu, unsigned int sht_oper) {
    int shifter_operand;
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int rs = CHECK_READ_REG15(cpu, RS);
    if (BITS(rs, 0, 7) == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = cpu->CFlag;
    } else if (BITS(rs, 0, 7) < 32) {
        shifter_operand = rm << BITS(rs, 0, 7);
        cpu->shifter_carry_out = BIT(rm, 32 - BITS(rs, 0, 7));
    } else if (BITS(rs, 0, 7) == 32) {
        shifter_operand = 0;
        cpu->shifter_carry_out = BIT(rm, 0);
    } else {
        shifter_operand = 0;
        cpu->shifter_carry_out = 0;
    }
    return shifter_operand;
}

static unsigned int DPO(LogicalShiftRightByImmediate)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand;
    int shift_imm = BITS(sht_oper, 7, 11);
    if (shift_imm == 0) {
        shifter_operand = 0;
        cpu->shifter_carry_out = BIT(rm, 31);
    } else {
        shifter_operand = rm >> shift_imm;
        cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
    }
    return shifter_operand;
}

static unsigned int DPO(LogicalShiftRightByRegister)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rs = CHECK_READ_REG15(cpu, RS);
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand;
    if (BITS(rs, 0, 7) == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = cpu->CFlag;
    } else if (BITS(rs, 0, 7) < 32) {
        shifter_operand = rm >> BITS(rs, 0, 7);
        cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
    } else if (BITS(rs, 0, 7) == 32) {
        shifter_operand = 0;
        cpu->shifter_carry_out = BIT(rm, 31);
    } else {
        shifter_operand = 0;
        cpu->shifter_carry_out = 0;
    }
    return shifter_operand;
}

static unsigned int DPO(ArithmeticShiftRightByImmediate)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand;
    int shift_imm = BITS(sht_oper, 7, 11);
    if (shift_imm == 0) {
        if (BIT(rm, 31) == 0)
            shifter_operand = 0;
        else
            shifter_operand = 0xFFFFFFFF;
        cpu->shifter_carry_out = BIT(rm, 31);
    } else {
        shifter_operand = static_cast<int>(rm) >> shift_imm;
        cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
    }
    return shifter_operand;
}

static unsigned int DPO(ArithmeticShiftRightByRegister)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rs = CHECK_READ_REG15(cpu, RS);
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int shifter_operand;
    if (BITS(rs, 0, 7) == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = cpu->CFlag;
    } else if (BITS(rs, 0, 7) < 32) {
        shifter_operand = static_cast<int>(rm) >> BITS(rs, 0, 7);
        cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 7) - 1);
    } else {
        if (BIT(rm, 31) == 0)
            shifter_operand = 0;
        else
            shifter_operand = 0xffffffff;
        cpu->shifter_carry_out = BIT(rm, 31);
    }
    return shifter_operand;
}

static unsigned int DPO(RotateRightByImmediate)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int shifter_operand;
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    int shift_imm = BITS(sht_oper, 7, 11);
    if (shift_imm == 0) {
        shifter_operand = (cpu->CFlag << 31) | (rm >> 1);
        cpu->shifter_carry_out = BIT(rm, 0);
    } else {
        shifter_operand = ROTATE_RIGHT_32(rm, shift_imm);
        cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
    }
    return shifter_operand;
}

static unsigned int DPO(RotateRightByRegister)(ARMul_State *cpu, unsigned int sht_oper) {
    unsigned int rm = CHECK_READ_REG15(cpu, RM);
    unsigned int rs = CHECK_READ_REG15(cpu, RS);
    unsigned int shifter_operand;
    if (BITS(rs, 0, 7) == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = cpu->CFlag;
    } else if (BITS(rs, 0, 4) == 0) {
        shifter_operand = rm;
        cpu->shifter_carry_out = BIT(rm, 31);
    } else {
        shifter_operand = ROTATE_RIGHT_32(rm, BITS(rs, 0, 4));
        cpu->shifter_carry_out = BIT(rm, BITS(rs, 0, 4) - 1);
    }
    return shifter_operand;
}

#define DEBUG_MSG                                        \
    LOG_DEBUG(eka2l1::CPU_DYNCOM, "inst is {:x}", inst); \
    CITRA_IGNORE_EXIT(0)

#define LnSWoUB(s) glue(LnSWoUB, s)
#define MLnS(s) glue(MLnS, s)
#define LdnStM(s) glue(LdnStM, s)

#define W_BIT BIT(inst, 21)
#define U_BIT BIT(inst, 23)
#define I_BIT BIT(inst, 25)
#define P_BIT BIT(inst, 24)
#define OFFSET_12 BITS(inst, 0, 11)

static void LnSWoUB(ImmediateOffset)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int addr;

    if (U_BIT)
        addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
    else
        addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;

    virt_addr = addr;
}

static void LnSWoUB(RegisterOffset)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
    unsigned int addr;

    if (U_BIT)
        addr = rn + rm;
    else
        addr = rn - rm;

    virt_addr = addr;
}

static void LnSWoUB(ImmediatePostIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);

    if (U_BIT)
        cpu->Reg[Rn] += OFFSET_12;
    else
        cpu->Reg[Rn] -= OFFSET_12;

    virt_addr = addr;
}

static void LnSWoUB(ImmediatePreIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int addr;

    if (U_BIT)
        addr = CHECK_READ_REG15_WA(cpu, Rn) + OFFSET_12;
    else
        addr = CHECK_READ_REG15_WA(cpu, Rn) - OFFSET_12;

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31)))
        cpu->Reg[Rn] = addr;
}

static void MLnS(RegisterPreIndexed)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int addr;
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);

    if (U_BIT)
        addr = rn + rm;
    else
        addr = rn - rm;

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31)))
        cpu->Reg[Rn] = addr;
}

static void LnSWoUB(RegisterPreIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
    unsigned int addr;

    if (U_BIT)
        addr = rn + rm;
    else
        addr = rn - rm;

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31))) {
        cpu->Reg[Rn] = addr;
    }
}

static void LnSWoUB(ScaledRegisterPreIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int shift = BITS(inst, 5, 6);
    unsigned int shift_imm = BITS(inst, 7, 11);
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int index = 0;
    unsigned int addr;
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);

    switch (shift) {
    case 0:
        index = rm << shift_imm;
        break;
    case 1:
        if (shift_imm == 0) {
            index = 0;
        } else {
            index = rm >> shift_imm;
        }
        break;
    case 2:
        if (shift_imm == 0) { // ASR #32
            if (BIT(rm, 31) == 1)
                index = 0xFFFFFFFF;
            else
                index = 0;
        } else {
            index = static_cast<int>(rm) >> shift_imm;
        }
        break;
    case 3:
        if (shift_imm == 0) {
            index = (cpu->CFlag << 31) | (rm >> 1);
        } else {
            index = ROTATE_RIGHT_32(rm, shift_imm);
        }
        break;
    }

    if (U_BIT)
        addr = rn + index;
    else
        addr = rn - index;

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31)))
        cpu->Reg[Rn] = addr;
}

static void LnSWoUB(ScaledRegisterPostIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int shift = BITS(inst, 5, 6);
    unsigned int shift_imm = BITS(inst, 7, 11);
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int index = 0;
    unsigned int addr = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);

    switch (shift) {
    case 0:
        index = rm << shift_imm;
        break;
    case 1:
        if (shift_imm == 0) {
            index = 0;
        } else {
            index = rm >> shift_imm;
        }
        break;
    case 2:
        if (shift_imm == 0) { // ASR #32
            if (BIT(rm, 31) == 1)
                index = 0xFFFFFFFF;
            else
                index = 0;
        } else {
            index = static_cast<int>(rm) >> shift_imm;
        }
        break;
    case 3:
        if (shift_imm == 0) {
            index = (cpu->CFlag << 31) | (rm >> 1);
        } else {
            index = ROTATE_RIGHT_32(rm, shift_imm);
        }
        break;
    }

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31))) {
        if (U_BIT)
            cpu->Reg[Rn] += index;
        else
            cpu->Reg[Rn] -= index;
    }
}

static void LnSWoUB(RegisterPostIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);

    virt_addr = CHECK_READ_REG15_WA(cpu, Rn);

    if (CondPassed(cpu, BITS(inst, 28, 31))) {
        if (U_BIT) {
            cpu->Reg[Rn] += rm;
        } else {
            cpu->Reg[Rn] -= rm;
        }
    }
}

static void MLnS(ImmediateOffset)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int immedL = BITS(inst, 0, 3);
    unsigned int immedH = BITS(inst, 8, 11);
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int addr;

    unsigned int offset_8 = (immedH << 4) | immedL;

    if (U_BIT)
        addr = CHECK_READ_REG15_WA(cpu, Rn) + offset_8;
    else
        addr = CHECK_READ_REG15_WA(cpu, Rn) - offset_8;

    virt_addr = addr;
}

static void MLnS(RegisterOffset)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int addr;
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);

    if (U_BIT)
        addr = rn + rm;
    else
        addr = rn - rm;

    virt_addr = addr;
}

static void MLnS(ImmediatePreIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int immedH = BITS(inst, 8, 11);
    unsigned int immedL = BITS(inst, 0, 3);
    unsigned int addr;
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int offset_8 = (immedH << 4) | immedL;

    if (U_BIT)
        addr = rn + offset_8;
    else
        addr = rn - offset_8;

    virt_addr = addr;

    if (CondPassed(cpu, BITS(inst, 28, 31)))
        cpu->Reg[Rn] = addr;
}

static void MLnS(ImmediatePostIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int immedH = BITS(inst, 8, 11);
    unsigned int immedL = BITS(inst, 0, 3);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);

    virt_addr = rn;

    if (CondPassed(cpu, BITS(inst, 28, 31))) {
        unsigned int offset_8 = (immedH << 4) | immedL;
        if (U_BIT)
            rn += offset_8;
        else
            rn -= offset_8;

        cpu->Reg[Rn] = rn;
    }
}

static void MLnS(RegisterPostIndexed)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);

    virt_addr = CHECK_READ_REG15_WA(cpu, Rn);

    if (CondPassed(cpu, BITS(inst, 28, 31))) {
        if (U_BIT)
            cpu->Reg[Rn] += rm;
        else
            cpu->Reg[Rn] -= rm;
    }
}

static void LdnStM(DecrementBefore)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int i = BITS(inst, 0, 15);
    int count = 0;

    while (i) {
        if (i & 1)
            count++;
        i = i >> 1;
    }

    virt_addr = CHECK_READ_REG15_WA(cpu, Rn) - count * 4;

    if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
        cpu->Reg[Rn] -= count * 4;
}

static void LdnStM(IncrementBefore)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int i = BITS(inst, 0, 15);
    int count = 0;

    while (i) {
        if (i & 1)
            count++;
        i = i >> 1;
    }

    virt_addr = CHECK_READ_REG15_WA(cpu, Rn) + 4;

    if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
        cpu->Reg[Rn] += count * 4;
}

static void LdnStM(IncrementAfter)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int i = BITS(inst, 0, 15);
    int count = 0;

    while (i) {
        if (i & 1)
            count++;
        i = i >> 1;
    }

    virt_addr = CHECK_READ_REG15_WA(cpu, Rn);

    if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21))
        cpu->Reg[Rn] += count * 4;
}

static void LdnStM(DecrementAfter)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr) {
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int i = BITS(inst, 0, 15);
    int count = 0;
    while (i) {
        if (i & 1)
            count++;
        i = i >> 1;
    }
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);
    unsigned int start_addr = rn - count * 4 + 4;

    virt_addr = start_addr;

    if (CondPassed(cpu, BITS(inst, 28, 31)) && BIT(inst, 21)) {
        cpu->Reg[Rn] -= count * 4;
    }
}

static void LnSWoUB(ScaledRegisterOffset)(ARMul_State *cpu, unsigned int inst,
    unsigned int &virt_addr) {
    unsigned int shift = BITS(inst, 5, 6);
    unsigned int shift_imm = BITS(inst, 7, 11);
    unsigned int Rn = BITS(inst, 16, 19);
    unsigned int Rm = BITS(inst, 0, 3);
    unsigned int index = 0;
    unsigned int addr;
    unsigned int rm = CHECK_READ_REG15_WA(cpu, Rm);
    unsigned int rn = CHECK_READ_REG15_WA(cpu, Rn);

    switch (shift) {
    case 0:
        index = rm << shift_imm;
        break;
    case 1:
        if (shift_imm == 0) {
            index = 0;
        } else {
            index = rm >> shift_imm;
        }
        break;
    case 2:
        if (shift_imm == 0) { // ASR #32
            if (BIT(rm, 31) == 1)
                index = 0xFFFFFFFF;
            else
                index = 0;
        } else {
            index = static_cast<int>(rm) >> shift_imm;
        }
        break;
    case 3:
        if (shift_imm == 0) {
            index = (cpu->CFlag << 31) | (rm >> 1);
        } else {
            index = ROTATE_RIGHT_32(rm, shift_imm);
        }
        break;
    }

    if (U_BIT) {
        addr = rn + index;
    } else
        addr = rn - index;

    virt_addr = addr;
}

shtop_fp_t GetShifterOp(unsigned int inst) {
    if (BIT(inst, 25)) {
        return DPO(Immediate);
    } else if (BITS(inst, 4, 11) == 0) {
        return DPO(Register);
    } else if (BITS(inst, 4, 6) == 0) {
        return DPO(LogicalShiftLeftByImmediate);
    } else if (BITS(inst, 4, 7) == 1) {
        return DPO(LogicalShiftLeftByRegister);
    } else if (BITS(inst, 4, 6) == 2) {
        return DPO(LogicalShiftRightByImmediate);
    } else if (BITS(inst, 4, 7) == 3) {
        return DPO(LogicalShiftRightByRegister);
    } else if (BITS(inst, 4, 6) == 4) {
        return DPO(ArithmeticShiftRightByImmediate);
    } else if (BITS(inst, 4, 7) == 5) {
        return DPO(ArithmeticShiftRightByRegister);
    } else if (BITS(inst, 4, 6) == 6) {
        return DPO(RotateRightByImmediate);
    } else if (BITS(inst, 4, 7) == 7) {
        return DPO(RotateRightByRegister);
    }
    return nullptr;
}

get_addr_fp_t GetAddressingOp(unsigned int inst) {
    if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 0) {
        return LnSWoUB(ImmediateOffset);
    } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
        return LnSWoUB(RegisterOffset);
    } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
        return LnSWoUB(ScaledRegisterOffset);
    } else if (BITS(inst, 24, 27) == 5 && BIT(inst, 21) == 1) {
        return LnSWoUB(ImmediatePreIndexed);
    } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BITS(inst, 4, 11) == 0) {
        return LnSWoUB(RegisterPreIndexed);
    } else if (BITS(inst, 24, 27) == 7 && BIT(inst, 21) == 1 && BIT(inst, 4) == 0) {
        return LnSWoUB(ScaledRegisterPreIndexed);
    } else if (BITS(inst, 24, 27) == 4 && BIT(inst, 21) == 0) {
        return LnSWoUB(ImmediatePostIndexed);
    } else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BITS(inst, 4, 11) == 0) {
        return LnSWoUB(RegisterPostIndexed);
    } else if (BITS(inst, 24, 27) == 6 && BIT(inst, 21) == 0 && BIT(inst, 4) == 0) {
        return LnSWoUB(ScaledRegisterPostIndexed);
    } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(ImmediateOffset);
    } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(RegisterOffset);
    } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 3 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(ImmediatePreIndexed);
    } else if (BITS(inst, 24, 27) == 1 && BITS(inst, 21, 22) == 1 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(RegisterPreIndexed);
    } else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 2 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(ImmediatePostIndexed);
    } else if (BITS(inst, 24, 27) == 0 && BITS(inst, 21, 22) == 0 && BIT(inst, 7) == 1 && BIT(inst, 4) == 1) {
        return MLnS(RegisterPostIndexed);
    } else if (BITS(inst, 23, 27) == 0x11) {
        return LdnStM(IncrementAfter);
    } else if (BITS(inst, 23, 27) == 0x13) {
        return LdnStM(IncrementBefore);
    } else if (BITS(inst, 23, 27) == 0x10) {
        return LdnStM(DecrementAfter);
    } else if (BITS(inst, 23, 27) == 0x12) {
        return LdnStM(DecrementBefore);
    }
    return nullptr;
}

// Specialized for LDRT, LDRBT, STRT, and STRBT, which have specific addressing mode requirements
get_addr_fp_t GetAddressingOpLoadStoreT(unsigned int inst) {
    if (BITS(inst, 25, 27) == 2) {
        return LnSWoUB(ImmediatePostIndexed);
    } else if (BITS(inst, 25, 27) == 3) {
        return LnSWoUB(ScaledRegisterPostIndexed);
    }
    // Reaching this would indicate the thumb version
    // of this instruction, however the 3DS CPU doesn't
    // support this variant (the 3DS CPU is only ARMv6K,
    // while this variant is added in ARMv6T2).
    // So it's sufficient for citra to not implement this.
    return nullptr;
}

enum { FETCH_SUCCESS,
    FETCH_FAILURE };

static ThumbDecodeStatus decode_thumb_instruction(ARMul_State *cpu, std::uint32_t inst, std::uint32_t addr, std::uint32_t *arm_inst, std::uint32_t *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 = static_cast<int>(arm_instruction_trans_len);
        std::uint32_t 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](cpu, tinstr, inst_index);
            } else {
                LOG_ERROR(eka2l1::CPU_DYNCOM, "thumb decoder error");
            }
            break;
        case 28:
            // Branch 2, unconditional branch
            inst_index = table_length - 5;
            *ptr_inst_base = arm_instruction_trans[inst_index](cpu, 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](cpu, tinstr, inst_index);
            break;
        case 30:
            // For BL 1 thumb instruction
            inst_index = table_length - 3;
            *ptr_inst_base = arm_instruction_trans[inst_index](cpu, tinstr, inst_index);
            break;
        case 31:
            // For BL 2 thumb instruction
            inst_index = table_length - 2;
            *ptr_inst_base = arm_instruction_trans[inst_index](cpu, tinstr, inst_index);
            break;
        default:
            ret = ThumbDecodeStatus::UNDEFINED;
            break;
        }
    }
    return ret;
}

enum { KEEP_GOING,
    FETCH_EXCEPTION };

static unsigned int InterpreterTranslateInstruction(ARMul_State *cpu, const std::uint32_t phys_addr,
    ARM_INST_PTR &inst_base) {
    std::uint32_t inst_size = 4;
    std::uint32_t inst = cpu->ReadCode(phys_addr & 0xFFFFFFFC);

    // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM
    // instruction
    if (cpu->TFlag) {
        std::uint32_t arm_inst;
        ThumbDecodeStatus state = decode_thumb_instruction(cpu, 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 (decode_arm_instruction(inst, &idx) == ARMDecodeStatus::FAILURE) {
        LOG_ERROR(eka2l1::CPU_DYNCOM, "Decode failure.\tPC: [{:#010X}]\tInstruction: {:08X}", phys_addr,
            inst);
        LOG_ERROR(eka2l1::CPU_DYNCOM, "cpsr={:#X}, cpu->TFlag={}, r15={:#010X}", cpu->Cpsr, cpu->TFlag,
            cpu->Reg[15]);
        CITRA_IGNORE_EXIT(-1);
    }
    inst_base = arm_instruction_trans[idx](cpu, inst, idx);

    return inst_size;
}

static int InterpreterTranslateBlock(ARMul_State *cpu, std::size_t &bb_start, std::uint32_t addr) {
    // 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 = cpu->trans_cache_buf_top;

    std::uint32_t phys_addr = addr;
    std::uint32_t 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, std::size_t &bb_start, std::uint32_t addr) {
    ARM_INST_PTR inst_base = nullptr;
    bb_start = cpu->trans_cache_buf_top;

    std::uint32_t phys_addr = addr;
    std::uint32_t 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;
}

unsigned InterpreterMainLoop(ARMul_State *cpu, std::uint32_t &num_instrs) {
#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 *)&cpu->trans_cache_buf[ptr]

#define INC_PC(l) ptr += (((sizeof(arm_inst) + l + 7) >> 3) << 3)
#define INC_PC_STUB ptr += (((sizeof(arm_inst) + 7) >> 3) << 3)

// 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                         \
    if (num_instrs >= cpu->NumInstrsToExecute) \
        goto END;                              \
    num_instrs++;                              \
    goto *InstLabel[inst_base->idx]
#else
#define GOTO_NEXT_INST                         \
    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 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;

    std::size_t 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;
    }

    inst_base = (arm_inst *)&cpu->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;

        std::uint32_t 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 (cpu->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;
        std::uint32_t rn_val = 0;

        // The ADR thumb instruction got disguised, under ADD. However unlike the other,
        // it uses aligned PC. So have to check
        if (cpu->TFlag) {
            std::uint32_t inst = cpu->ReadCode(cpu->Reg[15] & 0xFFFFFFFC);
            inst = GetThumbInstruction(inst, cpu->Reg[15]);

            if (((inst & 0xF800) >> 11) == 20) {
                rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
            } else {
                rn_val = CHECK_READ_REG15(cpu, inst_cream->Rn);
            }
        } else {
            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 (cpu->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;

        std::uint32_t lop = RN;
        std::uint32_t rop = SHIFTER_OPERAND;

        if (inst_cream->Rn == 15)
            lop += 2 * cpu->GetInstructionSize();

        RD = lop & rop;

        if (inst_cream->S && (inst_cream->Rd == 15)) {
            if (cpu->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)) {
        std::uint32_t lop = RN;
        if (inst_cream->Rn == 15) {
            lop += 2 * cpu->GetInstructionSize();
        }
        std::uint32_t rop = SHIFTER_OPERAND;
        RD = lop & (~rop);
        if ((inst_cream->S) && (inst_cream->Rd == 15)) {
            if (cpu->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 : {
    const std::uint32_t pc = cpu->Reg[15];

    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        bkpt_inst *const inst_cream = (bkpt_inst *)inst_base->component;
        LOG_DEBUG(eka2l1::CPU_DYNCOM, "Breakpoint instruction hit. Immediate: {:#010X}", inst_cream->imm);

        // Call the handler
        cpu->RaiseException(eka2l1::arm::exception_type_breakpoint, cpu->Reg[15]);

        LOAD_NZCVT;

        if (cpu->Reg[15] != pc) {
            goto DISPATCH;
        }
    }

    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 std::uint32_t 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;

        std::uint32_t 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->exmonitor()->clear_exclusive();
    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;

        std::uint32_t rn_val = RN;
        if (inst_cream->Rn == 15)
            rn_val += 2 * cpu->GetInstructionSize();

        bool carry;
        bool overflow;
        std::uint32_t 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;

        std::uint32_t rn_val = RN;
        if (inst_cream->Rn == 15)
            rn_val += 2 * cpu->GetInstructionSize();

        bool carry;
        bool overflow;
        std::uint32_t 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;
    std::uint32_t aif_val = 0;
    std::uint32_t 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;

        std::uint32_t lop = RN;
        if (inst_cream->Rn == 15) {
            lop += 2 * cpu->GetInstructionSize();
        }
        std::uint32_t rop = SHIFTER_OPERAND;
        RD = lop ^ rop;
        if (inst_cream->S && (inst_cream->Rd == 15)) {
            if (cpu->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(eka2l1::CPU_DYNCOM, "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 (cpu->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 std::uint32_t dest_index = BITS(inst_cream->inst, 12, 15);
        const std::uint32_t previous_mode = cpu->Mode;

        cpu->ChangePrivilegeMode(USER32MODE);
        const std::uint8_t 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;

        RD = cpu->exmonitor()->exclusive_read32(cpu->parent(), 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;

        RD = cpu->exmonitor()->exclusive_read8(cpu->parent(), 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;

        RD = cpu->exmonitor()->exclusive_read16(cpu->parent(), 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;

        const std::uint64_t valval = cpu->exmonitor()->exclusive_read64(cpu->parent(), read_addr);

        RD = static_cast<std::uint32_t>(valval);
        RD2 = static_cast<std::uint32_t>(valval >> 32);
    }
    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 std::uint32_t dest_index = BITS(inst_cream->inst, 12, 15);
        const std::uint32_t previous_mode = cpu->Mode;

        cpu->ChangePrivilegeMode(USER32MODE);
        const std::uint32_t 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(eka2l1::CPU_DYNCOM, "MCRR executed | Coprocessor: {}, CRm {}, opc1: {}, Rt: {}, Rt2: {}",
            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;

        std::uint64_t rm = RM;
        std::uint64_t rs = RS;
        std::uint64_t rn = RN;

        RD = static_cast<std::uint32_t>((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 (cpu->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(eka2l1::CPU_DYNCOM, "MRRC executed | Coprocessor: {}, CRm {}, opc1: {}, Rt: {}, Rt2: {}",
            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 std::uint32_t 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)];
        }
        std::uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0) | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
        std::uint32_t 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 (cpu->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;

        std::uint64_t rm = RM;
        std::uint64_t rs = RS;
        RD = static_cast<std::uint32_t>((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 (cpu->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;

        std::uint32_t lop = RN;
        std::uint32_t rop = SHIFTER_OPERAND;

        if (inst_cream->Rn == 15)
            lop += 2 * cpu->GetInstructionSize();

        RD = lop | rop;

        if (inst_cream->S && (inst_cream->Rd == 15)) {
            if (cpu->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<std::int32_t>(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 std::uint8_t op1 = inst_cream->op1;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;

        std::uint32_t 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) {
            std::uint32_t 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) {
            std::uint32_t 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 std::uint16_t rm_lo = (RM & 0xFFFF);
        const std::uint16_t rm_hi = ((RM >> 16) & 0xFFFF);
        const std::uint16_t rn_lo = (RN & 0xFFFF);
        const std::uint16_t rn_hi = ((RN >> 16) & 0xFFFF);
        const std::uint8_t op2 = inst_cream->op2;

        std::uint16_t lo_result = 0;
        std::uint16_t 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 std::uint8_t op1 = inst_cream->op1;
        const std::uint8_t 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;

    std::uint32_t 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;

        std::uint32_t 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 (cpu->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;

        std::uint32_t 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 (cpu->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 std::uint8_t op2 = inst_cream->op2;

        if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
            const std::int16_t rn_lo = (RN & 0xFFFF);
            const std::int16_t rn_hi = ((RN >> 16) & 0xFFFF);
            const std::int16_t rm_lo = (RM & 0xFFFF);
            const std::int16_t rm_hi = ((RM >> 16) & 0xFFFF);

            std::int32_t lo_result = 0;
            std::int32_t 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) {
            std::int32_t lo_val1, lo_val2;
            std::int32_t hi_val1, hi_val2;

            // SADD8
            if (op2 == 0x04) {
                lo_val1 = (std::int32_t)(std::int8_t)(RN & 0xFF) + (std::int32_t)(std::int8_t)(RM & 0xFF);
                lo_val2 = (std::int32_t)(std::int8_t)((RN >> 8) & 0xFF) + (std::int32_t)(std::int8_t)((RM >> 8) & 0xFF);
                hi_val1 = (std::int32_t)(std::int8_t)((RN >> 16) & 0xFF) + (std::int32_t)(std::int8_t)((RM >> 16) & 0xFF);
                hi_val2 = (std::int32_t)(std::int8_t)((RN >> 24) & 0xFF) + (std::int32_t)(std::int8_t)((RM >> 24) & 0xFF);
            }
            // SSUB8
            else {
                lo_val1 = (std::int32_t)(std::int8_t)(RN & 0xFF) - (std::int32_t)(std::int8_t)(RM & 0xFF);
                lo_val2 = (std::int32_t)(std::int8_t)((RN >> 8) & 0xFF) - (std::int32_t)(std::int8_t)((RM >> 8) & 0xFF);
                hi_val1 = (std::int32_t)(std::int8_t)((RN >> 16) & 0xFF) - (std::int32_t)(std::int8_t)((RM >> 16) & 0xFF);
                hi_val2 = (std::int32_t)(std::int8_t)((RN >> 24) & 0xFF) - (std::int32_t)(std::int8_t)((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;

        std::uint32_t 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 (cpu->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 std::uint32_t to = RM;
        const std::uint32_t from = RN;
        const std::uint32_t cpsr = cpu->Cpsr;

        std::uint32_t 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_WARN(eka2l1::CPU_DYNCOM, "SETEND {} 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(eka2l1::CPU_DYNCOM, "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 std::uint8_t op2 = inst_cream->op2;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;

        if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
            std::int32_t lo_result = 0;
            std::int32_t hi_result = 0;

            // SHADD16
            if (op2 == 0x00) {
                lo_result = ((std::int16_t)(rn_val & 0xFFFF) + (std::int16_t)(rm_val & 0xFFFF)) >> 1;
                hi_result = ((std::int16_t)((rn_val >> 16) & 0xFFFF) + (std::int16_t)((rm_val >> 16) & 0xFFFF)) >> 1;
            }
            // SHASX
            else if (op2 == 0x01) {
                lo_result = ((std::int16_t)(rn_val & 0xFFFF) - (std::int16_t)((rm_val >> 16) & 0xFFFF)) >> 1;
                hi_result = ((std::int16_t)((rn_val >> 16) & 0xFFFF) + (std::int16_t)(rm_val & 0xFFFF)) >> 1;
            }
            // SHSAX
            else if (op2 == 0x02) {
                lo_result = ((std::int16_t)(rn_val & 0xFFFF) + (std::int16_t)((rm_val >> 16) & 0xFFFF)) >> 1;
                hi_result = ((std::int16_t)((rn_val >> 16) & 0xFFFF) - (std::int16_t)(rm_val & 0xFFFF)) >> 1;
            }
            // SHSUB16
            else if (op2 == 0x03) {
                lo_result = ((std::int16_t)(rn_val & 0xFFFF) - (std::int16_t)(rm_val & 0xFFFF)) >> 1;
                hi_result = ((std::int16_t)((rn_val >> 16) & 0xFFFF) - (std::int16_t)((rm_val >> 16) & 0xFFFF)) >> 1;
            }

            RD = ((lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16));
        } else if (op2 == 0x04 || op2 == 0x07) {
            std::int16_t lo_val1, lo_val2;
            std::int16_t hi_val1, hi_val2;

            // SHADD8
            if (op2 == 0x04) {
                lo_val1 = ((std::int8_t)(rn_val & 0xFF) + (std::int8_t)(rm_val & 0xFF)) >> 1;
                lo_val2 = ((std::int8_t)((rn_val >> 8) & 0xFF) + (std::int8_t)((rm_val >> 8) & 0xFF)) >> 1;

                hi_val1 = ((std::int8_t)((rn_val >> 16) & 0xFF) + (std::int8_t)((rm_val >> 16) & 0xFF)) >> 1;
                hi_val2 = ((std::int8_t)((rn_val >> 24) & 0xFF) + (std::int8_t)((rm_val >> 24) & 0xFF)) >> 1;
            }
            // SHSUB8
            else {
                lo_val1 = ((std::int8_t)(rn_val & 0xFF) - (std::int8_t)(rm_val & 0xFF)) >> 1;
                lo_val2 = ((std::int8_t)((rn_val >> 8) & 0xFF) - (std::int8_t)((rm_val >> 8) & 0xFF)) >> 1;

                hi_val1 = ((std::int8_t)((rn_val >> 16) & 0xFF) - (std::int8_t)((rm_val >> 16) & 0xFF)) >> 1;
                hi_val2 = ((std::int8_t)((rn_val >> 24) & 0xFF) - (std::int8_t)((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;
        std::int32_t 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);

        std::uint32_t product = operand1 * operand2;
        std::uint32_t result = product + RN;
        if (AddOverflow(product, RN, result))
            cpu->Cpsr |= (1 << 27);
        RD = result;
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(smla_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}

SMLAD_INST:
SMLSD_INST:
SMUAD_INST:
SMUSD_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        smlad_inst *const inst_cream = (smlad_inst *)inst_base->component;
        const std::uint8_t op2 = inst_cream->op2;

        std::uint32_t rm_val = cpu->Reg[inst_cream->Rm];
        const std::uint32_t rn_val = cpu->Reg[inst_cream->Rn];

        if (inst_cream->m)
            rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));

        const std::int16_t rm_lo = (rm_val & 0xFFFF);
        const std::int16_t rm_hi = ((rm_val >> 16) & 0xFFFF);
        const std::int16_t rn_lo = (rn_val & 0xFFFF);
        const std::int16_t rn_hi = ((rn_val >> 16) & 0xFFFF);

        const std::uint32_t product1 = (rn_lo * rm_lo);
        const std::uint32_t product2 = (rn_hi * rm_hi);

        // SMUAD and SMLAD
        if (BIT(op2, 1) == 0) {
            std::uint32_t 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 {
            std::uint32_t 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;

        std::uint64_t operand1 = RN;
        std::uint64_t 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;

        std::uint64_t dest = ((std::uint64_t)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 std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;
        const std::uint32_t ra_val = cpu->Reg[inst_cream->Ra];
        const bool high = (inst_cream->m == 1);

        const std::int16_t operand2 = (high) ? ((rm_val >> 16) & 0xFFFF) : (rm_val & 0xFFFF);
        const std::int64_t result = (std::int64_t)(std::int32_t)rn_val * (std::int64_t)(std::int32_t)operand2 + ((std::int64_t)(std::int32_t)ra_val << 16);

        RD = BITS(result, 16, 47);

        if ((result >> 16) != (std::int32_t)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 std::uint32_t rdlo_val = RDLO;
        const std::uint32_t rdhi_val = RDHI;
        const std::uint32_t rn_val = RN;
        std::uint32_t rm_val = RM;

        if (do_swap)
            rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));

        const std::int32_t product1 = (std::int16_t)(rn_val & 0xFFFF) * (std::int16_t)(rm_val & 0xFFFF);
        const std::int32_t product2 = (std::int16_t)((rn_val >> 16) & 0xFFFF) * (std::int16_t)((rm_val >> 16) & 0xFFFF);
        std::int64_t result;

        // SMLALD
        if (BIT(inst_cream->op2, 1) == 0) {
            result = (product1 + product2) + (std::int64_t)(rdlo_val | ((std::int64_t)rdhi_val << 32));
        }
        // SMLSLD
        else {
            result = (product1 - product2) + (std::int64_t)(rdlo_val | ((std::int64_t)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 std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;
        const bool do_round = (inst_cream->m == 1);

        // Assume SMMUL by default.
        std::int64_t result = (std::int64_t)(std::int32_t)rn_val * (std::int64_t)(std::int32_t)rm_val;

        if (inst_cream->Ra != 15) {
            const std::uint32_t ra_val = cpu->Reg[inst_cream->Ra];

            // SMMLA, otherwise SMMLS
            if (BIT(inst_cream->op2, 1) == 0)
                result += ((std::int64_t)ra_val << 32);
            else
                result = ((std::int64_t)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;
        std::uint32_t 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;
        std::int64_t rm = RM;
        std::int64_t rs = RS;
        if (BIT(rm, 31)) {
            rm |= 0xffffffff00000000LL;
        }
        if (BIT(rs, 31)) {
            rs |= 0xffffffff00000000LL;
        }
        std::int64_t 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;

        std::int16_t rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);

        std::int64_t result = (std::int64_t)rm * (std::int64_t)(std::int32_t)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;

    std::uint32_t 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;

        std::uint8_t shift_type = inst_cream->shift_type;
        std::uint8_t shift_amount = inst_cream->imm5;
        std::uint32_t 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 = ((std::int32_t)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 std::uint8_t saturate_to = inst_cream->sat_imm;

        bool sat1 = false;
        bool sat2 = false;

        RD = (ARMul_SignedSatQ((std::int16_t)RN, saturate_to, &sat1) & 0xFFFF) | ARMul_SignedSatQ((std::int32_t)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(eka2l1::CPU_DYNCOM, "unimplemented instruction");
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(stc_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
STM_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
        unsigned int inst = inst_cream->inst;

        unsigned int Rn = BITS(inst, 16, 19);
        unsigned int old_RN = cpu->Reg[Rn];

        inst_cream->get_addr(cpu, inst_cream->inst, addr);
        if (BIT(inst_cream->inst, 22) == 1) {
            for (int i = 0; i < 13; i++) {
                if (BIT(inst_cream->inst, i)) {
                    cpu->WriteMemory32(addr, cpu->Reg[i]);
                    addr += 4;
                }
            }
            if (BIT(inst_cream->inst, 13)) {
                if (cpu->Mode == USER32MODE)
                    cpu->WriteMemory32(addr, cpu->Reg[13]);
                else
                    cpu->WriteMemory32(addr, cpu->Reg_usr[0]);

                addr += 4;
            }
            if (BIT(inst_cream->inst, 14)) {
                if (cpu->Mode == USER32MODE)
                    cpu->WriteMemory32(addr, cpu->Reg[14]);
                else
                    cpu->WriteMemory32(addr, cpu->Reg_usr[1]);

                addr += 4;
            }
            if (BIT(inst_cream->inst, 15)) {
                cpu->WriteMemory32(addr, cpu->Reg[15] + 8);
            }
        } else {
            for (unsigned int i = 0; i < 15; i++) {
                if (BIT(inst_cream->inst, i)) {
                    if (i == Rn)
                        cpu->WriteMemory32(addr, old_RN);
                    else
                        cpu->WriteMemory32(addr, cpu->Reg[i]);

                    addr += 4;
                }
            }

            // Check PC reg
            if (BIT(inst_cream->inst, 15)) {
                cpu->WriteMemory32(addr, cpu->Reg[15] + 8);
            }
        }
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(ldst_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
SXTB_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;

        unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
        if (BIT(operand2, 7)) {
            operand2 |= 0xffffff00;
        } else {
            operand2 &= 0xff;
        }
        RD = operand2;
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(sxtb_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
STR_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
        inst_cream->get_addr(cpu, inst_cream->inst, addr);

        unsigned int reg = BITS(inst_cream->inst, 12, 15);
        unsigned int value = cpu->Reg[reg];

        if (reg == 15)
            value += 2 * cpu->GetInstructionSize();

        cpu->WriteMemory32(addr, value);
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(ldst_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
UXTB_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        uxtb_inst *inst_cream = (uxtb_inst *)inst_base->component;
        RD = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(uxtb_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
UXTAB_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        uxtab_inst *inst_cream = (uxtab_inst *)inst_base->component;

        unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xff;
        RD = RN + operand2;
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(uxtab_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
STRB_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
        inst_cream->get_addr(cpu, inst_cream->inst, addr);
        unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
        cpu->WriteMemory8(addr, value);
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(ldst_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}
STRBT_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
        inst_cream->get_addr(cpu, inst_cream->inst, addr);

        const std::uint32_t previous_mode = cpu->Mode;
        const std::uint32_t 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];

        RD = (cpu->exmonitor()->exclusive_write32(cpu->parent(), write_addr, RM) ? 0 : 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];

        RD = (cpu->exmonitor()->exclusive_write8(cpu->parent(), write_addr, RM) ? 0 : 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];

        const std::uint32_t rt = cpu->Reg[inst_cream->Rm + 0];
        const std::uint32_t rt2 = cpu->Reg[inst_cream->Rm + 1];
        std::uint64_t value;

        if (cpu->InBigEndianMode())
            value = (((std::uint64_t)rt << 32) | rt2);
        else
            value = (((std::uint64_t)rt2 << 32) | rt);

        RD = (cpu->exmonitor()->exclusive_write64(cpu->parent(), write_addr, RM) ? 0 : 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];

        RD = (cpu->exmonitor()->exclusive_write16(cpu->parent(), write_addr, RM) ? 0 : 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 std::uint32_t previous_mode = cpu->Mode;
        const std::uint32_t rt_index = BITS(inst_cream->inst, 12, 15);

        std::uint32_t 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;

        std::uint32_t rn_val = CHECK_READ_REG15(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 (cpu->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 : {
    // Increase the PC first
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(swi_inst));

    // Some system calls modify PC, so do this first
    const std::uint32_t current_pc = cpu->Reg[15];

    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        SAVE_NZCVT;

        swi_inst *const inst_cream = (swi_inst *)inst_base->component;
        cpu->NumInstrsToExecute = num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
        cpu->RaiseSystemCall(inst_cream->num);
        // The kernel would call ERET to get here, which clears exclusive memory state.
        cpu->exmonitor()->clear_exclusive();

        LOAD_NZCVT;

        if (current_pc != cpu->Reg[15]) {
            goto DISPATCH;
        }
    }

    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 std::uint8_t rotation = inst_cream->rotate * 8;
        std::uint32_t rm_val = RM;
        std::uint32_t rn_val = RN;

        if (rotation)
            rm_val = ((rm_val << (32 - rotation)) | (rm_val >> rotation));

        // SXTB16
        if (inst_cream->Rn == 15) {
            std::uint32_t lo = (std::uint32_t)(std::int8_t)rm_val;
            std::uint32_t hi = (std::uint32_t)(std::int8_t)(rm_val >> 16);
            RD = (lo & 0xFFFF) | (hi << 16);
        }
        // SXTAB16
        else {
            std::uint32_t lo = rn_val + (std::uint32_t)(std::int8_t)(rm_val & 0xFF);
            std::uint32_t hi = (rn_val >> 16) + (std::uint32_t)(std::int8_t)((rm_val >> 16) & 0xFF);
            RD = (lo & 0xFFFF) | (hi << 16);
        }
    }

    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(sxtab_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}

SXTAH_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component;

        unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate) & 0xffff;
        // Sign extend for half
        operand2 = (0x8000 & operand2) ? (0xFFFF0000 | operand2) : operand2;
        RD = RN + operand2;
    }
    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC(sizeof(sxtah_inst));
    FETCH_INST;
    GOTO_NEXT_INST;
}

TEQ_INST : {
    if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
        teq_inst *const inst_cream = (teq_inst *)inst_base->component;

        std::uint32_t lop = RN;
        std::uint32_t rop = SHIFTER_OPERAND;

        if (inst_cream->Rn == 15)
            lop += cpu->GetInstructionSize() * 2;

        std::uint32_t 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;

        std::uint32_t lop = RN;
        std::uint32_t rop = SHIFTER_OPERAND;

        if (inst_cream->Rn == 15)
            lop += cpu->GetInstructionSize() * 2;

        std::uint32_t 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 std::uint8_t op2 = inst_cream->op2;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;

        std::int32_t lo_result = 0;
        std::int32_t 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) {
            std::int16_t sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
            std::int16_t sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
            std::int16_t sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
            std::int16_t 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) {
            std::int16_t diff1 = (rn_val & 0xFF) - (rm_val & 0xFF);
            std::int16_t diff2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
            std::int16_t diff3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
            std::int16_t 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 std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;
        const std::uint8_t op2 = inst_cream->op2;

        if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
            std::uint32_t lo_val = 0;
            std::uint32_t 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) {
            std::uint32_t sum1;
            std::uint32_t sum2;
            std::uint32_t sum3;
            std::uint32_t 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 std::uint64_t rm = RM;
        const std::uint64_t rn = RN;
        const std::uint64_t rd_lo = RDLO;
        const std::uint64_t rd_hi = RDHI;
        const std::uint64_t 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
    std::uint32_t 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 std::uint8_t op2 = inst_cream->op2;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;

        std::uint16_t lo_val = 0;
        std::uint16_t 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 std::uint8_t ra_idx = inst_cream->Ra;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rn_val = RN;

        const std::uint8_t diff1 = ARMul_UnsignedAbsoluteDifference(rn_val & 0xFF, rm_val & 0xFF);
        const std::uint8_t diff2 = ARMul_UnsignedAbsoluteDifference((rn_val >> 8) & 0xFF, (rm_val >> 8) & 0xFF);
        const std::uint8_t diff3 = ARMul_UnsignedAbsoluteDifference((rn_val >> 16) & 0xFF, (rm_val >> 16) & 0xFF);
        const std::uint8_t diff4 = ARMul_UnsignedAbsoluteDifference((rn_val >> 24) & 0xFF, (rm_val >> 24) & 0xFF);

        std::uint32_t 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;

        std::uint8_t shift_type = inst_cream->shift_type;
        std::uint8_t shift_amount = inst_cream->imm5;
        std::uint32_t 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 = ((std::int32_t)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 std::uint8_t saturate_to = inst_cream->sat_imm;

        bool sat1 = false;
        bool sat2 = false;

        RD = (ARMul_UnsignedSatQ((std::int16_t)RN, saturate_to, &sat1) & 0xFFFF) | ARMul_UnsignedSatQ((std::int32_t)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 std::uint8_t rn_idx = inst_cream->Rn;
        const std::uint32_t rm_val = RM;
        const std::uint32_t rotation = inst_cream->rotate * 8;
        const std::uint32_t rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));

        // UXTB16, otherwise UXTAB16
        if (rn_idx == 15) {
            RD = rotated_rm & 0x00FF00FF;
        } else {
            const std::uint32_t rn_val = RN;
            const std::uint8_t lo_rotated = (rotated_rm & 0xFF);
            const std::uint16_t lo_result = (rn_val & 0xFFFF) + (std::uint16_t)lo_rotated;
            const std::uint8_t hi_rotated = (rotated_rm >> 16) & 0xFF;
            const std::uint16_t hi_result = (rn_val >> 16) + (std::uint16_t)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(eka2l1::CPU_DYNCOM, "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(eka2l1::CPU_DYNCOM, "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(eka2l1::CPU_DYNCOM, "YIELD executed.");
    }

    cpu->Reg[15] += cpu->GetInstructionSize();
    INC_PC_STUB;
    FETCH_INST;
    GOTO_NEXT_INST;
}

#define VFP_INTERPRETER_IMPL
#include <cpu/dyncom/vfp/vfpinstr.h>
#undef VFP_INTERPRETER_IMPL

END : {
    SAVE_NZCVT;
    cpu->NumInstrsToExecute = 0;
    return num_instrs;
}
INIT_INST_LENGTH : {
    cpu->NumInstrsToExecute = 0;
    return num_instrs;
}
}
