//===------ SystemZDisassembler.cpp - Disassembler for PowerPC ------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

/* Capstone Disassembly Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2014 */

#ifdef CAPSTONE_HAS_SYSZ

#include <stdio.h>	// DEBUG
#include <stdlib.h>
#include <string.h>

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

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

#include "SystemZMCTargetDesc.h"

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

static DecodeStatus decodeRegisterClass(MCInst *Inst, uint64_t RegNo, const unsigned *Regs)
{
	//assert(RegNo < 16 && "Invalid register");
	RegNo = Regs[RegNo];
	if (RegNo == 0)
		return MCDisassembler_Fail;

	MCOperand_CreateReg0(Inst, (unsigned)RegNo);
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGR32BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_GR32Regs);
}

static DecodeStatus DecodeGRH32BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_GRH32Regs);
}

static DecodeStatus DecodeGR64BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_GR64Regs);
}

static DecodeStatus DecodeGR128BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_GR128Regs);
}

static DecodeStatus DecodeADDR64BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_GR64Regs);
}

static DecodeStatus DecodeFP32BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder) 
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_FP32Regs);
}

static DecodeStatus DecodeFP64BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_FP64Regs);
}

static DecodeStatus DecodeFP128BitRegisterClass(MCInst *Inst, uint64_t RegNo,
		uint64_t Address, const void *Decoder)
{
	return decodeRegisterClass(Inst, RegNo, SystemZMC_FP128Regs);
}

static DecodeStatus decodeUImmOperand(MCInst *Inst, uint64_t Imm)
{
	//assert(isUInt<N>(Imm) && "Invalid immediate");
	MCOperand_CreateImm0(Inst, Imm);
	return MCDisassembler_Success;
}

static DecodeStatus decodeSImmOperand(MCInst *Inst, uint64_t Imm, unsigned N)
{
	//assert(isUInt<N>(Imm) && "Invalid immediate");
	MCOperand_CreateImm0(Inst, SignExtend64(Imm, N));
	return MCDisassembler_Success;
}

static DecodeStatus decodeAccessRegOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeU4ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeU6ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeU8ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeU16ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeU32ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeUImmOperand(Inst, Imm);
}

static DecodeStatus decodeS8ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeSImmOperand(Inst, Imm, 8);
}

static DecodeStatus decodeS16ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder) 
{
	return decodeSImmOperand(Inst, Imm, 16);
}

static DecodeStatus decodeS32ImmOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodeSImmOperand(Inst, Imm, 32);
}

static DecodeStatus decodePCDBLOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, unsigned N)
{
	//assert(isUInt<N>(Imm) && "Invalid PC-relative offset");
	MCOperand_CreateImm0(Inst, SignExtend64(Imm, N) * 2 + Address);
	return MCDisassembler_Success;
}

static DecodeStatus decodePC16DBLOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address, const void *Decoder)
{
	return decodePCDBLOperand(Inst, Imm, Address, 16);
}

static DecodeStatus decodePC32DBLOperand(MCInst *Inst, uint64_t Imm,
		uint64_t Address,
		const void *Decoder)
{
	return decodePCDBLOperand(Inst, Imm, Address, 32);
}

static DecodeStatus decodeBDAddr12Operand(MCInst *Inst, uint64_t Field,
		const unsigned *Regs)
{
	uint64_t Base = Field >> 12;
	uint64_t Disp = Field & 0xfff;
	//assert(Base < 16 && "Invalid BDAddr12");

	MCOperand_CreateReg0(Inst, Base == 0 ? 0 : Regs[Base]);
	MCOperand_CreateImm0(Inst, Disp);

	return MCDisassembler_Success;
}

static DecodeStatus decodeBDAddr20Operand(MCInst *Inst, uint64_t Field,
		const unsigned *Regs)
{
	uint64_t Base = Field >> 20;
	uint64_t Disp = ((Field << 12) & 0xff000) | ((Field >> 8) & 0xfff);
	//assert(Base < 16 && "Invalid BDAddr20");

	MCOperand_CreateReg0(Inst, Base == 0 ? 0 : Regs[Base]);
	MCOperand_CreateImm0(Inst, SignExtend64(Disp, 20));
	return MCDisassembler_Success;
}

static DecodeStatus decodeBDXAddr12Operand(MCInst *Inst, uint64_t Field,
		const unsigned *Regs)
{
	uint64_t Index = Field >> 16;
	uint64_t Base = (Field >> 12) & 0xf;
	uint64_t Disp = Field & 0xfff;

	//assert(Index < 16 && "Invalid BDXAddr12");
	MCOperand_CreateReg0(Inst, Base == 0 ? 0 : Regs[Base]);
	MCOperand_CreateImm0(Inst, Disp);
	MCOperand_CreateReg0(Inst, Index == 0 ? 0 : Regs[Index]);

	return MCDisassembler_Success;
}

