jak-project/decompiler/Disasm/InstructionDecode.cpp
2023-11-04 13:14:14 -04:00

1239 lines
29 KiB
C++

/*!
* @file InstructionDecode.cpp
* The Instruction Decoder - converts a LinkedWord into a Instruction.
* This is the part of the disassembler that decodes MIPS instructions.
*/
#include "InstructionDecode.h"
#include "common/util/Assert.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "third-party/fmt/core.h"
namespace decompiler {
// utility class to extract fields of an opcode.
struct OpcodeFields {
OpcodeFields(uint32_t _data) : data(_data) {}
// 26 - 31
uint32_t op() { return (data >> 26); }
//////////////
// R - Type //
//////////////
// 21 - 25
uint32_t rs() { return (data >> 21) & 0x1f; }
// 16 - 20
uint32_t rt() { return (data >> 16) & 0x1f; }
// 11 - 15
uint32_t rd() { return (data >> 11) & 0x1f; }
// 6 - 10
uint32_t sa() { return (data >> 6) & 0x1f; }
// 0 - 5
// TODO - remove once we update clang-format
// clang-format off
uint32_t function() { return (data)&0x3f; }
// clang-format on
////////////////
// Immediates //
////////////////
int32_t simm16() { return (int16_t)(data); }
int32_t zimm16() { return (uint16_t)(data); }
uint32_t imm5() { return (data >> 6) & 0x1f; }
uint32_t imm15() { return (data >> 6) & 0b111111111111111; }
////////////////////
// Floating Point //
////////////////////
uint32_t cop_func() { return (data >> 21) & 0x1f; }
uint32_t ft() { return (data >> 16) & 0x1f; }
uint32_t fs() { return (data >> 11) & 0x1f; }
uint32_t fd() { return (data >> 6) & 0x1f; }
////////////
// Others //
////////////
uint32_t pcreg() { return (data >> 1) & 0x1f; }
uint32_t syscall() { return (data >> 6) & 0xfffff; }
uint32_t MMI_func() { return (data >> 6) & 0x1f; }
uint32_t lower11() { return (uint32_t)(data & 0x7ff); }
uint32_t lower6() { return (uint32_t)(data & 0b111111); }
uint32_t dest() { return (data >> 21) & 0b1111; }
uint32_t fs_f() { return (data >> 21) & 0b11; }
uint32_t ft_f() { return (data >> 23) & 0b11; }
uint32_t data;
};
//////////////////
// OPCODE DECODE
//////////////////
static InstructionKind decode_cop2(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.lower11()) {
case 0b0:
case 0b1:
switch (fields.cop_func()) {
case 0b00001:
return IK::QMFC2;
case 0b00101:
ASSERT(((fields.data >> 1) & (0b1111111111)) == 0);
return IK::QMTC2;
case 0b00010:
return IK::CFC2;
case 0b00110:
return IK::CTC2;
default:
ASSERT(false);
return IK::UNKNOWN;
}
break;
case 0b00010111100:
case 0b00010111101:
case 0b00010111110:
case 0b00010111111:
ASSERT(fields.data & (1 << 25));
return IK::VMADDA_BC;
case 0b00000111100:
case 0b00000111101:
case 0b00000111110:
case 0b00000111111:
ASSERT(fields.data & (1 << 25));
return IK::VADDA_BC;
case 0b00110111100:
case 0b00110111101:
case 0b00110111110:
case 0b00110111111:
ASSERT(fields.data & (1 << 25));
return IK::VMULA_BC;
case 0b01010111110:
ASSERT(fields.data & (1 << 25));
return IK::VMULA;
case 0b01010111100:
ASSERT(fields.data & (1 << 25));
return IK::VADDA;
case 0b01010111101:
ASSERT(fields.data & (1 << 25));
return IK::VMADDA;
case 0b00011111100:
case 0b00011111101:
case 0b00011111110:
case 0b00011111111:
ASSERT(fields.data & (1 << 25));
return IK::VMSUBA_BC;
case 0b00101111100:
ASSERT(fields.data & (1 << 25));
return IK::VFTOI0;
case 0b00101111101:
ASSERT(fields.data & (1 << 25));
return IK::VFTOI4;
case 0b00101111110:
ASSERT(fields.data & (1 << 25));
return IK::VFTOI12;
case 0b00101111111:
ASSERT(fields.data & (1 << 25));
return IK::VFTOI15;
case 0b00100111100:
ASSERT(fields.data & (1 << 25));
return IK::VITOF0;
case 0b00100111110:
ASSERT(fields.data & (1 << 25));
return IK::VITOF12;
case 0b00100111111:
ASSERT(fields.data & (1 << 25));
return IK::VITOF15;
case 0b00111111100:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.ft() == 0);
return IK::VMULAQ;
case 0b00111111101:
ASSERT(fields.data & (1 << 25));
return IK::VABS;
case 0b00111111111:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.dest() == 0b1110);
return IK::VCLIP;
case 0b01011111111:
ASSERT(fields.dest() == 0);
ASSERT(fields.ft() == 0);
ASSERT(fields.fs() == 0);
return IK::VNOP;
case 0b01101111101:
ASSERT(fields.data & (1 << 25));
return IK::VSQI;
case 0b01101111100:
ASSERT(fields.data & (1 << 25));
return IK::VLQI;
case 0b01110111111:
ASSERT(fields.dest() == 0);
ASSERT(fields.ft() == 0);
ASSERT(fields.fs() == 0);
return IK::VWAITQ;
case 0b01011111110:
ASSERT(fields.dest() == 0b1110);
ASSERT(fields.data & (1 << 25));
return IK::VOPMULA;
case 0b01011111101:
return IK::VMSUBA;
case 0b01100111100:
ASSERT(fields.data & (1 << 25));
return IK::VMOVE;
case 0b01100111101:
return IK::VMR32;
case 0b01110111100:
ASSERT(fields.data & (1 << 25));
return IK::VDIV;
case 0b01110111101:
ASSERT(fields.fs() == 0);
ASSERT(((fields.data >> 21) & 3) == 0);
ASSERT(fields.data & (1 << 25));
return IK::VSQRT;
case 0b01111111100:
ASSERT(((fields.data >> 23) & 3) == 0);
ASSERT(fields.data & (1 << 25));
return IK::VMTIR;
case 0b01110111110:
ASSERT(fields.data & (1 << 25));
return IK::VRSQRT;
case 0b10000111100:
ASSERT(fields.fs() == 0);
ASSERT(fields.data & (1 << 25));
return IK::VRNEXT;
case 0b10000111101:
ASSERT(fields.fs() == 0);
ASSERT(fields.data & (1 << 25));
return IK::VRGET;
case 0b10000111111:
ASSERT(fields.ft() == 0);
ASSERT(fields.data & (1 << 25));
ASSERT(((fields.data >> 23) & 3) == 0);
return IK::VRXOR;
default:
switch (fields.lower6()) {
case 0b000000:
case 0b000001:
case 0b000010:
case 0b000011:
ASSERT(fields.data & (1 << 25));
return IK::VADD_BC;
case 0b000100:
case 0b000101:
case 0b000110:
case 0b000111:
ASSERT(fields.data & (1 << 25));
return IK::VSUB_BC;
case 0b001000:
case 0b001001:
case 0b001010:
case 0b001011:
ASSERT(fields.data & (1 << 25));
return IK::VMADD_BC;
case 0b001100:
case 0b001101:
case 0b001110:
case 0b001111:
ASSERT(fields.data & (1 << 25));
return IK::VMSUB_BC;
case 0b010000:
case 0b010001:
case 0b010010:
case 0b010011:
ASSERT(fields.data & (1 << 25));
return IK::VMAX_BC;
case 0b010100:
case 0b010101:
case 0b010110:
case 0b010111:
ASSERT(fields.data & (1 << 25));
return IK::VMINI_BC;
case 0b011000:
case 0b011001:
case 0b011010:
case 0b011011:
ASSERT(fields.data & (1 << 25));
return IK::VMUL_BC;
case 0b011100:
ASSERT(fields.ft() == 0);
ASSERT(fields.data & (1 << 25));
return IK::VMULQ;
case 0b100000:
ASSERT(fields.data & (1 << 25));
return IK::VADDQ;
case 0b100100:
ASSERT(fields.data & (1 << 25));
return IK::VSUBQ;
case 0b100101:
ASSERT(fields.ft() == 0);
ASSERT(fields.data & (1 << 25));
return IK::VMSUBQ;
case 0b101000:
ASSERT(fields.data & (1 << 25));
return IK::VADD;
case 0b101001:
ASSERT(fields.data & (1 << 25));
return IK::VMADD;
case 0b101010:
ASSERT(fields.data & (1 << 25));
return IK::VMUL;
case 0b101011:
ASSERT(fields.data & (1 << 25));
return IK::VMAX;
case 0b101100:
ASSERT(fields.data & (1 << 25));
return IK::VSUB;
case 0b101101:
ASSERT(fields.data & (1 << 25));
return IK::VMSUB;
case 0b101110:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.dest() == 0b1110);
return IK::VOPMSUB;
case 0b101111:
ASSERT(fields.data & (1 << 25));
return IK::VMINI;
case 0b110010:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.dest() == 0b0);
return IK::VIADDI;
case 0b110100:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.dest() == 0b0);
return IK::VIAND;
case 0b111000:
ASSERT(fields.data & (1 << 25));
ASSERT(fields.dest() == 0b0);
return IK::VCALLMS;
default:
ASSERT_MSG(false, fmt::format("unknown cop2 lower11 case 0b{:b}\n", fields.lower11()));
return IK::UNKNOWN;
}
}
}
static InstructionKind decode_W(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.function()) {
case 0b100000:
ASSERT(fields.ft() == 0);
return IK::CVTSW;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_S(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.function()) {
case 0b000000:
return IK::ADDS;
case 0b000001:
return IK::SUBS;
case 0b000010:
return IK::MULS;
case 0b000011:
return IK::DIVS;
case 0b000101:
return IK::ABSS;
case 0b000110:
ASSERT(fields.ft() == 0);
return IK::MOVS;
case 0b000111:
ASSERT(fields.ft() == 0);
return IK::NEGS;
case 0b000100:
ASSERT(fields.fs() == 0);
return IK::SQRTS;
case 0b010110:
return IK::RSQRTS;
case 0b011000:
ASSERT(fields.fd() == 0);
return IK::ADDAS;
case 0b011010:
ASSERT(fields.fd() == 0);
return IK::MULAS;
case 0b011100:
return IK::MADDS;
case 0b011101:
return IK::MSUBS;
case 0b011110:
ASSERT(fields.fd() == 0);
return IK::MADDAS;
case 0b011111:
ASSERT(fields.fd() == 0);
return IK::MSUBAS;
case 0b100100:
ASSERT(fields.ft() == 0);
return IK::CVTWS;
case 0b101000:
return IK::MAXS;
case 0b101001:
return IK::MINS;
case 0b110010:
ASSERT(fields.fd() == 0);
return IK::CEQS;
case 0b110100:
ASSERT(fields.fd() == 0);
return IK::CLTS;
case 0b110110:
ASSERT(fields.fd() == 0);
return IK::CLES;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_BC1(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.ft()) {
case 0b00000:
return IK::BC1F;
case 0b00001:
return IK::BC1T;
case 0b00010:
return IK::BC1FL;
case 0b00011:
return IK::BC1TL;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_cop1(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.cop_func()) {
case 0b00000:
ASSERT(fields.sa() == 0);
ASSERT(fields.function() == 0);
return IK::MFC1;
case 0b00100:
ASSERT(fields.sa() == 0);
ASSERT(fields.function() == 0);
return IK::MTC1;
case 0b01000:
return decode_BC1(fields);
case 0b10000:
return decode_S(fields);
case 0b10100:
return decode_W(fields);
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_c0(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.function()) {
case 0b011000:
return IK::ERET;
case 0b111000:
ASSERT(fields.sa() == 0 && fields.rd() == 0 && fields.rt() == 0);
return IK::EI;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_mt0(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.lower11()) {
case 0b00000000000:
return IK::MTC0;
case 0b00000000100:
ASSERT(fields.rd() == 0b11000);
return IK::MTDAB;
case 0b00000000101:
ASSERT(fields.rd() == 0b11000);
return IK::MTDABM;
default:
if (fields.rd() == 0b11001 && fields.sa() == 0 && (fields.data & 1) == 1) {
return IK::MTPC;
} else {
ASSERT(false);
return IK::UNKNOWN;
}
}
}
static InstructionKind decode_mf0(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.lower11()) {
case 0b0:
return IK::MFC0;
default:
if (fields.rd() == 0b11001 && fields.sa() == 0 && (fields.data & 1) == 1) {
return IK::MFPC;
} else {
ASSERT(false);
return IK::UNKNOWN;
}
}
}
static InstructionKind decode_cop0(OpcodeFields fields) {
switch (fields.cop_func()) {
case 0b00000:
return decode_mf0(fields);
case 0b00100:
return decode_mt0(fields);
case 0b10000:
return decode_c0(fields);
default:
ASSERT(false);
return InstructionKind::UNKNOWN;
}
}
static InstructionKind decode_mmi3(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.MMI_func()) {
case 0b01010:
return IK::PINTEH;
case 0b01110:
return IK::PCPYUD;
case 0b10010:
return IK::POR;
case 0b10011:
return IK::PNOR;
case 0b11011:
ASSERT(fields.rs() == 0);
return IK::PCPYH;
case 0b11110:
return IK::PEXCW;
default:
ASSERT_MSG(false, fmt::format("unknown mmi3: 0b{:b}\n", fields.MMI_func()));
return IK::UNKNOWN;
}
}
static InstructionKind decode_mmi2(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.MMI_func()) {
case 0b01110:
return IK::PCPYLD;
case 0b10000:
return IK::PMADDH;
case 0b10010:
return IK::PAND;
case 0b10011:
return IK::PXOR;
case 0b11100:
return IK::PMULTH;
case 0b11110:
return IK::PEXEW;
case 0b11111:
return IK::PROT3W;
default:
ASSERT_MSG(false, fmt::format("unknown mmi2: 0b{:b}\n", fields.MMI_func()));
return IK::UNKNOWN;
}
}
static InstructionKind decode_mmi1(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.MMI_func()) {
case 0b00001:
return IK::PABSW;
case 0b00010:
return IK::PCEQW;
case 0b00011:
return IK::PMINW;
case 0b00111:
return IK::PMINH;
case 0b01010:
return IK::PCEQB;
case 0b10010:
return IK::PEXTUW;
case 0b10110:
return IK::PEXTUH;
case 0b11010:
return IK::PEXTUB;
case 0b11011:
return IK::QFSRV;
default:
ASSERT_MSG(false, fmt::format("unknown mmi1: 0b{:b}\n", fields.MMI_func()));
return IK::UNKNOWN;
}
}
static InstructionKind decode_mmi0(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.MMI_func()) {
case 0b00000:
return IK::PADDW;
case 0b00001:
return IK::PSUBW;
case 0b00010:
return IK::PCGTW;
case 0b00011:
return IK::PMAXW;
case 0b00100:
return IK::PADDH;
case 0b00101:
return IK::PSUBH;
case 0b00111:
return IK::PMAXH;
case 0b01000:
return IK::PADDB;
case 0b01010:
return IK::PCGTB;
case 0b10010:
return IK::PEXTLW;
case 0b10011:
return IK::PPACW;
case 0b10111:
return IK::PPACH;
case 0b10110:
return IK::PEXTLH;
case 0b11010:
return IK::PEXTLB;
case 0b11011:
return IK::PPACB;
default:
ASSERT_MSG(false, fmt::format("unknown mmi0: 0b{:b}\n", fields.MMI_func()));
return IK::UNKNOWN;
}
}
static InstructionKind decode_pmfhl(OpcodeFields fields) {
// the PMFHL instruction is split into several types, and we create different instructions for
// each.
typedef InstructionKind IK;
switch (fields.sa()) {
case 0b00001:
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
return IK::PMFHL_UW;
case 0b00000:
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
return IK::PMFHL_LW;
case 0b00011:
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
return IK::PMFHL_LH;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_mmi(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.function()) {
case 0b000100:
ASSERT(fields.sa() == 0);
ASSERT(fields.rt() == 0);
return IK::PLZCW;
case 0b001000:
return decode_mmi0(fields);
case 0b001001:
return decode_mmi2(fields);
case 0b010011:
ASSERT(fields.sa() == 0);
ASSERT(fields.rd() == 0);
ASSERT(fields.rt() == 0);
return IK::MTLO1;
case 0b010010:
ASSERT(fields.sa() == 0);
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
return IK::MFLO1;
case 0b101000:
return decode_mmi1(fields);
case 0b101001:
return decode_mmi3(fields);
case 0b110000:
return decode_pmfhl(fields);
case 0b110100:
return IK::PSLLH;
case 0b110110:
return IK::PSRLH;
case 0b110111:
return IK::PSRAH;
case 0b111100:
return IK::PSLLW;
case 0b111111:
return IK::PSRAW;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_regimm(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.rt()) {
case 0b00000:
return IK::BLTZ;
case 0b00001:
return IK::BGEZ;
case 0b00010:
return IK::BLTZL;
case 0b00011:
return IK::BGEZL;
case 0b10001:
return IK::BGEZAL;
case 0b11000:
return IK::MTSAB;
default:
ASSERT_MSG(false, fmt::format("unknown regimm: 0b{:b}\n", fields.rt()));
return IK::UNKNOWN;
}
}
static InstructionKind decode_sync(OpcodeFields fields) {
// the "sync" opcode has a "stype" field which picks between P and L type syncs.
// to avoid implementing this, we just split SYNC into two separate instructions.
typedef InstructionKind IK;
auto stype = fields.sa();
ASSERT(fields.rt() == 0);
ASSERT(fields.rs() == 0);
ASSERT(fields.rd() == 0);
if (stype == 0b00000) {
return IK::SYNCL;
} else if (stype == 0b10000) {
return IK::SYNCP;
} else {
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_special(OpcodeFields fields) {
typedef InstructionKind IK;
switch (fields.function()) {
case 0b000000:
ASSERT(fields.rs() == 0);
return IK::SLL;
// RESERVED
case 0b000010:
ASSERT(fields.rs() == 0);
return IK::SRL;
case 0b000011:
ASSERT(fields.rs() == 0);
return IK::SRA;
case 0b000100:
ASSERT(fields.sa() == 0);
return IK::SLLV;
// RESERVED
// SRLV
// SRAV
case 0b001000:
ASSERT(fields.sa() == 0);
ASSERT(fields.rd() == 0);
ASSERT(fields.rt() == 0);
return IK::JR;
case 0b001001:
ASSERT(fields.rt() == 0);
ASSERT(fields.sa() == 0);
return IK::JALR;
case 0b001010:
ASSERT(fields.sa() == 0);
return IK::MOVZ;
case 0b001011:
ASSERT(fields.sa() == 0);
return IK::MOVN;
case 0b001100:
return IK::SYSCALL;
// BREAK
// RESERVED
case 0b001111:
return decode_sync(fields);
case 0b010000:
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
ASSERT(fields.sa() == 0);
return IK::MFHI;
// MTHI
case 0b010010:
ASSERT(fields.rs() == 0);
ASSERT(fields.rt() == 0);
ASSERT(fields.sa() == 0);
return IK::MFLO;
// MTLO
case 0b010100:
ASSERT(fields.sa() == 0);
return IK::DSLLV;
// RESERVED
case 0b010110:
ASSERT(fields.sa() == 0);
return IK::DSRLV;
case 0b010111:
ASSERT(fields.sa() == 0);
return IK::DSRAV;
case 0b011000:
ASSERT(fields.sa() == 0);
return IK::MULT3;
case 0b011001:
ASSERT(fields.sa() == 0);
return IK::MULTU3;
case 0b011010:
ASSERT(fields.sa() == 0);
ASSERT(fields.rd() == 0);
return IK::DIV;
case 0b011011:
ASSERT(fields.sa() == 0);
ASSERT(fields.rd() == 0);
return IK::DIVU;
// 4x UNSUPPORTED
// ADD
case 0b100001:
ASSERT(fields.sa() == 0);
return IK::ADDU;
// SUB
case 0b100011:
ASSERT(fields.sa() == 0);
return IK::SUBU;
case 0b100100:
ASSERT(fields.sa() == 0);
return IK::AND;
case 0b100101:
ASSERT(fields.sa() == 0);
return IK::OR;
case 0b100110:
ASSERT(fields.sa() == 0);
return IK::XOR;
case 0b100111:
ASSERT(fields.sa() == 0);
return IK::NOR;
// MFSA
// MTSA
case 0b101010:
ASSERT(fields.sa() == 0);
return IK::SLT;
case 0b101011:
ASSERT(fields.sa() == 0);
return IK::SLTU;
// DADD
case 0b101101:
return IK::DADDU;
// DSUB
case 0b101111:
return IK::DSUBU;
// TGE
// TGEU
// TLT
// TLTU
// TEQ
// RESERVED
// TNE
// RESERVED
case 0b111000:
ASSERT(fields.rs() == 0);
return IK::DSLL;
// RESERVED
case 0b111010:
ASSERT(fields.rs() == 0);
return IK::DSRL;
case 0b111011:
ASSERT(fields.rs() == 0);
return IK::DSRA;
case 0b111100:
ASSERT(fields.rs() == 0);
return IK::DSLL32;
// RESERVED
case 0b111110:
ASSERT(fields.rs() == 0);
return IK::DSRL32;
case 0b111111:
ASSERT(fields.rs() == 0);
return IK::DSRA32;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
static InstructionKind decode_cache(OpcodeFields fields) {
typedef InstructionKind IK;
// there's only one cache instruction used (DXWBIN), so we just use a CACHE DXWBIN instruction
// to avoid having to implement the full cache instruction decoding.
switch (fields.rt()) {
case 0b10100:
return IK::CACHE_DXWBIN;
default:
ASSERT(false);
return IK::UNKNOWN;
}
}
/*!
* Top level opcode decode
*/
static InstructionKind decode_opcode(uint32_t code) {
OpcodeFields fields(code);
typedef InstructionKind IK;
switch (fields.op()) {
case 0b000000:
return decode_special(fields);
case 0b000001:
return decode_regimm(fields);
// J 010
// JAL 011
case 0b000100:
return IK::BEQ;
case 0b000101:
return IK::BNE;
case 0b000110:
return IK::BLEZ;
case 0b000111:
return IK::BGTZ;
// ADDI 1000
case 0b001001:
return IK::ADDIU;
case 0b001010:
return IK::SLTI;
case 0b001011:
return IK::SLTIU;
case 0b001100:
return IK::ANDI;
case 0b001101:
return IK::ORI;
case 0b001110:
return IK::XORI;
case 0b001111:
ASSERT(fields.rs() == 0);
return IK::LUI;
case 0b010000:
return decode_cop0(fields);
case 0b010001:
return decode_cop1(fields);
case 0b010010:
return decode_cop2(fields);
// 010011:
// reserved
case 0b010100:
return IK::BEQL;
case 0b010101:
return IK::BNEL;
// 010110
// blezl
case 0b010111:
ASSERT(fields.rt() == 0);
return IK::BGTZL;
// 0b011000:
// daddi
case 0b011001:
return IK::DADDIU;
case 0b011010:
return IK::LDL;
case 0b011011:
return IK::LDR;
case 0b011100:
return decode_mmi(fields);
// 0b011101:
// reserved
case 0b011110:
return IK::LQ;
case 0b011111:
return IK::SQ;
case 0b100000:
return IK::LB;
case 0b100001:
return IK::LH;
case 0b100010:
return IK::LWL;
case 0b100011:
return IK::LW;
case 0b100100:
return IK::LBU;
case 0b100101:
return IK::LHU;
case 0b100110:
return IK::LWR;
case 0b100111:
return IK::LWU;
case 0b101000:
return IK::SB;
case 0b101001:
return IK::SH;
case 0b101011:
return IK::SW;
// SDL
// SDR
// SWR
case 0b101111:
return decode_cache(fields);
// unsupported
case 0b110001:
return IK::LWC1;
// unsupported
case 0b110011:
return IK::PREF;
// unsupported
// unsupported
case 0b110110:
return IK::LQC2;
case 0b110111:
return IK::LD;
case 0b111001:
return IK::SWC1;
case 0b111110:
return IK::SQC2;
case 0b111111:
return IK::SD;
default:
ASSERT(false);
return IK::UNKNOWN;
break;
}
}
/*!
* Top level decode function.
*/
Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg_id, int word_id) {
// determine the opcode, and get info for it
Instruction i;
auto op = decode_opcode(word.data);
auto& info = gOpcodeInfo[(int)op];
if (!info.defined) {
return i;
}
i.kind = op;
OpcodeFields fields(word.data);
// loop through decoding steps to extract a value
for (int j = 0; j < info.step_count; j++) {
auto& step = info.steps[j];
int32_t value;
switch (step.field) {
case FieldType::RS:
value = fields.rs();
break;
case FieldType::RT:
value = fields.rt();
break;
case FieldType::RD:
value = fields.rd();
break;
case FieldType::FT:
value = fields.ft();
break;
case FieldType::FS:
value = fields.fs();
break;
case FieldType::FD:
value = fields.fd();
break;
case FieldType::SIMM16:
value = fields.simm16();
break;
case FieldType::ZIMM16:
value = fields.zimm16();
break;
case FieldType::SA:
value = fields.sa();
break;
case FieldType::SYSCALL:
value = fields.syscall();
break;
case FieldType::PCR:
value = fields.pcreg();
break;
case FieldType::DEST:
value = fields.dest();
break;
case FieldType::BC:
value = fields.data & 0b11;
break;
case FieldType::IMM5:
value = fields.imm5();
break;
case FieldType::IL:
value = fields.data & 1;
break;
case FieldType::IMM15:
value = fields.imm15();
break;
case FieldType::ZERO:
value = 0;
break;
case FieldType::FT_F:
value = fields.ft_f();
break;
case FieldType::FS_F:
value = fields.fs_f();
break;
default:
ASSERT(false);
}
// use the value, to possibly add an atom
InstructionAtom atom;
switch (step.decode) {
case DecodeType::GPR:
atom.set_reg(Register(Reg::GPR, value));
break;
case DecodeType::FPR:
atom.set_reg(Register(Reg::FPR, value));
break;
case DecodeType::COP0:
atom.set_reg(Register(Reg::COP0, value));
break;
case DecodeType::PCR:
atom.set_reg(Register(Reg::SPECIAL, Reg::PCR0 + value));
break;
case DecodeType::IMM:
atom.set_imm(value);
break;
case DecodeType::VF:
atom.set_reg(Register(Reg::VF, value));
break;
case DecodeType::VI:
atom.set_reg(Register(Reg::VI, value));
break;
case DecodeType::VU_ACC:
atom.set_vu_acc();
break;
case DecodeType::VU_Q:
atom.set_vu_q();
break;
case DecodeType::VCALLMS_TARGET:
atom.set_imm(value);
break;
case DecodeType::BRANCH_TARGET:
atom.set_label(file.get_label_id_for(seg_id, (word_id + value + 1) * 4));
break;
case DecodeType::VF_F:
atom.set_vf_field(value);
break;
// NOTE - these change a property of the instruction and don't add an atom.
case DecodeType::IL:
i.il = value;
continue;
case DecodeType::DEST:
i.cop2_dest = value;
continue;
case DecodeType::BC:
i.cop2_bc = value;
continue;
default:
ASSERT(false);
}
if (step.is_src) {
i.add_src(atom);
} else {
i.add_dst(atom);
}
}
if (word.kind() == LinkedWord::SYM_OFFSET) {
bool fixed = false;
for (int j = 0; j < i.n_src; j++) {
if (i.src[j].kind == InstructionAtom::IMM) {
fixed = true;
i.src[j].set_sym(word.symbol_name());
}
}
ASSERT(fixed);
}
if (word.kind() == LinkedWord::SYM_VAL_OFFSET) {
bool fixed = false;
for (int j = 0; j < i.n_src; j++) {
if (i.src[j].kind == InstructionAtom::IMM) {
fixed = true;
i.src[j].set_sym_val_ptr(word.symbol_name());
}
}
ASSERT(fixed);
}
if (word.kind() == LinkedWord::HI_PTR) {
ASSERT(i.kind == InstructionKind::LUI);
bool fixed = false;
for (int j = 0; j < i.n_src; j++) {
if (i.src[j].kind == InstructionAtom::IMM) {
fixed = true;
i.src[j].set_label(word.label_id());
}
}
ASSERT(fixed);
}
if (word.kind() == LinkedWord::LO_PTR) {
ASSERT(i.kind == InstructionKind::ORI);
bool fixed = false;
for (int j = 0; j < i.n_src; j++) {
if (i.src[j].kind == InstructionAtom::IMM) {
fixed = true;
i.src[j].set_label(word.label_id());
}
}
ASSERT(fixed);
}
return i;
}
} // namespace decompiler