// Very accurate PowerPC Architecture disassembler (both 32 and 64-bit instructions are supported)
// Branch Target in output parameters is NOT relative. Its already precalculated
// from effective address of current instruction.
// Note, that old mnemonics and operands will be overwritten, after next disasm
// call. So dont forget to copy them away, if supposed to use them lately.
// Instruction class can be combined from many flags. There can exist, for example,
// "FPU" + "LDST" instruction, except "ILLEGAL", which cannot be combined.
// RLWINM-like instructions mask is placed in output "target" parameter.
#include <stdio.h>
#include <string.h>
#include "Commondefs.h"
#include "ppcd.h"
#define POWERPC_32 // Use generic 32-bit model
//efine POWERPC_64 // Use generic 64-bit model
//efine GEKKO // Use Gekko (32-bit ISA)
//efine BROADWAY // Use Broadway (32-bit ISA)
#define SIMPLIFIED // Allow simplified mnemonics
//efine UPPERCASE // Use upper case strings in output
#define COMMA ", "
#define LPAREN " ("
#define RPAREN ")"
#define HEX1 "0x" // prefix
#define HEX2 "" // suffix
static int bigendian = -1; // Autodetect.
// ---------------------------------------------------------------------------
// Implementation. Look away, code is messed :)
// Dont miss 'l' and '1'.
static PPCD_CB *o;
#define Instr (o->instr)
#define DIS_PC (o->pc)
// Simple decoder
#define DIS_RD ((Instr >> 21) & 0x1f)
#define DIS_RS DIS_RD
#define DIS_RA ((Instr >> 16) & 0x1f)
#define DIS_RB ((Instr >> 11) & 0x1f)
#define DIS_RC ((Instr >> 6) & 0x1f)
#define DIS_RE ((Instr >> 1) & 0x1f)
#define DIS_MB DIS_RC
#define DIS_ME DIS_RE
#define DIS_OE (Instr & 0x400)
#define DIS_SIMM ((s16)Instr)
#define DIS_UIMM (Instr & 0xffff)
#define DIS_CRM ((Instr >> 12) & 0xff)
#define AA (Instr & 2)
#define LK (Instr & 1)
#define AALK (Instr & 3)
#define Rc LK
// GPRs. sp, sd1 and sd2 are named corresponding to PPC EABI.
static char *regname[] = {
#ifdef UPPERCASE
"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"
#else
"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"
#endif
};
#define REGD (regname[DIS_RD])
#define REGS (regname[DIS_RS])
#define REGA (regname[DIS_RA])
#define REGB (regname[DIS_RB])
// Illegal instruction.
static void ill(void)
{
#if 1
o->mnemonic[0] = o->operands[0] = '\0';
#else
strcpy(o->mnemonic, ".word");
sprintf(o->operands, HEX1 "%08X" HEX2, Instr);
#endif
o->iclass = PPC_DISA_ILLEGAL;
}
// Smart SIMM formatting (if hex=1, then force HEX; if s=1, use sign)
static char * simm(int val, int hex, int s)
{
static char out[16];
if( ((val >= -256) && (val <= 256)) && !hex) sprintf(out, "%i", val);
else
{
u16 hexval = (u16)val;
if((hexval & 0x8000) && s) sprintf(out, "-" HEX1 "%04X" HEX2, ((~hexval) & 0xffff) + 1);
else sprintf(out, HEX1 "%04X" HEX2, hexval);
}
return out;
}
// Simple instruction form + reserved bitmask.
static void put(char * mnem, u32 mask, u32 chkval=0, int iclass=PPC_DISA_OTHER)
{
if( (Instr & mask) != chkval ) { ill(); return; }
o->iclass |= iclass;
strncpy(o->mnemonic, mnem, sizeof(o->mnemonic));
}
// Simplified mnemonic trap conditions
static char * t_cond[32] = {
NULL, "lgt", "llt", NULL, "eq", "lge", "lle", NULL,
"gt", NULL, NULL, NULL, "ge", NULL, NULL, NULL,
"lt", NULL, NULL, NULL, "le", NULL, NULL, NULL,
"ne", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
// Trap instructions.
static void trap(int L, int imm)
{
static char t_mode[2] = { 'w', 'd' };
int rd = DIS_RD; // TO
int s = (rd & 0x18) ? 1 : 0;
#ifdef SIMPLIFIED
if(t_cond[rd] != NULL)
{
sprintf(o->mnemonic, "t%c%s%c", t_mode[L & 1], t_cond[rd], imm ? 'i' : 0);
if(imm) sprintf(o->operands, "%s" COMMA "%s", REGA, simm(DIS_SIMM, 0, s));
else sprintf(o->operands, "%s" COMMA "%s", REGA, REGB);
o->iclass |= PPC_DISA_SIMPLIFIED;
}
else
#endif
{
sprintf(o->mnemonic, "t%c%c", t_mode[L & 1], imm ? 'i' : 0);
if(imm) sprintf(o->operands, "%i" COMMA "%s" COMMA "%s", rd, REGA, simm(DIS_SIMM, 0, s));
else sprintf(o->operands, "%i" COMMA "%s" COMMA "%s", rd, REGA, REGB);
}
if(L) o->iclass |= PPC_DISA_64;
o->r[1] = DIS_RA; if(!imm) o->r[2] = DIS_RB;
if(imm)
{
o->immed = Instr & 0xFFFF;
if(o->immed & 0x8000) o->immed |= 0xFFFF0000;
}
}
// DAB mask
#define DAB_D 4
#define DAB_A 2
#define DAB_B 1
// ASB mask
#define ASB_A 4
#define ASB_S 2
#define ASB_B 1
// cr%i
#ifdef UPPERCASE
static char crname[] = "CR";
#else
static char crname[] = "cr";
#endif
// fr%i
#ifdef UPPERCASE
static char fregname[] = "FR";
#else
static char fregname[] = "fr";
#endif
// Integer instructions
// form: 'D' rD, rA, (s)IMM
// 'S' rA, rS, (s)IMM
// 'X' rD, rA, rB
// 'Z' rA, rS, rB
// 'F' frD, rA, rB
// dab LSB bits : [D][A][B] (D should always present)
// 'hex' for logic opcodes, 's' for alu opcodes, 'crfD' and 'L' for cmp opcodes
// 'imm': 1 to show immediate operand
static void integer(char *mnem, char form, int dab, int hex=0, int s=1, int crfD=0, int L=0, int imm=1)
{
char * ptr = o->operands;
int rd = DIS_RD, ra = DIS_RA, rb = DIS_RB;
strncpy(o->mnemonic, mnem, sizeof(o->mnemonic));
if(crfD) ptr += sprintf(ptr, "%s%i" COMMA, crname, rd >> 2); // CMP only
if(L) ptr += sprintf(ptr, "%i" COMMA, rd & 1); // CMP only
if(form == 'D')
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s", REGD);
if(dab & DAB_A)
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGA);
}
if(imm) ptr += sprintf(ptr, COMMA "%s", simm(s ? DIS_SIMM : DIS_UIMM, hex, s));
}
else if(form == 'S')
{
if(dab & ASB_A) ptr += sprintf(ptr, "%s", REGA);
if(dab & ASB_S)
{
if(dab & ASB_A) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGS);
}
if(imm) ptr += sprintf(ptr, COMMA "%s", simm(s ? DIS_SIMM : DIS_UIMM, hex, s));
}
else if(form == 'X') // DAB
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s", REGD);
if(dab & DAB_A)
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGA);
}
if(dab & DAB_B)
{
if(dab & (DAB_D|DAB_A)) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGB);
}
}
else if(form == 'F') // FPU DAB
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s%i", fregname, rd);
if(dab & DAB_A)
{
if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGA);
}
if(dab & DAB_B)
{
if(dab & (DAB_D|DAB_A)) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGB);
}
}
else if(form == 'Z') // ASB
{
if(dab & ASB_A) ptr += sprintf(ptr, "%s", REGA);
if(dab & ASB_S)
{
if(dab & ASB_A) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGS);
}
if(dab & ASB_B)
{
if(dab & (ASB_A|ASB_S)) ptr += sprintf(ptr, "%s", COMMA);
ptr += sprintf(ptr, "%s", REGB);
}
}
else { ill(); return; }
if(form == 'D' || form == 'X' || form == 'F') { o->r[0] = rd; o->r[1] = ra; }
if(form == 'S' || form == 'Z') { o->r[0] = ra; o->r[1] = rd; }
if(form == 'X' || form == 'Z' || form == 'F') o->r[2] = rb;
if((form == 'D' || form == 'S') && imm)
{
o->immed = Instr & 0xFFFF;
if(o->immed & 0x8000 && s) o->immed |= 0xFFFF0000;
}
o->iclass |= PPC_DISA_INTEGER;
}
// Compare instructions (wraps to integer call)
static void cmp(char *l, char *i)
{
char mnem[sizeof(o->mnemonic)];
int rd = DIS_RD;
if(rd & 2) { ill(); return; } // Reserved bit set
if(rd & 1)
{
#ifndef POWERPC_64
{ ill(); return; }
#endif
o->iclass |= PPC_DISA_64;
}
#ifdef SIMPLIFIED
sprintf(mnem, "cmp%s%c%s", l, (rd & 1) ? 'd' : 'w', i);
integer(mnem, (*i == 'i') ? 'D' : 'X', DAB_A|DAB_B, 0, 1, (rd >> 2) ? 1 : 0, 0);
o->iclass |= PPC_DISA_SIMPLIFIED;
#else
sprintf(mnem, "cmp%s%s", l, i);
integer(mnem, (*i == 'i') ? 'D' : 'X', DAB_A|DAB_B, 0, 1, 1, 1);
#endif
}
// Add immediate (wraps to integer call)
static void addi(char *suffix)
{
char mnem[sizeof(o->mnemonic)];
#ifdef SIMPLIFIED
if( (suffix[0] == '\0') && (DIS_RA == 0) ) // Load immediate
{
integer("li", 'D', DAB_D, 0, 1);
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
if( (suffix[0] == 's') && (DIS_RA == 0) ) // Load address HI
{
integer("lis", 'D', DAB_D, 1, 0);
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
if(DIS_UIMM & 0x8000)
{
sprintf(mnem, "subi%s", suffix);
// Fix immediate field.
u16 value = (u16)(~(DIS_UIMM) + 1);
Instr = (Instr & ~0xFFFF) | value;
integer(mnem, 'D', DAB_D|DAB_A, 0, 1);
o->iclass |= PPC_DISA_SIMPLIFIED;
}
else
{
sprintf(mnem, "addi%s", suffix);
integer(mnem, 'D', DAB_D|DAB_A, 0, 0);
}
#else
sprintf(mnem, "addi%s", suffix);
integer(mnem, 'D', DAB_D|DAB_A);
#endif
}
// Branch suffix: AA || LK.
static char *b_opt[4] = { "", "l", "a", "la" };
// Branch condition code: 4 * BO[1] + (BI & 3)
static char * b_cond[8] = {
"ge", "le", "ne", "ns", "lt", "gt", "eq", "so"
};
// Branch on CTR code: BO[0..3]
static char * b_ctr[16] = {
"dnzf", "dzf", NULL, NULL, "dnzt", "dzt", NULL, NULL,
"dnz", "dz", NULL, NULL, NULL, NULL, NULL, NULL
};
// Place target address in operands. Helper for bcx/bx calls.
static char *place_target(char *ptr, int comma)
{
char *old;
u32 *t = (u32 *)&o->target;
if(comma) ptr += sprintf(ptr, "%s", COMMA);
old = ptr;
#ifdef POWERPC_32
ptr += sprintf(ptr, HEX1 "%08X" HEX2, (u32)o->target);
#endif
#ifdef POWERPC_64
ptr = old;
if(bigendian) ptr += sprintf(ptr, HEX1 "%08X_%08X" HEX2, t[0], t[1]);
else ptr += sprintf(ptr, HEX1 "%08X_%08X" HEX2, t[1], t[0]);
#endif
return ptr;
}
// Branch conditional.
// Disp:1 - branch with displacement..
// Disp:0 - branch by register (L:1 for LR, L:0 for CTR).
static void bcx(int Disp, int L)
{
u64 bd = 0;
int bo = DIS_RD, bi = DIS_RA;
char *r = Disp ? "" : (L ? "lr" : "ctr");
char *ptr = o->operands;
if( DIS_RB && !Disp ) { ill(); return; }
o->operands[0] = '\0';
o->target = 0;
o->iclass |= PPC_DISA_BRANCH;
// Calculate displacement and target address
if(Disp)
{
bd = DIS_UIMM & ~3;
if(bd & 0x8000) bd |= 0xffffffffffff0000;
o->target = (AA ? 0 : DIS_PC) + bd;
}
else o->target = 0;
// Calculate branch prediction hint
char y = (bo & 1) ^ ((((s64)bd < 0) && Disp) ? 1 : 0);
y = y ? '+' : '-';
if(bo & 4) // No CTR decrement // BO[2]
{
if(bo & 16) // Branch always // BO[0]
{
#ifdef SIMPLIFIED
sprintf(o->mnemonic, "b%s%s", r, b_opt[Disp ? AALK : LK]);
if(Disp) ptr = place_target(ptr, 0);
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
#endif // SIMPLIFIED
}
else // Branch conditional
{
if(bo & 2) { ill(); return; } // BO[3]
#ifdef SIMPLIFIED
char *cond = b_cond[((bo & 8) >> 1) | (bi & 3)];
if(cond != NULL) // BO[1]
{
sprintf(o->mnemonic, "b%s%s%s%c", cond, r, b_opt[Disp ? AALK : LK], y);
if(bi >= 4) ptr += sprintf(ptr, "%s%i", crname, bi >> 2);
if(Disp) ptr = place_target(ptr, bi >= 4);
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
#endif // SIMPLIFIED
}
}
else // Decrement CTR
{
if(!L && !Disp) { ill(); return; }
if(bo & 8) { ill(); return; } // BO[1]
#ifdef SIMPLIFIED
if(b_ctr[bo >> 1])
{
sprintf(o->mnemonic, "b%s%s%s%c", b_ctr[bo >> 1], r, b_opt[Disp ? AALK : LK], y);
if(!(bo & 16)) ptr += sprintf(ptr, "%i", bi);
if(Disp) ptr = place_target(ptr, !(bo & 16));
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
#endif // SIMPLIFIED
}
// Not simplified standard form
sprintf(o->mnemonic, "bc%s%s", r, b_opt[Disp ? AALK : LK]);
ptr += sprintf(ptr, "%i" COMMA "%i", bo, bi);
if(Disp) ptr = place_target(ptr, 1);
}
// Branch unconditional
static void bx(void)
{
// Calculate displacement and target address
u64 bd = Instr & 0x03fffffc;
if(bd & 0x02000000) bd |= 0xfffffffffc000000;
o->target = (AA ? 0 : DIS_PC) + bd;
o->iclass |= PPC_DISA_BRANCH;
sprintf(o->mnemonic, "b%s", b_opt[AALK]);
place_target(o->operands, 0);
}
// Move CR field
static void mcrf(void)
{
if(Instr & 0x63f801) { ill(); return; }
strncpy(o->mnemonic, "mcrf", sizeof(o->mnemonic));
sprintf(o->operands, "%s%i" COMMA "%s%i", crname, DIS_RD >> 2, crname, DIS_RA >> 2);
}
// CR logic operations
static void crop(char *name, char *simp="", int ddd=0, int daa=0)
{
if(Instr & 1) { ill(); return; }
int crfD = DIS_RD, crfA = DIS_RA, crfB = DIS_RB;
#ifdef SIMPLIFIED
if( crfA == crfB )
{
if( (crfD == crfA) && ddd )
{
sprintf(o->mnemonic, "cr%s", simp);
sprintf(o->operands, "%i", crfD);
o->r[0] = crfD;
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
if( daa )
{
sprintf(o->mnemonic, "cr%s", simp);
sprintf(o->operands, "%i" COMMA "%i", crfD, crfA);
o->r[0] = crfD; o->r[1] = crfA;
o->iclass |= PPC_DISA_SIMPLIFIED;
return;
}
}
#endif
sprintf(o->mnemonic, "cr%s", name);
sprintf(o->operands, "%i" COMMA "%i" COMMA "%i", crfD, crfA, crfB);
o->r[0] = crfD; o->r[1] = crfA; o->r[2] = crfB;
}
#define MASK32(b, e) \
{ \
u32 mask = ((u32)0xffffffff >> (b)) ^ (((e) >= 31) ? 0 : ((u32)0xffffffff) >> ((e) + 1)); \
o->target = ((b) > (e)) ? (~mask) : (mask); \
}
#define MASK64(b, e) \
{ \
u64 mask = ((u64)0xffffffffffffffff >> (b)) ^ (((e) >= 63) ? 0 : ((u64)0xffffffffffffffff) >> ((e) + 1)); \
o->target = ((b) > (e)) ? (~mask) : (mask); \
}
// Rotate left word.
static void rlw(char *name, int rb, int ins=0)
{
int mb = DIS_MB, me = DIS_ME;
char * ptr = o->operands;
sprintf(o->mnemonic, "rlw%s%c", name, Rc ? '.' : '\0');
ptr += sprintf(ptr, "%s" COMMA "%s" COMMA, REGA, REGS);
if(rb) ptr += sprintf(ptr, "%s" COMMA, REGB);
else ptr += sprintf(ptr, "%i" COMMA, DIS_RB); // sh
ptr += sprintf(ptr, "%i" COMMA "%i", mb, me);
// Put mask in target.
MASK32(mb, me);
#ifdef POWERPC_64
MASK64(mb+32, me+32);
#endif
o->r[0] = DIS_RA;
o->r[1] = DIS_RS;
if(rb) o->r[2] = DIS_RB;
o->iclass |= PPC_DISA_INTEGER;
}
// RLD mask
#define RLDM_LEFT 0 // MASK(b, 63)
#define RLDM_RIGHT 1 // MASK(0, e)
#define RLDM_INS 2 // MASK(b, ~n)
// Rotate left double-word.
static void rld(char *name, int rb, int mtype)
{
#ifdef POWERPC_64
int m = DIS_MB, n = DIS_RB;
if(Instr & 0x20) m += 32; // b or e
if(Instr & 0x02) n += 32; // sh
char * ptr = o->operands;
sprintf(o->mnemonic, "rld%s%c", name, Rc ? '.' : '\0');
ptr += sprintf(ptr, "%s" COMMA "%s" COMMA, REGA, REGS);
if(rb) ptr += sprintf(ptr, "%s" COMMA, REGB);
else ptr += sprintf(ptr, "%i" COMMA, n);
ptr += sprintf(ptr, "%i", m);
// Put mask in target.
switch(mtype)
{
case RLDM_LEFT: MASK64(m, 63); break;
case RLDM_RIGHT: MASK64(0, m); break;
case RLDM_INS: MASK64(m, ~n); break;
}
o->r[0] = DIS_RA;
o->r[1] = DIS_RS;
if(rb) o->r[2] = DIS_RB;
o->iclass |= PPC_DISA_64 | PPC_DISA_INTEGER;
#endif
}
// Load/Store.
static void ldst(char *name, int x/*indexed*/, int load=1, int L=0, int string=0, int fload=0)
{
if(x) integer(name, fload ? 'F' : 'X', DAB_D|DAB_A|DAB_B);
else
{
int rd = DIS_RD, ra = DIS_RA;
s16 imm = DIS_SIMM;
strcpy (o->mnemonic, name);
if(fload) sprintf (o->operands, "%s%i" COMMA "%s" LPAREN "%s" RPAREN, fregname, rd, simm(imm, 0, 1), regname[ra]);
else sprintf (o->operands, "%s" COMMA "%s" LPAREN "%s" RPAREN, regname[rd], simm(imm, 0, 1), regname[ra]);
o->r[0] = rd;
o->r[1] = ra;
o->immed = DIS_UIMM & 0x8000 ? DIS_UIMM | 0xFFFF0000 : DIS_UIMM;
}
o->iclass = PPC_DISA_LDST;
if(L) o->iclass |= PPC_DISA_64;
if(string) o->iclass |= PPC_DISA_STRING;
if(fload) o->iclass |= PPC_DISA_FPU;
}
// Cache.
static void cache(char *name, int flag=PPC_DISA_OTHER)
{
if (DIS_RD) { ill(); return; }
else
{
integer(name, 'X', DAB_A|DAB_B);
o->r[0] = o->r[1];
o->r[1] = o->r[2];
o->r[2] = 0;
o->iclass &= ~PPC_DISA_INTEGER;
o->iclass |= flag;
}
}
static void movesr(char *name, int from, int L, int xform)
{
int reg = DIS_RD, sreg = DIS_RA & 0xF, regb = DIS_RB;
strncpy(o->mnemonic, name, sizeof(o->mnemonic));
if(xform)
{
if(Instr & 0x001F0001) { ill(); return; }
sprintf(o->operands, "%s" COMMA "%s", regname[reg], regname[regb]);
o->r[0] = reg;
o->r[1] = regb;
}
else
{
if(Instr & 0x0010F801) { ill(); return; }
if(from)
{
sprintf(o->operands, "%s" COMMA "%i", regname[reg], sreg);
o->r[0] = reg;
o->r[1] = sreg;
}
else
{
sprintf(o->operands, "%i" COMMA "%s", sreg, regname[reg]);
o->r[0] = sreg;
o->r[1] = reg;
}
}
if(L) o->iclass |= PPC_DISA_OEA | PPC_DISA_OPTIONAL | PPC_DISA_BRIDGE | PPC_DISA_64;
else o->iclass |= PPC_DISA_OEA | PPC_DISA_BRIDGE;
}
static void mtcrf(void)
{
int rs = DIS_RS, crm = DIS_CRM;
#ifdef SIMPLIFIED
if(crm == 0xFF)
{
strncpy(o->mnemonic, "mtcr", sizeof(o->mnemonic));
sprintf(o->operands, "%s", regname[rs]);
}
else
#endif
{
strncpy(o->mnemonic, "mtcrf", sizeof(o->mnemonic));
sprintf(o->operands, HEX1 "%02X" HEX2 COMMA "%s", crm, regname[rs]);
}
o->r[0] = rs;
}
static void mcrxr(void)
{
if (Instr & 0x007FF800) { ill(); return; }
strcpy (o->mnemonic, "mcrxr");
sprintf (o->operands, "%s%i", crname, DIS_RD >> 2);
o->r[0] = DIS_RD >> 2;
}
static char *spr_name(int n)
{
static char def[8];
switch(n)
{
// General architecture special-purpose registers.
case 1: return "XER";
case 8: return "LR";
case 9: return "CTR";
case 18: return "DSISR";
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";
#ifdef POWERPC_64
case 280: return "ASR";
#endif
case 284: return "TBL";
case 285: return "TBU";
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";
// Optional registers.
#if !defined(GEKKO) && !defined(BROADWAY)
case 282: return "EAR";
case 1013: return "DABR";
case 1022: return "FPECR";
case 1023: return "PIR";
#endif
// Gekko-specific SPRs
#ifdef GEKKO
case 282: return "EAR";
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 "DMAU";
case 923: return "DMAL";
case 936: return "UMMCR0";
case 940: return "UMMCR1";
case 937: return "UPMC1";
case 938: return "UPMC2";
case 939: return "USIA";
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 1013: return "DABR";
case 1017: return "L2CR";
case 1019: return "ICTC";
case 1020: return "THRM1";
case 1021: return "THRM2";
case 1022: return "THRM3";
#endif
}
sprintf(def, "%u", n);
return def;
}
static char *tbr_name(int n)
{
static char def[8];
switch(n)
{
// General architecture time-base registers.
case 268: return "TBL";
case 269: return "TBU";
}
sprintf(def, "%u", n);
return def;
}
static void movespr(int from)
{
int spr = (DIS_RB << 5) | DIS_RA, f = 1;
char *fix;
if( !((spr == 1) || (spr == 8) || (spr == 9)) ) o->iclass |= PPC_DISA_OEA;
// Handle simplified mnemonic
if (spr == 1) { fix = "xer"; o->iclass |= PPC_DISA_SIMPLIFIED; }
else if (spr == 8) { fix = "lr"; o->iclass |= PPC_DISA_SIMPLIFIED; }
else if (spr == 9) { fix = "ctr"; o->iclass |= PPC_DISA_SIMPLIFIED; }
else { fix = "spr"; f = 0; }
// Mnemonics and operands.
sprintf (o->mnemonic, "m%c%s", from ? 'f' : 't', fix);
if (f)
{
sprintf (o->operands, "%s", regname[DIS_RD]);
o->r[0] = DIS_RD;
}
else
{
if (from)
{
sprintf (o->operands, "%s" COMMA "%s", regname[DIS_RD], spr_name(spr));
o->r[0] = DIS_RD;
o->r[1] = spr;
}
else
{
sprintf (o->operands, "%s" COMMA "%s", spr_name(spr), regname[DIS_RD]);
o->r[0] = spr;
o->r[1] = DIS_RD;
}
}
}
static void movetbr(void)
{
int tbr = (DIS_RB << 5) | DIS_RA, f = 1;
char *fix;
// Handle simplified mnemonic
if (tbr == 268) { fix = "tbl"; o->iclass |= PPC_DISA_SIMPLIFIED; }
else if (tbr == 269) { fix = "tbu"; o->iclass |= PPC_DISA_SIMPLIFIED; }
else { fix = "tb"; f = 0; }
// Mnemonics and operands.
sprintf (o->mnemonic, "mf%s", fix);
if (f)
{
sprintf (o->operands, "%s", regname[DIS_RD]);
o->r[0] = DIS_RD;
}
else
{
sprintf (o->operands, "%s" COMMA "%s", regname[DIS_RD], tbr_name(tbr));
o->r[0] = DIS_RD;
o->r[1] = tbr;
}
}
static void srawi(void)
{
int rs = DIS_RS, ra = DIS_RA, sh = DIS_RB;
sprintf (o->mnemonic, "srawi%c", Rc ? '.' : 0);
sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[ra], regname[rs], sh);
o->r[0] = ra;
o->r[1] = rs;
o->r[2] = sh;
o->iclass = PPC_DISA_INTEGER;
}
static void sradi(void)
{
int rs = DIS_RS, ra = DIS_RA, sh = (((Instr >> 1) & 1) << 5) | DIS_RB;
sprintf (o->mnemonic, "sradi%c", Rc ? '.' : 0);
sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[ra], regname[rs], sh);
o->r[0] = ra;
o->r[1] = rs;
o->r[2] = sh;
o->iclass = PPC_DISA_INTEGER | PPC_DISA_64;
}
static void lsswi(char *name)
{
int rd = DIS_RD, ra = DIS_RA, nb = DIS_RB;
strcpy (o->mnemonic, name);
sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[rd], regname[ra], nb);
o->r[0] = rd;
o->r[1] = ra;
o->r[2] = nb;
o->iclass = PPC_DISA_LDST | PPC_DISA_STRING;
}
#define FPU_DAB 1
#define FPU_DB 2
#define FPU_DAC 3
#define FPU_DACB 4
#define FPU_D 5
static void fpu(char *name, u32 mask, int type, int flag=PPC_DISA_OTHER)
{
int d = DIS_RD, a = DIS_RA, c = DIS_RC, b = DIS_RB;
if(Instr & mask) { ill(); return; }
strcpy (o->mnemonic, name);
switch (type)
{
case FPU_DAB:
sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, b);
o->r[0] = d; o->r[1] = a; o->r[2] = b;
break;
case FPU_DB:
sprintf (o->operands, "%s%i" COMMA "%s%i", fregname, d, fregname, b);
o->r[0] = d; o->r[1] = b;
break;
case FPU_DAC:
sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c);
o->r[0] = d; o->r[1] = a; o->r[2] = c;
break;
case FPU_DACB:
sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c, fregname, b);
o->r[0] = d; o->r[1] = a; o->r[2] = c; o->r[3] = b;
break;
case FPU_D:
sprintf (o->operands, "%s%i", fregname, d);
o->r[0] = d;
break;
}
o->iclass = PPC_DISA_FPU | flag;
}
static void fcmp(char *name)
{
int crfd = DIS_RD >> 2, ra = DIS_RA, rb = DIS_RB;
if (Instr & 0x00600001) { ill(); return; }
strcpy (o->mnemonic, name);
sprintf (o->operands, "%i" COMMA "%s%i" COMMA "%s%i", crfd, fregname, ra, fregname, rb);
o->r[0] = crfd; o->r[1] = ra; o->r[2] = rb;
o->iclass = PPC_DISA_FPU;
}
static void mtfsf(void)
{
int fm = (Instr >> 17) & 0xFF, rb = DIS_RB;
if(Instr & 0x02010000) { ill(); return; }
sprintf (o->mnemonic, "mtfsf%c", Rc ? '.' : 0);
sprintf (o->operands, HEX1 "%02X" HEX2 COMMA "%s%i", fm, fregname, rb);
o->r[0] = fm; o->r[1] = rb;
o->iclass = PPC_DISA_FPU;
}
static void mtfsb(char *name)
{
int crbd = DIS_RD;
if (Instr & 0x001FF800) { ill(); return; }
strcpy (o->mnemonic, name);
sprintf (o->operands, "%i", crbd);
o->r[0] = crbd;
o->iclass = PPC_DISA_FPU;
}
static void mcrfs(void)
{
int crfD = DIS_RD >> 2, crfS = DIS_RA >> 2;
if (Instr & 0x0063F801) { ill(); return; }
strcpy (o->mnemonic, "mcrfs");
sprintf (o->operands, "%s%i" COMMA "%s%i", crname, crfD, crname, crfS);
o->r[0] = crfD; o->r[1] = crfS;
o->iclass = PPC_DISA_FPU;
}
static void mtfsfi(void)
{
int crfD = DIS_RD >> 2, imm = DIS_RB >> 1;
if (Instr & 0x007F0800) { ill(); return; }
sprintf (o->mnemonic, "mtfsfi%c", Rc ? '.' : 0);
sprintf (o->operands, "%s%i" COMMA "%i", crname, crfD, imm);
o->r[0] = crfD; o->r[1] = imm;
o->iclass = PPC_DISA_FPU;
}
/*
***********************************************************************************
* Architecture-specific extensions:
* Processor model: GEKKO
***********************************************************************************
*/
#ifdef GEKKO
static void ps_cmpx(int n)
{
static char *fix[] = { "u0", "o0", "u1", "o1" };
if(Instr & 0x00600001) { ill(); return; }
sprintf(o->mnemonic, "ps_cmp%s", fix[n]);
o->r[0] = DIS_RD>>2; o->r[1] = DIS_RA; o->r[2] = DIS_RB;
sprintf(o->operands, "%s%d" COMMA "%s%d" COMMA "%s%d", crname, o->r[0], fregname, o->r[1], fregname, o->r[2]);
o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC;
}
static char *ps_ldst_offs(unsigned long val)
{
static char buf[8];
if(val == 0)
{
return "0";
}
else
{
if(val <= 128)
{
sprintf(buf, "%i", val);
return buf;
}
if(val & 0x800) sprintf(buf, "-" HEX1 "%03X" HEX2, ((~val) & 0xfff) + 1);
else sprintf(buf, HEX1 "%03X" HEX2, val);
return buf;
}
}
static void ps_ldst(char *fix)
{
int s = DIS_RS, a = DIS_RA, d = (Instr & 0xfff);
sprintf(o->mnemonic, "psq_%s", fix);
sprintf( o->operands, "%s%i" COMMA "%s" LPAREN "%s" RPAREN COMMA "%i" COMMA "%i",
fregname, s, ps_ldst_offs(d), regname[a], (Instr >> 15) & 1, (Instr >> 12) & 7 );
o->r[0] = s; o->r[1] = a; o->r[2] = DIS_RB >> 1;
o->immed = d & 0x800 ? d | 0xFFFFF000 : d;
o->iclass = PPC_DISA_FPU | PPC_DISA_LDST | PPC_DISA_SPECIFIC;
}
static void ps_ldstx(char *fix)
{
int d = DIS_RD, a = DIS_RA, b = DIS_RB;
if(Instr & 1) { ill(); return; }
sprintf(o->mnemonic, "psq_%s", fix);
sprintf(o->operands, "%s%i" COMMA "%s" COMMA "%s" COMMA "%i" COMMA "%i", fregname, d, regname[a], regname[b], (Instr >> 10) & 1, (Instr >> 7) & 7);
o->r[0] = d; o->r[1] = a; o->r[2] = b; o->r[3] = DIS_RC >> 1;
o->iclass = PPC_DISA_FPU | PPC_DISA_LDST | PPC_DISA_SPECIFIC;
}
static void ps_dacb(char *fix)
{
int a = DIS_RA, b = DIS_RB, c = DIS_RC, d = DIS_RD;
sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0);
sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c, fregname, b);
o->r[0] = d; o->r[1] = a; o->r[2] = c; o->r[3] = b;
o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC;
}
static void ps_dac(char *fix)
{
int a = DIS_RA, c = DIS_RC, d = DIS_RD;
if(Instr & 0x0000F800) { ill(); return; }
sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0);
sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c);
o->r[0] = d; o->r[1] = a; o->r[2] = c;
o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC;
}
static void ps_dab(char *fix, int unmask=0)
{
int d = DIS_RD, a = DIS_RA, b = DIS_RB;
if(Instr & 0x000007C0 && !unmask) { ill(); return; }
sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0);
sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, b);
o->r[0] = d; o->r[1] = a; o->r[2] = b;
o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC;
}
static void ps_db(char *fix, int aonly=0)
{
int d = DIS_RD, b = DIS_RB;
if(aonly) { if(Instr & 0x001F0000) { ill(); return; } }
else { if(Instr & 0x001F07C0) { ill(); return; } }
sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0);
sprintf(o->operands, "%s%i" COMMA "%s%i", fregname, d, fregname, b);
o->r[0] = d; o->r[1] = b;
o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC;
}
#endif /* END OF GEKKO */
// ---------------------------------------------------------------------------
void PPCDisasm(PPCD_CB *discb)
{
// Save parameters in local variables for static calls
o = discb;
if(o == NULL) return;
// Detect endianness order.
if(bigendian == -1)
{
u8 test_value[2] = { 0xAA, 0xBB };
u16 *value = (u16 *)test_value;
if(*value == 0xAABB) bigendian = 1;
else bigendian = 0;
}
// Reset output parameters
o->iclass = PPC_DISA_OTHER;
o->r[0] = o->r[1] = o->r[2] = o->r[3] = 0;
o->immed = 0;
o->target = 0;
o->mnemonic[0] = o->operands[0] = '\0';
// Lets go!
/*
* Main table
*/
switch(Instr >> 26 /* Main opcode, base 8 */) {
#ifdef POWERPC_64
case 002: trap(1, 1); break; // tdi
#endif
case 003: trap(0, 1); break; // twi
case 007: integer("mulli", 'D', DAB_D|DAB_A); break; // mulli
case 010: integer("subfic", 'D', DAB_D|DAB_A); break; // subfic
case 012: cmp("l", "i"); break; // cmpli
case 013: cmp("", "i"); break; // cmpi
case 014: addi("c"); break; // addic
case 015: addi("c."); break; // addic.
case 016: addi(""); break; // addi
case 017: addi("s"); break; // addis
case 020: bcx(1, 0); break; // bcx
case 021: put("sc", 0x03ffffff, 2); break; // sc
case 022: bx(); break; // bx
case 024: rlw("imi", 0, 1); break; // rlwimix
case 025: rlw("inm", 0); break; // rlwinmx
case 027: rlw("nm", 1); break; // rlwnmx
case 030: // ori
#ifdef SIMPLIFIED
if(Instr == 0x60000000) put("nop", 0, 0, PPC_DISA_INTEGER | PPC_DISA_SIMPLIFIED);
else
#endif
integer("ori", 'S', ASB_A|ASB_S, 1, 0); break;
case 031: integer("oris", 'S', ASB_A|ASB_S, 1, 0); break; // oris
case 032: integer("xori", 'S', ASB_A|ASB_S, 1, 0); break; // xori
case 033: integer("xoris", 'S', ASB_A|ASB_S, 1, 0); break; // xoris
case 034: integer("andi.", 'S', ASB_A|ASB_S, 1, 0); break; // andi.
case 035: integer("andis.", 'S', ASB_A|ASB_S, 1, 0); break; // andis.
case 040: ldst("lwz", 0, 1); break; // lwz
case 041: ldst("lwzu", 0, 1); break; // lwzu
case 042: ldst("lbz", 0, 1); break; // lbz
case 043: ldst("lbzu", 0, 1); break; // lbzu
case 044: ldst("stw", 0, 0); break; // stw
case 045: ldst("stwu", 0, 0); break; // stwu
case 046: ldst("stb", 0, 0); break; // stb
case 047: ldst("stbu", 0, 0); break; // stbu
case 050: ldst("lhz", 0, 1); break; // lhz
case 051: ldst("lhzu", 0, 1); break; // lhzu
case 052: ldst("lha", 0, 1); break; // lha
case 053: ldst("lhau", 0, 1); break; // lhau
case 054: ldst("sth", 0, 0); break; // sth
case 055: ldst("sthu", 0, 0); break; // sthu
case 056: ldst("lmw", 0, 1, 0, 1); break; // lmw
case 057: ldst("stmw", 0, 0, 0, 1); break; // stmw
case 060: ldst("lfs", 0, 1, 0, 0, 1); break; // lfs
case 061: ldst("lfsu", 0, 1, 0, 0, 1); break; // lfsu
case 062: ldst("lfd", 0, 1, 0, 0, 1); break; // lfd
case 063: ldst("lfdu", 0, 1, 0, 0, 1); break; // lfdu
case 064: ldst("stfs", 0, 0, 0, 0, 1); break; // stfs
case 065: ldst("stfsu", 0, 0, 0, 0, 1); break; // stfsu
case 066: ldst("stfd", 0, 0, 0, 0, 1); break; // stfd
case 067: ldst("stfdu", 0, 0, 0, 0, 1); break; // stfdu
/*
* Extention 1.
*/
case 023:
switch((Instr >> 1) & 0x3ff /* Extended opcode 023, base 8 */) {
case 00020: bcx(0, 1); break; // bclrx
case 01020: bcx(0, 0); break; // bcctrx
case 00000: mcrf(); break; // mcrf
case 00401: crop("and"); break; // crand
case 00201: crop("andc"); break; // crandc
case 00441: crop("eqv", "set", 1); break; // creqv
case 00341: crop("nand"); break; // crnand
case 00041: crop("nor", "not", 0, 1); break; // crnor
case 00701: crop("or", "move", 0, 1); break; // cror
case 00641: crop("orc"); break; // crorc
case 00301: crop("xor", "clr", 1); break; // crxor
case 00226: put("isync", 0x3fff801); break; // isync
#ifdef POWERPC_32
case 00062: put("rfi", 0x3fff801, 0, PPC_DISA_OEA | PPC_DISA_BRIDGE ); break; // rfi
#endif
#ifdef POWERPC_64
case 00022: put("rfid", 0x3fff801, 0, PPC_DISA_OEA | PPC_DISA_64 ); break; // rfid
#endif
default: ill(); break;
} break;
#ifdef POWERPC_64
case 036:
switch((Instr >> 1) & 0xf /* Rotate left double */) {
case 0x0: rld("icl", 0, RLDM_LEFT); break; // rldiclx
case 0x1: rld("icl", 0, RLDM_LEFT); break;
case 0x2: rld("icr", 0, RLDM_RIGHT); break; // rldicrx
case 0x3: rld("icr", 0, RLDM_RIGHT); break;
case 0x4: rld("ic", 0, RLDM_INS); break; // rldicx
case 0x5: rld("ic", 0, RLDM_INS); break;
case 0x6: rld("imi", 0, RLDM_INS); break; // rldimix
case 0x7: rld("imi", 0, RLDM_INS); break;
case 0x8: rld("cl", 1, RLDM_LEFT); break; // rldclx
case 0x9: rld("cr", 1, RLDM_RIGHT); break; // rldcrx
default: ill(); break;
} break;
#endif
/*
* Extention 2.
*/
#define OE 02000
case 037:
switch(Instr & 0x7ff /* Extended opcode 037, base 8 */) {
case 00000: cmp("", ""); break; // cmp
case 00010: // tw
#ifdef SIMPLIFIED
if(Instr == 0x7FE00008) put("trap", 0, 0, PPC_DISA_SIMPLIFIED);
else
#endif
trap(0, 0); break;
case 00020: integer("subfc", 'X', DAB_D|DAB_A|DAB_B); break; // subfcx
case 00020|OE: integer("subfco", 'X', DAB_D|DAB_A|DAB_B); break;
case 00021: integer("subfc.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00021|OE: integer("subfco.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00024: integer("addc", 'X', DAB_D|DAB_A|DAB_B); break; // addcx
case 00024|OE: integer("addco", 'X', DAB_D|DAB_A|DAB_B); break;
case 00025: integer("addc.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00025|OE: integer("addco.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00026: integer("mulhwu", 'X', DAB_D|DAB_A|DAB_B); break; // mulhwu
case 00027: integer("mulhwu.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00046: if(DIS_RA | DIS_RB) ill(); // mfcr
else { integer("mfcr", 'D', DAB_D, 0,0,0,0,0); o->iclass = PPC_DISA_OTHER; } break;
case 00050: ldst("lwarx", 1); break; // lwarx
case 00056: ldst("lwzx", 1); break; // lwzx
case 00060: integer("slw", 'Z', ASB_A|ASB_S|ASB_B); break; // slwx
case 00061: integer("slw.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 00064: if(DIS_RB) ill(); // cntlzwx
else integer("cntlzw", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 00065: if(DIS_RB) ill();
else integer("cntlzw.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 00070: integer("and", 'Z', ASB_A|ASB_S|ASB_B); break; // andx
case 00071: integer("and.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 00100: cmp("l", ""); break; // cmpl
case 00120: integer("subf", 'X', DAB_D|DAB_A|DAB_B); break; // subfx
case 00120|OE: integer("subfo", 'X', DAB_D|DAB_A|DAB_B); break;
case 00121: integer("subf.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00121|OE: integer("subfo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00154: cache("dcbst"); break; // dcbst
case 00156: ldst("lwzux", 1); break; // lwzux
case 00170: integer("andc", 'Z', ASB_A|ASB_S|ASB_B); break; // andcx
case 00171: integer("andc.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 00226: integer("mulhw", 'X', DAB_D|DAB_A|DAB_B); break; // mulhw
case 00227: integer("mulhw.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00246: if(DIS_RA || DIS_RB) ill(); // mfmsr
else { integer("mfmsr", 'X', DAB_D); o->iclass = PPC_DISA_OEA; } break;
case 00254: cache("dcbf"); break; // dcbf
case 00256: ldst("lbzx", 1, 1, 0); break; // lbzx
case 00320: if(DIS_RB) ill(); // negx
else integer("neg", 'X', DAB_D|DAB_A); break;
case 00321: if(DIS_RB) ill();
else integer("neg.", 'X', DAB_D|DAB_A); break;
case 00320|OE: if(DIS_RB) ill();
else integer("nego", 'X', DAB_D|DAB_A); break;
case 00321|OE: if(DIS_RB) ill();
else integer("nego.", 'X', DAB_D|DAB_A); break;
case 00356: ldst("lbzux", 1, 1); break; // lbzux
case 00370: // norx
#ifdef SIMPLIFIED
if(DIS_RS == DIS_RB) { integer("not", 'Z', ASB_A|ASB_S); o->iclass |= PPC_DISA_SIMPLIFIED; }
else
#endif
integer("nor", 'Z', ASB_A|ASB_S|ASB_B); break;
case 00371: integer("nor.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 00420: integer("subfe", 'X', DAB_D|DAB_A|DAB_B); break; // subfex
case 00420|OE: integer("subfeo", 'X', DAB_D|DAB_A|DAB_B); break;
case 00421: integer("subfe.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00421|OE: integer("subfeo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00424: integer("adde", 'X', DAB_D|DAB_A|DAB_B); break; // addex
case 00424|OE: integer("addeo", 'X', DAB_D|DAB_A|DAB_B); break;
case 00425: integer("adde.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00425|OE: integer("addeo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00440: mtcrf(); break; // mtcrf
#ifdef POWERPC_32
case 00444: if(DIS_RA || DIS_RB) ill(); // mtmsr
else { integer("mtmsr", 'X', DAB_D); o->iclass = PPC_DISA_OEA | PPC_DISA_BRIDGE; } break;
#endif
case 00455: ldst("stwcx.", 1, 0, 0); break; // stwcx.
case 00456: ldst("stwx", 1, 0, 0); break; // stwx
case 00556: ldst("stwux", 1, 0, 0); break; // stwux
case 00620: if(DIS_RB) ill(); // subfzex
else integer("subfze", 'X', DAB_D|DAB_A); break;
case 00620|OE: if(DIS_RB) ill();
else integer("subfzeo", 'X', DAB_D|DAB_A); break;
case 00621: if(DIS_RB) ill();
else integer("subfze.", 'X', DAB_D|DAB_A); break;
case 00621|OE: if(DIS_RB) ill();
else integer("subfzeo.", 'X', DAB_D|DAB_A); break;
case 00624: if(DIS_RB) ill(); // addzex
else integer("addze", 'X', DAB_D|DAB_A); break;
case 00624|OE: if(DIS_RB) ill();
else integer("addzeo", 'X', DAB_D|DAB_A); break;
case 00625: if(DIS_RB) ill();
else integer("addze.", 'X', DAB_D|DAB_A); break;
case 00625|OE: if(DIS_RB) ill();
else integer("addzeo.", 'X', DAB_D|DAB_A); break;
#ifdef POWERPC_32
case 00644: movesr("mtsr", 0, 0, 0); break; // mtsr
#endif
case 00656: ldst("stbx", 1, 0, 0); break; // stbx
case 00720: if(DIS_RB) ill(); // subfmex
else integer("subfme", 'X', DAB_D|DAB_A); break;
case 00720|OE: if(DIS_RB) ill();
else integer("subfmeo", 'X', DAB_D|DAB_A); break;
case 00721: if(DIS_RB) ill();
else integer("subfme.", 'X', DAB_D|DAB_A); break;
case 00721|OE: if(DIS_RB) ill();
else integer("subfmeo.", 'X', DAB_D|DAB_A); break;
case 00724: if(DIS_RB) ill(); // addmex
else integer("addme", 'X', DAB_D|DAB_A); break;
case 00724|OE: if(DIS_RB) ill();
else integer("addmeo", 'X', DAB_D|DAB_A); break;
case 00725: if(DIS_RB) ill();
else integer("addme.", 'X', DAB_D|DAB_A); break;
case 00725|OE: if(DIS_RB) ill();
else integer("addmeo.", 'X', DAB_D|DAB_A); break;
case 00726: integer("mullw", 'X', DAB_D|DAB_A|DAB_B); break; // mullwx
case 00726|OE: integer("mullwo", 'X', DAB_D|DAB_A|DAB_B); break;
case 00727: integer("mullw.", 'X', DAB_D|DAB_A|DAB_B); break;
case 00727|OE: integer("mullwo.", 'X', DAB_D|DAB_A|DAB_B); break;
#ifdef POWERPC_32
case 00744: movesr("mtsrin", 0, 0, 1); break; // mtsrin
#endif
case 00754: cache("dcbtst"); break; // dcbtst
case 00756: ldst("stbux", 1, 0, 0); break; // stbux
case 01024: integer("add", 'X', DAB_D|DAB_A|DAB_B); break; // addx
case 01024|OE: integer("addo", 'X', DAB_D|DAB_A|DAB_B); break;
case 01025: integer("add.", 'X', DAB_D|DAB_A|DAB_B); break;
case 01025|OE: integer("addo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 01054: cache("dcbt"); break; // dcbt
case 01056: ldst("lhzx", 1); break; // lhzx
case 01070: integer("eqv", 'Z', ASB_A|ASB_S|ASB_B); break; // eqvx
case 01071: integer("eqv.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 01144: if(DIS_RD || DIS_RA) ill(); // tlbie
else { integer("tlbie", 'X', DAB_B); o->iclass = PPC_DISA_OEA | PPC_DISA_OPTIONAL; o->r[0] = o->r[2]; o->r[2] = 0; } break;
case 01154: integer("eciwx", 'X', DAB_D|DAB_A|DAB_B); o->iclass = PPC_DISA_OPTIONAL; break; // eciwx
case 01156: ldst("lhzux", 1); break; // lhzux
case 01170: integer("xor", 'Z', ASB_A|ASB_S|ASB_B); break; // xorx
case 01171: integer("xor.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 01246: movespr(1); break; // mfspr
case 01256: ldst("lhax", 1); break; // lhax
#if !defined(GEKKO)
case 01344: put("tlbia", 0x03FFF800, 0, PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // tlbia
#endif
case 01346: movetbr(); break; // mftb
case 01356: ldst("lhaux", 1); break; // lhaux
case 01456: ldst("sthx", 1, 0); break; // sthx
case 01470: integer("orc", 'Z', ASB_A|ASB_S|ASB_B); break; // orcx
case 01471: integer("orc.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 01554: integer("ecowx", 'X', DAB_D|DAB_A|DAB_B); o->iclass = PPC_DISA_OPTIONAL; break; // ecowx
case 01556: ldst("sthux", 1, 0); break; // sthux
case 01570: integer("or", 'Z', ASB_A|ASB_S|ASB_B); break; // orx
case 01571: integer("or.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 01626: integer("divwu", 'X', DAB_D|DAB_A|DAB_B); break; // divwux
case 01626|OE: integer("divwuo", 'X', DAB_D|DAB_A|DAB_B); break;
case 01627: integer("divwu.", 'X', DAB_D|DAB_A|DAB_B); break;
case 01627|OE: integer("divwuo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 01646: movespr(0); break; // mtspr
case 01654: cache("dcbi", PPC_DISA_OEA); break; // dcbi
case 01670: integer("nand", 'Z', ASB_A|ASB_S|ASB_B); break; // nandx
case 01671: integer("nand.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 01726: integer("divw", 'X', DAB_D|DAB_A|DAB_B); break; // divwx
case 01726|OE: integer("divwo", 'X', DAB_D|DAB_A|DAB_B); break;
case 01727: integer("divw.", 'X', DAB_D|DAB_A|DAB_B); break;
case 01727|OE: integer("divwo.", 'X', DAB_D|DAB_A|DAB_B); break;
case 02000: mcrxr(); break; // mcrxr
case 02052: ldst("lswx", 1, 1, 0, 1); break; // lswx
case 02054: ldst("lwbrx", 1); break; // lwbrx
case 02056: ldst("lfsx", 1, 1, 0, 0, 1); break; // lfsx
case 02060: integer("srw", 'Z', ASB_A|ASB_S|ASB_B); break; // srwx
case 02061: integer("srw.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 02154: put("tlbsync", 0x03FFF800, 0, PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // tlbsync
case 02156: ldst("lfsux", 1, 1, 0, 0, 1); break; // lfsux
#ifdef POWERPC_32
case 02246: movesr("mfsr", 1, 0, 0); break; // mfsr
#endif
case 02252: lsswi("lswi"); break; // lswi
case 02254: put("sync", 0x03FFF800, 0); break; // sync
case 02256: ldst("lfdx", 1, 1, 0, 0, 1); break; // lfdx
case 02356: ldst("lfdux", 1, 1, 0, 0, 1); break; // lfdux
#ifdef POWERPC_32
case 02446: movesr("mfsrin", 1, 0, 1); break; // mfsrin
#endif
case 02452: ldst("stswx", 1, 1, 0, 1); break; // stswx
case 02454: ldst("stwbrx", 1, 0); break; // stwbrx
case 02456: ldst("stfsx", 1, 1, 0, 0, 1); break; // stfsx
case 02556: ldst("stfsux", 1, 1, 0, 0, 1); break; // stfsux
case 02652: lsswi("stswi"); break; // stswi
case 02656: ldst("stfdx", 1, 1, 0, 0, 1); break; // stfdx
#if !defined(GEKKO)
case 02754: cache("dcba", PPC_DISA_OPTIONAL); break; // dcba
#endif
case 02756: ldst("stfdux", 1, 1, 0, 0, 1); break; // stfdux
case 03054: ldst("lhbrx", 1); break; // lhbrx
case 03060: integer("sraw", 'Z', ASB_A|ASB_S|ASB_B); break; // srawx
case 03061: integer("sraw.", 'Z', ASB_A|ASB_S|ASB_B); break;
case 03160: srawi(); break; // srawi
case 03161: srawi(); break;
case 03254: put("eieio", 0x03FFF800, 0); break; // eieio
case 03454: ldst("sthbrx", 1, 0); break; // sthbrx
case 03464: if(DIS_RB) ill(); // extshx
else integer("extsh", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 03465: if(DIS_RB) ill();
else integer("extsh.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 03564: if(DIS_RB) ill(); // extsbx
else integer("extsb", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 03565: if(DIS_RB) ill();
else integer("extsb.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break;
case 03654: cache("icbi"); break; // icbi
case 03656: ldst("stfiwx", 1, 1, 0, 0, 1); o->iclass |= PPC_DISA_OPTIONAL; break; // stfiwx
case 03754: cache("dcbz"); break; // dcbz
#ifdef POWERPC_64
case 00022: integer("mulhdu", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulhdux
case 00023: integer("mulhdu.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 00052: ldst("ldx", 1, 1, 1); break; // ldx
case 00066: integer("sld", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // sldx
case 00067: integer("sld.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break;
case 00152: ldst("ldux", 1, 1, 1); break; // ldux
case 00164: if(DIS_RB) ill(); // cntlzdx
else integer("cntlzd", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; break;
case 00165: if(DIS_RB) ill();
else integer("cntlzd.", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; break;
case 00210: trap(1, 0); break;
case 00222: integer("mulhd", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulhdx
case 00223: integer("mulhd.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 00244: movesr("mtsrd", 0, 1, 0); break; // mtrsd
case 00250: ldst("ldarx", 1, 1, 1); break; // ldarx
case 00344: movesr("mtsrdin", 0, 1, 1); break; // mtrsdin
case 00452: ldst("stdx", 1, 0, 1); break; // stdx
case 00544: if(DIS_RA || DIS_RB) ill(); // mtmsrd
else { integer("mtmsrd", 'X', DAB_D); o->iclass = PPC_DISA_OEA | PPC_DISA_64; } break;
case 00552: ldst("stdux", 1, 0, 1); break; // stdux
case 00655: ldst("stdcx.", 1, 0, 1); break; // stdcx.
case 00722: integer("mulld", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulldx
case 00722|OE: integer("mulldo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 00723: integer("mulld.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 00723|OE: integer("mulldo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01252: ldst("lwax", 1, 1, 1); break; // lwax
case 01352: ldst("lwaux", 1, 1, 1); break; // lwaux
case 03164: sradi(); break; // sradi
case 03165: sradi(); break;
case 03166: sradi(); break;
case 03167: sradi(); break;
case 01544: if(DIS_RD || DIS_RA) ill(); // slbie
else { integer("slbie", 'X', DAB_B); o->iclass = PPC_DISA_64 | PPC_DISA_OEA | PPC_DISA_OPTIONAL; o->r[0] = o->r[2]; o->r[2] = 0; } break;
case 01622: integer("divdu", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // divdux
case 01622|OE: integer("divduo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01623: integer("divdu.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01623|OE: integer("divduo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01722: integer("divd", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // divdx
case 01722|OE: integer("divdo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01723: integer("divd.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01723|OE: integer("divdo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break;
case 01744: put("slbia", 0x03FFF800, 0, PPC_DISA_64 | PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // slbia
case 02066: integer("srd", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // srdx
case 02067: integer("srd.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break;
case 03064: integer("srad", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // sradx
case 03065: integer("srad.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break;
case 03664: if(DIS_RB) ill(); // extswx
else { integer("extsw", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; } break;
case 03665: if(DIS_RB) ill();
else { integer("extsw.", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; } break;
#endif
default: ill(); break;
} break;
/*
* Extention 3.
*/
#ifdef POWERPC_64
case 072:
switch(Instr & 3) {
case 0: Instr &= ~3; ldst("ld", 0, 1, 1); break; // ld
case 1: Instr &= ~3; ldst("ldu", 0, 1, 1); break; // ldu
case 2: Instr &= ~3; ldst("lwa", 0, 1, 1); break; // lwa
default: ill(); break;
} break;
#endif
/*
* Extention 4.
*/
#define MASK_D (0x1F << 21)
#define MASK_A (0x1F << 16)
#define MASK_B (0x1F << 11)
#define MASK_C (0x1F << 6)
case 073:
switch(Instr & 0x3F) {
case 044: fpu("fdivs", MASK_C, FPU_DAB); break; // fdivsx
case 045: fpu("fdivs.", MASK_C, FPU_DAB); break;
case 050: fpu("fsubs", MASK_C, FPU_DAB); break; // fsubsx
case 051: fpu("fsubs.", MASK_C, FPU_DAB); break;
case 052: fpu("fadds", MASK_C, FPU_DAB); break; // faddsx
case 053: fpu("fadds.", MASK_C, FPU_DAB); break;
#if !defined(GEKKO)
case 054: fpu("fsqrts", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fsqrtsx
case 055: fpu("fsqrts.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break;
#endif
case 060: fpu("fres", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fresx
case 061: fpu("fres.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break;
case 062: fpu("fmuls", MASK_B, FPU_DAC); break; // fmulsx
case 063: fpu("fmuls.", MASK_B, FPU_DAC); break;
case 070: fpu("fmsubs", 0, FPU_DACB); break; // fmsubsx
case 071: fpu("fmsubs.", 0, FPU_DACB); break;
case 072: fpu("fmadds", 0, FPU_DACB); break; // fmaddsx
case 073: fpu("fmadds.", 0, FPU_DACB); break;
case 074: fpu("fnmsubs", 0, FPU_DACB); break; // fnmsubsx
case 075: fpu("fnmsubs.", 0, FPU_DACB); break;
case 076: fpu("fnmadds", 0, FPU_DACB); break; // fnmaddsx
case 077: fpu("fnmadds.", 0, FPU_DACB); break;
default: ill(); break;
} break;
/*
* Extention 5.
*/
#ifdef POWERPC_64
case 076:
switch(Instr & 3) {
case 0: Instr &= ~3; ldst("std", 0, 0, 1); break; // std
case 1: Instr &= ~3; ldst("stdu", 0, 0, 1); break; // stdu
default: ill(); break;
} break;
#endif
/*
* Extention 6.
*/
case 077:
switch(Instr & 0x3F) {
case 000:
switch(DIS_RC)
{
case 0: fcmp("fcmpu"); break; // fcmpu
case 1: fcmp("fcmpo"); break; // fcmpo
case 2: mcrfs(); break; // mcrfs
default: ill(); break;
}
break;
case 014:
switch(DIS_RC)
{
case 1: mtfsb("mtfsb1"); break; // mtfsb1
case 2: mtfsb("mtfsb0"); break; // mtfsb0
case 4: mtfsfi(); break; // mtfsfi
default: ill(); break;
}
break;
case 015:
switch(DIS_RC)
{
case 1: mtfsb("mtfsb1."); break; // mtfsb1.
case 2: mtfsb("mtfsb0."); break; // mtfsb0.
case 4: mtfsfi(); break; // mtfsfi.
default: ill(); break;
}
break;
case 016:
switch(DIS_RC)
{
case 18: fpu("mffs", MASK_A|MASK_B, FPU_D); break; // mffs
case 22: mtfsf(); break; // mtfsf
default: ill(); break;
}
break;
case 017:
switch(DIS_RC)
{
case 18: fpu("mffs.", MASK_A|MASK_B, FPU_D); break; // mffs.
case 22: mtfsf(); break; // mtfsf.
default: ill(); break;
}
break;
case 020:
switch(DIS_RC)
{
case 1: fpu("fneg", MASK_A, FPU_DB); break; // fneg
case 2: fpu("fmr", MASK_A, FPU_DB); break; // fmr
case 4: fpu("fnabs", MASK_A, FPU_DB); break; // fnabs
case 8: fpu("fabs", MASK_A, FPU_DB); break; // fabs
default: ill(); break;
}
break;
case 021:
switch(DIS_RC)
{
case 1: fpu("fneg.", MASK_A, FPU_DB); break; // fneg
case 2: fpu("fmr.", MASK_A, FPU_DB); break; // fmr
case 4: fpu("fnabs.", MASK_A, FPU_DB); break; // fnabs
case 8: fpu("fabs.", MASK_A, FPU_DB); break; // fabs
default: ill(); break;
}
break;
case 030:
switch(DIS_RC)
{
case 0: fpu("frsp", MASK_A, FPU_DB); break; // frsp
default: ill(); break;
}
break;
case 031:
switch(DIS_RC)
{
case 0: fpu("frsp.", MASK_A, FPU_DB); break; // frsp.
default: ill(); break;
}
break;
case 034:
switch(DIS_RC)
{
case 0: fpu("fctiw", MASK_A, FPU_DB); break; // fctiw
#ifdef POWERPC_64
case 25: fpu("fctid", MASK_A, FPU_DB); break; // fctid
case 26: fpu("fcfid", MASK_A, FPU_DB); break; // fcfid
#endif
default: ill(); break;
}
break;
case 035:
switch(DIS_RC)
{
case 0: fpu("fctiw.", MASK_A, FPU_DB); break; // fctiw.
#ifdef POWERPC_64
case 25: fpu("fctid.", MASK_A, FPU_DB); break; // fctid.
case 26: fpu("fcfid.", MASK_A, FPU_DB); break; // fcfid.
#endif
default: ill(); break;
}
break;
case 036:
switch(DIS_RC)
{
case 0: fpu("fctiwz", MASK_A, FPU_DB); break; // fctiwz
#ifdef POWERPC_64
case 25: fpu("fctidz", MASK_A, FPU_DB); break; // fctidz
#endif
default: ill(); break;
}
break;
case 037:
switch(DIS_RC)
{
case 0: fpu("fctiwz.", MASK_A, FPU_DB); break; // fctiwz.
#ifdef POWERPC_64
case 25: fpu("fctidz.", MASK_A, FPU_DB); break; // fctidz.
#endif
default: ill(); break;
}
break;
case 044: fpu("fdiv", MASK_C, FPU_DAB); break; // fdivx
case 045: fpu("fdiv.", MASK_C, FPU_DAB); break;
case 050: fpu("fsub", MASK_C, FPU_DAB); break; // fsubx
case 051: fpu("fsub.", MASK_C, FPU_DAB); break;
case 052: fpu("fadd", MASK_C, FPU_DAB); break; // faddx
case 053: fpu("fadd.", MASK_C, FPU_DAB); break;
#if !defined(GEKKO)
case 054: fpu("fsqrt", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fsqrtx
case 055: fpu("fsqrt.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break;
#endif
case 056: fpu("fsel", 0, FPU_DACB, PPC_DISA_OPTIONAL); break; // fselx
case 057: fpu("fsel.", 0, FPU_DACB, PPC_DISA_OPTIONAL); break;
case 062: fpu("fmul", MASK_B, FPU_DAC); break; // fmulx
case 063: fpu("fmul.", MASK_B, FPU_DAC); break;
case 064: fpu("frsqrte", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // frsqrtex
case 065: fpu("frsqrte.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break;
case 070: fpu("fmsub", 0, FPU_DACB); break; // fmsubx
case 071: fpu("fmsub.", 0, FPU_DACB); break;
case 072: fpu("fmadd", 0, FPU_DACB); break; // fmaddx
case 073: fpu("fmadd.", 0, FPU_DACB); break;
case 074: fpu("fnmsub", 0, FPU_DACB); break; // fnmsubx
case 075: fpu("fnmsub.", 0, FPU_DACB); break;
case 076: fpu("fnmadd", 0, FPU_DACB); break; // fnmaddx
case 077: fpu("fnmadd.", 0, FPU_DACB); break;
default: ill(); break;
} break;
/*
***********************************************************************************
* GEKKO Extention.
***********************************************************************************
*/
#ifdef GEKKO
case 004:
if(((Instr >> 1) & 0x3FF) == 1014)
{
cache("dcbz_l", PPC_DISA_SPECIFIC); // dcbz_l
}
else switch((Instr >> 1) & 0x1f)
{
case 0: ps_cmpx((Instr >> 6) & 3); break; // ps_cmpXX
case 6: if(Instr & 0x40) ps_ldstx("lux"); // ps_lux
else ps_ldstx("lx"); break; // ps_lx
case 7: if(Instr & 0x40) ps_ldstx("stux"); // ps_stux
else ps_ldstx("stx"); break; // ps_stx
case 8:
switch((Instr >> 6) & 0x1f)
{
case 1: ps_db("neg", 1); break; // ps_negx
case 2: ps_db("mr", 1); break; // ps_mrx
case 4: ps_db("nabs", 1); break; // ps_nabsx
case 8: ps_db("abs", 1); break; // ps_absx
default: ill(); break;
} break;
case 10: ps_dacb("sum0"); break; // ps_sum0x
case 11: ps_dacb("sum1"); break; // ps_sum1x
case 12: ps_dac("muls0"); break; // ps_muls0x
case 13: ps_dac("muls1"); break; // ps_muls1x
case 14: ps_dacb("madds0"); break; // ps_madds0x
case 15: ps_dacb("madds1"); break; // ps_madds1x
case 16:
switch((Instr >> 6) & 0x1f)
{
case 16: ps_dab("merge00", 1); break; // ps_merge00x
case 17: ps_dab("merge01", 1); break; // ps_merge11x
case 18: ps_dab("merge10", 1); break; // ps_merge10x
case 19: ps_dab("merge11", 1); break; // ps_merge11x
default: ill(); break;
} break;
case 18: ps_dab("div"); break; // ps_divx
case 20: ps_dab("sub"); break; // ps_subx
case 21: ps_dab("add"); break; // ps_addx
case 23: ps_dacb("sel"); break; // ps_selx
case 24: ps_db("res"); break; // ps_resx
case 25: ps_dac("mul"); break; // ps_mulx
case 26: ps_db("rsqrte"); break; // ps_rsqrtex
case 28: ps_dacb("msub"); break; // ps_msubx
case 29: ps_dacb("madd"); break; // ps_maddx
case 30: ps_dacb("nmsub"); break; // ps_nmsubx
case 31: ps_dacb("nmadd"); break; // ps_nmaddx
default: ill(); break;
} break;
case 070: ps_ldst("l"); break; // psq_l
case 071: ps_ldst("lu"); break; // psq_lu
case 074: ps_ldst("st"); break; // psq_st
case 075: ps_ldst("stu"); break; // psq_stu
#endif /* GEKKO */
default: ill(); break;
}
#ifdef UPPERCASE
strupr(o->mnemonic);
#endif
}
char *PPCDisasmSimple(u64 pc, u32 instr)
{
PPCD_CB dis_out;
static char output[256];
dis_out.pc = pc;
dis_out.instr = instr;
PPCDisasm(&dis_out);
sprintf(output, "%08X %08X %-10s %s", pc, instr, dis_out.mnemonic, dis_out.operands);
return output;
}