/* Copyright (c) 2013-2014 Jeffrey Pfau
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/arm/decoder.h>

#include <mgba/internal/arm/decoder-inlines.h>
#include <mgba/internal/arm/emitter-arm.h>
#include <mgba/internal/arm/isa-inlines.h>

#define ADDR_MODE_1_SHIFT(OP) \
	info->op3.reg = opcode & 0x0000000F; \
	info->op3.shifterOp = ARM_SHIFT_ ## OP; \
	info->operandFormat |= ARM_OPERAND_REGISTER_3; \
	if (opcode & 0x00000010) { \
		info->op3.shifterReg = (opcode >> 8) & 0xF; \
		++info->iCycles; \
		info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
	} else { \
		info->op3.shifterImm = (opcode >> 7) & 0x1F; \
		info->operandFormat |= ARM_OPERAND_SHIFT_IMMEDIATE_3; \
	}

#define ADDR_MODE_1_LSL \
	ADDR_MODE_1_SHIFT(LSL) \
	if (!info->op3.shifterImm) { \
		info->operandFormat &= ~ARM_OPERAND_SHIFT_IMMEDIATE_3; \
		info->op3.shifterOp = ARM_SHIFT_NONE; \
	}

#define ADDR_MODE_1_LSR ADDR_MODE_1_SHIFT(LSR)
#define ADDR_MODE_1_ASR ADDR_MODE_1_SHIFT(ASR)
#define ADDR_MODE_1_ROR \
	ADDR_MODE_1_SHIFT(ROR) \
	if (!info->op3.shifterImm) { \
		info->op3.shifterOp = ARM_SHIFT_RRX; \
	}

#define ADDR_MODE_1_IMM \
	int rotate = (opcode & 0x00000F00) >> 7; \
	int immediate = opcode & 0x000000FF; \
	info->op3.immediate = ROR(immediate, rotate); \
	info->operandFormat |= ARM_OPERAND_IMMEDIATE_3;

#define ADDR_MODE_2_SHIFT(OP) \
	info->memory.format |= ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_SHIFTED_OFFSET; \
	info->memory.offset.shifterOp = ARM_SHIFT_ ## OP; \
	info->memory.offset.shifterImm = (opcode >> 7) & 0x1F; \
	info->memory.offset.reg = opcode & 0x0000000F;

#define ADDR_MODE_2_LSL \
	ADDR_MODE_2_SHIFT(LSL) \
	if (!info->memory.offset.shifterImm) { \
		info->memory.format &= ~ARM_MEMORY_SHIFTED_OFFSET; \
		info->memory.offset.shifterOp = ARM_SHIFT_NONE; \
	}

#define ADDR_MODE_2_LSR ADDR_MODE_2_SHIFT(LSR) \
	if (!info->memory.offset.shifterImm) { \
		info->memory.offset.shifterImm = 32; \
	}

#define ADDR_MODE_2_ASR ADDR_MODE_2_SHIFT(ASR) \
	if (!info->memory.offset.shifterImm) { \
		info->memory.offset.shifterImm = 32; \
	}

#define ADDR_MODE_2_ROR \
	ADDR_MODE_2_SHIFT(ROR) \
	if (!info->memory.offset.shifterImm) { \
		info->memory.offset.shifterOp = ARM_SHIFT_RRX; \
	}

#define ADDR_MODE_2_IMM \
	info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \
	info->memory.offset.immediate = opcode & 0x00000FFF;

#define ADDR_MODE_3_REG \
	info->memory.format |= ARM_MEMORY_REGISTER_OFFSET; \
	info->memory.offset.reg = opcode & 0x0000000F;

#define ADDR_MODE_3_IMM \
	info->memory.format |= ARM_MEMORY_IMMEDIATE_OFFSET; \
	info->memory.offset.immediate = (opcode & 0x0000000F) | ((opcode & 0x00000F00) >> 4);

#define DEFINE_DECODER_ARM(NAME, MNEMONIC, BODY) \
	static void _ARMDecode ## NAME (uint32_t opcode, struct ARMInstructionInfo* info) { \
		UNUSED(opcode); \
		info->mnemonic = ARM_MN_ ## MNEMONIC; \
		BODY; \
	}

#define DEFINE_ALU_DECODER_EX_ARM(NAME, MNEMONIC, S, SHIFTER, OTHER_AFFECTED, SKIPPED) \
	DEFINE_DECODER_ARM(NAME, MNEMONIC, \
		info->op1.reg = (opcode >> 12) & 0xF; \
		info->op2.reg = (opcode >> 16) & 0xF; \
		info->operandFormat = ARM_OPERAND_REGISTER_1 | \
			OTHER_AFFECTED | \
			ARM_OPERAND_REGISTER_2; \
		info->affectsCPSR = S; \
		SHIFTER; \
		if (SKIPPED == 1) { \
			info->op1 = info->op2; \
			info->op2 = info->op3; \
			info->operandFormat >>= 8; \
		} else if (SKIPPED == 2) { \
			info->op2 = info->op3; \
			info->operandFormat |= info->operandFormat >> 8; \
			info->operandFormat &= ~ARM_OPERAND_3; \
		} \
		if (info->op1.reg == ARM_PC) { \
			info->branchType = ARM_BRANCH_INDIRECT; \
		})

#define DEFINE_ALU_DECODER_ARM(NAME, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 0, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 0, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## S_LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 0, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 0, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## S_ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 0, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## SI, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_AFFECTED_1, SKIPPED)

#define DEFINE_ALU_DECODER_S_ONLY_ARM(NAME) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSL, NAME, 1, ADDR_MODE_1_LSL, ARM_OPERAND_NONE, 1) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _LSR, NAME, 1, ADDR_MODE_1_LSR, ARM_OPERAND_NONE, 1) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _ASR, NAME, 1, ADDR_MODE_1_ASR, ARM_OPERAND_NONE, 1) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## _ROR, NAME, 1, ADDR_MODE_1_ROR, ARM_OPERAND_NONE, 1) \
	DEFINE_ALU_DECODER_EX_ARM(NAME ## I, NAME, 1, ADDR_MODE_1_IMM, ARM_OPERAND_NONE, 1)

#define DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S, OTHER_AFFECTED) \
	DEFINE_DECODER_ARM(NAME, MNEMONIC, \
		info->op1.reg = (opcode >> 16) & 0xF; \
		info->op2.reg = opcode & 0xF; \
		info->op3.reg = (opcode >> 8) & 0xF; \
		info->op4.reg = (opcode >> 12) & 0xF; \
		info->operandFormat = ARM_OPERAND_REGISTER_1 | \
			ARM_OPERAND_AFFECTED_1 | \
			ARM_OPERAND_REGISTER_2 | \
			ARM_OPERAND_REGISTER_3 | \
			OTHER_AFFECTED; \
		info->affectsCPSR = S; \
		if (info->op1.reg == ARM_PC) { \
			info->branchType = ARM_BRANCH_INDIRECT; \
		})

#define DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, MNEMONIC, S) \
	DEFINE_DECODER_ARM(NAME, MNEMONIC, \
		info->op1.reg = (opcode >> 12) & 0xF; \
		info->op2.reg = (opcode >> 16) & 0xF; \
		info->op3.reg = opcode & 0xF; \
		info->op4.reg = (opcode >> 8) & 0xF; \
		info->operandFormat = ARM_OPERAND_REGISTER_1 | \
			ARM_OPERAND_AFFECTED_1 | \
			ARM_OPERAND_REGISTER_2 | \
			ARM_OPERAND_AFFECTED_2 | \
			ARM_OPERAND_REGISTER_3 | \
			ARM_OPERAND_REGISTER_4; \
		info->affectsCPSR = S; \
		if (info->op1.reg == ARM_PC) { \
			info->branchType = ARM_BRANCH_INDIRECT; \
		})

#define DEFINE_MULTIPLY_DECODER_ARM(NAME, OTHER_AFFECTED) \
	DEFINE_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0, OTHER_AFFECTED) \
	DEFINE_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1, OTHER_AFFECTED)

#define DEFINE_LONG_MULTIPLY_DECODER_ARM(NAME) \
	DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME, NAME, 0) \
	DEFINE_LONG_MULTIPLY_DECODER_EX_ARM(NAME ## S, NAME, 1)

#define DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, ADDRESSING_MODE, ADDRESSING_DECODING, CYCLES, TYPE) \
	DEFINE_DECODER_ARM(NAME, MNEMONIC, \
		info->op1.reg = (opcode >> 12) & 0xF; \
		info->memory.baseReg = (opcode >> 16) & 0xF; \
		info->memory.width = TYPE; \
		info->operandFormat = ARM_OPERAND_REGISTER_1 | \
			ARM_OPERAND_AFFECTED_1 | /* TODO: Remove this for STR */ \
			ARM_OPERAND_MEMORY_2; \
		info->memory.format = ARM_MEMORY_REGISTER_BASE | ADDRESSING_MODE; \
		ADDRESSING_DECODING; \
		CYCLES;)

