This commit is contained in:
NoName 2025-04-23 14:33:21 -07:00 committed by GitHub
commit 90b78d8aa7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 362 additions and 88 deletions

View file

@ -681,6 +681,7 @@ void GekkoIRPlugin::EvalTerminalRel(Terminal type, const AssemblerToken& tok)
case Terminal::Bin: case Terminal::Bin:
case Terminal::GPR: case Terminal::GPR:
case Terminal::FPR: case Terminal::FPR:
case Terminal::GQR:
case Terminal::SPR: case Terminal::SPR:
case Terminal::CRField: case Terminal::CRField:
case Terminal::Lt: case Terminal::Lt:
@ -732,6 +733,7 @@ void GekkoIRPlugin::EvalTerminalAbs(Terminal type, const AssemblerToken& tok)
case Terminal::Bin: case Terminal::Bin:
case Terminal::GPR: case Terminal::GPR:
case Terminal::FPR: case Terminal::FPR:
case Terminal::GQR:
case Terminal::SPR: case Terminal::SPR:
case Terminal::CRField: case Terminal::CRField:
case Terminal::Lt: case Terminal::Lt:

View file

@ -187,12 +187,14 @@ std::optional<T> EvalIntegral(TokenType tp, std::string_view val)
if (CaseInsensitiveEquals(val, "rtoc")) if (CaseInsensitiveEquals(val, "rtoc"))
return T{2}; return T{2};
[[fallthrough]]; [[fallthrough]];
case TokenType::FPR: case TokenType::FPR: // BE CAREFUL WHAT YOU PUT IN BETWEEN fallthrough and FPR
return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step); return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step);
case TokenType::CRField: case TokenType::CRField:
return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step); return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step);
case TokenType::SPR: case TokenType::SPR:
return static_cast<T>(*sprg_map.Find(val)); return static_cast<T>(*sprg_map.Find(val));
case TokenType::GQR:
return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step);
case TokenType::Lt: case TokenType::Lt:
return T{0}; return T{0};
case TokenType::Gt: case TokenType::Gt:
@ -220,6 +222,8 @@ std::string_view TokenTypeToStr(TokenType tp)
return "GPR"; return "GPR";
case TokenType::FPR: case TokenType::FPR:
return "FPR"; return "FPR";
case TokenType::GQR:
return "GQR";
case TokenType::SPR: case TokenType::SPR:
return "SPR"; return "SPR";
case TokenType::CRField: case TokenType::CRField:
@ -668,6 +672,15 @@ TokenType Lexer::ClassifyAlnum() const
{ {
return TokenType::FPR; return TokenType::FPR;
} }
else if (std::tolower(alnum[0]) == 'p' && valid_regnum(alnum.substr(1)))
{
return TokenType::FPR;
}
else if (alnum.length() == 3 && CaseInsensitiveEquals(alnum.substr(0, 2), "qr") &&
alnum[2] >= '0' && alnum[2] <= '7')
{
return TokenType::GQR;
}
else if (alnum.length() == 3 && CaseInsensitiveEquals(alnum.substr(0, 2), "cr") && else if (alnum.length() == 3 && CaseInsensitiveEquals(alnum.substr(0, 2), "cr") &&
alnum[2] >= '0' && alnum[2] <= '7') alnum[2] >= '0' && alnum[2] <= '7')
{ {

View file

@ -31,6 +31,7 @@ enum class TokenType
FloatLit, FloatLit,
GPR, GPR,
FPR, FPR,
GQR,
CRField, CRField,
SPR, SPR,
Lt, Lt,

View file

@ -127,6 +127,9 @@ void ParsePpcBuiltin(ParseState* state)
case TokenType::FPR: case TokenType::FPR:
state->plugin.OnTerminal(Terminal::FPR, tok); state->plugin.OnTerminal(Terminal::FPR, tok);
break; break;
case TokenType::GQR:
state->plugin.OnTerminal(Terminal::GQR, tok);
break;
case TokenType::SPR: case TokenType::SPR:
state->plugin.OnTerminal(Terminal::SPR, tok); state->plugin.OnTerminal(Terminal::SPR, tok);
break; break;
@ -176,6 +179,7 @@ void ParseBaseexpr(ParseState* state)
case TokenType::GPR: case TokenType::GPR:
case TokenType::FPR: case TokenType::FPR:
case TokenType::SPR: case TokenType::SPR:
case TokenType::GQR:
case TokenType::CRField: case TokenType::CRField:
case TokenType::Lt: case TokenType::Lt:
case TokenType::Gt: case TokenType::Gt:

View file

@ -56,6 +56,7 @@ enum class Terminal
Id, Id,
GPR, GPR,
FPR, FPR,
GQR,
SPR, SPR,
CRField, CRField,
Lt, Lt,

View file

@ -449,7 +449,7 @@ void GekkoDisassembler::trapi(u32 in, unsigned char dmode)
if (cnd != nullptr) if (cnd != nullptr)
{ {
m_opcode = fmt::format("t{}{}", dmode ? 'd' : 'w', cnd); m_opcode = fmt::format("t{}{}i", dmode ? 'd' : 'w', cnd);
} }
else else
{ {
@ -512,8 +512,8 @@ size_t GekkoDisassembler::branch(u32 in, std::string_view bname, int aform, int
char y = (char)(bo & 1); char y = (char)(bo & 1);
const char* ext = b_ext[aform * 2 + (int)(in & 1)]; const char* ext = b_ext[aform * 2 + (int)(in & 1)];
if (bdisp < 0) /* if (bdisp < 0)
y ^= 1; y ^= 1; */
y = (y != 0) ? '+' : '-'; y = (y != 0) ? '+' : '-';
if (bo & 4) if (bo & 4)
@ -631,7 +631,7 @@ void GekkoDisassembler::nooper(u32 in, std::string_view name)
} }
} }
void GekkoDisassembler::rlw(u32 in, std::string_view name, int i) void GekkoDisassembler::rlw(u32 in, std::string_view name, int i, bool for_assemble)
{ {
int s = (int)PPCGETD(in); int s = (int)PPCGETD(in);
int a = (int)PPCGETA(in); int a = (int)PPCGETA(in);
@ -640,9 +640,17 @@ void GekkoDisassembler::rlw(u32 in, std::string_view name, int i)
int me = (int)PPCGETM(in); int me = (int)PPCGETM(in);
m_opcode = fmt::format("rlw{}{}", name, (in & 1) ? "." : ""); m_opcode = fmt::format("rlw{}{}", name, (in & 1) ? "." : "");
if (!for_assemble)
{
m_operands = fmt::format("{}, {}, {}{}, {}, {} ({:08x})", regnames[a], regnames[s], regsel[i], m_operands = fmt::format("{}, {}, {}{}, {}, {} ({:08x})", regnames[a], regnames[s], regsel[i],
bsh, mb, me, HelperRotateMask(bsh, mb, me)); bsh, mb, me, HelperRotateMask(bsh, mb, me));
} }
else
{
m_operands =
fmt::format("{}, {}, {}{}, {}, {}", regnames[a], regnames[s], regsel[i], bsh, mb, me);
}
}
void GekkoDisassembler::ori(u32 in, std::string_view name) void GekkoDisassembler::ori(u32 in, std::string_view name)
{ {
@ -1019,7 +1027,7 @@ void GekkoDisassembler::mtfsb(u32 in, int n)
#define IX ((inst >> 7) & 0x7) #define IX ((inst >> 7) & 0x7)
#define WX ((inst >> 10) & 0x1) #define WX ((inst >> 10) & 0x1)
void GekkoDisassembler::ps(u32 inst) void GekkoDisassembler::ps(u32 inst, bool for_assemble)
{ {
switch ((inst >> 1) & 0x1F) switch ((inst >> 1) & 0x1F)
{ {
@ -1034,88 +1042,200 @@ void GekkoDisassembler::ps(u32 inst)
return; return;
case 18: case 18:
m_opcode = "ps_div"; m_opcode = fmt::format("ps_div{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}/p{}", FD, FA, FB); m_operands = fmt::format("p{}, p{}/p{}", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 20: case 20:
m_opcode = "ps_sub"; m_opcode = fmt::format("ps_sub{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}-p{}", FD, FA, FB); m_operands = fmt::format("p{}, p{}-p{}", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 21: case 21:
m_opcode = "ps_add"; m_opcode = fmt::format("ps_add{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}+p{}", FD, FA, FB); m_operands = fmt::format("p{}, p{}+p{}", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 23: case 23:
m_opcode = "ps_sel"; m_opcode = fmt::format("ps_sel{}", rcsel[inst & 1]);
m_operands = fmt::format("p{}>=0?p{}:p{}", FD, FA, FC); if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}>=0?p{}:p{}", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 24: case 24:
m_opcode = "ps_res"; m_opcode = fmt::format("ps_res{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, (1/p{})", FD, FB); m_operands = fmt::format("p{}, (1/p{})", FD, FB);
}
else
{
m_operands = fmt::format("p{}, p{}", FD, FB);
}
return; return;
case 25: case 25:
m_opcode = "ps_mul"; m_opcode = fmt::format("ps_mul{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}", FD, FA, FC); m_operands = fmt::format("p{}, p{}*p{}", FD, FA, FC);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
}
return; return;
case 26: // rsqrte case 26: // rsqrte
m_opcode = "ps_rsqrte"; m_opcode = fmt::format("ps_rsqrte{}", rcsel[inst & 1]);
m_operands = fmt::format("p{}, p{}", FD, FB); m_operands = fmt::format("p{}, p{}", FD, FB);
return; return;
case 28: // msub case 28: // msub
m_opcode = "ps_msub"; m_opcode = fmt::format("ps_msub{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}-p{}", FD, FA, FC, FB); m_operands = fmt::format("p{}, p{}*p{}-p{}", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 29: // madd case 29: // madd
m_opcode = "ps_madd"; m_opcode = fmt::format("ps_madd{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}+p{}", FD, FA, FC, FB); m_operands = fmt::format("p{}, p{}*p{}+p{}", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 30: // nmsub case 30: // nmsub
m_opcode = "ps_nmsub"; m_opcode = fmt::format("ps_nmsub{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, -(p{}*p{}-p{})", FD, FA, FC, FB); m_operands = fmt::format("p{}, -(p{}*p{}-p{})", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 31: // nmadd case 31: // nmadd
m_opcode = "ps_nmadd"; m_opcode = fmt::format("ps_nmadd{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, -(p{}*p{}+p{})", FD, FA, FC, FB); m_operands = fmt::format("p{}, -(p{}*p{}+p{})", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 10: case 10:
m_opcode = "ps_sum0"; m_opcode = fmt::format("ps_sum0{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, 0=p{}+p{}, 1=p{}", FD, FA, FB, FC); m_operands = fmt::format("p{}, 0=p{}+p{}, 1=p{}", FD, FA, FB, FC);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 11: case 11:
m_opcode = "ps_sum1"; m_opcode = fmt::format("ps_sum1{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, 0=p{}, 1=p{}+p{}", FD, FC, FA, FB); m_operands = fmt::format("p{}, 0=p{}, 1=p{}+p{}", FD, FC, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 12: case 12:
m_opcode = "ps_muls0"; m_opcode = fmt::format("ps_muls0{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}[0]", FD, FA, FC); m_operands = fmt::format("p{}, p{}*p{}[0]", FD, FA, FC);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
}
return; return;
case 13: case 13:
m_opcode = "ps_muls1"; m_opcode = fmt::format("ps_muls1{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}[1]", FD, FA, FC); m_operands = fmt::format("p{}, p{}*p{}[1]", FD, FA, FC);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
}
return; return;
case 14: case 14:
m_opcode = "ps_madds0"; m_opcode = fmt::format("ps_madds0{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}[0]+p{}", FD, FA, FC, FB); m_operands = fmt::format("p{}, p{}*p{}[0]+p{}", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
case 15: case 15:
m_opcode = "ps_madds1"; m_opcode = fmt::format("ps_madds1{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}*p{}[1]+p{}", FD, FA, FC, FB); m_operands = fmt::format("p{}, p{}*p{}[1]+p{}", FD, FA, FC, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
}
return; return;
} }
@ -1123,23 +1243,44 @@ void GekkoDisassembler::ps(u32 inst)
{ {
// 10-bit suckers (?) // 10-bit suckers (?)
case 40: // nmadd case 40: // nmadd
m_opcode = "ps_neg"; m_opcode = fmt::format("ps_neg{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, -p{}", FD, FB); m_operands = fmt::format("p{}, -p{}", FD, FB);
}
else
{
m_operands = fmt::format("p{}, p{}", FD, FB);
}
return; return;
case 72: // nmadd case 72: // nmadd
m_opcode = "ps_mr"; m_opcode = fmt::format("ps_mr{}", rcsel[inst & 1]);
m_operands = fmt::format("p{}, p{}", FD, FB); m_operands = fmt::format("p{}, p{}", FD, FB);
return; return;
case 136: case 136:
m_opcode = "ps_nabs"; m_opcode = fmt::format("ps_nabs{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, -|p{}|", FD, FB); m_operands = fmt::format("p{}, -|p{}|", FD, FB);
}
else
{
m_operands = fmt::format("p{}, p{}", FD, FB);
}
return; return;
case 264: case 264:
m_opcode = "ps_abs"; m_opcode = fmt::format("ps_abs{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, |p{}|", FD, FB); m_operands = fmt::format("p{}, |p{}|", FD, FB);
}
else
{
m_operands = fmt::format("p{}, p{}", FD, FB);
}
return; return;
case 0: case 0:
@ -1156,23 +1297,51 @@ void GekkoDisassembler::ps(u32 inst)
return; return;
} }
case 528: case 528:
m_opcode = "ps_merge00"; m_opcode = fmt::format("ps_merge00{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}[0], p{}[0]", FD, FA, FB); m_operands = fmt::format("p{}, p{}[0], p{}[0]", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 560: case 560:
m_opcode = "ps_merge01"; m_opcode = fmt::format("ps_merge01{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}[0], p{}[1]", FD, FA, FB); m_operands = fmt::format("p{}, p{}[0], p{}[1]", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 592: case 592:
m_opcode = "ps_merge10"; m_opcode = fmt::format("ps_merge10{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}[1], p{}[0]", FD, FA, FB); m_operands = fmt::format("p{}, p{}[1], p{}[0]", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 624: case 624:
m_opcode = "ps_merge11"; m_opcode = fmt::format("ps_merge11{}", rcsel[inst & 1]);
if (!for_assemble)
{
m_operands = fmt::format("p{}, p{}[1], p{}[1]", FD, FA, FB); m_operands = fmt::format("p{}, p{}[1], p{}[1]", FD, FA, FB);
}
else
{
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
}
return; return;
case 1014: case 1014:
@ -1217,7 +1386,7 @@ void GekkoDisassembler::ps_mem(u32 inst)
// Disassemble PPC instruction and return a pointer to the next // Disassemble PPC instruction and return a pointer to the next
// instruction, or nullptr if an error occurred. // instruction, or nullptr if an error occurred.
u32* GekkoDisassembler::DoDisassembly(bool big_endian) u32* GekkoDisassembler::DoDisassembly(bool for_assemble, bool big_endian)
{ {
u32 in = *m_instr; u32 in = *m_instr;
@ -1240,7 +1409,7 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
break; break;
case 4: case 4:
ps(in); ps(in, for_assemble);
break; break;
case 56: case 56:
@ -1361,15 +1530,15 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
break; break;
case 20: case 20:
rlw(in, "imi", 0); // rlwimi rlw(in, "imi", 0, for_assemble); // rlwimi
break; break;
case 21: case 21:
rlw(in, "inm", 0); // rlwinm rlw(in, "inm", 0, for_assemble); // rlwinm
break; break;
case 23: case 23:
rlw(in, "nm", 1); // rlwnm rlw(in, "nm", 1, for_assemble); // rlwnm
break; break;
case 24: case 24:
@ -2275,7 +2444,7 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
// simplified interface // simplified interface
std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_address, std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_address,
bool big_endian) bool for_assemble, bool big_endian)
{ {
u32 opc = opcode; u32 opc = opcode;
u32 addr = current_instruction_address; u32 addr = current_instruction_address;
@ -2283,7 +2452,7 @@ std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_a
m_instr = (u32*)&opc; m_instr = (u32*)&opc;
m_iaddr = (u32*)&addr; m_iaddr = (u32*)&addr;
DoDisassembly(big_endian); DoDisassembly(for_assemble, big_endian);
return m_opcode.append("\t").append(m_operands); return m_opcode.append("\t").append(m_operands);
} }

View file

@ -43,7 +43,7 @@ namespace Common
class GekkoDisassembler final class GekkoDisassembler final
{ {
public: public:
static std::string Disassemble(u32 opcode, u32 current_instruction_address, static std::string Disassemble(u32 opcode, u32 current_instruction_address, bool for_assemble,
bool big_endian = true); bool big_endian = true);
static const char* GetGPRName(u32 index); static const char* GetGPRName(u32 index);
static const char* GetFPRName(u32 index); static const char* GetFPRName(u32 index);
@ -67,7 +67,7 @@ private:
static void mcrf(u32 in, std::string_view suffix); static void mcrf(u32 in, std::string_view suffix);
static void crop(u32 in, std::string_view n1, std::string_view n2); static void crop(u32 in, std::string_view n1, std::string_view n2);
static void nooper(u32 in, std::string_view name); static void nooper(u32 in, std::string_view name);
static void rlw(u32 in, std::string_view name, int i); static void rlw(u32 in, std::string_view name, int i, bool for_assemble);
static void ori(u32 in, std::string_view name); static void ori(u32 in, std::string_view name);
static void rld(u32 in, std::string_view name, int i); static void rld(u32 in, std::string_view name, int i);
static void cmp(u32 in); static void cmp(u32 in);
@ -85,10 +85,10 @@ private:
static void fdab(u32 in, std::string_view name); static void fdab(u32 in, std::string_view name);
static void fcmp(u32 in, char c); static void fcmp(u32 in, char c);
static void mtfsb(u32 in, int n); static void mtfsb(u32 in, int n);
static void ps(u32 inst); static void ps(u32 inst, bool for_assemble);
static void ps_mem(u32 inst); static void ps_mem(u32 inst);
static u32* DoDisassembly(bool big_endian); static u32* DoDisassembly(bool for_assemble, bool big_endian);
enum Flags enum Flags
{ {

View file

@ -62,7 +62,8 @@ public:
// Threads // Threads
virtual Common::Debug::Threads GetThreads(const CPUThreadGuard& guard) const = 0; virtual Common::Debug::Threads GetThreads(const CPUThreadGuard& guard) const = 0;
virtual std::string Disassemble(const CPUThreadGuard* /*guard*/, u32 /*address*/) const virtual std::string Disassemble(const CPUThreadGuard* /*guard*/, u32 /*address*/,
bool /*for_assemble*/) const
{ {
return "NODEBUGGER"; return "NODEBUGGER";
} }

View file

@ -276,7 +276,8 @@ Common::Debug::Threads PPCDebugInterface::GetThreads(const Core::CPUThreadGuard&
return threads; return threads;
} }
std::string PPCDebugInterface::Disassemble(const Core::CPUThreadGuard* guard, u32 address) const std::string PPCDebugInterface::Disassemble(const Core::CPUThreadGuard* guard, u32 address,
bool for_assemble) const
{ {
if (guard) if (guard)
{ {
@ -286,7 +287,7 @@ std::string PPCDebugInterface::Disassemble(const Core::CPUThreadGuard* guard, u3
} }
const u32 op = PowerPC::MMU::HostRead_Instruction(*guard, address); const u32 op = PowerPC::MMU::HostRead_Instruction(*guard, address);
std::string disasm = Common::GekkoDisassembler::Disassemble(op, address); std::string disasm = Common::GekkoDisassembler::Disassemble(op, address, for_assemble);
const UGeckoInstruction inst{op}; const UGeckoInstruction inst{op};
if (inst.OPCD == 1) if (inst.OPCD == 1)

View file

@ -75,7 +75,8 @@ public:
// Threads // Threads
Common::Debug::Threads GetThreads(const Core::CPUThreadGuard& guard) const override; Common::Debug::Threads GetThreads(const Core::CPUThreadGuard& guard) const override;
std::string Disassemble(const Core::CPUThreadGuard* guard, u32 address) const override; std::string Disassemble(const Core::CPUThreadGuard* guard, u32 address,
bool for_assemble = false) const override;
std::string GetRawMemoryString(const Core::CPUThreadGuard& guard, int memory, std::string GetRawMemoryString(const Core::CPUThreadGuard& guard, int memory,
u32 address) const override; u32 address) const override;
bool IsAlive() const override; bool IsAlive() const override;

View file

@ -454,7 +454,7 @@ void CachedInterpreter::LogGeneratedCode() const
std::span{m_code_buffer.data(), code_block.m_num_instructions}) std::span{m_code_buffer.data(), code_block.m_num_instructions})
{ {
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address, fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address)); Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address, false));
} }
stream << "\nHost Code:\n"; stream << "\nHost Code:\n";

View file

@ -97,7 +97,8 @@ void Interpreter::Trace(const UGeckoInstruction& inst)
fregs += fmt::format("f{:02d}: {:08x} {:08x} ", i, ps.PS0AsU64(), ps.PS1AsU64()); fregs += fmt::format("f{:02d}: {:08x} {:08x} ", i, ps.PS0AsU64(), ps.PS1AsU64());
} }
const std::string ppc_inst = Common::GekkoDisassembler::Disassemble(inst.hex, m_ppc_state.pc); const std::string ppc_inst =
Common::GekkoDisassembler::Disassemble(inst.hex, m_ppc_state.pc, false);
DEBUG_LOG_FMT(POWERPC, DEBUG_LOG_FMT(POWERPC,
"INTER PC: {:08x} SRR0: {:08x} SRR1: {:08x} CRval: {:016x} " "INTER PC: {:08x} SRR0: {:08x} SRR1: {:08x} CRval: {:016x} "
"FPSCR: {:08x} MSR: {:08x} LR: {:08x} {} {:08x} {}", "FPSCR: {:08x} MSR: {:08x} LR: {:08x} {} {:08x} {}",
@ -294,7 +295,7 @@ void Interpreter::unknown_instruction(Interpreter& interpreter, UGeckoInstructio
const u32 last_pc = interpreter.m_last_pc; const u32 last_pc = interpreter.m_last_pc;
const u32 opcode = PowerPC::MMU::HostRead_U32(guard, last_pc); const u32 opcode = PowerPC::MMU::HostRead_U32(guard, last_pc);
const std::string disasm = Common::GekkoDisassembler::Disassemble(opcode, last_pc); const std::string disasm = Common::GekkoDisassembler::Disassemble(opcode, last_pc, false);
NOTICE_LOG_FMT(POWERPC, "Last PC = {:08x} : {}", last_pc, disasm); NOTICE_LOG_FMT(POWERPC, "Last PC = {:08x} : {}", last_pc, disasm);
Dolphin_Debugger::PrintCallstack(guard, Common::Log::LogType::POWERPC, Dolphin_Debugger::PrintCallstack(guard, Common::Log::LogType::POWERPC,
Common::Log::LogLevel::LNOTICE); Common::Log::LogLevel::LNOTICE);

View file

@ -1177,7 +1177,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
#if defined(_DEBUG) || defined(DEBUGFAST) #if defined(_DEBUG) || defined(DEBUGFAST)
if (!gpr.SanityCheck() || !fpr.SanityCheck()) if (!gpr.SanityCheck() || !fpr.SanityCheck())
{ {
const std::string ppc_inst = Common::GekkoDisassembler::Disassemble(op.inst.hex, em_address); const std::string ppc_inst =
Common::GekkoDisassembler::Disassemble(op.inst.hex, em_address, false);
NOTICE_LOG_FMT(DYNA_REC, "Unflushed register: {}", ppc_inst); NOTICE_LOG_FMT(DYNA_REC, "Unflushed register: {}", ppc_inst);
} }
#endif #endif
@ -1320,7 +1321,7 @@ void Jit64::LogGeneratedCode() const
std::span{m_code_buffer.data(), code_block.m_num_instructions}) std::span{m_code_buffer.data(), code_block.m_num_instructions})
{ {
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address, fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address)); Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address, false));
} }
const JitBlock* const block = js.curBlock; const JitBlock* const block = js.curBlock;

View file

@ -1401,7 +1401,7 @@ void JitArm64::LogGeneratedCode() const
std::span{m_code_buffer.data(), code_block.m_num_instructions}) std::span{m_code_buffer.data(), code_block.m_num_instructions})
{ {
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address, fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address)); Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address, false));
} }
const JitBlock* const block = js.curBlock; const JitBlock* const block = js.curBlock;

