// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    ppcfe.c

    Front-end for PowerPC recompiler

***************************************************************************/

#include "emu.h"
#include "ppcfe.h"
#include "ppccom.h"


//**************************************************************************
//  MACROS
//**************************************************************************

#define GPR_USED(desc, x)           do { (desc).regin[0] |= REGFLAG_R(x); } while (0)
#define GPR_USED_OR_ZERO(desc, x)   do { (desc).regin[0] |= ((x) == 0 ? 0 : REGFLAG_R(x)); } while (0)
#define GPR_MODIFIED(desc, x)       do { (desc).regout[0] |= REGFLAG_R(x); } while (0)

#define FPR_USED(desc, x)           do { (desc).regin[1] |= REGFLAG_FR(x); } while (0)
#define FPR_MODIFIED(desc, x)       do { (desc).regout[1] |= REGFLAG_FR(x); } while (0)

#define CR_USED(desc, x)            do { (desc).regin[2] |= REGFLAG_CR(x); } while (0)
#define CR_BIT_USED(desc, x)        do { (desc).regin[2] |= REGFLAG_CR_BIT(x); } while (0)
#define CR_MODIFIED(desc, x)        do { (desc).regout[2] |= REGFLAG_CR(x); } while (0)
#define CR_BIT_MODIFIED(desc, x)    do { (desc).regout[2] |= REGFLAG_CR_BIT(x); } while (0)

#define XER_CA_USED(desc)           do { (desc).regin[3] |= REGFLAG_XER_CA; } while (0)
#define XER_OV_USED(desc)           do { (desc).regin[3] |= REGFLAG_XER_OV; } while (0)
#define XER_SO_USED(desc)           do { (desc).regin[3] |= REGFLAG_XER_SO; } while (0)
#define XER_COUNT_USED(desc)        do { (desc).regin[3] |= REGFLAG_XER_COUNT; } while (0)
#define XER_CA_MODIFIED(desc)       do { (desc).regout[3] |= REGFLAG_XER_CA; } while (0)
#define XER_OV_MODIFIED(desc)       do { (desc).regout[3] |= REGFLAG_XER_OV; } while (0)
#define XER_SO_MODIFIED(desc)       do { (desc).regout[3] |= REGFLAG_XER_SO; } while (0)
#define XER_COUNT_MODIFIED(desc)    do { (desc).regout[3] |= REGFLAG_XER_COUNT; } while (0)

#define CTR_USED(desc)              do { (desc).regin[3] |= REGFLAG_CTR; } while (0)
#define CTR_MODIFIED(desc)          do { (desc).regout[3] |= REGFLAG_CTR; } while (0)
#define LR_USED(desc)               do { (desc).regin[3] |= REGFLAG_LR; } while (0)
#define LR_MODIFIED(desc)           do { (desc).regout[3] |= REGFLAG_LR; } while (0)

#define FPSCR_USED(desc, x)         do { (desc).regin[3] |= REGFLAG_FPSCR(x); } while (0)
#define FPSCR_MODIFIED(desc, x)     do { (desc).regout[3] |= REGFLAG_FPSCR(x); } while (0)



//**************************************************************************
//  PPC FRONTEND
//**************************************************************************

//-------------------------------------------------
//  frontend - constructor
//-------------------------------------------------

ppc_device::frontend::frontend(ppc_device &ppc, uint32_t window_start, uint32_t window_end, uint32_t max_sequence)
	: drc_frontend(ppc, window_start, window_end, max_sequence)
	, m_ppc(ppc)
{
}


//-------------------------------------------------
//  describe - build a description of a single
//  instruction
//-------------------------------------------------