#define DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
		ARM_MEMORY_POST_INCREMENT | \
		ARM_MEMORY_WRITEBACK | \
		ARM_MEMORY_OFFSET_SUBTRACT, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
		ARM_MEMORY_POST_INCREMENT | \
		ARM_MEMORY_WRITEBACK, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \
		ARM_MEMORY_OFFSET_SUBTRACT, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \
		ARM_MEMORY_PRE_INCREMENT | \
		ARM_MEMORY_WRITEBACK | \
		ARM_MEMORY_OFFSET_SUBTRACT, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \
		0, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \
		ARM_MEMORY_WRITEBACK, \
		ADDRESSING_MODE, CYCLES, TYPE)

#define DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE)

#define DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME, MNEMONIC, ADDR_MODE_3_REG, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_3_IMM, CYCLES, TYPE)

#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
		ARM_MEMORY_POST_INCREMENT | \
		ARM_MEMORY_WRITEBACK | \
		ARM_MEMORY_OFFSET_SUBTRACT, \
		ADDRESSING_MODE, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
		ARM_MEMORY_POST_INCREMENT | \
		ARM_MEMORY_WRITEBACK, \
		ADDRESSING_MODE, CYCLES, TYPE)

#define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSL_, MNEMONIC, ADDR_MODE_2_LSL, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _LSR_, MNEMONIC, ADDR_MODE_2_LSR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ASR_, MNEMONIC, ADDR_MODE_2_ASR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## _ROR_, MNEMONIC, ADDR_MODE_2_ROR, CYCLES, TYPE) \
	DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME ## I, MNEMONIC, ADDR_MODE_2_IMM, CYCLES, TYPE)

