// DSP disassembler
#include "pch.h"

namespace DSP
{

	std::string DspDisasm::ParameterToString(DspParameter index, DecoderInfo& info)
	{
		std::string text;

		switch (index)
		{
			// Registers

			case DspParameter::r0: text = "r0"; break;
			case DspParameter::r1: text = "r1"; break;
			case DspParameter::r2: text = "r2"; break;
			case DspParameter::r3: text = "r3"; break;
			case DspParameter::m0: text = "m0"; break;
			case DspParameter::m1: text = "m1"; break;
			case DspParameter::m2: text = "m2"; break;
			case DspParameter::m3: text = "m3"; break;
			case DspParameter::l0: text = "l0"; break;
			case DspParameter::l1: text = "l1"; break;
			case DspParameter::l2: text = "l2"; break;
			case DspParameter::l3: text = "l3"; break;
			case DspParameter::pcs: text = "pcs"; break;
			case DspParameter::pss: text = "pss"; break;
			case DspParameter::eas: text = "eas"; break;
			case DspParameter::lcs: text = "lcs"; break;
			case DspParameter::a2: text = "a2"; break;
			case DspParameter::b2: text = "b2"; break;
			case DspParameter::dpp: text = "dpp"; break;
			case DspParameter::psr: text = "psr"; break;
			case DspParameter::ps0: text = "ps0"; break;
			case DspParameter::ps1: text = "ps1"; break;
			case DspParameter::ps2: text = "ps2"; break;
			case DspParameter::pc1: text = "pc1"; break;
			case DspParameter::x0: text = "x0"; break;
			case DspParameter::y0: text = "y0"; break;
			case DspParameter::x1: text = "x1"; break;
			case DspParameter::y1: text = "y1"; break;
			case DspParameter::a0: text = "a0"; break;
			case DspParameter::b0: text = "b0"; break;
			case DspParameter::a1: text = "a1"; break;
			case DspParameter::b1: text = "b1"; break;

			case DspParameter::a: text = "a"; break;
			case DspParameter::b: text = "b"; break;

			case DspParameter::x: text = "x"; break;
			case DspParameter::y: text = "y"; break;

			case DspParameter::prod: text = "p"; break;

			// PSR bits

			case DspParameter::psr_c: text = "c"; break;
			case DspParameter::psr_v: text = "v"; break;
			case DspParameter::psr_z: text = "z"; break;
			case DspParameter::psr_n: text = "n"; break;
			case DspParameter::psr_e: text = "e"; break;
			case DspParameter::psr_u: text = "u"; break;
			case DspParameter::psr_tb: text = "tb"; break;
			case DspParameter::psr_sv: text = "sv"; break;
			case DspParameter::psr_te0: text = "te0"; break;
			case DspParameter::psr_te1: text = "te1"; break;
			case DspParameter::psr_te2: text = "te2"; break;
			case DspParameter::psr_te3: text = "te3"; break;
			case DspParameter::psr_et: text = "et"; break;
			case DspParameter::psr_im: text = "im"; break;
			case DspParameter::psr_xl: text = "xl"; break;
			case DspParameter::psr_dp: text = "dp"; break;

			// Modifier

			case DspParameter::mod_none: text = "0"; break;
			case DspParameter::mod_dec: text = "-1"; break;
			case DspParameter::mod_inc: text = "+1"; break;
			case DspParameter::mod_minus_m: text = "-m"; break;
			case DspParameter::mod_plus_m0: text = "+m0"; break;
			case DspParameter::mod_plus_m1: text = "+m1"; break;
			case DspParameter::mod_plus_m2: text = "+m2"; break;
			case DspParameter::mod_plus_m3: text = "+m3"; break;
			case DspParameter::mod_plus_m: text = "+m"; break;

			// Immediates

			case DspParameter::Byte:
				text = "#0x" + ToHexString(info.ImmOperand.Byte);
				break;
			case DspParameter::Byte2:
				text = "#0x" + ToHexString(info.ImmOperand2.Byte);
				break;
			case DspParameter::SignedByte:
				text = std::to_string((int)(int16_t)info.ImmOperand.SignedByte);
				break;
			case DspParameter::SignedByte2:
				text = std::to_string((int)(int16_t)info.ImmOperand2.SignedByte);
				break;
			case DspParameter::SignedShort:
				text = std::to_string((int)info.ImmOperand.SignedShort);
				break;
			case DspParameter::SignedShort2:
				text = std::to_string((int)info.ImmOperand2.SignedShort);
				break;
			case DspParameter::UnsignedShort:
				text = "#0x" + ToHexString((uint16_t)info.ImmOperand.UnsignedShort);
				break;
			case DspParameter::UnsignedShort2:
				text = "#0x" + ToHexString((uint16_t)info.ImmOperand2.UnsignedShort);
				break;
			case DspParameter::Address:
				if (IsHardwareReg(info.ImmOperand.Address))
				{
					text = HardwareRegName(info.ImmOperand.Address);
				}
				else
				{
					text = "$0x" + ToHexString((uint16_t)info.ImmOperand.Address);
				}
				break;
			case DspParameter::Address2:
				text = "$0x" + ToHexString((uint16_t)info.ImmOperand2.Address);
				break;
		}

		return text;
	}

