#include "stdafx.h"
#include "PPUDisAsm.h"
#include "PPUFunction.h"

const ppu_decoder<PPUDisAsm> s_ppu_disasm;

u32 PPUDisAsm::disasm(u32 pc)
{
	const u32 op = *(be_t<u32>*)(offset + pc);
	(this->*(s_ppu_disasm.decode(op)))({ op });
	return 4;
}

constexpr std::pair<const char*, char> get_BC_info(u32 bo, u32 bi)
{
	std::pair<const char*, char> info{};

	switch (bo)
	{
	case 0b00000:
	case 0b00001:
	{
		info = {"bdnzf", 'f'}; break;
	}
	case 0b00010:
	case 0b00011:
	{
		info = {"bdzf", 'f'}; break;
	}
	case 0b01000:
	case 0b01001:
	{
		info = {"bdnzt", 't'}; break;
	}
	case 0b01010:
	case 0b01011:
	{
		info = {"bdzt", 't'}; break;
	}
	case 0b10010:
	{
		info.first = "bdz"; break;
	}
	case 0b11010:
	{
		info = {"bdz", '-'}; break;
	}
	case 0b11011:
	{
		info = {"bdz", '+'}; break;
	}
	case 0b10000:
	{
		info.first = "bdnz"; break;
	}
	case 0b11000:
	{
		info = {"bdnz", '-'}; break;
	}
	case 0b11001:
	{
		info = {"bdnz", '+'}; break;
	}
	case 0b00100:
	{
		switch (bi % 4)
		{
		case 0x0: info.first = "bge"; break;
		case 0x1: info.first = "ble"; break;
		case 0x2: info.first = "bne"; break;
		case 0x3: info.first = "bns"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	case 0b00110:
	{
		info.second = '-';
		switch (bi % 4)
		{
		case 0x0: info.first = "bge"; break;
		case 0x1: info.first = "ble"; break;
		case 0x2: info.first = "bne"; break;
		case 0x3: info.first = "bns"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	case 0b00111:
	{
		info.second = '+';
		switch (bi % 4)
		{
		case 0x0: info.first = "bge"; break;
		case 0x1: info.first = "ble"; break;
		case 0x2: info.first = "bne"; break;
		case 0x3: info.first = "bns"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	case 0b01100:
	{
		switch (bi % 4)
		{
		case 0x0: info.first = "blt"; break;
		case 0x1: info.first = "bgt"; break;
		case 0x2: info.first = "beq"; break;
		case 0x3: info.first = "bso"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	case 0b01110:
	{
		info.second = '-';
		switch (bi % 4)
		{
		case 0x0: info.first = "blt"; break;
		case 0x1: info.first = "bgt"; break;
		case 0x2: info.first = "beq"; break;
		case 0x3: info.first = "bso"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	case 0b01111:
	{
		info.second = '+';
		switch (bi % 4)
		{
		case 0x0: info.first = "blt"; break;
		case 0x1: info.first = "bgt"; break;
		case 0x2: info.first = "beq"; break;
		case 0x3: info.first = "bso"; break;
		default: ASSUME(0); break;
		}
		break;
	}
	//case 0b10100:
	//{
	//	info.first = "b"; break;
	//}
	default: break;
	}

	return info;
}

void PPUDisAsm::MFVSCR(ppu_opcode_t op)
{
	DisAsm_V1("mfvscr", op.vd);
}

void PPUDisAsm::MTVSCR(ppu_opcode_t op)
{
	DisAsm_V1("mtvscr", op.vb);
}

void PPUDisAsm::VADDCUW(ppu_opcode_t op)
{
	DisAsm_V3("vaddcuw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDFP(ppu_opcode_t op)
{
	DisAsm_V3("vaddfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDSBS(ppu_opcode_t op)
{
	DisAsm_V3("vaddsbs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDSHS(ppu_opcode_t op)
{
	DisAsm_V3("vaddshs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDSWS(ppu_opcode_t op)
{
	DisAsm_V3("vaddsws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUBM(ppu_opcode_t op)
{
	DisAsm_V3("vaddubm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUBS(ppu_opcode_t op)
{
	DisAsm_V3("vaddubs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUHM(ppu_opcode_t op)
{
	DisAsm_V3("vadduhm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUHS(ppu_opcode_t op)
{
	DisAsm_V3("vadduhs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUWM(ppu_opcode_t op)
{
	DisAsm_V3("vadduwm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VADDUWS(ppu_opcode_t op)
{
	DisAsm_V3("vadduws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAND(ppu_opcode_t op)
{
	DisAsm_V3("vand", op.vd, op.va, op.vb);
}

void PPUDisAsm::VANDC(ppu_opcode_t op)
{
	DisAsm_V3("vandc", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGSB(ppu_opcode_t op)
{
	DisAsm_V3("vavgsb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGSH(ppu_opcode_t op)
{
	DisAsm_V3("vavgsh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGSW(ppu_opcode_t op)
{
	DisAsm_V3("vavgsw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGUB(ppu_opcode_t op)
{
	DisAsm_V3("vavgub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGUH(ppu_opcode_t op)
{
	DisAsm_V3("vavguh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VAVGUW(ppu_opcode_t op)
{
	DisAsm_V3("vavguw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCFSX(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vcfsx", op.vd, op.vb, op.vuimm);
}

void PPUDisAsm::VCFUX(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vcfux", op.vd, op.vb, op.vuimm);
}

void PPUDisAsm::VCMPBFP(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpbfp." : "vcmpbfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPEQFP(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpeqfp." : "vcmpeqfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPEQUB(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpequb." : "vcmpequb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPEQUH(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpequh." : "vcmpequh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPEQUW(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpequw." : "vcmpequw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGEFP(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgefp." : "vcmpgefp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTFP(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtfp." : "vcmpgtfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTSB(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtsb." : "vcmpgtsb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTSH(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtsh." : "vcmpgtsh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTSW(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtsw." : "vcmpgtsw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTUB(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtub." : "vcmpgtub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTUH(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtuh." : "vcmpgtuh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCMPGTUW(ppu_opcode_t op)
{
	DisAsm_V3(op.oe ? "vcmpgtuw." : "vcmpgtuw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VCTSXS(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vctsxs", op.vd, op.vb, op.vuimm);
}

void PPUDisAsm::VCTUXS(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vctuxs", op.vd, op.vb, op.vuimm);
}

void PPUDisAsm::VEXPTEFP(ppu_opcode_t op)
{
	DisAsm_V2("vexptefp", op.vd, op.vb);
}

void PPUDisAsm::VLOGEFP(ppu_opcode_t op)
{
	DisAsm_V2("vlogefp", op.vd, op.vb);
}

void PPUDisAsm::VMADDFP(ppu_opcode_t op)
{
	DisAsm_V4("vmaddfp", op.vd, op.va, op.vc, op.vb);
}

void PPUDisAsm::VMAXFP(ppu_opcode_t op)
{
	DisAsm_V3("vmaxfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXSB(ppu_opcode_t op)
{
	DisAsm_V3("vmaxsb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXSH(ppu_opcode_t op)
{
	DisAsm_V3("vmaxsh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXSW(ppu_opcode_t op)
{
	DisAsm_V3("vmaxsw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXUB(ppu_opcode_t op)
{
	DisAsm_V3("vmaxub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXUH(ppu_opcode_t op)
{
	DisAsm_V3("vmaxuh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMAXUW(ppu_opcode_t op)
{
	DisAsm_V3("vmaxuw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMHADDSHS(ppu_opcode_t op)
{
	DisAsm_V4("vmhaddshs", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMHRADDSHS(ppu_opcode_t op)
{
	DisAsm_V4("vmhraddshs", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMINFP(ppu_opcode_t op)
{
	DisAsm_V3("vminfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINSB(ppu_opcode_t op)
{
	DisAsm_V3("vminsb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINSH(ppu_opcode_t op)
{
	DisAsm_V3("vminsh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINSW(ppu_opcode_t op)
{
	DisAsm_V3("vminsw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINUB(ppu_opcode_t op)
{
	DisAsm_V3("vminub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINUH(ppu_opcode_t op)
{
	DisAsm_V3("vminuh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMINUW(ppu_opcode_t op)
{
	DisAsm_V3("vminuw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMLADDUHM(ppu_opcode_t op)
{
	DisAsm_V4("vmladduhm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMRGHB(ppu_opcode_t op)
{
	DisAsm_V3("vmrghb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMRGHH(ppu_opcode_t op)
{
	DisAsm_V3("vmrghh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMRGHW(ppu_opcode_t op)
{
	DisAsm_V3("vmrghw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMRGLB(ppu_opcode_t op)
{
	DisAsm_V3("vmrglb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMRGLH(ppu_opcode_t op)
{
	DisAsm_V3("vmrglh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMRGLW(ppu_opcode_t op)
{
	DisAsm_V3("vmrglw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMSUMMBM(ppu_opcode_t op)
{
	DisAsm_V4("vmsummbm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMSUMSHM(ppu_opcode_t op)
{
	DisAsm_V4("vmsumshm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMSUMSHS(ppu_opcode_t op)
{
	DisAsm_V4("vmsumshs", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMSUMUBM(ppu_opcode_t op)
{
	DisAsm_V4("vmsumubm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMSUMUHM(ppu_opcode_t op)
{
	DisAsm_V4("vmsumuhm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMSUMUHS(ppu_opcode_t op)
{
	DisAsm_V4("vmsumuhs", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VMULESB(ppu_opcode_t op)
{
	DisAsm_V3("vmulesb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULESH(ppu_opcode_t op)
{
	DisAsm_V3("vmulesh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULEUB(ppu_opcode_t op)
{
	DisAsm_V3("vmuleub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULEUH(ppu_opcode_t op)
{
	DisAsm_V3("vmuleuh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULOSB(ppu_opcode_t op)
{
	DisAsm_V3("vmulosb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULOSH(ppu_opcode_t op)
{
	DisAsm_V3("vmulosh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULOUB(ppu_opcode_t op)
{
	DisAsm_V3("vmuloub", op.vd, op.va, op.vb);
}

void PPUDisAsm::VMULOUH(ppu_opcode_t op)
{
	DisAsm_V3("vmulouh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VNMSUBFP(ppu_opcode_t op)
{
	DisAsm_V4("vnmsubfp", op.vd, op.va, op.vc, op.vb);
}

void PPUDisAsm::VNOR(ppu_opcode_t op)
{
	DisAsm_V3("vnor", op.vd, op.va, op.vb);
}

void PPUDisAsm::VOR(ppu_opcode_t op)
{
	DisAsm_V3("vor", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPERM(ppu_opcode_t op)
{
	DisAsm_V4("vperm", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VPKPX(ppu_opcode_t op)
{
	DisAsm_V3("vpkpx", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKSHSS(ppu_opcode_t op)
{
	DisAsm_V3("vpkshss", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKSHUS(ppu_opcode_t op)
{
	DisAsm_V3("vpkshus", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKSWSS(ppu_opcode_t op)
{
	DisAsm_V3("vpkswss", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKSWUS(ppu_opcode_t op)
{
	DisAsm_V3("vpkswus", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKUHUM(ppu_opcode_t op)
{
	DisAsm_V3("vpkuhum", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKUHUS(ppu_opcode_t op)
{
	DisAsm_V3("vpkuhus", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKUWUM(ppu_opcode_t op)
{
	DisAsm_V3("vpkuwum", op.vd, op.va, op.vb);
}

void PPUDisAsm::VPKUWUS(ppu_opcode_t op)
{
	DisAsm_V3("vpkuwus", op.vd, op.va, op.vb);
}

void PPUDisAsm::VREFP(ppu_opcode_t op)
{
	DisAsm_V2("vrefp", op.vd, op.vb);
}

void PPUDisAsm::VRFIM(ppu_opcode_t op)
{
	DisAsm_V2("vrfim", op.vd, op.vb);
}

void PPUDisAsm::VRFIN(ppu_opcode_t op)
{
	DisAsm_V2("vrfin", op.vd, op.vb);
}

void PPUDisAsm::VRFIP(ppu_opcode_t op)
{
	DisAsm_V2("vrfip", op.vd, op.vb);
}

void PPUDisAsm::VRFIZ(ppu_opcode_t op)
{
	DisAsm_V2("vrfiz", op.vd, op.vb);
}

void PPUDisAsm::VRLB(ppu_opcode_t op)
{
	DisAsm_V3("vrlb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VRLH(ppu_opcode_t op)
{
	DisAsm_V3("vrlh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VRLW(ppu_opcode_t op)
{
	DisAsm_V3("vrlw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VRSQRTEFP(ppu_opcode_t op)
{
	DisAsm_V2("vrsqrtefp", op.vd, op.vb);
}

void PPUDisAsm::VSEL(ppu_opcode_t op)
{
	DisAsm_V4("vsel", op.vd, op.va, op.vb, op.vc);
}

void PPUDisAsm::VSL(ppu_opcode_t op)
{
	DisAsm_V3("vsl", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSLB(ppu_opcode_t op)
{
	DisAsm_V3("vslb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSLDOI(ppu_opcode_t op)
{
	DisAsm_V3_UIMM("vsldoi", op.vd, op.va, op.vb, op.vsh);
}

void PPUDisAsm::VSLH(ppu_opcode_t op)
{
	DisAsm_V3("vslh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSLO(ppu_opcode_t op)
{
	DisAsm_V3("vslo", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSLW(ppu_opcode_t op)
{
	DisAsm_V3("vslw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSPLTB(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vspltb", op.vd, op.vb, op.vuimm & 0xf);
}

void PPUDisAsm::VSPLTH(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vsplth", op.vd, op.vb, op.vuimm & 0x7);
}

void PPUDisAsm::VSPLTISB(ppu_opcode_t op)
{
	DisAsm_V1_SIMM("vspltisb", op.vd, op.vsimm);
}

void PPUDisAsm::VSPLTISH(ppu_opcode_t op)
{
	DisAsm_V1_SIMM("vspltish", op.vd, op.vsimm);
}

void PPUDisAsm::VSPLTISW(ppu_opcode_t op)
{
	DisAsm_V1_SIMM("vspltisw", op.vd, op.vsimm);
}

void PPUDisAsm::VSPLTW(ppu_opcode_t op)
{
	DisAsm_V2_UIMM("vspltw", op.vd, op.vb, op.vuimm & 0x3);
}

void PPUDisAsm::VSR(ppu_opcode_t op)
{
	DisAsm_V3("vsr", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRAB(ppu_opcode_t op)
{
	DisAsm_V3("vsrab", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRAH(ppu_opcode_t op)
{
	DisAsm_V3("vsrah", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRAW(ppu_opcode_t op)
{
	DisAsm_V3("vsraw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRB(ppu_opcode_t op)
{
	DisAsm_V3("vsrb", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRH(ppu_opcode_t op)
{
	DisAsm_V3("vsrh", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRO(ppu_opcode_t op)
{
	DisAsm_V3("vsro", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSRW(ppu_opcode_t op)
{
	DisAsm_V3("vsrw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBCUW(ppu_opcode_t op)
{
	DisAsm_V3("vsubcuw", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBFP(ppu_opcode_t op)
{
	DisAsm_V3("vsubfp", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBSBS(ppu_opcode_t op)
{
	DisAsm_V3("vsubsbs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBSHS(ppu_opcode_t op)
{
	DisAsm_V3("vsubshs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBSWS(ppu_opcode_t op)
{
	DisAsm_V3("vsubsws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUBM(ppu_opcode_t op)
{
	DisAsm_V3("vsububm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUBS(ppu_opcode_t op)
{
	DisAsm_V3("vsububs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUHM(ppu_opcode_t op)
{
	DisAsm_V3("vsubuhm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUHS(ppu_opcode_t op)
{
	DisAsm_V3("vsubuhs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUWM(ppu_opcode_t op)
{
	DisAsm_V3("vsubuwm", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUBUWS(ppu_opcode_t op)
{
	DisAsm_V3("vsubuws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUMSWS(ppu_opcode_t op)
{
	DisAsm_V3("vsumsws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUM2SWS(ppu_opcode_t op)
{
	DisAsm_V3("vsum2sws", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUM4SBS(ppu_opcode_t op)
{
	DisAsm_V3("vsum4sbs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUM4SHS(ppu_opcode_t op)
{
	DisAsm_V3("vsum4shs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VSUM4UBS(ppu_opcode_t op)
{
	DisAsm_V3("vsum4ubs", op.vd, op.va, op.vb);
}

void PPUDisAsm::VUPKHPX(ppu_opcode_t op)
{
	DisAsm_V2("vupkhpx", op.vd, op.vb);
}

void PPUDisAsm::VUPKHSB(ppu_opcode_t op)
{
	DisAsm_V2("vupkhsb", op.vd, op.vb);
}

void PPUDisAsm::VUPKHSH(ppu_opcode_t op)
{
	DisAsm_V2("vupkhsh", op.vd, op.vb);
}

void PPUDisAsm::VUPKLPX(ppu_opcode_t op)
{
	DisAsm_V2("vupklpx", op.vd, op.vb);
}

void PPUDisAsm::VUPKLSB(ppu_opcode_t op)
{
	DisAsm_V2("vupklsb", op.vd, op.vb);
}

void PPUDisAsm::VUPKLSH(ppu_opcode_t op)
{
	DisAsm_V2("vupklsh", op.vd, op.vb);
}

void PPUDisAsm::VXOR(ppu_opcode_t op)
{
	DisAsm_V3("vxor", op.vd, op.va, op.vb);
}

void PPUDisAsm::TDI(ppu_opcode_t op)
{
	DisAsm_INT1_R1_IMM("tdi", op.bo, op.ra, op.simm16);
}

void PPUDisAsm::TWI(ppu_opcode_t op)
{
	DisAsm_INT1_R1_IMM("twi", op.bo, op.ra, op.simm16);
}

void PPUDisAsm::MULLI(ppu_opcode_t op)
{
	DisAsm_R2_IMM("mulli", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::SUBFIC(ppu_opcode_t op)
{
	DisAsm_R2_IMM("subfic", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::CMPLI(ppu_opcode_t op)
{
	DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.uimm16);
}

void PPUDisAsm::CMPI(ppu_opcode_t op)
{
	DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16);
}

void PPUDisAsm::ADDIC(ppu_opcode_t op)
{
	DisAsm_R2_IMM(op.main & 1 ? "addic." : "addic", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::ADDI(ppu_opcode_t op)
{
	if (op.ra == 0)
	{
		DisAsm_R1_IMM("li", op.rd, op.simm16);
	}
	else
	{
		DisAsm_R2_IMM("addi", op.rd, op.ra, op.simm16);
	}
}

void PPUDisAsm::ADDIS(ppu_opcode_t op)
{
	if (op.ra == 0)
	{
		DisAsm_R1_IMM("lis", op.rd, op.simm16);
	}
	else
	{
		DisAsm_R2_IMM("addis", op.rd, op.ra, op.simm16);
	}
}

void PPUDisAsm::BC(ppu_opcode_t op)
{
	const u32 bo = op.bo;
	const u32 bi = op.bi;
	const s32 bd = op.ds * 4;
	const u32 aa = op.aa;
	const u32 lk = op.lk;

	if (m_mode == CPUDisAsm_CompilerElfMode)
	{
		Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
		return;
	}

	const auto [inst, sign] = get_BC_info(bo, bi);

	if (!inst)
	{
		Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
		return;
	}

	const auto final = std::string(inst) + (lk ? "l" : "") + (aa ? "a" : "") + sign;

	// Check if need to display full BI value
	if (sign == 't' || sign == 'f')
	{
		if (aa)
		{
			DisAsm_BI_BRANCH_A(final, bi, bd);
		}
		else
		{
			DisAsm_BI_BRANCH(final, bi, bd);
		}

		return;
	}

	if (aa)
	{
		DisAsm_CR_BRANCH_A(final, bi / 4, bd);
	}
	else
	{
		DisAsm_CR_BRANCH(final, bi / 4, bd);
	}
}

void PPUDisAsm::SC(ppu_opcode_t op)
{
	if (op.opcode != ppu_instructions::SC(0))
	{
		return UNK(op);
	}

	Write("sc");
}

void PPUDisAsm::B(ppu_opcode_t op)
{
	const u32 li = op.li;
	const u32 aa = op.aa;
	const u32 lk = op.lk;

	if (m_mode == CPUDisAsm_CompilerElfMode)
	{
		Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
		return;
	}

	switch (lk)
	{
	case 0:
		switch (aa)
		{
		case 0:	DisAsm_BRANCH("b", li);		break;
		case 1:	DisAsm_BRANCH_A("ba", li);	break;
		}
		break;

	case 1:
		switch (aa)
		{
		case 0: DisAsm_BRANCH("bl", li);	break;
		case 1: DisAsm_BRANCH_A("bla", li);	break;
		}
		break;
	}
}

void PPUDisAsm::MCRF(ppu_opcode_t op)
{
	DisAsm_CR2("mcrf", op.crfd, op.crfs);
}

void PPUDisAsm::BCLR(ppu_opcode_t op)
{
	const u32 bo = op.bo;
	const u32 bi = op.bi;
	const u32 bh = op.bh;
	const u32 lk = op.lk;

	if (bo == 0b10100)
	{
		Write(lk ? "blrl" : "blr");
		return;
	}

	const auto [inst, sign] = get_BC_info(bo, bi);

	if (!inst)
	{
		Write(fmt::format("bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
		return;
	}

	const auto final = std::string(inst) + (lk ? "lrl" : "lr") + sign;

	// Check if need to display full BI value
	if (sign == 't' || sign == 'f')
	{
		DisAsm_BI_BRANCH(final, bi, bh);
		return;
	}

	DisAsm_CR_BRANCH(final, bi / 4, bh);
}

void PPUDisAsm::CRNOR(ppu_opcode_t op)
{
	if (op.crba == op.crbb)
	{
		DisAsm_BI2("crnot", op.crbd, op.crba);
		return;
	}

	DisAsm_BI3("crnor", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CRANDC(ppu_opcode_t op)
{
	DisAsm_BI3("crandc", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::ISYNC(ppu_opcode_t op)
{
	Write("isync");
}

void PPUDisAsm::CRXOR(ppu_opcode_t op)
{
	if (op.crba == op.crbb && op.crba == op.crbd)
	{
		DisAsm_BI1("crclr", op.crbd);
		return;
	}

	DisAsm_BI3("crxor", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CRNAND(ppu_opcode_t op)
{
	DisAsm_BI3("crnand", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CRAND(ppu_opcode_t op)
{
	DisAsm_BI3("crand", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CREQV(ppu_opcode_t op)
{
	if (op.crba == op.crbb && op.crba == op.crbd)
	{
		DisAsm_BI1("crset", op.crbd);
		return;
	}

	DisAsm_BI3("creqv", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CRORC(ppu_opcode_t op)
{
	DisAsm_BI3("crorc", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::CROR(ppu_opcode_t op)
{
	if (op.crba == op.crbb)
	{
		DisAsm_BI2("crmove", op.crbd, op.crba);
		return;
	}

	DisAsm_BI3("cror", op.crbd, op.crba, op.crbb);
}

void PPUDisAsm::BCCTR(ppu_opcode_t op)
{
	const u32 bo = op.bo;
	const u32 bi = op.bi;
	const u32 bh = op.bh;
	const u32 lk = op.lk;

	if (bo == 0b10100)
	{
		Write(lk ? "bctrl" : "bctr");
		return;
	}

	const auto [inst, sign] = get_BC_info(bo, bi);

	if (!inst || inst[1] == 'd')
	{
		// Invalid or unknown bcctr form
		Write(fmt::format("bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
		return;
	}

	DisAsm_CR_BRANCH(std::string(inst) + (lk ? "ctrl" : "clr") + sign, bi / 4, bh);
}

void PPUDisAsm::RLWIMI(ppu_opcode_t op)
{
	DisAsm_R2_INT3_RC("rlwimi", op.ra, op.rs, op.sh32, op.mb32, op.me32, op.rc);
}

void PPUDisAsm::RLWINM(ppu_opcode_t op)
{
	DisAsm_R2_INT3_RC("rlwinm", op.ra, op.rs, op.sh32, op.mb32, op.me32, op.rc);
}

void PPUDisAsm::RLWNM(ppu_opcode_t op)
{
	DisAsm_R3_INT2_RC("rlwnm", op.ra, op.rs, op.rb, op.mb32, op.me32, op.rc);
}

void PPUDisAsm::ORI(ppu_opcode_t op)
{
	if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
	if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs);
	DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::ORIS(ppu_opcode_t op)
{
	if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
	DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::XORI(ppu_opcode_t op)
{
	DisAsm_R2_IMM("xori", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::XORIS(ppu_opcode_t op)
{
	DisAsm_R2_IMM("xoris", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::ANDI(ppu_opcode_t op)
{
	DisAsm_R2_IMM("andi.", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::ANDIS(ppu_opcode_t op)
{
	DisAsm_R2_IMM("andis.", op.ra, op.rs, op.uimm16);
}

void PPUDisAsm::RLDICL(ppu_opcode_t op)
{
	const u32 sh = op.sh64;
	const u32 mb = op.mbe64;

	if (sh == 0)
	{
		DisAsm_R2_INT1_RC("clrldi", op.ra, op.rs, mb, op.rc);
	}
	else if (mb == 0)
	{
		DisAsm_R2_INT1_RC("rotldi", op.ra, op.rs, sh, op.rc);
	}
	else if (mb == 64 - sh)
	{
		DisAsm_R2_INT1_RC("srdi", op.ra, op.rs, mb, op.rc);
	}
	else
	{
		DisAsm_R2_INT2_RC("rldicl", op.ra, op.rs, sh, mb, op.rc);
	}
}

void PPUDisAsm::RLDICR(ppu_opcode_t op)
{
	const u32 sh = op.sh64;
	const u32 me = op.mbe64;

	DisAsm_R2_INT2_RC("rldicr", op.ra, op.rs, sh, me, op.rc);
}

void PPUDisAsm::RLDIC(ppu_opcode_t op)
{
	const u32 sh = op.sh64;
	const u32 mb = op.mbe64;

	DisAsm_R2_INT2_RC("rldic", op.ra, op.rs, sh, mb, op.rc);
}

void PPUDisAsm::RLDIMI(ppu_opcode_t op)
{
	const u32 sh = op.sh64;
	const u32 mb = op.mbe64;

	DisAsm_R2_INT2_RC("rldimi", op.ra, op.rs, sh, mb, op.rc);
}

void PPUDisAsm::RLDCL(ppu_opcode_t op)
{
	const u32 mb = op.mbe64;

	DisAsm_R3_INT2_RC("rldcl", op.ra, op.rs, op.rb, mb, 0, op.rc);
}

void PPUDisAsm::RLDCR(ppu_opcode_t op)
{
	const u32 me = op.mbe64;

	DisAsm_R3_INT2_RC("rldcr", op.ra, op.rs, op.rb, me, 0, op.rc);
}

void PPUDisAsm::CMP(ppu_opcode_t op)
{
	DisAsm_CR1_R2(op.l10 ? "cmpd" : "cmpw", op.crfd, op.ra, op.rb);
}

void PPUDisAsm::TW(ppu_opcode_t op)
{
	DisAsm_INT1_R2("tw", op.bo, op.ra, op.rb);
}

void PPUDisAsm::LVSL(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvsl", op.vd, op.ra, op.rb);
}

void PPUDisAsm::LVEBX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvebx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::SUBFC(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("subfc", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::ADDC(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("addc", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::MULHDU(ppu_opcode_t op)
{
	DisAsm_R3_RC("mulhdu", op.rd, op.ra, op.rb, op.rc);
}

void PPUDisAsm::MULHWU(ppu_opcode_t op)
{
	DisAsm_R3_RC("mulhwu", op.rd, op.ra, op.rb, op.rc);
}

void PPUDisAsm::MFOCRF(ppu_opcode_t op)
{
	if (op.l11)
	{
		DisAsm_R1_IMM("mfocrf", op.rd, op.crm);
	}
	else
	{
		DisAsm_R1("mfcr", op.rd);
	}
}

void PPUDisAsm::LWARX(ppu_opcode_t op)
{
	DisAsm_R3("lwarx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LDX(ppu_opcode_t op)
{
	DisAsm_R3("ldx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LWZX(ppu_opcode_t op)
{
	DisAsm_R3("lwzx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::SLW(ppu_opcode_t op)
{
	DisAsm_R3_RC("slw", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::CNTLZW(ppu_opcode_t op)
{
	DisAsm_R2_RC("cntlzw", op.ra, op.rs, op.rc);
}

void PPUDisAsm::SLD(ppu_opcode_t op)
{
	DisAsm_R3_RC("sld", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::AND(ppu_opcode_t op)
{
	DisAsm_R3_RC("and", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::CMPL(ppu_opcode_t op)
{
	DisAsm_CR1_R2(op.l10 ? "cmpld" : "cmplw", op.crfd, op.ra, op.rb);
}

void PPUDisAsm::LVSR(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvsr", op.vd, op.ra, op.rb);
}

void PPUDisAsm::LVEHX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvehx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::SUBF(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("subf", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::LDUX(ppu_opcode_t op)
{
	DisAsm_R3("ldux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::DCBST(ppu_opcode_t op)
{
	DisAsm_R2("dcbst", op.ra, op.rb);
}

void PPUDisAsm::LWZUX(ppu_opcode_t op)
{
	DisAsm_R3("lwzux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::CNTLZD(ppu_opcode_t op)
{
	DisAsm_R2_RC("cntlzd", op.ra, op.rs, op.rc);
}

void PPUDisAsm::ANDC(ppu_opcode_t op)
{
	DisAsm_R3_RC("andc", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::TD(ppu_opcode_t op)
{
	DisAsm_INT1_R2("td", op.bo, op.ra, op.rb);
}

void PPUDisAsm::LVEWX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvewx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::MULHD(ppu_opcode_t op)
{
	DisAsm_R3_RC("mulhd", op.rd, op.ra, op.rb, op.rc);
}

void PPUDisAsm::MULHW(ppu_opcode_t op)
{
	DisAsm_R3_RC("mulhw", op.rd, op.ra, op.rb, op.rc);
}

void PPUDisAsm::LDARX(ppu_opcode_t op)
{
	DisAsm_R3("ldarx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::DCBF(ppu_opcode_t op)
{
	DisAsm_R2("dcbf", op.ra, op.rb);
}

void PPUDisAsm::LBZX(ppu_opcode_t op)
{
	DisAsm_R3("lbzx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LVX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::NEG(ppu_opcode_t op)
{
	DisAsm_R2_OE_RC("neg", op.rd, op.ra, op.oe, op.rc);
}

void PPUDisAsm::LBZUX(ppu_opcode_t op)
{
	DisAsm_R3("lbzux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::NOR(ppu_opcode_t op)
{
	if (op.rs == op.rb)
	{
		DisAsm_R2_RC("not", op.ra, op.rs, op.rc);
	}
	else
	{
		DisAsm_R3_RC("nor", op.ra, op.rs, op.rb, op.rc);
	}
}

void PPUDisAsm::STVEBX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvebx", op.vs, op.ra, op.rb);
}

void PPUDisAsm::SUBFE(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("subfe", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::ADDE(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("adde", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::MTOCRF(ppu_opcode_t op)
{
	if (op.l11)
	{
		DisAsm_INT1_R1("mtocrf", op.crm, op.rs);
	}
	else
	{
		DisAsm_INT1_R1("mtcrf", op.crm, op.rs);
	}
}

void PPUDisAsm::STDX(ppu_opcode_t op)
{
	DisAsm_R3("stdx.", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STWCX(ppu_opcode_t op)
{
	DisAsm_R3("stwcx.", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STWX(ppu_opcode_t op)
{
	DisAsm_R3("stwx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STVEHX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvehx", op.vs, op.ra, op.rb);
}

void PPUDisAsm::STDUX(ppu_opcode_t op)
{
	DisAsm_R3("stdux", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STWUX(ppu_opcode_t op)
{
	DisAsm_R3("stwux", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STVEWX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvewx", op.vs, op.ra, op.rb);
}

void PPUDisAsm::SUBFZE(ppu_opcode_t op)
{
	DisAsm_R2_OE_RC("subfze", op.rd, op.ra, op.oe, op.rc);
}

void PPUDisAsm::ADDZE(ppu_opcode_t op)
{
	DisAsm_R2_OE_RC("addze", op.rd, op.ra, op.oe, op.rc);
}

void PPUDisAsm::STDCX(ppu_opcode_t op)
{
	DisAsm_R3("stdcx.", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STBX(ppu_opcode_t op)
{
	DisAsm_R3("stbx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STVX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::SUBFME(ppu_opcode_t op)
{
	DisAsm_R2_OE_RC("subfme", op.rd, op.ra, op.oe, op.rc);
}

void PPUDisAsm::MULLD(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("mulld", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::ADDME(ppu_opcode_t op)
{
	DisAsm_R2_OE_RC("addme", op.rd, op.ra, op.oe, op.rc);
}

void PPUDisAsm::MULLW(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("mullw", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::DCBTST(ppu_opcode_t op)
{
	DisAsm_R3("dcbtst", op.ra, op.rb, op.bo);
}

void PPUDisAsm::STBUX(ppu_opcode_t op)
{
	DisAsm_R3("stbux", op.rs, op.ra, op.rb);
}

void PPUDisAsm::ADD(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("add", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::DCBT(ppu_opcode_t op)
{
	DisAsm_R2("dcbt", op.ra, op.rb);
}

void PPUDisAsm::LHZX(ppu_opcode_t op)
{
	DisAsm_R3("lhzx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::EQV(ppu_opcode_t op)
{
	DisAsm_R3_RC("eqv", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::ECIWX(ppu_opcode_t op)
{
	DisAsm_R3("eciwx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LHZUX(ppu_opcode_t op)
{
	DisAsm_R3("lhzux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::XOR(ppu_opcode_t op)
{
	DisAsm_R3_RC("xor", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::MFSPR(ppu_opcode_t op)
{
	const u32 n = (op.spr >> 5) | ((op.spr & 0x1f) << 5);
	switch (n)
	{
	case 0x001: DisAsm_R1("mfxer", op.rd); break;
	case 0x008: DisAsm_R1("mflr", op.rd); break;
	case 0x009: DisAsm_R1("mfctr", op.rd); break;
	default: DisAsm_R1_IMM("mfspr", op.rd, op.spr); break;
	}
}

void PPUDisAsm::LWAX(ppu_opcode_t op)
{
	DisAsm_R3("lwax", op.rd, op.ra, op.rb);
}

void PPUDisAsm::DST(ppu_opcode_t op)
{
	DisAsm_R2("dst(t)", op.ra, op.rb);
}

void PPUDisAsm::LHAX(ppu_opcode_t op)
{
	DisAsm_R3("lhax", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LVXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvxl", op.vd, op.ra, op.rb);
}

void PPUDisAsm::MFTB(ppu_opcode_t op)
{
	const u32 n = (op.spr >> 5) | ((op.spr & 0x1f) << 5);
	switch (n)
	{
	case 268: DisAsm_R1("mftb", op.rd); break;
	case 269: DisAsm_R1("mftbu", op.rd); break;
	default: DisAsm_R1_IMM("mftb", op.rd, op.spr); break;
	}
}

void PPUDisAsm::LWAUX(ppu_opcode_t op)
{
	DisAsm_R3("lwaux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::DSTST(ppu_opcode_t op)
{
	DisAsm_R2("dstst(t)", op.ra, op.rb);
}

void PPUDisAsm::LHAUX(ppu_opcode_t op)
{
	DisAsm_R3("lhaux", op.rd, op.ra, op.rb);
}

void PPUDisAsm::STHX(ppu_opcode_t op)
{
	DisAsm_R3("sthx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::ORC(ppu_opcode_t op)
{
	DisAsm_R3_RC("orc", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::ECOWX(ppu_opcode_t op)
{
	DisAsm_R3("ecowx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STHUX(ppu_opcode_t op)
{
	DisAsm_R3("sthux", op.rs, op.ra, op.rb);
}

void PPUDisAsm::OR(ppu_opcode_t op)
{
	if (op.rs == op.rb)
	{
		switch (op.opcode)
		{
		case 0x7f9ce378: return Write("db8cyc");
		case 0x7fbdeb78: return Write("db10cyc");
		case 0x7fdef378: return Write("db12cyc");
		case 0x7ffffb78: return Write("db16cyc");
		default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc);
		}
	}
	else
	{
		DisAsm_R3_RC("or", op.ra, op.rs, op.rb, op.rc);
	}
}

void PPUDisAsm::DIVDU(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("divdu", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::DIVWU(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("divwu", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::MTSPR(ppu_opcode_t op)
{
	const u32 n = (op.spr & 0x1f) + ((op.spr >> 5) & 0x1f);

	switch (n)
	{
	case 0x001: DisAsm_R1("mtxer", op.rs); break;
	case 0x008: DisAsm_R1("mtlr", op.rs); break;
	case 0x009: DisAsm_R1("mtctr", op.rs); break;
	default: DisAsm_IMM_R1("mtspr", op.spr, op.rs); break;
	}
}

void PPUDisAsm::DCBI(ppu_opcode_t op)
{
	DisAsm_R2("dcbi", op.ra, op.rb);
}

void PPUDisAsm::NAND(ppu_opcode_t op)
{
	DisAsm_R3_RC("nand", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::STVXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvxl", op.vs, op.ra, op.rb);
}

void PPUDisAsm::DIVD(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("divd", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::DIVW(ppu_opcode_t op)
{
	DisAsm_R3_OE_RC("divw", op.rd, op.ra, op.rb, op.oe, op.rc);
}

void PPUDisAsm::LVLX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvlx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::LDBRX(ppu_opcode_t op)
{
	DisAsm_R3("ldbrx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LSWX(ppu_opcode_t op)
{
	DisAsm_R3("lswx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LWBRX(ppu_opcode_t op)
{
	DisAsm_R3("lwbrx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LFSX(ppu_opcode_t op)
{
	DisAsm_F1_R2("lfsx", op.frd, op.ra, op.rb);
}

void PPUDisAsm::SRW(ppu_opcode_t op)
{
	DisAsm_R3_RC("srw", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::SRD(ppu_opcode_t op)
{
	DisAsm_R3_RC("srd", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::LVRX(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvrx", op.vd, op.ra, op.rb);
}

void PPUDisAsm::LSWI(ppu_opcode_t op)
{
	DisAsm_R2_INT1("lswi", op.rd, op.ra, op.rb);
}

void PPUDisAsm::LFSUX(ppu_opcode_t op)
{
	DisAsm_F1_R2("lfsux", op.frd, op.ra, op.rb);
}

void PPUDisAsm::SYNC(ppu_opcode_t op)
{
	Write(op.l10 ? "lwsync" : "sync");
}

void PPUDisAsm::LFDX(ppu_opcode_t op)
{
	DisAsm_F1_R2("lfdx", op.frd, op.ra, op.rb);
}

void PPUDisAsm::LFDUX(ppu_opcode_t op)
{
	DisAsm_F1_R2("lfdux", op.frd, op.ra, op.rb);
}

void PPUDisAsm::STVLX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvlx", op.vs, op.ra, op.rb);
}

void PPUDisAsm::STDBRX(ppu_opcode_t op)
{
	DisAsm_R3("stdbrx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STSWX(ppu_opcode_t op)
{
	DisAsm_R3("swswx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STWBRX(ppu_opcode_t op)
{
	DisAsm_R3("stwbrx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::STFSX(ppu_opcode_t op)
{
	DisAsm_F1_R2("stfsx", op.frs, op.ra, op.rb);
}

void PPUDisAsm::STVRX(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvrx", op.vs, op.ra, op.rb);
}

void PPUDisAsm::STFSUX(ppu_opcode_t op)
{
	DisAsm_F1_R2("stfsux", op.frs, op.ra, op.rb);
}

void PPUDisAsm::STSWI(ppu_opcode_t op)
{
	DisAsm_R2_INT1("stswi", op.rd, op.ra, op.rb);
}

void PPUDisAsm::STFDX(ppu_opcode_t op)
{
	DisAsm_F1_R2("stfdx", op.frs, op.ra, op.rb);
}

void PPUDisAsm::STFDUX(ppu_opcode_t op)
{
	DisAsm_F1_R2("stfdux", op.frs, op.ra, op.rb);
}

void PPUDisAsm::LVLXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvlxl", op.vd, op.ra, op.rb);
}

void PPUDisAsm::LHBRX(ppu_opcode_t op)
{
	DisAsm_R3("lhbrx", op.rd, op.ra, op.rb);
}

void PPUDisAsm::SRAW(ppu_opcode_t op)
{
	DisAsm_R3_RC("sraw", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::SRAD(ppu_opcode_t op)
{
	DisAsm_R3_RC("srad", op.ra, op.rs, op.rb, op.rc);
}

void PPUDisAsm::LVRXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("lvrxl", op.vd, op.ra, op.rb);
}

void PPUDisAsm::DSS(ppu_opcode_t op)
{
	Write("dss()");
}

void PPUDisAsm::SRAWI(ppu_opcode_t op)
{
	DisAsm_R2_INT1_RC("srawi", op.ra, op.rs, op.sh32, op.rc);
}

void PPUDisAsm::SRADI(ppu_opcode_t op)
{
	DisAsm_R2_INT1_RC("sradi", op.ra, op.rs, op.sh64, op.rc);
}

void PPUDisAsm::EIEIO(ppu_opcode_t op)
{
	Write("eieio");
}

void PPUDisAsm::STVLXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvlxl", op.vs, op.ra, op.rb);
}

void PPUDisAsm::STHBRX(ppu_opcode_t op)
{
	DisAsm_R3("sthbrx", op.rs, op.ra, op.rb);
}

void PPUDisAsm::EXTSH(ppu_opcode_t op)
{
	DisAsm_R2_RC("extsh", op.ra, op.rs, op.rc);
}

void PPUDisAsm::STVRXL(ppu_opcode_t op)
{
	DisAsm_V1_R2("stvrxl", op.vs, op.ra, op.rb);
}

void PPUDisAsm::EXTSB(ppu_opcode_t op)
{
	DisAsm_R2_RC("extsb", op.ra, op.rs, op.rc);
}

void PPUDisAsm::STFIWX(ppu_opcode_t op)
{
	DisAsm_F1_R2("stfiwx", op.frs, op.ra, op.rb);
}

void PPUDisAsm::EXTSW(ppu_opcode_t op)
{
	DisAsm_R2_RC("extsw", op.ra, op.rs, op.rc);
}

void PPUDisAsm::ICBI(ppu_opcode_t op)
{
	DisAsm_R2("icbi", op.ra, op.rb);
}

void PPUDisAsm::DCBZ(ppu_opcode_t op)
{
	DisAsm_R2("dcbz", op.ra, op.rb);
}

void PPUDisAsm::LWZ(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lwz", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::LWZU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lwzu", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::LBZ(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lbz", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::LBZU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lbzu", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::STW(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stw", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::STWU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stwu", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::STB(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stb", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::STBU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stbu", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LHZ(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lhz", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LHZU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lhzu", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LHA(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lha", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LHAU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lhau", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::STH(ppu_opcode_t op)
{
	DisAsm_R2_IMM("sth", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::STHU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("sthu", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LMW(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lmw", op.rd, op.ra, op.simm16);
}

void PPUDisAsm::STMW(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stmw", op.rs, op.ra, op.simm16);
}

void PPUDisAsm::LFS(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("lfs", op.frd, op.simm16, op.ra);
}

void PPUDisAsm::LFSU(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("lfsu", op.frd, op.simm16, op.ra);
}

void PPUDisAsm::LFD(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("lfd", op.frd, op.simm16, op.ra);
}

void PPUDisAsm::LFDU(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("lfdu", op.frd, op.simm16, op.ra);
}

void PPUDisAsm::STFS(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("stfs", op.frs, op.simm16, op.ra);
}

void PPUDisAsm::STFSU(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("stfsu", op.frs, op.simm16, op.ra);
}

void PPUDisAsm::STFD(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("stfd", op.frs, op.simm16, op.ra);
}

void PPUDisAsm::STFDU(ppu_opcode_t op)
{
	DisAsm_F1_IMM_R1("stfdu", op.frs, op.simm16, op.ra);
}

void PPUDisAsm::LD(ppu_opcode_t op)
{
	DisAsm_R2_IMM("ld", op.rd, op.ra, op.ds * 4);
}

void PPUDisAsm::LDU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("ldu", op.rd, op.ra, op.ds * 4);
}

void PPUDisAsm::LWA(ppu_opcode_t op)
{
	DisAsm_R2_IMM("lwa", op.rd, op.ra, op.ds * 4);
}

void PPUDisAsm::STD(ppu_opcode_t op)
{
	DisAsm_R2_IMM("std", op.rs, op.ra, op.ds * 4);
}

void PPUDisAsm::STDU(ppu_opcode_t op)
{
	DisAsm_R2_IMM("stdu", op.rs, op.ra, op.ds * 4);
}

void PPUDisAsm::FDIVS(ppu_opcode_t op)
{
	DisAsm_F3_RC("fdivs", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FSUBS(ppu_opcode_t op)
{
	DisAsm_F3_RC("fsubs", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FADDS(ppu_opcode_t op)
{
	DisAsm_F3_RC("fadds", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FSQRTS(ppu_opcode_t op)
{
	DisAsm_F2_RC("fsqrts", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FRES(ppu_opcode_t op)
{
	DisAsm_F2_RC("fres", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FMULS(ppu_opcode_t op)
{
	DisAsm_F3_RC("fmuls", op.frd, op.fra, op.frc, op.rc);
}

void PPUDisAsm::FMADDS(ppu_opcode_t op)
{
	DisAsm_F4_RC("fmadds", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FMSUBS(ppu_opcode_t op)
{
	DisAsm_F4_RC("fmsubs", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FNMSUBS(ppu_opcode_t op)
{
	DisAsm_F4_RC("fnmsubs", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FNMADDS(ppu_opcode_t op)
{
	DisAsm_F4_RC("fnmadds", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::MTFSB1(ppu_opcode_t op)
{
	Write(fmt::format("mtfsb1%s %d", op.rc ? "." : "", op.crbd));
}

void PPUDisAsm::MCRFS(ppu_opcode_t op)
{
	DisAsm_CR2("mcrfs", op.crfd, op.crfs);
}

void PPUDisAsm::MTFSB0(ppu_opcode_t op)
{
	Write(fmt::format("mtfsb0%s %d", op.rc ? "." : "", op.crbd));
}

void PPUDisAsm::MTFSFI(ppu_opcode_t op)
{
	Write(fmt::format("mtfsfi%s cr%d,%d,%d", op.rc ? "." : "", op.crfd, op.i, op.l15));
}

void PPUDisAsm::MFFS(ppu_opcode_t op)
{
	DisAsm_F1_RC("mffs", op.frd, op.rc);
}

void PPUDisAsm::MTFSF(ppu_opcode_t op)
{
	Write(fmt::format("mtfsf%s %d,f%d,%d,%d", op.rc ? "." : "", op.rc, op.flm, op.frb, op.l6, op.l15));
}

void PPUDisAsm::FCMPU(ppu_opcode_t op)
{
	DisAsm_CR1_F2("fcmpu", op.crfd, op.fra, op.frb);
}

void PPUDisAsm::FRSP(ppu_opcode_t op)
{
	DisAsm_F2_RC("frsp", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FCTIW(ppu_opcode_t op)
{
	DisAsm_F2_RC("fctiw", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FCTIWZ(ppu_opcode_t op)
{
	DisAsm_F2_RC("fctiwz", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FDIV(ppu_opcode_t op)
{
	DisAsm_F3_RC("fdiv", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FSUB(ppu_opcode_t op)
{
	DisAsm_F3_RC("fsub", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FADD(ppu_opcode_t op)
{
	DisAsm_F3_RC("fadd", op.frd, op.fra, op.frb, op.rc);
}

void PPUDisAsm::FSQRT(ppu_opcode_t op)
{
	DisAsm_F2_RC("fsqrt", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FSEL(ppu_opcode_t op)
{
	DisAsm_F4_RC("fsel", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FMUL(ppu_opcode_t op)
{
	DisAsm_F3_RC("fmul", op.frd, op.fra, op.frc, op.rc);
}

void PPUDisAsm::FRSQRTE(ppu_opcode_t op)
{
	DisAsm_F2_RC("frsqrte", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FMSUB(ppu_opcode_t op)
{
	DisAsm_F4_RC("fmsub", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FMADD(ppu_opcode_t op)
{
	DisAsm_F4_RC("fmadd", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FNMSUB(ppu_opcode_t op)
{
	DisAsm_F4_RC("fnmsub", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FNMADD(ppu_opcode_t op)
{
	DisAsm_F4_RC("fnmadd", op.frd, op.fra, op.frc, op.frb, op.rc);
}

void PPUDisAsm::FCMPO(ppu_opcode_t op)
{
	DisAsm_F3("fcmpo", op.crfd, op.fra, op.frb);
}

void PPUDisAsm::FNEG(ppu_opcode_t op)
{
	DisAsm_F2_RC("fneg", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FMR(ppu_opcode_t op)
{
	DisAsm_F2_RC("fmr", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FNABS(ppu_opcode_t op)
{
	DisAsm_F2_RC("fnabs", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FABS(ppu_opcode_t op)
{
	DisAsm_F2_RC("fabs", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FCTID(ppu_opcode_t op)
{
	DisAsm_F2_RC("fctid", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FCTIDZ(ppu_opcode_t op)
{
	DisAsm_F2_RC("fctidz", op.frd, op.frb, op.rc);
}

void PPUDisAsm::FCFID(ppu_opcode_t op)
{
	DisAsm_F2_RC("fcfid", op.frd, op.frb, op.rc);
}

extern std::vector<std::string> g_ppu_function_names;

void PPUDisAsm::UNK(ppu_opcode_t op)
{
	if (op.opcode == dump_pc && ppu_function_manager::addr)
	{
		// HLE function index
		const u32 index = (dump_pc - ppu_function_manager::addr) / 8;

		if (index < ppu_function_manager::get().size())
		{
			Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
			return;
		}
	}

	Write(fmt::format("Unknown/Illegal opcode! (0x%08x)", op.opcode));
}