#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME, MNEMONIC, DIRECTION, WRITEBACK) \
	DEFINE_DECODER_ARM(NAME, MNEMONIC, \
		info->memory.baseReg = (opcode >> 16) & 0xF; \
		info->op1.immediate = opcode & 0x0000FFFF; \
		if (info->op1.immediate & (1 << ARM_PC)) { \
			info->branchType = ARM_BRANCH_INDIRECT; \
		} \
		info->operandFormat = ARM_OPERAND_MEMORY_1; \
		info->memory.format = ARM_MEMORY_REGISTER_BASE | \
			WRITEBACK | \
			ARM_MEMORY_ ## DIRECTION;)


#define DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(NAME) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DA,   NAME, DECREMENT_AFTER, 0) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DAW,  NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DB,   NAME, DECREMENT_BEFORE, 0) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## DBW,  NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IA,   NAME, INCREMENT_AFTER, 0) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IAW,  NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IB,   NAME, INCREMENT_BEFORE, 0) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## IBW,  NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDA,  NAME, DECREMENT_AFTER, ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDAW, NAME, DECREMENT_AFTER, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDB,  NAME, DECREMENT_BEFORE, ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SDBW, NAME, DECREMENT_BEFORE, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIA,  NAME, INCREMENT_AFTER, ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIAW, NAME, INCREMENT_AFTER, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIB,  NAME, INCREMENT_BEFORE, ARM_MEMORY_SPSR_SWAP) \
	DEFINE_LOAD_STORE_MULTIPLE_DECODER_EX_ARM(NAME ## SIBW, NAME, INCREMENT_BEFORE, ARM_MEMORY_WRITEBACK | ARM_MEMORY_SPSR_SWAP)

#define DEFINE_SWP_DECODER_ARM(NAME, TYPE) \
	DEFINE_DECODER_ARM(NAME, SWP, \
		info->memory.baseReg = (opcode >> 16) & 0xF; \
		info->op1.reg = (opcode >> 12) & 0xF; \
		info->op2.reg = opcode & 0xF; \
		info->operandFormat = ARM_OPERAND_REGISTER_1 | \
			ARM_OPERAND_AFFECTED_1 | \
			ARM_OPERAND_REGISTER_2 | \
			ARM_OPERAND_MEMORY_3; \
		info->memory.format = ARM_MEMORY_REGISTER_BASE; \
		info->memory.width = TYPE;)

DEFINE_ALU_DECODER_ARM(ADD, 0)
DEFINE_ALU_DECODER_ARM(ADC, 0)
DEFINE_ALU_DECODER_ARM(AND, 0)
DEFINE_ALU_DECODER_ARM(BIC, 0)
DEFINE_ALU_DECODER_S_ONLY_ARM(CMN)
DEFINE_ALU_DECODER_S_ONLY_ARM(CMP)
DEFINE_ALU_DECODER_ARM(EOR, 0)
DEFINE_ALU_DECODER_ARM(MOV, 2)
DEFINE_ALU_DECODER_ARM(MVN, 2)
DEFINE_ALU_DECODER_ARM(ORR, 0)
DEFINE_ALU_DECODER_ARM(RSB, 0)
DEFINE_ALU_DECODER_ARM(RSC, 0)
DEFINE_ALU_DECODER_ARM(SBC, 0)
DEFINE_ALU_DECODER_ARM(SUB, 0)
DEFINE_ALU_DECODER_S_ONLY_ARM(TEQ)
DEFINE_ALU_DECODER_S_ONLY_ARM(TST)

// TOOD: Estimate cycles
DEFINE_MULTIPLY_DECODER_ARM(MLA, ARM_OPERAND_REGISTER_4)
DEFINE_MULTIPLY_DECODER_ARM(MUL, ARM_OPERAND_NONE)

DEFINE_LONG_MULTIPLY_DECODER_ARM(SMLAL)
DEFINE_LONG_MULTIPLY_DECODER_ARM(SMULL)
DEFINE_LONG_MULTIPLY_DECODER_ARM(UMLAL)
DEFINE_LONG_MULTIPLY_DECODER_ARM(UMULL)

// Begin load/store definitions

DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDR, LDR, LOAD_CYCLES, ARM_ACCESS_WORD)
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(LDRB, LDR, LOAD_CYCLES, ARM_ACCESS_BYTE)
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRH, LDR, LOAD_CYCLES, ARM_ACCESS_HALFWORD)
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSB, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_BYTE)
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(LDRSH, LDR, LOAD_CYCLES, ARM_ACCESS_SIGNED_HALFWORD)
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STR, STR, STORE_CYCLES, ARM_ACCESS_WORD)
DEFINE_LOAD_STORE_MODE_2_DECODER_ARM(STRB, STR, STORE_CYCLES, ARM_ACCESS_BYTE)
DEFINE_LOAD_STORE_MODE_3_DECODER_ARM(STRH, STR, STORE_CYCLES, ARM_ACCESS_HALFWORD)

