#include "core/gba/gba.h"

#include "core/gba/gbaCpu.h"
#include "core/gba/gbaInline.h"
#include "core/gba/gbaGlobals.h"

#if defined(VBAM_ENABLE_DEBUGGER)
#include "core/gba/gbaRemote.h"
#endif  // defined(VBAM_ENABLE_DEBUGGER)

#ifdef PROFILING
#include "prof/prof.h"
#endif

#ifdef _MSC_VER
// Disable "empty statement" warnings
#pragma warning(disable : 4390)
// Visual C's inline assembler treats "offset" as a reserved word, so we
// tell it otherwise.  If you want to use it, write "OFFSET" in capitals.
#define offset offset_
#endif

///////////////////////////////////////////////////////////////////////////

static int clockTicks;

static INSN_REGPARM void armUnknownInsn(uint32_t opcode)
{
#ifdef GBA_LOGGING
    if (systemVerbose & VERBOSE_UNDEFINED) {
        log("Undefined ARM instruction %08x at %08x\n", opcode,
            armNextPC - 4);
    }
#endif
    CPUUndefinedException();
}

#ifdef VBAM_ENABLE_DEBUGGER
static INSN_REGPARM void armBreakpoint(uint32_t opcode)
{
    reg[15].I -= 4;
    armNextPC -= 4;
    dbgSignal(5, (opcode & 0x0f) | ((opcode >> 4) & 0xfff0));
    clockTicks = -1;
}
#endif