	std::string DspDisasm::InstrToString(DspRegularInstruction instr, ConditionCode cc)
	{
		std::string text;

		switch (instr)
		{
			case DspRegularInstruction::jmp: text = "jmp" + CondCodeToString(cc); break;
			case DspRegularInstruction::call: text = "call" + CondCodeToString(cc); break;
			case DspRegularInstruction::rets: text = "rets" + CondCodeToString(cc); break;
			case DspRegularInstruction::reti: text = "reti" + CondCodeToString(cc); break;
			case DspRegularInstruction::trap: text = "trap"; break;
			case DspRegularInstruction::wait: text = "wait"; break;
			case DspRegularInstruction::exec: text = "exec" + CondCodeToString(cc); break;
			case DspRegularInstruction::loop: text = "loop"; break;
			case DspRegularInstruction::rep: text = "rep"; break;
			case DspRegularInstruction::pld: text = "pld"; break;
			case DspRegularInstruction::nop: text = "nop"; break;
			case DspRegularInstruction::mr: text = "mr"; break;
			case DspRegularInstruction::adsi: text = "adsi"; break;
			case DspRegularInstruction::adli: text = "adli"; break;
			case DspRegularInstruction::cmpsi: text = "cmpsi"; break;
			case DspRegularInstruction::cmpli: text = "cmpli"; break;
			case DspRegularInstruction::lsfi: text = "lsfi"; break;
			case DspRegularInstruction::asfi: text = "asfi"; break;
			case DspRegularInstruction::xorli: text = "xorli"; break;
			case DspRegularInstruction::anli: text = "anli"; break;
			case DspRegularInstruction::orli: text = "orli"; break;
			case DspRegularInstruction::norm: text = "norm"; break;
			case DspRegularInstruction::div: text = "div"; break;
			case DspRegularInstruction::addc: text = "addc"; break;
			case DspRegularInstruction::subc: text = "subc"; break;
			case DspRegularInstruction::negc: text = "negc"; break;
			case DspRegularInstruction::max: text = "max"; break;
			case DspRegularInstruction::lsf: text = "lsf"; break;
			case DspRegularInstruction::asf: text = "asf"; break;
			case DspRegularInstruction::ld: text = "ld"; break;
			case DspRegularInstruction::st: text = "st"; break;
			case DspRegularInstruction::ldsa: text = "ldsa"; break;
			case DspRegularInstruction::stsa: text = "stsa"; break;
			case DspRegularInstruction::ldla: text = "ldla"; break;
			case DspRegularInstruction::stla: text = "stla"; break;
			case DspRegularInstruction::mv: text = "mv"; break;
			case DspRegularInstruction::mvsi: text = "mvsi"; break;
			case DspRegularInstruction::mvli: text = "mvli"; break;
			case DspRegularInstruction::stli: text = "stli"; break;
			case DspRegularInstruction::clr: text = "clr"; break;
			case DspRegularInstruction::set: text = "set"; break;
			case DspRegularInstruction::btstl: text = "btstl"; break;
			case DspRegularInstruction::btsth: text = "btsth"; break;
		}

		while (text.size() < 5)
		{
			text += " ";
		}

		return text;
	}