DEFINE_LOAD_STORE_T_DECODER_ARM(LDRBT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_BYTE)
DEFINE_LOAD_STORE_T_DECODER_ARM(LDRT, LDR, LOAD_CYCLES, ARM_ACCESS_TRANSLATED_WORD)
DEFINE_LOAD_STORE_T_DECODER_ARM(STRBT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_BYTE)
DEFINE_LOAD_STORE_T_DECODER_ARM(STRT, STR, STORE_CYCLES, ARM_ACCESS_TRANSLATED_WORD)

DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(LDM)
DEFINE_LOAD_STORE_MULTIPLE_DECODER_ARM(STM)

DEFINE_SWP_DECODER_ARM(SWP, ARM_ACCESS_WORD)
DEFINE_SWP_DECODER_ARM(SWPB, ARM_ACCESS_BYTE)

// End load/store definitions

// Begin branch definitions

DEFINE_DECODER_ARM(B, B,
	int32_t offset = opcode << 8;
	info->op1.immediate = offset >> 6;
	info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
	info->branchType = ARM_BRANCH;)

DEFINE_DECODER_ARM(BL, BL,
	int32_t offset = opcode << 8;
	info->op1.immediate = offset >> 6;
	info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
	info->branchType = ARM_BRANCH_LINKED;)

