/* DingusPPC - The Experimental PowerPC Macintosh emulator Copyright (C) 2018-20 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** @file Logic for the disassembler. @author maximumspatium */ #define _CRT_SECURE_NO_WARNINGS /* shut up MSVC regarding the unsafe strcpy/strcat */ #include "ppcdisasm.h" #include #include /* without this, MSVC doesn't understand std::function */ #include #include #include using namespace std; template std::string my_sprintf(const char* format, Args... args) { int length = std::snprintf(nullptr, 0, format, args...); if (length <= 0) return {}; /* empty string in C++11 */ char* buf = new char[length + 1]; std::snprintf(buf, length + 1, format, args...); std::string str(buf); delete[] buf; return str; } const char* arith_im_mnem[9] = {"mulli", "subfic", "", "", "", "addic", "addic.", "addi", "addis"}; const char* bx_mnem[4] = {"b", "bl", "ba", "bla"}; const char* bclrx_mnem[2] = {"blr", "blrl"}; const char* bcctrx_mnem[2] = {"bctr", "bctrl"}; const char* br_cond[8] = {/* simplified branch conditions */ "ge", "le", "ne", "ns", "lt", "gt", "eq", "so"}; const char* bclrx_cond[8] = {/* simplified branch conditions */ "gelr", "lelr", "nelr", "nslr", "ltlr", "gtlr", "eqlr", "solr"}; const char* bcctrx_cond[8] = {/* simplified branch conditions */ "gectr", "lectr", "nectr", "nsctr", "ltctr", "gtctr", "eqctr", "soctr"}; const char* opc_idx_ldst[32] = {/* indexed load/store opcodes */ "lwzx", "lwzux", "lbzx", "lbzux", "stwx", "stwux", "stbx", "stbux", "lhzx", "lhzux", "lhax", "lhaux", "sthx", "sthux", "", "", "lfsx", "lfsux", "lfdx", "lfdux", "stfsx", "stfsux", "stfdx", "stfdux", "", "", "", "", "", "", "stfiwx", ""}; const char* opc_bim_str[6] = {/* boolean immediate instructions */ "ori", "oris", "xori", "xoris", "andi.", "andis."}; const char* opc_logic[16] = {/* indexed load/store opcodes */ "and", "andc", "", "nor", "", "", "", "", "eqv", "xor", "", "", "orc", "or", "nand", ""}; const char* opc_subs[16] = {/* subtracts & friends */ "subfc", "subf", "", "neg", "subfe", "", "subfze", "subfme", "doz", "", "", "abs", "", "", "", "nabs"}; const char* opc_adds[9] = {/* additions */ "addc", "", "", "", "adde", "", "addze", "addme", "add"}; const char* opc_muldivs[16] = {/* multiply and division instructions */ "mulhwu", "", "mulhw", "mul", "", "", "", "mullw", "", "", "div", "divs", "", "", "divwu", "divw"}; const char* opc_shft_reg[32]{ /* Regular shift instructions */ "slw", "", "", "", "slq", "sliq", "sllq", "slliq", "", "", "", "", "", "", "", "", "srw", "", "", "", "srq", "sriq", "srlq", "srliq", "sraw", "srawi", "", "", "sraq", "sraiq", "", ""}; const char* opc_shft_ext[32]{ /* Extended shift instructions (601 only) */ "", "", "", "", "sle", "", "sleq", "", "", "", "", "", "", "", "", "", "rrib", "", "", "", "sre", "", "sreq", "", "", "", "", "", "srea", "", "", ""}; const char* opc_int_ldst[16] = {/* integer load and store instructions */ "lwz", "lwzu", "lbz", "lbzu", "stw", "stwu", "stb", "stbu", "lhz", "lhzu", "lha", "lhau", "sth", "sthu", "lmw", "stmw"}; /* processor management + byte reversed load and store */ const char* proc_mgmt_str[32] = { "", "dcbst", "dcbf", "", "stwcx.", "", "", "dcbtst", "dcbt", "eciwx", "", "", "", "ecowx", "dcbi", "", "lwbrx", "tlbsync", "sync", "", "stwbrx", "", "", "dcba", "lhbrx", "", "eieio", "", "sthbrx", "", "icbi", "dcbz"}; const char* opc_flt_ldst[8] = {/* integer load and store instructions */ "lfs", "lfsu", "lfd", "lfdu", "stfs", "stfsu", "sftd", "sftdu"}; const char* opc_flt_ext_arith[32] = { /* integer load and store instructions */ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "fsel", "", "fmul", "", "", "fmsub", "fmadd", "fnmsub", "fnmadd", }; const char* trap_cond[32] = {/*Trap conditions*/ "", "twlgt", "twllt", "", "tweq", "twlge", "twlle", "", "twgt", "", "", "", "twge", "", "", "", "twlt", "", "", "", "twle", "", "", "", "twne", "", "", "", "", "", "", ""}; const char* spr_index0[32] = {"mq", "xer", "", "", "rtcu", "rtcl", "", "", "lr", "ctr", "", "", "", "", "", "", "", "", "dsisr", "dar", "", "", "dec", "", "", "sdr1", "srr0", "srr1", "", "", "", ""}; const char* spr_index8[32] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "sprg0", "sprg1", "sprg2", "sprg3", "sprg4", "sprg5", "sprg6", "sprg7", "", "", "ear", "", "tbl", "tbu", "", "pvr"}; const char* spr_index16[32] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ibat0u", "ibat0l", "ibat1u", "ibat1l", "ibat2u", "ibat2l", "ibat3u", "ibat3l", "dbat0u", "dbat0l", "dbat1u", "dbat1l", "dbat2u", "dbat2l", "dbat3u", "dbat3l", }; const char* spr_index29[32] = { "", "", "", "", "", "", "", "", "ummcr0", "upmc1", "upmc2", "usia", "ummcr1", "upmc3", "upmc4", "", "", "", "", "", "", "", "", "", "mmcr0", "pmc1", "pmc2", "sia", "mmcr1", "pmc3", "pmc4", "sda"}; const char* spr_index30[32] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "dmiss", "dcmp", "hash1", "hash2", "imiss", "icmp", "rpa", "", "", "", "", "", "", "", "", ""}; const char* spr_index31[32] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "hid0", "hid1", "iabr", "", "", "dabr", "", "", "", "l2cr", "", "ictc", "thrm1", "thrm2", "thrm3", "pir", }; /** various formatting helpers. */ void fmt_oneop(string& buf, const char* opc, int src) { buf = my_sprintf("%-8sr%d", opc, src); } void fmt_twoop(string& buf, const char* opc, int dst, int src) { buf = my_sprintf("%-8sr%d, r%d", opc, dst, src); } void fmt_twoop_simm(string& buf, const char* opc, int dst, int imm) { buf = my_sprintf("%-8sr%d, %s0x%X", opc, dst, (imm < 0) ? "-" : "", abs(imm)); } void fmt_twoop_fromspr(string& buf, const char* opc, int dst, int src) { buf = my_sprintf("%-8sr%d, spr%d", opc, dst, src); } void fmt_twoop_tospr(string& buf, const char* opc, int dst, int src) { buf = my_sprintf("%-8sspr%d, r%d", opc, dst, src); } void fmt_twoop_flt(string& buf, const char* opc, int dst, int src1) { buf = my_sprintf("%-8sf%d, f%d", opc, dst, src1); } void fmt_threeop(string& buf, const char* opc, int dst, int src1, int src2) { buf = my_sprintf("%-8sr%d, r%d, r%d", opc, dst, src1, src2); } void fmt_threeop_crb(string& buf, const char* opc, int dst, int src1, int src2) { buf = my_sprintf("%-8scrb%d, crb%d, crb%d", opc, dst, src1, src2); } void fmt_threeop_uimm(string& buf, const char* opc, int dst, int src1, int imm) { buf = my_sprintf("%-8sr%d, r%d, 0x%X", opc, dst, src1, imm); } void fmt_threeop_simm(string& buf, const char* opc, int dst, int src1, int imm) { buf = my_sprintf("%-8sr%d, r%d, %s0x%X", opc, dst, src1, (imm < 0) ? "-" : "", abs(imm)); } void fmt_threeop_flt(string& buf, const char* opc, int dst, int src1, int src2) { buf = my_sprintf("%-8sf%d, f%d, f%d", opc, dst, src1, src2); } void fmt_fourop_flt(string& buf, const char* opc, int dst, int src1, int src2, int src3) { buf = my_sprintf("%-8sf%d, f%d, f%d, f%d", opc, dst, src1, src2, src3); } void fmt_rotateop(string& buf, const char* opc, int dst, int src, int sh, int mb, int me, bool imm) { if (imm) buf = my_sprintf("%-8sr%d, r%d, %d, %d, %d", opc, dst, src, sh, mb, me); else buf = my_sprintf("%-8sr%d, r%d, r%d, %d, %d", opc, dst, src, sh, mb, me); } /* Opcodes */ void opc_illegal(PPCDisasmContext* ctx) { ctx->instr_str = my_sprintf("%-8s0x%08X", "dc.l", ctx->instr_code); } void opc_twi(PPCDisasmContext* ctx) { char opcode[10] = ""; auto to = (ctx->instr_code >> 21) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; int32_t imm = SIGNEXT(ctx->instr_code & 0xFFFF, 15); if (ctx->simplified) { strcpy(opcode, trap_cond[to]); if (strlen(opcode) > 0) { strcat(opcode, "i"); ctx->instr_str = my_sprintf("%-8sr%d, 0x%X", opcode, ra, imm); return; } } ctx->instr_str = my_sprintf("%-8s%d, r%d, 0x%X", "twi", to, ra, imm); } void opc_group4(PPCDisasmContext* ctx) { printf("Altivec group 4 not supported yet\n"); } void opc_ar_im(PPCDisasmContext* ctx) { auto ra = (ctx->instr_code >> 16) & 0x1F; auto rd = (ctx->instr_code >> 21) & 0x1F; int32_t imm = SIGNEXT(ctx->instr_code & 0xFFFF, 15); if (ctx->simplified) { if (((ctx->instr_code >> 26) == 0xE) & !ra) { fmt_twoop_simm(ctx->instr_str, "li", rd, imm); return; } else if (((ctx->instr_code >> 26) == 0xF) & !ra) { fmt_twoop_simm(ctx->instr_str, "lis", rd, imm); return; } if (imm > 0x7FFF) { switch ((ctx->instr_code >> 26)) { case 0xC: fmt_threeop_simm(ctx->instr_str, "subic", rd, ra, imm); return; case 0xD: fmt_threeop_simm(ctx->instr_str, "subic.", rd, ra, imm); return; case 0xE: fmt_threeop_simm(ctx->instr_str, "subi", rd, ra, imm); return; case 0xF: fmt_threeop_simm(ctx->instr_str, "subis", rd, ra, imm); return; } } } fmt_threeop_simm(ctx->instr_str, arith_im_mnem[(ctx->instr_code >> 26) - 7], rd, ra, imm); } void power_dozi(PPCDisasmContext* ctx) { auto ra = (ctx->instr_code >> 16) & 0x1F; auto rd = (ctx->instr_code >> 21) & 0x1F; auto imm = ctx->instr_code & 0xFFFF; fmt_threeop_simm(ctx->instr_str, "dozi", rd, ra, imm); } void fmt_rot_imm(PPCDisasmContext* ctx, const char* opc, int ra, int rs, int n) { char opcode[10]; strcpy(opcode, opc); if (ctx->instr_code & 1) strcat(opcode, "."); ctx->instr_str = my_sprintf("%-8sr%d, r%d, %d", opcode, ra, rs, n); } void fmt_rot_2imm(PPCDisasmContext* ctx, const char* opc, int ra, int rs, int n, int b) { char opcode[10]; strcpy(opcode, opc); if (ctx->instr_code & 1) strcat(opcode, "."); ctx->instr_str = my_sprintf("%-8sr%d, r%d, %d, %d", opcode, ra, rs, n, b); } void opc_rlwimi(PPCDisasmContext* ctx) { char opcode[10]; auto rs = (ctx->instr_code >> 21) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto sh = (ctx->instr_code >> 11) & 0x1F; auto mb = (ctx->instr_code >> 6) & 0x1F; auto me = (ctx->instr_code >> 1) & 0x1F; if (ctx->simplified) { if ((32 - sh) == mb) { fmt_rot_2imm(ctx, "inslwi", ra, rs, me + 1 - mb, mb); return; } else if (sh == 32 - (me + 1)) { fmt_rot_2imm(ctx, "insrwi", ra, rs, (me - mb + 1), mb); return; } } strcpy(opcode, "rlwimi"); if (ctx->instr_code & 1) strcat(opcode, "."); fmt_rotateop(ctx->instr_str, opcode, ra, rs, sh, mb, me, true); } void opc_rlwinm(PPCDisasmContext* ctx) { char opcode[10]; auto rs = (ctx->instr_code >> 21) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto sh = (ctx->instr_code >> 11) & 0x1F; auto mb = (ctx->instr_code >> 6) & 0x1F; auto me = (ctx->instr_code >> 1) & 0x1F; if (ctx->simplified) { if ((mb == 0) && (me == 31)) { if (sh < 16) { fmt_rot_imm(ctx, "rotlwi", ra, rs, sh); } else { fmt_rot_imm(ctx, "rotrwi", ra, rs, 32 - sh); } return; } else if (me == 31) { if ((32 - sh) == mb) { fmt_rot_imm(ctx, "srwi", ra, rs, mb); } else if (sh == 0) { fmt_rot_imm(ctx, "clrlwi", ra, rs, mb); } else { fmt_rot_2imm(ctx, "extrwi", ra, rs, (32 - mb), (sh - (32 - mb))); } return; } else if (mb == 0) { if ((31 - me) == sh) { fmt_rot_imm(ctx, "slwi", ra, rs, sh); } else if (sh == 0) { fmt_rot_imm(ctx, "clrrwi", ra, rs, (31 - me)); } else { fmt_rot_2imm(ctx, "extlwi", ra, rs, (me + 1), sh); } return; } else if (mb) { if ((31 - me) == sh) { fmt_rot_2imm(ctx, "clrlslwi", ra, rs, (mb + sh), sh); return; } } } strcpy(opcode, "rlwinm"); if (ctx->instr_code & 1) strcat(opcode, "."); fmt_rotateop(ctx->instr_str, opcode, ra, rs, sh, mb, me, true); } void opc_rlmi(PPCDisasmContext* ctx) { auto rs = (ctx->instr_code >> 21) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rb = (ctx->instr_code >> 11) & 0x1F; auto mb = (ctx->instr_code >> 6) & 0x1F; auto me = (ctx->instr_code >> 1) & 0x1F; if (ctx->instr_code & 1) fmt_rotateop(ctx->instr_str, "rlmi.", ra, rs, rb, mb, me, false); else fmt_rotateop(ctx->instr_str, "rlmi", ra, rs, rb, mb, me, false); } void opc_rlwnm(PPCDisasmContext* ctx) { char opcode[10]; auto rs = (ctx->instr_code >> 21) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rb = (ctx->instr_code >> 11) & 0x1F; auto mb = (ctx->instr_code >> 6) & 0x1F; auto me = (ctx->instr_code >> 1) & 0x1F; if (ctx->simplified) { if ((me == 31) && (mb == 0)) { strcpy(opcode, "rotlw"); if (ctx->instr_code & 1) strcat(opcode, "."); fmt_threeop(ctx->instr_str, opcode, ra, rs, rb); return; } } strcpy(opcode, "rlwnm"); if (ctx->instr_code & 1) strcat(opcode, "."); fmt_rotateop(ctx->instr_str, "rlwnm", ra, rs, rb, mb, me, false); } void opc_cmp_i_li(PPCDisasmContext* ctx) { auto ls = (ctx->instr_code >> 21) & 0x1; auto ra = (ctx->instr_code >> 16) & 0x1F; auto crfd = (ctx->instr_code >> 23) & 0x07; int imm = ctx->instr_code & 0xFFFF; if (ctx->simplified) { if (!ls) { if ((ctx->instr_code >> 26) & 0x1) ctx->instr_str = my_sprintf("%-8scr%d, r%d, 0x%X", "cmpwi", crfd, ra, imm); else ctx->instr_str = my_sprintf( "%-8scr%d, r%d, %s0x%X", "cmplwi", crfd, ra, (imm < 0) ? "-" : "", abs(imm)); return; } } if ((ctx->instr_code >> 26) & 0x1) ctx->instr_str = my_sprintf("%-8scr%d, %d, r%d, 0x%X", "cmpi", crfd, ls, ra, imm); else ctx->instr_str = my_sprintf( "%-8scr%d, %d, r%d, %s0x%X", "cmpli", crfd, ls, ra, (imm < 0) ? "-" : "", abs(imm)); } void opc_bool_im(PPCDisasmContext* ctx) { char opcode[10]; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rs = (ctx->instr_code >> 21) & 0x1F; auto index = ((ctx->instr_code >> 26) & 0x1F) - 24; auto imm = ctx->instr_code & 0xFFFF; if (ctx->simplified) { if (index == 0) { if (imm == 0 && !ra && !rs && !imm) { /* unofficial, produced by IDA */ ctx->instr_str = my_sprintf("%-8s", "nop"); return; } } } strcpy(opcode, opc_bim_str[index]); fmt_threeop_uimm(ctx->instr_str, opcode, ra, rs, imm); } void generic_bcx(PPCDisasmContext* ctx, uint32_t bo, uint32_t bi, uint32_t dst) { char opcode[10] = "bc"; if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } if (ctx->instr_code & 2) { strcat(opcode, "a"); /* add suffix "a" if the AA bit is set */ } ctx->instr_str = my_sprintf("%-8s%d, %d, 0x%08X", opcode, bo, bi, dst); } void generic_bcctrx(PPCDisasmContext* ctx, uint32_t bo, uint32_t bi) { char opcode[10] = "bcctr"; if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } ctx->instr_str = my_sprintf("%-8s%d, %d, 0x%08X", opcode, bo, bi); } void generic_bclrx(PPCDisasmContext* ctx, uint32_t bo, uint32_t bi) { char opcode[10] = "bclr"; if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } ctx->instr_str = my_sprintf("%-8s%d, %d, 0x%08X", opcode, bo, bi); } void opc_bcx(PPCDisasmContext* ctx) { uint32_t bo, bi, dst, cr; char opcode[10] = "b"; char operands[12] = ""; bo = (ctx->instr_code >> 21) & 0x1F; bi = (ctx->instr_code >> 16) & 0x1F; cr = bi >> 2; dst = ((ctx->instr_code & 2) ? 0 : ctx->instr_addr) + SIGNEXT(ctx->instr_code & 0xFFFC, 15); if (!ctx->simplified || ((bo & 0x10) && bi) || (((bo & 0x14) == 0x14) && (bo & 0xB) && bi)) { generic_bcx(ctx, bo, bi, dst); return; } if ((bo & 0x14) == 0x14) { ctx->instr_str = my_sprintf("%-8s0x%08X", bx_mnem[0], dst); return; } if (!(bo & 4)) { strcat(opcode, "d"); strcat(opcode, (bo & 2) ? "z" : "nz"); if (!(bo & 0x10)) { strcat(opcode, (bo & 8) ? "t" : "f"); if (cr) { strcat(operands, "4*cr0+"); operands[4] = cr + '0'; } strcat(operands, br_cond[4 + (bi & 3)]); strcat(operands, ", "); } } else { /* CTR ignored */ strcat(opcode, br_cond[((bo >> 1) & 4) | (bi & 3)]); if (cr) { strcat(operands, "cr0, "); operands[2] = cr + '0'; } } if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } if (ctx->instr_code & 2) { strcat(opcode, "a"); /* add suffix "a" if the AA bit is set */ } if (bo & 1) { /* incorporate prediction bit if set */ strcat(opcode, (ctx->instr_code & 0x8000) ? "-" : "+"); } ctx->instr_str = my_sprintf("%-8s%s0x%08X", opcode, operands, dst); } void opc_bcctrx(PPCDisasmContext* ctx) { uint32_t bo, bi, cr; char opcode[10] = "b"; char operands[4] = ""; bo = (ctx->instr_code >> 21) & 0x1F; bi = (ctx->instr_code >> 16) & 0x1F; cr = bi >> 2; if (!(bo & 4)) { /* bcctr with BO[2] = 0 is invalid */ opc_illegal(ctx); } if (!ctx->simplified || ((bo & 0x10) && bi) || (((bo & 0x14) == 0x14) && (bo & 0xB) && bi)) { generic_bcctrx(ctx, bo, bi); return; } if ((bo & 0x14) == 0x14) { ctx->instr_str = my_sprintf("%-8s", bcctrx_mnem[ctx->instr_code & 1]); return; } strcat(opcode, br_cond[((bo >> 1) & 4) | (bi & 3)]); strcat(opcode, "ctr"); if (cr) { strcat(operands, "cr0"); operands[2] = cr + '0'; } if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } if (bo & 1) { /* incorporate prediction bit if set */ strcat(opcode, "+"); } ctx->instr_str = my_sprintf("%-8s%s", opcode, operands); } void opc_bclrx(PPCDisasmContext* ctx) { uint32_t bo, bi, cr; char opcode[10] = "b"; char operands[12] = ""; bo = (ctx->instr_code >> 21) & 0x1F; bi = (ctx->instr_code >> 16) & 0x1F; cr = bi >> 2; if (!ctx->simplified || ((bo & 0x10) && bi) || (((bo & 0x14) == 0x14) && (bo & 0xB) && bi)) { generic_bclrx(ctx, bo, bi); return; } if ((bo & 0x14) == 0x14) { ctx->instr_str = my_sprintf("%-8s", bclrx_mnem[ctx->instr_code & 1]); return; } if (!(bo & 4)) { strcat(opcode, "d"); strcat(opcode, (bo & 2) ? "z" : "nz"); if (!(bo & 0x10)) { strcat(opcode, (bo & 8) ? "t" : "f"); if (cr) { strcat(operands, "4*cr0+"); operands[4] = cr + '0'; } strcat(operands, br_cond[4 + (bi & 3)]); } } else { /* CTR ignored */ strcat(opcode, br_cond[((bo >> 1) & 4) | (bi & 3)]); if (cr) { strcat(operands, "cr0"); operands[2] = cr + '0'; } } strcat(opcode, "lr"); if (ctx->instr_code & 1) { strcat(opcode, "l"); /* add suffix "l" if the LK bit is set */ } if (bo & 1) { /* incorporate prediction bit if set */ strcat(opcode, "+"); } ctx->instr_str = my_sprintf("%-8s%s", opcode, operands); } void opc_bx(PPCDisasmContext* ctx) { uint32_t dst = ((ctx->instr_code & 2) ? 0 : ctx->instr_addr) + SIGNEXT(ctx->instr_code & 0x3FFFFFC, 25); ctx->instr_str = my_sprintf("%-8s0x%08X", bx_mnem[ctx->instr_code & 3], dst); } void opc_sc(PPCDisasmContext* ctx) { ctx->instr_str = my_sprintf("%-8s", "sc"); } void opc_group19(PPCDisasmContext* ctx) { auto rb = (ctx->instr_code >> 11) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rs = (ctx->instr_code >> 21) & 0x1F; int ext_opc = (ctx->instr_code >> 1) & 0x3FF; /* extract extended opcode */ char operand1[12] = ""; char operand2[12] = ""; char operand3[12] = ""; if (rs > 3) { strcat(operand1, "4*cr0+"); operand1[4] = (rs >> 2) + '0'; } strcat(operand1, br_cond[((rs % 4) + 4)]); if (ra > 3) { strcat(operand2, "4*cr0+"); operand2[4] = (ra >> 2) + '0'; } strcat(operand2, br_cond[((ra % 4) + 4)]); if (rb > 3) { strcat(operand3, "4*cr0+"); operand3[4] = (rb >> 2) + '0'; } strcat(operand3, br_cond[((rb % 4) + 4)]); switch (ext_opc) { case 0: ctx->instr_str = my_sprintf("%-8scr%d, cr%d", "mcrf", (rs >> 2), (ra >> 2)); return; case 16: opc_bclrx(ctx); return; case 33: if (ctx->simplified && (ra == rb)) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crnor", operand1, operand2, operand3); } else { fmt_threeop_crb(ctx->instr_str, "crnor", rs, ra, rb); } return; case 50: ctx->instr_str = my_sprintf("%-8s", "rfi"); return; case 129: if (ctx->simplified) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crandc", operand1, operand2, operand3); } else { fmt_threeop_crb(ctx->instr_str, "crandc", rs, ra, rb); } return; case 150: ctx->instr_str = my_sprintf("%-8s", "isync"); return; case 193: if (ctx->simplified && (rs == ra) && (rs == rb)) { ctx->instr_str = my_sprintf("%-8scrb%d", "crclr", rs); return; } else if (ctx->simplified && ((rs != ra) || (rs != rb))) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crxor", operand1, operand2, operand3); return; } fmt_threeop_crb(ctx->instr_str, "crxor", rs, ra, rb); return; case 225: if (ctx->simplified) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crnand", operand1, operand2, operand3); } else { fmt_threeop_crb(ctx->instr_str, "crnand", rs, ra, rb); } return; case 257: if (ctx->simplified) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crand", operand1, operand2, operand3); } else { fmt_threeop_crb(ctx->instr_str, "crand", rs, ra, rb); } return; case 289: if (ctx->simplified && (rs == ra) && (rs == rb)) { ctx->instr_str = my_sprintf("%-8scrb%d", "crset", rs); return; } else if (ctx->simplified && ((rs != ra) || (rs != rb))) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "creqv", operand1, operand2, operand3); return; } fmt_threeop_crb(ctx->instr_str, "creqv", rs, ra, rb); return; case 417: if (ctx->simplified) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "crorc", operand1, operand2, operand3); } else { fmt_threeop_crb(ctx->instr_str, "crorc", rs, ra, rb); } return; case 449: if (ctx->simplified && (ra == rb)) { ctx->instr_str = my_sprintf("%-8scrb%d, crb%d", "crmove", rs, ra); return; } else if (ctx->simplified && (ra != rb)) { ctx->instr_str = my_sprintf("%-8s%s, %s, %s", "cror", operand1, operand2, operand3); return; } fmt_threeop_crb(ctx->instr_str, "cror", rs, ra, rb); return; case 528: opc_bcctrx(ctx); return; } } void opc_group31(PPCDisasmContext* ctx) { char opcode[10] = ""; auto rb = (ctx->instr_code >> 11) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rs = (ctx->instr_code >> 21) & 0x1F; int ext_opc = (ctx->instr_code >> 1) & 0x3FF; /* extract extended opcode */ int index = ext_opc >> 5; bool rc_set = ctx->instr_code & 1; switch (ext_opc & 0x1F) { case 8: /* subtracts & friends */ index &= 0xF; /* strip OE bit */ if (!strlen(opc_subs[index])) { opc_illegal(ctx); } else { strcpy(opcode, opc_subs[index]); if (ext_opc & 0x200) /* check OE bit */ strcat(opcode, "o"); if (rc_set) strcat(opcode, "."); if (index == 3 || index == 6 || index == 7 || index == 11 || index == 15) { /* ugly check for two-operands instructions */ if (rb != 0) opc_illegal(ctx); else fmt_twoop(ctx->instr_str, opcode, rs, ra); } else fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); } return; case 10: /* additions */ index &= 0xF; /* strip OE bit */ if (index > 8 || !strlen(opc_adds[index])) { opc_illegal(ctx); } else { strcpy(opcode, opc_adds[index]); if (ext_opc & 0x200) /* check OE bit */ strcat(opcode, "o"); if (rc_set) strcat(opcode, "."); if (index == 6 || index == 7) { if (rb != 0) opc_illegal(ctx); else fmt_twoop(ctx->instr_str, opcode, rs, ra); } else fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); } return; case 11: /* integer multiplications and divisions */ index &= 0xF; /* strip OE bit */ if (!strlen(opc_muldivs[index])) { opc_illegal(ctx); } else { strcpy(opcode, opc_muldivs[index]); if (ext_opc & 0x200) /* check OE bit */ strcat(opcode, "o"); if (rc_set) strcat(opcode, "."); if ((!index || index == 2) && (ext_opc & 0x200)) opc_illegal(ctx); else fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); } return; case 0x12: /* tlb & segment register instructions */ if (index == 4) { /* mtmsr */ if ((ra != 0) || (rb != 0) || rc_set) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%d", "mtmsr", rs); } else if (index == 6) { /* mtsr */ if (ra & 16) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8s%d, r%d", "mtsr", ra, rs); } else if (index == 7) { /* mtsrin */ ctx->instr_str = my_sprintf("%-8sr%d, r%d", "mtsrin", rs, rb); } else if (index == 9) { /* tlbie */ ctx->instr_str = my_sprintf("%-8sr%d", "tlbie", rb); } else if (index == 11) { /* tlbia */ ctx->instr_str = my_sprintf("%-8s", "tlbia"); } else if (index == 30) { /* tlbld - 603 only */ if (!rs & !ra) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%s", "tlbld", rb); } else if (index == 30) { /* tlbli - 603 only */ if (!rs & !ra) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%s", "tlbli", rb); } return; case 0x18: /* Shifting instructions */ strcpy(opcode, opc_shft_reg[index]); if (rc_set) strcat(opcode, "."); if ((index == 0) | (index == 4) | (index == 6) | (index == 16) | (index == 20) | (index == 22) | (index == 24) | (index == 28)) { fmt_threeop(ctx->instr_str, opcode, ra, rs, rb); } else if ((index == 5) | (index == 7) | (index == 21) | (index == 23) | (index == 25) | (index == 29)) { fmt_threeop_simm(ctx->instr_str, opcode, ra, rs, rb); } else { opc_illegal(ctx); } return; case 0x19: /* (Extended) Shifting instructions - 601 only*/ strcpy(opcode, opc_shft_ext[index]); if (rc_set) strcat(opcode, "."); if ((index == 4) | (index == 6) | (index == 16) | (index == 20) | (index == 22) | (index == 28)) { fmt_threeop(ctx->instr_str, opcode, ra, rs, rb); } else { opc_illegal(ctx); } return; case 0x1A: /* Byte sign extend instructions (and cntlzw) */ if (index == 0) strcpy(opcode, "cntlzw"); else if (index == 28) strcpy(opcode, "extsh"); else if (index == 29) strcpy(opcode, "extsb"); if (rc_set) strcat(opcode, "."); fmt_twoop(ctx->instr_str, opcode, rs, ra); return; case 0x1C: /* logical instructions */ if (index == 13 && rs == rb && ctx->simplified) { fmt_twoop(ctx->instr_str, rc_set ? "mr." : "mr", ra, rs); } else { strcpy(opcode, opc_logic[index]); if (!strlen(opcode)) { opc_illegal(ctx); } else { if (rc_set) strcat(opcode, "."); fmt_threeop(ctx->instr_str, opcode, ra, rs, rb); } } return; case 0x17: /* indexed load/store instructions */ if (index == 30) { /* stfiwx sneaks in here */ if (rc_set) { opc_illegal(ctx); return; } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sf%d, 0, r%d", opc_idx_ldst[index], rs, rb); else { ctx->instr_str = my_sprintf("%-8sf%d, r%d, r%d", opc_idx_ldst[index], rs, ra, rb); } return; } } if (index > 23 || rc_set || strlen(opc_idx_ldst[index]) == 0) { opc_illegal(ctx); return; } if (index < 16) { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", opc_idx_ldst[index], rs, rb); else fmt_threeop(ctx->instr_str, opc_idx_ldst[index], rs, ra, rb); return; } else { ctx->instr_str = my_sprintf("%-8sf%d, r%d, r%d", opc_idx_ldst[index], rs, ra, rb); } return; case 0x16: /* processor mgmt + byte reversed load and store instructions */ strcpy(opcode, proc_mgmt_str[index]); if (index == 4) { /* stwcx.*/ if (!rc_set) { opc_illegal(ctx); return; } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", opcode, rs, rb); else fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); return; } } /* eciwx, ecowx, lhbrx, lwbrx, stwbrx, sthbrx */ else if ((index == 9) | (index == 13) | (index == 16) | (index == 20) | (index == 24) | (index == 28)) { if (rc_set) { opc_illegal(ctx); return; } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", opcode, rs, rb); else fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); return; } } else if ((index == 18) | (index == 26)) { /* sync, eieio */ ctx->instr_str = my_sprintf("%-8s", opcode); return; } /* dcba, dcbf, dcbi, dcbst, dcbt, dcbz, icbi */ else if ( (index == 1) | (index == 2) | (index == 7) | (index == 8) | (index == 14) | (index == 23) | (index == 30) | (index == 31)) { if (rc_set | (rs != 0)) { opc_illegal(ctx); return; } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8s0, r%d", opcode, rb); else fmt_twoop(ctx->instr_str, opcode, ra, rb); return; } } else if (index == 17) { /* tlbsync */ ctx->instr_str = my_sprintf("%-8s", opcode); return; } else { opc_illegal(ctx); } return; break; } auto ref_spr = (((ctx->instr_code >> 11) & 31) << 5) | ((ctx->instr_code >> 16) & 31); auto spr_high = (ctx->instr_code >> 11) & 31; auto spr_low = (ctx->instr_code >> 16) & 31; switch (ext_opc) { case 0: /* cmp */ if (rc_set) { opc_illegal(ctx); return; } if (ctx->simplified) { if (!(rs & 1)) { if ((rs >> 2) == 0) { ctx->instr_str = my_sprintf("%-8sr%d, r%d", "cmpw", ra, rb); return; } else { ctx->instr_str = my_sprintf("%-8scr%d, r%d, r%d", "cmpw", (rs >> 2), ra, rb); return; } } } ctx->instr_str = my_sprintf("%-8scr%d, r%d, r%d", "cmp", (rs >> 2), ra, rb); break; case 4: /* tw */ if (rc_set) { opc_illegal(ctx); } else { if (ctx->simplified) { strcpy(opcode, trap_cond[rs]); if (strlen(opcode) != 0) { ctx->instr_str = my_sprintf("%-8sr%d, r%d", opcode, ra, rb); break; } } ctx->instr_str = my_sprintf("%-8s%d, r%d, r%d", "tw", rs, ra, rb); } break; case 19: /* mfcr */ fmt_oneop(ctx->instr_str, "mfcr", rs); break; case 20: /* lwarx */ if (rc_set) { opc_illegal(ctx); } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", "lwarx", rs, rb); else fmt_threeop(ctx->instr_str, "lwarx", rs, ra, rb); } break; case 29: /* maskg */ strcpy(opcode, "maskg"); if (rc_set) strcat(opcode, "."); fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); break; case 32: /* cmpl */ if (rc_set) { opc_illegal(ctx); return; } if (ctx->simplified) { if (!(rs & 1)) { if ((rs >> 2) == 0) ctx->instr_str = my_sprintf("%-8sr%d, r%d", "cmplw", ra, rb); else ctx->instr_str = my_sprintf("%-8scr%d, r%d, r%d", "cmplw", (rs >> 2), ra, rb); return; } } else { ctx->instr_str = my_sprintf( "%-8scr%d, %d, r%d, r%d", "cmpl", (rs >> 2), (rs & 1), ra, rb); } break; case 83: /* mfmsr */ ctx->instr_str = my_sprintf("%-8sr%d", "mfmsr", (ctx->instr_code >> 21) & 0x1F); break; case 144: /* mtcrf */ if (ctx->instr_code & 0x100801) opc_illegal(ctx); else { if (((ctx->instr_code >> 12) & 0xFF) == 0xFF) ctx->instr_str = my_sprintf("%-8sr%d", "mtcr", rs); else ctx->instr_str = my_sprintf( "%-8s0x%02X, r%d", "mtcrf", (ctx->instr_code >> 12) & 0xFF, rs); } break; case 277: /* lscbx */ strcpy(opcode, "lscbx"); if (rc_set) strcat(opcode, "."); fmt_threeop(ctx->instr_str, opcode, rs, ra, rb); break; case 339: /* mfspr */ if (ctx->simplified) { switch (spr_high) { case 0: strcpy(opcode, "mf"); strcat(opcode, spr_index0[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 8: strcpy(opcode, "mf"); strcat(opcode, spr_index8[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 16: strcpy(opcode, "mf"); strcat(opcode, spr_index16[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 29: strcpy(opcode, "mf"); strcat(opcode, spr_index29[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 30: strcpy(opcode, "mf"); strcat(opcode, spr_index30[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 31: strcpy(opcode, "mf"); strcat(opcode, spr_index31[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; } } fmt_twoop_fromspr(ctx->instr_str, "mfspr", rs, ref_spr); break; case 371: /* mftb */ if (ctx->simplified) { if (ref_spr == 268) { fmt_oneop(ctx->instr_str, "mftbl", rs); } else if (ref_spr == 269) { fmt_oneop(ctx->instr_str, "mftbu", rs); } return; } ctx->instr_str = my_sprintf("%-8sr%d, %d", "mftb", rs, ref_spr); break; case 467: /* mtspr */ if (ctx->simplified) { switch (spr_high) { case 0: strcpy(opcode, "mt"); strcat(opcode, spr_index0[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 8: strcpy(opcode, "mt"); strcat(opcode, spr_index8[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 16: strcpy(opcode, "mt"); strcat(opcode, spr_index16[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 29: strcpy(opcode, "mt"); strcat(opcode, spr_index29[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 30: strcpy(opcode, "mt"); strcat(opcode, spr_index30[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; case 31: strcpy(opcode, "mt"); strcat(opcode, spr_index31[spr_low]); ctx->instr_str = my_sprintf("%-8sr%d", opcode, rs); return; } } fmt_twoop_tospr(ctx->instr_str, "mtspr", ref_spr, rs); break; case 512: /* mcrxr */ ctx->instr_str = my_sprintf("%-8scr%d", "mcrxr", (rs >> 2)); break; case 531: /* clcs */ strcpy(opcode, "clcs"); if (rc_set) strcat(opcode, "."); fmt_twoop(ctx->instr_str, opcode, rs, ra); break; case 533: /* lswx */ if (rc_set) { opc_illegal(ctx); } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", "lswx", rs, rb); else fmt_threeop(ctx->instr_str, "lswx", rs, ra, rb); } break; case 541: /* maskir */ strcpy(opcode, "maskir"); if (rc_set) strcat(opcode, "."); fmt_threeop(ctx->instr_str, opcode, ra, rs, rb); return; case 595: /* mfsr */ if (ra & 16) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%d, %d", "mfsr", rs, ra); break; case 597: /* lswi */ if (rc_set) { opc_illegal(ctx); } else { if (rb == 0) rb = 32; if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, %x", "lswi", rs, rb); else fmt_threeop_simm(ctx->instr_str, "lswi", rs, ra, rb); } break; case 659: /* mfsrin */ ctx->instr_str = my_sprintf("%-8sr%d, r%d", "mtsrin", rs, rb); break; case 661: /* stswx */ if (rc_set) { opc_illegal(ctx); return; } else { if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, r%d", "stswx", rs, rb); else fmt_threeop(ctx->instr_str, "stswx", rs, ra, rb); return; } break; case 725: /* stswi */ if (rc_set) { opc_illegal(ctx); return; } else { if (rb == 0) rb = 32; if (ra == 0) ctx->instr_str = my_sprintf("%-8sr%d, 0, %d", "stswi", rs, rb); else fmt_threeop_simm(ctx->instr_str, "stswi", rs, ra, rb); return; } break; default: opc_illegal(ctx); } } void opc_group59(PPCDisasmContext* ctx) { char opcode[10] = ""; auto rc = (ctx->instr_code >> 6) & 0x1F; auto rb = (ctx->instr_code >> 11) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rs = (ctx->instr_code >> 21) & 0x1F; int ext_opc = (ctx->instr_code >> 1) & 0x3FF; /* extract extended opcode */ bool rc_set = ctx->instr_code & 1; switch (ext_opc & 0x1F) { case 18: /* floating point division */ strcpy(opcode, "fdivs"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 20: /* floating point subtract */ strcpy(opcode, "fsubs"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 21: /* floating point addition */ strcpy(opcode, "fadds"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 22: /* floating point square root */ strcpy(opcode, "fsqrts"); if (rc_set) strcat(opcode, "."); if ((rc != 0) | (ra != 0)) opc_illegal(ctx); else fmt_twoop_flt(ctx->instr_str, "fsqrts", rs, rb); return; case 24: /* fres */ strcpy(opcode, "fres"); if (rc_set) strcat(opcode, "."); if ((rc != 0) | (ra != 0)) opc_illegal(ctx); else fmt_twoop_flt(ctx->instr_str, opcode, rs, rb); return; case 25: /* fmuls */ strcpy(opcode, opc_flt_ext_arith[25]); strcat(opcode, "s"); if (rc_set) strcat(opcode, "."); if (rb != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rc); return; case 28: /* fmsubs */ strcpy(opcode, opc_flt_ext_arith[28]); strcat(opcode, "s"); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 29: /* fmadds */ strcpy(opcode, opc_flt_ext_arith[29]); strcat(opcode, "s"); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 30: /* fnmsubs */ strcpy(opcode, opc_flt_ext_arith[30]); strcat(opcode, "s"); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 31: /* fnmadds */ strcpy(opcode, opc_flt_ext_arith[31]); strcat(opcode, "s"); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; } } void opc_group63(PPCDisasmContext* ctx) { char opcode[10] = ""; auto rc = (ctx->instr_code >> 6) & 0x1F; auto rb = (ctx->instr_code >> 11) & 0x1F; auto ra = (ctx->instr_code >> 16) & 0x1F; auto rs = (ctx->instr_code >> 21) & 0x1F; int ext_opc = (ctx->instr_code >> 1) & 0x3FF; /* extract extended opcode */ bool rc_set = ctx->instr_code & 1; switch (ext_opc & 0x1F) { case 18: /* floating point division */ strcpy(opcode, "fdiv"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 20: /* floating point subtract */ strcpy(opcode, "fsub"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 21: /* floating point addition */ strcpy(opcode, "fadd"); if (rc_set) strcat(opcode, "."); if (rc != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 22: /* floating point square root */ strcpy(opcode, "fsqrt"); if (rc_set) strcat(opcode, "."); if ((rc != 0) | (ra != 0)) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rb); return; case 23: /* fsel */ strcpy(opcode, opc_flt_ext_arith[23]); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 25: /* fmul */ strcpy(opcode, opc_flt_ext_arith[25]); if (rc_set) strcat(opcode, "."); if (rb != 0) opc_illegal(ctx); else fmt_threeop_flt(ctx->instr_str, opcode, rs, ra, rc); return; case 26: /* frsqrte */ strcpy(opcode, "frsqrte"); if (rc_set) strcat(opcode, "."); if ((rc != 0) | (ra != 0)) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sf%d, f%d", opcode, rs, rb); return; case 28: /* fmsub */ strcpy(opcode, opc_flt_ext_arith[28]); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 29: /* fmadd */ strcpy(opcode, opc_flt_ext_arith[29]); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 30: /* fnmsub */ strcpy(opcode, opc_flt_ext_arith[30]); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; case 31: /* fnmadd */ strcpy(opcode, opc_flt_ext_arith[31]); if (rc_set) strcat(opcode, "."); fmt_fourop_flt(ctx->instr_str, opcode, rs, ra, rc, rb); return; } auto fm = (ctx->instr_code >> 17) & 0xFF; switch (ext_opc) { case 0: /* fcmpu */ if (rs & 3) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8scr%d, f%d, f%d", "fcmpu", (rs >> 2), ra, rb); break; case 12: /* frsp */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8s%d, r%d, r%d", "frsp", rs, rb); break; case 14: /* fctiw */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8s%d, r%d, r%d", "fctiw", rs, rb); break; case 15: /* fctiwz */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8s%d, r%d, r%d", "fctiwz", rs, rb); break; case 32: /* fcmpo */ if (rs & 3) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8scr%d, f%d, f%d", "fcmpo", (rs >> 2), ra, rb); break; case 38: /* mtfsb1 */ strcpy(opcode, "mtfsb1"); if (rc_set) strcat(opcode, "."); ctx->instr_str = my_sprintf("%-8s%d", opcode, rs); break; case 40: /* fneg */ strcpy(opcode, "fneg"); if (rc_set) strcat(opcode, "."); if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%d, r%d", opcode, rs, rb); break; case 64: strcpy(opcode, "mcrfs"); ctx->instr_str = my_sprintf("%-8scr%d, cr%d", opcode, (rs >> 2), (ra >> 2)); break; case 70: /* mtfsb0 */ strcpy(opcode, "mtfsb0"); if (rc_set) strcat(opcode, "."); ctx->instr_str = my_sprintf("%-8scrb%d", opcode, rs); break; case 72: /* fmr */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sr%d, r%d", "fmr", rs, rb); break; case 134: /* mtfsfi */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8scr%d, r%d", "mtfsfi", (rs >> 2), (rb >> 1)); break; case 136: /* fnabs */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sf%d, f%d", "fnabs", rs, rb); break; case 264: /* fabs */ if (ra != 0) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8s%d, f%d, f%d", "fabs", rs, rb); break; case 583: /* mffs */ strcpy(opcode, "mffs"); if (rc_set) strcat(opcode, "."); if ((ra != 0) | (rb != 0)) opc_illegal(ctx); else ctx->instr_str = my_sprintf("%-8sf%d", opcode, rs); break; case 711: /* mtfsf */ ctx->instr_str = my_sprintf("%-8sfm%d, r%d", "mtfsf", fm, rb); break; default: opc_illegal(ctx); } } void opc_intldst(PPCDisasmContext* ctx) { int32_t opcode = (ctx->instr_code >> 26) - 32; int32_t ra = (ctx->instr_code >> 16) & 0x1F; int32_t rd = (ctx->instr_code >> 21) & 0x1F; int32_t imm = SIGNEXT(ctx->instr_code & 0xFFFF, 15); /* ra = 0 is forbidden for loads and stores with update */ /* ra = rd is forbidden for loads with update */ if (((opcode < 14) && (opcode & 5) == 1 && ra == rd) || ((opcode & 1) && !ra)) { opc_illegal(ctx); return; } if (ra) { ctx->instr_str = my_sprintf( "%-8sr%d, %s0x%X(r%d)", opc_int_ldst[opcode], rd, ((imm < 0) ? "-" : ""), abs(imm), ra); } else { ctx->instr_str = my_sprintf( "%-8sr%d, %s0x%X", opc_int_ldst[opcode], rd, ((imm < 0) ? "-" : ""), abs(imm)); } } void opc_fltldst(PPCDisasmContext* ctx) { int32_t opcode = (ctx->instr_code >> 26) - 48; int32_t ra = (ctx->instr_code >> 16) & 0x1F; int32_t rd = (ctx->instr_code >> 21) & 0x1F; int32_t imm = SIGNEXT(ctx->instr_code & 0xFFFF, 15); /* ra = 0 is forbidden for loads and stores with update */ /* ra = rd is forbidden for loads with update */ if ((((opcode == 1) || (opcode == 3)) && ra == rd) || ((opcode & 1) && !ra)) { opc_illegal(ctx); return; } if (ra) { ctx->instr_str = my_sprintf( "%-8sf%d, %s0x%X(r%d)", opc_flt_ldst[opcode], rd, ((imm < 0) ? "-" : ""), abs(imm), ra); } else { ctx->instr_str = my_sprintf( "%-8sf%d, %s0x%X", opc_flt_ldst[opcode], rd, ((imm < 0) ? "-" : ""), abs(imm)); } } /** main dispatch table. */ static std::function OpcodeDispatchTable[64] = { opc_illegal, opc_illegal, opc_illegal, opc_twi, opc_group4, opc_illegal, opc_illegal, opc_ar_im, opc_ar_im, power_dozi, opc_cmp_i_li, opc_cmp_i_li, opc_ar_im, opc_ar_im, opc_ar_im, opc_ar_im, opc_bcx, opc_sc, opc_bx, opc_group19, opc_rlwimi, opc_rlwinm, opc_rlmi, opc_rlwnm, opc_bool_im, opc_bool_im, opc_bool_im, opc_bool_im, opc_bool_im, opc_bool_im, opc_illegal, opc_group31, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_intldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_fltldst, opc_illegal, opc_illegal, opc_illegal, opc_group59, opc_illegal, opc_illegal, opc_illegal, opc_group63}; string disassemble_single(PPCDisasmContext* ctx) { if (ctx->instr_addr & 3) { throw std::invalid_argument(string("PPC instruction address must be a multiply of 4!")); } OpcodeDispatchTable[ctx->instr_code >> 26](ctx); ctx->instr_addr += 4; return ctx->instr_str; }