// Subroutine to count instructions (for debugging/optimizing)
//#define INSN_COUNTER  // comment out if you don't want it
#ifdef INSN_COUNTER
static void count(uint32_t opcode, int cond_res)
{
    static int insncount = 0; // number of insns seen
    static int executed = 0; // number of insns executed
    static int mergewith[4096]; // map instructions to routines
    static int count[4096]; // count of each 12-bit code
    int index = ((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x0F);
    static FILE* outfile = NULL;

    if (!insncount) {
        for (int i = 0; i < 4096; i++) {
            for (int j = 0; j < i; j++) {
                if (armInsnTable[i] == armInsnTable[j])
                    break;
            }
            mergewith[i] = j;
        }
        outfile = fopen("VBA-armcount.txt", "w");
    }
    if (cond_res) {
        count[mergewith[index]]++;
        executed++;
    }
    insncount++;
    if (outfile && insncount % 1000000 == 0) {
        fprintf(outfile, "Total instructions: %d\n", insncount);
        fprintf(outfile, "Instructions executed: %d\n", executed);
        for (int i = 0; i < 4096; i++) {
            if (count[i])
                fprintf(outfile, "arm%03X: %d\n", i, count[i]);
        }
    }
}
#endif

// Common macros //////////////////////////////////////////////////////////

#ifdef VBAM_ENABLE_DEBUGGER
#define CONSOLE_OUTPUT(a, b)                                      \
    do {                                                          \
        if ((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) { \
            dbgOutput((a), (b));                                  \
        }                                                         \
        while (0)
#else
#define CONSOLE_OUTPUT(a, b) /* nothing */
#endif

#define NEG(i) ((i) >> 31)
#define POS(i) ((~(i)) >> 31)

// The following macros are used for optimization; any not defined for a
// particular compiler/CPU combination default to the C core versions.
//
//    ALU_INIT_C:   Used at the beginning of ALU instructions (AND/EOR/...).
//    (ALU_INIT_NC) Can consist of variable declarations, like the C core,
//                  or the start of a continued assembly block, like the
//                  x86-optimized version.  The _C version is used when the
//                  carry flag from the shift operation is needed (logical
//                  operations that set condition codes, like ANDS); the
//                  _NC version is used when the carry result is ignored.
//    VALUE_XXX: Retrieve the second operand's value for an ALU instruction.
//               The _C and _NC versions are used the same way as ALU_INIT.
//    OP_XXX: ALU operations.  XXX is the instruction name.
//    ALU_FINISH: Appended to all ALU instructions.  Usually empty, but if
//                ALU_INIT started a block ALU_FINISH can be used to end it
//                (as with the asm(...) statement in the x86 core).
//    SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL
//                  when the condition codes are not set.  Usually empty.
//    SETCOND_MUL: Used in multiply instructions to set the condition codes.
//    ROR_IMM_MSR: Used to rotate the immediate operand for MSR.
//    ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR
//                instructions.
//    RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and
//                STR instructions.

#ifndef C_CORE

#if 0 // definitions have changed
//#ifdef __POWERPC__
#define OP_SUBS                            \
    {                                      \
        int Flags;                \
        int Result;               \
        asm volatile("subco. %0, %2, %3\n" \
                     "mcrxr cr1\n"         \
                     "mfcr %1\n"           \
                     : "=r"(Result),       \
                     "=r"(Flags)           \
                     : "r"(reg[base].I),   \
                     "r"(value));          \
        reg[dest].I = Result;              \
        Z_FLAG = (Flags >> 29) & 1;        \
        N_FLAG = (Flags >> 31) & 1;        \
        C_FLAG = (Flags >> 25) & 1;        \
        V_FLAG = (Flags >> 26) & 1;        \
    }
#define OP_RSBS                             \
    {                                       \
        int Flags;                 \
        int Result;                \
        asm volatile("subfco. %0, %2, %3\n" \
                     "mcrxr cr1\n"          \
                     "mfcr %1\n"            \
                     : "=r"(Result),        \
                     "=r"(Flags)            \
                     : "r"(reg[base].I),    \
                     "r"(value));           \
        reg[dest].I = Result;               \
        Z_FLAG = (Flags >> 29) & 1;         \
        N_FLAG = (Flags >> 31) & 1;         \
        C_FLAG = (Flags >> 25) & 1;         \
        V_FLAG = (Flags >> 26) & 1;         \
    }
#define OP_ADDS                            \
    {                                      \
        int Flags;                \
        int Result;               \
        asm volatile("addco. %0, %2, %3\n" \
                     "mcrxr cr1\n"         \
                     "mfcr %1\n"           \
                     : "=r"(Result),       \
                     "=r"(Flags)           \
                     : "r"(reg[base].I),   \
                     "r"(value));          \
        reg[dest].I = Result;              \
        Z_FLAG = (Flags >> 29) & 1;        \
        N_FLAG = (Flags >> 31) & 1;        \
        C_FLAG = (Flags >> 25) & 1;        \
        V_FLAG = (Flags >> 26) & 1;        \
    }
#define OP_ADCS                            \
    {                                      \
        int Flags;                \
        int Result;               \
        asm volatile("mtspr xer, %4\n"     \
                     "addeo. %0, %2, %3\n" \
                     "mcrxr cr1\n"         \
                     "mfcr      %1\n"      \
                     : "=r"(Result),       \
                     "=r"(Flags)           \
                     : "r"(reg[base].I),   \
                     "r"(value),           \
                     "r"(C_FLAG << 29));   \
        reg[dest].I = Result;              \
        Z_FLAG = (Flags >> 29) & 1;        \
        N_FLAG = (Flags >> 31) & 1;        \
        C_FLAG = (Flags >> 25) & 1;        \
        V_FLAG = (Flags >> 26) & 1;        \
    }
#define OP_SBCS                             \
    {                                       \
        int Flags;                 \
        int Result;                \
        asm volatile("mtspr xer, %4\n"      \
                     "subfeo. %0, %3, %2\n" \
                     "mcrxr cr1\n"          \
                     "mfcr      %1\n"       \
                     : "=r"(Result),        \
                     "=r"(Flags)            \
                     : "r"(reg[base].I),    \
                     "r"(value),            \
                     "r"(C_FLAG << 29));    \
        reg[dest].I = Result;               \
        Z_FLAG = (Flags >> 29) & 1;         \
        N_FLAG = (Flags >> 31) & 1;         \
        C_FLAG = (Flags >> 25) & 1;         \
        V_FLAG = (Flags >> 26) & 1;         \
    }
#define OP_RSCS                             \
    {                                       \
        int Flags;                 \
        int Result;                \
        asm volatile("mtspr xer, %4\n"      \
                     "subfeo. %0, %2, %3\n" \
                     "mcrxr cr1\n"          \
                     "mfcr      %1\n"       \
                     : "=r"(Result),        \
                     "=r"(Flags)            \
                     : "r"(reg[base].I),    \
                     "r"(value),            \
                     "r"(C_FLAG << 29));    \
        reg[dest].I = Result;               \
        Z_FLAG = (Flags >> 29) & 1;         \
        N_FLAG = (Flags >> 31) & 1;         \
        C_FLAG = (Flags >> 25) & 1;         \
        V_FLAG = (Flags >> 26) & 1;         \
    }
#define OP_CMP                             \
    {                                      \
        int Flags;                \
        int Result;               \
        asm volatile("subco. %0, %2, %3\n" \
                     "mcrxr cr1\n"         \
                     "mfcr %1\n"           \
                     : "=r"(Result),       \
                     "=r"(Flags)           \
                     : "r"(reg[base].I),   \
                     "r"(value));          \
        Z_FLAG = (Flags >> 29) & 1;        \
        N_FLAG = (Flags >> 31) & 1;        \
        C_FLAG = (Flags >> 25) & 1;        \
        V_FLAG = (Flags >> 26) & 1;        \
    }
#define OP_CMN                             \
    {                                      \
        int Flags;                \
        int Result;               \
        asm volatile("addco. %0, %2, %3\n" \
                     "mcrxr cr1\n"         \
                     "mfcr %1\n"           \
                     : "=r"(Result),       \
                     "=r"(Flags)           \
                     : "r"(reg[base].I),   \
                     "r"(value));          \
        Z_FLAG = (Flags >> 29) & 1;        \
        N_FLAG = (Flags >> 31) & 1;        \
        C_FLAG = (Flags >> 25) & 1;        \
        V_FLAG = (Flags >> 26) & 1;        \
    }

#else // !__POWERPC__

// Macros to emit instructions in the format used by the particular compiler.
// We use GNU assembler syntax: "op src, dest" rather than "op dest, src"

#ifdef __GNUC__
#define ALU_HEADER           asm("mov %%ecx, %%edi; "
#define ALU_TRAILER          : "=D" (opcode) : "c" (opcode) : "eax", "ebx", "edx", "esi")
#define EMIT0(op) #op "; "
#define EMIT1(op, arg) #op " " arg "; "
#define EMIT2(op, src, dest) #op " " src ", " dest "; "
#define KONST(val) "$" #val
#define ASMVAR(cvar) ASMVAR2(__USER_LABEL_PREFIX__, cvar)
#define ASMVAR2(prefix, cvar) STRING(prefix) \
    cvar
#define STRING(x) #x
#define VAR(var) ASMVAR(#var)
#define VARL(var) ASMVAR(#var)
#define REGREF1(index) ASMVAR("reg(" index ")")
#define REGREF2(index, scale) ASMVAR("reg(," index "," #scale ")")
#define LABEL(n) #n ": "
#define LABELREF(n, dir) #n #dir
#define al "%%al"
#define ah "%%ah"
#define eax "%%eax"
#define bl "%%bl"
#define bh "%%bh"
#define ebx "%%ebx"
#define cl "%%cl"
#define ch "%%ch"
#define ecx "%%ecx"
#define dl "%%dl"
#define dh "%%dh"
#define edx "%%edx"
#define esp "%%esp"
#define ebp "%%ebp"
#define esi "%%esi"
#define edi "%%edi"
#define movzx movzb
#else
#define ALU_HEADER __asm { __asm mov ecx, opcode
#define ALU_TRAILER }
#define EMIT0(op) __asm op
#define EMIT1(op, arg) __asm op arg
#define EMIT2(op, src, dest) __asm op dest, src
#define KONST(val) val
#define VAR(var) var
#define VARL(var) dword ptr var
#define REGREF1(index) reg[index]
#define REGREF2(index, scale) reg[index * scale]
#define LABEL(n) __asm l##n:
#define LABELREF(n, dir) l##n
#endif

//X//#ifndef _MSC_VER
// ALU op register usage:
//    EAX -> 2nd operand value, result (RSB/RSC)
//    EBX -> C_OUT (carry flag from shift/rotate)
//    ECX -> opcode (input), shift/rotate count
//    EDX -> Rn (base) value, result (all except RSB/RSC)
//    ESI -> Rd (destination) index * 4

// Helper macros for loading value / shift count
#define VALUE_LOAD_IMM               \
    EMIT2(and, KONST(0x0F), eax)     \
    EMIT2(mov, REGREF2(eax, 4), eax) \
    EMIT2(shr, KONST(7), ecx)        \
    EMIT2(and, KONST(0x1F), ecx)
#define VALUE_LOAD_REG               \
    EMIT2(and, KONST(0x0F), eax)     \
    EMIT2(cmp, KONST(0x0F), eax)     \
    EMIT2(mov, REGREF2(eax, 4), eax) \
    EMIT1(jne, LABELREF(3, f))       \
    EMIT2(add, KONST(4), eax)        \
    LABEL(3)                         \
    EMIT2(movzx, ch, ecx)            \
    EMIT2(and, KONST(0x0F), ecx)     \
    EMIT2(mov, REGREF2(ecx, 4), ecx)

// Helper macros for setting flags
#define SETCOND_LOGICAL      \
    EMIT1(sets, VAR(N_FLAG)) \
    EMIT1(setz, VAR(Z_FLAG)) \
    EMIT2(mov, bl, VAR(C_FLAG))
#define SETCOND_ADD          \
    EMIT1(sets, VAR(N_FLAG)) \
    EMIT1(setz, VAR(Z_FLAG)) \
    EMIT1(seto, VAR(V_FLAG)) \
    EMIT1(setc, VAR(C_FLAG))
#define SETCOND_SUB          \
    EMIT1(sets, VAR(N_FLAG)) \
    EMIT1(setz, VAR(Z_FLAG)) \
    EMIT1(seto, VAR(V_FLAG)) \
    EMIT1(setnc, VAR(C_FLAG))

// ALU initialization
#define ALU_INIT(LOAD_C_FLAG)     \
    ALU_HEADER                    \
    LOAD_C_FLAG                   \
    EMIT2(mov, ecx, edx)          \
    EMIT2(shr, KONST(14), edx)    \
    EMIT2(mov, ecx, eax)          \
    EMIT2(mov, ecx, esi)          \
    EMIT2(shr, KONST(10), esi)    \
    EMIT2(and, KONST(0x3C), edx)  \
    EMIT2(mov, REGREF1(edx), edx) \
    EMIT2(and, KONST(0x3C), esi)

#define LOAD_C_FLAG_YES EMIT2(mov, VAR(C_FLAG), bl)
#define LOAD_C_FLAG_NO /*nothing*/
#define ALU_INIT_C ALU_INIT(LOAD_C_FLAG_YES)
#define ALU_INIT_NC ALU_INIT(LOAD_C_FLAG_NO)

// Macros to load the value operand for an ALU op; these all set N/Z
// according to the value

// OP Rd,Rb,Rm LSL #
#define VALUE_LSL_IMM_C        \
    VALUE_LOAD_IMM             \
    EMIT1(jnz, LABELREF(1, f)) \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2(shl, cl, eax)        \
    EMIT1(setc, bl)            \
    LABEL(0)
#define VALUE_LSL_IMM_NC \
    VALUE_LOAD_IMM       \
    EMIT2(shl, cl, eax)

// OP Rd,Rb,Rm LSL Rs
#define VALUE_LSL_REG_C         \
    VALUE_LOAD_REG              \
    EMIT2(test, cl, cl)         \
    EMIT1(jz, LABELREF(0, f))   \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(je, LABELREF(1, f))   \
    EMIT1(ja, LABELREF(2, f))   \
    EMIT2(shl, cl, eax)         \
    EMIT1(setc, bl)             \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2(test, KONST(1), al)   \
    EMIT1(setnz, bl)            \
    EMIT2 (xor, eax, eax)       \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(2)                    \
    EMIT2 (xor, ebx, ebx)       \
    EMIT2 (xor, eax, eax)       \
    LABEL(0)
#define VALUE_LSL_REG_NC        \
    VALUE_LOAD_REG              \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(jae, LABELREF(1, f))  \
    EMIT2(shl, cl, eax)         \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2 (xor, eax, eax)       \
    LABEL(0)

// OP Rd,Rb,Rm LSR #
#define VALUE_LSR_IMM_C        \
    VALUE_LOAD_IMM             \
    EMIT1(jz, LABELREF(1, f))  \
    EMIT2(shr, cl, eax)        \
    EMIT1(setc, bl)            \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2(test, eax, eax)      \
    EMIT1(sets, bl)            \
    EMIT2 (xor, eax, eax)      \
    LABEL(0)
#define VALUE_LSR_IMM_NC       \
    VALUE_LOAD_IMM             \
    EMIT1(jz, LABELREF(1, f))  \
    EMIT2(shr, cl, eax)        \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2 (xor, eax, eax)      \
    LABEL(0)

// OP Rd,Rb,Rm LSR Rs
#define VALUE_LSR_REG_C         \
    VALUE_LOAD_REG              \
    EMIT2(test, cl, cl)         \
    EMIT1(jz, LABELREF(0, f))   \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(je, LABELREF(1, f))   \
    EMIT1(ja, LABELREF(2, f))   \
    EMIT2(shr, cl, eax)         \
    EMIT1(setc, bl)             \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2(test, eax, eax)       \
    EMIT1(sets, bl)             \
    EMIT2 (xor, eax, eax)       \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(2)                    \
    EMIT2 (xor, ebx, ebx)       \
    EMIT2 (xor, eax, eax)       \
    LABEL(0)
#define VALUE_LSR_REG_NC        \
    VALUE_LOAD_REG              \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(jae, LABELREF(1, f))  \
    EMIT2(shr, cl, eax)         \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2 (xor, eax, eax)       \
    LABEL(0)

// OP Rd,Rb,Rm ASR #
#define VALUE_ASR_IMM_C        \
    VALUE_LOAD_IMM             \
    EMIT1(jz, LABELREF(1, f))  \
    EMIT2(sar, cl, eax)        \
    EMIT1(setc, bl)            \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2(sar, KONST(31), eax) \
    EMIT1(sets, bl)            \
    LABEL(0)
#define VALUE_ASR_IMM_NC       \
    VALUE_LOAD_IMM             \
    EMIT1(jz, LABELREF(1, f))  \
    EMIT2(sar, cl, eax)        \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2(sar, KONST(31), eax) \
    LABEL(0)

// OP Rd,Rb,Rm ASR Rs
#define VALUE_ASR_REG_C         \
    VALUE_LOAD_REG              \
    EMIT2(test, cl, cl)         \
    EMIT1(jz, LABELREF(0, f))   \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(jae, LABELREF(1, f))  \
    EMIT2(sar, cl, eax)         \
    EMIT1(setc, bl)             \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2(sar, KONST(31), eax)  \
    EMIT1(sets, bl)             \
    LABEL(0)
#define VALUE_ASR_REG_NC        \
    VALUE_LOAD_REG              \
    EMIT2(cmp, KONST(0x20), cl) \
    EMIT1(jae, LABELREF(1, f))  \
    EMIT2(sar, cl, eax)         \
    EMIT1(jmp, LABELREF(0, f))  \
    LABEL(1)                    \
    EMIT2(sar, KONST(31), eax)  \
    LABEL(0)

// OP Rd,Rb,Rm ROR #
#define VALUE_ROR_IMM_C        \
    VALUE_LOAD_IMM             \
    EMIT1(jz, LABELREF(1, f))  \
    EMIT2(ror, cl, eax)        \
    EMIT1(jmp, LABELREF(0, f)) \
    LABEL(1)                   \
    EMIT2(bt, KONST(0), ebx)   \
    EMIT2(rcr, KONST(1), eax)  \
    LABEL(0)                   \
    EMIT1(setc, bl)
#define VALUE_ROR_IMM_NC              \
    VALUE_LOAD_IMM                    \
    EMIT1(jz, LABELREF(1, f))         \
    EMIT2(ror, cl, eax)               \
    EMIT1(jmp, LABELREF(0, f))        \
    LABEL(1)                          \
    EMIT2(bt, KONST(0), VARL(C_FLAG)) \
    EMIT2(rcr, KONST(1), eax)         \
    LABEL(0)

// OP Rd,Rb,Rm ROR Rs
#define VALUE_ROR_REG_C      \
    VALUE_LOAD_REG           \
    EMIT2(bt, KONST(0), ebx) \
    EMIT2(ror, cl, eax)      \
    EMIT1(setc, bl)
#define VALUE_ROR_REG_NC \
    VALUE_LOAD_REG       \
    EMIT2(ror, cl, eax)

// OP Rd,Rb,# ROR #
#define VALUE_IMM_C          \
    EMIT2(movzx, ch, ecx)    \
    EMIT2(add, ecx, ecx)     \
    EMIT2(movzx, al, eax)    \
    EMIT2(bt, KONST(0), ebx) \
    EMIT2(ror, cl, eax)      \
    EMIT1(setc, bl)
#define VALUE_IMM_NC      \
    EMIT2(movzx, ch, ecx) \
    EMIT2(add, ecx, ecx)  \
    EMIT2(movzx, al, eax) \
    EMIT2(ror, cl, eax)

// Macros to perform ALU ops

// Set condition codes iff the destination register is not R15 (PC)
#define CHECK_PC(OP, SETCOND)      \
    EMIT2(cmp, KONST(0x3C), esi)   \
    EMIT1(je, LABELREF(8, f))      \
    OP SETCOND                     \
    EMIT1(jmp, LABELREF(9, f))     \
    LABEL(8)                       \
    OP                             \
    LABEL(9)

#define OP_AND           \
    EMIT2(and, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_ANDS CHECK_PC(OP_AND, SETCOND_LOGICAL)
#define OP_EOR            \
    EMIT2 (xor, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_EORS CHECK_PC(OP_EOR, SETCOND_LOGICAL)
#define OP_SUB           \
    EMIT2(sub, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_SUBS CHECK_PC(OP_SUB, SETCOND_SUB)
#define OP_RSB           \
    EMIT2(sub, edx, eax) \
    EMIT2(mov, eax, REGREF1(esi))
#define OP_RSBS CHECK_PC(OP_RSB, SETCOND_SUB)
#define OP_ADD           \
    EMIT2(add, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_ADDS CHECK_PC(OP_ADD, SETCOND_ADD)
#define OP_ADC                        \
    EMIT2(bt, KONST(0), VARL(C_FLAG)) \
    EMIT2(adc, eax, edx)              \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_ADCS CHECK_PC(OP_ADC, SETCOND_ADD)
#define OP_SBC                        \
    EMIT2(bt, KONST(0), VARL(C_FLAG)) \
    EMIT0(cmc)                        \
    EMIT2(sbb, eax, edx)              \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_SBCS CHECK_PC(OP_SBC, SETCOND_SUB)
#define OP_RSC                        \
    EMIT2(bt, KONST(0), VARL(C_FLAG)) \
    EMIT0(cmc)                        \
    EMIT2(sbb, edx, eax)              \
    EMIT2(mov, eax, REGREF1(esi))
#define OP_RSCS CHECK_PC(OP_RSC, SETCOND_SUB)
#define OP_TST           \
    EMIT2(and, eax, edx) \
    SETCOND_LOGICAL
#define OP_TEQ            \
    EMIT2 (xor, eax, edx) \
    SETCOND_LOGICAL
#define OP_CMP           \
    EMIT2(sub, eax, edx) \
    SETCOND_SUB
#define OP_CMN           \
    EMIT2(add, eax, edx) \
    SETCOND_ADD
#define OP_ORR          \
    EMIT2(or, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_ORRS CHECK_PC(OP_ORR, SETCOND_LOGICAL)
#define OP_MOV \
    EMIT2(mov, eax, REGREF1(esi))
#define OP_MOVS CHECK_PC(EMIT2(test, eax, eax) EMIT2(mov, eax, REGREF1(esi)), SETCOND_LOGICAL)
#define OP_BIC           \
    EMIT1(not, eax)      \
    EMIT2(and, eax, edx) \
    EMIT2(mov, edx, REGREF1(esi))
#define OP_BICS CHECK_PC(OP_BIC, SETCOND_LOGICAL)
#define OP_MVN      \
    EMIT1(not, eax) \
    EMIT2(mov, eax, REGREF1(esi))
#define OP_MVNS CHECK_PC(OP_MVN EMIT2(test, eax, eax), SETCOND_LOGICAL)

// ALU cleanup macro
#define ALU_FINISH ALU_TRAILER

// End of ALU macros
//X//#endif //_MSC_VER

#ifdef __GNUC__

#define ROR_IMM_MSR        \
    asm("ror %%cl, %%eax;" \
        : "=a"(value)      \
        : "a"(opcode & 0xFF), "c"(shift));

#define ROR_OFFSET     \
    asm("ror %%cl, %0" \
        : "=r"(offset) \
        : "0"(offset), "c"(shift));

#define RRX_OFFSET                                     \
    asm(EMIT2(btl, KONST(0), VAR(C_FLAG)) "rcr $1, %0" \
        : "=r"(offset)                                 \
        : "0"(offset));

#else // !__GNUC__, i.e. Visual C++

#define ROR_IMM_MSR                                                                                             \
    __asm {                             \
        __asm mov ecx, shift            \
        __asm ror value, cl             \
    }

#define ROR_OFFSET                                                                                               \
    __asm {                             \
        __asm mov ecx, shift            \
        __asm ror offset, cl            \
    }

#define RRX_OFFSET                                                                                              \
    __asm {                             \
        __asm bt dword ptr C_FLAG, 0    \
        __asm rcr offset, 1             \
    }

#endif // !__GNUC__

#endif // !__POWERPC__
#endif // !C_CORE

// C core

#define C_SETCOND_LOGICAL                       \
    N_FLAG = ((int32_t)res < 0) ? true : false; \
    Z_FLAG = (res == 0) ? true : false;         \
    C_FLAG = C_OUT;
#define C_SETCOND_ADD                                                                               \
    N_FLAG = ((int32_t)res < 0) ? true : false;                                                     \
    Z_FLAG = (res == 0) ? true : false;                                                             \
    V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;  \
    C_FLAG = ((NEG(lhs) & NEG(rhs)) | (NEG(lhs) & POS(res)) | (NEG(rhs) & POS(res))) ? true : false;
#define C_SETCOND_SUB                                                                               \
    N_FLAG = ((int32_t)res < 0) ? true : false;                                                     \
    Z_FLAG = (res == 0) ? true : false;                                                             \
    V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;  \
    C_FLAG = ((NEG(lhs) & POS(rhs)) | (NEG(lhs) & POS(res)) | (POS(rhs) & POS(res))) ? true : false;

#define maybe_unused(var) (void) var

#ifndef ALU_INIT_C
#define ALU_INIT_C                                      \
    int dest = (opcode >> 12) & 15; maybe_unused(dest); \
    bool C_OUT = C_FLAG; maybe_unused(C_OUT);           \
    uint32_t value; maybe_unused(value);
#endif
// OP Rd,Rb,Rm LSL #
#ifndef VALUE_LSL_IMM_C
#define VALUE_LSL_IMM_C                                 \
    unsigned int shift = (opcode >> 7) & 0x1F;          \
    if (LIKELY(!shift)) { /* LSL #0 most common? */     \
        value = reg[opcode & 0x0F].I;                   \
    } else {                                            \
        uint32_t v = reg[opcode & 0x0F].I;              \
        C_OUT = (v >> (32 - shift)) & 1 ? true : false; \
        value = v << shift;                             \
    }
#endif
// OP Rd,Rb,Rm LSL Rs
#ifndef VALUE_LSL_REG_C
#define VALUE_LSL_REG_C                                     \
    uint32_t shift = reg[(opcode >> 8) & 15].B.B0;          \
    uint32_t rm = reg[opcode & 0x0F].I;                     \
    if ((opcode & 0x0F) == 15) {                            \
        rm += 4;                                            \
    }                                                       \
    if (LIKELY(shift)) {                                    \
        if (shift == 32) {                                  \
            value = 0;                                      \
            C_OUT = (rm & 1 ? true : false);                \
        } else if (LIKELY(shift < 32)) {                    \
            uint32_t v = rm;                                \
            C_OUT = (v >> (32 - shift)) & 1 ? true : false; \
            value = v << shift;                             \
        } else {                                            \
            value = 0;                                      \
            C_OUT = false;                                  \
        }                                                   \
    } else {                                                \
        value = rm;                                         \
    }
#endif
// OP Rd,Rb,Rm LSR #
#ifndef VALUE_LSR_IMM_C
#define VALUE_LSR_IMM_C                                             \
    uint32_t shift = (opcode >> 7) & 0x1F;                          \
    if (LIKELY(shift)) {                                            \
        uint32_t v = reg[opcode & 0x0F].I;                          \
        C_OUT = (v >> (shift - 1)) & 1 ? true : false;              \
        value = v >> shift;                                         \
    } else {                                                        \
        value = 0;                                                  \
        C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false; \
    }
#endif
// OP Rd,Rb,Rm LSR Rs
#ifndef VALUE_LSR_REG_C
#define VALUE_LSR_REG_C                                    \
    unsigned int shift = reg[(opcode >> 8) & 15].B.B0;     \
    uint32_t rm = reg[opcode & 0x0F].I;                    \
    if ((opcode & 0x0F) == 15) {                           \
        rm += 4;                                           \
    }                                                      \
    if (LIKELY(shift)) {                                   \
        if (shift == 32) {                                 \
            value = 0;                                     \
            C_OUT = (rm & 0x80000000 ? true : false);      \
        } else if (LIKELY(shift < 32)) {                   \
            uint32_t v = rm;                               \
            C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
            value = v >> shift;                            \
        } else {                                           \
            value = 0;                                     \
            C_OUT = false;                                 \
        }                                                  \
    } else {                                               \
        value = rm;                                        \
    }
#endif
// OP Rd,Rb,Rm ASR #
#ifndef VALUE_ASR_IMM_C
#define VALUE_ASR_IMM_C                                       \
    unsigned int shift = (opcode >> 7) & 0x1F;                \
    if (LIKELY(shift)) {                                      \
        /* VC++ BUG: uint32_t v; (int32_t)v>>n is optimized to shr! */ \
        int32_t v = reg[opcode & 0x0F].I;                     \
        C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;   \
        value = v >> (int)shift;                              \
    } else {                                                  \
        if (reg[opcode & 0x0F].I & 0x80000000) {              \
            value = 0xFFFFFFFF;                               \
            C_OUT = true;                                     \
        } else {                                              \
            value = 0;                                        \
            C_OUT = false;                                    \
        }                                                     \
    }
#endif
// OP Rd,Rb,Rm ASR Rs
#ifndef VALUE_ASR_REG_C
#define VALUE_ASR_REG_C                                         \
    unsigned int shift = reg[(opcode >> 8) & 15].B.B0;          \
    uint32_t rm = reg[opcode & 0x0F].I;                         \
    if ((opcode & 0x0F) == 15) {                                \
        rm += 4;                                                \
    }                                                           \
    if (LIKELY(shift < 32)) {                                   \
        if (LIKELY(shift)) {                                    \
            int32_t v = rm;                                     \
            C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false; \
            value = v >> (int)shift;                            \
        } else {                                                \
            value = rm;                                         \
        }                                                       \
    } else {                                                    \
        if (reg[opcode & 0x0F].I & 0x80000000) {                \
            value = 0xFFFFFFFF;                                 \
            C_OUT = true;                                       \
        } else {                                                \
            value = 0;                                          \
            C_OUT = false;                                      \
        }                                                       \
    }
#endif
// OP Rd,Rb,Rm ROR #
#ifndef VALUE_ROR_IMM_C
#define VALUE_ROR_IMM_C                                \
    unsigned int shift = (opcode >> 7) & 0x1F;         \
    if (LIKELY(shift)) {                               \
        uint32_t v = reg[opcode & 0x0F].I;             \
        C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
        value = ((v << (32 - shift)) | (v >> shift));  \
    } else {                                           \
        uint32_t v = reg[opcode & 0x0F].I;             \
        C_OUT = (v & 1) ? true : false;                \
        value = ((v >> 1) | (C_FLAG << 31));           \
    }
#endif
// OP Rd,Rb,Rm ROR Rs
#ifndef VALUE_ROR_REG_C
#define VALUE_ROR_REG_C                                  \
    unsigned int shift = reg[(opcode >> 8) & 15].B.B0;   \
    uint32_t rm = reg[opcode & 0x0F].I;                  \
    if ((opcode & 0x0F) == 15) {                         \
        rm += 4;                                         \
    }                                                    \
    if (LIKELY(shift & 0x1F)) {                          \
        uint32_t v = rm;                                 \
        C_OUT = (v >> (shift - 1)) & 1 ? true : false;   \
        value = ((v << (32 - shift)) | (v >> shift));    \
    } else {                                             \
        value = rm;                                      \
        if (shift)                                       \
            C_OUT = (value & 0x80000000 ? true : false); \
    }
#endif
// OP Rd,Rb,# ROR #
#ifndef VALUE_IMM_C
#define VALUE_IMM_C                                    \
    int shift = (opcode & 0xF00) >> 7;                 \
    if (UNLIKELY(shift)) {                             \
        uint32_t v = opcode & 0xFF;                    \
        C_OUT = (v >> (shift - 1)) & 1 ? true : false; \
        value = ((v << (32 - shift)) | (v >> shift));  \
    } else {                                           \
        value = opcode & 0xFF;                         \
    }
#endif

// Make the non-carry versions default to the carry versions
// (this is fine for C--the compiler will optimize the dead code out)
#ifndef ALU_INIT_NC
#define ALU_INIT_NC ALU_INIT_C
#endif
#ifndef VALUE_LSL_IMM_NC
#define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C
#endif
#ifndef VALUE_LSL_REG_NC
#define VALUE_LSL_REG_NC VALUE_LSL_REG_C
#endif
#ifndef VALUE_LSR_IMM_NC
#define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C
#endif
#ifndef VALUE_LSR_REG_NC
#define VALUE_LSR_REG_NC VALUE_LSR_REG_C
#endif
#ifndef VALUE_ASR_IMM_NC
#define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C
#endif
#ifndef VALUE_ASR_REG_NC
#define VALUE_ASR_REG_NC VALUE_ASR_REG_C
#endif
#ifndef VALUE_ROR_IMM_NC
#define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C
#endif
#ifndef VALUE_ROR_REG_NC
#define VALUE_ROR_REG_NC VALUE_ROR_REG_C
#endif
#ifndef VALUE_IMM_NC
#define VALUE_IMM_NC VALUE_IMM_C
#endif

#define C_CHECK_PC(SETCOND)   \
    if (LIKELY(dest != 15)) { \
        SETCOND               \
    }
#ifndef OP_AND
#define OP_AND                                         \
    uint32_t res = reg[(opcode >> 16) & 15].I & value; \
    reg[dest].I = res;
#endif
#ifndef OP_ANDS
#define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL)
#endif
#ifndef OP_EOR
#define OP_EOR                                         \
    uint32_t res = reg[(opcode >> 16) & 15].I ^ value; \
    reg[dest].I = res;
#endif
#ifndef OP_EORS
#define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL)
#endif
#ifndef OP_SUB
#define OP_SUB                                 \
    uint32_t lhs = reg[(opcode >> 16) & 15].I; \
    uint32_t rhs = value;                      \
    uint32_t res = lhs - rhs;                  \
    reg[dest].I = res;
#endif
#ifndef OP_SUBS
#define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB)
#endif
#ifndef OP_RSB
#define OP_RSB                                 \
    uint32_t lhs = value;                      \
    uint32_t rhs = reg[(opcode >> 16) & 15].I; \
    uint32_t res = lhs - rhs;                  \
    reg[dest].I = res;
#endif
#ifndef OP_RSBS
#define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB)
#endif
#ifndef OP_ADD
#define OP_ADD                                 \
    uint32_t lhs = reg[(opcode >> 16) & 15].I; \
    uint32_t rhs = value;                      \
    uint32_t res = lhs + rhs;                  \
    reg[dest].I = res;
#endif
#ifndef OP_ADDS
#define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD)
#endif
#ifndef OP_ADC
#define OP_ADC                                   \
    uint32_t lhs = reg[(opcode >> 16) & 15].I;   \
    uint32_t rhs = value;                        \
    uint32_t res = lhs + rhs + (uint32_t)C_FLAG; \
    reg[dest].I = res;
#endif
#ifndef OP_ADCS
#define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD)
#endif
#ifndef OP_SBC
#define OP_SBC                                      \
    uint32_t lhs = reg[(opcode >> 16) & 15].I;      \
    uint32_t rhs = value;                           \
    uint32_t res = lhs - rhs - !((uint32_t)C_FLAG); \
    reg[dest].I = res;
#endif
#ifndef OP_SBCS
#define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB)
#endif
#ifndef OP_RSC
#define OP_RSC                                      \
    uint32_t lhs = value;                           \
    uint32_t rhs = reg[(opcode >> 16) & 15].I;      \
    uint32_t res = lhs - rhs - !((uint32_t)C_FLAG); \
    reg[dest].I = res;
#endif
#ifndef OP_RSCS
#define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB)
#endif
#ifndef OP_TST
#define OP_TST                                           \
    uint32_t res = reg[(opcode >> 16) & 0x0F].I & value; \
    C_SETCOND_LOGICAL;
#endif
#ifndef OP_TEQ
#define OP_TEQ                                           \
    uint32_t res = reg[(opcode >> 16) & 0x0F].I ^ value; \
    C_SETCOND_LOGICAL;
#endif
#ifndef OP_CMP
#define OP_CMP                                 \
    uint32_t lhs = reg[(opcode >> 16) & 15].I; \
    uint32_t rhs = value;                      \
    uint32_t res = lhs - rhs;                  \
    C_SETCOND_SUB;
#endif
#ifndef OP_CMN
#define OP_CMN                                 \
    uint32_t lhs = reg[(opcode >> 16) & 15].I; \
    uint32_t rhs = value;                      \
    uint32_t res = lhs + rhs;                  \
    C_SETCOND_ADD;
#endif
#ifndef OP_ORR
#define OP_ORR                                           \
    uint32_t res = reg[(opcode >> 16) & 0x0F].I | value; \
    reg[dest].I = res;
#endif
#ifndef OP_ORRS
#define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL)
#endif
#ifndef OP_MOV
#define OP_MOV            \
    uint32_t res = value; \
    reg[dest].I = res;
#endif
#ifndef OP_MOVS
#define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL)
#endif
#ifndef OP_BIC
#define OP_BIC                                              \
    uint32_t res = reg[(opcode >> 16) & 0x0F].I & (~value); \
    reg[dest].I = res;
#endif
#ifndef OP_BICS
#define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL)
#endif
#ifndef OP_MVN
#define OP_MVN             \
    uint32_t res = ~value; \
    reg[dest].I = res;
#endif
#ifndef OP_MVNS
#define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL)
#endif

#ifndef SETCOND_NONE
#define SETCOND_NONE /*nothing*/
#endif
#ifndef SETCOND_MUL
#define SETCOND_MUL                                     \
    N_FLAG = ((int32_t)reg[dest].I < 0) ? true : false; \
    Z_FLAG = reg[dest].I ? false : true;
#endif
#ifndef SETCOND_MULL
#define SETCOND_MULL                                    \
    N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \
    Z_FLAG = reg[dest].I || reg[acc].I ? false : true;
#endif

#ifndef ALU_FINISH
#define ALU_FINISH /*nothing*/
#endif

#ifndef ROR_IMM_MSR
#define ROR_IMM_MSR             \
    uint32_t v = opcode & 0xff; \
    value = ((v << (32 - shift)) | (v >> shift));
#endif
#ifndef ROR_OFFSET
#define ROR_OFFSET \
    offset = ((offset << (32 - shift)) | (offset >> shift));
#endif
#ifndef RRX_OFFSET
#define RRX_OFFSET \
    offset = ((offset >> 1) | ((int)C_FLAG << 31));
#endif

// ALU ops (except multiply) //////////////////////////////////////////////

// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC)
// GETVALUE: load value and shift/rotate (VALUE_XXX)
// OP: ALU operation (OP_XXX)
// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES
// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise
// ALU_INIT, GETVALUE, OP, and ALU_FINISH are concatenated in order.
#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \
    ALU_INIT GETVALUE OP ALU_FINISH;                             \
    if (LIKELY((opcode & 0x0000F000) != 0x0000F000)) {           \
        clockTicks = 1 + ISREGSHIFT                              \
            + codeTicksAccessSeq32(armNextPC);                   \
    } else {                                                     \
        MODECHANGE;                                              \
        if (armState) {                                          \
            reg[15].I &= 0xFFFFFFFC;                             \
            armNextPC = reg[15].I;                               \
            reg[15].I += 4;                                      \
            ARM_PREFETCH;                                        \
        } else {                                                 \
            reg[15].I &= 0xFFFFFFFE;                             \
            armNextPC = reg[15].I;                               \
            reg[15].I += 2;                                      \
            THUMB_PREFETCH;                                      \
        }                                                        \
        clockTicks = 3 + ISREGSHIFT                              \
            + codeTicksAccess32(armNextPC)                       \
            + codeTicksAccessSeq32(armNextPC)                    \
            + codeTicksAccessSeq32(armNextPC);                   \
    }

#define MODECHANGE_NO /*nothing*/
#define MODECHANGE_YES CPUSwitchMode(reg[17].I & 0x1f, false);

#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE)                                                                                     \
    static INSN_REGPARM void arm##CODE1##0(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##1(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##2(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##3(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##4(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##5(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##6(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##7(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE2##0(uint32_t opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }
#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE)                                                                                      \
    static INSN_REGPARM void arm##CODE1##0(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##1(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##2(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##3(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##4(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##5(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE1##6(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } \
    static INSN_REGPARM void arm##CODE1##7(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); } \
    static INSN_REGPARM void arm##CODE2##0(uint32_t opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }

// AND
DEFINE_ALU_INSN_NC(00, 20, AND, NO)
// ANDS
DEFINE_ALU_INSN_C(01, 21, ANDS, YES)

// EOR
DEFINE_ALU_INSN_NC(02, 22, EOR, NO)
// EORS
DEFINE_ALU_INSN_C(03, 23, EORS, YES)

// SUB
DEFINE_ALU_INSN_NC(04, 24, SUB, NO)
// SUBS
DEFINE_ALU_INSN_NC(05, 25, SUBS, YES)

// RSB
DEFINE_ALU_INSN_NC(06, 26, RSB, NO)
// RSBS
DEFINE_ALU_INSN_NC(07, 27, RSBS, YES)

// ADD
DEFINE_ALU_INSN_NC(08, 28, ADD, NO)
// ADDS
DEFINE_ALU_INSN_NC(09, 29, ADDS, YES)

// ADC
DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO)
// ADCS
DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES)

// SBC
DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO)
// SBCS
DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES)

// RSC
DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO)
// RSCS
DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES)

// TST
DEFINE_ALU_INSN_C(11, 31, TST, NO)

// TEQ
DEFINE_ALU_INSN_C(13, 33, TEQ, NO)

// CMP
DEFINE_ALU_INSN_NC(15, 35, CMP, NO)

// CMN
DEFINE_ALU_INSN_NC(17, 37, CMN, NO)

// ORR
DEFINE_ALU_INSN_NC(18, 38, ORR, NO)
// ORRS
DEFINE_ALU_INSN_C(19, 39, ORRS, YES)

// MOV
DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO)
// MOVS
DEFINE_ALU_INSN_C(1B, 3B, MOVS, YES)

// BIC
DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO)
// BICS
DEFINE_ALU_INSN_C(1D, 3D, BICS, YES)

