#ifndef __TMS320_DASM_H__
#define __TMS320_DASM_H__

#define IDA_COMPATIBLE_MODE	1

/* forward declarations */

struct tms320_instruction;
typedef struct tms320_instruction insn_item_t;

struct tms320_instruction_mask;
typedef struct tms320_instruction_mask insn_mask_t;

struct tms320_instruction_flag;
typedef struct tms320_instruction_flag insn_flag_t;

struct tms320_instruction_head;
typedef struct tms320_instruction_head insn_head_t;

typedef enum {
	TMS320_FLAG_E = 0x10,
	TMS320_FLAG_R,
	TMS320_FLAG_U,
	TMS320_FLAG_u,
	TMS320_FLAG_g,
	TMS320_FLAG_r,
	TMS320_FLAG_t,

	TMS320_FLAG_uu,
	TMS320_FLAG_mm,
	TMS320_FLAG_cc,
	TMS320_FLAG_tt,
	TMS320_FLAG_vv,
	TMS320_FLAG_ss,
	TMS320_FLAG_dd,
	TMS320_FLAG_SS,
	TMS320_FLAG_DD,

	TMS320_FLAG_k3,
	TMS320_FLAG_k4,
	TMS320_FLAG_k5,
	TMS320_FLAG_k6,
	TMS320_FLAG_k8,
	TMS320_FLAG_k12,
	TMS320_FLAG_k16,

	TMS320_FLAG_K8,
	TMS320_FLAG_K16,

	TMS320_FLAG_l1,
	TMS320_FLAG_l3,
	TMS320_FLAG_l7,
	TMS320_FLAG_l16,

	TMS320_FLAG_L7,
	TMS320_FLAG_L8,
	TMS320_FLAG_L16,

	TMS320_FLAG_P8,
	TMS320_FLAG_P24,
	TMS320_FLAG_D16,

	TMS320_FLAG_SHFT,
	TMS320_FLAG_SHIFTW,
	TMS320_FLAG_CCCCCCC,
	TMS320_FLAG_AAAAAAAI,

	TMS320_FLAG_FSSS,
	TMS320_FLAG_FDDD,
	TMS320_FLAG_XSSS,
	TMS320_FLAG_XDDD,
	TMS320_FLAG_XACS,
	TMS320_FLAG_XACD,

	TMS320_FLAG_XXX,
	TMS320_FLAG_MMM,
	TMS320_FLAG_Y,
	TMS320_FLAG_YY,
} insn_flag_e;

struct tms320_instruction {
#define i_list_last(x)			!(((x)->i_list || (x)->m_list || (x)->f_list || (x)->syntax))
	insn_item_t	* i_list;

	insn_mask_t	* m_list;
	insn_flag_t	* f_list;

	char		* syntax;
};

struct tms320_instruction_mask {
#define m_list_last(x)			!(((x)->f || (x)->n || (x)->v))
	ut8		f, n, v;	/* from, number, value */
};

struct tms320_instruction_flag {
#define f_list_last(x)			!(((x)->f || (x)->v))
	ut8		f, v;		/* from, value */
};

struct tms320_instruction_head {
	ut8		byte;
	ut8		size;
	insn_item_t	insn;
};

/*
 * TMS320 dasm instance
 */

typedef struct {
	insn_head_t		* head;
	insn_item_t		* insn;

	union {
		ut8		opcode;
		ut8		stream[8];
	};
	ut64		opcode64;

#define TMS320_S_INVAL		0x01
	ut8			status;
	ut8			length;
	char			syntax[1024];

#define def_field(name, size)			\
	unsigned int bf_##name##_valid:1;	\
	unsigned int bf_##name##_value:size

	struct {
		def_field	(E, 1);
		def_field	(R, 1);
		def_field	(U, 1);
		def_field	(u, 1);
		def_field	(g, 1);
		def_field	(r, 1);
		def_field	(t, 1);

		def_field	(k3, 3);
		def_field	(k4, 4);
		def_field	(k5, 5);
		def_field	(k6, 6);
		def_field	(k8, 8);
		def_field	(k12, 12);
		def_field	(k16, 16);

		def_field	(l1, 1);
		def_field	(l3, 3);
		def_field	(l7, 7);
		def_field	(l16, 16);

		def_field	(K8, 8);
		def_field	(K16, 16);

		def_field	(L7, 7);
		def_field	(L8, 8);
		def_field	(L16, 16);

		def_field	(P8, 8);
		def_field	(P24, 24);

		def_field	(D16, 16);

		def_field	(SHFT, 4);
		def_field	(SHIFTW, 6);

		def_field	(ss, 2);
		def_field	(dd, 2);

		def_field	(uu, 2);
		def_field	(cc, 2);
		def_field	(mm, 2);
		def_field	(vv, 2);
		def_field	(tt, 2);

		def_field	(FSSS, 4);
		def_field	(FDDD, 4);
		def_field	(XSSS, 4);
		def_field	(XDDD, 4);
		def_field	(XACS, 4);
		def_field	(XACD, 4);

		def_field	(CCCCCCC, 7);
		def_field	(AAAAAAAI, 8);

		def_field	(SS, 2);
		def_field	(SS2, 2);
		def_field	(DD, 2);
		def_field	(DD2, 2);

		// aggregates

		def_field	(Xmem_mmm, 3);
		def_field	(Xmem_reg, 3);

		def_field	(Ymem_mmm, 3);
		def_field	(Ymem_reg, 3);

		// qualifiers

		def_field	(q_lr, 1);
		def_field	(q_cr, 1);
	} f;
	HtUP *map;

#define TMS320_F_CPU_C54X	0x0000001
#define TMS320_F_CPU_C55X	0x0000002
#define TMS320_F_CPU_C55X_PLUS	0x0000003
#define TMS320_F_CPU_MASK	0x00000FF
	ut32			features;
#define tms320_f_get_cpu(d)	((d)->features & TMS320_F_CPU_MASK)
#define tms320_f_set_cpu(d, v)	((d)->features = ((d)->features & ~TMS320_F_CPU_MASK) | (v))
} tms320_dasm_t;

#define field_valid(d, name)		\
	(d)->f.bf_##name##_valid
#define field_value(d, name)		\
	(d)->f.bf_##name##_value

#ifdef _MSC_VER
#define set_field_value(d, name, value)	\
{					\
	field_valid(d, name) = 1;	\
	field_value(d, name) = value;	\
}
#else
#define set_field_value(d, name, value)	\
({					\
	field_valid(d, name) = 1;	\
	field_value(d, name) = value;	\
})
#endif

#define LIST_END			{ 0 }

#define INSN_MASK(af, an, av)		{ .f = af, .n = an, .v = av }
#define INSN_FLAG(af, av)		{ .f = af, .v = TMS320_FLAG_##av }
#define INSN_SYNTAX(...)		(char *)#__VA_ARGS__

extern int tms320_dasm(tms320_dasm_t *, const ut8 *, int);

extern bool tms320_dasm_init(tms320_dasm_t *);
extern int tms320_dasm_fini(tms320_dasm_t *);

#endif /* __TMS320_DASM_H__ */