	std::string DspDisasm::ParrallelInstrToString(DspParallelInstruction instr)
	{
		std::string text;

		switch (instr)
		{
			case DspParallelInstruction::add: text = "add"; break;
			case DspParallelInstruction::addl: text = "addl"; break;
			case DspParallelInstruction::sub: text = "sub"; break;
			case DspParallelInstruction::amv: text = "amv"; break;
			case DspParallelInstruction::cmp: text = "cmp"; break;
			case DspParallelInstruction::inc: text = "inc"; break;
			case DspParallelInstruction::dec: text = "dec"; break;
			case DspParallelInstruction::abs: text = "abs"; break;
			case DspParallelInstruction::neg: text = "neg"; break;
			case DspParallelInstruction::clr: text = "clr"; break;
			case DspParallelInstruction::rnd: text = "rnd"; break;
			case DspParallelInstruction::rndp: text = "rndp"; break;
			case DspParallelInstruction::tst: text = "tst"; break;
			case DspParallelInstruction::lsl16: text = "lsl16"; break;
			case DspParallelInstruction::lsr16: text = "lsr16"; break;
			case DspParallelInstruction::asr16: text = "asr16"; break;
			case DspParallelInstruction::addp: text = "addp"; break;
			// Dont show nop2's
			case DspParallelInstruction::nop: break;
			case DspParallelInstruction::set: text = "set"; break;
			case DspParallelInstruction::mpy: text = "mpy"; break;
			case DspParallelInstruction::mac: text = "mac"; break;
			case DspParallelInstruction::macn: text = "macn"; break;
			case DspParallelInstruction::mvmpy: text = "mvmpy"; break;
			case DspParallelInstruction::rnmpy: text = "rnmpy"; break;
			case DspParallelInstruction::admpy: text = "admpy"; break;
			case DspParallelInstruction::_not: text = "not"; break;
			case DspParallelInstruction::_xor: text = "xor"; break;
			case DspParallelInstruction::_and: text = "and"; break;
			case DspParallelInstruction::_or: text = "or"; break;
			case DspParallelInstruction::lsf: text = "lsf"; break;
			case DspParallelInstruction::asf: text = "asf"; break;
		}

		while (text.size() < 5)
		{
			text += " ";
		}

		return text;
	}

	std::string DspDisasm::ParrallelMemInstrToString(DspParallelMemInstruction instr)
	{
		std::string text;

		switch (instr)
		{
			case DspParallelMemInstruction::nop: break;
			case DspParallelMemInstruction::ldd: text = "ldd"; break;
			case DspParallelMemInstruction::ls: text = "ls"; break;
			case DspParallelMemInstruction::ld: text = "ld"; break;
			case DspParallelMemInstruction::st: text = "st"; break;
			case DspParallelMemInstruction::mv: text = "mv"; break;
			case DspParallelMemInstruction::mr: text = "mr"; break;
		}

		while (text.size() < 5)
		{
			text += " ";
		}

		return text;
	}

	bool DspDisasm::IsHardwareReg(DspAddress address)
	{
		return address >= 0xFF00;
	}

	std::string DspDisasm::HardwareRegName(DspAddress address)
	{
		std::string text;

		switch ((DspHardwareRegs)address)
		{
			case DspHardwareRegs::CMBH: text = "CMBH"; break;
			case DspHardwareRegs::CMBL: text = "CMBL"; break;
			case DspHardwareRegs::DMBH: text = "DMBH"; break;
			case DspHardwareRegs::DMBL: text = "DMBL"; break;

			case DspHardwareRegs::DSMAH: text = "DSMAH"; break;
			case DspHardwareRegs::DSMAL: text = "DSMAL"; break;
			case DspHardwareRegs::DSPA: text = "DSPA"; break;
			case DspHardwareRegs::DSCR: text = "DSCR"; break;
			case DspHardwareRegs::DSBL: text = "DSBL"; break;

			case DspHardwareRegs::ACDAT2: text = "ACDAT2"; break;
			case DspHardwareRegs::ACSAH: text = "ACSAH"; break;
			case DspHardwareRegs::ACSAL: text = "ACSAL"; break;
			case DspHardwareRegs::ACEAH: text = "ACEAH"; break;
			case DspHardwareRegs::ACEAL: text = "ACEAL"; break;
			case DspHardwareRegs::ACCAH: text = "ACCAH"; break;
			case DspHardwareRegs::ACCAL: text = "ACCAL"; break;
			case DspHardwareRegs::ACDAT: text = "ACDAT"; break;

			case DspHardwareRegs::DIRQ: text = "DIRQ"; break;

			case DspHardwareRegs::ACFMT: text = "ACFMT"; break;
			case DspHardwareRegs::ACPDS: text = "ACPDS"; break;
			case DspHardwareRegs::ACYN1: text = "ACYN1"; break;
			case DspHardwareRegs::ACYN2: text = "ACYN2"; break;
			case DspHardwareRegs::ACGAN: text = "ACGAN"; break;

			case DspHardwareRegs::ADPCM_A00: text = "ADPCM_A00"; break;
			case DspHardwareRegs::ADPCM_A10: text = "ADPCM_A10"; break;
			case DspHardwareRegs::ADPCM_A20: text = "ADPCM_A20"; break;
			case DspHardwareRegs::ADPCM_A30: text = "ADPCM_A30"; break;
			case DspHardwareRegs::ADPCM_A40: text = "ADPCM_A40"; break;
			case DspHardwareRegs::ADPCM_A50: text = "ADPCM_A50"; break;
			case DspHardwareRegs::ADPCM_A60: text = "ADPCM_A60"; break;
			case DspHardwareRegs::ADPCM_A70: text = "ADPCM_A70"; break;
			case DspHardwareRegs::ADPCM_A01: text = "ADPCM_A01"; break;
			case DspHardwareRegs::ADPCM_A11: text = "ADPCM_A11"; break;
			case DspHardwareRegs::ADPCM_A21: text = "ADPCM_A21"; break;
			case DspHardwareRegs::ADPCM_A31: text = "ADPCM_A31"; break;
			case DspHardwareRegs::ADPCM_A41: text = "ADPCM_A41"; break;
			case DspHardwareRegs::ADPCM_A51: text = "ADPCM_A51"; break;
			case DspHardwareRegs::ADPCM_A61: text = "ADPCM_A61"; break;
			case DspHardwareRegs::ADPCM_A71: text = "ADPCM_A71"; break;

			default:
				text = "UnkHW_" + ToHexString((uint16_t)address);
		}

		return "$(" + text + ")";
	}

