/* Capstone Disassembly Engine */
/* TMS320C64x Backend by Fotis Loukos <me@fotisl.com> 2016 */

#ifdef CAPSTONE_HAS_TMS320C64X

#include <string.h>

#include "../../cs_priv.h"
#include "../../utils.h"

#include "TMS320C64xDisassembler.h"

#include "../../MCInst.h"
#include "../../MCInstrDesc.h"
#include "../../MCFixedLenDisassembler.h"
#include "../../MCRegisterInfo.h"
#include "../../MCDisassembler.h"
#include "../../MathExtras.h"

static uint64_t getFeatureBits(int mode);

static DecodeStatus DecodeGPRegsRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeControlRegsRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeScst5(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeScst16(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodePCRelScst7(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodePCRelScst10(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodePCRelScst12(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodePCRelScst21(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeMemOperand(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeMemOperandSc(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeMemOperand2(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeRegPair5(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeRegPair4(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeCondRegister(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeCondRegisterZero(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeSide(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeParallel(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeCrosspathX1(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeCrosspathX2(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeCrosspathX3(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

static DecodeStatus DecodeNop(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder);

#include "TMS320C64xGenDisassemblerTables.inc"

#define GET_REGINFO_ENUM
#define GET_REGINFO_MC_DESC
#include "TMS320C64xGenRegisterInfo.inc"

static const unsigned GPRegsDecoderTable[] = {
	TMS320C64x_A0,  TMS320C64x_A1,  TMS320C64x_A2,  TMS320C64x_A3,
	TMS320C64x_A4,  TMS320C64x_A5,  TMS320C64x_A6,  TMS320C64x_A7,
	TMS320C64x_A8,  TMS320C64x_A9,  TMS320C64x_A10, TMS320C64x_A11,
	TMS320C64x_A12, TMS320C64x_A13, TMS320C64x_A14, TMS320C64x_A15,
	TMS320C64x_A16, TMS320C64x_A17, TMS320C64x_A18, TMS320C64x_A19,
	TMS320C64x_A20, TMS320C64x_A21, TMS320C64x_A22, TMS320C64x_A23,
	TMS320C64x_A24, TMS320C64x_A25, TMS320C64x_A26, TMS320C64x_A27,
	TMS320C64x_A28, TMS320C64x_A29, TMS320C64x_A30, TMS320C64x_A31
};

static const unsigned ControlRegsDecoderTable[] = {
	TMS320C64x_AMR,    TMS320C64x_CSR,  TMS320C64x_ISR,   TMS320C64x_ICR,
	TMS320C64x_IER,    TMS320C64x_ISTP, TMS320C64x_IRP,   TMS320C64x_NRP,
	~0U,               ~0U,             TMS320C64x_TSCL,  TMS320C64x_TSCH,
	~0U,               TMS320C64x_ILC,  TMS320C64x_RILC,  TMS320C64x_REP,
	TMS320C64x_PCE1,   TMS320C64x_DNUM, ~0U,              ~0U,
	~0U,               TMS320C64x_SSR,  TMS320C64x_GPLYA, TMS320C64x_GPLYB,
	TMS320C64x_GFPGFR, TMS320C64x_DIER, TMS320C64x_TSR,   TMS320C64x_ITSR,
	TMS320C64x_NTSR,   TMS320C64x_ECR,  ~0U,              TMS320C64x_IERR
};

static uint64_t getFeatureBits(int mode)
{
	// support everything
	return (uint64_t)-1;
}

static unsigned getReg(const unsigned *RegTable, unsigned RegNo)
{
	if(RegNo > 31)
		return ~0U;
	return RegTable[RegNo];
}

static DecodeStatus DecodeGPRegsRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder)
{
	unsigned Reg;

	if(RegNo > 31)
		return MCDisassembler_Fail;

	Reg = getReg(GPRegsDecoderTable, RegNo);
	if(Reg == ~0U)
		return MCDisassembler_Fail;
	MCOperand_CreateReg0(Inst, Reg);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeControlRegsRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder)
{
	unsigned Reg;

	if(RegNo > 31)
		return MCDisassembler_Fail;

	Reg = getReg(ControlRegsDecoderTable, RegNo);
	if(Reg == ~0U)
		return MCDisassembler_Fail;
	MCOperand_CreateReg0(Inst, Reg);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeScst5(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 5 bit value */
	if(imm & (1 << (5 - 1)))
		imm |= ~((1 << 5) - 1);

	MCOperand_CreateImm0(Inst, imm);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeScst16(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 16 bit value */
	if(imm & (1 << (16 - 1)))
		imm |= ~((1 << 16) - 1);

	MCOperand_CreateImm0(Inst, imm);

	return MCDisassembler_Success;
}

static DecodeStatus DecodePCRelScst7(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 7 bit value */
	if(imm & (1 << (7 - 1)))
		imm |= ~((1 << 7) - 1);

	/* Address is relative to the address of the first instruction in the fetch packet */
	MCOperand_CreateImm0(Inst, (Address & ~31) + (imm * 4));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePCRelScst10(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 10 bit value */
	if(imm & (1 << (10 - 1)))
		imm |= ~((1 << 10) - 1);

	/* Address is relative to the address of the first instruction in the fetch packet */
	MCOperand_CreateImm0(Inst, (Address & ~31) + (imm * 4));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePCRelScst12(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 12 bit value */
	if(imm & (1 << (12 - 1)))
		imm |= ~((1 << 12) - 1);

	/* Address is relative to the address of the first instruction in the fetch packet */
	MCOperand_CreateImm0(Inst, (Address & ~31) + (imm * 4));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePCRelScst21(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	int32_t imm;

	imm = Val;
	/* Sign extend 21 bit value */
	if(imm & (1 << (21 - 1)))
		imm |= ~((1 << 21) - 1);

	/* Address is relative to the address of the first instruction in the fetch packet */
	MCOperand_CreateImm0(Inst, (Address & ~31) + (imm * 4));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemOperand(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	return DecodeMemOperandSc(Inst, Val | (1 << 15), Address, Decoder);
}

static DecodeStatus DecodeMemOperandSc(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	uint8_t scaled, base, offset, mode, unit;
	unsigned basereg, offsetreg;

	scaled = (Val >> 15) & 1;
	base = (Val >> 10) & 0x1f;
	offset = (Val >> 5) & 0x1f;
	mode = (Val >> 1) & 0xf;
	unit = Val & 1;

	if((base >= TMS320C64X_REG_A0) && (base <= TMS320C64X_REG_A31))
		base = (base - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
	// base cannot be a B register, because it was ANDed above with 0x1f.
	// And the TMS320C64X_REG_B0 > 31
	basereg = getReg(GPRegsDecoderTable, base);
	if (basereg ==  ~0U)
		return MCDisassembler_Fail;

	switch(mode) {
		case 0:
		case 1:
		case 8:
		case 9:
		case 10:
		case 11:
			MCOperand_CreateImm0(Inst, (scaled << 19) | (basereg << 12) | (offset << 5) | (mode << 1) | unit);
			break;
		case 4:
		case 5:
		case 12:
		case 13:
		case 14:
		case 15:
			if((offset >= TMS320C64X_REG_A0) && (offset <= TMS320C64X_REG_A31))
				offset = (offset - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
			// offset cannot be a B register, because it was ANDed above with 0x1f.
			// And the TMS320C64X_REG_B0 > 31
			offsetreg = getReg(GPRegsDecoderTable, offset);
			if (offsetreg ==  ~0U)
				return MCDisassembler_Fail;
			MCOperand_CreateImm0(Inst, (scaled << 19) | (basereg << 12) | (offsetreg << 5) | (mode << 1) | unit);
			break;
		default:
			return MCDisassembler_Fail;
	}

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemOperand2(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	uint16_t offset;
	unsigned basereg;

	if(Val & 1)
		basereg = TMS320C64X_REG_B15;
	else
		basereg = TMS320C64X_REG_B14;

	offset = (Val >> 1) & 0x7fff;
	MCOperand_CreateImm0(Inst, (offset << 7) | basereg);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeRegPair5(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder)
{
	unsigned Reg;

	if(RegNo > 31)
		return MCDisassembler_Fail;

	Reg = getReg(GPRegsDecoderTable, RegNo);
	MCOperand_CreateReg0(Inst, Reg);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeRegPair4(MCInst *Inst, unsigned RegNo,
		uint64_t Address, void *Decoder)
{
	unsigned Reg;

	if(RegNo > 15)
		return MCDisassembler_Fail;

	Reg = getReg(GPRegsDecoderTable, RegNo << 1);
	MCOperand_CreateReg0(Inst, Reg);

	return MCDisassembler_Success;
}

static DecodeStatus DecodeCondRegister(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
		case 7:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_INVALID;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_B0;
			break;
		case 2:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_B1;
			break;
		case 3:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_B2;
			break;
		case 4:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_A1;
			break;
		case 5:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_A2;
			break;
		case 6:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_A0;
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.condition.reg = TMS320C64X_REG_INVALID;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeCondRegisterZero(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.condition.zero = 0;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.condition.zero = 1;
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.condition.zero = 0;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeSide(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;
	MCOperand *op;
	int i;

	/* This is pretty messy, probably we should find a better way */
	if(Val == 1) {
		for(i = 0; i < Inst->size; i++) {
			op = &Inst->Operands[i];
			if(op->Kind == kRegister) {
				if((op->RegVal >= TMS320C64X_REG_A0) && (op->RegVal <= TMS320C64X_REG_A31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
				else if((op->RegVal >= TMS320C64X_REG_B0) && (op->RegVal <= TMS320C64X_REG_B31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_B0 + TMS320C64X_REG_A0);
			}
		}
	}

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.funit.side = 1;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.funit.side = 2;
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.funit.side = 0;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeParallel(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.parallel = 0;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.parallel = 1;
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.parallel = -1;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeCrosspathX1(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;
	MCOperand *op;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 0;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 1;
			op = &Inst->Operands[0];
			if(op->Kind == kRegister) {
				if((op->RegVal >= TMS320C64X_REG_A0) && (op->RegVal <= TMS320C64X_REG_A31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
				else if((op->RegVal >= TMS320C64X_REG_B0) && (op->RegVal <= TMS320C64X_REG_B31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_B0 + TMS320C64X_REG_A0);
			}
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = -1;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeCrosspathX2(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;
	MCOperand *op;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 0;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 1;
			op = &Inst->Operands[1];
			if(op->Kind == kRegister) {
				if((op->RegVal >= TMS320C64X_REG_A0) && (op->RegVal <= TMS320C64X_REG_A31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
				else if((op->RegVal >= TMS320C64X_REG_B0) && (op->RegVal <= TMS320C64X_REG_B31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_B0 + TMS320C64X_REG_A0);
			}
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = -1;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}

static DecodeStatus DecodeCrosspathX3(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	DecodeStatus ret = MCDisassembler_Success;
	MCOperand *op;

	if(!Inst->flat_insn->detail)
		return MCDisassembler_Success;

	switch(Val) {
		case 0:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 0;
			break;
		case 1:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = 2;
			op = &Inst->Operands[2];
			if(op->Kind == kRegister) {
				if((op->RegVal >= TMS320C64X_REG_A0) && (op->RegVal <= TMS320C64X_REG_A31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_A0 + TMS320C64X_REG_B0);
				else if((op->RegVal >= TMS320C64X_REG_B0) && (op->RegVal <= TMS320C64X_REG_B31))
					op->RegVal = (op->RegVal - TMS320C64X_REG_B0 + TMS320C64X_REG_A0);
			}
			break;
		default:
			Inst->flat_insn->detail->tms320c64x.funit.crosspath = -1;
			ret = MCDisassembler_Fail;
			break;
	}

	return ret;
}


static DecodeStatus DecodeNop(MCInst *Inst, unsigned Val,
		uint64_t Address, void *Decoder)
{
	MCOperand_CreateImm0(Inst, Val + 1);

	return MCDisassembler_Success;
}

#define GET_INSTRINFO_ENUM
#include "TMS320C64xGenInstrInfo.inc"

bool TMS320C64x_getInstruction(csh ud, const uint8_t *code, size_t code_len,
		MCInst *MI, uint16_t *size, uint64_t address, void *info)
{
	uint32_t insn;
	DecodeStatus result;

	if(code_len < 4) {
		*size = 0;
		return MCDisassembler_Fail;
	}

	if(MI->flat_insn->detail)
		memset(MI->flat_insn->detail, 0, offsetof(cs_detail, tms320c64x)+sizeof(cs_tms320c64x));

	insn = readBytes32(MI, code);
	result = decodeInstruction_4(DecoderTable32, MI, insn, address, info, 0);

	if(result == MCDisassembler_Success) {
		*size = 4;
		return true;
	}

	MCInst_clear(MI);
	*size = 0;
	return false;
}

void TMS320C64x_init(MCRegisterInfo *MRI)
{
	MCRegisterInfo_InitMCRegisterInfo(MRI, TMS320C64xRegDesc, 90,
			0, 0,
			TMS320C64xMCRegisterClasses, 7,
			0, 0,
			TMS320C64xRegDiffLists,
			0,
			TMS320C64xSubRegIdxLists, 1,
			0);
}

#endif
