Merge bcefc8ba70
into 1ae0b23265
This commit is contained in:
commit
ed62d79af8
|
@ -681,6 +681,7 @@ void GekkoIRPlugin::EvalTerminalRel(Terminal type, const AssemblerToken& tok)
|
|||
case Terminal::Bin:
|
||||
case Terminal::GPR:
|
||||
case Terminal::FPR:
|
||||
case Terminal::GQR:
|
||||
case Terminal::SPR:
|
||||
case Terminal::CRField:
|
||||
case Terminal::Lt:
|
||||
|
@ -732,6 +733,7 @@ void GekkoIRPlugin::EvalTerminalAbs(Terminal type, const AssemblerToken& tok)
|
|||
case Terminal::Bin:
|
||||
case Terminal::GPR:
|
||||
case Terminal::FPR:
|
||||
case Terminal::GQR:
|
||||
case Terminal::SPR:
|
||||
case Terminal::CRField:
|
||||
case Terminal::Lt:
|
||||
|
|
|
@ -187,12 +187,14 @@ std::optional<T> EvalIntegral(TokenType tp, std::string_view val)
|
|||
if (CaseInsensitiveEquals(val, "rtoc"))
|
||||
return T{2};
|
||||
[[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);
|
||||
case TokenType::CRField:
|
||||
return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step);
|
||||
case TokenType::SPR:
|
||||
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:
|
||||
return T{0};
|
||||
case TokenType::Gt:
|
||||
|
@ -220,6 +222,8 @@ std::string_view TokenTypeToStr(TokenType tp)
|
|||
return "GPR";
|
||||
case TokenType::FPR:
|
||||
return "FPR";
|
||||
case TokenType::GQR:
|
||||
return "GQR";
|
||||
case TokenType::SPR:
|
||||
return "SPR";
|
||||
case TokenType::CRField:
|
||||
|
@ -668,6 +672,15 @@ TokenType Lexer::ClassifyAlnum() const
|
|||
{
|
||||
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") &&
|
||||
alnum[2] >= '0' && alnum[2] <= '7')
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ enum class TokenType
|
|||
FloatLit,
|
||||
GPR,
|
||||
FPR,
|
||||
GQR,
|
||||
CRField,
|
||||
SPR,
|
||||
Lt,
|
||||
|
|
|
@ -127,6 +127,9 @@ void ParsePpcBuiltin(ParseState* state)
|
|||
case TokenType::FPR:
|
||||
state->plugin.OnTerminal(Terminal::FPR, tok);
|
||||
break;
|
||||
case TokenType::GQR:
|
||||
state->plugin.OnTerminal(Terminal::GQR, tok);
|
||||
break;
|
||||
case TokenType::SPR:
|
||||
state->plugin.OnTerminal(Terminal::SPR, tok);
|
||||
break;
|
||||
|
@ -176,6 +179,7 @@ void ParseBaseexpr(ParseState* state)
|
|||
case TokenType::GPR:
|
||||
case TokenType::FPR:
|
||||
case TokenType::SPR:
|
||||
case TokenType::GQR:
|
||||
case TokenType::CRField:
|
||||
case TokenType::Lt:
|
||||
case TokenType::Gt:
|
||||
|
|
|
@ -56,6 +56,7 @@ enum class Terminal
|
|||
Id,
|
||||
GPR,
|
||||
FPR,
|
||||
GQR,
|
||||
SPR,
|
||||
CRField,
|
||||
Lt,
|
||||
|
|
|
@ -449,7 +449,7 @@ void GekkoDisassembler::trapi(u32 in, unsigned char dmode)
|
|||
|
||||
if (cnd != nullptr)
|
||||
{
|
||||
m_opcode = fmt::format("t{}{}", dmode ? 'd' : 'w', cnd);
|
||||
m_opcode = fmt::format("t{}{}i", dmode ? 'd' : 'w', cnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -512,8 +512,8 @@ size_t GekkoDisassembler::branch(u32 in, std::string_view bname, int aform, int
|
|||
char y = (char)(bo & 1);
|
||||
const char* ext = b_ext[aform * 2 + (int)(in & 1)];
|
||||
|
||||
if (bdisp < 0)
|
||||
y ^= 1;
|
||||
/* if (bdisp < 0)
|
||||
y ^= 1; */
|
||||
y = (y != 0) ? '+' : '-';
|
||||
|
||||
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 a = (int)PPCGETA(in);
|
||||
|
@ -640,8 +640,16 @@ void GekkoDisassembler::rlw(u32 in, std::string_view name, int i)
|
|||
int me = (int)PPCGETM(in);
|
||||
|
||||
m_opcode = fmt::format("rlw{}{}", name, (in & 1) ? "." : "");
|
||||
m_operands = fmt::format("{}, {}, {}{}, {}, {} ({:08x})", regnames[a], regnames[s], regsel[i],
|
||||
bsh, mb, me, HelperRotateMask(bsh, mb, me));
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("{}, {}, {}{}, {}, {} ({:08x})", regnames[a], regnames[s], regsel[i],
|
||||
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)
|
||||
|
@ -1019,7 +1027,7 @@ void GekkoDisassembler::mtfsb(u32 in, int n)
|
|||
#define IX ((inst >> 7) & 0x7)
|
||||
#define WX ((inst >> 10) & 0x1)
|
||||
|
||||
void GekkoDisassembler::ps(u32 inst)
|
||||
void GekkoDisassembler::ps(u32 inst, bool for_assemble)
|
||||
{
|
||||
switch ((inst >> 1) & 0x1F)
|
||||
{
|
||||
|
@ -1034,88 +1042,200 @@ void GekkoDisassembler::ps(u32 inst)
|
|||
return;
|
||||
|
||||
case 18:
|
||||
m_opcode = "ps_div";
|
||||
m_operands = fmt::format("p{}, p{}/p{}", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_div{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}/p{}", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 20:
|
||||
m_opcode = "ps_sub";
|
||||
m_operands = fmt::format("p{}, p{}-p{}", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_sub{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}-p{}", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 21:
|
||||
m_opcode = "ps_add";
|
||||
m_operands = fmt::format("p{}, p{}+p{}", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_add{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}+p{}", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 23:
|
||||
m_opcode = "ps_sel";
|
||||
m_operands = fmt::format("p{}>=0?p{}:p{}", FD, FA, FC);
|
||||
m_opcode = fmt::format("ps_sel{}", rcsel[inst & 1]);
|
||||
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;
|
||||
|
||||
case 24:
|
||||
m_opcode = "ps_res";
|
||||
m_operands = fmt::format("p{}, (1/p{})", FD, FB);
|
||||
m_opcode = fmt::format("ps_res{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, (1/p{})", FD, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 25:
|
||||
m_opcode = "ps_mul";
|
||||
m_operands = fmt::format("p{}, p{}*p{}", FD, FA, FC);
|
||||
m_opcode = fmt::format("ps_mul{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}*p{}", FD, FA, FC);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
|
||||
}
|
||||
return;
|
||||
|
||||
case 26: // rsqrte
|
||||
m_opcode = "ps_rsqrte";
|
||||
m_opcode = fmt::format("ps_rsqrte{}", rcsel[inst & 1]);
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
return;
|
||||
|
||||
case 28: // msub
|
||||
m_opcode = "ps_msub";
|
||||
m_operands = fmt::format("p{}, p{}*p{}-p{}", FD, FA, FC, FB);
|
||||
m_opcode = fmt::format("ps_msub{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
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;
|
||||
|
||||
case 29: // madd
|
||||
m_opcode = "ps_madd";
|
||||
m_operands = fmt::format("p{}, p{}*p{}+p{}", FD, FA, FC, FB);
|
||||
m_opcode = fmt::format("ps_madd{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
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;
|
||||
|
||||
case 30: // nmsub
|
||||
m_opcode = "ps_nmsub";
|
||||
m_operands = fmt::format("p{}, -(p{}*p{}-p{})", FD, FA, FC, FB);
|
||||
m_opcode = fmt::format("ps_nmsub{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
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;
|
||||
|
||||
case 31: // nmadd
|
||||
m_opcode = "ps_nmadd";
|
||||
m_operands = fmt::format("p{}, -(p{}*p{}+p{})", FD, FA, FC, FB);
|
||||
m_opcode = fmt::format("ps_nmadd{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
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;
|
||||
|
||||
case 10:
|
||||
m_opcode = "ps_sum0";
|
||||
m_operands = fmt::format("p{}, 0=p{}+p{}, 1=p{}", FD, FA, FB, FC);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 11:
|
||||
m_opcode = "ps_sum1";
|
||||
m_operands = fmt::format("p{}, 0=p{}, 1=p{}+p{}", FD, FC, FA, FB);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 12:
|
||||
m_opcode = "ps_muls0";
|
||||
m_operands = fmt::format("p{}, p{}*p{}[0]", FD, FA, FC);
|
||||
m_opcode = fmt::format("ps_muls0{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}*p{}[0]", FD, FA, FC);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
|
||||
}
|
||||
return;
|
||||
|
||||
case 13:
|
||||
m_opcode = "ps_muls1";
|
||||
m_operands = fmt::format("p{}, p{}*p{}[1]", FD, FA, FC);
|
||||
m_opcode = fmt::format("ps_muls1{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}*p{}[1]", FD, FA, FC);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FC);
|
||||
}
|
||||
return;
|
||||
|
||||
case 14:
|
||||
m_opcode = "ps_madds0";
|
||||
m_operands = fmt::format("p{}, p{}*p{}[0]+p{}", FD, FA, FC, FB);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 15:
|
||||
m_opcode = "ps_madds1";
|
||||
m_operands = fmt::format("p{}, p{}*p{}[1]+p{}", FD, FA, FC, FB);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}, p{}", FD, FA, FC, FB);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1123,23 +1243,44 @@ void GekkoDisassembler::ps(u32 inst)
|
|||
{
|
||||
// 10-bit suckers (?)
|
||||
case 40: // nmadd
|
||||
m_opcode = "ps_neg";
|
||||
m_operands = fmt::format("p{}, -p{}", FD, FB);
|
||||
m_opcode = fmt::format("ps_neg{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, -p{}", FD, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 72: // nmadd
|
||||
m_opcode = "ps_mr";
|
||||
m_opcode = fmt::format("ps_mr{}", rcsel[inst & 1]);
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
return;
|
||||
|
||||
case 136:
|
||||
m_opcode = "ps_nabs";
|
||||
m_operands = fmt::format("p{}, -|p{}|", FD, FB);
|
||||
m_opcode = fmt::format("ps_nabs{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, -|p{}|", FD, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 264:
|
||||
m_opcode = "ps_abs";
|
||||
m_operands = fmt::format("p{}, |p{}|", FD, FB);
|
||||
m_opcode = fmt::format("ps_abs{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, |p{}|", FD, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}", FD, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 0:
|
||||
|
@ -1156,23 +1297,51 @@ void GekkoDisassembler::ps(u32 inst)
|
|||
return;
|
||||
}
|
||||
case 528:
|
||||
m_opcode = "ps_merge00";
|
||||
m_operands = fmt::format("p{}, p{}[0], p{}[0]", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_merge00{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}[0], p{}[0]", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 560:
|
||||
m_opcode = "ps_merge01";
|
||||
m_operands = fmt::format("p{}, p{}[0], p{}[1]", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_merge01{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}[0], p{}[1]", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 592:
|
||||
m_opcode = "ps_merge10";
|
||||
m_operands = fmt::format("p{}, p{}[1], p{}[0]", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_merge10{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}[1], p{}[0]", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 624:
|
||||
m_opcode = "ps_merge11";
|
||||
m_operands = fmt::format("p{}, p{}[1], p{}[1]", FD, FA, FB);
|
||||
m_opcode = fmt::format("ps_merge11{}", rcsel[inst & 1]);
|
||||
if (!for_assemble)
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}[1], p{}[1]", FD, FA, FB);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_operands = fmt::format("p{}, p{}, p{}", FD, FA, FB);
|
||||
}
|
||||
return;
|
||||
|
||||
case 1014:
|
||||
|
@ -1217,7 +1386,7 @@ void GekkoDisassembler::ps_mem(u32 inst)
|
|||
|
||||
// Disassemble PPC instruction and return a pointer to the next
|
||||
// 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;
|
||||
|
||||
|
@ -1240,7 +1409,7 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
|
|||
break;
|
||||
|
||||
case 4:
|
||||
ps(in);
|
||||
ps(in, for_assemble);
|
||||
break;
|
||||
|
||||
case 56:
|
||||
|
@ -1361,15 +1530,15 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
|
|||
break;
|
||||
|
||||
case 20:
|
||||
rlw(in, "imi", 0); // rlwimi
|
||||
rlw(in, "imi", 0, for_assemble); // rlwimi
|
||||
break;
|
||||
|
||||
case 21:
|
||||
rlw(in, "inm", 0); // rlwinm
|
||||
rlw(in, "inm", 0, for_assemble); // rlwinm
|
||||
break;
|
||||
|
||||
case 23:
|
||||
rlw(in, "nm", 1); // rlwnm
|
||||
rlw(in, "nm", 1, for_assemble); // rlwnm
|
||||
break;
|
||||
|
||||
case 24:
|
||||
|
@ -2275,7 +2444,7 @@ u32* GekkoDisassembler::DoDisassembly(bool big_endian)
|
|||
|
||||
// simplified interface
|
||||
std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_address,
|
||||
bool big_endian)
|
||||
bool for_assemble, bool big_endian)
|
||||
{
|
||||
u32 opc = opcode;
|
||||
u32 addr = current_instruction_address;
|
||||
|
@ -2283,7 +2452,7 @@ std::string GekkoDisassembler::Disassemble(u32 opcode, u32 current_instruction_a
|
|||
m_instr = (u32*)&opc;
|
||||
m_iaddr = (u32*)&addr;
|
||||
|
||||
DoDisassembly(big_endian);
|
||||
DoDisassembly(for_assemble, big_endian);
|
||||
|
||||
return m_opcode.append("\t").append(m_operands);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Common
|
|||
class GekkoDisassembler final
|
||||
{
|
||||
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);
|
||||
static const char* GetGPRName(u32 index);
|
||||
static const char* GetFPRName(u32 index);
|
||||
|
@ -67,7 +67,7 @@ private:
|
|||
static void mcrf(u32 in, std::string_view suffix);
|
||||
static void crop(u32 in, std::string_view n1, std::string_view n2);
|
||||
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 rld(u32 in, std::string_view name, int i);
|
||||
static void cmp(u32 in);
|
||||
|
@ -85,10 +85,10 @@ private:
|
|||
static void fdab(u32 in, std::string_view name);
|
||||
static void fcmp(u32 in, char c);
|
||||
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 u32* DoDisassembly(bool big_endian);
|
||||
static u32* DoDisassembly(bool for_assemble, bool big_endian);
|
||||
|
||||
enum Flags
|
||||
{
|
||||
|
|
|
@ -62,7 +62,8 @@ public:
|
|||
// Threads
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -276,7 +276,8 @@ Common::Debug::Threads PPCDebugInterface::GetThreads(const Core::CPUThreadGuard&
|
|||
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)
|
||||
{
|
||||
|
@ -286,7 +287,7 @@ std::string PPCDebugInterface::Disassemble(const Core::CPUThreadGuard* guard, u3
|
|||
}
|
||||
|
||||
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};
|
||||
|
||||
if (inst.OPCD == 1)
|
||||
|
|
|
@ -75,7 +75,8 @@ public:
|
|||
// Threads
|
||||
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,
|
||||
u32 address) const override;
|
||||
bool IsAlive() const override;
|
||||
|
|
|
@ -454,7 +454,7 @@ void CachedInterpreter::LogGeneratedCode() const
|
|||
std::span{m_code_buffer.data(), code_block.m_num_instructions})
|
||||
{
|
||||
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";
|
||||
|
|
|
@ -97,7 +97,8 @@ void Interpreter::Trace(const UGeckoInstruction& inst)
|
|||
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,
|
||||
"INTER PC: {:08x} SRR0: {:08x} SRR1: {:08x} CRval: {:016x} "
|
||||
"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 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);
|
||||
Dolphin_Debugger::PrintCallstack(guard, Common::Log::LogType::POWERPC,
|
||||
Common::Log::LogLevel::LNOTICE);
|
||||
|
|
|
@ -1177,7 +1177,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
@ -1320,7 +1321,7 @@ void Jit64::LogGeneratedCode() const
|
|||
std::span{m_code_buffer.data(), code_block.m_num_instructions})
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -1401,7 +1401,7 @@ void JitArm64::LogGeneratedCode() const
|
|||
std::span{m_code_buffer.data(), code_block.m_num_instructions})
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -38,8 +38,9 @@ QString HtmlFormatErrorLine(const Common::GekkoAssembler::AssemblerError& err)
|
|||
}
|
||||
} // namespace
|
||||
|
||||
AssembleInstructionDialog::AssembleInstructionDialog(QWidget* parent, u32 address, u32 value)
|
||||
: QDialog(parent), m_code(value), m_address(address)
|
||||
AssembleInstructionDialog::AssembleInstructionDialog(QWidget* parent, u32 address, u32 value,
|
||||
QString disasm)
|
||||
: QDialog(parent), m_code(value), m_address(address), m_disassembly(disasm)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
@ -66,7 +67,7 @@ void AssembleInstructionDialog::CreateWidgets()
|
|||
layout->addWidget(m_error_line_label);
|
||||
layout->addWidget(m_msg_label);
|
||||
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);
|
||||
OnEditChanged();
|
||||
|
@ -86,6 +87,31 @@ void AssembleInstructionDialog::OnEditChanged()
|
|||
std::string line = m_input_edit->text().toStdString();
|
||||
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);
|
||||
|
||||
if (IsFailure(asm_result))
|
||||
|
|
|
@ -15,7 +15,7 @@ class AssembleInstructionDialog : public QDialog
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AssembleInstructionDialog(QWidget* parent, u32 address, u32 value);
|
||||
explicit AssembleInstructionDialog(QWidget* parent, u32 address, u32 value, QString disasm);
|
||||
|
||||
u32 GetCode() const;
|
||||
|
||||
|
@ -28,6 +28,7 @@ private:
|
|||
u32 m_code;
|
||||
u32 m_address;
|
||||
|
||||
QString m_disassembly;
|
||||
QLineEdit* m_input_edit;
|
||||
QLabel* m_error_loc_label;
|
||||
QLabel* m_error_line_label;
|
||||
|
|
|
@ -306,7 +306,7 @@ static QVariant GetValidSymbolStringVariant(const QVariant& symbol_name_v)
|
|||
|
||||
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');
|
||||
// I wish I could disassemble just the mnemonic!
|
||||
if (split == std::string::npos)
|
||||
|
|
|
@ -228,7 +228,7 @@ void CodeViewWidget::FontBasedSizing()
|
|||
// 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
|
||||
// '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 std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
|
||||
const std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
|
||||
|
@ -1030,7 +1030,12 @@ void CodeViewWidget::DoPatchInstruction(bool 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);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
|
|
|
@ -55,6 +55,10 @@ public:
|
|||
HighlightCurToken(HighlightFormat::FPR);
|
||||
break;
|
||||
|
||||
case Terminal::GQR:
|
||||
HighlightCurToken(HighlightFormat::GQR);
|
||||
break;
|
||||
|
||||
case Terminal::SPR:
|
||||
HighlightCurToken(HighlightFormat::SPR);
|
||||
break;
|
||||
|
@ -221,6 +225,7 @@ void GekkoSyntaxHighlight::HighlightSubstr(int start, int len, HighlightFormat f
|
|||
break;
|
||||
case HighlightFormat::GPR:
|
||||
case HighlightFormat::FPR:
|
||||
case HighlightFormat::GQR:
|
||||
case HighlightFormat::SPR:
|
||||
case HighlightFormat::CRField:
|
||||
case HighlightFormat::CRFlag:
|
||||
|
|
|
@ -18,6 +18,7 @@ enum class HighlightFormat
|
|||
Immediate,
|
||||
GPR,
|
||||
FPR,
|
||||
GQR,
|
||||
SPR,
|
||||
CRField,
|
||||
CRFlag,
|
||||
|
|
|
@ -213,7 +213,7 @@ static void DisassembleCodeBuffer(const JitBlock& block, PPCSymbolDB& ppc_symbol
|
|||
next_address = 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,9 +55,9 @@ void PatchInstructionDialog::OnEditChanged()
|
|||
|
||||
m_button_box->button(QDialogButtonBox::Ok)->setEnabled(good);
|
||||
|
||||
m_preview_label->setText(
|
||||
tr("Instruction: %1")
|
||||
.arg(QString::fromStdString(Common::GekkoDisassembler::Disassemble(m_code, m_address))));
|
||||
m_preview_label->setText(tr("Instruction: %1")
|
||||
.arg(QString::fromStdString(Common::GekkoDisassembler::Disassemble(
|
||||
m_code, m_address, false))));
|
||||
}
|
||||
|
||||
u32 PatchInstructionDialog::GetCode() const
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "Common/Assembler/GekkoAssembler.h"
|
||||
#include "Common/GekkoDisassembler.h"
|
||||
|
||||
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"
|
||||
"subis 0, 4, 8\n"
|
||||
"subic 0, 4, 8\n"
|
||||
|
|
Loading…
Reference in New Issue