	std::string DspDisasm::CondCodeToString(ConditionCode cc)
	{
		std::string text = "";

		switch (cc)
		{
			case ConditionCode::ge: text = "ge"; break;
			case ConditionCode::lt: text = "lt"; break;
			case ConditionCode::gt: text = "gt"; break;
			case ConditionCode::le: text = "le"; break;
			case ConditionCode::nz: text = "nz"; break;
			case ConditionCode::z: text = "z"; break;
			case ConditionCode::nc: text = "nc"; break;
			case ConditionCode::c: text = "c"; break;
			case ConditionCode::ne: text = "ne"; break;
			case ConditionCode::e: text = "e"; break;
			case ConditionCode::nm: text = "nm"; break;
			case ConditionCode::m: text = "m"; break;
			case ConditionCode::nt: text = "nt"; break;
			case ConditionCode::t: text = "t"; break;
			case ConditionCode::v: text = "v"; break;
			case ConditionCode::always: text = ""; break;
		}

		return text;
	}

	std::string DspDisasm::Disasm(DspAddress startAddr, DecoderInfo& info)
	{
		bool firstParam;
		std::string text = "";

		// Address and code bytes

		text += ToHexString((uint16_t)startAddr);
		text += " ";

		for (size_t i = 0; i < DspCore::MaxInstructionSizeInBytes; i++)
		{
			if (i < info.sizeInBytes)
			{
				text += ToHexString(info.bytes[i]) + " ";
			}
			else
			{
				text += "   ";
			}
		}

		// Regular instruction

		if (!info.parallel)
		{
			if (info.instr != DspRegularInstruction::Unknown)
			{
				text += "\t" + DspDisasm::InstrToString(info.instr, info.cc) + "\t";
			}
			else
			{
				text += "\t??? ";
			}

			firstParam = true;
			for (size_t i = 0; i < info.numParameters; i++)
			{
				if (!firstParam)
				{
					text += ", ";
				}
				text += ParameterToString(info.params[i], info);
				firstParam = false;
			}
		}

		// Parallel instruction pair (same line)

		else
		{
			// Top

			if (info.parallelInstr != DspParallelInstruction::Unknown)
			{
				text += "\t" + DspDisasm::ParrallelInstrToString(info.parallelInstr) + "\t";
			}
			else
			{
				text += "\t??? ";
			}

			firstParam = true;
			for (size_t i = 0; i < info.numParameters; i++)
			{
				if (!firstParam)
				{
					text += ", ";
				}
				text += ParameterToString(info.params[i], info);
				firstParam = false;
			}

			// Bottom

			while (text.size() < 40)
			{
				text += " ";
			}

			if (info.parallelMemInstr != DspParallelMemInstruction::Unknown)
			{
				text += "\t" + ParrallelMemInstrToString(info.parallelMemInstr) + "\t";
			}
			else
			{
				text += "???";
			}

			firstParam = true;
			for (size_t i = 0; i < info.numParametersEx; i++)
			{
				if (!firstParam)
				{
					text += ", ";
				}
				text += ParameterToString(info.paramsEx[i], info);
				firstParam = false;
			}
		}

		return text;
	}

}
