dolphin/Source/Core/Common/GekkoDisassembler.cpp
Lioncash 0718937237 Common: Introduce the new Gekko disassembler to Common.
This moves the Gekko disassembler to Common where it should be. Having it in the Bochs disassembly Externals is incorrect.

Unlike the PowerPC disassembler prior however, this one is updated to have an API that is more fitting for C++. e.g. Not needing to specify a string buffer and size. It does all of this under the hood.

This modifies all the DebuggingInterfaces as necessary to handle this.
2014-08-04 00:45:07 -04:00

2299 lines
41 KiB
C++

/* $VER: ppc_disasm.c V1.5 (27.05.2009)
*
* Disassembler module for the PowerPC microprocessor family
* Copyright (c) 1998-2001,2009,2011 Frank Wille
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// Modified for use with Dolphin
#include <string>
#include "Common/CommonTypes.h"
#include "Common/GekkoDisassembler.h"
#include "Common/StringUtil.h"
// version/revision
#define PPCDISASM_VER 1
#define PPCDISASM_REV 6
// general defines
#define PPCIDXMASK 0xfc000000
#define PPCIDX2MASK 0x000007fe
#define PPCDMASK 0x03e00000
#define PPCAMASK 0x001f0000
#define PPCBMASK 0x0000f800
#define PPCCMASK 0x000007c0
#define PPCMMASK 0x0000003e
#define PPCCRDMASK 0x03800000
#define PPCCRAMASK 0x001c0000
#define PPCLMASK 0x00600000
#define PPCOE 0x00000400
#define PPCVRC 0x00000400
#define PPCDST 0x02000000
#define PPCSTRM 0x00600000
#define PPCIDXSH 26
#define PPCDSH 21
#define PPCASH 16
#define PPCBSH 11
#define PPCCSH 6
#define PPCMSH 1
#define PPCCRDSH 23
#define PPCCRASH 18
#define PPCLSH 21
#define PPCIDX2SH 1
#define PPCGETIDX(x) (((x)&PPCIDXMASK)>>PPCIDXSH)
#define PPCGETD(x) (((x)&PPCDMASK)>>PPCDSH)
#define PPCGETA(x) (((x)&PPCAMASK)>>PPCASH)
#define PPCGETB(x) (((x)&PPCBMASK)>>PPCBSH)
#define PPCGETC(x) (((x)&PPCCMASK)>>PPCCSH)
#define PPCGETM(x) (((x)&PPCMMASK)>>PPCMSH)
#define PPCGETCRD(x) (((x)&PPCCRDMASK)>>PPCCRDSH)
#define PPCGETCRA(x) (((x)&PPCCRAMASK)>>PPCCRASH)
#define PPCGETL(x) (((x)&PPCLMASK)>>PPCLSH)
#define PPCGETIDX2(x) (((x)&PPCIDX2MASK)>>PPCIDX2SH)
#define PPCGETSTRM(x) (((x)&PPCSTRM)>>PPCDSH)
static const char* trap_condition[32] = {
nullptr, "lgt", "llt", nullptr, "eq", "lge", "lle", nullptr,
"gt", nullptr, nullptr, nullptr, "ge", nullptr, nullptr, nullptr,
"lt", nullptr, nullptr, nullptr, "le", nullptr, nullptr, nullptr,
"ne", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
static const char* cmpname[4] = {
"cmpw", "cmpd", "cmplw", "cmpld"
};
static const char* b_ext[4] = {
"", "l", "a", "la"
};
static const char* b_condition[8] = {
"ge", "le", "ne", "ns", "lt", "gt", "eq", "so"
};
static const char* b_decr[16] = {
"nzf", "zf", nullptr, nullptr, "nzt", "zt", nullptr, nullptr,
"nz", "z", nullptr, nullptr, "nz", "z", nullptr, nullptr
};
static const char* regsel[2] = {
"", "r"
};
static const char* oesel[2] = {
"", "o"
};
static const char* rcsel[2] = {
"", "."
};
static const char* ldstnames[24] = {
"lwz", "lwzu", "lbz", "lbzu", "stw", "stwu", "stb", "stbu", "lhz", "lhzu",
"lha", "lhau", "sth", "sthu", "lmw", "stmw", "lfs", "lfsu", "lfd", "lfdu",
"stfs", "stfsu", "stfd", "stfdu"
};
static const char* regnames[32] = {
"r0", "sp", "rtoc", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
};
// Initialize static class variables.
u32* GekkoDisassembler::m_instr = nullptr;
u32* GekkoDisassembler::m_iaddr = nullptr;
std::string GekkoDisassembler::m_opcode = "";
std::string GekkoDisassembler::m_operands = "";
unsigned char GekkoDisassembler::m_type = 0;
unsigned char GekkoDisassembler::m_flags = PPCF_ILLEGAL;
unsigned short GekkoDisassembler::m_sreg = 0;
u32 GekkoDisassembler::m_displacement = 0;
static std::string spr_name(int i)
{
switch (i)
{
case 1: return "XER";
case 8: return "LR";
case 9: return "CTR";
case 18: return "DSIR";
case 19: return "DAR";
case 22: return "DEC";
case 25: return "SDR1";
case 26: return "SRR0";
case 27: return "SRR1";
case 272: return "SPRG0";
case 273: return "SPRG1";
case 274: return "SPRG2";
case 275: return "SPRG3";
case 282: return "EAR";
case 287: return "PVR";
case 528: return "IBAT0U";
case 529: return "IBAT0L";
case 530: return "IBAT1U";
case 531: return "IBAT1L";
case 532: return "IBAT2U";
case 533: return "IBAT2L";
case 534: return "IBAT3U";
case 535: return "IBAT3L";
case 536: return "DBAT0U";
case 537: return "DBAT0L";
case 538: return "DBAT1U";
case 539: return "DBAT1L";
case 540: return "DBAT2U";
case 541: return "DBAT2L";
case 542: return "DBAT3U";
case 543: return "DBAT3L";
case 912: return "GQR0";
case 913: return "GQR1";
case 914: return "GQR2";
case 915: return "GQR3";
case 916: return "GQR4";
case 917: return "GQR5";
case 918: return "GQR6";
case 919: return "GQR7";
case 920: return "HID2";
case 921: return "WPAR";
case 922: return "DMA_U";
case 923: return "DMA_L";
case 924: return "ECID_U";
case 925: return "ECID_M";
case 926: return "ECID_L";
case 936: return "UMMCR0";
case 937: return "UPMC1";
case 938: return "UPMC2";
case 939: return "USIA";
case 940: return "UMMCR1";
case 941: return "UPMC3";
case 942: return "UPMC4";
case 943: return "USDA";
case 952: return "MMCR0";
case 953: return "PMC1";
case 954: return "PMC2";
case 955: return "SIA";
case 956: return "MMCR1";
case 957: return "PMC3";
case 958: return "PMC4";
case 959: return "SDA";
case 1008: return "HID0";
case 1009: return "HID1";
case 1010: return "IABR";
case 1011: return "HID4";
case 1013: return "DABR";
case 1017: return "L2CR";
case 1019: return "ICTC";
case 1020: return "THRM1";
case 1021: return "THRM2";
case 1022: return "THRM3";
}
return StringFromFormat("%d", i);
}
static u32 swapda(u32 w)
{
return ((w & 0xfc00ffff) | ((w&PPCAMASK) << 5) | ((w&PPCDMASK) >> 5));
}
static u32 swapab(u32 w)
{
return ((w & 0xffe007ff) | ((w&PPCBMASK) << 5) | ((w&PPCAMASK) >> 5));
}
void GekkoDisassembler::ill(u32 in)
{
if (in == 0)
{
m_opcode = "";
m_operands = "---";
}
else
{
m_opcode = "(ill)";
m_operands = StringFromFormat("%08x", in);
}
m_flags |= PPCF_ILLEGAL;
}
// Generate immediate instruction operand.
//
// Type 0: D-mode, D,A,imm
// Type 1: S-mode, A,S,imm
// Type 2: S/D register is ignored (trap,cmpi)
// Type 3: A register is ignored (li)
std::string GekkoDisassembler::imm(u32 in, int uimm, int type, bool hex)
{
int i = (int)(in & 0xffff);
m_type = PPCINSTR_IMM;
if (uimm == 0)
{
if (i > 0x7fff)
i -= 0x10000;
}
else
{
m_flags |= PPCF_UNSIGNED;
}
m_displacement = i;
switch (type)
{
case 0:
return StringFromFormat("%s, %s, %d", regnames[(int)PPCGETD(in)], regnames[(int)PPCGETA(in)], i);
case 1:
if (hex)
return StringFromFormat("%s, %s, 0x%.4X", regnames[(int)PPCGETA(in)], regnames[(int)PPCGETD(in)], i);
else
return StringFromFormat("%s, %s, %d", regnames[(int)PPCGETA(in)], regnames[(int)PPCGETD(in)], i);
case 2:
return StringFromFormat("%s, %d", regnames[(int)PPCGETA(in)], i);
case 3:
if (hex)
return StringFromFormat("%s, 0x%.4X", regnames[(int)PPCGETD(in)], i);
else
return StringFromFormat("%s, %d", regnames[(int)PPCGETD(in)], i);
default:
return StringFromFormat("%s", "imm(): Wrong type");
}
}
std::string GekkoDisassembler::ra_rb(u32 in)
{
return StringFromFormat("%s, %s", regnames[(int)PPCGETA(in)], regnames[(int)PPCGETB(in)]);
}
std::string GekkoDisassembler::rd_ra_rb(u32 in, int mask)
{
std::string result;
if (mask)
{
if (mask & 4)
result += StringFromFormat("%s, ", regnames[(int)PPCGETD(in)]);
if (mask & 2)
result += StringFromFormat("%s, ", regnames[(int)PPCGETA(in)]);
if (mask & 1)
result += StringFromFormat("%s, ", regnames[(int)PPCGETB(in)]);
size_t pos = result.rfind(", ");
if (pos != std::string::npos)
{
result.erase(pos, result.length() - pos);
}
}
return result;
}
std::string GekkoDisassembler::fd_ra_rb(u32 in, int mask)
{
std::string result;
if (mask)
{
if (mask & 4)
result += StringFromFormat("f%d,", (int)PPCGETD(in));
if (mask & 2)
result += StringFromFormat("%s,", regnames[(int)PPCGETA(in)]);
if (mask & 1)
result += StringFromFormat("%s,", regnames[(int)PPCGETB(in)]);
// Drop the trailing comma
result.pop_back();
}
return result;
}
void GekkoDisassembler::trapi(u32 in, unsigned char dmode)
{
const char* cnd = trap_condition[PPCGETD(in)];
if (cnd != nullptr)
{
m_flags |= dmode;
m_opcode = StringFromFormat("t%c%s", dmode ? 'd' : 'w', cnd);
m_operands = imm(in, 0, 2, false);
}
else
{
ill(in);
}
}
void GekkoDisassembler::cmpi(u32 in, int uimm)
{
int i = (int)PPCGETL(in);
if (i < 2)
{
if (i != 0)
m_flags |= PPCF_64;
m_opcode = StringFromFormat("%si", cmpname[uimm * 2 + i]);
i = (int)PPCGETCRD(in);
if (i != 0)
{
m_operands += StringFromFormat("cr%c,", '0' + i);
}
m_operands += imm(in, uimm, 2, false);
}
else
{
ill(in);
}
}
void GekkoDisassembler::addi(u32 in, const std::string& ext)
{
if ((in & 0x08000000) && !PPCGETA(in))
{
m_opcode = StringFromFormat("l%s", ext.c_str()); // li, lis
if (ext == "i")
m_operands = imm(in, 0, 3, false);
else
m_operands = imm(in, 1, 3, true);
}
else
{
m_opcode = StringFromFormat("%s%s", (in & 0x8000) ? "sub" : "add", ext.c_str());
if (in & 0x8000)
in = (in ^ 0xffff) + 1;
m_operands = imm(in, 1, 0, false);
}
}
// Build a branch instr. and return number of chars written to operand.
size_t GekkoDisassembler::branch(u32 in, const char* bname, int aform, int bdisp)
{
int bo = (int)PPCGETD(in);
int bi = (int)PPCGETA(in);
char y = (char)(bo & 1);
const char* ext = b_ext[aform * 2 + (int)(in & 1)];
if (bdisp < 0)
y ^= 1;
y = (y != 0) ? '+' : '-';
if (bo & 4)
{
// standard case - no decrement
if (bo & 16)
{
// branch always
if (PPCGETIDX(in) != 16)
{
m_opcode = StringFromFormat("b%s%s", bname, ext);
}
else
{
m_opcode = StringFromFormat("bc%s", ext);
m_operands = StringFromFormat("%d, %d", bo, bi);
}
}
else // Branch conditional
{
m_opcode = StringFromFormat("b%s%s%s%c", b_condition[((bo & 8) >> 1) + (bi & 3)], bname, ext, y);
if (bi >= 4)
{
m_operands = StringFromFormat("cr%d", bi >> 2);
}
}
}
else
{
// CTR is decremented and checked
m_opcode = StringFromFormat("bd%s%s%s%c", b_decr[bo >> 1], bname, ext, y);
if ((bo & 16) == 0)
{
m_operands = StringFromFormat("%d", bi);
}
}
return m_operands.length();
}
void GekkoDisassembler::bc(u32 in)
{
unsigned int d = (int)(in & 0xfffc);
if (d & 0x8000)
d |= 0xffff0000;
branch(in, "", (in & 2) ? 1 : 0, d);
if (in & 2) // AA ?
m_operands = StringFromFormat("->0x%.8X", d);
else
m_operands = StringFromFormat("->0x%.8X", *m_iaddr + d);
m_type = PPCINSTR_BRANCH;
m_displacement = d;
}
void GekkoDisassembler::bli(u32 in)
{
unsigned int d = (unsigned int)(in & 0x3fffffc);
if (d & 0x02000000)
d |= 0xfc000000;
m_opcode = StringFromFormat("b%s", b_ext[in & 3]);
if (in & 2) // AA ?
m_operands = StringFromFormat("->0x%.8X", d);
else
m_operands = StringFromFormat("->0x%.8X", *m_iaddr + d);
m_type = PPCINSTR_BRANCH;
m_displacement = d;
}
void GekkoDisassembler::mcrf(u32 in, char c)
{
if ((in & 0x0063f801) == 0)
{
m_opcode = StringFromFormat("mcrf%c", c);
m_operands = StringFromFormat("cr%d, cr%d", (int)PPCGETCRD(in), (int)PPCGETCRA(in));
}
else
{
ill(in);
}
}
void GekkoDisassembler::crop(u32 in, const char* n1, const char* n2)
{
int crd = (int)PPCGETD(in);
int cra = (int)PPCGETA(in);
int crb = (int)PPCGETB(in);
if ((in & 1) == 0)
{
m_opcode = StringFromFormat("cr%s", (cra == crb && n2) ? n2 : n1);
if (cra == crb && n2)
m_operands = StringFromFormat("%d, %d", crd, cra);
else
m_operands = StringFromFormat("%d, %d, %d", crd, cra, crb);
}
else
{
ill(in);
}
}
void GekkoDisassembler::nooper(u32 in, const char* name, unsigned char dmode)
{
if (in & (PPCDMASK | PPCAMASK | PPCBMASK | 1))
{
ill(in);
}
else
{
m_flags |= dmode;
m_opcode = name;
}
}
void GekkoDisassembler::rlw(u32 in, const char* name, int i)
{
int s = (int)PPCGETD(in);
int a = (int)PPCGETA(in);
int bsh = (int)PPCGETB(in);
int mb = (int)PPCGETC(in);
int me = (int)PPCGETM(in);
m_opcode = StringFromFormat("rlw%s%c", name, in & 1 ? '.' : '\0');
m_operands = StringFromFormat("%s, %s, %s%d, %d, %d (%08x)", regnames[a], regnames[s], regsel[i], bsh, mb, me, HelperRotateMask(bsh, mb, me));
}
void GekkoDisassembler::ori(u32 in, const char* name)
{
m_opcode = name;
m_operands = imm(in, 1, 1, true);
}
void GekkoDisassembler::rld(u32 in, const char* name, int i)
{
int s = (int)PPCGETD(in);
int a = (int)PPCGETA(in);
int bsh = i ? (int)PPCGETB(in) : (int)(((in & 2) << 4) + PPCGETB(in));
int m = (int)(in & 0x7e0) >> 5;
m_flags |= PPCF_64;
m_opcode = StringFromFormat("rld%s%c", name, in & 1 ? '.' : '\0');
m_operands = StringFromFormat("%s, %s, %s%d, %d", regnames[a], regnames[s], regsel[i], bsh, m);
}
void GekkoDisassembler::cmp(u32 in)
{
int i = (int)PPCGETL(in);
if (i < 2)
{
if (i != 0)
m_flags |= PPCF_64;
m_opcode = cmpname[((in&PPCIDX2MASK) ? 2 : 0) + i];
i = (int)PPCGETCRD(in);
if (i != 0)
m_operands += StringFromFormat("cr%c,", '0' + i);
m_operands += ra_rb(in);
}
else
{
ill(in);
}
}
void GekkoDisassembler::trap(u32 in, unsigned char dmode)
{
int to = (int)PPCGETD(in);
const char* cnd = trap_condition[to];
if (cnd != nullptr)
{
m_flags |= dmode;
m_opcode = StringFromFormat("t%c%s", dmode ? 'd' : 'w', cnd);
m_operands = ra_rb(in);
}
else
{
if (to == 31)
{
if (dmode)
{
m_flags |= dmode;
m_opcode = "td";
m_operands = "31,0,0";
}
else
{
m_opcode = "trap";
}
}
else
{
ill(in);
}
}
}
// Standard instruction: xxxx rD,rA,rB
void GekkoDisassembler::dab(u32 in, const char* name, int mask, int smode, int chkoe, int chkrc, unsigned char dmode)
{
if (chkrc >= 0 && ((in & 1) != (unsigned int)chkrc))
{
ill(in);
}
else
{
m_flags |= dmode;
// rA,rS,rB
if (smode)
in = swapda(in);
m_opcode = StringFromFormat("%s%s%s", name, oesel[chkoe && (in&PPCOE)], rcsel[(chkrc < 0) && (in & 1)]);
m_operands = rd_ra_rb(in, mask);
}
}
// Last operand is no register: xxxx rD,rA,NB
void GekkoDisassembler::rrn(u32 in, const char* name, int smode, int chkoe, int chkrc, unsigned char dmode)
{
if (chkrc >= 0 && ((in & 1) != (unsigned int)chkrc))
{
ill(in);
}
else
{
m_flags |= dmode;
// rA,rS,NB
if (smode)
in = swapda(in);
m_opcode = StringFromFormat("%s%s%s", name, oesel[chkoe && (in&PPCOE)], rcsel[(chkrc < 0) && (in & 1)]);
m_operands = rd_ra_rb(in, 6);
m_operands += StringFromFormat(",%d",(int)PPCGETB(in));
}
}
void GekkoDisassembler::mtcr(u32 in)
{
int s = (int)PPCGETD(in);
int crm = (int)(in & 0x000ff000) >> 12;
if (in & 0x00100801)
{
ill(in);
}
else
{
m_opcode = StringFromFormat("mtcr%c", crm == 0xff ? '\0' : 'f');
if (crm != 0xff)
m_operands += StringFromFormat("0x%02x,", crm);
m_operands += regnames[s];
}
}
void GekkoDisassembler::msr(u32 in, int smode)
{
int s = (int)PPCGETD(in);
int sr = (int)(in & 0x000f0000) >> 16;
if (in & 0x0010f801)
{
ill(in);
}
else
{
m_flags |= PPCF_SUPER;
m_opcode = StringFromFormat("m%csr", smode ? 't' : 'f');
if (smode)
m_operands = StringFromFormat("%d, %s", sr, regnames[s]);
else
m_operands = StringFromFormat("%s, %d", regnames[s], sr);
}
}
void GekkoDisassembler::mspr(u32 in, int smode)
{
int d = (int)PPCGETD(in);
int spr = (int)((PPCGETB(in) << 5) + PPCGETA(in));
int fmt = 0;
if (in & 1)
{
ill(in);
}
else
{
if (spr != 1 && spr != 8 && spr != 9)
m_flags |= PPCF_SUPER;
const char* x;
switch (spr)
{
case 1:
x = "xer";
break;
case 8:
x = "lr";
break;
case 9:
x = "ctr";
break;
default:
x = "spr";
fmt = 1;
break;
}
m_opcode = StringFromFormat("m%c%s", smode ? 't' : 'f', x);
if (fmt)
{
if (smode)
m_operands = StringFromFormat("%s, %s", spr_name(spr).c_str(), regnames[d]);
else
m_operands = StringFromFormat("%s, %s", regnames[d], spr_name(spr).c_str());
}
else
{
m_operands = regnames[d];
}
}
}
void GekkoDisassembler::mtb(u32 in)
{
int d = (int)PPCGETD(in);
int tbr = (int)((PPCGETB(in) << 5) + PPCGETA(in));
if (in & 1)
{
ill(in);
}
else
{
m_operands += regnames[d];
char x;
switch (tbr)
{
case 268:
x = 'l';
break;
case 269:
x = 'u';
break;
default:
x = '\0';
m_flags |= PPCF_SUPER;
m_operands += StringFromFormat(",%d", tbr);
break;
}
m_opcode = StringFromFormat("mftb%c", x);
}
}
void GekkoDisassembler::sradi(u32 in)
{
int s = (int)PPCGETD(in);
int a = (int)PPCGETA(in);
int bsh = (int)(((in & 2) << 4) + PPCGETB(in));
m_flags |= PPCF_64;
m_opcode = StringFromFormat("sradi%c", in & 1 ? '.' : '\0');
m_operands = StringFromFormat("%s, %s, %d", regnames[a], regnames[s], bsh);
}
void GekkoDisassembler::ldst(u32 in, const char* name, char reg, unsigned char dmode)
{
int s = (int)PPCGETD(in);
int a = (int)PPCGETA(in);
int d = (u32)(in & 0xffff);
m_type = PPCINSTR_LDST;
m_flags |= dmode;
m_sreg = (short)a;
// if (d >= 0x8000)
// d -= 0x10000;
m_displacement = (u32)d;
m_opcode = name;
if (reg == 'r')
{
m_operands = StringFromFormat("%s, %s (%s)", regnames[s], ldst_offs(d).c_str(), regnames[a]);
}
else
{
m_operands = StringFromFormat("%c%d, %s (%s)", reg, s, ldst_offs(d).c_str(), regnames[a]);
}
}
// Standard floating point instruction: xxxx fD,fA,fC,fB
void GekkoDisassembler::fdabc(u32 in, const char* name, int mask, unsigned char dmode)
{
int err = 0;
m_flags |= dmode;
m_opcode = StringFromFormat("f%s%s", name, rcsel[in & 1]);
m_operands += StringFromFormat("f%d,", (int)PPCGETD(in));
if (mask & 4)
m_operands += StringFromFormat("f%d,", (int)PPCGETA(in));
else
err |= (int)PPCGETA(in);
if (mask & 2)
m_operands += StringFromFormat("f%d,", (int)PPCGETC(in));
else if (PPCGETC(in))
err |= (int)PPCGETC(in);
if (mask & 1)
m_operands += StringFromFormat("f%d,", (int)PPCGETB(in));
else if (!(mask & 8))
err |= (int)PPCGETB(in);
// Drop the trailing comma
m_operands.pop_back();
if (err)
ill(in);
}
void GekkoDisassembler::fmr(u32 in)
{
m_opcode = StringFromFormat("fmr%s", rcsel[in & 1]);
m_operands = StringFromFormat("f%d, f%d", (int)PPCGETD(in), (int)PPCGETB(in));
}
// Indexed float instruction: xxxx fD,rA,rB
void GekkoDisassembler::fdab(u32 in, const char* name, int mask)
{
m_opcode = name;
m_operands = fd_ra_rb(in, mask);
}
void GekkoDisassembler::fcmp(u32 in, char c)
{
if (in & 0x00600001)
{
ill(in);
}
else
{
m_opcode = StringFromFormat("fcmp%c", c);
m_operands = StringFromFormat("cr%d,f%d,f%d", (int)PPCGETCRD(in), (int)PPCGETA(in), (int)PPCGETB(in));
}
}
void GekkoDisassembler::mtfsb(u32 in, int n)
{
if (in & (PPCAMASK | PPCBMASK))
{
ill(in);
}
else
{
m_opcode = StringFromFormat("mtfsb%d%s", n, rcsel[in & 1]);
m_operands = StringFromFormat("%d", (int)PPCGETD(in));
}
}
// Paired instructions
#define RA ((inst >> 16) & 0x1f)
#define RB ((inst >> 11) & 0x1f)
#define RC ((inst >> 6) & 0x1f)
#define RD ((inst >> 21) & 0x1f)
#define RS ((inst >> 21) & 0x1f)
#define FA ((inst >> 16) & 0x1f)
#define FB ((inst >> 11) & 0x1f)
#define FC ((inst >> 6) & 0x1f)
#define FD ((inst >> 21) & 0x1f)
#define FS ((inst >> 21) & 0x1f)
#define IMM (inst & 0xffff)
#define UIMM (inst & 0xffff)
#define OFS (inst & 0xffff)
#define OPCD ((inst >> 26) & 0x3f)
#define XO_10 ((inst >> 1) & 0x3ff)
#define XO_9 ((inst >> 1) & 0x1ff)
#define XO_5 ((inst >> 1) & 0x1f)
#define Rc (inst & 1)
#define SH ((inst >> 11) & 0x1f)
#define MB ((inst >> 6) & 0x1f)
#define ME ((inst >> 1) & 0x1f)
#define OE ((inst >> 10) & 1)
#define TO ((inst >> 21) & 0x1f)
#define CRFD ((inst >> 23) & 0x7)
#define CRFS ((inst >> 18) & 0x7)
#define CRBD ((inst >> 21) & 0x1f)
#define CRBA ((inst >> 16) & 0x1f)
#define CRBB ((inst >> 11) & 0x1f)
#define L ((inst >> 21) & 1)
#define NB ((inst >> 11) & 0x1f)
#define AA ((inst >> 1) & 1)
#define LK (inst & 1)
#define LI ((inst >> 2) & 0xffffff)
#define BO ((inst >> 21) & 0x1f)
#define BI ((inst >> 16) & 0x1f)
#define BD ((inst >> 2) & 0x3fff)
#define MTFSFI_IMM ((inst >> 12) & 0xf)
#define FM ((inst >> 17) & 0xff)
#define SR ((inst >> 16) & 0xf)
#define SPR ((inst >> 11) & 0x3ff)
#define TBR ((inst >> 11) & 0x3ff)
#define CRM ((inst >> 12) & 0xff)
void GekkoDisassembler::ps(u32 inst)
{
switch ((inst >> 1) & 0x1F)
{
case 6:
m_opcode = "ps_lux";
m_operands = StringFromFormat("p%u, (r%u + r%u)", FD, RA, RB);
return;
case 18:
m_opcode = "ps_div";
m_operands = StringFromFormat("p%u, p%u/p%u", FD, FA, FB);
return;
case 20:
m_opcode = "ps_sub";
m_operands = StringFromFormat("p%u, p%u-p%u", FD, FA, FB);
return;
case 21:
m_opcode = "ps_add";
m_operands = StringFromFormat("p%u, p%u+p%u", FD, FA, FB);
return;
case 23:
m_opcode = "ps_sel";
m_operands = StringFromFormat("p%u>=0?p%u:p%u", FD, FA, FC);
return;
case 24:
m_opcode = "ps_res";
m_operands = StringFromFormat("p%u, (1/p%u)", FD, FB);
return;
case 25:
m_opcode = "ps_mul";
m_operands = StringFromFormat("p%u, p%u*p%u", FD, FA, FC);
return;
case 26: // rsqrte
m_opcode = "ps_rsqrte";
m_operands = StringFromFormat("p%u, p%u", FD, FB);
return;
case 28: // msub
m_opcode = "ps_msub";
m_operands = StringFromFormat("p%u, p%u*p%u-p%u", FD, FA, FC, FB);
return;
case 29: // madd
m_opcode = "ps_madd";
m_operands = StringFromFormat("p%u, p%u*p%u+p%u", FD, FA, FC, FB);
return;
case 30: // nmsub
m_opcode = "ps_nmsub";
m_operands = StringFromFormat("p%u, -(p%u*p%u-p%u)", FD, FA, FC, FB);
return;
case 31: // nmadd
m_opcode = "ps_nmadd";
m_operands = StringFromFormat("p%u, -(p%u*p%u+p%u)", FD, FA, FC, FB);
return;
case 10:
m_opcode = "ps_sum0";
m_operands = StringFromFormat("p%u, 0=p%u+p%u, 1=p%u", FD, FA, FB, FC);
return;
case 11:
m_opcode = "ps_sum1";
m_operands = StringFromFormat("p%u, 0=p%u, 1=p%u+p%u", FD, FC, FA, FB);
return;
case 12:
m_opcode = "ps_muls0";
m_operands = StringFromFormat("p%u, p%u*p%u[0]", FD, FA, FC);
return;
case 13:
m_opcode = "ps_muls1";
m_operands = StringFromFormat("p%u, p%u*p%u[1]", FD, FA, FC);
return;
case 14:
m_opcode = "ps_madds0";
m_operands = StringFromFormat("p%u, p%u*p%u[0]+p%u", FD, FA, FC, FB);
return;
case 15:
m_opcode = "ps_madds1";
m_operands = StringFromFormat("p%u, p%u*p%u[1]+p%u", FD, FA, FC, FB);
return;
}
switch ((inst >> 1) & 0x3FF)
{
// 10-bit suckers (?)
case 40: // nmadd
m_opcode = "ps_neg";
m_operands = StringFromFormat("p%u, -p%u", FD, FB);
return;
case 72: // nmadd
m_opcode = "ps_mr";
m_operands = StringFromFormat("p%u, p%u", FD, FB);
return;
case 136:
m_opcode = "ps_nabs";
m_operands = StringFromFormat("p%u, -|p%u|", FD, FB);
return;
case 264:
m_opcode = "ps_abs";
m_operands = StringFromFormat("p%u, |p%u|", FD, FB);
return;
case 0:
m_opcode = "ps_cmpu0";
m_operands = "ps_cmpu0";
return;
case 32:
m_opcode = "ps_cmpq0";
m_operands = "ps_cmpo0";
return;
case 64:
m_opcode = "ps_cmpu1";
m_operands = "ps_cmpu1";
return;
case 96:
m_opcode = "ps_cmpo1";
m_operands = "ps_cmpo1";
return;
case 528:
m_opcode = "ps_merge00";
m_operands = StringFromFormat("p%u, p%u[0],p%u[0]", FD, FA, FB);
return;
case 560:
m_opcode = "ps_merge01";
m_operands = StringFromFormat("p%u, p%u[0],p%u[1]", FD, FA, FB);
return;
case 592:
m_opcode = "ps_merge10";
m_operands = StringFromFormat("p%u, p%u[1],p%u[0]", FD, FA, FB);
return;
case 624:
m_opcode = "ps_merge11";
m_operands = StringFromFormat("p%u, p%u[1],p%u[1]", FD, FA, FB);
return;
case 1014:
m_opcode = "dcbz_l";
m_operands = "";
return;
}
// default:
m_opcode = StringFromFormat("ps_%i", ((inst >> 1) & 0x1f));
m_operands = "---";
}
void GekkoDisassembler::ps_mem(u32 inst)
{
switch (PPCGETIDX(inst))
{
case 56:
m_opcode = "psq_l";
m_operands = StringFromFormat("p%u, %i(r%u)", RS, SEX12(inst & 0xFFF), RA);
break;
case 57:
m_opcode = "psq_lu";
m_operands = "";
break;
case 60:
m_opcode = "psq_st";
m_operands = StringFromFormat("%i(r%u), p%u", SEX12(inst & 0xFFF), RA, RS);
break;
case 61:
m_opcode = "psq_stu";
m_operands = StringFromFormat("r%u, p%u ?", RA, RS);
break;
}
}
// Disassemble PPC instruction and return a pointer to the next
// instruction, or nullptr if an error occured.
u32* GekkoDisassembler::DoDisassembly(bool big_endian)
{
u32 in = *m_instr;
if (!big_endian)
{
in = (in & 0xff) << 24 | (in & 0xff00) << 8 | (in & 0xff0000) >> 8 |
(in & 0xff000000) >> 24;
}
m_opcode.clear();
m_operands.clear();
m_type = PPCINSTR_OTHER;
m_flags = 0;
switch (PPCGETIDX(in))
{
case 0:
{
int block = in & 0x3FFFFFF;
if (block)
{
m_opcode = "JITblock";
m_operands = StringFromFormat("%i", block);
}
else
{
m_opcode = "";
m_operands = "---";
}
}
break;
case 1: // HLE call
m_opcode = "HLE";
break;
case 2:
trapi(in, PPCF_64); // tdi
break;
case 3:
trapi(in, 0); // twi
break;
case 4:
ps(in);
break;
case 56:
case 57:
case 60:
case 61:
ps_mem(in);
break;
case 7:
m_opcode = "mulli";
m_operands = imm(in, 0, 0, false);
break;
case 8:
m_opcode = "subfic";
m_operands = imm(in, 0, 0, false);
break;
case 10:
cmpi(in, 1); // cmpli
break;
case 11:
cmpi(in, 0); // cmpi
break;
case 12:
addi(in, "ic"); // addic
break;
case 13:
addi(in, "ic."); // addic.
break;
case 14:
addi(in, "i"); // addi
break;
case 15:
addi(in, "is"); // addis
break;
case 16:
bc(in);
break;
case 17:
if ((in & ~PPCIDXMASK) == 2)
m_opcode = "sc";
else
ill(in);
break;
case 18:
bli(in);
break;
case 19:
switch (PPCGETIDX2(in))
{
case 0:
mcrf(in, '\0'); // mcrf
break;
case 16:
branch(in, "lr", 0, 0); // bclr
break;
case 33:
crop(in, "nor", "not"); // crnor
break;
case 50:
nooper(in, "rfi", PPCF_SUPER);
break;
case 129:
crop(in, "andc", nullptr); // crandc
break;
case 150:
nooper(in, "isync", 0);
break;
case 193:
crop(in, "xor", "clr"); // crxor
break;
case 225:
crop(in, "nand", nullptr); // crnand
break;
case 257:
crop(in, "and", nullptr); // crand
break;
case 289:
crop(in, "eqv", "set"); // creqv
break;
case 417:
crop(in, "orc", nullptr); // crorc
break;
case 449:
crop(in, "or", "move"); // cror
break;
case 528:
branch(in, "ctr", 0, 0); // bcctr
break;
default:
ill(in);
break;
}
break;
case 20:
rlw(in, "imi", 0); // rlwimi
break;
case 21:
rlw(in, "inm", 0); // rlwinm
break;
case 23:
rlw(in, "nm", 1); // rlwnm
break;
case 24:
if (in & ~PPCIDXMASK)
ori(in, "ori");
else
m_opcode = "nop";
break;
case 25:
ori(in, "oris");
break;
case 26:
ori(in, "xori");
break;
case 27:
ori(in, "xoris");
break;
case 28:
ori(in, "andi.");
break;
case 29:
ori(in, "andis.");
break;
case 30:
switch (in & 0x1c)
{
case 0:
rld(in, "icl", 0); // rldicl
break;
case 1:
rld(in, "icr", 0); // rldicr
break;
case 2:
rld(in, "ic", 0); // rldic
break;
case 3:
rld(in, "imi", 0); // rldimi
break;
case 4:
rld(in, in & 2 ? "cl" : "cr", 1); // rldcl, rldcr
break;
default:
ill(in);
break;
}
break;
case 31:
switch (PPCGETIDX2(in))
{
case 0:
case 32:
if (in & 1)
ill(in);
else
cmp(in); // cmp, cmpl
break;
case 4:
if (in & 1)
ill(in);
else
trap(in, 0); // tw
break;
case 8:
case (PPCOE >> 1) + 8:
dab(swapab(in), "subc", 7, 0, 1, -1, 0);
break;
case 9:
dab(in, "mulhdu", 7, 0, 0, -1, PPCF_64);
break;
case 10:
case (PPCOE >> 1) + 10:
dab(in, "addc", 7, 0, 1, -1, 0);
break;
case 11:
dab(in, "mulhwu", 7, 0, 0, -1, 0);
break;
case 19:
if (in & (PPCAMASK | PPCBMASK))
ill(in);
else
dab(in, "mfcr", 4, 0, 0, 0, 0);
break;
case 20:
dab(in, "lwarx", 7, 0, 0, 0, 0);
break;
case 21:
dab(in, "ldx", 7, 0, 0, 0, PPCF_64);
break;
case 23:
dab(in, "lwzx", 7, 0, 0, 0, 0);
break;
case 24:
dab(in, "slw", 7, 1, 0, -1, 0);
break;
case 26:
if (in & PPCBMASK)
ill(in);
else
dab(in, "cntlzw", 6, 1, 0, -1, 0);
break;
case 27:
dab(in, "sld", 7, 1, 0, -1, PPCF_64);
break;
case 28:
dab(in, "and", 7, 1, 0, -1, 0);
break;
case 40:
case (PPCOE >> 1) + 40:
dab(swapab(in), "sub", 7, 0, 1, -1, 0);
break;
case 53:
dab(in, "ldux", 7, 0, 0, 0, PPCF_64);
break;
case 54:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbst", 3, 0, 0, 0, 0);
break;
case 55:
dab(in, "lwzux", 7, 0, 0, 0, 0);
break;
case 58:
if (in & PPCBMASK)
ill(in);
else
dab(in, "cntlzd", 6, 1, 0, -1, PPCF_64);
break;
case 60:
dab(in, "andc", 7, 1, 0, -1, 0);
break;
case 68:
trap(in, PPCF_64); // td
break;
case 73:
dab(in, "mulhd", 7, 0, 0, -1, PPCF_64);
break;
case 75:
dab(in, "mulhw", 7, 0, 0, -1, 0);
break;
case 83:
if (in & (PPCAMASK | PPCBMASK))
ill(in);
else
dab(in, "mfmsr", 4, 0, 0, 0, PPCF_SUPER);
break;
case 84:
dab(in, "ldarx", 7, 0, 0, 0, PPCF_64);
break;
case 86:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbf", 3, 0, 0, 0, 0);
break;
case 87:
dab(in, "lbzx", 7, 0, 0, 0, 0);
break;
case 104:
case (PPCOE >> 1) + 104:
if (in & PPCBMASK)
ill(in);
else
dab(in, "neg", 6, 0, 1, -1, 0);
break;
case 119:
dab(in, "lbzux", 7, 0, 0, 0, 0);
break;
case 124:
if (PPCGETD(in) == PPCGETB(in))
dab(in, "not", 6, 1, 0, -1, 0);
else
dab(in, "nor", 7, 1, 0, -1, 0);
break;
case 136:
case (PPCOE >> 1) + 136:
dab(in, "subfe", 7, 0, 1, -1, 0);
break;
case 138:
case (PPCOE >> 1) + 138:
dab(in, "adde", 7, 0, 1, -1, 0);
break;
case 144:
mtcr(in);
break;
case 146:
if (in & (PPCAMASK | PPCBMASK))
ill(in);
else
dab(in, "mtmsr", 4, 0, 0, 0, PPCF_SUPER);
break;
case 149:
dab(in, "stdx", 7, 0, 0, 0, PPCF_64);
break;
case 150:
dab(in, "stwcx.", 7, 0, 0, 1, 0);
break;
case 151:
dab(in, "stwx", 7, 0, 0, 0, 0);
break;
case 181:
dab(in, "stdux", 7, 0, 0, 0, PPCF_64);
break;
case 183:
dab(in, "stwux", 7, 0, 0, 0, 0);
break;
case 200:
case (PPCOE >> 1) + 200:
if (in & PPCBMASK)
ill(in);
else
dab(in, "subfze", 6, 0, 1, -1, 0);
break;
case 202:
case (PPCOE >> 1) + 202:
if (in & PPCBMASK)
ill(in);
else
dab(in, "addze", 6, 0, 1, -1, 0);
break;
case 210:
msr(in, 1); // mfsr
break;
case 214:
dab(in, "stdcx.", 7, 0, 0, 1, PPCF_64);
break;
case 215:
dab(in, "stbx", 7, 0, 0, 0, 0);
break;
case 232:
case (PPCOE >> 1) + 232:
if (in & PPCBMASK)
ill(in);
else
dab(in, "subfme", 6, 0, 1, -1, 0);
break;
case 233:
case (PPCOE >> 1) + 233:
dab(in, "mulld", 7, 0, 1, -1, PPCF_64);
break;
case 234:
case (PPCOE >> 1) + 234:
if (in & PPCBMASK)
ill(in);
else
dab(in, "addme", 6, 0, 1, -1, 0);
break;
case 235:
case (PPCOE >> 1) + 235:
dab(in, "mullw", 7, 0, 1, -1, 0);
break;
case 242:
if (in & PPCAMASK)
ill(in);
else
dab(in, "mtsrin", 5, 0, 0, 0, PPCF_SUPER);
break;
case 246:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbtst", 3, 0, 0, 0, 0);
break;
case 247:
dab(in, "stbux", 7, 0, 0, 0, 0);
break;
case 266:
case (PPCOE >> 1) + 266:
dab(in, "add", 7, 0, 1, -1, 0);
break;
case 278:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbt", 3, 0, 0, 0, 0);
break;
case 279:
dab(in, "lhzx", 7, 0, 0, 0, 0);
break;
case 284:
dab(in, "eqv", 7, 1, 0, -1, 0);
break;
case 306:
if (in & (PPCDMASK | PPCAMASK))
ill(in);
else
dab(in, "tlbie", 1, 0, 0, 0, PPCF_SUPER);
break;
case 310:
dab(in, "eciwx", 7, 0, 0, 0, 0);
break;
case 311:
dab(in, "lhzux", 7, 0, 0, 0, 0);
break;
case 316:
dab(in, "xor", 7, 1, 0, -1, 0);
break;
case 339:
mspr(in, 0); // mfspr
break;
case 341:
dab(in, "lwax", 7, 0, 0, 0, PPCF_64);
break;
case 343:
dab(in, "lhax", 7, 0, 0, 0, 0);
break;
case 370:
nooper(in, "tlbia", PPCF_SUPER);
break;
case 371:
mtb(in); // mftb
break;
case 373:
dab(in, "lwaux", 7, 0, 0, 0, PPCF_64);
break;
case 375:
dab(in, "lhaux", 7, 0, 0, 0, 0);
break;
case 407:
dab(in, "sthx", 7, 0, 0, 0, 0);
break;
case 412:
dab(in, "orc", 7, 1, 0, -1, 0);
break;
case 413:
sradi(in); // sradi
break;
case 434:
if (in & (PPCDMASK | PPCAMASK))
ill(in);
else
dab(in, "slbie", 1, 0, 0, 0, PPCF_SUPER | PPCF_64);
break;
case 438:
dab(in, "ecowx", 7, 0, 0, 0, 0);
break;
case 439:
dab(in, "sthux", 7, 0, 0, 0, 0);
break;
case 444:
if (PPCGETD(in) == PPCGETB(in))
dab(in, "mr", 6, 1, 0, -1, 0);
else
dab(in, "or", 7, 1, 0, -1, 0);
break;
case 457:
case (PPCOE >> 1) + 457:
dab(in, "divdu", 7, 0, 1, -1, PPCF_64);
break;
case 459:
case (PPCOE >> 1) + 459:
dab(in, "divwu", 7, 0, 1, -1, 0);
break;
case 467:
mspr(in, 1); // mtspr
break;
case 470:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbi", 3, 0, 0, 0, 0);
break;
case 476:
dab(in, "nand", 7, 1, 0, -1, 0);
break;
case 489:
case (PPCOE >> 1) + 489:
dab(in, "divd", 7, 0, 1, -1, PPCF_64);
break;
case 491:
case (PPCOE >> 1) + 491:
dab(in, "divw", 7, 0, 1, -1, 0);
break;
case 498:
nooper(in, "slbia", PPCF_SUPER | PPCF_64);
break;
case 512:
if (in & 0x007ff801)
{
ill(in);
}
else
{
m_opcode = "mcrxr";
m_operands = StringFromFormat("cr%d", (int)PPCGETCRD(in));
}
break;
case 533:
dab(in, "lswx", 7, 0, 0, 0, 0);
break;
case 534:
dab(in, "lwbrx", 7, 0, 0, 0, 0);
break;
case 535:
fdab(in, "lfsx", 7);
break;
case 536:
dab(in, "srw", 7, 1, 0, -1, 0);
break;
case 539:
dab(in, "srd", 7, 1, 0, -1, PPCF_64);
break;
case 566:
nooper(in, "tlbsync", PPCF_SUPER);
break;
case 567:
fdab(in, "lfsux", 7);
break;
case 595:
msr(in, 0); // mfsr
break;
case 597:
rrn(in, "lswi", 0, 0, 0, 0);
break;
case 598:
nooper(in, "sync", PPCF_SUPER);
break;
case 599:
fdab(in, "lfdx", 7);
break;
case 631:
fdab(in, "lfdux", 7);
break;
case 659:
if (in & PPCAMASK)
ill(in);
else
dab(in, "mfsrin", 5, 0, 0, 0, PPCF_SUPER);
break;
case 661:
dab(in, "stswx", 7, 0, 0, 0, 0);
break;
case 662:
dab(in, "stwbrx", 7, 0, 0, 0, 0);
break;
case 663:
fdab(in, "stfsx", 7);
break;
case 695:
fdab(in, "stfsux", 7);
break;
case 725:
rrn(in, "stswi", 0, 0, 0, 0);
break;
case 727:
fdab(in, "stfdx", 7);
break;
case 759:
fdab(in, "stfdux", 7);
break;
case 790:
dab(in, "lhbrx", 7, 0, 0, 0, 0);
break;
case 792:
dab(in, "sraw", 7, 1, 0, -1, 0);
break;
case 794:
dab(in, "srad", 7, 1, 0, -1, PPCF_64);
break;
case 824:
rrn(in, "srawi", 1, 0, -1, 0);
break;
case 854:
nooper(in, "eieio", PPCF_SUPER);
break;
case 918:
dab(in, "sthbrx", 7, 0, 0, 0, 0);
break;
case 922:
if (in & PPCBMASK)
ill(in);
else
dab(in, "extsh", 6, 1, 0, -1, 0);
break;
case 954:
if (in & PPCBMASK)
ill(in);
else
dab(in, "extsb", 6, 1, 0, -1, 0);
break;
case 982:
if (in & PPCDMASK)
ill(in);
else
dab(in, "icbi", 3, 0, 0, 0, 0);
break;
case 983:
fdab(in, "stfiwx", 7);
break;
case 986:
if (in & PPCBMASK)
ill(in);
else
dab(in, "extsw", 6, 1, 0, -1, PPCF_64);
break;
case 1014:
if (in & PPCDMASK)
ill(in);
else
dab(in, "dcbz", 3, 0, 0, 0, 0);
break;
default:
ill(in);
break;
}
break;
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
ldst(in, ldstnames[PPCGETIDX(in) - 32], 'r', 0);
break;
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
ldst(in, ldstnames[PPCGETIDX(in) - 32], 'f', 0);
break;
case 58:
switch (in & 3)
{
case 0:
ldst(in&~3, "ld", 'r', PPCF_64);
break;
case 1:
ldst(in&~3, "ldu", 'r', PPCF_64);
break;
case 2:
ldst(in&~3, "lwa", 'r', PPCF_64);
break;
default:
ill(in);
break;
}
break;
case 59:
switch (in & 0x3e)
{
case 36:
fdabc(in, "divs", 5, 0);
break;
case 40:
fdabc(in, "subs", 5, 0);
break;
case 42:
fdabc(in, "adds", 5, 0);
break;
case 44:
fdabc(in, "sqrts", 2, 0);
break;
case 48:
fdabc(in, "res", 2, 0);
break;
case 50:
fdabc(in, "muls", 6, 0);
break;
case 56:
fdabc(in, "msubs", 7, 0);
break;
case 58:
fdabc(in, "madds", 7, 0);
break;
case 60:
fdabc(in, "nmsubs", 7, 0);
break;
case 62:
fdabc(in, "nmadds", 7, 0);
break;
default:
ill(in);
break;
}
break;
case 62:
switch (in & 3)
{
case 0:
ldst(in&~3, "std", 'r', PPCF_64);
break;
case 1:
ldst(in&~3, "stdu", 'r', PPCF_64);
break;
default:
ill(in);
break;
}
break;
case 63:
if (in & 32)
{
switch (in & 0x1e)
{
case 4:
fdabc(in, "div", 5, 0);
break;
case 8:
fdabc(in, "sub", 5, 0);
break;
case 10:
fdabc(in, "add", 5, 0);
break;
case 12:
fdabc(in, "sqrt", 2, 0);
break;
case 14:
fdabc(in, "sel", 7, 0);
break;
case 18:
fdabc(in, "mul", 6, 0);
break;
case 20:
fdabc(in, "rsqrte", 1, 0);
break;
case 24:
fdabc(in, "msub", 7, 0);
break;
case 26:
fdabc(in, "madd", 7, 0);
break;
case 28:
fdabc(in, "nmsub", 7, 0);
break;
case 30:
fdabc(in, "nmadd", 7, 0);
break;
case 52:
m_opcode = "XXX dp 52";
break;
default:
ill(in);
break;
}
}
else
{
switch (PPCGETIDX2(in))
{
case 0:
fcmp(in, 'u');
break;
case 12:
fdabc(in, "rsp", 1, 0);
break;
case 14:
fdabc(in, "ctiw", 1, 0);
break;
case 15:
fdabc(in, "ctiwz", 1, 0);
break;
case 32:
fcmp(in, 'o');
break;
case 38:
mtfsb(in, 1);
break;
case 40:
fdabc(in, "neg", 10, 0);
break;
case 64:
mcrf(in, 's'); // mcrfs
break;
case 70:
mtfsb(in, 0);
break;
case 72:
fmr(in);
break;
case 134:
if ((in & 0x006f0800) == 0)
{
m_opcode = StringFromFormat("mtfsfi%s", rcsel[in & 1]);
m_operands = StringFromFormat("cr%d,%d", (int)PPCGETCRD(in), (int)(in & 0xf000) >> 12);
}
else
{
ill(in);
}
break;
case 136:
fdabc(in, "nabs", 10, 0);
break;
case 264:
fdabc(in, "abs", 10, 0);
break;
case 583:
if (in & (PPCAMASK | PPCBMASK))
ill(in);
else
dab(in, "mffs", 4, 0, 0, -1, 0);
break;
case 711:
if ((in & 0x02010000) == 0)
{
m_opcode = StringFromFormat("mtfsf%s", rcsel[in & 1]);
m_operands = StringFromFormat("0x%x,%u", (unsigned int)(in & 0x01fe) >> 17, (int)PPCGETB(in));
}
else
{
ill(in);
}
break;
case 814:
fdabc(in, "fctid", 10, PPCF_64);
break;
case 815:
fdabc(in, "fctidz", 10, PPCF_64);
break;
case 846:
fdabc(in, "fcfid", 10, PPCF_64);
break;
default:
ill(in);
break;
}
}
break;
default:
ill(in);
break;
}
return (m_instr + 1);
}
// simplified interface
std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_address, bool big_endian)
{
u32 opc = opcode;
u32 addr = current_instruction_address;
m_instr = (u32*)&opc;
m_iaddr = (u32*)&addr;
DoDisassembly(big_endian);
return m_opcode.append("\t").append(m_operands);
}
static const char* gprnames[] =
{
" r0", " r1", " r2", " r3", " r4", " r5", " r6", " r7",
" r8", " r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
};
const char* GekkoDisassembler::GetGPRName(u32 index)
{
if (index < 32)
return gprnames[index];
return 0;
}
static const char* fprnames[] =
{
" f0", " f1", " f2", " f3", " f4", " f5", " f6", " f7",
" f8", " f9", "f10", "f11", "f12", "f13", "f14", "f15",
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
};
const char* GekkoDisassembler::GetFPRName(u32 index)
{
if (index < 32)
return fprnames[index];
return 0;
}