// MVN
DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO)
// MVNS
DEFINE_ALU_INSN_C(1F, 3F, MVNS, YES)

// Multiply instructions //////////////////////////////////////////////////

// OP: OP_MUL, OP_MLA etc.
// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL
// CYCLES: base cycle count (1, 2, or 3)
#define MUL_INSN(OP, SETCOND, CYCLES)                                  \
    int mult = (opcode & 0x0F);                                        \
    uint32_t rs = reg[(opcode >> 8) & 0x0F].I;                         \
    int acc = (opcode >> 12) & 0x0F; /* or destLo */                   \
    maybe_unused(acc);                                                 \
    int dest = (opcode >> 16) & 0x0F; /* or destHi */                  \
    OP;                                                                \
    SETCOND;                                                           \
    if ((int32_t)rs < 0)                                               \
        rs = ~rs;                                                      \
    if ((rs & 0xFFFFFF00) == 0)                                        \
        clockTicks += 0;                                               \
    else if ((rs & 0xFFFF0000) == 0)                                   \
        clockTicks += 1;                                               \
    else if ((rs & 0xFF000000) == 0)                                   \
        clockTicks += 2;                                               \
    else                                                               \
        clockTicks += 3;                                               \
    if (busPrefetchCount == 0)                                         \
        busPrefetchCount = ((busPrefetchCount + 1) << clockTicks) - 1; \
    clockTicks += CYCLES + 1 + codeTicksAccess32(armNextPC);