static DecodeStatus decodeBDXAddr20Operand(MCInst *Inst, uint64_t Field,
		const unsigned *Regs)
{
	uint64_t Index = Field >> 24;
	uint64_t Base = (Field >> 20) & 0xf;
	uint64_t Disp = ((Field & 0xfff00) >> 8) | ((Field & 0xff) << 12);

	//assert(Index < 16 && "Invalid BDXAddr20");
	MCOperand_CreateReg0(Inst, Base == 0 ? 0 : Regs[Base]);
	MCOperand_CreateImm0(Inst, SignExtend64(Disp, 20));
	MCOperand_CreateReg0(Inst, Index == 0 ? 0 : Regs[Index]);

	return MCDisassembler_Success;
}

static DecodeStatus decodeBDLAddr12Len8Operand(MCInst *Inst, uint64_t Field,
		const unsigned *Regs)
{
	uint64_t Length = Field >> 16;
	uint64_t Base = (Field >> 12) & 0xf;
	uint64_t Disp = Field & 0xfff;
	//assert(Length < 256 && "Invalid BDLAddr12Len8");

	MCOperand_CreateReg0(Inst, Base == 0 ? 0 : Regs[Base]);
	MCOperand_CreateImm0(Inst, Disp);
	MCOperand_CreateImm0(Inst, Length + 1);

	return MCDisassembler_Success;
}

static DecodeStatus decodeBDAddr32Disp12Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDAddr12Operand(Inst, Field, SystemZMC_GR32Regs);
}

static DecodeStatus decodeBDAddr32Disp20Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDAddr20Operand(Inst, Field, SystemZMC_GR32Regs);
}

static DecodeStatus decodeBDAddr64Disp12Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDAddr12Operand(Inst, Field, SystemZMC_GR64Regs);
}

static DecodeStatus decodeBDAddr64Disp20Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDAddr20Operand(Inst, Field, SystemZMC_GR64Regs);
}

static DecodeStatus decodeBDXAddr64Disp12Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDXAddr12Operand(Inst, Field, SystemZMC_GR64Regs);
}

static DecodeStatus decodeBDXAddr64Disp20Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDXAddr20Operand(Inst, Field, SystemZMC_GR64Regs);
}

static DecodeStatus decodeBDLAddr64Disp12Len8Operand(MCInst *Inst, uint64_t Field,
		uint64_t Address, const void *Decoder)
{
	return decodeBDLAddr12Len8Operand(Inst, Field, SystemZMC_GR64Regs);
}

#define GET_SUBTARGETINFO_ENUM
#include "SystemZGenSubtargetInfo.inc"
#include "SystemZGenDisassemblerTables.inc"
bool SystemZ_getInstruction(csh ud, const uint8_t *code, size_t code_len, MCInst *MI,
		uint16_t *size, uint64_t address, void *info)
{
	uint64_t Inst;
	uint8_t Bytes[6];
	const uint8_t *Table;
	uint16_t I; 

	// The top 2 bits of the first byte specify the size.
	if (*code < 0x40) {
		*size = 2;
		Table = DecoderTable16;
	} else if (*code < 0xc0) {
		*size = 4;
		Table = DecoderTable32;
	} else {
		*size = 6;
		Table = DecoderTable48;
	}

	if (code_len < *size)
		// short of input data
		return false;

	if (MI->flat_insn->detail) {
		memset(MI->flat_insn->detail, 0, sizeof(cs_detail));
	}

	memcpy(Bytes, code, *size);

	// Construct the instruction.
	Inst = 0;
	for (I = 0; I < *size; ++I)
		Inst = (Inst << 8) | Bytes[I];

	return decodeInstruction(Table, MI, Inst, address, info, 0);
}

#define GET_REGINFO_ENUM
#define GET_REGINFO_MC_DESC
#include "SystemZGenRegisterInfo.inc"
void SystemZ_init(MCRegisterInfo *MRI)
{
	/*
	InitMCRegisterInfo(SystemZRegDesc, 98, RA, PC,
			SystemZMCRegisterClasses, 12,
			SystemZRegUnitRoots,
			49,
			SystemZRegDiffLists,
			SystemZRegStrings,
			SystemZSubRegIdxLists,
			7,
			SystemZSubRegIdxRanges,
			SystemZRegEncodingTable);
	*/

	MCRegisterInfo_InitMCRegisterInfo(MRI, SystemZRegDesc, 98,
			0, 0,
			SystemZMCRegisterClasses, 12,
			0, 0,
			SystemZRegDiffLists,
			0,
			SystemZSubRegIdxLists, 7,
			0);
}

#endif
