/* MSPDebug - debugging tool for the eZ430
 * Copyright (C) 2009, 2010 Daniel Beer
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef DIS_H_

#include <stdint.h>
#include "util.h"

/* Addressing modes.
 *
 * Addressing modes are not determined solely by the address mode bits
 * in an instruction. Rather, those bits specify one of four possible
 * modes (REGISTER, INDEXED, INDIRECT and INDIRECT_INC). Using some of
 * these modes in conjunction with special registers like PC or the
 * constant generator registers results in extra modes. For example, the
 * following code, written using INDIRECT_INC on PC:
 *
 *     MOV      @PC+, R5
 *     .word    0x5729
 *
 * can also be written as an instruction using IMMEDIATE addressing:
 *
 *     MOV      #0x5729, R5
 */
typedef enum {
	MSP430_AMODE_REGISTER           = 0x0,
	MSP430_AMODE_INDEXED            = 0x1,
	MSP430_AMODE_SYMBOLIC           = 0x81,
	MSP430_AMODE_ABSOLUTE           = 0x82,
	MSP430_AMODE_INDIRECT           = 0x2,
	MSP430_AMODE_INDIRECT_INC       = 0x3,
	MSP430_AMODE_IMMEDIATE          = 0x83
} msp430_amode_t;

/* MSP430 registers.
 *
 * These are divided into:
 *
 *     PC/R0:    program counter
 *     SP/R1:    stack pointer
 *     SR/R2:    status register/constant generator 1
 *     R3:       constant generator 2
 *     R4-R15:   general purpose registers
 */
typedef enum {
	MSP430_REG_PC           = 0,
	MSP430_REG_SP           = 1,
	MSP430_REG_SR           = 2,
	MSP430_REG_R3           = 3,
	MSP430_REG_R4           = 4,
	MSP430_REG_R5           = 5,
	MSP430_REG_R6           = 6,
	MSP430_REG_R7           = 7,
	MSP430_REG_R8           = 8,
	MSP430_REG_R9           = 9,
	MSP430_REG_R10          = 10,
	MSP430_REG_R11          = 11,
	MSP430_REG_R12          = 12,
	MSP430_REG_R13          = 13,
	MSP430_REG_R14          = 14,
	MSP430_REG_R15          = 15,
} msp430_reg_t;

/* Status register bits. */
#define MSP430_SR_V             0x0100
#define MSP430_SR_SCG1          0x0080
#define MSP430_SR_SCG0          0x0040
#define MSP430_SR_OSCOFF        0x0020
#define MSP430_SR_CPUOFF        0x0010
#define MSP430_SR_GIE           0x0008
#define MSP430_SR_N             0x0004
#define MSP430_SR_Z             0x0002
#define MSP430_SR_C             0x0001

/* MSP430 instruction formats.
 *
 * NOARG is not an actual instruction format recognised by the CPU.
 * It is used only for emulated instructions.
 */
typedef enum {
	MSP430_ITYPE_NOARG,
	MSP430_ITYPE_JUMP,
	MSP430_ITYPE_DOUBLE,
	MSP430_ITYPE_SINGLE
} msp430_itype_t;

/* MSP430(X) data sizes.
 *
 * An address-word is a 20-bit value. When stored in memory, they are
 * stored as two 16-bit words in the following order:
 *
 *    data[15:0], {12'b0, data[19:16]}
 */
typedef enum {
	MSP430_DSIZE_WORD    = 0,
	MSP430_DSIZE_BYTE    = 1,
	MSP430_DSIZE_UNKNOWN = 2,
	MSP430_DSIZE_AWORD   = 3,
} msp430_dsize_t;

/* MSP430 operations.
 *
 * Some of these are emulated instructions. Emulated instructions are
 * alternate mnemonics for combinations of some real opcodes with
 * common operand values. For example, the following real instruction:
 *
 *    MOV   #0, R8
 *
 * can be written as the following emulated instruction:
 *
 *    CLR   R8
 */