#define OP_MUL \
    reg[dest].I = reg[mult].I * rs;
#define OP_MLA \
    reg[dest].I = reg[mult].I * rs + reg[acc].I;
#define OP_UMULL                                              \
    uint64_t res = (uint64_t)(uint32_t)reg[mult].I            \
        * (uint64_t)(uint32_t)rs;                             \
    reg[acc].I = (uint32_t)res;                               \
    reg[dest].I = (uint32_t)(res >> 32);
#define OP_SMULL                                              \
    int64_t res = (int64_t)(int32_t)reg[mult].I               \
        * (int64_t)(int32_t)rs;                               \
    reg[acc].I = (uint32_t)res;                               \
    reg[dest].I = (uint32_t)(res >> 32);
#define OP_UMLAL                                              \
    uint64_t res = ((uint64_t)reg[dest].I << 32 | reg[acc].I) \
        + ((uint64_t)(uint32_t)reg[mult].I                    \
        * (uint64_t)(uint32_t)rs);                            \
    reg[acc].I = (uint32_t)res;                               \
    reg[dest].I = (uint32_t)(res >> 32);
#define OP_SMLAL                                              \
    int64_t res = ((int64_t)reg[dest].I << 32 | reg[acc].I)   \
        + ((int64_t)(int32_t)reg[mult].I                      \
        * (int64_t)(int32_t)rs);                              \
    reg[acc].I = (uint32_t)res;                               \
    reg[dest].I = (uint32_t)(res >> 32);

// MUL Rd, Rm, Rs
static INSN_REGPARM void arm009(uint32_t opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); }
// MULS Rd, Rm, Rs
static INSN_REGPARM void arm019(uint32_t opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); }

// MLA Rd, Rm, Rs, Rn
static INSN_REGPARM void arm029(uint32_t opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); }
// MLAS Rd, Rm, Rs, Rn
static INSN_REGPARM void arm039(uint32_t opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); }

// UMULL RdLo, RdHi, Rn, Rs
static INSN_REGPARM void arm089(uint32_t opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); }
// UMULLS RdLo, RdHi, Rn, Rs
static INSN_REGPARM void arm099(uint32_t opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); }

// UMLAL RdLo, RdHi, Rn, Rs
static INSN_REGPARM void arm0A9(uint32_t opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); }
// UMLALS RdLo, RdHi, Rn, Rs
static INSN_REGPARM void arm0B9(uint32_t opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); }

// SMULL RdLo, RdHi, Rm, Rs
static INSN_REGPARM void arm0C9(uint32_t opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); }
// SMULLS RdLo, RdHi, Rm, Rs
static INSN_REGPARM void arm0D9(uint32_t opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); }

// SMLAL RdLo, RdHi, Rm, Rs
static INSN_REGPARM void arm0E9(uint32_t opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); }
// SMLALS RdLo, RdHi, Rm, Rs
static INSN_REGPARM void arm0F9(uint32_t opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); }

// Misc instructions //////////////////////////////////////////////////////

// SWP Rd, Rm, [Rn]
static INSN_REGPARM void arm109(uint32_t opcode)
{
    uint32_t address = reg[(opcode >> 16) & 15].I;
    uint32_t temp = CPUReadMemory(address);
    CPUWriteMemory(address, reg[opcode & 15].I);
    reg[(opcode >> 12) & 15].I = temp;
    clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address)
        + codeTicksAccess32(armNextPC);
}

// SWPB Rd, Rm, [Rn]
static INSN_REGPARM void arm149(uint32_t opcode)
{
    uint32_t address = reg[(opcode >> 16) & 15].I;
    uint32_t temp = CPUReadByte(address);
    CPUWriteByte(address, reg[opcode & 15].B.B0);
    reg[(opcode >> 12) & 15].I = temp;
    clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address)
        + codeTicksAccess32(armNextPC);
}

// MRS Rd, CPSR
static INSN_REGPARM void arm100(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FFF0FFF) == 0x010F0000)) {
        CPUUpdateCPSR();
        reg[(opcode >> 12) & 0x0F].I = reg[16].I;
    } else {
        armUnknownInsn(opcode);
    }
}

// MRS Rd, SPSR
static INSN_REGPARM void arm140(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FFF0FFF) == 0x014F0000)) {
        reg[(opcode >> 12) & 0x0F].I = reg[17].I;
    } else {
        armUnknownInsn(opcode);
    }
}

// MSR CPSR_fields, Rm
static INSN_REGPARM void arm120(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FF0FFF0) == 0x0120F000)) {
        CPUUpdateCPSR();
        uint32_t value = reg[opcode & 15].I;
        uint32_t newValue = reg[16].I;
        if (armMode > 0x10) {
            if (opcode & 0x00010000)
                newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
            if (opcode & 0x00020000)
                newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
            if (opcode & 0x00040000)
                newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
        }
        if (opcode & 0x00080000)
            newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);
        newValue |= 0x10;
        CPUSwitchMode(newValue & 0x1F, false);
        reg[16].I = newValue;
        CPUUpdateFlags();
        if (!armState) { // this should not be allowed, but it seems to work
            THUMB_PREFETCH;
            reg[15].I = armNextPC + 2;
        }
    } else {
        armUnknownInsn(opcode);
    }
}

// MSR SPSR_fields, Rm
static INSN_REGPARM void arm160(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FF0FFF0) == 0x0160F000)) {
        uint32_t value = reg[opcode & 15].I;
        if (armMode > 0x10 && armMode < 0x1F) {
            if (opcode & 0x00010000)
                reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
            if (opcode & 0x00020000)
                reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
            if (opcode & 0x00040000)
                reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
            if (opcode & 0x00080000)
                reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
        }
    } else {
        armUnknownInsn(opcode);
    }
}

// MSR CPSR_fields, #
static INSN_REGPARM void arm320(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FF0F000) == 0x0320F000)) {
        CPUUpdateCPSR();
        uint32_t value = opcode & 0xFF;
        int shift = (opcode & 0xF00) >> 7;
        if (shift) {
            ROR_IMM_MSR;
        }
        uint32_t newValue = reg[16].I;
        if (armMode > 0x10) {
            if (opcode & 0x00010000)
                newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
            if (opcode & 0x00020000)
                newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
            if (opcode & 0x00040000)
                newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
        }
        if (opcode & 0x00080000)
            newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);

        newValue |= 0x10;

        CPUSwitchMode(newValue & 0x1F, false);
        reg[16].I = newValue;
        CPUUpdateFlags();
        if (!armState) { // this should not be allowed, but it seems to work
            THUMB_PREFETCH;
            reg[15].I = armNextPC + 2;
        }
    } else {
        armUnknownInsn(opcode);
    }
}

// MSR SPSR_fields, #
static INSN_REGPARM void arm360(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FF0F000) == 0x0360F000)) {
        if (armMode > 0x10 && armMode < 0x1F) {
            uint32_t value = opcode & 0xFF;
            int shift = (opcode & 0xF00) >> 7;
            if (shift) {
                ROR_IMM_MSR;
            }
            if (opcode & 0x00010000)
                reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
            if (opcode & 0x00020000)
                reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
            if (opcode & 0x00040000)
                reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
            if (opcode & 0x00080000)
                reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
        }
    } else {
        armUnknownInsn(opcode);
    }
}

// BX Rm
static INSN_REGPARM void arm121(uint32_t opcode)
{
    if (LIKELY((opcode & 0x0FFFFFF0) == 0x012FFF10)) {
        int base = opcode & 0x0F;
        busPrefetchCount = 0;
        armState = reg[base].I & 1 ? false : true;
        if (armState) {
            reg[15].I = reg[base].I & 0xFFFFFFFC;
            armNextPC = reg[15].I;
            reg[15].I += 4;
            ARM_PREFETCH;
            clockTicks = 3 + codeTicksAccessSeq32(armNextPC)
                + codeTicksAccessSeq32(armNextPC)
                + codeTicksAccess32(armNextPC);
        } else {
            reg[15].I = reg[base].I & 0xFFFFFFFE;
            armNextPC = reg[15].I;
            reg[15].I += 2;
            THUMB_PREFETCH;
            clockTicks = 3 + codeTicksAccessSeq16(armNextPC)
                + codeTicksAccessSeq16(armNextPC)
                + codeTicksAccess16(armNextPC);
        }
    } else {
        armUnknownInsn(opcode);
    }
}

// Load/store /////////////////////////////////////////////////////////////

#define OFFSET_IMM \
    int offset = opcode & 0xFFF;
#define OFFSET_IMM8 \
    int offset = ((opcode & 0x0F) | ((opcode >> 4) & 0xF0));
#define OFFSET_REG \
    int offset = reg[opcode & 15].I;
#define OFFSET_LSL \
    int offset = reg[opcode & 15].I << ((opcode >> 7) & 31);
#define OFFSET_LSR                  \
    int shift = (opcode >> 7) & 31; \
    int offset = shift ? reg[opcode & 15].I >> shift : 0;
#define OFFSET_ASR                                            \
    int shift = (opcode >> 7) & 31;                           \
    int offset;                                               \
    if (shift)                                                \
        offset = (int)((int32_t)reg[opcode & 15].I >> shift); \
    else if (reg[opcode & 15].I & 0x80000000)                 \
        offset = 0xFFFFFFFF;                                  \
    else                                                      \
        offset = 0;
