2020-08-22 23:30:17 -04:00
|
|
|
/*!
|
|
|
|
* @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"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2022-02-08 19:02:47 -05:00
|
|
|
#include "common/util/Assert.h"
|
2020-08-22 23:30:17 -04:00
|
|
|
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
|
|
|
|
2024-03-05 22:11:52 -05:00
|
|
|
#include "fmt/core.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2021-01-06 20:04:15 -05:00
|
|
|
namespace decompiler {
|
2020-08-22 23:30:17 -04:00
|
|
|
// 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
|
2023-11-04 13:14:14 -04:00
|
|
|
// TODO - remove once we update clang-format
|
|
|
|
// clang-format off
|
2020-08-22 23:30:17 -04:00
|
|
|
uint32_t function() { return (data)&0x3f; }
|
2023-11-04 13:14:14 -04:00
|
|
|
// clang-format on
|
2020-08-22 23:30:17 -04:00
|
|
|
|
|
|
|
////////////////
|
|
|
|
// 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; }
|
2021-02-13 12:05:50 -05:00
|
|
|
uint32_t fs_f() { return (data >> 21) & 0b11; }
|
|
|
|
uint32_t ft_f() { return (data >> 23) & 0b11; }
|
2020-08-22 23:30:17 -04:00
|
|
|
|
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(((fields.data >> 1) & (0b1111111111)) == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::QMTC2;
|
|
|
|
|
|
|
|
case 0b00010:
|
|
|
|
return IK::CFC2;
|
|
|
|
|
|
|
|
case 0b00110:
|
|
|
|
return IK::CTC2;
|
|
|
|
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0b00010111100:
|
|
|
|
case 0b00010111101:
|
|
|
|
case 0b00010111110:
|
|
|
|
case 0b00010111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMADDA_BC;
|
|
|
|
|
|
|
|
case 0b00000111100:
|
|
|
|
case 0b00000111101:
|
|
|
|
case 0b00000111110:
|
|
|
|
case 0b00000111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VADDA_BC;
|
|
|
|
|
|
|
|
case 0b00110111100:
|
|
|
|
case 0b00110111101:
|
|
|
|
case 0b00110111110:
|
|
|
|
case 0b00110111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMULA_BC;
|
|
|
|
|
|
|
|
case 0b01010111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMULA;
|
|
|
|
|
|
|
|
case 0b01010111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VADDA;
|
|
|
|
|
|
|
|
case 0b01010111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMADDA;
|
|
|
|
|
|
|
|
case 0b00011111100:
|
|
|
|
case 0b00011111101:
|
|
|
|
case 0b00011111110:
|
|
|
|
case 0b00011111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMSUBA_BC;
|
|
|
|
|
|
|
|
case 0b00101111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VFTOI0;
|
|
|
|
|
|
|
|
case 0b00101111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VFTOI4;
|
|
|
|
|
|
|
|
case 0b00101111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VFTOI12;
|
|
|
|
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b00101111111:
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
return IK::VFTOI15;
|
|
|
|
|
2020-08-22 23:30:17 -04:00
|
|
|
case 0b00100111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VITOF0;
|
|
|
|
|
|
|
|
case 0b00100111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VITOF12;
|
|
|
|
|
|
|
|
case 0b00100111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VITOF15;
|
|
|
|
|
|
|
|
case 0b00111111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.ft() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMULAQ;
|
|
|
|
|
|
|
|
case 0b00111111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VABS;
|
|
|
|
|
|
|
|
case 0b00111111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.dest() == 0b1110);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VCLIP;
|
|
|
|
|
|
|
|
case 0b01011111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.dest() == 0);
|
|
|
|
ASSERT(fields.ft() == 0);
|
|
|
|
ASSERT(fields.fs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VNOP;
|
|
|
|
case 0b01101111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VSQI;
|
|
|
|
case 0b01101111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VLQI;
|
|
|
|
case 0b01110111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.dest() == 0);
|
|
|
|
ASSERT(fields.ft() == 0);
|
|
|
|
ASSERT(fields.fs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VWAITQ;
|
|
|
|
|
|
|
|
case 0b01011111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.dest() == 0b1110);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VOPMULA;
|
|
|
|
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b01011111101:
|
|
|
|
return IK::VMSUBA;
|
|
|
|
|
2020-08-22 23:30:17 -04:00
|
|
|
case 0b01100111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMOVE;
|
|
|
|
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b01100111101:
|
|
|
|
return IK::VMR32;
|
|
|
|
|
2020-08-22 23:30:17 -04:00
|
|
|
case 0b01110111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VDIV;
|
|
|
|
|
|
|
|
case 0b01110111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fs() == 0);
|
|
|
|
ASSERT(((fields.data >> 21) & 3) == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VSQRT;
|
|
|
|
|
|
|
|
case 0b01111111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(((fields.data >> 23) & 3) == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMTIR;
|
|
|
|
|
|
|
|
case 0b01110111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VRSQRT;
|
|
|
|
|
|
|
|
case 0b10000111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fs() == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VRNEXT;
|
|
|
|
|
|
|
|
case 0b10000111101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fs() == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VRGET;
|
|
|
|
|
|
|
|
case 0b10000111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(((fields.data >> 23) & 3) == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VRXOR;
|
|
|
|
default:
|
|
|
|
|
|
|
|
switch (fields.lower6()) {
|
|
|
|
case 0b000000:
|
|
|
|
case 0b000001:
|
|
|
|
case 0b000010:
|
|
|
|
case 0b000011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VADD_BC;
|
|
|
|
|
|
|
|
case 0b000100:
|
|
|
|
case 0b000101:
|
|
|
|
case 0b000110:
|
|
|
|
case 0b000111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VSUB_BC;
|
|
|
|
|
|
|
|
case 0b001000:
|
|
|
|
case 0b001001:
|
|
|
|
case 0b001010:
|
|
|
|
case 0b001011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMADD_BC;
|
|
|
|
|
|
|
|
case 0b001100:
|
|
|
|
case 0b001101:
|
|
|
|
case 0b001110:
|
|
|
|
case 0b001111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMSUB_BC;
|
|
|
|
|
|
|
|
case 0b010000:
|
|
|
|
case 0b010001:
|
|
|
|
case 0b010010:
|
|
|
|
case 0b010011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMAX_BC;
|
|
|
|
|
|
|
|
case 0b010100:
|
|
|
|
case 0b010101:
|
|
|
|
case 0b010110:
|
|
|
|
case 0b010111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMINI_BC;
|
|
|
|
|
|
|
|
case 0b011000:
|
|
|
|
case 0b011001:
|
|
|
|
case 0b011010:
|
|
|
|
case 0b011011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMUL_BC;
|
|
|
|
|
|
|
|
case 0b011100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMULQ;
|
|
|
|
|
|
|
|
case 0b100000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VADDQ;
|
|
|
|
|
|
|
|
case 0b100100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VSUBQ;
|
|
|
|
|
|
|
|
case 0b100101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMSUBQ;
|
|
|
|
|
|
|
|
case 0b101000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VADD;
|
|
|
|
case 0b101001:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMADD;
|
|
|
|
case 0b101010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMUL;
|
|
|
|
case 0b101011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMAX;
|
|
|
|
case 0b101100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VSUB;
|
|
|
|
case 0b101101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMSUB;
|
|
|
|
case 0b101110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.dest() == 0b1110);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VOPMSUB;
|
|
|
|
case 0b101111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VMINI;
|
|
|
|
case 0b110010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.dest() == 0b0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VIADDI;
|
|
|
|
case 0b110100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.dest() == 0b0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VIAND;
|
|
|
|
case 0b111000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.data & (1 << 25));
|
|
|
|
ASSERT(fields.dest() == 0b0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::VCALLMS;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown cop2 lower11 case 0b{:b}\n", fields.lower11()));
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_W(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.function()) {
|
|
|
|
case 0b100000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::CVTSW;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MOVS;
|
|
|
|
case 0b000111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::NEGS;
|
|
|
|
case 0b000100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SQRTS;
|
|
|
|
case 0b010110:
|
|
|
|
return IK::RSQRTS;
|
|
|
|
case 0b011000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::ADDAS;
|
|
|
|
case 0b011010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MULAS;
|
|
|
|
case 0b011100:
|
|
|
|
return IK::MADDS;
|
|
|
|
case 0b011101:
|
|
|
|
return IK::MSUBS;
|
|
|
|
case 0b011110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MADDAS;
|
|
|
|
case 0b011111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MSUBAS;
|
|
|
|
case 0b100100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.ft() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::CVTWS;
|
|
|
|
case 0b101000:
|
|
|
|
return IK::MAXS;
|
|
|
|
case 0b101001:
|
|
|
|
return IK::MINS;
|
|
|
|
case 0b110010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::CEQS;
|
|
|
|
case 0b110100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::CLTS;
|
|
|
|
case 0b110110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.fd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::CLES;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_cop1(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.cop_func()) {
|
|
|
|
case 0b00000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.function() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MFC1;
|
|
|
|
case 0b00100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.function() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MTC1;
|
|
|
|
case 0b01000:
|
|
|
|
return decode_BC1(fields);
|
|
|
|
case 0b10000:
|
|
|
|
return decode_S(fields);
|
|
|
|
case 0b10100:
|
|
|
|
return decode_W(fields);
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_c0(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.function()) {
|
|
|
|
case 0b011000:
|
|
|
|
return IK::ERET;
|
|
|
|
case 0b111000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0 && fields.rd() == 0 && fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::EI;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_mt0(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.lower11()) {
|
|
|
|
case 0b00000000000:
|
|
|
|
return IK::MTC0;
|
|
|
|
case 0b00000000100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rd() == 0b11000);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MTDAB;
|
|
|
|
case 0b00000000101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rd() == 0b11000);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MTDABM;
|
|
|
|
default:
|
|
|
|
if (fields.rd() == 0b11001 && fields.sa() == 0 && (fields.data & 1) == 1) {
|
|
|
|
return IK::MTPC;
|
|
|
|
} else {
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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 {
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::PCPYH;
|
2023-10-12 18:07:37 -04:00
|
|
|
case 0b11110:
|
|
|
|
return IK::PEXCW;
|
2020-08-22 23:30:17 -04:00
|
|
|
default:
|
2023-10-12 18:07:37 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown mmi3: 0b{:b}\n", fields.MMI_func()));
|
2020-08-22 23:30:17 -04:00
|
|
|
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;
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b10011:
|
|
|
|
return IK::PXOR;
|
2020-08-22 23:30:17 -04:00
|
|
|
case 0b11100:
|
|
|
|
return IK::PMULTH;
|
|
|
|
case 0b11110:
|
|
|
|
return IK::PEXEW;
|
|
|
|
case 0b11111:
|
|
|
|
return IK::PROT3W;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown mmi2: 0b{:b}\n", fields.MMI_func()));
|
2020-08-22 23:30:17 -04:00
|
|
|
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;
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b11011:
|
|
|
|
return IK::QFSRV;
|
2020-08-22 23:30:17 -04:00
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown mmi1: 0b{:b}\n", fields.MMI_func()));
|
2020-08-22 23:30:17 -04:00
|
|
|
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;
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b00101:
|
|
|
|
return IK::PSUBH;
|
2020-08-22 23:30:17 -04:00
|
|
|
case 0b00111:
|
|
|
|
return IK::PMAXH;
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b01000:
|
|
|
|
return IK::PADDB;
|
|
|
|
case 0b01010:
|
|
|
|
return IK::PCGTB;
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown mmi0: 0b{:b}\n", fields.MMI_func()));
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::PMFHL_UW;
|
|
|
|
case 0b00000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::PMFHL_LW;
|
|
|
|
case 0b00011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::PMFHL_LH;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_mmi(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.function()) {
|
|
|
|
case 0b000100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::PLZCW;
|
|
|
|
case 0b001000:
|
|
|
|
return decode_mmi0(fields);
|
|
|
|
case 0b001001:
|
|
|
|
return decode_mmi2(fields);
|
|
|
|
|
|
|
|
case 0b010011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rd() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MTLO1;
|
|
|
|
case 0b010010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-06 17:58:49 -04:00
|
|
|
static InstructionKind decode_regimm(OpcodeFields fields) {
|
2020-08-22 23:30:17 -04:00
|
|
|
typedef InstructionKind IK;
|
2022-06-06 17:58:49 -04:00
|
|
|
switch (fields.rt()) {
|
2020-08-22 23:30:17 -04:00
|
|
|
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;
|
2022-06-06 17:58:49 -04:00
|
|
|
case 0b11000:
|
|
|
|
return IK::MTSAB;
|
2020-08-22 23:30:17 -04:00
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT_MSG(false, fmt::format("unknown regimm: 0b{:b}\n", fields.rt()));
|
2020-08-22 23:30:17 -04:00
|
|
|
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();
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rt() == 0);
|
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
if (stype == 0b00000) {
|
|
|
|
return IK::SYNCL;
|
|
|
|
} else if (stype == 0b10000) {
|
|
|
|
return IK::SYNCP;
|
|
|
|
} else {
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static InstructionKind decode_special(OpcodeFields fields) {
|
|
|
|
typedef InstructionKind IK;
|
|
|
|
switch (fields.function()) {
|
|
|
|
case 0b000000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SLL;
|
|
|
|
// RESERVED
|
|
|
|
case 0b000010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SRL;
|
|
|
|
case 0b000011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SRA;
|
|
|
|
case 0b000100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SLLV;
|
|
|
|
// RESERVED
|
|
|
|
// SRLV
|
|
|
|
// SRAV
|
|
|
|
case 0b001000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rd() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::JR;
|
|
|
|
case 0b001001:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rt() == 0);
|
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::JALR;
|
|
|
|
case 0b001010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MOVZ;
|
|
|
|
case 0b001011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MOVN;
|
|
|
|
case 0b001100:
|
|
|
|
return IK::SYSCALL;
|
|
|
|
// BREAK
|
|
|
|
// RESERVED
|
|
|
|
case 0b001111:
|
|
|
|
return decode_sync(fields);
|
|
|
|
|
|
|
|
case 0b010000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MFHI;
|
|
|
|
// MTHI
|
|
|
|
case 0b010010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
|
|
|
ASSERT(fields.rt() == 0);
|
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MFLO;
|
|
|
|
// MTLO
|
|
|
|
case 0b010100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSLLV;
|
|
|
|
// RESERVED
|
|
|
|
case 0b010110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRLV;
|
|
|
|
case 0b010111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRAV;
|
|
|
|
case 0b011000:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MULT3;
|
|
|
|
case 0b011001:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::MULTU3;
|
|
|
|
case 0b011010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DIV;
|
|
|
|
case 0b011011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
|
|
|
ASSERT(fields.rd() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DIVU;
|
|
|
|
// 4x UNSUPPORTED
|
|
|
|
// ADD
|
|
|
|
case 0b100001:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::ADDU;
|
|
|
|
// SUB
|
|
|
|
case 0b100011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SUBU;
|
|
|
|
case 0b100100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::AND;
|
|
|
|
case 0b100101:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::OR;
|
|
|
|
case 0b100110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::XOR;
|
|
|
|
case 0b100111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::NOR;
|
|
|
|
// MFSA
|
|
|
|
// MTSA
|
|
|
|
case 0b101010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::SLT;
|
|
|
|
case 0b101011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.sa() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSLL;
|
|
|
|
// RESERVED
|
|
|
|
case 0b111010:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRL;
|
|
|
|
case 0b111011:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRA;
|
|
|
|
case 0b111100:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSLL32;
|
|
|
|
// RESERVED
|
|
|
|
case 0b111110:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRL32;
|
|
|
|
case 0b111111:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
return IK::DSRA32;
|
|
|
|
default:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rs() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fields.rt() == 0);
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2022-06-06 17:58:49 -04:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
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;
|
2021-02-13 12:05:50 -05:00
|
|
|
case FieldType::FT_F:
|
|
|
|
value = fields.ft_f();
|
|
|
|
break;
|
|
|
|
case FieldType::FS_F:
|
|
|
|
value = fields.fs_f();
|
|
|
|
break;
|
2020-08-22 23:30:17 -04:00
|
|
|
default:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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:
|
2021-06-18 21:10:00 -04:00
|
|
|
atom.set_reg(Register(Reg::SPECIAL, Reg::PCR0 + value));
|
2020-08-22 23:30:17 -04:00
|
|
|
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:
|
2021-06-21 19:47:39 -04:00
|
|
|
atom.set_imm(value);
|
2020-08-22 23:30:17 -04:00
|
|
|
break;
|
|
|
|
case DecodeType::BRANCH_TARGET:
|
|
|
|
atom.set_label(file.get_label_id_for(seg_id, (word_id + value + 1) * 4));
|
|
|
|
break;
|
2021-02-13 12:05:50 -05:00
|
|
|
case DecodeType::VF_F:
|
|
|
|
atom.set_vf_field(value);
|
|
|
|
break;
|
2020-08-22 23:30:17 -04:00
|
|
|
|
|
|
|
// 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:
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(false);
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (step.is_src) {
|
|
|
|
i.add_src(atom);
|
|
|
|
} else {
|
|
|
|
i.add_dst(atom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-04 12:33:18 -05:00
|
|
|
if (word.kind() == LinkedWord::SYM_OFFSET) {
|
2020-08-22 23:30:17 -04:00
|
|
|
bool fixed = false;
|
|
|
|
for (int j = 0; j < i.n_src; j++) {
|
|
|
|
if (i.src[j].kind == InstructionAtom::IMM) {
|
|
|
|
fixed = true;
|
2021-12-04 12:33:18 -05:00
|
|
|
i.src[j].set_sym(word.symbol_name());
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
}
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fixed);
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
|
2022-10-16 18:19:59 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-12-04 12:33:18 -05:00
|
|
|
if (word.kind() == LinkedWord::HI_PTR) {
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(i.kind == InstructionKind::LUI);
|
2020-08-22 23:30:17 -04:00
|
|
|
bool fixed = false;
|
|
|
|
for (int j = 0; j < i.n_src; j++) {
|
|
|
|
if (i.src[j].kind == InstructionAtom::IMM) {
|
|
|
|
fixed = true;
|
2021-12-04 12:33:18 -05:00
|
|
|
i.src[j].set_label(word.label_id());
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
}
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fixed);
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
|
2021-12-04 12:33:18 -05:00
|
|
|
if (word.kind() == LinkedWord::LO_PTR) {
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(i.kind == InstructionKind::ORI);
|
2020-08-22 23:30:17 -04:00
|
|
|
bool fixed = false;
|
|
|
|
for (int j = 0; j < i.n_src; j++) {
|
|
|
|
if (i.src[j].kind == InstructionAtom::IMM) {
|
|
|
|
fixed = true;
|
2021-12-04 12:33:18 -05:00
|
|
|
i.src[j].set_label(word.label_id());
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
}
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fixed);
|
2020-08-22 23:30:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
2020-10-15 20:59:30 -04:00
|
|
|
}
|
2021-03-06 10:46:26 -05:00
|
|
|
} // namespace decompiler
|