bool ppc_device::frontend::describe(opcode_desc &desc, const opcode_desc *prev)
{
	uint32_t op, opswitch;
	int regnum;

	// compute the physical PC
	if (m_ppc.ppccom_translate_address_internal(TR_FETCH, false, desc.physpc) > 1)
	{
		// uh-oh: a page fault; leave the description empty and just if this is the first instruction, leave it empty and
		// mark as needing to validate; otherwise, just end the sequence here
		desc.flags |= OPFLAG_VALIDATE_TLB | OPFLAG_CAN_CAUSE_EXCEPTION | OPFLAG_COMPILER_PAGE_FAULT | OPFLAG_VIRTUAL_NOOP | OPFLAG_END_SEQUENCE;
		return true;
	}

	// fetch the opcode
	op = desc.opptr.l[0] = m_ppc.m_pr32(desc.physpc);

	// all instructions are 4 bytes and default to a single cycle each
	desc.length = 4;
	desc.cycles = 1;

	// parse the instruction
	opswitch = op >> 26;
	switch (opswitch)
	{
		case 0x02:  // TDI - 64-bit only
		case 0x1e:  // 0x1e group - 64-bit only
		case 0x3a:  // 0x3a group - 64-bit only
		case 0x3e:  // 0x3e group - 64-bit only
			return false;

		case 0x03:  // TWI
			GPR_USED(desc, G_RA(op));
			desc.flags |= OPFLAG_CAN_CAUSE_EXCEPTION;
			if (is_603_class())
				desc.cycles = 2;    // 603
			return true;

		case 0x07:  // MULLI
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			if (is_403_class())
				desc.cycles = 4;    // 4XX
			else if (is_601_class())
				desc.cycles = 5;    // 601
			else if (is_603_class())
				desc.cycles = 2;    // 603: 2-3
			else
				desc.cycles = 2;    // ???
			return true;

		case 0x09:  // DOZI (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			return true;

		case 0x0e:  // ADDI
		case 0x0f:  // ADDIS
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			return true;

		case 0x0a:  // CMPLI
		case 0x0b:  // CMPI
			GPR_USED(desc, G_RA(op));
			XER_SO_USED(desc);
			CR_MODIFIED(desc, G_CRFD(op));
			return true;

		case 0x08:  // SUBFIC
		case 0x0c:  // ADDIC
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_CA_MODIFIED(desc);
			return true;

		case 0x0d:  // ADDIC.
			GPR_USED(desc, G_RA(op));
			XER_SO_USED(desc);
			GPR_MODIFIED(desc, G_RT(op));
			XER_CA_MODIFIED(desc);
			CR_MODIFIED(desc, 0);
			return true;

		case 0x10:  // BCx
			if (!(G_BO(op) & 0x10))
			{
				CR_BIT_USED(desc, G_BI(op));
				// branch folding
				if (prev == nullptr || prev->regout[2] == 0)
					desc.cycles = 0;
			}
			if (!(G_BO(op) & 0x04))
			{
				CTR_USED(desc);
				CTR_MODIFIED(desc);
			}
			if (op & M_LK)
				LR_MODIFIED(desc);
			if ((G_BO(op) & 0x14) == 0x14)
				desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
			else
				desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH;
			desc.targetpc = (int16_t)(G_BD(op) << 2) + ((op & M_AA) ? 0 : desc.pc);
			if (desc.targetpc == desc.pc && desc.cycles == 0)
				desc.cycles = 1;
			return true;

		case 0x11:  // SC
			if (!(m_ppc.m_cap & (PPCCAP_OEA | PPCCAP_4XX)))
				return false;
			desc.flags |= OPFLAG_WILL_CAUSE_EXCEPTION;
			if (is_601_class())
				desc.cycles = 16;   // 601
			else if (is_603_class())
				desc.cycles = 3;    // 603
			else
				desc.cycles = 3;    // ???
			return true;

		case 0x12:  // Bx
			if (op & M_LK)
				LR_MODIFIED(desc);
			desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
			desc.targetpc = ((int32_t)(G_LI(op) << 8) >> 6) + ((op & M_AA) ? 0 : desc.pc);
			// branch folding
			if (desc.targetpc != desc.pc)
				desc.cycles = 0;
			return true;

		case 0x13:  // 0x13 group
			return describe_13(op, desc, prev);

		case 0x14:  // RLWIMIx
			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x15:  // RLWINMx
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x17:  // RLWNMx
			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x18:  // ORI
		case 0x19:  // ORIS
		case 0x1a:  // XORI
		case 0x1b:  // XORIS
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			return true;

		case 0x1c:  // ANDI.
		case 0x1d:  // ANDIS.
			GPR_USED(desc, G_RS(op));
			XER_SO_USED(desc);
			GPR_MODIFIED(desc, G_RA(op));
			CR_MODIFIED(desc, 0);
			return true;

		case 0x1f:  // 0x1f group
			return describe_1f(op, desc, prev);

		case 0x20:  // LWZ
		case 0x22:  // LBZ
		case 0x28:  // LHZ
		case 0x2a:  // LHA
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x21:  // LWZU
		case 0x23:  // LBZU
		case 0x29:  // LHZU
		case 0x2b:  // LHAU
			if (G_RA(op) == 0 || G_RA(op) == G_RD(op))
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			GPR_MODIFIED(desc, G_RA(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x24:  // STW
		case 0x26:  // STB
		case 0x2c:  // STH
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x25:  // STWU
		case 0x27:  // STBU
		case 0x2d:  // STHU
			if (G_RA(op) == 0)
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x2e:  // LMW
			GPR_USED_OR_ZERO(desc, G_RA(op));

			for (regnum = G_RD(op); regnum < 32; regnum++)
			{
				if (regnum != G_RA(op) || ((m_ppc.m_cap & PPCCAP_4XX) && regnum == 31))
					GPR_MODIFIED(desc, regnum);
			}
			desc.flags |= OPFLAG_READS_MEMORY;
			desc.cycles = 32 - G_RD(op);
			return true;

		case 0x2f:  // STMW
			GPR_USED_OR_ZERO(desc, G_RA(op));
			for (regnum = G_RS(op); regnum < 32; regnum++)
				GPR_USED(desc, regnum);
			desc.flags |= OPFLAG_WRITES_MEMORY;
			desc.cycles = 32 - G_RS(op);
			return true;

		case 0x30:  // LFS
		case 0x32:  // LFD
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			FPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x31:  // LFSU
		case 0x33:  // LFDU
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			if (G_RA(op) == 0)
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RA(op));
			FPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x34:  // STFS
		case 0x36:  // STFD
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			FPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x35:  // STFSU
		case 0x37:  // STFDU
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			if (G_RA(op) == 0)
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RA(op));
			FPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x3b:  // 0x3b group
			return describe_3b(op, desc, prev);

		case 0x3f:  // 0x3f group
			return describe_3f(op, desc, prev);
	}

	return false;
}