#define OFFSET_ROR                        \
    int shift = (opcode >> 7) & 31;       \
    uint32_t offset = reg[opcode & 15].I; \
    if (shift) {                          \
        ROR_OFFSET;                       \
    } else {                              \
        RRX_OFFSET;                       \
    }

#define ADDRESS_POST (reg[base].I)
#define ADDRESS_PREDEC (reg[base].I - offset)
#define ADDRESS_PREINC (reg[base].I + offset)

#define OP_STR CPUWriteMemory(address, reg[dest].I + (dest == 15 ? 4 : 0))
#define OP_STRH CPUWriteHalfWord(address, reg[dest].W.W0)
#define OP_STRB CPUWriteByte(address, reg[dest].B.B0)
#define OP_LDR reg[dest].I = CPUReadMemory(address)
#define OP_LDRH reg[dest].I = CPUReadHalfWord(address)
#define OP_LDRB reg[dest].I = CPUReadByte(address)
#define OP_LDRSH reg[dest].I = (uint32_t)CPUReadHalfWordSigned(address)
#define OP_LDRSB reg[dest].I = (int8_t)CPUReadByte(address)

#define WRITEBACK_NONE /*nothing*/
#define WRITEBACK_PRE reg[base].I = address
#define WRITEBACK_POSTDEC reg[base].I = address - offset
#define WRITEBACK_POSTINC reg[base].I = address + offset

#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \
    if (busPrefetchCount == 0)                 \
        busPrefetch = busPrefetchEnable;       \
    int dest = (opcode >> 12) & 15;            \
    int base = (opcode >> 16) & 15;            \
    CALC_OFFSET;                               \
    uint32_t address = CALC_ADDRESS;

#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \
    LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS);                                      \
    WRITEBACK1;                                                                  \
    STORE_DATA;                                                                  \
    WRITEBACK2;                                                                  \
    clockTicks = 2 + dataTicksAccess##SIZE(address)                              \
        + codeTicksAccess32(armNextPC);
#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \
    LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS);                        \
    LOAD_DATA;                                                     \
    if (dest != base) {                                            \
        WRITEBACK;                                                 \
    }                                                              \
    clockTicks = 0;                                                \
    if (dest == 15) {                                              \
        reg[15].I &= 0xFFFFFFFC;                                   \
        armNextPC = reg[15].I;                                     \
        reg[15].I += 4;                                            \
        ARM_PREFETCH;                                              \
        clockTicks += 2 + dataTicksAccessSeq32(address)            \
            + dataTicksAccessSeq32(address);                       \
    }                                                              \
    clockTicks += 3 + dataTicksAccess##SIZE(address)               \
        + codeTicksAccess32(armNextPC);
#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE)
#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE)
#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE)
#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE)
#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE)
#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \
    STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE)
#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE)
#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE)
#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE)
#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE)
#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE)
#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \
    LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE)

// STRH Rd, [Rn], -Rm
static INSN_REGPARM void arm00B(uint32_t opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn], #-offset
static INSN_REGPARM void arm04B(uint32_t opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); }
// STRH Rd, [Rn], Rm
static INSN_REGPARM void arm08B(uint32_t opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn], #offset
static INSN_REGPARM void arm0CB(uint32_t opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); }
// STRH Rd, [Rn, -Rm]
static INSN_REGPARM void arm10B(uint32_t opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn, -Rm]!
static INSN_REGPARM void arm12B(uint32_t opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn, -#offset]
static INSN_REGPARM void arm14B(uint32_t opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); }
// STRH Rd, [Rn, -#offset]!
static INSN_REGPARM void arm16B(uint32_t opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); }
// STRH Rd, [Rn, Rm]
static INSN_REGPARM void arm18B(uint32_t opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn, Rm]!
static INSN_REGPARM void arm1AB(uint32_t opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); }
// STRH Rd, [Rn, #offset]
static INSN_REGPARM void arm1CB(uint32_t opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); }
// STRH Rd, [Rn, #offset]!
static INSN_REGPARM void arm1EB(uint32_t opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); }

// LDRH Rd, [Rn], -Rm
static INSN_REGPARM void arm01B(uint32_t opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn], #-offset
static INSN_REGPARM void arm05B(uint32_t opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); }
// LDRH Rd, [Rn], Rm
static INSN_REGPARM void arm09B(uint32_t opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn], #offset
static INSN_REGPARM void arm0DB(uint32_t opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); }
// LDRH Rd, [Rn, -Rm]
static INSN_REGPARM void arm11B(uint32_t opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn, -Rm]!
static INSN_REGPARM void arm13B(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn, -#offset]
static INSN_REGPARM void arm15B(uint32_t opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); }
// LDRH Rd, [Rn, -#offset]!
static INSN_REGPARM void arm17B(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); }
// LDRH Rd, [Rn, Rm]
static INSN_REGPARM void arm19B(uint32_t opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn, Rm]!
static INSN_REGPARM void arm1BB(uint32_t opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); }
// LDRH Rd, [Rn, #offset]
static INSN_REGPARM void arm1DB(uint32_t opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); }
// LDRH Rd, [Rn, #offset]!
static INSN_REGPARM void arm1FB(uint32_t opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); }

// LDRSB Rd, [Rn], -Rm
static INSN_REGPARM void arm01D(uint32_t opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn], #-offset
static INSN_REGPARM void arm05D(uint32_t opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); }
// LDRSB Rd, [Rn], Rm
static INSN_REGPARM void arm09D(uint32_t opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn], #offset
static INSN_REGPARM void arm0DD(uint32_t opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, -Rm]
static INSN_REGPARM void arm11D(uint32_t opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, -Rm]!
static INSN_REGPARM void arm13D(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, -#offset]
static INSN_REGPARM void arm15D(uint32_t opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, -#offset]!
static INSN_REGPARM void arm17D(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, Rm]
static INSN_REGPARM void arm19D(uint32_t opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, Rm]!
static INSN_REGPARM void arm1BD(uint32_t opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, #offset]
static INSN_REGPARM void arm1DD(uint32_t opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); }
// LDRSB Rd, [Rn, #offset]!
static INSN_REGPARM void arm1FD(uint32_t opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); }

// LDRSH Rd, [Rn], -Rm
static INSN_REGPARM void arm01F(uint32_t opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn], #-offset
static INSN_REGPARM void arm05F(uint32_t opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); }
// LDRSH Rd, [Rn], Rm
static INSN_REGPARM void arm09F(uint32_t opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn], #offset
static INSN_REGPARM void arm0DF(uint32_t opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, -Rm]
static INSN_REGPARM void arm11F(uint32_t opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, -Rm]!
static INSN_REGPARM void arm13F(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, -#offset]
static INSN_REGPARM void arm15F(uint32_t opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, -#offset]!
static INSN_REGPARM void arm17F(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, Rm]
static INSN_REGPARM void arm19F(uint32_t opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, Rm]!
static INSN_REGPARM void arm1BF(uint32_t opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, #offset]
static INSN_REGPARM void arm1DF(uint32_t opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); }
// LDRSH Rd, [Rn, #offset]!
static INSN_REGPARM void arm1FF(uint32_t opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); }

// STR[T] Rd, [Rn], -#
// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc)
static INSN_REGPARM void arm400(uint32_t opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); }
// LDR[T] Rd, [Rn], -#
static INSN_REGPARM void arm410(uint32_t opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); }
// STRB[T] Rd, [Rn], -#
static INSN_REGPARM void arm440(uint32_t opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); }
// LDRB[T] Rd, [Rn], -#
static INSN_REGPARM void arm450(uint32_t opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); }
// STR[T] Rd, [Rn], #
static INSN_REGPARM void arm480(uint32_t opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); }
// LDR Rd, [Rn], #
static INSN_REGPARM void arm490(uint32_t opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); }
// STRB[T] Rd, [Rn], #
static INSN_REGPARM void arm4C0(uint32_t opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); }
// LDRB[T] Rd, [Rn], #
static INSN_REGPARM void arm4D0(uint32_t opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); }
// STR Rd, [Rn, -#]
static INSN_REGPARM void arm500(uint32_t opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); }
// LDR Rd, [Rn, -#]
static INSN_REGPARM void arm510(uint32_t opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); }
// STR Rd, [Rn, -#]!
static INSN_REGPARM void arm520(uint32_t opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); }
// LDR Rd, [Rn, -#]!
static INSN_REGPARM void arm530(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); }
// STRB Rd, [Rn, -#]
static INSN_REGPARM void arm540(uint32_t opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); }
// LDRB Rd, [Rn, -#]
static INSN_REGPARM void arm550(uint32_t opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); }
// STRB Rd, [Rn, -#]!
static INSN_REGPARM void arm560(uint32_t opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); }
// LDRB Rd, [Rn, -#]!
static INSN_REGPARM void arm570(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); }
// STR Rd, [Rn, #]
static INSN_REGPARM void arm580(uint32_t opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); }
// LDR Rd, [Rn, #]
static INSN_REGPARM void arm590(uint32_t opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); }
// STR Rd, [Rn, #]!
static INSN_REGPARM void arm5A0(uint32_t opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); }
// LDR Rd, [Rn, #]!
static INSN_REGPARM void arm5B0(uint32_t opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); }
// STRB Rd, [Rn, #]
static INSN_REGPARM void arm5C0(uint32_t opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); }
// LDRB Rd, [Rn, #]
static INSN_REGPARM void arm5D0(uint32_t opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); }
// STRB Rd, [Rn, #]!
static INSN_REGPARM void arm5E0(uint32_t opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); }
// LDRB Rd, [Rn, #]!
static INSN_REGPARM void arm5F0(uint32_t opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); }

