/*  PCSX2 - PS2 Emulator for PCs
 *  Copyright (C) 2002-2014  PCSX2 Dev Team
 *
 *  PCSX2 is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU Lesser General Public License as published by the Free Software Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  PCSX2 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 PCSX2.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#include "PrecompiledHeader.h"

#include "DebugInterface.h"
#include "Memory.h"
#include "R5900.h"
#include "AppCoreThread.h"
#include "Debug.h"
#include "../VU.h"
#include "Counters.h"

#include "../R3000A.h"
#include "../IopMem.h"
#include "SymbolMap.h"

extern AppCoreThread CoreThread;

R5900DebugInterface r5900Debug;
R3000DebugInterface r3000Debug;

#ifdef WIN32
#define strcasecmp stricmp
#endif

enum ReferenceIndexType {
	REF_INDEX_PC = 32,
	REF_INDEX_HI = 33,
	REF_INDEX_LO = 34,
	REF_INDEX_FPU = 0x1000,
	REF_INDEX_FPU_INT = 0x2000,
	REF_INDEX_VFPU = 0x4000,
	REF_INDEX_VFPU_INT = 0x8000,
	REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU,
};


class MipsExpressionFunctions: public IExpressionFunctions
{
public:
	MipsExpressionFunctions(DebugInterface* cpu): cpu(cpu) { };

	virtual bool parseReference(char* str, u64& referenceIndex)
	{
		for (int i = 0; i < 32; i++)
		{
			char reg[8];
			sprintf(reg, "r%d", i);

			if (strcasecmp(str, reg) == 0 || strcasecmp(str, cpu->getRegisterName(0, i)) == 0)
			{
				referenceIndex = i;
				return true;
			}
		}

		if (strcasecmp(str, "pc") == 0)
		{
			referenceIndex = REF_INDEX_PC;
			return true;
		}

		if (strcasecmp(str, "hi") == 0)
		{
			referenceIndex = REF_INDEX_HI;
			return true;
		}

		if (strcasecmp(str, "lo") == 0)
		{
			referenceIndex = REF_INDEX_LO;
			return true;
		}

		return false;
	}

	virtual bool parseSymbol(char* str, u64& symbolValue)
	{
		u32 value;
		bool result = symbolMap.GetLabelValue(str,value);
		symbolValue = value;
		return result;
	}

	virtual u64 getReferenceValue(u64 referenceIndex)
	{
		if (referenceIndex < 32)
			return cpu->getRegister(0, referenceIndex)._u64[0];
		if (referenceIndex == REF_INDEX_PC)
			return cpu->getPC();
		if (referenceIndex == REF_INDEX_HI)
			return cpu->getHI()._u64[0];
		if (referenceIndex == REF_INDEX_LO)
			return cpu->getLO()._u64[0];
		return -1;
	}

	virtual ExpressionType getReferenceType(u64 referenceIndex) {
		if (referenceIndex & REF_INDEX_IS_FLOAT) {
			return EXPR_TYPE_FLOAT;
		}
		return EXPR_TYPE_UINT;
	}
	
	virtual bool getMemoryValue(u32 address, int size, u64& dest, char* error)
	{
		switch (size)
		{
		case 1: case 2: case 4: case 8:
			break;
		default:
			sprintf(error,"Invalid memory access size %d",size);
			return false;
		}

		if (address % size)
		{
			sprintf(error,"Invalid memory access (unaligned)");
			return false;
		}

		switch (size)
		{
		case 1:
			dest = cpu->read8(address);
			break;
		case 2:
			dest = cpu->read16(address);
			break;
		case 4:
			dest = cpu->read32(address);
			break;
		case 8:
			dest = cpu->read64(address);
			break;
		}

		return true;
	}

private:
	DebugInterface* cpu;
};

//
// DebugInterface
//

bool DebugInterface::isAlive()
{
	return GetCoreThread().IsOpen() && g_FrameCount > 0;
}

bool DebugInterface::isCpuPaused()
{
	return GetCoreThread().IsPaused();
}

void DebugInterface::pauseCpu()
{
	SysCoreThread& core = GetCoreThread();
	if (!core.IsPaused())
		core.Pause();
}

void DebugInterface::resumeCpu()
{
	SysCoreThread& core = GetCoreThread();
	if (core.IsPaused())
		core.Resume();
}

bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
{
	MipsExpressionFunctions funcs(this);
	return initPostfixExpression(exp,&funcs,dest);
}

bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest)
{
	MipsExpressionFunctions funcs(this);
	return parsePostfixExpression(exp,&funcs,dest);
}


//
// R5900DebugInterface
//

u32 R5900DebugInterface::read8(u32 address)
{
	if (!isValidAddress(address))
		return -1;

	return memRead8(address);
}

u32 R5900DebugInterface::read16(u32 address)
{
	if (!isValidAddress(address) || address % 2)
		return -1;

	return memRead16(address);
}

u32 R5900DebugInterface::read32(u32 address)
{
	if (!isValidAddress(address) || address % 4)
		return -1;

	return memRead32(address);
}

u64 R5900DebugInterface::read64(u32 address)
{
	if (!isValidAddress(address) || address % 8)
		return -1;

	u64 result;
	memRead64(address,result);
	return result;
}

u128 R5900DebugInterface::read128(u32 address)
{
	__aligned16 u128 result;
	if (!isValidAddress(address) || address % 16)
	{
		result.hi = result.lo = -1;
		return result;
	}

	memRead128(address,result);
	return result;
}

void R5900DebugInterface::write8(u32 address, u8 value)
{
	if (!isValidAddress(address))
		return;

	memWrite8(address,value);
}

void R5900DebugInterface::write32(u32 address, u32 value)
{
	if (!isValidAddress(address))
		return;

	memWrite32(address,value);
}


int R5900DebugInterface::getRegisterCategoryCount()
{
	return EECAT_COUNT;
}

const char* R5900DebugInterface::getRegisterCategoryName(int cat)
{
	switch (cat)
	{
	case EECAT_GPR:
		return "GPR";
	case EECAT_CP0:
		return "CP0";
	case EECAT_FPR:
		return "FPR";
	case EECAT_FCR:
		return "FCR";
	case EECAT_VU0F:
		return "VU0f";
	case EECAT_VU0I:
		return "VU0i";
	default:
		return "Invalid";
	}
}

int R5900DebugInterface::getRegisterSize(int cat)
{
	switch (cat)
	{
	case EECAT_GPR:
	case EECAT_VU0F:
		return 128;
	case EECAT_CP0:
	case EECAT_FPR:
	case EECAT_FCR:
	case EECAT_VU0I:
		return 32;
	default:
		return 0;
	}
}

int R5900DebugInterface::getRegisterCount(int cat)
{
	switch (cat)
	{
	case EECAT_GPR:
		return 35;	// 32 + pc + hi + lo
	case EECAT_CP0:
	case EECAT_FPR:
	case EECAT_FCR:
	case EECAT_VU0F:
	case EECAT_VU0I:
		return 32;
	default:
		return 0;
	}
}

DebugInterface::RegisterType R5900DebugInterface::getRegisterType(int cat)
{
	switch (cat)
	{
	case EECAT_GPR:
	case EECAT_CP0:
	case EECAT_VU0F:
	case EECAT_VU0I:
	case EECAT_FCR:
	default:
		return NORMAL;
	case EECAT_FPR:
		return SPECIAL;
	}
}

const char* R5900DebugInterface::getRegisterName(int cat, int num)
{
	switch (cat)
	{
	case EECAT_GPR:
		switch (num)
		{
		case 32:	// pc
			return "pc";
		case 33:	// hi
			return "hi";
		case 34:	// lo
			return "lo";
		default:
			return R5900::GPR_REG[num];
		}
	case EECAT_CP0:
		return R5900::COP0_REG[num];
	case EECAT_FPR:
		return R5900::COP1_REG_FP[num];
	case EECAT_FCR:
		return R5900::COP1_REG_FCR[num];
	case EECAT_VU0F:
		return R5900::COP2_REG_FP[num];
	case EECAT_VU0I:
		return R5900::COP2_REG_CTL[num];
	default:
		return "Invalid";
	}
}

u128 R5900DebugInterface::getRegister(int cat, int num)
{
	u128 result;
	switch (cat)
	{
	case EECAT_GPR:
		switch (num)
		{
		case 32:	// pc
			result = u128::From32(cpuRegs.pc);
			break;
		case 33:	// hi
			result = cpuRegs.HI.UQ;
			break;
		case 34:	// lo
			result = cpuRegs.LO.UQ;
			break;
		default:
			result = cpuRegs.GPR.r[num].UQ;
			break;
		}
		break;
	case EECAT_CP0:
		result = u128::From32(cpuRegs.CP0.r[num]);
		break;
	case EECAT_FPR:
		result = u128::From32(fpuRegs.fpr[num].UL);
		break;
	case EECAT_FCR:
		result = u128::From32(fpuRegs.fprc[num]);
		break;
	case EECAT_VU0F:
		result = VU1.VF[num].UQ;
		break;
	case EECAT_VU0I:
		result = u128::From32(VU1.VI[num].UL);
		break;
	default:
		result.From32(0);
		break;
	}

	return result;
}

wxString R5900DebugInterface::getRegisterString(int cat, int num)
{
	switch (cat)
	{
	case EECAT_GPR:
	case EECAT_CP0:
	case EECAT_FCR:
		return getRegister(cat,num).ToString();
	case EECAT_FPR:
		{
			char str[64];
			sprintf(str,"%f",fpuRegs.fpr[num].f);
			return wxString(str,wxConvUTF8);
		}
	default:
		return L"";
	}
}


u128 R5900DebugInterface::getHI()
{
	return cpuRegs.HI.UQ;
}

u128 R5900DebugInterface::getLO()
{
	return cpuRegs.LO.UQ;
}

u32 R5900DebugInterface::getPC()
{
	return cpuRegs.pc;
}

void R5900DebugInterface::setPc(u32 newPc)
{
	cpuRegs.pc = newPc;
}

void R5900DebugInterface::setRegister(int cat, int num, u128 newValue)
{
	switch (cat)
	{
	case EECAT_GPR:
		switch (num)
		{
		case 32:	// pc
			cpuRegs.pc = newValue._u32[0];
			break;
		case 33:	// hi
			cpuRegs.HI.UQ = newValue;
			break;
		case 34:	// lo
			cpuRegs.LO.UQ = newValue;
			break;
		default:
			cpuRegs.GPR.r[num].UQ = newValue;
			break;
		}
		break;
	case EECAT_CP0:
		cpuRegs.CP0.r[num] = newValue._u32[0];
		break;
	case EECAT_FPR:
		fpuRegs.fpr[num].UL = newValue._u32[0];
		break;
	case EECAT_FCR:
		fpuRegs.fprc[num] = newValue._u32[0];
		break;
	case EECAT_VU0F:
		VU1.VF[num].UQ = newValue;
		break;
	case EECAT_VU0I:
		VU1.VI[num].UL = newValue._u32[0];
		break;
	default:
		break;
	}
}

std::string R5900DebugInterface::disasm(u32 address, bool simplify)
{
	std::string out;

	u32 op = read32(address);
	R5900::disR5900Fasm(out,op,address,simplify);
	return out;
}

bool R5900DebugInterface::isValidAddress(u32 addr)
{
	// ee can't access the first part of memory.
	if (addr < 0x80000)
		return false;

	if (addr >= 0xFFFF8000)
		return true;

	addr &= 0x7FFFFFFF;

	// get rid of ee ram mirrors
	if ((addr >> 28) == 2 || (addr >> 28) == 3)
		addr &= ~(0xF << 28);
	
	// registers
	if (addr >= 0x10000000 && addr < 0x10010000)
		return true;
	if (addr >= 0x12000000 && addr < 0x12001100)
		return true;

	// scratchpad
	if (addr >= 0x70000000 && addr < 0x70004000)
		return true;

	return !(addr & 0x40000000) && vtlb_GetPhyPtr(addr & 0x1FFFFFFF) != NULL;
}


u32 R5900DebugInterface::getCycles()
{
	return cpuRegs.cycle;
}

//
// R3000DebugInterface
//


u32 R3000DebugInterface::read8(u32 address)
{
	if (!isValidAddress(address))
		return -1;
	return iopMemRead8(address);
}

u32 R3000DebugInterface::read16(u32 address)
{
	if (!isValidAddress(address))
		return -1;
	return iopMemRead16(address);
}

u32 R3000DebugInterface::read32(u32 address)
{
	if (!isValidAddress(address))
		return -1;
	return iopMemRead32(address);
}

u64 R3000DebugInterface::read64(u32 address)
{
	return 0;
}

u128 R3000DebugInterface::read128(u32 address)
{
	return u128::From32(0);
}

void R3000DebugInterface::write8(u32 address, u8 value)
{
	if (!isValidAddress(address))
		return;

	iopMemWrite8(address,value);
}

void R3000DebugInterface::write32(u32 address, u32 value)
{
	if (!isValidAddress(address))
		return;

	iopMemWrite32(address,value);
}

int R3000DebugInterface::getRegisterCategoryCount()
{
	return IOPCAT_COUNT;
}

const char* R3000DebugInterface::getRegisterCategoryName(int cat)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		return "GPR";
	default:
		return "Invalid";
	}
}

int R3000DebugInterface::getRegisterSize(int cat)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		return 32;
	default:
		return 0;
	}
}

int R3000DebugInterface::getRegisterCount(int cat)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		return 35;	// 32 + pc + hi + lo
	default:
		return 0;
	}
}

DebugInterface::RegisterType R3000DebugInterface::getRegisterType(int cat)
{
	switch (cat)
	{
	case IOPCAT_GPR:
	default:
		return DebugInterface::NORMAL;
	}
}

const char* R3000DebugInterface::getRegisterName(int cat, int num)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		switch (num)
		{
		case 32:	// pc
			return "pc";
		case 33:	// hi
			return "hi";
		case 34:	// lo
			return "lo";
		default:
			return R5900::GPR_REG[num];
		}
	default:
		return "Invalid";
	}
}

u128 R3000DebugInterface::getRegister(int cat, int num)
{
	u32 value;
	
	switch (cat)
	{
	case IOPCAT_GPR:
		switch (num)
		{
		case 32:	// pc
			value = psxRegs.pc;
			break;
		case 33:	// hi
			value = psxRegs.GPR.n.hi;
			break;
		case 34:	// lo
			value = psxRegs.GPR.n.lo;
			break;
		default:
			value = psxRegs.GPR.r[num];
			break;
		}
		break;
	default:
		value = -1;
		break;
	}

	return u128::From32(value);
}

wxString R3000DebugInterface::getRegisterString(int cat, int num)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		return getRegister(cat,num).ToString();
	default:
		return L"Invalid";
	}
}

u128 R3000DebugInterface::getHI()
{
	return u128::From32(psxRegs.GPR.n.hi);
}

u128 R3000DebugInterface::getLO()
{
	return u128::From32(psxRegs.GPR.n.lo);
}

u32 R3000DebugInterface::getPC()
{
	return psxRegs.pc;
}

void R3000DebugInterface::setPc(u32 newPc)
{
	psxRegs.pc = newPc;
}

void R3000DebugInterface::setRegister(int cat, int num, u128 newValue)
{
	switch (cat)
	{
	case IOPCAT_GPR:
		switch (num)
		{
		case 32:	// pc
			psxRegs.pc = newValue._u32[0];
			break;
		case 33:	// hi
			psxRegs.GPR.n.hi = newValue._u32[0];
			break;
		case 34:	// lo
			psxRegs.GPR.n.lo = newValue._u32[0];
			break;
		default:
			psxRegs.GPR.r[num] = newValue._u32[0];
			break;
		}
		break;
	default:
		break;
	}
}

std::string R3000DebugInterface::disasm(u32 address, bool simplify)
{
	std::string out;

	u32 op = read32(address);
	R5900::disR5900Fasm(out,op,address,simplify);
	return out;
}

bool R3000DebugInterface::isValidAddress(u32 addr)
{
	if (addr >= 0x10000000 && addr < 0x10010000)
		return true;
	if (addr >= 0x12000000 && addr < 0x12001100)
		return true;
	if (addr >= 0x70000000 && addr < 0x70004000)
		return true;

	return !(addr & 0x40000000) && vtlb_GetPhyPtr(addr & 0x1FFFFFFF) != NULL;
}

u32 R3000DebugInterface::getCycles()
{
	return psxRegs.cycle;
}