/*-------------------------------------------------
    describe_instruction_13 - build a
    description of a single instruction in the
    0x13 group
-------------------------------------------------*/

bool ppc_device::frontend::describe_13(uint32_t op, opcode_desc &desc, const opcode_desc *prev)
{
	uint32_t opswitch = (op >> 1) & 0x3ff;

	switch (opswitch)
	{
		case 0x000: // MTCRF
			CR_USED(desc, G_CRFS(op));
			CR_MODIFIED(desc, G_CRFD(op));
			// CR logical folding
			if (prev == nullptr || prev->regout[2] == 0)
				desc.cycles = 0;
			return true;

		case 0x010: // BCLRx
			LR_USED(desc);
			if (!(G_BO(op) & 0x10))
				CR_BIT_USED(desc, G_BI(op));
			if (!(G_BO(op) & 0x04))
			{
				CTR_USED(desc);
				CTR_MODIFIED(desc);
			}
			if (op & M_LK)
				LR_MODIFIED(desc);
			if ((G_BO(op) & 0x14) == 0x14)
				desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
			else
				desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH;
			desc.targetpc = BRANCH_TARGET_DYNAMIC;
			return true;

		case 0x021: // CRNOR
		case 0x081: // CRANDC
		case 0x0c1: // CRXOR
		case 0x0e1: // CRNAND
		case 0x101: // CRAND
		case 0x121: // CREQV
		case 0x1a1: // CRORC
		case 0x1c1: // CROR
			CR_BIT_USED(desc, G_CRBA(op));
			CR_BIT_USED(desc, G_CRBB(op));
			CR_BIT_MODIFIED(desc, G_CRBD(op));
			// CR logical folding
			if (prev == nullptr || prev->regout[2] == 0)
				desc.cycles = 0;
			return true;

		case 0x032: // RFI
			if (!(m_ppc.m_cap & (PPCCAP_OEA | PPCCAP_4XX)))
				return false;
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CHANGE_MODES | OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE | OPFLAG_CAN_CAUSE_EXCEPTION;
			desc.targetpc = BRANCH_TARGET_DYNAMIC;
			if (is_601_class())
				desc.cycles = 13;   // 601
			else if (is_603_class())
				desc.cycles = 3;    // 603
			else
				desc.cycles = 3;    // ???
			return true;

		case 0x033: // RFCI
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CHANGE_MODES | OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE | OPFLAG_CAN_CAUSE_EXCEPTION;
			desc.targetpc = BRANCH_TARGET_DYNAMIC;
			return true;

		case 0x096: // ISYNC
			if (!(m_ppc.m_cap & (PPCCAP_VEA | PPCCAP_4XX)))
				return false;
			if (is_601_class())
				desc.cycles = 6;    // 601
			return true;

		case 0x210: // BCCTRx
			CTR_USED(desc);
			if (!(G_BO(op) & 0x10))
				CR_BIT_USED(desc, G_BI(op));
			if (!(G_BO(op) & 0x04))
				return false;
			if (op & M_LK)
				LR_MODIFIED(desc);
			if ((G_BO(op) & 0x14) == 0x14)
				desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
			else
				desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH;
			desc.targetpc = BRANCH_TARGET_DYNAMIC;
			return true;
	}

	return false;
}