// STR[T] Rd, [Rn], -Rm, LSL #
static INSN_REGPARM void arm600(uint32_t opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); }
// STR[T] Rd, [Rn], -Rm, LSR #
static INSN_REGPARM void arm602(uint32_t opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); }
// STR[T] Rd, [Rn], -Rm, ASR #
static INSN_REGPARM void arm604(uint32_t opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); }
// STR[T] Rd, [Rn], -Rm, ROR #
static INSN_REGPARM void arm606(uint32_t opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); }
// LDR[T] Rd, [Rn], -Rm, LSL #
static INSN_REGPARM void arm610(uint32_t opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); }
// LDR[T] Rd, [Rn], -Rm, LSR #
static INSN_REGPARM void arm612(uint32_t opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); }
// LDR[T] Rd, [Rn], -Rm, ASR #
static INSN_REGPARM void arm614(uint32_t opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); }
// LDR[T] Rd, [Rn], -Rm, ROR #
static INSN_REGPARM void arm616(uint32_t opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); }
// STRB[T] Rd, [Rn], -Rm, LSL #
static INSN_REGPARM void arm640(uint32_t opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); }
// STRB[T] Rd, [Rn], -Rm, LSR #
static INSN_REGPARM void arm642(uint32_t opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); }
// STRB[T] Rd, [Rn], -Rm, ASR #
static INSN_REGPARM void arm644(uint32_t opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); }
// STRB[T] Rd, [Rn], -Rm, ROR #
static INSN_REGPARM void arm646(uint32_t opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); }
// LDRB[T] Rd, [Rn], -Rm, LSL #
static INSN_REGPARM void arm650(uint32_t opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); }
// LDRB[T] Rd, [Rn], -Rm, LSR #
static INSN_REGPARM void arm652(uint32_t opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); }
// LDRB[T] Rd, [Rn], -Rm, ASR #
static INSN_REGPARM void arm654(uint32_t opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); }
// LDRB Rd, [Rn], -Rm, ROR #
static INSN_REGPARM void arm656(uint32_t opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); }
// STR[T] Rd, [Rn], Rm, LSL #
static INSN_REGPARM void arm680(uint32_t opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); }
// STR[T] Rd, [Rn], Rm, LSR #
static INSN_REGPARM void arm682(uint32_t opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); }
// STR[T] Rd, [Rn], Rm, ASR #
static INSN_REGPARM void arm684(uint32_t opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); }
// STR[T] Rd, [Rn], Rm, ROR #
static INSN_REGPARM void arm686(uint32_t opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); }
// LDR[T] Rd, [Rn], Rm, LSL #
static INSN_REGPARM void arm690(uint32_t opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); }
// LDR[T] Rd, [Rn], Rm, LSR #
static INSN_REGPARM void arm692(uint32_t opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); }
// LDR[T] Rd, [Rn], Rm, ASR #
static INSN_REGPARM void arm694(uint32_t opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); }
// LDR[T] Rd, [Rn], Rm, ROR #
static INSN_REGPARM void arm696(uint32_t opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); }
// STRB[T] Rd, [Rn], Rm, LSL #
static INSN_REGPARM void arm6C0(uint32_t opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); }
// STRB[T] Rd, [Rn], Rm, LSR #
static INSN_REGPARM void arm6C2(uint32_t opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); }
// STRB[T] Rd, [Rn], Rm, ASR #
static INSN_REGPARM void arm6C4(uint32_t opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); }
// STRB[T] Rd, [Rn], Rm, ROR #
static INSN_REGPARM void arm6C6(uint32_t opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); }
// LDRB[T] Rd, [Rn], Rm, LSL #
static INSN_REGPARM void arm6D0(uint32_t opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); }
// LDRB[T] Rd, [Rn], Rm, LSR #
static INSN_REGPARM void arm6D2(uint32_t opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); }
// LDRB[T] Rd, [Rn], Rm, ASR #
static INSN_REGPARM void arm6D4(uint32_t opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); }
// LDRB[T] Rd, [Rn], Rm, ROR #
static INSN_REGPARM void arm6D6(uint32_t opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); }
// STR Rd, [Rn, -Rm, LSL #]
static INSN_REGPARM void arm700(uint32_t opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); }
// STR Rd, [Rn, -Rm, LSR #]
static INSN_REGPARM void arm702(uint32_t opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); }
// STR Rd, [Rn, -Rm, ASR #]
static INSN_REGPARM void arm704(uint32_t opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); }
// STR Rd, [Rn, -Rm, ROR #]
static INSN_REGPARM void arm706(uint32_t opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); }
// LDR Rd, [Rn, -Rm, LSL #]
static INSN_REGPARM void arm710(uint32_t opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, LSR #]
static INSN_REGPARM void arm712(uint32_t opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, ASR #]
static INSN_REGPARM void arm714(uint32_t opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, ROR #]
static INSN_REGPARM void arm716(uint32_t opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); }
// STR Rd, [Rn, -Rm, LSL #]!
static INSN_REGPARM void arm720(uint32_t opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); }
// STR Rd, [Rn, -Rm, LSR #]!
static INSN_REGPARM void arm722(uint32_t opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); }
// STR Rd, [Rn, -Rm, ASR #]!
static INSN_REGPARM void arm724(uint32_t opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); }
// STR Rd, [Rn, -Rm, ROR #]!
static INSN_REGPARM void arm726(uint32_t opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); }
// LDR Rd, [Rn, -Rm, LSL #]!
static INSN_REGPARM void arm730(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, LSR #]!
static INSN_REGPARM void arm732(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, ASR #]!
static INSN_REGPARM void arm734(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); }
// LDR Rd, [Rn, -Rm, ROR #]!
static INSN_REGPARM void arm736(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); }
// STRB Rd, [Rn, -Rm, LSL #]
static INSN_REGPARM void arm740(uint32_t opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, LSR #]
static INSN_REGPARM void arm742(uint32_t opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, ASR #]
static INSN_REGPARM void arm744(uint32_t opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, ROR #]
static INSN_REGPARM void arm746(uint32_t opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); }
// LDRB Rd, [Rn, -Rm, LSL #]
static INSN_REGPARM void arm750(uint32_t opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, LSR #]
static INSN_REGPARM void arm752(uint32_t opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, ASR #]
static INSN_REGPARM void arm754(uint32_t opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, ROR #]
static INSN_REGPARM void arm756(uint32_t opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); }
// STRB Rd, [Rn, -Rm, LSL #]!
static INSN_REGPARM void arm760(uint32_t opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, LSR #]!
static INSN_REGPARM void arm762(uint32_t opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, ASR #]!
static INSN_REGPARM void arm764(uint32_t opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); }
// STRB Rd, [Rn, -Rm, ROR #]!
static INSN_REGPARM void arm766(uint32_t opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); }
// LDRB Rd, [Rn, -Rm, LSL #]!
static INSN_REGPARM void arm770(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, LSR #]!
static INSN_REGPARM void arm772(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, ASR #]!
static INSN_REGPARM void arm774(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); }
// LDRB Rd, [Rn, -Rm, ROR #]!
static INSN_REGPARM void arm776(uint32_t opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); }
// STR Rd, [Rn, Rm, LSL #]
static INSN_REGPARM void arm780(uint32_t opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); }
// STR Rd, [Rn, Rm, LSR #]
static INSN_REGPARM void arm782(uint32_t opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); }
// STR Rd, [Rn, Rm, ASR #]
static INSN_REGPARM void arm784(uint32_t opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); }
// STR Rd, [Rn, Rm, ROR #]
static INSN_REGPARM void arm786(uint32_t opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); }
// LDR Rd, [Rn, Rm, LSL #]
static INSN_REGPARM void arm790(uint32_t opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, LSR #]
static INSN_REGPARM void arm792(uint32_t opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, ASR #]
static INSN_REGPARM void arm794(uint32_t opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, ROR #]
static INSN_REGPARM void arm796(uint32_t opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); }
// STR Rd, [Rn, Rm, LSL #]!
static INSN_REGPARM void arm7A0(uint32_t opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); }
// STR Rd, [Rn, Rm, LSR #]!
static INSN_REGPARM void arm7A2(uint32_t opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); }
// STR Rd, [Rn, Rm, ASR #]!
static INSN_REGPARM void arm7A4(uint32_t opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); }
// STR Rd, [Rn, Rm, ROR #]!
static INSN_REGPARM void arm7A6(uint32_t opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); }
// LDR Rd, [Rn, Rm, LSL #]!
static INSN_REGPARM void arm7B0(uint32_t opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, LSR #]!
static INSN_REGPARM void arm7B2(uint32_t opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, ASR #]!
static INSN_REGPARM void arm7B4(uint32_t opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); }
// LDR Rd, [Rn, Rm, ROR #]!
static INSN_REGPARM void arm7B6(uint32_t opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); }
// STRB Rd, [Rn, Rm, LSL #]
static INSN_REGPARM void arm7C0(uint32_t opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, LSR #]
static INSN_REGPARM void arm7C2(uint32_t opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, ASR #]
static INSN_REGPARM void arm7C4(uint32_t opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, ROR #]
static INSN_REGPARM void arm7C6(uint32_t opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); }
// LDRB Rd, [Rn, Rm, LSL #]
static INSN_REGPARM void arm7D0(uint32_t opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, LSR #]
static INSN_REGPARM void arm7D2(uint32_t opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, ASR #]
static INSN_REGPARM void arm7D4(uint32_t opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, ROR #]
static INSN_REGPARM void arm7D6(uint32_t opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); }
// STRB Rd, [Rn, Rm, LSL #]!
static INSN_REGPARM void arm7E0(uint32_t opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, LSR #]!
static INSN_REGPARM void arm7E2(uint32_t opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, ASR #]!
static INSN_REGPARM void arm7E4(uint32_t opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); }
// STRB Rd, [Rn, Rm, ROR #]!
static INSN_REGPARM void arm7E6(uint32_t opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); }
// LDRB Rd, [Rn, Rm, LSL #]!
static INSN_REGPARM void arm7F0(uint32_t opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, LSR #]!
static INSN_REGPARM void arm7F2(uint32_t opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, ASR #]!
static INSN_REGPARM void arm7F4(uint32_t opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); }
// LDRB Rd, [Rn, Rm, ROR #]!
static INSN_REGPARM void arm7F6(uint32_t opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); }

// STM/LDM ////////////////////////////////////////////////////////////////

#define STM_REG(bit, num)                                    \
    if (opcode & (1U << (bit))) {                            \
        CPUWriteMemory(address, reg[(num)].I);               \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        count++;                                             \
        address += 4;                                        \
    }
#define STMW_REG(bit, num)                                   \
    if (opcode & (1U << (bit))) {                            \
        CPUWriteMemory(address, reg[(num)].I);               \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        reg[base].I = temp;                                  \
        count++;                                             \
        address += 4;                                        \
    }
#define LDM_REG(bit, num)                                    \
    if (opcode & (1U << (bit))) {                            \
        reg[(num)].I = CPUReadMemory(address);               \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        count++;                                             \
        address += 4;                                        \
    }
#define STM_LOW(STORE_REG) \
    STORE_REG(0, 0);       \
    STORE_REG(1, 1);       \
    STORE_REG(2, 2);       \
    STORE_REG(3, 3);       \
    STORE_REG(4, 4);       \
    STORE_REG(5, 5);       \
    STORE_REG(6, 6);       \
    STORE_REG(7, 7);
#define STM_HIGH(STORE_REG) \
    STORE_REG(8, 8);        \
    STORE_REG(9, 9);        \
    STORE_REG(10, 10);      \
    STORE_REG(11, 11);      \
    STORE_REG(12, 12);      \
    STORE_REG(13, 13);      \
    STORE_REG(14, 14);
#define STM_HIGH_2(STORE_REG)                 \
    if (armMode == 0x11) {                    \
        STORE_REG(8, R8_FIQ);                 \
        STORE_REG(9, R9_FIQ);                 \
        STORE_REG(10, R10_FIQ);               \
        STORE_REG(11, R11_FIQ);               \
        STORE_REG(12, R12_FIQ);               \
    } else {                                  \
        STORE_REG(8, 8);                      \
        STORE_REG(9, 9);                      \
        STORE_REG(10, 10);                    \
        STORE_REG(11, 11);                    \
        STORE_REG(12, 12);                    \
    }                                         \
    if (armMode != 0x10 && armMode != 0x1F) { \
        STORE_REG(13, R13_USR);               \
        STORE_REG(14, R14_USR);               \
    } else {                                  \
        STORE_REG(13, 13);                    \
        STORE_REG(14, 14);                    \
    }
#define STM_PC                                               \
    if (opcode & (1U << 15)) {                               \
        CPUWriteMemory(address, reg[15].I + 4);              \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        count++;                                             \
    }
#define STMW_PC                                              \
    if (opcode & (1U << 15)) {                               \
        CPUWriteMemory(address, reg[15].I + 4);              \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        reg[base].I = temp;                                  \
        count++;                                             \
    }
#define LDM_LOW    \
    LDM_REG(0, 0); \
    LDM_REG(1, 1); \
    LDM_REG(2, 2); \
    LDM_REG(3, 3); \
    LDM_REG(4, 4); \
    LDM_REG(5, 5); \
    LDM_REG(6, 6); \
    LDM_REG(7, 7);
#define LDM_HIGH     \
    LDM_REG(8, 8);   \
    LDM_REG(9, 9);   \
    LDM_REG(10, 10); \
    LDM_REG(11, 11); \
    LDM_REG(12, 12); \
    LDM_REG(13, 13); \
    LDM_REG(14, 14);
#define LDM_HIGH_2                            \
    if (armMode == 0x11) {                    \
        LDM_REG(8, R8_FIQ);                   \
        LDM_REG(9, R9_FIQ);                   \
        LDM_REG(10, R10_FIQ);                 \
        LDM_REG(11, R11_FIQ);                 \
        LDM_REG(12, R12_FIQ);                 \
    } else {                                  \
        LDM_REG(8, 8);                        \
        LDM_REG(9, 9);                        \
        LDM_REG(10, 10);                      \
        LDM_REG(11, 11);                      \
        LDM_REG(12, 12);                      \
    }                                         \
    if (armMode != 0x10 && armMode != 0x1F) { \
        LDM_REG(13, R13_USR);                 \
        LDM_REG(14, R14_USR);                 \
    } else {                                  \
        LDM_REG(13, 13);                      \
        LDM_REG(14, 14);                      \
    }
#define STM_ALL        \
    STM_LOW(STM_REG);  \
    STM_HIGH(STM_REG); \
    STM_PC;
#define STMW_ALL        \
    STM_LOW(STMW_REG);  \
    STM_HIGH(STMW_REG); \
    STMW_PC;
#define LDM_ALL                                              \
    LDM_LOW;                                                 \
    LDM_HIGH;                                                \
    if (opcode & (1U << 15)) {                               \
        reg[15].I = CPUReadMemory(address);                  \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        count++;                                             \
    }                                                        \
    if (opcode & (1U << 15)) {                               \
        armNextPC = reg[15].I;                               \
        reg[15].I += 4;                                      \
        ARM_PREFETCH;                                        \
        clockTicks += 1 + codeTicksAccessSeq32(armNextPC);   \
    }
#define STM_ALL_2        \
    STM_LOW(STM_REG);    \
    STM_HIGH_2(STM_REG); \
    STM_PC;
#define STMW_ALL_2        \
    STM_LOW(STMW_REG);    \
    STM_HIGH_2(STMW_REG); \
    STMW_PC;
#define LDM_ALL_2                                            \
    LDM_LOW;                                                 \
    if (opcode & (1U << 15)) {                               \
        LDM_HIGH;                                            \
        reg[15].I = CPUReadMemory(address);                  \
        if (!count) {                                        \
            clockTicks += 1 + dataTicksAccess32(address);    \
        } else {                                             \
            clockTicks += 1 + dataTicksAccessSeq32(address); \
        }                                                    \
        count++;                                             \
    } else {                                                 \
        LDM_HIGH_2;                                          \
    }
#define LDM_ALL_2B                                         \
    if (opcode & (1U << 15)) {                             \
        CPUSwitchMode(reg[17].I & 0x1F, false);            \
        if (armState) {                                    \
            armNextPC = reg[15].I & 0xFFFFFFFC;            \
            reg[15].I = armNextPC + 4;                     \
            ARM_PREFETCH;                                  \
        } else {                                           \
            armNextPC = reg[15].I & 0xFFFFFFFE;            \
            reg[15].I = armNextPC + 2;                     \
            THUMB_PREFETCH;                                \
        }                                                  \
        clockTicks += 1 + codeTicksAccessSeq32(armNextPC); \
    }

// STMDA Rn, {Rlist}
static INSN_REGPARM void arm800(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    STM_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDA Rn, {Rlist}
static INSN_REGPARM void arm810(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMDA Rn!, {Rlist}
static INSN_REGPARM void arm820(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    STMW_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDA Rn!, {Rlist}
static INSN_REGPARM void arm830(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
}

// STMDA Rn, {Rlist}^
static INSN_REGPARM void arm840(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    STM_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDA Rn, {Rlist}^
static INSN_REGPARM void arm850(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMDA Rn!, {Rlist}^
static INSN_REGPARM void arm860(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    STMW_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDA Rn!, {Rlist}^
static INSN_REGPARM void arm870(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (temp + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIA Rn, {Rlist}
static INSN_REGPARM void arm880(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    STM_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIA Rn, {Rlist}
static INSN_REGPARM void arm890(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIA Rn!, {Rlist}
static INSN_REGPARM void arm8A0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
    STMW_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIA Rn!, {Rlist}
static INSN_REGPARM void arm8B0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
}

// STMIA Rn, {Rlist}^
static INSN_REGPARM void arm8C0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    STM_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIA Rn, {Rlist}^
static INSN_REGPARM void arm8D0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIA Rn!, {Rlist}^
static INSN_REGPARM void arm8E0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
    STMW_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIA Rn!, {Rlist}^
static INSN_REGPARM void arm8F0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = reg[base].I & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMDB Rn, {Rlist}
static INSN_REGPARM void arm900(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    STM_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDB Rn, {Rlist}
static INSN_REGPARM void arm910(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMDB Rn!, {Rlist}
static INSN_REGPARM void arm920(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    STMW_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDB Rn!, {Rlist}
static INSN_REGPARM void arm930(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
}

// STMDB Rn, {Rlist}^
static INSN_REGPARM void arm940(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    STM_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDB Rn, {Rlist}^
static INSN_REGPARM void arm950(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMDB Rn!, {Rlist}^
static INSN_REGPARM void arm960(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    STMW_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMDB Rn!, {Rlist}^
static INSN_REGPARM void arm970(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = temp & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIB Rn, {Rlist}
static INSN_REGPARM void arm980(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    STM_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIB Rn, {Rlist}
static INSN_REGPARM void arm990(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIB Rn!, {Rlist}
static INSN_REGPARM void arm9A0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
    STMW_ALL;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIB Rn!, {Rlist}
static INSN_REGPARM void arm9B0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
}

// STMIB Rn, {Rlist}^
static INSN_REGPARM void arm9C0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    STM_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIB Rn, {Rlist}^
static INSN_REGPARM void arm9D0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// STMIB Rn!, {Rlist}^
static INSN_REGPARM void arm9E0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]);
    STMW_ALL_2;
    clockTicks += 1 + codeTicksAccess32(armNextPC);
}

// LDMIB Rn!, {Rlist}^
static INSN_REGPARM void arm9F0(uint32_t opcode)
{
    if (busPrefetchCount == 0)
        busPrefetch = busPrefetchEnable;
    int base = (opcode & 0x000F0000) >> 16;
    uint32_t temp = reg[base].I + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
    uint32_t address = (reg[base].I + 4) & 0xFFFFFFFC;
    int count = 0;
    LDM_ALL_2;
    if (!(opcode & (1U << base)))
        reg[base].I = temp;
    LDM_ALL_2B;
    clockTicks += 2 + codeTicksAccess32(armNextPC);
}

// B/BL/SWI and (unimplemented) coproc support ////////////////////////////

// B <offset>
static INSN_REGPARM void armA00(uint32_t opcode)
{
    int32_t offset = ((int32_t)(opcode & 0x00FFFFFF) << 8) >> 6;
    reg[15].I += offset;
    armNextPC = reg[15].I;
    reg[15].I += 4;
    ARM_PREFETCH;
    clockTicks = (codeTicksAccessSeq32(armNextPC) * 2) + codeTicksAccess32(armNextPC) + 3;
    busPrefetchCount = 0;
}

// BL <offset>
static INSN_REGPARM void armB00(uint32_t opcode)
{
    int32_t offset = ((int32_t)(opcode & 0x00FFFFFF) << 8) >> 6;
    reg[14].I = reg[15].I - 4;
    reg[15].I += offset;
    armNextPC = reg[15].I;
    reg[15].I += 4;
    ARM_PREFETCH;
    clockTicks = (codeTicksAccessSeq32(armNextPC) * 2) + codeTicksAccess32(armNextPC) + 3;
    busPrefetchCount = 0;
}

#ifdef GP_SUPPORT
// MRC
static INSN_REGPARM void armE01(uint32_t opcode)
{
}
#else
#define armE01 armUnknownInsn
#endif

// SWI <comment>
static INSN_REGPARM void armF00(uint32_t opcode)
{
    clockTicks = (codeTicksAccessSeq32(armNextPC) * 2) + codeTicksAccess32(armNextPC) + 3;
    busPrefetchCount = 0;
    CPUSoftwareInterrupt(opcode & 0x00FFFFFF);
}

// Instruction table //////////////////////////////////////////////////////

typedef INSN_REGPARM void (*insnfunc_t)(uint32_t opcode);
#define REP16(insn)                                 \
    insn, insn, insn, insn, insn, insn, insn, insn, \
        insn, insn, insn, insn, insn, insn, insn, insn
#define REP256(insn)                                        \
    REP16(insn)                                             \
    , REP16(insn), REP16(insn), REP16(insn),                \
        REP16(insn), REP16(insn), REP16(insn), REP16(insn), \
        REP16(insn), REP16(insn), REP16(insn), REP16(insn), \
        REP16(insn), REP16(insn), REP16(insn), REP16(insn)
#define arm_UI armUnknownInsn
#ifdef VBAM_ENABLE_DEBUGGER
#define arm_BP armBreakpoint
#else
#define arm_BP armUnknownInsn
#endif
static insnfunc_t armInsnTable[4096] = {
    arm000, arm001, arm002, arm003, arm004, arm005, arm006, arm007, // 000
    arm000, arm009, arm002, arm00B, arm004, arm_UI, arm006, arm_UI, // 008
    arm010, arm011, arm012, arm013, arm014, arm015, arm016, arm017, // 010
    arm010, arm019, arm012, arm01B, arm014, arm01D, arm016, arm01F, // 018
    arm020, arm021, arm022, arm023, arm024, arm025, arm026, arm027, // 020
    arm020, arm029, arm022, arm_UI, arm024, arm_UI, arm026, arm_UI, // 028
    arm030, arm031, arm032, arm033, arm034, arm035, arm036, arm037, // 030
    arm030, arm039, arm032, arm_UI, arm034, arm01D, arm036, arm01F, // 038
    arm040, arm041, arm042, arm043, arm044, arm045, arm046, arm047, // 040
    arm040, arm_UI, arm042, arm04B, arm044, arm_UI, arm046, arm_UI, // 048
    arm050, arm051, arm052, arm053, arm054, arm055, arm056, arm057, // 050
    arm050, arm_UI, arm052, arm05B, arm054, arm05D, arm056, arm05F, // 058
    arm060, arm061, arm062, arm063, arm064, arm065, arm066, arm067, // 060
    arm060, arm_UI, arm062, arm_UI, arm064, arm_UI, arm066, arm_UI, // 068
    arm070, arm071, arm072, arm073, arm074, arm075, arm076, arm077, // 070
    arm070, arm_UI, arm072, arm_UI, arm074, arm05D, arm076, arm05F, // 078
    arm080, arm081, arm082, arm083, arm084, arm085, arm086, arm087, // 080
    arm080, arm089, arm082, arm08B, arm084, arm_UI, arm086, arm_UI, // 088
    arm090, arm091, arm092, arm093, arm094, arm095, arm096, arm097, // 090
    arm090, arm099, arm092, arm09B, arm094, arm09D, arm096, arm09F, // 098
    arm0A0, arm0A1, arm0A2, arm0A3, arm0A4, arm0A5, arm0A6, arm0A7, // 0A0
    arm0A0, arm0A9, arm0A2, arm_UI, arm0A4, arm_UI, arm0A6, arm_UI, // 0A8
    arm0B0, arm0B1, arm0B2, arm0B3, arm0B4, arm0B5, arm0B6, arm0B7, // 0B0
    arm0B0, arm0B9, arm0B2, arm_UI, arm0B4, arm09D, arm0B6, arm09F, // 0B8
    arm0C0, arm0C1, arm0C2, arm0C3, arm0C4, arm0C5, arm0C6, arm0C7, // 0C0
    arm0C0, arm0C9, arm0C2, arm0CB, arm0C4, arm_UI, arm0C6, arm_UI, // 0C8
    arm0D0, arm0D1, arm0D2, arm0D3, arm0D4, arm0D5, arm0D6, arm0D7, // 0D0
    arm0D0, arm0D9, arm0D2, arm0DB, arm0D4, arm0DD, arm0D6, arm0DF, // 0D8
    arm0E0, arm0E1, arm0E2, arm0E3, arm0E4, arm0E5, arm0E6, arm0E7, // 0E0
    arm0E0, arm0E9, arm0E2, arm0CB, arm0E4, arm_UI, arm0E6, arm_UI, // 0E8
    arm0F0, arm0F1, arm0F2, arm0F3, arm0F4, arm0F5, arm0F6, arm0F7, // 0F0
    arm0F0, arm0F9, arm0F2, arm0DB, arm0F4, arm0DD, arm0F6, arm0DF, // 0F8

    arm100, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, // 100
    arm_UI, arm109, arm_UI, arm10B, arm_UI, arm_UI, arm_UI, arm_UI, // 108
    arm110, arm111, arm112, arm113, arm114, arm115, arm116, arm117, // 110
    arm110, arm_UI, arm112, arm11B, arm114, arm11D, arm116, arm11F, // 118
    arm120, arm121, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_BP, // 120
    arm_UI, arm_UI, arm_UI, arm12B, arm_UI, arm_UI, arm_UI, arm_UI, // 128
    arm130, arm131, arm132, arm133, arm134, arm135, arm136, arm137, // 130
    arm130, arm_UI, arm132, arm13B, arm134, arm13D, arm136, arm13F, // 138
    arm140, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, // 140
    arm_UI, arm149, arm_UI, arm14B, arm_UI, arm_UI, arm_UI, arm_UI, // 148
    arm150, arm151, arm152, arm153, arm154, arm155, arm156, arm157, // 150
    arm150, arm_UI, arm152, arm15B, arm154, arm15D, arm156, arm15F, // 158
    arm160, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, arm_UI, // 160
    arm_UI, arm_UI, arm_UI, arm16B, arm_UI, arm_UI, arm_UI, arm_UI, // 168
    arm170, arm171, arm172, arm173, arm174, arm175, arm176, arm177, // 170
    arm170, arm_UI, arm172, arm17B, arm174, arm17D, arm176, arm17F, // 178
    arm180, arm181, arm182, arm183, arm184, arm185, arm186, arm187, // 180
    arm180, arm_UI, arm182, arm18B, arm184, arm_UI, arm186, arm_UI, // 188
    arm190, arm191, arm192, arm193, arm194, arm195, arm196, arm197, // 190
    arm190, arm_UI, arm192, arm19B, arm194, arm19D, arm196, arm19F, // 198
    arm1A0, arm1A1, arm1A2, arm1A3, arm1A4, arm1A5, arm1A6, arm1A7, // 1A0
    arm1A0, arm_UI, arm1A2, arm1AB, arm1A4, arm_UI, arm1A6, arm_UI, // 1A8
    arm1B0, arm1B1, arm1B2, arm1B3, arm1B4, arm1B5, arm1B6, arm1B7, // 1B0
    arm1B0, arm_UI, arm1B2, arm1BB, arm1B4, arm1BD, arm1B6, arm1BF, // 1B8
    arm1C0, arm1C1, arm1C2, arm1C3, arm1C4, arm1C5, arm1C6, arm1C7, // 1C0
    arm1C0, arm_UI, arm1C2, arm1CB, arm1C4, arm_UI, arm1C6, arm_UI, // 1C8
    arm1D0, arm1D1, arm1D2, arm1D3, arm1D4, arm1D5, arm1D6, arm1D7, // 1D0
    arm1D0, arm_UI, arm1D2, arm1DB, arm1D4, arm1DD, arm1D6, arm1DF, // 1D8
    arm1E0, arm1E1, arm1E2, arm1E3, arm1E4, arm1E5, arm1E6, arm1E7, // 1E0
    arm1E0, arm_UI, arm1E2, arm1EB, arm1E4, arm_UI, arm1E6, arm_UI, // 1E8
    arm1F0, arm1F1, arm1F2, arm1F3, arm1F4, arm1F5, arm1F6, arm1F7, // 1F0
    arm1F0, arm_UI, arm1F2, arm1FB, arm1F4, arm1FD, arm1F6, arm1FF, // 1F8

    REP16(arm200), REP16(arm210), REP16(arm220), REP16(arm230), // 200
    REP16(arm240), REP16(arm250), REP16(arm260), REP16(arm270), // 240
    REP16(arm280), REP16(arm290), REP16(arm2A0), REP16(arm2B0), // 280
    REP16(arm2C0), REP16(arm2D0), REP16(arm2E0), REP16(arm2F0), // 2C0
    REP16(arm_UI), REP16(arm310), REP16(arm320), REP16(arm330), // 300
    REP16(arm_UI), REP16(arm350), REP16(arm360), REP16(arm370), // 340
    REP16(arm380), REP16(arm390), REP16(arm3A0), REP16(arm3B0), // 380
    REP16(arm3C0), REP16(arm3D0), REP16(arm3E0), REP16(arm3F0), // 3C0

    REP16(arm400), REP16(arm410), REP16(arm400), REP16(arm410), // 400
    REP16(arm440), REP16(arm450), REP16(arm440), REP16(arm450), // 440
    REP16(arm480), REP16(arm490), REP16(arm480), REP16(arm490), // 480
    REP16(arm4C0), REP16(arm4D0), REP16(arm4C0), REP16(arm4D0), // 4C0
    REP16(arm500), REP16(arm510), REP16(arm520), REP16(arm530), // 500
    REP16(arm540), REP16(arm550), REP16(arm560), REP16(arm570), // 540
    REP16(arm580), REP16(arm590), REP16(arm5A0), REP16(arm5B0), // 580
    REP16(arm5C0), REP16(arm5D0), REP16(arm5E0), REP16(arm5F0), // 5C0

    arm600, arm_UI, arm602, arm_UI, arm604, arm_UI, arm606, arm_UI, // 600
    arm600, arm_UI, arm602, arm_UI, arm604, arm_UI, arm606, arm_UI, // 608
    arm610, arm_UI, arm612, arm_UI, arm614, arm_UI, arm616, arm_UI, // 610
    arm610, arm_UI, arm612, arm_UI, arm614, arm_UI, arm616, arm_UI, // 618
    arm600, arm_UI, arm602, arm_UI, arm604, arm_UI, arm606, arm_UI, // 620
    arm600, arm_UI, arm602, arm_UI, arm604, arm_UI, arm606, arm_UI, // 628
    arm610, arm_UI, arm612, arm_UI, arm614, arm_UI, arm616, arm_UI, // 630
    arm610, arm_UI, arm612, arm_UI, arm614, arm_UI, arm616, arm_UI, // 638
    arm640, arm_UI, arm642, arm_UI, arm644, arm_UI, arm646, arm_UI, // 640
    arm640, arm_UI, arm642, arm_UI, arm644, arm_UI, arm646, arm_UI, // 648
    arm650, arm_UI, arm652, arm_UI, arm654, arm_UI, arm656, arm_UI, // 650
    arm650, arm_UI, arm652, arm_UI, arm654, arm_UI, arm656, arm_UI, // 658
    arm640, arm_UI, arm642, arm_UI, arm644, arm_UI, arm646, arm_UI, // 660
    arm640, arm_UI, arm642, arm_UI, arm644, arm_UI, arm646, arm_UI, // 668
    arm650, arm_UI, arm652, arm_UI, arm654, arm_UI, arm656, arm_UI, // 670
    arm650, arm_UI, arm652, arm_UI, arm654, arm_UI, arm656, arm_UI, // 678
    arm680, arm_UI, arm682, arm_UI, arm684, arm_UI, arm686, arm_UI, // 680
    arm680, arm_UI, arm682, arm_UI, arm684, arm_UI, arm686, arm_UI, // 688
    arm690, arm_UI, arm692, arm_UI, arm694, arm_UI, arm696, arm_UI, // 690
    arm690, arm_UI, arm692, arm_UI, arm694, arm_UI, arm696, arm_UI, // 698
    arm680, arm_UI, arm682, arm_UI, arm684, arm_UI, arm686, arm_UI, // 6A0
    arm680, arm_UI, arm682, arm_UI, arm684, arm_UI, arm686, arm_UI, // 6A8
    arm690, arm_UI, arm692, arm_UI, arm694, arm_UI, arm696, arm_UI, // 6B0
    arm690, arm_UI, arm692, arm_UI, arm694, arm_UI, arm696, arm_UI, // 6B8
    arm6C0, arm_UI, arm6C2, arm_UI, arm6C4, arm_UI, arm6C6, arm_UI, // 6C0
    arm6C0, arm_UI, arm6C2, arm_UI, arm6C4, arm_UI, arm6C6, arm_UI, // 6C8
    arm6D0, arm_UI, arm6D2, arm_UI, arm6D4, arm_UI, arm6D6, arm_UI, // 6D0
    arm6D0, arm_UI, arm6D2, arm_UI, arm6D4, arm_UI, arm6D6, arm_UI, // 6D8
    arm6C0, arm_UI, arm6C2, arm_UI, arm6C4, arm_UI, arm6C6, arm_UI, // 6E0
    arm6C0, arm_UI, arm6C2, arm_UI, arm6C4, arm_UI, arm6C6, arm_UI, // 6E8
    arm6D0, arm_UI, arm6D2, arm_UI, arm6D4, arm_UI, arm6D6, arm_UI, // 6F0
    arm6D0, arm_UI, arm6D2, arm_UI, arm6D4, arm_UI, arm6D6, arm_UI, // 6F8

    arm700, arm_UI, arm702, arm_UI, arm704, arm_UI, arm706, arm_UI, // 700
    arm700, arm_UI, arm702, arm_UI, arm704, arm_UI, arm706, arm_UI, // 708
    arm710, arm_UI, arm712, arm_UI, arm714, arm_UI, arm716, arm_UI, // 710
    arm710, arm_UI, arm712, arm_UI, arm714, arm_UI, arm716, arm_UI, // 718
    arm720, arm_UI, arm722, arm_UI, arm724, arm_UI, arm726, arm_UI, // 720
    arm720, arm_UI, arm722, arm_UI, arm724, arm_UI, arm726, arm_UI, // 728
    arm730, arm_UI, arm732, arm_UI, arm734, arm_UI, arm736, arm_UI, // 730
    arm730, arm_UI, arm732, arm_UI, arm734, arm_UI, arm736, arm_UI, // 738
    arm740, arm_UI, arm742, arm_UI, arm744, arm_UI, arm746, arm_UI, // 740
    arm740, arm_UI, arm742, arm_UI, arm744, arm_UI, arm746, arm_UI, // 748
    arm750, arm_UI, arm752, arm_UI, arm754, arm_UI, arm756, arm_UI, // 750
    arm750, arm_UI, arm752, arm_UI, arm754, arm_UI, arm756, arm_UI, // 758
    arm760, arm_UI, arm762, arm_UI, arm764, arm_UI, arm766, arm_UI, // 760
    arm760, arm_UI, arm762, arm_UI, arm764, arm_UI, arm766, arm_UI, // 768
    arm770, arm_UI, arm772, arm_UI, arm774, arm_UI, arm776, arm_UI, // 770
    arm770, arm_UI, arm772, arm_UI, arm774, arm_UI, arm776, arm_UI, // 778
    arm780, arm_UI, arm782, arm_UI, arm784, arm_UI, arm786, arm_UI, // 780
    arm780, arm_UI, arm782, arm_UI, arm784, arm_UI, arm786, arm_UI, // 788
    arm790, arm_UI, arm792, arm_UI, arm794, arm_UI, arm796, arm_UI, // 790
    arm790, arm_UI, arm792, arm_UI, arm794, arm_UI, arm796, arm_UI, // 798
    arm7A0, arm_UI, arm7A2, arm_UI, arm7A4, arm_UI, arm7A6, arm_UI, // 7A0
    arm7A0, arm_UI, arm7A2, arm_UI, arm7A4, arm_UI, arm7A6, arm_UI, // 7A8
    arm7B0, arm_UI, arm7B2, arm_UI, arm7B4, arm_UI, arm7B6, arm_UI, // 7B0
    arm7B0, arm_UI, arm7B2, arm_UI, arm7B4, arm_UI, arm7B6, arm_UI, // 7B8
    arm7C0, arm_UI, arm7C2, arm_UI, arm7C4, arm_UI, arm7C6, arm_UI, // 7C0
    arm7C0, arm_UI, arm7C2, arm_UI, arm7C4, arm_UI, arm7C6, arm_UI, // 7C8
    arm7D0, arm_UI, arm7D2, arm_UI, arm7D4, arm_UI, arm7D6, arm_UI, // 7D0
    arm7D0, arm_UI, arm7D2, arm_UI, arm7D4, arm_UI, arm7D6, arm_UI, // 7D8
    arm7E0, arm_UI, arm7E2, arm_UI, arm7E4, arm_UI, arm7E6, arm_UI, // 7E0
    arm7E0, arm_UI, arm7E2, arm_UI, arm7E4, arm_UI, arm7E6, arm_UI, // 7E8
    arm7F0, arm_UI, arm7F2, arm_UI, arm7F4, arm_UI, arm7F6, arm_UI, // 7F0
    arm7F0, arm_UI, arm7F2, arm_UI, arm7F4, arm_UI, arm7F6, arm_BP, // 7F8

    REP16(arm800), REP16(arm810), REP16(arm820), REP16(arm830), // 800
    REP16(arm840), REP16(arm850), REP16(arm860), REP16(arm870), // 840
    REP16(arm880), REP16(arm890), REP16(arm8A0), REP16(arm8B0), // 880
    REP16(arm8C0), REP16(arm8D0), REP16(arm8E0), REP16(arm8F0), // 8C0
    REP16(arm900), REP16(arm910), REP16(arm920), REP16(arm930), // 900
    REP16(arm940), REP16(arm950), REP16(arm960), REP16(arm970), // 940
    REP16(arm980), REP16(arm990), REP16(arm9A0), REP16(arm9B0), // 980
    REP16(arm9C0), REP16(arm9D0), REP16(arm9E0), REP16(arm9F0), // 9C0

    REP256(armA00), // A00
    REP256(armB00), // B00
    REP256(arm_UI), // C00
    REP256(arm_UI), // D00

    arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, // E00
    arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, // E08
    arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, // E10
    arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, arm_UI, armE01, // E18
    REP16(arm_UI), // E20
    REP16(arm_UI), // E30
    REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), // E40
    REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), // E80
    REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), REP16(arm_UI), // EC0

    REP256(armF00), // F00
};

// Wrapper routine (execution loop) ///////////////////////////////////////

#if 0
#include <time.h>
static void tester(void) {
  static int ran=0;if(ran)return;ran=1;
  FILE*f=fopen("p:\\timing.txt","w");if(!f)return;
  for (int op=/*0*/9; op</*0xF00*/10;op++){if(armInsnTable[op]==arm_UI)continue;
    int i;for(i=0;i<op;i++)if(armInsnTable[op]==armInsnTable[i])break;if(i<op)continue;
    for(i=0;i<16;i++)reg[i].I=0x3100000;
    clock_t s=clock();for(i=0;i<10000000;i++)armInsnTable[op](0);clock_t e=clock();
    fprintf(f,"arm%03X %6ld\n",op,e-s);fflush(f);
  }fclose(f);
}
#endif

int armExecute()
{
    do {
        if (coreOptions.cheatsEnabled) {
            cpuMasterCodeCheck();
        }

        if ((armNextPC & 0x0803FFFF) == 0x08020000)
            busPrefetchCount = 0x100;

        uint32_t opcode = cpuPrefetch[0];
        cpuPrefetch[0] = cpuPrefetch[1];

        busPrefetch = false;
        if (busPrefetchCount & 0xFFFFFE00)
            busPrefetchCount = 0x100 | (busPrefetchCount & 0xFF);

        clockTicks = 0;
        int oldArmNextPC = armNextPC;

#ifndef FINAL_VERSION
        if (armNextPC == stop) {
            armNextPC++;
        }
#endif

        armNextPC = reg[15].I;
        reg[15].I += 4;
        ARM_PREFETCH_NEXT;

#ifdef VBAM_ENABLE_DEBUGGER
        uint32_t memAddr = armNextPC;
        memoryMap* m = &map[memAddr >> 24];
        if (m->breakPoints && BreakARMCheck(m->breakPoints, memAddr & m->mask)) {
            if (debuggerBreakOnExecution(memAddr, armState)) {
                // Revert tickcount?
                debugger = true;
                return 0;
            }
        }
#endif

        int cond = opcode >> 28;
        bool cond_res = true;
        if (UNLIKELY(cond != 0x0E)) { // most opcodes are AL (always)
            switch (cond) {
            case 0x00: // EQ
                cond_res = Z_FLAG;
                break;
            case 0x01: // NE
                cond_res = !Z_FLAG;
                break;
            case 0x02: // CS
                cond_res = C_FLAG;
                break;
            case 0x03: // CC
                cond_res = !C_FLAG;
                break;
            case 0x04: // MI
                cond_res = N_FLAG;
                break;
            case 0x05: // PL
                cond_res = !N_FLAG;
                break;
            case 0x06: // VS
                cond_res = V_FLAG;
                break;
            case 0x07: // VC
                cond_res = !V_FLAG;
                break;
            case 0x08: // HI
                cond_res = C_FLAG && !Z_FLAG;
                break;
            case 0x09: // LS
                cond_res = !C_FLAG || Z_FLAG;
                break;
            case 0x0A: // GE
                cond_res = N_FLAG == V_FLAG;
                break;
            case 0x0B: // LT
                cond_res = N_FLAG != V_FLAG;
                break;
            case 0x0C: // GT
                cond_res = !Z_FLAG && (N_FLAG == V_FLAG);
                break;
            case 0x0D: // LE
                cond_res = Z_FLAG || (N_FLAG != V_FLAG);
                break;
            case 0x0E: // AL (impossible, checked above)
                cond_res = true;
                break;
            case 0x0F:
            default:
                // ???
                cond_res = false;
                break;
            }
        }

        if (cond_res)
            (*armInsnTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x0F)])(opcode);
#ifdef INSN_COUNTER
        count(opcode, cond_res);
#endif

#ifdef VBAM_ENABLE_DEBUGGER
        if (enableRegBreak) {
            if (lowRegBreakCounter[0])
                breakReg_check(0);
            if (lowRegBreakCounter[1])
                breakReg_check(1);
            if (lowRegBreakCounter[2])
                breakReg_check(2);
            if (lowRegBreakCounter[3])
                breakReg_check(3);
            if (medRegBreakCounter[0])
                breakReg_check(4);
            if (medRegBreakCounter[1])
                breakReg_check(5);
            if (medRegBreakCounter[2])
                breakReg_check(6);
            if (medRegBreakCounter[3])
                breakReg_check(7);
            if (highRegBreakCounter[0])
                breakReg_check(8);
            if (highRegBreakCounter[1])
                breakReg_check(9);
            if (highRegBreakCounter[2])
                breakReg_check(10);
            if (highRegBreakCounter[3])
                breakReg_check(11);
            if (statusRegBreakCounter[0])
                breakReg_check(12);
            if (statusRegBreakCounter[1])
                breakReg_check(13);
            if (statusRegBreakCounter[2])
                breakReg_check(14);
            if (statusRegBreakCounter[3])
                breakReg_check(15);
        }
#endif
        if (clockTicks < 0)
            return 0;
        if (clockTicks == 0)
            clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC);
        cpuTotalTicks += clockTicks;

    } while (cpuTotalTicks < cpuNextEvent && armState && !holdState && !SWITicks && !debugger);

    return 1;
}
