/***************************************************************************
 *   Copyright (C) 2006 by Dominic Rath                                    *
 *   Dominic.Rath@gmx.de                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef ARM_DISASSEMBLER_H
#define ARM_DISASSEMBLER_H

#include "types.h"

enum arm_instruction_type
{
	ARM_UNKNOWN_INSTUCTION,
	
	/* Branch instructions */
	ARM_B,
	ARM_BL,
	ARM_BX,
	ARM_BLX,
	
	/* Data processing instructions */
	ARM_AND,
	ARM_EOR,
	ARM_SUB,
	ARM_RSB,
	ARM_ADD,
	ARM_ADC,
	ARM_SBC,
	ARM_RSC,
	ARM_TST,
	ARM_TEQ,
	ARM_CMP,
	ARM_CMN,
	ARM_ORR,
	ARM_MOV,
	ARM_BIC,
	ARM_MVN,
	
	/* Load/store instructions */
	ARM_LDR,
	ARM_LDRB,
	ARM_LDRT,
	ARM_LDRBT,
	
	ARM_LDRH,
	ARM_LDRSB,
	ARM_LDRSH,
	
	ARM_LDM,

	ARM_STR,
	ARM_STRB,
	ARM_STRT,
	ARM_STRBT,
	
	ARM_STRH,
	
	ARM_STM,
	
	/* Status register access instructions */
	ARM_MRS,
	ARM_MSR,
	
	/* Multiply instructions */
	ARM_MUL,
	ARM_MLA,
	ARM_SMULL,
	ARM_SMLAL,
	ARM_UMULL,
	ARM_UMLAL,
	
	/* Miscellaneous instructions */
	ARM_CLZ,
	
	/* Exception generating instructions */
	ARM_BKPT,
	ARM_SWI,
	
	/* Coprocessor instructions */
	ARM_CDP,
	ARM_LDC,
	ARM_STC,
	ARM_MCR,
	ARM_MRC,
	
	/* Semaphore instructions */
	ARM_SWP,
	ARM_SWPB,
	
	/* Enhanced DSP extensions */
	ARM_MCRR,
	ARM_MRRC,
	ARM_PLD,
	ARM_QADD,
	ARM_QDADD,
	ARM_QSUB,
	ARM_QDSUB,
	ARM_SMLAxy,
	ARM_SMLALxy,
	ARM_SMLAWy,
	ARM_SMULxy,
	ARM_SMULWy,
	ARM_LDRD,
	ARM_STRD,

	ARM_UNDEFINED_INSTRUCTION = 0xffffffff,
};

typedef struct arm_b_bl_bx_blx_instr_s
{
	int reg_operand;
	u32 target_address;
} arm_b_bl_bx_blx_instr_t;

union arm_shifter_operand
{
	struct {
		u32 immediate;
	} immediate;
	struct {
		u8 Rm;
		u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
		u8 shift_imm;
	} immediate_shift;
	struct {
		u8 Rm;
		u8 shift;
		u8 Rs;
	} register_shift;
};

typedef struct arm_data_proc_instr_s
{
	int variant; /* 0: immediate, 1: immediate_shift, 2: register_shift */
	u8 S;
	u8 Rn;
	u8 Rd;
	union arm_shifter_operand shifter_operand;
} arm_data_proc_instr_t;

typedef struct arm_load_store_instr_s
{
	u8 Rd;
	u8 Rn;
	u8 U;
	int index_mode; /* 0: offset, 1: pre-indexed, 2: post-indexed */
	int offset_mode; /* 0: immediate, 1: (scaled) register */
	union
	{
		u32 offset;
		struct {
			u8 Rm;
			u8 shift; /* 0: LSL, 1: LSR, 2: ASR, 3: ROR, 4: RRX */
			u8 shift_imm;
		} reg;
	} offset;
} arm_load_store_instr_t;

typedef struct arm_load_store_multiple_instr_s
{
	u8 Rn;
	u32 register_list;
	u8 addressing_mode; /* 0: IA, 1: IB, 2: DA, 3: DB */
	u8 S;
	u8 W;
} arm_load_store_multiple_instr_t;

typedef struct arm_instruction_s
{
	enum arm_instruction_type type;
	char text[128];
	u32 opcode;
	
	union {
		arm_b_bl_bx_blx_instr_t b_bl_bx_blx;
		arm_data_proc_instr_t data_proc;
		arm_load_store_instr_t load_store;
		arm_load_store_multiple_instr_t load_store_multiple;
	} info;

} arm_instruction_t;

extern int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction);
extern int thumb_evaluate_opcode(u16 opcode, u32 address, arm_instruction_t *instruction);
extern int arm_access_size(arm_instruction_t *instruction);

#define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000)>>28])

#endif /* ARM_DISASSEMBLER_H */