/*-------------------------------------------------
    describe_instruction_1f - build a
    description of a single instruction in the
    0x1f group
-------------------------------------------------*/

bool ppc_device::frontend::describe_1f(uint32_t op, opcode_desc &desc, const opcode_desc *prev)
{
	uint32_t opswitch = (op >> 1) & 0x3ff;
	int spr, regnum;

	switch (opswitch)
	{
		case 0x009: // MULHDUx - 64-bit only
		case 0x015: // LDX - 64-bit only
		case 0x01b: // SLDx - 64-bit only
		case 0x035: // LDUX - 64-bit only
		case 0x03a: // CNTLZDx - 64-bit only
		case 0x044: // TD - 64-bit only
		case 0x049: // MULHDx - 64-bit only
		case 0x054: // LDARX - 64-bit only
		case 0x095: // STDX - 64-bit only
		case 0x0b5: // STDUX - 64-bit only
		case 0x0d6: // STDCX. - 64-bit only
		case 0x0e9: // MULLD - 64-bit only
		case 0x2e9: // MULLDO - 64-bit only
		case 0x155: // LWAX - 64-bit only
		case 0x175: // LWAUX - 64-bit only
		case 0x33a: // SRADIx - 64-bit only
		case 0x33b: // SRADIx - 64-bit only
		case 0x1b2: // SLBIE - 64-bit only
		case 0x1c9: // DIVDUx - 64-bit only
		case 0x3c9: // DIVDUOx - 64-bit only
		case 0x1e9: // DIVDx - 64-bit only
		case 0x3e9: // DIVDOx - 64-bit only
		case 0x1f2: // SLBIA - 64-bit only
		case 0x21b: // SRDx - 64-bit only
		case 0x31a: // SRADx - 64-bit only
		case 0x3da: // EXTSW - 64-bit only
			return false;

		case 0x000: // CMP
		case 0x020: // CMPL
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			XER_SO_USED(desc);
			CR_MODIFIED(desc, G_CRFD(op));
			return true;

		case 0x004: // TW
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_CAN_CAUSE_EXCEPTION;
			if (is_603_class())
				desc.cycles = 2;    // 603
			return true;

		case 0x008: // SUBFCx
		case 0x00a: // ADDCx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x088: // SUBFEx
		case 0x08a: // ADDEx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			XER_CA_USED(desc);
			GPR_MODIFIED(desc, G_RD(op));
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x0c8: // SUBFZEx
		case 0x0ca: // ADDZEx
		case 0x0e8: // SUBFMEx
		case 0x0ea: // ADDMEx
			GPR_USED(desc, G_RA(op));
			XER_CA_USED(desc);
			GPR_MODIFIED(desc, G_RD(op));
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x00b: // MULHWUx
		case 0x04b: // MULHWx
		case 0x0eb: // MULLWx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			if (is_403_class())
				desc.cycles = 4;    // 4XX
			else if (is_601_class())
				desc.cycles = 5;    // 601: 5/9/10
			else if (is_603_class())
				desc.cycles = 2;    // 603: 2,3,4,5,6
			else
				desc.cycles = 2;    // ???
			return true;

		case 0x1cb: // DIVWUx
		case 0x1eb: // DIVWx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			if (is_403_class())
				desc.cycles = 33;   // 4XX
			else if (is_601_class())
				desc.cycles = 36;   // 601
			else if (is_603_class())
				desc.cycles = 37;   // 603
			else
				desc.cycles = 33;   // ???
			return true;

		case 0x028: // SUBFx
		case 0x10a: // ADDx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x208: // SUBFCOx
		case 0x20a: // ADDCOx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x288: // SUBFEOx
		case 0x28a: // ADDEOx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			XER_CA_USED(desc);
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x2c8: // SUBFZEOx
		case 0x2ca: // ADDZEOx
		case 0x2e8: // SUBFMEOx
		case 0x2ea: // ADDMEOx
			GPR_USED(desc, G_RA(op));
			XER_CA_USED(desc);
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x2eb: // MULLWOx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			if (is_403_class())
				desc.cycles = 4;    // 4XX
			else if (is_601_class())
				desc.cycles = 5;    // 601: 5/9/10
			else if (is_603_class())
				desc.cycles = 2;    // 603: 2,3,4,5,6
			else
				desc.cycles = 2;    // ???
			return true;

		case 0x3cb: // DIVWUOx
		case 0x3eb: // DIVWOx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			if (is_403_class())
				desc.cycles = 33;   // 4XX
			else if (is_601_class())
				desc.cycles = 36;   // 601
			else if (is_603_class())
				desc.cycles = 37;   // 603
			else
				desc.cycles = 33;   // ???
			return true;

		case 0x228: // SUBFOx
		case 0x30a: // ADDOx
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x013: // MFCR
			CR_USED(desc, 0);
			CR_USED(desc, 1);
			CR_USED(desc, 2);
			CR_USED(desc, 3);
			CR_USED(desc, 4);
			CR_USED(desc, 5);
			CR_USED(desc, 6);
			CR_USED(desc, 7);
			GPR_MODIFIED(desc, G_RD(op));
			return true;

		case 0x136: // ECIWX
			if (!(m_ppc.m_cap & PPCCAP_VEA))
				return false;
			[[fallthrough]];
		case 0x014: // LWARX
		case 0x017: // LWZX
		case 0x057: // LBZX
		case 0x117: // LHZX
		case 0x157: // LHAX
		case 0x216: // LWBRX
		case 0x316: // LHBRX
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x018: // SLWx
		case 0x01c: // ANDx
		case 0x03c: // ANDCx
		case 0x07c: // NORx
		case 0x11c: // EQVx
		case 0x13c: // XORx
		case 0x19c: // ORCx
		case 0x1bc: // ORx
		case 0x1dc: // NANDx
			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x218: // SRWx
		case 0x318: // SRAWx
			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x01a: // CNTLZWx
		case 0x39a: // EXTSHx
		case 0x3ba: // EXTSBx
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x036: // DCBST
		case 0x056: // DCBF
		case 0x0f6: // DCBTST
		case 0x116: // DCBT
		case 0x2f6: // DCBA
		case 0x3d6: // ICBI
			if (!(m_ppc.m_cap & (PPCCAP_VEA | PPCCAP_4XX)))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			return true;

		case 0x1d6: // DCBI
			if (!(m_ppc.m_cap & (PPCCAP_OEA | PPCCAP_4XX)))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x037: // LWZUX
		case 0x077: // LBZUX
		case 0x137: // LHZUX
		case 0x177: // LHAUX
			if (G_RA(op) == 0 || G_RA(op) == G_RD(op))
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			GPR_MODIFIED(desc, G_RA(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x153: // MFSPR
			GPR_MODIFIED(desc, G_RD(op));
			spr = compute_spr(G_SPR(op));
			if (spr == SPR_LR)
				LR_USED(desc);
			if (spr == SPR_CTR)
				CTR_USED(desc);
			if (spr == SPR_XER)
			{
				XER_COUNT_USED(desc);
				XER_CA_USED(desc);
				XER_OV_USED(desc);
				XER_SO_USED(desc);
			}
			if (spr & 0x010)
				desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			if ((m_ppc.m_cap & PPCCAP_4XX) && spr == SPR4XX_TBLU)
				desc.cycles = POWERPC_COUNT_READ_TBL;
			else if ((m_ppc.m_cap & PPCCAP_VEA) && spr == SPRVEA_TBL_R)
				desc.cycles = POWERPC_COUNT_READ_TBL;
			else if ((m_ppc.m_cap & PPCCAP_OEA) && spr == SPROEA_DEC)
				desc.cycles = POWERPC_COUNT_READ_DEC;
			return true;

		case 0x053: // MFMSR
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION | OPFLAG_CAN_EXPOSE_EXTERNAL_INT;
			if (is_601_class())
				desc.cycles = 2;    // 601
			return true;

		case 0x253: // MFSR
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x293: // MFSRIN
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x173: // MFTB
			if (!(m_ppc.m_cap & PPCCAP_VEA))
				return false;
			GPR_MODIFIED(desc, G_RD(op));
			spr = compute_spr(G_SPR(op));
			if (spr == SPRVEA_TBL_R)
				desc.cycles = POWERPC_COUNT_READ_TBL;
			return true;

		case 0x068: // NEGx
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x268: // NEGOx
			GPR_USED(desc, G_RA(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x090: // MTCRF
			GPR_USED(desc, G_RS(op));
			if (G_CRM(op) & 0x80) CR_MODIFIED(desc, 0);
			if (G_CRM(op) & 0x40) CR_MODIFIED(desc, 1);
			if (G_CRM(op) & 0x20) CR_MODIFIED(desc, 2);
			if (G_CRM(op) & 0x10) CR_MODIFIED(desc, 3);
			if (G_CRM(op) & 0x08) CR_MODIFIED(desc, 4);
			if (G_CRM(op) & 0x04) CR_MODIFIED(desc, 5);
			if (G_CRM(op) & 0x02) CR_MODIFIED(desc, 6);
			if (G_CRM(op) & 0x01) CR_MODIFIED(desc, 7);
			return true;

		case 0x092: // MTMSR
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION | OPFLAG_CAN_CHANGE_MODES | OPFLAG_END_SEQUENCE;
			if (is_601_class())
				desc.cycles = 17;   // 601
			else if (is_603_class())
				desc.cycles = 2;    // 603
			return true;

		case 0x0d2: // MTSR
			if (!(m_ppc.m_cap & PPCCAP_OEA))
				return false;
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x1d3: // MTSPR
			GPR_USED(desc, G_RS(op));
			spr = compute_spr(G_SPR(op));
			if (spr == SPR_LR)
				LR_MODIFIED(desc);
			if (spr == SPR_CTR)
				CTR_MODIFIED(desc);
			if (spr == SPR_XER)
			{
				XER_COUNT_MODIFIED(desc);
				XER_CA_MODIFIED(desc);
				XER_OV_MODIFIED(desc);
				XER_SO_MODIFIED(desc);
			}
			if (spr & 0x010)
				desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x1b6: // ECOWX
			if (!(m_ppc.m_cap & PPCCAP_VEA))
				return false;
			[[fallthrough]];
		case 0x096: // STWCX.
		case 0x097: // STWX
		case 0x0d7: // STBX
		case 0x197: // STHX
		case 0x296: // STWBRX
		case 0x396: // STHBRX
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x0b7: // STWUX
		case 0x0f7: // STBUX
		case 0x1b7: // STHUX
			if (G_RA(op) == 0)
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x0f2: // MTSRIN
			if (!(m_ppc.m_cap & PPCCAP_OEA))
				return false;
			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x132: // TLBIE
			if (!(m_ppc.m_cap & PPCCAP_OEA))
				return false;
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x172: // TLBIA
			if (!(m_ppc.m_cap & PPCCAP_OEA) || (m_ppc.m_cap & PPCCAP_603_MMU))
				return false;
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x3d2: // TLBLD
		case 0x3f2: // TLBLI
			if (!(m_ppc.m_cap & PPCCAP_603_MMU) && !is_602_class())
				return false;
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x200: // MCRXR
			XER_CA_USED(desc);
			XER_OV_USED(desc);
			XER_SO_USED(desc);
			CR_MODIFIED(desc, G_CRFD(op));
			XER_CA_MODIFIED(desc);
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			return true;

		case 0x215: // LSWX
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			XER_COUNT_USED(desc);
			for (regnum = 0; regnum < 32; regnum++)
				GPR_MODIFIED(desc, regnum);
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x217: // LFSX
		case 0x257: // LFDX
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			FPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x236: // TLBSYNC
			if (!(m_ppc.m_cap & PPCCAP_OEA))
				return false;
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x256: // SYNC
			return true;

		case 0x356: // EIEIO
			if (!(m_ppc.m_cap & (PPCCAP_VEA | PPCCAP_4XX)))
				return false;
			return true;

		case 0x237: // LFSUX
		case 0x277: // LFDUX
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			if (G_RA(op) == 0)
				return false;
			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			FPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_READS_MEMORY;
			return true;

		case 0x255: // LSWI
			GPR_USED_OR_ZERO(desc, G_RA(op));
			for (regnum = 0; regnum < ((G_NB(op) - 1) & 0x1f) + 1; regnum += 4)
				GPR_MODIFIED(desc, (G_RD(op) + regnum / 4) % 32);
			desc.flags |= OPFLAG_READS_MEMORY;
			desc.cycles = (((G_NB(op) - 1) & 0x1f) + 1 + 3) / 4;
			return true;

		case 0x295: // STSWX
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			XER_COUNT_USED(desc);
			for (regnum = 0; regnum < 32; regnum++)
				GPR_USED(desc, regnum);
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x2d5: // STSWI
			GPR_USED_OR_ZERO(desc, G_RA(op));
			for (regnum = 0; regnum < ((G_NB(op) - 1) & 0x1f) + 1; regnum += 4)
				GPR_USED(desc, (G_RD(op) + regnum / 4) % 32);
			desc.flags |= OPFLAG_WRITES_MEMORY;
			desc.cycles = (((G_NB(op) - 1) & 0x1f) + 1 + 3) / 4;
			return true;

		case 0x297: // STFSX
		case 0x2d7: // STFDX
		case 0x3d7: // STFIWX
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			FPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x2b7: // STFSUX
		case 0x2f7: // STFDUX
			if (!(m_ppc.m_cap & PPCCAP_FPU))
				return false;
			if (G_RA(op) == 0)
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			FPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x338: // SRAWIx
			GPR_USED(desc, G_RS(op));
			GPR_MODIFIED(desc, G_RA(op));
			XER_CA_MODIFIED(desc);
			if (op & M_RC)
			{
				XER_SO_USED(desc);
				CR_MODIFIED(desc, 0);
			}
			return true;

		case 0x3f6: // DCBZ
			if (!(m_ppc.m_cap & (PPCCAP_VEA | PPCCAP_4XX)))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_WRITES_MEMORY;
			return true;

		case 0x106: // ICBT
		case 0x1c6: // DCCCI
		case 0x3c6: // ICCCI
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x1e6: // DCREAD
		case 0x3e6: // ICREAD
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			GPR_USED_OR_ZERO(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RT(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x143: // MFDCR
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			GPR_MODIFIED(desc, G_RD(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION;
			return true;

		case 0x1c3: // MTDCR
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_PRIVILEGED | OPFLAG_CAN_CAUSE_EXCEPTION | OPFLAG_CAN_EXPOSE_EXTERNAL_INT;
			return true;

		case 0x083: // WRTEE
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			GPR_USED(desc, G_RS(op));
			desc.flags |= OPFLAG_CAN_EXPOSE_EXTERNAL_INT;
			return true;

		case 0x0a3: // WRTEEI
			if (!(m_ppc.m_cap & PPCCAP_4XX))
				return false;
			if (op & MSR_EE)
				desc.flags |= OPFLAG_CAN_EXPOSE_EXTERNAL_INT;
			return true;

		case 0x254: // ESA
		case 0x274: // DSA
			if (!is_602_class())
				return false;
			desc.flags |= OPFLAG_CAN_CHANGE_MODES | OPFLAG_CAN_CAUSE_EXCEPTION | OPFLAG_END_SEQUENCE;
			return true;

		case 0x14b: // DIV (POWER)
		case 0x16b: // DIVS (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x6b: // MUL (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x108: // DOZ (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x168: // ABS (POWER)
		case 0x1e8: // NABS (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED(desc, G_RA(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RD(op));
			XER_OV_MODIFIED(desc);
			XER_SO_MODIFIED(desc);
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;

		case 0x21d: // MASKIR (POWER)
			if (!(m_ppc.m_cap & PPCCAP_LEGACY_POWER))
			{
				return false;
			}

			GPR_USED(desc, G_RS(op));
			GPR_USED(desc, G_RB(op));
			GPR_MODIFIED(desc, G_RA(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 0);
			return true;
	}

	return false;
}


/*-------------------------------------------------
    describe_instruction_3b - build a
    description of a single instruction in the
    0x3b group
-------------------------------------------------*/

bool ppc_device::frontend::describe_3b(uint32_t op, opcode_desc &desc, const opcode_desc *prev)
{
	uint32_t opswitch = (op >> 1) & 0x1f;

	if (!(m_ppc.m_cap & PPCCAP_FPU))
		return false;

	switch (opswitch)
	{
		case 0x12:  // FDIVSx
			FPR_USED(desc, G_RA(op));
			FPR_USED(desc, G_RB(op));
			FPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 1);
			if (is_601_class())
				desc.cycles = 17;   // 601
			else if (is_603_class())
				desc.cycles = 18;   // 603
			else
				desc.cycles = 17;   // ???
			FPSCR_MODIFIED(desc, 4);
			return true;

		case 0x14:  // FSUBSx
		case 0x15:  // FADDSx
			FPR_USED(desc, G_RA(op));
			FPR_USED(desc, G_RB(op));
			FPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 1);
			FPSCR_MODIFIED(desc, 4);
			return true;

		case 0x19:  // FMULSx - not the same form as FSUB/FADD!
			FPR_USED(desc, G_RA(op));
			FPR_USED(desc, G_REGC(op));
			FPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 1);
			FPSCR_MODIFIED(desc, 4);
			return true;

		case 0x16:  // FSQRTSx
		case 0x18:  // FRESx
			FPR_USED(desc, G_RB(op));
			FPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 1);
			FPSCR_MODIFIED(desc, 4);
			return true;

		case 0x1c:  // FMSUBSx
		case 0x1d:  // FMADDSx
		case 0x1e:  // FNMSUBSx
		case 0x1f:  // FNMADDSx
			FPR_USED(desc, G_RA(op));
			FPR_USED(desc, G_RB(op));
			FPR_USED(desc, G_REGC(op));
			FPR_MODIFIED(desc, G_RD(op));
			if (op & M_RC)
				CR_MODIFIED(desc, 1);
			FPSCR_MODIFIED(desc, 4);
			return true;
	}

	return false;
}


/*-------------------------------------------------
    describe_instruction_3f - build a
    description of a single instruction in the
    0x3f group
-------------------------------------------------*/

bool ppc_device::frontend::describe_3f(uint32_t op, opcode_desc &desc, const opcode_desc *prev)
{
	uint32_t opswitch = (op >> 1) & 0x3ff;

	if (!(m_ppc.m_cap & PPCCAP_FPU))
		return false;

	if (opswitch & 0x10)
	{
		opswitch &= 0x1f;
		switch (opswitch)
		{
			case 0x12:  // FDIVx
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_RB(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				if (is_601_class())
					desc.cycles = 31;   // 601
				else if (is_603_class())
					desc.cycles = 33;   // 603
				else
					desc.cycles = 31;   // ???
				FPSCR_MODIFIED(desc, 4);
				return true;

			case 0x19:  // FMULx
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_REGC(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				desc.cycles = 2;    // 601/603
				FPSCR_MODIFIED(desc, 4);
				return true;

			case 0x14:  // FSUBx
			case 0x15:  // FADDx
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_RB(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				FPSCR_MODIFIED(desc, 4);
				return true;

			case 0x16:  // FSQRTx
			case 0x1a:  // FSQRTEx
				FPR_USED(desc, G_RB(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				FPSCR_MODIFIED(desc, 4);
				return true;

			case 0x17:  // FSELx
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_RB(op));
				FPR_USED(desc, G_REGC(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				desc.cycles = 2;    // 601/603
				return true;

			case 0x1c:  // FMSUBx
			case 0x1d:  // FMADDx
			case 0x1e:  // FNMSUBx
			case 0x1f:  // FNMADDx
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_RB(op));
				FPR_USED(desc, G_REGC(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				desc.cycles = 2;    // 601/603
				FPSCR_MODIFIED(desc, 4);
				return true;
		}
	}
	else
	{
		switch (opswitch)
		{
			case 0x32e: // FCTIDx - 64-bit only
			case 0x32f: // FCTIDZx - 64-bit only
			case 0x34e: // FCFIDx - 64-bit only
				return false;

			case 0x000: // FCMPU
			case 0x020: // FCMPO
				FPR_USED(desc, G_RA(op));
				FPR_USED(desc, G_RB(op));
				CR_MODIFIED(desc, G_CRFD(op));
				return true;

			case 0x00c: // FRSPx
			case 0x00e: // FCTIWx
			case 0x00f: // FCTIWZx
				FPSCR_MODIFIED(desc, 4);
				[[fallthrough]];
			case 0x028: // FNEGx
			case 0x048: // FMRx
			case 0x088: // FNABSx
			case 0x108: // FABSx
				FPR_USED(desc, G_RB(op));
				FPR_MODIFIED(desc, G_RD(op));
				if (op & M_RC)
					CR_MODIFIED(desc, 1);
				return true;

			case 0x026: // MTFSB1x
			case 0x046: // MTFSB0x
				FPSCR_MODIFIED(desc, G_CRBD(op) / 4);
				return true;

			case 0x040: // MCRFS
				FPSCR_USED(desc, G_CRFS(op));
				CR_MODIFIED(desc, G_CRFD(op));
				return true;

			case 0x086: // MTFSFIx
				FPSCR_MODIFIED(desc, G_CRFD(op));
				return true;

			case 0x247: // MFFSx
				FPSCR_USED(desc, 0);
				FPSCR_USED(desc, 1);
				FPSCR_USED(desc, 2);
				FPSCR_USED(desc, 3);
				FPSCR_USED(desc, 4);
				FPSCR_USED(desc, 5);
				FPSCR_USED(desc, 6);
				FPSCR_USED(desc, 7);
				FPR_MODIFIED(desc, G_RD(op));
				return true;

			case 0x2c7: // MTFSFx
				FPR_USED(desc, G_RB(op));
				if (G_CRM(op) & 0x80) FPSCR_MODIFIED(desc, 0);
				if (G_CRM(op) & 0x40) FPSCR_MODIFIED(desc, 1);
				if (G_CRM(op) & 0x20) FPSCR_MODIFIED(desc, 2);
				if (G_CRM(op) & 0x10) FPSCR_MODIFIED(desc, 3);
				if (G_CRM(op) & 0x08) FPSCR_MODIFIED(desc, 4);
				if (G_CRM(op) & 0x04) FPSCR_MODIFIED(desc, 5);
				if (G_CRM(op) & 0x02) FPSCR_MODIFIED(desc, 6);
				if (G_CRM(op) & 0x01) FPSCR_MODIFIED(desc, 7);
				return true;
		}
	}

	return false;
}