View file

@ -38,8 +38,9 @@ QString HtmlFormatErrorLine(const Common::GekkoAssembler::AssemblerError& err)
} }
} // namespace } // namespace
AssembleInstructionDialog::AssembleInstructionDialog(QWidget* parent, u32 address, u32 value) AssembleInstructionDialog::AssembleInstructionDialog(QWidget* parent, u32 address, u32 value,
: QDialog(parent), m_code(value), m_address(address) QString disasm)
: QDialog(parent), m_code(value), m_address(address), m_disassembly(disasm)
{ {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowModality(Qt::WindowModal); setWindowModality(Qt::WindowModal);
@ -66,7 +67,7 @@ void AssembleInstructionDialog::CreateWidgets()
layout->addWidget(m_error_line_label); layout->addWidget(m_error_line_label);
layout->addWidget(m_msg_label); layout->addWidget(m_msg_label);
layout->addWidget(m_button_box); layout->addWidget(m_button_box);
m_input_edit->setText(QStringLiteral(".4byte 0x%1").arg(m_code, 8, 16, QLatin1Char('0'))); m_input_edit->setText(m_disassembly);
setLayout(layout); setLayout(layout);
OnEditChanged(); OnEditChanged();
@ -86,6 +87,31 @@ void AssembleInstructionDialog::OnEditChanged()
std::string line = m_input_edit->text().toStdString(); std::string line = m_input_edit->text().toStdString();
Common::ToLower(&line); Common::ToLower(&line);
size_t posArrow = line.find("->");
if (posArrow != std::string::npos)
{
std::string start = line.substr(0, posArrow);
std::string dest = line.substr(posArrow + 2);
u32 destAddress = 0;
size_t posHex = dest.find("0x");
if (posHex == std::string::npos)
{
destAddress = (u32)strtoul(dest.c_str(), nullptr, 10);
}
else
{
destAddress = (u32)strtoul(dest.substr(posHex + 2).c_str(), nullptr, 16);
}
if (destAddress < m_address)
{
line = start + " -" + std::to_string(m_address - destAddress);
}
else
{
line = start + " " + std::to_string(destAddress - m_address);
}
}
FailureOr<std::vector<CodeBlock>> asm_result = Assemble(line, m_address); FailureOr<std::vector<CodeBlock>> asm_result = Assemble(line, m_address);
if (IsFailure(asm_result)) if (IsFailure(asm_result))

View file

@ -15,7 +15,7 @@ class AssembleInstructionDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit AssembleInstructionDialog(QWidget* parent, u32 address, u32 value); explicit AssembleInstructionDialog(QWidget* parent, u32 address, u32 value, QString disasm);
u32 GetCode() const; u32 GetCode() const;
@ -28,6 +28,7 @@ private:
u32 m_code; u32 m_code;
u32 m_address; u32 m_address;
QString m_disassembly;
QLineEdit* m_input_edit; QLineEdit* m_input_edit;
QLabel* m_error_loc_label; QLabel* m_error_loc_label;
QLabel* m_error_line_label; QLabel* m_error_line_label;

View file

@ -306,7 +306,7 @@ static QVariant GetValidSymbolStringVariant(const QVariant& symbol_name_v)
static QString GetInstructionMnemonic(u32 hex) static QString GetInstructionMnemonic(u32 hex)
{ {
const std::string disas = Common::GekkoDisassembler::Disassemble(hex, 0); const std::string disas = Common::GekkoDisassembler::Disassemble(hex, 0, false);
const std::string::size_type split = disas.find('\t'); const std::string::size_type split = disas.find('\t');
// I wish I could disassemble just the mnemonic! // I wish I could disassemble just the mnemonic!
if (split == std::string::npos) if (split == std::string::npos)

View file

@ -228,7 +228,7 @@ void CodeViewWidget::FontBasedSizing()
// Similarly, the longest parameter set is 'rtoc, rtoc, r10, 10, 10 (00000800)' (0x5c425294u), // Similarly, the longest parameter set is 'rtoc, rtoc, r10, 10, 10 (00000800)' (0x5c425294u),
// but one is unlikely to encounter that in practice, so let's use a slightly more reasonable // but one is unlikely to encounter that in practice, so let's use a slightly more reasonable
// 'r31, r31, 16, 16, 31 (ffff0000)'. The user can resize the columns as necessary anyway. // 'r31, r31, 16, 16, 31 (ffff0000)'. The user can resize the columns as necessary anyway.
const std::string disas = Common::GekkoDisassembler::Disassemble(0x57ff843fu, 0); const std::string disas = Common::GekkoDisassembler::Disassemble(0x57ff843fu, 0, false);
const auto split = disas.find('\t'); const auto split = disas.find('\t');
const std::string ins = (split == std::string::npos ? disas : disas.substr(0, split)); const std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
const std::string param = (split == std::string::npos ? "" : disas.substr(split + 1)); const std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
@ -1030,7 +1030,12 @@ void CodeViewWidget::DoPatchInstruction(bool assemble)
if (assemble) if (assemble)
{ {
AssembleInstructionDialog dialog(this, addr, debug_interface.ReadInstruction(guard, addr)); std::string code_line =
m_system.GetPowerPC().GetDebugInterface().Disassemble(&guard, addr, true);
std::ranges::replace(code_line, '\t', ' ');
AssembleInstructionDialog dialog(this, addr, debug_interface.ReadInstruction(guard, addr),
QString::fromStdString(code_line));
SetQWidgetWindowDecorations(&dialog); SetQWidgetWindowDecorations(&dialog);
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted)
{ {

View file

@ -55,6 +55,10 @@ public:
HighlightCurToken(HighlightFormat::FPR); HighlightCurToken(HighlightFormat::FPR);
break; break;
case Terminal::GQR:
HighlightCurToken(HighlightFormat::GQR);
break;
case Terminal::SPR: case Terminal::SPR:
HighlightCurToken(HighlightFormat::SPR); HighlightCurToken(HighlightFormat::SPR);
break; break;
@ -221,6 +225,7 @@ void GekkoSyntaxHighlight::HighlightSubstr(int start, int len, HighlightFormat f
break; break;
case HighlightFormat::GPR: case HighlightFormat::GPR:
case HighlightFormat::FPR: case HighlightFormat::FPR:
case HighlightFormat::GQR:
case HighlightFormat::SPR: case HighlightFormat::SPR:
case HighlightFormat::CRField: case HighlightFormat::CRField:
case HighlightFormat::CRFlag: case HighlightFormat::CRFlag:

View file

@ -18,6 +18,7 @@ enum class HighlightFormat
Immediate, Immediate,
GPR, GPR,
FPR, FPR,
GQR,
SPR, SPR,
CRField, CRField,
CRFlag, CRFlag,

View file

@ -213,7 +213,7 @@ static void DisassembleCodeBuffer(const JitBlock& block, PPCSymbolDB& ppc_symbol
next_address = address; next_address = address;
} }
fmt::print(stream, "0x{:08x}\t{}\n", address, fmt::print(stream, "0x{:08x}\t{}\n", address,
Common::GekkoDisassembler::Disassemble(inst.hex, address)); Common::GekkoDisassembler::Disassemble(inst.hex, address, false));
next_address += sizeof(UGeckoInstruction); next_address += sizeof(UGeckoInstruction);
} }
} }

View file

@ -55,9 +55,9 @@ void PatchInstructionDialog::OnEditChanged()
m_button_box->button(QDialogButtonBox::Ok)->setEnabled(good); m_button_box->button(QDialogButtonBox::Ok)->setEnabled(good);
m_preview_label->setText( m_preview_label->setText(tr("Instruction: %1")
tr("Instruction: %1") .arg(QString::fromStdString(Common::GekkoDisassembler::Disassemble(
.arg(QString::fromStdString(Common::GekkoDisassembler::Disassemble(m_code, m_address)))); m_code, m_address, false))));
} }
u32 PatchInstructionDialog::GetCode() const u32 PatchInstructionDialog::GetCode() const

View file

@ -6,7 +6,9 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <fmt/format.h>
#include "Common/Assembler/GekkoAssembler.h" #include "Common/Assembler/GekkoAssembler.h"
#include "Common/GekkoDisassembler.h"
using namespace Common::GekkoAssembler; using namespace Common::GekkoAssembler;
@ -456,6 +458,45 @@ TEST(Assembler, AllInstructions)
} }
} }
TEST(Assembler, RoundTripTest)
{
auto res = Assemble(instructions, 0);
ASSERT_TRUE(!IsFailure(res));
auto&& code_blocks = GetT(res);
ASSERT_EQ(code_blocks.size(), 1);
ASSERT_EQ(code_blocks[0].instructions.size(), sizeof(expected_instructions));
ASSERT_EQ(code_blocks[0].instructions.size() & 3, 0);
for (size_t i = 0; i < code_blocks[0].instructions.size(); i += sizeof(u32))
{
const u32 hex_code =
(code_blocks[0].instructions[i] << 24) | (code_blocks[0].instructions[i + 1] << 16) |
(code_blocks[0].instructions[i + 2] << 8) | code_blocks[0].instructions[i + 3];
std::string text_code = Common::GekkoDisassembler::Disassemble(hex_code, 0, true);
// NEED TO FIX ABSOLUTE ADDRESSES COMING IN FROM DISASSEMBLER
size_t posArrow = text_code.find("->");
if (posArrow != std::string::npos)
{
u32 address = (u32)strtoul(text_code.substr(posArrow + 2).c_str(), nullptr, 16) & 0xFFFF;
if (address & 0x8000)
{
text_code = fmt::format("{} -0x{:04X}", text_code.substr(0, posArrow), 0x10000 - address);
}
else
{
text_code = fmt::format("{} 0x{:04X}", text_code.substr(0, posArrow), address);
}
}
auto recompiled = Assemble(text_code, 0);
auto&& code_blocks_reassembled = GetT(recompiled);
for (size_t j = 0; j < sizeof(u32); j++)
{
EXPECT_EQ(code_blocks[0].instructions[i + j], code_blocks_reassembled[0].instructions[j])
<< " -> i=" << i;
}
}
}
constexpr char extended_instructions[] = "subi 0, 4, 8\n" constexpr char extended_instructions[] = "subi 0, 4, 8\n"
"subis 0, 4, 8\n" "subis 0, 4, 8\n"
"subic 0, 4, 8\n" "subic 0, 4, 8\n"