#include "GBA.h"
#include "GBAcpu.h"
#include "GBAinline.h"
#include "Globals.h"
#include "Sound.h"
#include "bios.h"

#ifdef _MSC_VER
# define snprintf _snprintf
#endif

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

static int clockTicks;

static INSN_REGPARM void thumbUnknownInsn(uint32_t opcode)
{
	CPUUndefinedException();
}

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

static inline uint32_t NEG(uint32_t i) { return i >> 31; }
static inline uint32_t POS(uint32_t i) { return ~i >> 31; }

#ifndef C_CORE
# ifdef __GNUC__
#  ifdef __POWERPC__
#   define ADD_RD_RS_RN(N) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("addco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[source].I), \
		"r" (reg[N].I) \
		); \
	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 ADD_RD_RS_O3(N) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("addco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[source].I), \
		"r" (N) \
		); \
	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 ADD_RD_RS_O3_0 ADD_RD_RS_O3
#   define ADD_RN_O8(d) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("addco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[(d)].I), \
		"r" (opcode & 255) \
		); \
	reg[(d)].I = Result; \
	Z_FLAG = (Flags >> 29) & 1; \
	N_FLAG = (Flags >> 31) & 1; \
	C_FLAG = (Flags >> 25) & 1; \
	V_FLAG = (Flags >> 26) & 1; \
}
#   define CMN_RD_RS \
{\
	register int Flags; \
	register int Result; \
	asm volatile("addco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[dest].I), \
		"r" (value) \
		); \
	Z_FLAG = (Flags >> 29) & 1; \
	N_FLAG = (Flags >> 31) & 1; \
	C_FLAG = (Flags >> 25) & 1; \
	V_FLAG = (Flags >> 26) & 1; \
}
#   define ADC_RD_RS \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("mtspr 1, %4\n" \ /* reg 1 is xer */
		"addeo. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr	%1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[dest].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 SUB_RD_RS_RN(N) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[source].I), \
		"r" (reg[N].I) \
		); \
	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 SUB_RD_RS_O3(N) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[source].I), \
		"r" (N) \
		); \
	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 SUB_RD_RS_O3_0 SUB_RD_RS_O3
#   define SUB_RN_O8(d) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[(d)].I), \
		"r" (opcode & 255) \
		); \
	reg[(d)].I = Result; \
	Z_FLAG = (Flags >> 29) & 1; \
	N_FLAG = (Flags >> 31) & 1; \
	C_FLAG = (Flags >> 25) & 1; \
	V_FLAG = (Flags >> 26) & 1; \
}
#   define CMP_RN_O8(d) \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[(d)].I), \
		"r" (opcode & 255) \
		); \
	Z_FLAG = (Flags >> 29) & 1; \
	N_FLAG = (Flags >> 31) & 1; \
	C_FLAG = (Flags >> 25) & 1; \
	V_FLAG = (Flags >> 26) & 1; \
}
#   define SBC_RD_RS \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("mtspr 1, %4\n" \ /* reg 1 is xer */
		"subfeo. %0, %3, %2\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[dest].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 NEG_RD_RS \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subfco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[source].I), \
		"r" (0) \
	); \
	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 CMP_RD_RS \
{ \
	register int Flags; \
	register int Result; \
	asm volatile("subco. %0, %2, %3\n" \
		"mcrxr cr1\n" \
		"mfcr %1\n" \
		: "=r" (Result), \
		"=r" (Flags) \
		: "r" (reg[dest].I), \
		"r" (value) \
		); \
	Z_FLAG = (Flags >> 29) & 1; \
	N_FLAG = (Flags >> 31) & 1; \
	C_FLAG = (Flags >> 25) & 1; \
	V_FLAG = (Flags >> 26) & 1; \
}
#  else
#   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 REGREF1(index) ASMVAR("reg(" index ")")
#   define REGREF2(index, scale) ASMVAR("reg(," index "," #scale ")")
#   define eax "%%eax"
#   define ecx "%%ecx"
#   define edx "%%edx"
#   define ADD_RN_O8(d) \
	asm("andl $0xFF, %%eax;"\
		"addl %%eax, %0;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setcb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: "=m" (reg[(d)].I));
#   define CMN_RD_RS \
	asm("add %0, %1;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setcb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: \
		: "r" (value), "r" (reg[dest].I) : "1");
#   define ADC_RD_RS \
	asm(EMIT2(bt, KONST(0), VAR(C_FLAG)) \
		"adc %1, %%ebx;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setcb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: "=b" (reg[dest].I) \
		: "r" (value), "b" (reg[dest].I));
#   define SUB_RN_O8(d) \
	asm("andl $0xFF, %%eax;" \
		"subl %%eax, %0;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setncb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: "=m" (reg[(d)].I));
#   define MOV_RN_O8(d) \
	asm("andl $0xFF, %%eax;" \
		EMIT2(movb, KONST(0), VAR(N_FLAG)) \
		"movl %%eax, %0;" \
		EMIT1(setzb, VAR(Z_FLAG)) \
		: "=m" (reg[(d)].I));
#   define CMP_RN_O8(d) \
	asm("andl $0xFF, %%eax;" \
		"cmpl %%eax, %0;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setncb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: \
		: "m" (reg[(d)].I));
#   define SBC_RD_RS \
	asm volatile(EMIT2(bt, KONST(0), VAR(C_FLAG)) \
		"cmc;" \
		"sbb %1, %%ebx;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setncb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: "=b" (reg[dest].I) \
		: "r" (value), "b" (reg[dest].I) : "cc", "memory");
#   define LSL_RD_RS \
	asm("shl %%cl, %%eax;" \
		EMIT1(setcb, VAR(C_FLAG)) \
		: "=a" (value) \
		: "a" (reg[dest].I), "c" (value));
#   define LSR_RD_RS \
	asm("shr %%cl, %%eax;" \
		EMIT1(setcb, VAR(C_FLAG)) \
		: "=a" (value) \
		: "a" (reg[dest].I), "c" (value));
#   define ASR_RD_RS \
	asm("sar %%cl, %%eax;" \
		EMIT1(setcb, VAR(C_FLAG)) \
		: "=a" (value) \
		: "a" (reg[dest].I), "c" (value));
#   define ROR_RD_RS \
	asm("ror %%cl, %%eax;" \
		EMIT1(setcb, VAR(C_FLAG)) \
		: "=a" (value) \
		: "a" (reg[dest].I), "c" (value));
#   define NEG_RD_RS \
	asm("neg %%ebx;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setncb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: "=b" (reg[dest].I) \
		: "b" (reg[source].I));
#   define CMP_RD_RS \
	asm("sub %0, %1;" \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT1(setncb, VAR(C_FLAG)) \
		EMIT1(setob, VAR(V_FLAG)) \
		: \
		: "r" (value), "r" (reg[dest].I) : "1");
#   define IMM5_INSN(OP,N) \
	asm("movl %%eax,%%ecx;" \
		"shrl $1,%%eax;" \
		"andl $7,%%ecx;" \
		"andl $0x1C,%%eax;" \
		EMIT2(movl, REGREF1(eax), edx) \
		OP \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT2(movl, edx, REGREF2(ecx,4)) \
		: : "i" (N))
#   define IMM5_INSN_0(OP) \
	asm("movl %%eax,%%ecx;" \
		"shrl $1,%%eax;" \
		"andl $7,%%ecx;" \
		"andl $0x1C,%%eax;" \
		EMIT2(movl, REGREF1(eax), edx) \
		OP \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT2(movl, edx, REGREF2(ecx,4)) \
		: : )
#   define IMM5_LSL \
	"shll %0,%%edx;"\
	EMIT1(setcb, VAR(C_FLAG))
#   define IMM5_LSL_0 \
	"testl %%edx,%%edx;"
#   define IMM5_LSR \
	"shrl %0,%%edx;"\
	EMIT1(setcb, VAR(C_FLAG))
#   define IMM5_LSR_0 \
	"testl %%edx,%%edx;"\
	EMIT1(setsb, VAR(C_FLAG)) \
	"xorl %%edx,%%edx;"
#   define IMM5_ASR \
	"sarl %0,%%edx;"\
	EMIT1(setcb, VAR(C_FLAG))
#  define IMM5_ASR_0 \
	"sarl $31,%%edx;"\
	EMIT1(setsb, VAR(C_FLAG))
#   define THREEARG_INSN(OP, N) \
	asm("movl %%eax,%%edx;" \
		"shrl $1,%%edx;" \
		"andl $0x1C,%%edx;" \
		"andl $7,%%eax;" \
		EMIT2(movl, REGREF1(edx), ecx) \
		OP(N) \
		EMIT1(setsb, VAR(N_FLAG)) \
		EMIT1(setzb, VAR(Z_FLAG)) \
		EMIT2(movl, ecx, REGREF2(eax,4)) \
		: : )
#   define ADD_RD_RS_RN(N) \
	EMIT2(add, VAR(reg) "+" #N "*4", ecx) \
	EMIT1(setcb, VAR(C_FLAG)) \
	EMIT1(setob, VAR(V_FLAG))
#   define ADD_RD_RS_O3(N) \
	"add $" #N ",%%ecx;" \
	EMIT1(setcb, VAR(C_FLAG)) \
	EMIT1(setob, VAR(V_FLAG))
#   define ADD_RD_RS_O3_0(N) \
	EMIT2(movb, KONST(0), VAR(C_FLAG)) \
	"add $0,%%ecx;" \
	EMIT2(movb, KONST(0), VAR(V_FLAG))
#   define SUB_RD_RS_RN(N) \
	EMIT2(sub, VAR(reg) "+" #N "*4", ecx) \
	EMIT1(setncb, VAR(C_FLAG)) \
	EMIT1(setob, VAR(V_FLAG))
#   define SUB_RD_RS_O3(N) \
	"sub $"#N",%%ecx;" \
	EMIT1(setncb, VAR(C_FLAG)) \
	EMIT1(setob, VAR(V_FLAG))
#   define SUB_RD_RS_O3_0(N) \
	EMIT2(movb, KONST(1), VAR(C_FLAG)) \
	"sub $0,%%ecx;" \
	EMIT2(movb, KONST(0), VAR(V_FLAG))
#  endif
# else // !__GNUC__
#  define ADD_RD_RS_RN(N) \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm add ebx, dword ptr [OFFSET reg + 4 * N] \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define ADD_RD_RS_O3(N) \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm add ebx, N \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define ADD_RD_RS_O3_0 \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm add ebx, 0 \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm mov byte ptr C_FLAG, 0 \
	__asm mov byte ptr V_FLAG, 0 \
}
#  define ADD_RN_O8(d) \
{ \
	__asm mov ebx, opcode \
	__asm and ebx, 255 \
	__asm add dword ptr [OFFSET reg + 4 * (d)], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define CMN_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm add ebx, value \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define ADC_RD_RS \
{ \
	__asm mov ebx, dest \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \
	__asm bt word ptr C_FLAG, 0 \
	__asm adc ebx, value \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define SUB_RD_RS_RN(N) \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm sub ebx, dword ptr [OFFSET reg + 4 * N] \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define SUB_RD_RS_O3(N) \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm sub ebx, N \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define SUB_RD_RS_O3_0 \
{ \
	__asm mov eax, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm sub ebx, 0 \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm mov byte ptr C_FLAG, 1 \
	__asm mov byte ptr V_FLAG, 0 \
}
#  define SUB_RN_O8(d) \
{ \
	__asm mov ebx, opcode \
	__asm and ebx, 255 \
	__asm sub dword ptr [OFFSET reg + 4 * (d)], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define MOV_RN_O8(d) \
{ \
	__asm mov eax, opcode \
	__asm and eax, 255 \
	__asm mov dword ptr [OFFSET reg + 4 * (d)], eax \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
}
#  define CMP_RN_O8(d) \
{ \
	__asm mov eax, dword ptr [OFFSET reg + 4 * (d)] \
	__asm mov ebx, opcode \
	__asm and ebx, 255 \
	__asm sub eax, ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define SBC_RD_RS \
{ \
	__asm mov ebx, dest \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \
	__asm mov eax, value \
	__asm bt word ptr C_FLAG, 0 \
	__asm cmc \
	__asm sbb ebx, eax \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define LSL_RD_RM_I5 \
{ \
	__asm mov eax, source \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr shift \
	__asm shl eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define LSL_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr value \
	__asm shl eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define LSR_RD_RM_I5 \
{ \
	__asm mov eax, source \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr shift \
	__asm shr eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define LSR_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr value \
	__asm shr eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define ASR_RD_RM_I5 \
{ \
	__asm mov eax, source \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr shift \
	__asm sar eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define ASR_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr value \
	__asm sar eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define ROR_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov eax, dword ptr [OFFSET reg + 4 * eax] \
	__asm mov cl, byte ptr value \
	__asm ror eax, cl \
	__asm mov value, eax \
	__asm setc byte ptr C_FLAG \
}
#  define NEG_RD_RS \
{ \
	__asm mov ebx, source \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \
	__asm neg ebx \
	__asm mov eax, dest \
	__asm mov dword ptr [OFFSET reg + 4 * eax], ebx \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
#  define CMP_RD_RS \
{ \
	__asm mov eax, dest \
	__asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \
	__asm sub ebx, value \
	__asm sets byte ptr N_FLAG \
	__asm setz byte ptr Z_FLAG \
	__asm setnc byte ptr C_FLAG \
	__asm seto byte ptr V_FLAG \
}
# endif
#endif

// C core
#ifndef ADDCARRY
# define ADDCARRY(a, b, c) \
	C_FLAG = !!((NEG(a) & NEG(b)) | (NEG(a) & POS(c)) | (NEG(b) & POS(c)));
#endif
#ifndef ADDOVERFLOW
# define ADDOVERFLOW(a, b, c) \
	V_FLAG = !!((NEG(a) & NEG(b) & POS(c)) | (POS(a) & POS(b) & NEG(c)));
#endif
#ifndef SUBCARRY
# define SUBCARRY(a, b, c) \
	C_FLAG = !!((NEG(a) & POS(b)) | (NEG(a) & POS(c)) | (POS(b) & POS(c)));
#endif
#ifndef SUBOVERFLOW
# define SUBOVERFLOW(a, b, c) \
	V_FLAG = !!((NEG(a) & POS(b) & POS(c)) | (POS(a) & NEG(b) & NEG(c)));
#endif
#ifndef ADD_RD_RS_RN
# define ADD_RD_RS_RN(N) \
{ \
	uint32_t lhs = reg[source].I; \
	uint32_t rhs = reg[N].I; \
	uint32_t res = lhs + rhs; \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	ADDCARRY(lhs, rhs, res); \
	ADDOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef ADD_RD_RS_O3
# define ADD_RD_RS_O3(N) \
{ \
	uint32_t lhs = reg[source].I; \
	uint32_t rhs = N; \
	uint32_t res = lhs + rhs; \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	ADDCARRY(lhs, rhs, res); \
	ADDOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef ADD_RD_RS_O3_0
# define ADD_RD_RS_O3_0 ADD_RD_RS_O3
#endif
#ifndef ADD_RN_O8
# define ADD_RN_O8(d) \
{ \
	uint32_t lhs = reg[(d)].I; \
	uint32_t rhs = opcode & 255; \
	uint32_t res = lhs + rhs; \
	reg[(d)].I = res;\
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	ADDCARRY(lhs, rhs, res); \
	ADDOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef CMN_RD_RS
# define CMN_RD_RS \
{ \
	uint32_t lhs = reg[dest].I; \
	uint32_t rhs = value; \
	uint32_t res = lhs + rhs; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	ADDCARRY(lhs, rhs, res); \
	ADDOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef ADC_RD_RS
# define ADC_RD_RS \
{ \
	uint32_t lhs = reg[dest].I; \
	uint32_t rhs = value; \
	uint32_t res = lhs + rhs + static_cast<uint32_t>(C_FLAG); \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	ADDCARRY(lhs, rhs, res); \
	ADDOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef SUB_RD_RS_RN
# define SUB_RD_RS_RN(N) \
{ \
	uint32_t lhs = reg[source].I; \
	uint32_t rhs = reg[N].I; \
	uint32_t res = lhs - rhs; \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef SUB_RD_RS_O3
# define SUB_RD_RS_O3(N) \
{ \
	uint32_t lhs = reg[source].I; \
	uint32_t rhs = N; \
	uint32_t res = lhs - rhs; \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef SUB_RD_RS_O3_0
# define SUB_RD_RS_O3_0 SUB_RD_RS_O3
#endif
#ifndef SUB_RN_O8
# define SUB_RN_O8(d) \
{ \
	uint32_t lhs = reg[(d)].I; \
	uint32_t rhs = opcode & 255; \
	uint32_t res = lhs - rhs; \
	reg[(d)].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef MOV_RN_O8
# define MOV_RN_O8(d) \
{ \
	reg[d].I = opcode & 255; \
	N_FLAG = false; \
	Z_FLAG = !reg[d].I; \
}
#endif
#ifndef CMP_RN_O8
# define CMP_RN_O8(d) \
{ \
	uint32_t lhs = reg[(d)].I; \
	uint32_t rhs = opcode & 255; \
	uint32_t res = lhs - rhs; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef SBC_RD_RS
# define SBC_RD_RS \
{ \
	uint32_t lhs = reg[dest].I; \
	uint32_t rhs = value; \
	uint32_t res = lhs - rhs - !static_cast<uint32_t>(C_FLAG); \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef LSL_RD_RM_I5
# define LSL_RD_RM_I5 \
{ \
	C_FLAG = !!((reg[source].I >> (32 - shift)) & 1); \
	value = reg[source].I << shift; \
}
#endif
#ifndef LSL_RD_RS
# define LSL_RD_RS \
{ \
	C_FLAG = !!((reg[dest].I >> (32 - value)) & 1); \
	value = reg[dest].I << value; \
}
#endif
#ifndef LSR_RD_RM_I5
# define LSR_RD_RM_I5 \
{ \
	C_FLAG = !!((reg[source].I >> (shift - 1)) & 1); \
	value = reg[source].I >> shift; \
}
#endif
#ifndef LSR_RD_RS
# define LSR_RD_RS \
{ \
	C_FLAG = !!((reg[dest].I >> (value - 1)) & 1); \
	value = reg[dest].I >> value; \
}
#endif
#ifndef ASR_RD_RM_I5
# define ASR_RD_RM_I5 \
{ \
	C_FLAG = !!((static_cast<int32_t>(reg[source].I) >> static_cast<int>(shift - 1)) & 1); \
	value = static_cast<int32_t>(reg[source].I) >> static_cast<int>(shift); \
}
#endif
#ifndef ASR_RD_RS
# define ASR_RD_RS \
{ \
	C_FLAG = !!((static_cast<int32_t>(reg[dest].I >> static_cast<int>(value - 1)) & 1); \
	value = static_cast<int32_t>(reg[dest].I) >> static_cast<int>(value); \
}
#endif
#ifndef ROR_RD_RS
# define ROR_RD_RS \
{ \
	C_FLAG = !!((reg[dest].I >> (value - 1)) & 1); \
	value = (reg[dest].I << (32 - value)) | (reg[dest].I >> value); \
}
#endif
#ifndef NEG_RD_RS
# define NEG_RD_RS \
{ \
	uint32_t lhs = reg[source].I; \
	uint32_t rhs = 0; \
	uint32_t res = rhs - lhs; \
	reg[dest].I = res; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(rhs, lhs, res); \
	SUBOVERFLOW(rhs, lhs, res); \
}
#endif
#ifndef CMP_RD_RS
# define CMP_RD_RS \
{ \
	uint32_t lhs = reg[dest].I; \
	uint32_t rhs = value; \
	uint32_t res = lhs - rhs; \
	Z_FLAG = !res; \
	N_FLAG = !!NEG(res); \
	SUBCARRY(lhs, rhs, res); \
	SUBOVERFLOW(lhs, rhs, res); \
}
#endif
#ifndef IMM5_INSN
# define IMM5_INSN(OP, N) \
	int dest = opcode & 0x07; \
	int source = (opcode >> 3) & 0x07; \
	uint32_t value; \
	OP(N); \
	reg[dest].I = value; \
	N_FLAG = !!(value & 0x80000000); \
	Z_FLAG = !value;
# define IMM5_INSN_0(OP) \
	int dest = opcode & 0x07; \
	int source = (opcode >> 3) & 0x07; \
	uint32_t value; \
	OP; \
	reg[dest].I = value; \
	N_FLAG = !!(value & 0x80000000); \
	Z_FLAG = !value;
# define IMM5_LSL(N) \
	int shift = N; \
	LSL_RD_RM_I5;
# define IMM5_LSL_0 \
	value = reg[source].I;
# define IMM5_LSR(N) \
	int shift = N; \
	LSR_RD_RM_I5;
# define IMM5_LSR_0 \
	C_FLAG = !!(reg[source].I & 0x80000000); \
	value = 0;
# define IMM5_ASR(N) \
	int shift = N; \
	ASR_RD_RM_I5;
# define IMM5_ASR_0 \
	if (reg[source].I & 0x80000000) \
	{ \
		value = 0xFFFFFFFF; \
		C_FLAG = true; \
	} \
	else \
	{ \
		value = 0; \
		C_FLAG = false; \
	}
#endif
#ifndef THREEARG_INSN
# define THREEARG_INSN(OP, N) \
	int dest = opcode & 0x07; \
	int source = (opcode >> 3) & 0x07; \
	OP(N);
#endif

// Shift instructions /////////////////////////////////////////////////////

#define DEFINE_IMM5_INSN(OP, BASE) \
	static INSN_REGPARM void thumb##BASE##_00(uint32_t opcode) { IMM5_INSN_0(OP##_0); } \
	static INSN_REGPARM void thumb##BASE##_01(uint32_t opcode) { IMM5_INSN(OP, 1); } \
	static INSN_REGPARM void thumb##BASE##_02(uint32_t opcode) { IMM5_INSN(OP, 2); } \
	static INSN_REGPARM void thumb##BASE##_03(uint32_t opcode) { IMM5_INSN(OP, 3); } \
	static INSN_REGPARM void thumb##BASE##_04(uint32_t opcode) { IMM5_INSN(OP, 4); } \
	static INSN_REGPARM void thumb##BASE##_05(uint32_t opcode) { IMM5_INSN(OP, 5); } \
	static INSN_REGPARM void thumb##BASE##_06(uint32_t opcode) { IMM5_INSN(OP, 6); } \
	static INSN_REGPARM void thumb##BASE##_07(uint32_t opcode) { IMM5_INSN(OP, 7); } \
	static INSN_REGPARM void thumb##BASE##_08(uint32_t opcode) { IMM5_INSN(OP, 8); } \
	static INSN_REGPARM void thumb##BASE##_09(uint32_t opcode) { IMM5_INSN(OP, 9); } \
	static INSN_REGPARM void thumb##BASE##_0A(uint32_t opcode) { IMM5_INSN(OP, 10); } \
	static INSN_REGPARM void thumb##BASE##_0B(uint32_t opcode) { IMM5_INSN(OP, 11); } \
	static INSN_REGPARM void thumb##BASE##_0C(uint32_t opcode) { IMM5_INSN(OP, 12); } \
	static INSN_REGPARM void thumb##BASE##_0D(uint32_t opcode) { IMM5_INSN(OP, 13); } \
	static INSN_REGPARM void thumb##BASE##_0E(uint32_t opcode) { IMM5_INSN(OP, 14); } \
	static INSN_REGPARM void thumb##BASE##_0F(uint32_t opcode) { IMM5_INSN(OP, 15); } \
	static INSN_REGPARM void thumb##BASE##_10(uint32_t opcode) { IMM5_INSN(OP, 16); } \
	static INSN_REGPARM void thumb##BASE##_11(uint32_t opcode) { IMM5_INSN(OP, 17); } \
	static INSN_REGPARM void thumb##BASE##_12(uint32_t opcode) { IMM5_INSN(OP, 18); } \
	static INSN_REGPARM void thumb##BASE##_13(uint32_t opcode) { IMM5_INSN(OP, 19); } \
	static INSN_REGPARM void thumb##BASE##_14(uint32_t opcode) { IMM5_INSN(OP, 20); } \
	static INSN_REGPARM void thumb##BASE##_15(uint32_t opcode) { IMM5_INSN(OP, 21); } \
	static INSN_REGPARM void thumb##BASE##_16(uint32_t opcode) { IMM5_INSN(OP, 22); } \
	static INSN_REGPARM void thumb##BASE##_17(uint32_t opcode) { IMM5_INSN(OP, 23); } \
	static INSN_REGPARM void thumb##BASE##_18(uint32_t opcode) { IMM5_INSN(OP, 24); } \
	static INSN_REGPARM void thumb##BASE##_19(uint32_t opcode) { IMM5_INSN(OP, 25); } \
	static INSN_REGPARM void thumb##BASE##_1A(uint32_t opcode) { IMM5_INSN(OP, 26); } \
	static INSN_REGPARM void thumb##BASE##_1B(uint32_t opcode) { IMM5_INSN(OP, 27); } \
	static INSN_REGPARM void thumb##BASE##_1C(uint32_t opcode) { IMM5_INSN(OP, 28); } \
	static INSN_REGPARM void thumb##BASE##_1D(uint32_t opcode) { IMM5_INSN(OP, 29); } \
	static INSN_REGPARM void thumb##BASE##_1E(uint32_t opcode) { IMM5_INSN(OP, 30); } \
	static INSN_REGPARM void thumb##BASE##_1F(uint32_t opcode) { IMM5_INSN(OP, 31); }

// LSL Rd, Rm, #Imm 5
DEFINE_IMM5_INSN(IMM5_LSL, 00)
// LSR Rd, Rm, #Imm 5
DEFINE_IMM5_INSN(IMM5_LSR, 08)
// ASR Rd, Rm, #Imm 5
DEFINE_IMM5_INSN(IMM5_ASR, 10)

// 3-argument ADD/SUB /////////////////////////////////////////////////////

#define DEFINE_REG3_INSN(OP, BASE) \
	static INSN_REGPARM void thumb##BASE##_0(uint32_t opcode) { THREEARG_INSN(OP, 0); } \
	static INSN_REGPARM void thumb##BASE##_1(uint32_t opcode) { THREEARG_INSN(OP, 1); } \
	static INSN_REGPARM void thumb##BASE##_2(uint32_t opcode) { THREEARG_INSN(OP, 2); } \
	static INSN_REGPARM void thumb##BASE##_3(uint32_t opcode) { THREEARG_INSN(OP, 3); } \
	static INSN_REGPARM void thumb##BASE##_4(uint32_t opcode) { THREEARG_INSN(OP, 4); } \
	static INSN_REGPARM void thumb##BASE##_5(uint32_t opcode) { THREEARG_INSN(OP, 5); } \
	static INSN_REGPARM void thumb##BASE##_6(uint32_t opcode) { THREEARG_INSN(OP, 6); } \
	static INSN_REGPARM void thumb##BASE##_7(uint32_t opcode) { THREEARG_INSN(OP, 7); }

#define DEFINE_IMM3_INSN(OP, BASE) \
	static INSN_REGPARM void thumb##BASE##_0(uint32_t opcode) { THREEARG_INSN(OP##_0, 0); } \
	static INSN_REGPARM void thumb##BASE##_1(uint32_t opcode) { THREEARG_INSN(OP, 1); } \
	static INSN_REGPARM void thumb##BASE##_2(uint32_t opcode) { THREEARG_INSN(OP, 2); } \
	static INSN_REGPARM void thumb##BASE##_3(uint32_t opcode) { THREEARG_INSN(OP, 3); } \
	static INSN_REGPARM void thumb##BASE##_4(uint32_t opcode) { THREEARG_INSN(OP, 4); } \
	static INSN_REGPARM void thumb##BASE##_5(uint32_t opcode) { THREEARG_INSN(OP, 5); } \
	static INSN_REGPARM void thumb##BASE##_6(uint32_t opcode) { THREEARG_INSN(OP, 6); } \
	static INSN_REGPARM void thumb##BASE##_7(uint32_t opcode) { THREEARG_INSN(OP, 7); }

// ADD Rd, Rs, Rn
DEFINE_REG3_INSN(ADD_RD_RS_RN, 18)
// SUB Rd, Rs, Rn
DEFINE_REG3_INSN(SUB_RD_RS_RN, 1A)
// ADD Rd, Rs, #Offset3
DEFINE_IMM3_INSN(ADD_RD_RS_O3, 1C)
// SUB Rd, Rs, #Offset3
DEFINE_IMM3_INSN(SUB_RD_RS_O3, 1E)

// MOV/CMP/ADD/SUB immediate //////////////////////////////////////////////

// MOV R0, #Offset8
static INSN_REGPARM void thumb20(uint32_t opcode) { MOV_RN_O8(0); }
// MOV R1, #Offset8
static INSN_REGPARM void thumb21(uint32_t opcode) { MOV_RN_O8(1); }
// MOV R2, #Offset8
static INSN_REGPARM void thumb22(uint32_t opcode) { MOV_RN_O8(2); }
// MOV R3, #Offset8
static INSN_REGPARM void thumb23(uint32_t opcode) { MOV_RN_O8(3); }
// MOV R4, #Offset8
static INSN_REGPARM void thumb24(uint32_t opcode) { MOV_RN_O8(4); }
// MOV R5, #Offset8
static INSN_REGPARM void thumb25(uint32_t opcode) { MOV_RN_O8(5); }
// MOV R6, #Offset8
static INSN_REGPARM void thumb26(uint32_t opcode) { MOV_RN_O8(6); }
// MOV R7, #Offset8
static INSN_REGPARM void thumb27(uint32_t opcode) { MOV_RN_O8(7); }

// CMP R0, #Offset8
static INSN_REGPARM void thumb28(uint32_t opcode) { CMP_RN_O8(0); }
// CMP R1, #Offset8
static INSN_REGPARM void thumb29(uint32_t opcode) { CMP_RN_O8(1); }
// CMP R2, #Offset8
static INSN_REGPARM void thumb2A(uint32_t opcode) { CMP_RN_O8(2); }
// CMP R3, #Offset8
static INSN_REGPARM void thumb2B(uint32_t opcode) { CMP_RN_O8(3); }
// CMP R4, #Offset8
static INSN_REGPARM void thumb2C(uint32_t opcode) { CMP_RN_O8(4); }
// CMP R5, #Offset8
static INSN_REGPARM void thumb2D(uint32_t opcode) { CMP_RN_O8(5); }
// CMP R6, #Offset8
static INSN_REGPARM void thumb2E(uint32_t opcode) { CMP_RN_O8(6); }
// CMP R7, #Offset8
static INSN_REGPARM void thumb2F(uint32_t opcode) { CMP_RN_O8(7); }

// ADD R0,#Offset8
static INSN_REGPARM void thumb30(uint32_t opcode) { ADD_RN_O8(0); }
// ADD R1,#Offset8
static INSN_REGPARM void thumb31(uint32_t opcode) { ADD_RN_O8(1); }
// ADD R2,#Offset8
static INSN_REGPARM void thumb32(uint32_t opcode) { ADD_RN_O8(2); }
// ADD R3,#Offset8
static INSN_REGPARM void thumb33(uint32_t opcode) { ADD_RN_O8(3); }
// ADD R4,#Offset8
static INSN_REGPARM void thumb34(uint32_t opcode) { ADD_RN_O8(4); }
// ADD R5,#Offset8
static INSN_REGPARM void thumb35(uint32_t opcode) { ADD_RN_O8(5); }
// ADD R6,#Offset8
static INSN_REGPARM void thumb36(uint32_t opcode) { ADD_RN_O8(6); }
// ADD R7,#Offset8
static INSN_REGPARM void thumb37(uint32_t opcode) { ADD_RN_O8(7); }

// SUB R0,#Offset8
static INSN_REGPARM void thumb38(uint32_t opcode) { SUB_RN_O8(0); }
// SUB R1,#Offset8
static INSN_REGPARM void thumb39(uint32_t opcode) { SUB_RN_O8(1); }
// SUB R2,#Offset8
static INSN_REGPARM void thumb3A(uint32_t opcode) { SUB_RN_O8(2); }
// SUB R3,#Offset8
static INSN_REGPARM void thumb3B(uint32_t opcode) { SUB_RN_O8(3); }
// SUB R4,#Offset8
static INSN_REGPARM void thumb3C(uint32_t opcode) { SUB_RN_O8(4); }
// SUB R5,#Offset8
static INSN_REGPARM void thumb3D(uint32_t opcode) { SUB_RN_O8(5); }
// SUB R6,#Offset8
static INSN_REGPARM void thumb3E(uint32_t opcode) { SUB_RN_O8(6); }
// SUB R7,#Offset8
static INSN_REGPARM void thumb3F(uint32_t opcode) { SUB_RN_O8(7); }

// ALU operations /////////////////////////////////////////////////////////

// AND Rd, Rs
static INSN_REGPARM void thumb40_0(uint32_t opcode)
{
	int dest = opcode & 7;
	reg[dest].I &= reg[(opcode >> 3) & 7].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
}

// EOR Rd, Rs
static INSN_REGPARM void thumb40_1(uint32_t opcode)
{
	int dest = opcode & 7;
	reg[dest].I ^= reg[(opcode >> 3) & 7].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
}

// LSL Rd, Rs
static INSN_REGPARM void thumb40_2(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].B.B0;
	if (value)
	{
		if (value == 32)
		{
			value = 0;
			C_FLAG = !!(reg[dest].I & 1);
		}
		else if (value < 32)
		{
			LSL_RD_RS;
		}
		else
		{
			value = 0;
			C_FLAG = false;
		}
		reg[dest].I = value;
	}
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
	clockTicks = codeTicksAccess16(armNextPC) + 2;
}

// LSR Rd, Rs
static INSN_REGPARM void thumb40_3(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].B.B0;
	if (value)
	{
		if (value == 32)
		{
			value = 0;
			C_FLAG = !!(reg[dest].I & 0x80000000);
		}
		else if (value < 32)
		{
			LSR_RD_RS;
		}
		else
		{
			value = 0;
			C_FLAG = false;
		}
		reg[dest].I = value;
	}
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
	clockTicks = codeTicksAccess16(armNextPC) + 2;
}

// ASR Rd, Rs
static INSN_REGPARM void thumb41_0(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].B.B0;
	if (value)
	{
		if (value < 32)
		{
			ASR_RD_RS;
			reg[dest].I = value;
		}
		else
		{
			if (reg[dest].I & 0x80000000)
			{
				reg[dest].I = 0xFFFFFFFF;
				C_FLAG = true;
			}
			else
			{
				reg[dest].I = 0x00000000;
				C_FLAG = false;
			}
		}
	}
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
	clockTicks = codeTicksAccess16(armNextPC) + 2;
}

// ADC Rd, Rs
static INSN_REGPARM void thumb41_1(uint32_t opcode)
{
	int dest = opcode & 0x07;
	uint32_t value = reg[(opcode >> 3) & 7].I;
	ADC_RD_RS;
}

// SBC Rd, Rs
static INSN_REGPARM void thumb41_2(uint32_t opcode)
{
	int dest = opcode & 0x07;
	uint32_t value = reg[(opcode >> 3) & 7].I;
	SBC_RD_RS;
}

// ROR Rd, Rs
static INSN_REGPARM void thumb41_3(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].B.B0;

	if (value)
	{
		value &= 0x1f;
		if (!value)
			C_FLAG = !!(reg[dest].I & 0x80000000);
		else
		{
			ROR_RD_RS;
			reg[dest].I = value;
		}
	}
	clockTicks = codeTicksAccess16(armNextPC) + 2;
	N_FLAG = !!(reg[dest].I & 0x80000000);
	Z_FLAG = !reg[dest].I;
}

// TST Rd, Rs
static INSN_REGPARM void thumb42_0(uint32_t opcode)
{
	uint32_t value = reg[opcode & 7].I & reg[(opcode >> 3) & 7].I;
	N_FLAG = !!(value & 0x80000000);
	Z_FLAG = !value;
}

// NEG Rd, Rs
static INSN_REGPARM void thumb42_1(uint32_t opcode)
{
	int dest = opcode & 7;
	int source = (opcode >> 3) & 7;
	NEG_RD_RS;
}

// CMP Rd, Rs
static INSN_REGPARM void thumb42_2(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].I;
	CMP_RD_RS;
}

// CMN Rd, Rs
static INSN_REGPARM void thumb42_3(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[(opcode >> 3) & 7].I;
	CMN_RD_RS;
}

// ORR Rd, Rs
static INSN_REGPARM void thumb43_0(uint32_t opcode)
{
	int dest = opcode & 7;
	reg[dest].I |= reg[(opcode >> 3) & 7].I;
	Z_FLAG = !reg[dest].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
}

// MUL Rd, Rs
static INSN_REGPARM void thumb43_1(uint32_t opcode)
{
	clockTicks = 1;
	int dest = opcode & 7;
	uint32_t rm = reg[dest].I;
	reg[dest].I = reg[(opcode >> 3) & 7].I * rm;
	if (static_cast<int32_t>(rm) < 0)
		rm = ~rm;
	if (!(rm & 0xFFFFFF00))
		; // No-op
	else if (!(rm & 0xFFFF0000))
		++clockTicks;
	else if (!(rm & 0xFF000000))
		clockTicks += 2;
	else
		clockTicks += 3;
	busPrefetchCount = (busPrefetchCount << clockTicks) | (0xFF >> (8 - clockTicks));
	clockTicks += codeTicksAccess16(armNextPC) + 1;
	Z_FLAG = !reg[dest].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
}

// BIC Rd, Rs
static INSN_REGPARM void thumb43_2(uint32_t opcode)
{
	int dest = opcode & 7;
	reg[dest].I &= ~reg[(opcode >> 3) & 7].I;
	Z_FLAG = !reg[dest].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
}

// MVN Rd, Rs
static INSN_REGPARM void thumb43_3(uint32_t opcode)
{
	int dest = opcode & 7;
	reg[dest].I = ~reg[(opcode >> 3) & 7].I;
	Z_FLAG = !reg[dest].I;
	N_FLAG = !!(reg[dest].I & 0x80000000);
}

// High-register instructions and BX //////////////////////////////////////

// ADD Rd, Hs
static INSN_REGPARM void thumb44_1(uint32_t opcode)
{
	reg[opcode & 7].I += reg[((opcode >> 3) & 7) + 8].I;
}

// ADD Hd, Rs
static INSN_REGPARM void thumb44_2(uint32_t opcode)
{
	reg[(opcode & 7) + 8].I += reg[(opcode >> 3) & 7].I;
	if ((opcode & 7) == 7)
	{
		reg[15].I &= 0xFFFFFFFE;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
  }
}

// ADD Hd, Hs
static INSN_REGPARM void thumb44_3(uint32_t opcode)
{
	reg[(opcode & 7) + 8].I += reg[((opcode >> 3) & 7) + 8].I;
	if ((opcode & 7) == 7)
	{
		reg[15].I &= 0xFFFFFFFE;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	}
}

// CMP Rd, Hs
static INSN_REGPARM void thumb45_1(uint32_t opcode)
{
	int dest = opcode & 7;
	uint32_t value = reg[((opcode >> 3) & 7) + 8].I;
	CMP_RD_RS;
}

// CMP Hd, Rs
static INSN_REGPARM void thumb45_2(uint32_t opcode)
{
	int dest = (opcode & 7) + 8;
	uint32_t value = reg[(opcode >> 3) & 7].I;
	CMP_RD_RS;
}

// CMP Hd, Hs
static INSN_REGPARM void thumb45_3(uint32_t opcode)
{
	int dest = (opcode & 7) + 8;
	uint32_t value = reg[((opcode >> 3) & 7) + 8].I;
	CMP_RD_RS;
}

// MOV Rd, Rs
static INSN_REGPARM void thumb46_0(uint32_t opcode)
{
	reg[opcode & 7].I = reg[(opcode >> 3) & 7].I;
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
}

// MOV Rd, Hs
static INSN_REGPARM void thumb46_1(uint32_t opcode)
{
	reg[opcode & 7].I = reg[((opcode >> 3) & 7) + 8].I;
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
}

// MOV Hd, Rs
static INSN_REGPARM void thumb46_2(uint32_t opcode)
{
	reg[(opcode & 7) + 8].I = reg[(opcode >> 3) & 7].I;
	if ((opcode & 7) == 7)
	{
		reg[15].I &= 0xFFFFFFFE;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	}
}

// MOV Hd, Hs
static INSN_REGPARM void thumb46_3(uint32_t opcode)
{
	reg[(opcode & 7) + 8].I = reg[((opcode >> 3) & 7) + 8].I;
	if ((opcode & 7) == 7)
	{
		reg[15].I &= 0xFFFFFFFE;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	}
}

// BX Rs
static INSN_REGPARM void thumb47(uint32_t opcode)
{
	int base = (opcode >> 3) & 15;
	busPrefetchCount = 0;
	reg[15].I = reg[base].I;
	if (reg[base].I & 1)
	{
		armState = false;
		reg[15].I &= 0xFFFFFFFE;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	}
	else
	{
		armState = true;
		reg[15].I &= 0xFFFFFFFC;
		armNextPC = reg[15].I;
		reg[15].I += 4;
		ARM_PREFETCH();
		clockTicks = codeTicksAccessSeq32(armNextPC) * 2 + codeTicksAccess32(armNextPC) + 3;
	}
}

// Load/store instructions ////////////////////////////////////////////////

// LDR R0~R7,[PC, #Imm]
static INSN_REGPARM void thumb48(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2);
	reg[regist].I = CPUReadMemoryQuick(address);
	busPrefetchCount = 0;
	clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC);
}

// STR Rd, [Rs, Rn]
static INSN_REGPARM void thumb50(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	CPUWriteMemory(address, reg[opcode & 7].I);
	clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2;
}

// STRH Rd, [Rs, Rn]
static INSN_REGPARM void thumb52(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	CPUWriteHalfWord(address, reg[opcode & 7].W.W0);
	clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2;
}

// STRB Rd, [Rs, Rn]
static INSN_REGPARM void thumb54(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	CPUWriteByte(address, reg[opcode & 7].B.B0);
	clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2;
}

// LDSB Rd, [Rs, Rn]
static INSN_REGPARM void thumb56(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	reg[opcode & 7].I = static_cast<int8_t>(CPUReadByte(address));
	clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC);
}

// LDR Rd, [Rs, Rn]
static INSN_REGPARM void thumb58(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	reg[opcode & 7].I = CPUReadMemory(address);
	clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC);
}

// LDRH Rd, [Rs, Rn]
static INSN_REGPARM void thumb5A(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	reg[opcode & 7].I = CPUReadHalfWord(address);
	clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC);
}

// LDRB Rd, [Rs, Rn]
static INSN_REGPARM void thumb5C(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	reg[opcode & 7].I = CPUReadByte(address);
	clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC);
}

// LDSH Rd, [Rs, Rn]
static INSN_REGPARM void thumb5E(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I;
	reg[opcode & 7].I = (uint32_t)CPUReadHalfWordSigned(address);
	clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC);
}

// STR Rd, [Rs, #Imm]
static INSN_REGPARM void thumb60(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 2);
	CPUWriteMemory(address, reg[opcode & 7].I);
	clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2;
}

// LDR Rd, [Rs, #Imm]
static INSN_REGPARM void thumb68(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 2);
	reg[opcode & 7].I = CPUReadMemory(address);
	clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC);
}

// STRB Rd, [Rs, #Imm]
static INSN_REGPARM void thumb70(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + ((opcode >> 6) & 31);
	CPUWriteByte(address, reg[opcode & 7].B.B0);
	clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2;
}

// LDRB Rd, [Rs, #Imm]
static INSN_REGPARM void thumb78(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + ((opcode >> 6) & 31);
	reg[opcode & 7].I = CPUReadByte(address);
	clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC);
}

// STRH Rd, [Rs, #Imm]
static INSN_REGPARM void thumb80(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 1);
	CPUWriteHalfWord(address, reg[opcode & 7].W.W0);
	clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2;
}

// LDRH Rd, [Rs, #Imm]
static INSN_REGPARM void thumb88(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 1);
	reg[opcode & 7].I = CPUReadHalfWord(address);
	clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC);
}

// STR R0~R7, [SP, #Imm]
static INSN_REGPARM void thumb90(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[13].I + ((opcode & 255) << 2);
	CPUWriteMemory(address, reg[regist].I);
	clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2;
}

// LDR R0~R7, [SP, #Imm]
static INSN_REGPARM void thumb98(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[13].I + ((opcode & 255) << 2);
	reg[regist].I = CPUReadMemoryQuick(address);
	clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC);
}

// PC/stack-related ///////////////////////////////////////////////////////

// ADD R0~R7, PC, Imm
static INSN_REGPARM void thumbA0(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	reg[regist].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2);
	clockTicks = 1 + codeTicksAccess16(armNextPC);
}

// ADD R0~R7, SP, Imm
static INSN_REGPARM void thumbA8(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	reg[regist].I = reg[13].I + ((opcode & 255) << 2);
	clockTicks = 1 + codeTicksAccess16(armNextPC);
}

// ADD SP, Imm
static INSN_REGPARM void thumbB0(uint32_t opcode)
{
	int offset = (opcode & 127) << 2;
	if (opcode & 0x80)
		offset = -offset;
	reg[13].I += offset;
	clockTicks = 1 + codeTicksAccess16(armNextPC);
}

// Push and pop ///////////////////////////////////////////////////////////

#define PUSH_REG(val, r) \
	if (opcode & (val)) \
	{ \
		CPUWriteMemory(address, reg[(r)].I); \
		if (!count) \
			clockTicks += 1 + dataTicksAccess32(address); \
		else \
			clockTicks += 1 + dataTicksAccessSeq32(address); \
		++count; \
		address += 4; \
	}

#define POP_REG(val, r) \
	if (opcode & (val)) \
	{ \
		reg[(r)].I = CPUReadMemory(address); \
		if (!count) \
			clockTicks += 1 + dataTicksAccess32(address); \
		else \
			clockTicks += 1 + dataTicksAccessSeq32(address); \
		++count; \
		address += 4; \
	}

// PUSH {Rlist}
static INSN_REGPARM void thumbB4(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	int count = 0;
	uint32_t temp = reg[13].I - 4 * cpuBitsSet[opcode & 0xff];
	uint32_t address = temp & 0xFFFFFFFC;
	PUSH_REG(1, 0);
	PUSH_REG(2, 1);
	PUSH_REG(4, 2);
	PUSH_REG(8, 3);
	PUSH_REG(16, 4);
	PUSH_REG(32, 5);
	PUSH_REG(64, 6);
	PUSH_REG(128, 7);
	clockTicks += 1 + codeTicksAccess16(armNextPC);
	reg[13].I = temp;
}

// PUSH {Rlist, LR}
static INSN_REGPARM void thumbB5(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	int count = 0;
	uint32_t temp = reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff];
	uint32_t address = temp & 0xFFFFFFFC;
	PUSH_REG(1, 0);
	PUSH_REG(2, 1);
	PUSH_REG(4, 2);
	PUSH_REG(8, 3);
	PUSH_REG(16, 4);
	PUSH_REG(32, 5);
	PUSH_REG(64, 6);
	PUSH_REG(128, 7);
	PUSH_REG(256, 14);
	clockTicks += 1 + codeTicksAccess16(armNextPC);
	reg[13].I = temp;
}

// POP {Rlist}
static INSN_REGPARM void thumbBC(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	int count = 0;
	uint32_t address = reg[13].I & 0xFFFFFFFC;
	uint32_t temp = reg[13].I + 4 * cpuBitsSet[opcode & 0xFF];
	POP_REG(1, 0);
	POP_REG(2, 1);
	POP_REG(4, 2);
	POP_REG(8, 3);
	POP_REG(16, 4);
	POP_REG(32, 5);
	POP_REG(64, 6);
	POP_REG(128, 7);
	reg[13].I = temp;
	clockTicks = 2 + codeTicksAccess16(armNextPC);
}

// POP {Rlist, PC}
static INSN_REGPARM void thumbBD(uint32_t opcode)
{
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	int count = 0;
	uint32_t address = reg[13].I & 0xFFFFFFFC;
	uint32_t temp = reg[13].I + 4 + 4 * cpuBitsSet[opcode & 0xFF];
	POP_REG(1, 0);
	POP_REG(2, 1);
	POP_REG(4, 2);
	POP_REG(8, 3);
	POP_REG(16, 4);
	POP_REG(32, 5);
	POP_REG(64, 6);
	POP_REG(128, 7);
	reg[15].I = CPUReadMemory(address) & 0xFFFFFFFE;
	if (!count)
		clockTicks += 1 + dataTicksAccess32(address);
	else
		clockTicks += 1 + dataTicksAccessSeq32(address);
	++count;
	armNextPC = reg[15].I;
	reg[15].I += 2;
	reg[13].I = temp;
	THUMB_PREFETCH();
	busPrefetchCount = 0;
	clockTicks += 3 + codeTicksAccess16(armNextPC) + codeTicksAccess16(armNextPC);
}

// Load/store multiple ////////////////////////////////////////////////////

#define THUMB_STM_REG(val, r, b) \
	if (opcode & (val)) \
	{ \
		CPUWriteMemory(address, reg[(r)].I); \
		reg[(b)].I = temp; \
		if (!count) \
			clockTicks += 1 + dataTicksAccess32(address); \
		else \
			clockTicks += 1 + dataTicksAccessSeq32(address); \
		++count; \
		address += 4; \
	}

#define THUMB_LDM_REG(val, r) \
	if (opcode & (val)) \
	{ \
		reg[(r)].I = CPUReadMemory(address); \
		if (!count) \
			clockTicks += 1 + dataTicksAccess32(address); \
		else \
			clockTicks += 1 + dataTicksAccessSeq32(address); \
		++count; \
		address += 4; \
	}

// STM R0~7!, {Rlist}
static INSN_REGPARM void thumbC0(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[regist].I & 0xFFFFFFFC;
	uint32_t temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xff];
	int count = 0;
	// store
	THUMB_STM_REG(1, 0, regist);
	THUMB_STM_REG(2, 1, regist);
	THUMB_STM_REG(4, 2, regist);
	THUMB_STM_REG(8, 3, regist);
	THUMB_STM_REG(16, 4, regist);
	THUMB_STM_REG(32, 5, regist);
	THUMB_STM_REG(64, 6, regist);
	THUMB_STM_REG(128, 7, regist);
	clockTicks = 1 + codeTicksAccess16(armNextPC);
}

// LDM R0~R7!, {Rlist}
static INSN_REGPARM void thumbC8(uint32_t opcode)
{
	uint8_t regist = (opcode >> 8) & 7;
	if (!busPrefetchCount)
		busPrefetch = busPrefetchEnable;
	uint32_t address = reg[regist].I & 0xFFFFFFFC;
	uint32_t temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xFF];
	int count = 0;
	// load
	THUMB_LDM_REG(1, 0);
	THUMB_LDM_REG(2, 1);
	THUMB_LDM_REG(4, 2);
	THUMB_LDM_REG(8, 3);
	THUMB_LDM_REG(16, 4);
	THUMB_LDM_REG(32, 5);
	THUMB_LDM_REG(64, 6);
	THUMB_LDM_REG(128, 7);
	clockTicks = 2 + codeTicksAccess16(armNextPC);
	if (!(opcode & (1 << regist)))
		reg[regist].I = temp;
}

// Conditional branches ///////////////////////////////////////////////////

// BEQ offset
static INSN_REGPARM void thumbD0(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (Z_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BNE offset
static INSN_REGPARM void thumbD1(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!Z_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BCS offset
static INSN_REGPARM void thumbD2(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (C_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BCC offset
static INSN_REGPARM void thumbD3(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!C_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BMI offset
static INSN_REGPARM void thumbD4(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (N_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BPL offset
static INSN_REGPARM void thumbD5(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!N_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BVS offset
static INSN_REGPARM void thumbD6(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BVC offset
static INSN_REGPARM void thumbD7(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BHI offset
static INSN_REGPARM void thumbD8(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (C_FLAG && !Z_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BLS offset
static INSN_REGPARM void thumbD9(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!C_FLAG || Z_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BGE offset
static INSN_REGPARM void thumbDA(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (N_FLAG == V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BLT offset
static INSN_REGPARM void thumbDB(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (N_FLAG != V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BGT offset
static INSN_REGPARM void thumbDC(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
	if (!Z_FLAG && N_FLAG == V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// BLE offset
static INSN_REGPARM void thumbDD(uint32_t opcode)
{
	clockTicks = codeTicksAccessSeq16(armNextPC);
	if (Z_FLAG || N_FLAG != V_FLAG)
	{
		reg[15].I += static_cast<int8_t>(opcode & 0xFF) << 1;
		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH();
		clockTicks += codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 2;
		busPrefetchCount = 0;
	}
}

// SWI, B, BL /////////////////////////////////////////////////////////////

// SWI #comment
static INSN_REGPARM void thumbDF(uint32_t opcode)
{
	uint32_t address = 0;
	clockTicks = 3;
	busPrefetchCount = 0;
	CPUSoftwareInterrupt(opcode & 0xFF);
}

// B offset
static INSN_REGPARM void thumbE0(uint32_t opcode)
{
	int offset = (opcode & 0x3FF) << 1;
	if (opcode & 0x0400)
		offset |= 0xFFFFF800;
	reg[15].I += offset;
	armNextPC = reg[15].I;
	reg[15].I += 2;
	THUMB_PREFETCH();
	clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	busPrefetchCount = 0;
}

// BLL #offset (forward)
static INSN_REGPARM void thumbF0(uint32_t opcode)
{
	int offset = opcode & 0x7FF;
	reg[14].I = reg[15].I + (offset << 12);
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
}

// BLL #offset (backward)
static INSN_REGPARM void thumbF4(uint32_t opcode)
{
	int offset = opcode & 0x7FF;
	reg[14].I = reg[15].I + ((offset << 12) | 0xFF800000);
	clockTicks = codeTicksAccessSeq16(armNextPC) + 1;
}

// BLH #offset
static INSN_REGPARM void thumbF8(uint32_t opcode)
{
	int offset = opcode & 0x7FF;
	uint32_t temp = reg[15].I - 2;
	reg[15].I = (reg[14].I + (offset << 1)) & 0xFFFFFFFE;
	armNextPC = reg[15].I;
	reg[15].I += 2;
	reg[14].I = temp | 1;
	THUMB_PREFETCH();
	clockTicks = codeTicksAccessSeq16(armNextPC) * 2 + codeTicksAccess16(armNextPC) + 3;
	busPrefetchCount = 0;
}

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

typedef INSN_REGPARM void (*insnfunc_t)(uint32_t opcode);
#define thumbUI thumbUnknownInsn
#define thumbBP thumbUnknownInsn
static insnfunc_t thumbInsnTable[] =
{
	thumb00_00,thumb00_01,thumb00_02,thumb00_03,thumb00_04,thumb00_05,thumb00_06,thumb00_07,  // 00
	thumb00_08,thumb00_09,thumb00_0A,thumb00_0B,thumb00_0C,thumb00_0D,thumb00_0E,thumb00_0F,
	thumb00_10,thumb00_11,thumb00_12,thumb00_13,thumb00_14,thumb00_15,thumb00_16,thumb00_17,
	thumb00_18,thumb00_19,thumb00_1A,thumb00_1B,thumb00_1C,thumb00_1D,thumb00_1E,thumb00_1F,
	thumb08_00,thumb08_01,thumb08_02,thumb08_03,thumb08_04,thumb08_05,thumb08_06,thumb08_07,  // 08
	thumb08_08,thumb08_09,thumb08_0A,thumb08_0B,thumb08_0C,thumb08_0D,thumb08_0E,thumb08_0F,
	thumb08_10,thumb08_11,thumb08_12,thumb08_13,thumb08_14,thumb08_15,thumb08_16,thumb08_17,
	thumb08_18,thumb08_19,thumb08_1A,thumb08_1B,thumb08_1C,thumb08_1D,thumb08_1E,thumb08_1F,
	thumb10_00,thumb10_01,thumb10_02,thumb10_03,thumb10_04,thumb10_05,thumb10_06,thumb10_07,  // 10
	thumb10_08,thumb10_09,thumb10_0A,thumb10_0B,thumb10_0C,thumb10_0D,thumb10_0E,thumb10_0F,
	thumb10_10,thumb10_11,thumb10_12,thumb10_13,thumb10_14,thumb10_15,thumb10_16,thumb10_17,
	thumb10_18,thumb10_19,thumb10_1A,thumb10_1B,thumb10_1C,thumb10_1D,thumb10_1E,thumb10_1F,
	thumb18_0,thumb18_1,thumb18_2,thumb18_3,thumb18_4,thumb18_5,thumb18_6,thumb18_7,          // 18
	thumb1A_0,thumb1A_1,thumb1A_2,thumb1A_3,thumb1A_4,thumb1A_5,thumb1A_6,thumb1A_7,
	thumb1C_0,thumb1C_1,thumb1C_2,thumb1C_3,thumb1C_4,thumb1C_5,thumb1C_6,thumb1C_7,
	thumb1E_0,thumb1E_1,thumb1E_2,thumb1E_3,thumb1E_4,thumb1E_5,thumb1E_6,thumb1E_7,
	thumb20,thumb20,thumb20,thumb20,thumb21,thumb21,thumb21,thumb21,  // 20
	thumb22,thumb22,thumb22,thumb22,thumb23,thumb23,thumb23,thumb23,
	thumb24,thumb24,thumb24,thumb24,thumb25,thumb25,thumb25,thumb25,
	thumb26,thumb26,thumb26,thumb26,thumb27,thumb27,thumb27,thumb27,
	thumb28,thumb28,thumb28,thumb28,thumb29,thumb29,thumb29,thumb29,  // 28
	thumb2A,thumb2A,thumb2A,thumb2A,thumb2B,thumb2B,thumb2B,thumb2B,
	thumb2C,thumb2C,thumb2C,thumb2C,thumb2D,thumb2D,thumb2D,thumb2D,
	thumb2E,thumb2E,thumb2E,thumb2E,thumb2F,thumb2F,thumb2F,thumb2F,
	thumb30,thumb30,thumb30,thumb30,thumb31,thumb31,thumb31,thumb31,  // 30
	thumb32,thumb32,thumb32,thumb32,thumb33,thumb33,thumb33,thumb33,
	thumb34,thumb34,thumb34,thumb34,thumb35,thumb35,thumb35,thumb35,
	thumb36,thumb36,thumb36,thumb36,thumb37,thumb37,thumb37,thumb37,
	thumb38,thumb38,thumb38,thumb38,thumb39,thumb39,thumb39,thumb39,  // 38
	thumb3A,thumb3A,thumb3A,thumb3A,thumb3B,thumb3B,thumb3B,thumb3B,
	thumb3C,thumb3C,thumb3C,thumb3C,thumb3D,thumb3D,thumb3D,thumb3D,
	thumb3E,thumb3E,thumb3E,thumb3E,thumb3F,thumb3F,thumb3F,thumb3F,
	thumb40_0,thumb40_1,thumb40_2,thumb40_3,thumb41_0,thumb41_1,thumb41_2,thumb41_3,  // 40
	thumb42_0,thumb42_1,thumb42_2,thumb42_3,thumb43_0,thumb43_1,thumb43_2,thumb43_3,
	thumbUI,thumb44_1,thumb44_2,thumb44_3,thumbUI,thumb45_1,thumb45_2,thumb45_3,
	thumb46_0,thumb46_1,thumb46_2,thumb46_3,thumb47,thumb47,thumbUI,thumbUI,
	thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,  // 48
	thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,
	thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,
	thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,thumb48,
	thumb50,thumb50,thumb50,thumb50,thumb50,thumb50,thumb50,thumb50,  // 50
	thumb52,thumb52,thumb52,thumb52,thumb52,thumb52,thumb52,thumb52,
	thumb54,thumb54,thumb54,thumb54,thumb54,thumb54,thumb54,thumb54,
	thumb56,thumb56,thumb56,thumb56,thumb56,thumb56,thumb56,thumb56,
	thumb58,thumb58,thumb58,thumb58,thumb58,thumb58,thumb58,thumb58,  // 58
	thumb5A,thumb5A,thumb5A,thumb5A,thumb5A,thumb5A,thumb5A,thumb5A,
	thumb5C,thumb5C,thumb5C,thumb5C,thumb5C,thumb5C,thumb5C,thumb5C,
	thumb5E,thumb5E,thumb5E,thumb5E,thumb5E,thumb5E,thumb5E,thumb5E,
	thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,  // 60
	thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,
	thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,
	thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,thumb60,
	thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,  // 68
	thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,
	thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,
	thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,thumb68,
	thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,  // 70
	thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,
	thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,
	thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,thumb70,
	thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,  // 78
	thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,
	thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,
	thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,thumb78,
	thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,  // 80
	thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,
	thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,
	thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,thumb80,
	thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,  // 88
	thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,
	thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,
	thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,thumb88,
	thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,  // 90
	thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,
	thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,
	thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,thumb90,
	thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,  // 98
	thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,
	thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,
	thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,thumb98,
	thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,  // A0
	thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,
	thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,
	thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,thumbA0,
	thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,  // A8
	thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,
	thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,
	thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,thumbA8,
	thumbB0,thumbB0,thumbB0,thumbB0,thumbUI,thumbUI,thumbUI,thumbUI,  // B0
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbB4,thumbB4,thumbB4,thumbB4,thumbB5,thumbB5,thumbB5,thumbB5,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,  // B8
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbBC,thumbBC,thumbBC,thumbBC,thumbBD,thumbBD,thumbBD,thumbBD,
	thumbBP,thumbBP,thumbBP,thumbBP,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,  // C0
	thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,
	thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,
	thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,thumbC0,
	thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,  // C8
	thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,
	thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,
	thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,thumbC8,
	thumbD0,thumbD0,thumbD0,thumbD0,thumbD1,thumbD1,thumbD1,thumbD1,  // D0
	thumbD2,thumbD2,thumbD2,thumbD2,thumbD3,thumbD3,thumbD3,thumbD3,
	thumbD4,thumbD4,thumbD4,thumbD4,thumbD5,thumbD5,thumbD5,thumbD5,
	thumbD6,thumbD6,thumbD6,thumbD6,thumbD7,thumbD7,thumbD7,thumbD7,
	thumbD8,thumbD8,thumbD8,thumbD8,thumbD9,thumbD9,thumbD9,thumbD9,  // D8
	thumbDA,thumbDA,thumbDA,thumbDA,thumbDB,thumbDB,thumbDB,thumbDB,
	thumbDC,thumbDC,thumbDC,thumbDC,thumbDD,thumbDD,thumbDD,thumbDD,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbDF,thumbDF,thumbDF,thumbDF,
	thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,  // E0
	thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,
	thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,
	thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,thumbE0,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,  // E8
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,thumbUI,
	thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,  // F0
	thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,thumbF0,
	thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,
	thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,thumbF4,
	thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,  // F8
	thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,
	thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,
	thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,thumbF8,
};

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

int thumbExecute()
{
	do
	{
		uint32_t opcode = cpuPrefetch[0];
		cpuPrefetch[0] = cpuPrefetch[1];

		busPrefetch = false;
		if (busPrefetchCount & 0xFFFFFF00)
			busPrefetchCount = 0x100 | (busPrefetchCount & 0xFF);
		clockTicks = 0;
		uint32_t oldArmNextPC = armNextPC;

		armNextPC = reg[15].I;
		reg[15].I += 2;
		THUMB_PREFETCH_NEXT();

		(*thumbInsnTable[opcode >> 6])(opcode);

		if (clockTicks < 0)
			return 0;
		if (!clockTicks)
			clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1;
		cpuTotalTicks += clockTicks;
	} while (cpuTotalTicks < cpuNextEvent && !armState && !holdState && !SWITicks);
	return 1;
}