DEFINE_DECODER_ARM(BX, BX,
	info->op1.reg = opcode & 0x0000000F;
	info->operandFormat = ARM_OPERAND_REGISTER_1;
	info->branchType = ARM_BRANCH_INDIRECT;)

// End branch definitions

// Begin coprocessor definitions

DEFINE_DECODER_ARM(CDP, ILL, info->operandFormat = ARM_OPERAND_NONE;)
DEFINE_DECODER_ARM(LDC, ILL, info->operandFormat = ARM_OPERAND_NONE;)
DEFINE_DECODER_ARM(STC, ILL, info->operandFormat = ARM_OPERAND_NONE;)
DEFINE_DECODER_ARM(MCR, ILL, info->operandFormat = ARM_OPERAND_NONE;)
DEFINE_DECODER_ARM(MRC, ILL, info->operandFormat = ARM_OPERAND_NONE;)

// Begin miscellaneous definitions

DEFINE_DECODER_ARM(BKPT, BKPT,
	info->operandFormat = ARM_OPERAND_NONE;
	info->traps = 1;) // Not strictly in ARMv4T, but here for convenience
DEFINE_DECODER_ARM(ILL, ILL,
	info->operandFormat = ARM_OPERAND_NONE;
	info->traps = 1;) // Illegal opcode

DEFINE_DECODER_ARM(MSR, MSR,
	info->affectsCPSR = 1;
	info->op1.reg = ARM_CPSR;
	info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
	info->op2.reg = opcode & 0x0000000F;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_REGISTER_2;)

DEFINE_DECODER_ARM(MSRR, MSR,
	info->op1.reg = ARM_SPSR;
	info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
	info->op2.reg = opcode & 0x0000000F;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_REGISTER_2;)

DEFINE_DECODER_ARM(MRS, MRS,
	info->affectsCPSR = 1;
	info->op1.reg = (opcode >> 12) & 0xF;
	info->op2.reg = ARM_CPSR;
	info->op2.psrBits = 0;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_REGISTER_2;)

DEFINE_DECODER_ARM(MRSR, MRS,
	info->op1.reg = (opcode >> 12) & 0xF;
	info->op2.reg = ARM_SPSR;
	info->op2.psrBits = 0;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_REGISTER_2;)

DEFINE_DECODER_ARM(MSRI, MSR,
	int rotate = (opcode & 0x00000F00) >> 7;
	int32_t operand = ROR(opcode & 0x000000FF, rotate);
	info->affectsCPSR = 1;
	info->op1.reg = ARM_CPSR;
	info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
	info->op2.immediate = operand;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_IMMEDIATE_2;)

DEFINE_DECODER_ARM(MSRRI, MSR,
	int rotate = (opcode & 0x00000F00) >> 7;
	int32_t operand = ROR(opcode & 0x000000FF, rotate);
	info->op1.reg = ARM_SPSR;
	info->op1.psrBits = (opcode >> 16) & ARM_PSR_MASK;
	info->op2.immediate = operand;
	info->operandFormat = ARM_OPERAND_REGISTER_1 |
		ARM_OPERAND_AFFECTED_1 |
		ARM_OPERAND_IMMEDIATE_2;)

DEFINE_DECODER_ARM(SWI, SWI,
	info->op1.immediate = opcode & 0xFFFFFF;
	info->operandFormat = ARM_OPERAND_IMMEDIATE_1;
	info->traps = 1;)

typedef void (*ARMDecoder)(uint32_t opcode, struct ARMInstructionInfo* info);

static const ARMDecoder _armDecoderTable[0x1000] = {
	DECLARE_ARM_EMITTER_BLOCK(_ARMDecode)
};

void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info) {
	memset(info, 0, sizeof(*info));
	info->execMode = MODE_ARM;
	info->opcode = opcode;
	info->branchType = ARM_BRANCH_NONE;
	info->condition = opcode >> 28;
	info->sInstructionCycles = 1;
	ARMDecoder decoder = _armDecoderTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
	decoder(opcode, info);
}
