mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 05:45:38 +00:00
CPU: Add disassembly of GTE instructions
This commit is contained in:
parent
e08f888e72
commit
cffb383c10
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "cpu_disasm.h"
|
#include "cpu_disasm.h"
|
||||||
#include "cpu_core.h"
|
#include "cpu_core.h"
|
||||||
|
#include "cpu_types.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/small_string.h"
|
#include "common/small_string.h"
|
||||||
|
@ -172,6 +173,89 @@ static const std::array<std::pair<CopCommonInstruction, const char*>, 4> s_cop_c
|
||||||
|
|
||||||
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
|
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
|
||||||
|
|
||||||
|
static constexpr const std::array<const char*, 64> s_gte_register_names = {
|
||||||
|
{"v0_xy", "v0_z", "v1_xy", "v1_z", "v2_xy", "v2_z", "rgbc", "otz", "ir0", "ir1", "ir2", "ir3", "sxy0",
|
||||||
|
"sxy1", "sxy2", "sxyp", "sz0", "sz1", "sz2", "sz3", "rgb0", "rgb1", "rgb2", "res1", "mac0", "mac1",
|
||||||
|
"mac2", "mac3", "irgb", "orgb", "lzcs", "lzcr", "rt_0", "rt_1", "rt_2", "rt_3", "rt_4", "trx", "try",
|
||||||
|
"trz", "llm_0", "llm_1", "llm_2", "llm_3", "llm_4", "rbk", "gbk", "bbk", "lcm_0", "lcm_1", "lcm_2", "lcm_3",
|
||||||
|
"lcm_4", "rfc", "gfc", "bfc", "ofx", "ofy", "h", "dqa", "dqb", "zsf3", "zsf4", "flag"}};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct GTEInstructionTable
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
bool sf;
|
||||||
|
bool lm;
|
||||||
|
bool mvmva;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
static constexpr const std::array<GTEInstructionTable, 64> s_gte_instructions = {{
|
||||||
|
{"rtps", true, true, false}, // 0x00
|
||||||
|
{"nclip", false, false, false}, // 0x01
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x02
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x03
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x04
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x05
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x06
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x07
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x08
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x09
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x0A
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x0B
|
||||||
|
{"op", true, true, false}, // 0x0C
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x0D
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x0E
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x0F
|
||||||
|
{"dpcs", true, true, false}, // 0x10
|
||||||
|
{"intpl", true, true, false}, // 0x11
|
||||||
|
{"mvmva", true, true, true}, // 0x12
|
||||||
|
{"ncds", true, true, false}, // 0x13
|
||||||
|
{"cdp", true, true, false}, // 0x14
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x15
|
||||||
|
{"ncdt", true, true, false}, // 0x16
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x17
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x18
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x19
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x1A
|
||||||
|
{"nccs", true, true, false}, // 0x1B
|
||||||
|
{"cc", true, true, false}, // 0x1C
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x1D
|
||||||
|
{"ncs", true, true, false}, // 0x1E
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x1F
|
||||||
|
{"nct", true, true, false}, // 0x20
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x21
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x22
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x23
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x24
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x25
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x26
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x27
|
||||||
|
{"sqr", true, true, false}, // 0x28
|
||||||
|
{"dcpl", true, true, false}, // 0x29
|
||||||
|
{"dpct", true, true, false}, // 0x2A
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x2B
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x2C
|
||||||
|
{"avsz3", false, false, false}, // 0x2D
|
||||||
|
{"avsz4", false, false, false}, // 0x2E
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x2F
|
||||||
|
{"rtpt", true, true, false}, // 0x30
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x31
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x32
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x33
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x34
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x35
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x36
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x37
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x38
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x39
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x3A
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x3B
|
||||||
|
{"UNKNOWN", false, false, false}, // 0x3C
|
||||||
|
{"gpf", true, true, false}, // 0x3D
|
||||||
|
{"gpl", true, true, false}, // 0x3E
|
||||||
|
{"ncct", true, true, false}, // 0x3F
|
||||||
|
}};
|
||||||
|
|
||||||
static void FormatInstruction(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
|
static void FormatInstruction(SmallStringBase* dest, const Instruction inst, u32 pc, const char* format)
|
||||||
{
|
{
|
||||||
dest->clear();
|
dest->clear();
|
||||||
|
@ -242,12 +326,18 @@ static void FormatInstruction(SmallStringBase* dest, const Instruction inst, u32
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "coprd", 5) == 0)
|
else if (std::strncmp(str, "coprd", 5) == 0)
|
||||||
{
|
{
|
||||||
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
|
if (inst.IsCop2Instruction())
|
||||||
|
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rd.GetValue())));
|
||||||
|
else
|
||||||
|
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
|
||||||
str += 5;
|
str += 5;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "coprt", 5) == 0)
|
else if (std::strncmp(str, "coprt", 5) == 0)
|
||||||
{
|
{
|
||||||
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rt.GetValue())));
|
if (inst.IsCop2Instruction())
|
||||||
|
dest->append(GetGTERegisterName(static_cast<u8>(inst.r.rt.GetValue())));
|
||||||
|
else
|
||||||
|
dest->append_format("{}", ZeroExtend32(static_cast<u8>(inst.r.rt.GetValue())));
|
||||||
str += 5;
|
str += 5;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "cop", 3) == 0)
|
else if (std::strncmp(str, "cop", 3) == 0)
|
||||||
|
@ -274,20 +364,20 @@ static void FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc,
|
||||||
if (std::strncmp(str, "rs", 2) == 0)
|
if (std::strncmp(str, "rs", 2) == 0)
|
||||||
{
|
{
|
||||||
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rs),
|
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rs),
|
||||||
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
|
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
|
||||||
|
|
||||||
str += 2;
|
str += 2;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "rt", 2) == 0)
|
else if (std::strncmp(str, "rt", 2) == 0)
|
||||||
{
|
{
|
||||||
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rt),
|
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rt),
|
||||||
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
|
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
|
||||||
str += 2;
|
str += 2;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "rd", 2) == 0)
|
else if (std::strncmp(str, "rd", 2) == 0)
|
||||||
{
|
{
|
||||||
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rd),
|
dest->append_format("{}{}=0x{:08X}", dest->empty() ? "" : ", ", GetRegName(inst.r.rd),
|
||||||
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
|
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
|
||||||
str += 2;
|
str += 2;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "shamt", 5) == 0)
|
else if (std::strncmp(str, "shamt", 5) == 0)
|
||||||
|
@ -310,7 +400,7 @@ static void FormatComment(SmallStringBase* dest, const Instruction inst, u32 pc,
|
||||||
{
|
{
|
||||||
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
|
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
|
||||||
dest->append_format("{}addr={:08X}", dest->empty() ? "" : ", ",
|
dest->append_format("{}addr={:08X}", dest->empty() ? "" : ", ",
|
||||||
regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
|
regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
|
||||||
str += 8;
|
str += 8;
|
||||||
}
|
}
|
||||||
else if (std::strncmp(str, "jt", 2) == 0)
|
else if (std::strncmp(str, "jt", 2) == 0)
|
||||||
|
@ -370,6 +460,25 @@ static void FormatCopComment(SmallStringBase* dest, u32 pc, Registers* regs, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FormatGTEInstruction(SmallStringBase* dest, u32 pc, const Instruction inst)
|
||||||
|
{
|
||||||
|
const GTE::Instruction gi{inst.bits};
|
||||||
|
const GTEInstructionTable& t = s_gte_instructions[gi.command];
|
||||||
|
dest->append(t.name);
|
||||||
|
|
||||||
|
if (t.sf && gi.sf)
|
||||||
|
dest->append(" sf");
|
||||||
|
|
||||||
|
if (t.lm && gi.lm)
|
||||||
|
dest->append(" lm");
|
||||||
|
|
||||||
|
if (t.mvmva)
|
||||||
|
{
|
||||||
|
dest->append_format(" m={} v={} t={}", static_cast<u8>(gi.mvmva_multiply_matrix),
|
||||||
|
static_cast<u8>(gi.mvmva_multiply_vector), static_cast<u8>(gi.mvmva_translation_vector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits)
|
void DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits)
|
||||||
{
|
{
|
||||||
const Instruction inst{bits};
|
const Instruction inst{bits};
|
||||||
|
@ -398,8 +507,13 @@ void DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionOp::cop1:
|
|
||||||
case InstructionOp::cop2:
|
case InstructionOp::cop2:
|
||||||
|
{
|
||||||
|
FormatGTEInstruction(dest, pc, inst);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionOp::cop1:
|
||||||
case InstructionOp::cop3:
|
case InstructionOp::cop3:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -459,8 +573,11 @@ void DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits, Regi
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionOp::cop1:
|
|
||||||
case InstructionOp::cop2:
|
case InstructionOp::cop2:
|
||||||
|
// TODO: Show GTE regs?
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionOp::cop1:
|
||||||
case InstructionOp::cop3:
|
case InstructionOp::cop3:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -491,4 +608,9 @@ void DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits, Regi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* CPU::GetGTERegisterName(u32 index)
|
||||||
|
{
|
||||||
|
return (index < s_gte_register_names.size()) ? s_gte_register_names[index] : "";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CPU
|
} // namespace CPU
|
|
@ -7,6 +7,10 @@
|
||||||
class SmallStringBase;
|
class SmallStringBase;
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
|
|
||||||
void DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits);
|
void DisassembleInstruction(SmallStringBase* dest, u32 pc, u32 bits);
|
||||||
void DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits, Registers* regs);
|
void DisassembleInstructionComment(SmallStringBase* dest, u32 pc, u32 bits, Registers* regs);
|
||||||
|
|
||||||
|
const char* GetGTERegisterName(u32 index);
|
||||||
|
|
||||||
} // namespace CPU
|
} // namespace CPU
|
||||||
|
|
|
@ -1401,15 +1401,3 @@ GTE::InstructionImpl GTE::GetInstructionImpl(u32 inst_bits, TickCount* ticks)
|
||||||
Panic("Missing handler");
|
Panic("Missing handler");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* GTE::GetRegisterName(u32 index)
|
|
||||||
{
|
|
||||||
static constexpr std::array<const char*, 64> names = {
|
|
||||||
{"V0_XY", "V0_Z", "V1_XY", "V1_Z", "V2_XY", "V2_Z", "RGBC", "OTZ", "IR0", "IR1", "IR2", "IR3", "SXY0",
|
|
||||||
"SXY1", "SXY2", "SXYP", "SZ0", "SZ1", "SZ2", "SZ3", "RGB0", "RGB1", "RGB2", "RES1", "MAC0", "MAC1",
|
|
||||||
"MAC2", "MAC3", "IRGB", "ORGB", "LZCS", "LZCR", "RT_0", "RT_1", "RT_2", "RT_3", "RT_4", "TRX", "TRY",
|
|
||||||
"TRZ", "LLM_0", "LLM_1", "LLM_2", "LLM_3", "LLM_4", "RBK", "GBK", "BBK", "LCM_0", "LCM_1", "LCM_2", "LCM_3",
|
|
||||||
"LCM_4", "RFC", "GFC", "BFC", "OFX", "OFY", "H", "DQA", "DQB", "ZSF3", "ZSF4", "FLAG"}};
|
|
||||||
|
|
||||||
return (index < names.size()) ? names[index] : "";
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,6 +25,4 @@ void ExecuteInstruction(u32 inst_bits);
|
||||||
using InstructionImpl = void (*)(Instruction);
|
using InstructionImpl = void (*)(Instruction);
|
||||||
InstructionImpl GetInstructionImpl(u32 inst_bits, TickCount* ticks);
|
InstructionImpl GetInstructionImpl(u32 inst_bits, TickCount* ticks);
|
||||||
|
|
||||||
const char* GetRegisterName(u32 index);
|
|
||||||
|
|
||||||
} // namespace GTE
|
} // namespace GTE
|
||||||
|
|
Loading…
Reference in a new issue