typedef enum {
	/* Single operand */
	MSP430_OP_RRC           = 0x1000,
	MSP430_OP_SWPB          = 0x1080,
	MSP430_OP_RRA           = 0x1100,
	MSP430_OP_SXT           = 0x1180,
	MSP430_OP_PUSH          = 0x1200,
	MSP430_OP_CALL          = 0x1280,
	MSP430_OP_RETI          = 0x1300,

	/* Jump */
	MSP430_OP_JNZ           = 0x2000,
	MSP430_OP_JZ            = 0x2400,
	MSP430_OP_JNC           = 0x2800,
	MSP430_OP_JC            = 0x2C00,
	MSP430_OP_JN            = 0x3000,
	MSP430_OP_JGE           = 0x3400,
	MSP430_OP_JL            = 0x3800,
	MSP430_OP_JMP           = 0x3C00,

	/* Double operand */
	MSP430_OP_MOV           = 0x4000,
	MSP430_OP_ADD           = 0x5000,
	MSP430_OP_ADDC          = 0x6000,
	MSP430_OP_SUBC          = 0x7000,
	MSP430_OP_SUB           = 0x8000,
	MSP430_OP_CMP           = 0x9000,
	MSP430_OP_DADD          = 0xA000,
	MSP430_OP_BIT           = 0xB000,
	MSP430_OP_BIC           = 0xC000,
	MSP430_OP_BIS           = 0xD000,
	MSP430_OP_XOR           = 0xE000,
	MSP430_OP_AND           = 0xF000,

	/* Emulated instructions */
	MSP430_OP_ADC           = 0x10000,
	MSP430_OP_BR            = 0x10001,
	MSP430_OP_CLR           = 0x10002,
	MSP430_OP_CLRC          = 0x10003,
	MSP430_OP_CLRN          = 0x10004,
	MSP430_OP_CLRZ          = 0x10005,
	MSP430_OP_DADC          = 0x10006,
	MSP430_OP_DEC           = 0x10007,
	MSP430_OP_DECD          = 0x10008,
	MSP430_OP_DINT          = 0x10009,
	MSP430_OP_EINT          = 0x1000A,
	MSP430_OP_INC           = 0x1000B,
	MSP430_OP_INCD          = 0x1000C,
	MSP430_OP_INV           = 0x1000D,
	MSP430_OP_NOP           = 0x1000E,
	MSP430_OP_POP           = 0x1000F,
	MSP430_OP_RET           = 0x10010,
	MSP430_OP_RLA           = 0x10011,
	MSP430_OP_RLC           = 0x10012,
	MSP430_OP_SBC           = 0x10013,
	MSP430_OP_SETC          = 0x10014,
	MSP430_OP_SETN          = 0x10015,
	MSP430_OP_SETZ          = 0x10016,
	MSP430_OP_TST           = 0x10017,

	/* MSP430X single operand (extension word) */
	MSP430_OP_RRCX          = 0x21000,
	MSP430_OP_RRUX          = 0x21001, /* note: ZC = 1 */
	MSP430_OP_SWPBX         = 0x21080,
	MSP430_OP_RRAX          = 0x21100,
	MSP430_OP_SXTX          = 0x21180,
	MSP430_OP_PUSHX         = 0x21200,

	/* MSP430X double operand (extension word) */
	MSP430_OP_MOVX          = 0x24000,
	MSP430_OP_ADDX          = 0x25000,
	MSP430_OP_ADDCX         = 0x26000,
	MSP430_OP_SUBCX         = 0x27000,
	MSP430_OP_SUBX          = 0x28000,
	MSP430_OP_CMPX          = 0x29000,
	MSP430_OP_DADDX         = 0x2A000,
	MSP430_OP_BITX          = 0x2B000,
	MSP430_OP_BICX          = 0x2C000,
	MSP430_OP_BISX          = 0x2D000,
	MSP430_OP_XORX          = 0x2E000,
	MSP430_OP_ANDX          = 0x2F000,

	/* MSP430X group 13xx */
	MSP430_OP_CALLA         = 0x21300,

	/* MSP430X group 14xx */
	MSP430_OP_PUSHM         = 0x1400,
	MSP430_OP_POPM		= 0x1600,

	/* MSP430X address instructions */
	MSP430_OP_MOVA          = 0x0000,
	MSP430_OP_CMPA          = 0x0090,
	MSP430_OP_ADDA          = 0x00A0,
	MSP430_OP_SUBA          = 0x00B0,

	/* MSP430X group 00xx, non-address */
	MSP430_OP_RRCM		= 0x0040,
	MSP430_OP_RRAM		= 0x0140,
	MSP430_OP_RLAM		= 0x0240,
	MSP430_OP_RRUM		= 0x0340,

	/* MSP430X emulated instructions */
	MSP430_OP_ADCX		= 0x40000,
	MSP430_OP_BRA		= 0x40001,
	MSP430_OP_RETA		= 0x40002,
	MSP430_OP_CLRX		= 0x40003,
	MSP430_OP_DADCX         = 0x40004,
	MSP430_OP_DECX		= 0x40005,
	MSP430_OP_DECDA		= 0x40006,
	MSP430_OP_DECDX		= 0x40007,
	MSP430_OP_INCX		= 0x40008,
	MSP430_OP_INCDA		= 0x40009,
	MSP430_OP_INVX		= 0x4000A,
	MSP430_OP_RLAX		= 0x4000B,
	MSP430_OP_RLCX		= 0x4000C,
	MSP430_OP_SECX		= 0x4000D,
	MSP430_OP_TSTA		= 0x4000E,
	MSP430_OP_TSTX		= 0x4000F,
	MSP430_OP_POPX		= 0x40010,
	MSP430_OP_INCDX		= 0x40011,
} msp430_op_t;

/* This represents a decoded instruction. All decoded addresses are
 * absolute or register-indexed, depending on the addressing mode.
 *
 * For jump instructions, the target address is stored in dst_operand.
 */
struct msp430_instruction {
	address_t               offset;
	int                     len;

	msp430_op_t             op;
	msp430_itype_t          itype;
	msp430_dsize_t          dsize;

	msp430_amode_t          src_mode;
	address_t               src_addr;
	msp430_reg_t            src_reg;

	msp430_amode_t          dst_mode;
	address_t		dst_addr;
	msp430_reg_t            dst_reg;

	int			rep_index;
	int			rep_register;
};

/* Decode a single instruction.
 *
 * Returns the number of bytes consumed, or -1 if an error occured.
 *
 * The caller needs to pass a pointer to the bytes to be decoded, the
 * virtual offset of those bytes, and the maximum number available. If
 * successful, the decoded instruction is written into the structure
 * pointed to by insn.
 */
int dis_decode(const uint8_t *code,
	       address_t offset, address_t len,
	       struct msp430_instruction *insn);

/* Look up names for registers and opcodes */
int dis_opcode_from_name(const char *name);
const char *dis_opcode_name(msp430_op_t op);
int dis_reg_from_name(const char *name);
const char *dis_reg_name(msp430_reg_t reg);

#endif