GE Debugger: Allow expressions for goto address.

This commit is contained in:
Unknown W. Brackets 2022-09-05 12:16:20 -07:00
parent 542e7aa555
commit f595299fe5
4 changed files with 251 additions and 10 deletions

View file

@ -580,8 +580,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
return true;
}
bool parseExpression(char* exp, IExpressionFunctions* funcs, uint32_t& dest)
{
bool parseExpression(const char *exp, IExpressionFunctions *funcs, uint32_t &dest) {
PostfixExpression postfix;
if (initPostfixExpression(exp,funcs,postfix) == false) return false;
return parsePostfixExpression(postfix,funcs,dest);

View file

@ -15,7 +15,234 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "GPUDebugInterface.h"
#include "Common/Log.h"
#include "Common/Math/expression_parser.h"
#include "Core/Debugger/SymbolMap.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Debugger/GECommandTable.h"
#include "GPU/GPUState.h"
enum class GEReferenceIndex : uint32_t {
VADDR = 0x100,
IADDR,
OFFSET,
PC,
STALL,
BFLAG,
BONE_MATRIX = 0x200,
WORLD_MATRIX = 0x260,
VIEW_MATRIX = 0x26C,
PROJ_MATRIX = 0x278,
TGEN_MATRIX = 0x288,
MATRIX_END = 0x294,
};
ENUM_CLASS_BITOPS(GEReferenceIndex);
struct ReferenceName {
GEReferenceIndex index;
const char *name;
};
static constexpr ReferenceName referenceNames[] = {
{ GEReferenceIndex::VADDR, "vaddr" },
{ GEReferenceIndex::IADDR, "iaddr" },
{ GEReferenceIndex::OFFSET, "offset" },
{ GEReferenceIndex::PC, "pc" },
{ GEReferenceIndex::STALL, "stall" },
{ GEReferenceIndex::BFLAG, "bflag" },
{ GEReferenceIndex::BFLAG, "boundflag" },
};
class GEExpressionFunctions : public IExpressionFunctions {
public:
GEExpressionFunctions(GPUDebugInterface *gpu) : gpu_(gpu) {}
bool parseReference(char *str, uint32_t &referenceIndex) override;
bool parseSymbol(char *str, uint32_t &symbolValue) override;
uint32_t getReferenceValue(uint32_t referenceIndex) override;
ExpressionType getReferenceType(uint32_t referenceIndex) override;
bool getMemoryValue(uint32_t address, int size, uint32_t &dest, char *error) override;
private:
GPUDebugInterface *gpu_;
};
bool GEExpressionFunctions::parseReference(char *str, uint32_t &referenceIndex) {
// TODO: Support formats and a form of fields (i.e. vtype.throughmode.)
// For now, let's just support the register bits directly.
GECmdInfo info;
if (GECmdInfoByName(str, info)) {
referenceIndex = info.reg;
return true;
}
// Also allow non-register references.
for (const auto &entry : referenceNames) {
if (strcasecmp(str, entry.name) == 0) {
referenceIndex = (uint32_t)entry.index;
return true;
}
}
// And matrix data. Maybe should allow column/row specification.
int subindex = -1;
int len = -1;
if (sscanf(str, "bone%i%n", &subindex, &len) == 1) {
if (len == strlen(str) && subindex < 96) {
referenceIndex = (uint32_t)GEReferenceIndex::BONE_MATRIX + subindex;
return true;
}
}
if (sscanf(str, "world%i%n", &subindex, &len) == 1) {
if (len == strlen(str) && subindex < 12) {
referenceIndex = (uint32_t)GEReferenceIndex::WORLD_MATRIX + subindex;
return true;
}
}
if (sscanf(str, "view%i%n", &subindex, &len) == 1) {
if (len == strlen(str) && subindex < 12) {
referenceIndex = (uint32_t)GEReferenceIndex::VIEW_MATRIX + subindex;
return true;
}
}
if (sscanf(str, "proj%i%n", &subindex, &len) == 1) {
if (len == strlen(str) && subindex < 16) {
referenceIndex = (uint32_t)GEReferenceIndex::PROJ_MATRIX + subindex;
return true;
}
}
if (sscanf(str, "tgen%i%n", &subindex, &len) == 1 || sscanf(str, "texgen%i%n", &subindex, &len) == 1) {
if (len == strlen(str) && subindex < 12) {
referenceIndex = (uint32_t)GEReferenceIndex::TGEN_MATRIX + subindex;
return true;
}
}
return false;
}
bool GEExpressionFunctions::parseSymbol(char *str, uint32_t &symbolValue) {
// Mainly useful for checking memory addresses.
return g_symbolMap->GetLabelValue(str, symbolValue);
}
uint32_t GEExpressionFunctions::getReferenceValue(uint32_t referenceIndex) {
if (referenceIndex < 0x100) {
uint32_t value = gpu_->GetGState().cmdmem[referenceIndex];
// TODO: Later, support float values and similar.
return value & 0x00FFFFFF;
}
// We return the matrix value as float bits, which gets interpreted correctly in the parser.
if (referenceIndex >= (uint32_t)GEReferenceIndex::BONE_MATRIX && referenceIndex < (uint32_t)GEReferenceIndex::MATRIX_END) {
GPUgstate state = gpu_->GetGState();
float value;
if (referenceIndex >= (uint32_t)GEReferenceIndex::TGEN_MATRIX) {
value = state.tgenMatrix[referenceIndex - (uint32_t)GEReferenceIndex::TGEN_MATRIX];
} else if (referenceIndex >= (uint32_t)GEReferenceIndex::PROJ_MATRIX) {
value = state.projMatrix[referenceIndex - (uint32_t)GEReferenceIndex::PROJ_MATRIX];
} else if (referenceIndex >= (uint32_t)GEReferenceIndex::VIEW_MATRIX) {
value = state.viewMatrix[referenceIndex - (uint32_t)GEReferenceIndex::VIEW_MATRIX];
} else if (referenceIndex >= (uint32_t)GEReferenceIndex::WORLD_MATRIX) {
value = state.worldMatrix[referenceIndex - (uint32_t)GEReferenceIndex::WORLD_MATRIX];
} else {
value = state.boneMatrix[referenceIndex - (uint32_t)GEReferenceIndex::BONE_MATRIX];
}
uint32_t result;
memcpy(&result, &value, sizeof(result));
return result;
}
GEReferenceIndex ref = (GEReferenceIndex)referenceIndex;
DisplayList list;
switch (ref) {
case GEReferenceIndex::VADDR:
return gpu_->GetVertexAddress();
case GEReferenceIndex::IADDR:
return gpu_->GetIndexAddress();
case GEReferenceIndex::OFFSET:
// TODO: Should use an interface method, probably.
return gstate_c.offsetAddr;
case GEReferenceIndex::PC:
if (gpu_->GetCurrentDisplayList(list)) {
return list.pc;
}
return 0;
case GEReferenceIndex::STALL:
if (gpu_->GetCurrentDisplayList(list)) {
return list.stall;
}
return 0;
case GEReferenceIndex::BFLAG:
if (gpu_->GetCurrentDisplayList(list)) {
return list.bboxResult ? 1 : 0;
}
return 0;
case GEReferenceIndex::BONE_MATRIX:
case GEReferenceIndex::WORLD_MATRIX:
case GEReferenceIndex::VIEW_MATRIX:
case GEReferenceIndex::PROJ_MATRIX:
case GEReferenceIndex::TGEN_MATRIX:
case GEReferenceIndex::MATRIX_END:
// Shouldn't have gotten here.
break;
}
_assert_msg_(false, "Invalid reference index");
return 0;
}
ExpressionType GEExpressionFunctions::getReferenceType(uint32_t referenceIndex) {
if (referenceIndex < 0x100) {
// TODO: Later, support float values and similar.
return EXPR_TYPE_UINT;
}
if (referenceIndex >= (uint32_t)GEReferenceIndex::BONE_MATRIX && referenceIndex < (uint32_t)GEReferenceIndex::MATRIX_END)
return EXPR_TYPE_FLOAT;
return EXPR_TYPE_UINT;
}
bool GEExpressionFunctions::getMemoryValue(uint32_t address, int size, uint32_t &dest, char *error) {
if (!Memory::IsValidRange(address, size)) {
sprintf(error, "Invalid address or size %08x + %d", address, size);
return false;
}
switch (size) {
case 1:
dest = Memory::Read_U8(address);
return true;
case 2:
dest = Memory::Read_U16(address);
return true;
case 4:
dest = Memory::Read_U32(address);
return true;
}
sprintf(error, "Unexpected memory access size %d", size);
return false;
}
bool GPUDebugInitExpression(GPUDebugInterface *g, const char *str, PostfixExpression &exp) {
GEExpressionFunctions funcs(g);
return initPostfixExpression(str, &funcs, exp);
}
bool GPUDebugExecExpression(GPUDebugInterface *g, PostfixExpression &exp, uint32_t &result) {
GEExpressionFunctions funcs(g);
return parsePostfixExpression(exp, &funcs, result);
}
bool GPUDebugExecExpression(GPUDebugInterface *g, const char *str, uint32_t &result) {
GEExpressionFunctions funcs(g);
return parseExpression(str, &funcs, result);
}
void GPUDebugBuffer::Allocate(u32 stride, u32 height, GEBufferFormat fmt, bool flipped, bool reversed) {
GPUDebugBufferFormat actualFmt = GPUDebugBufferFormat(fmt);

View file

@ -20,9 +20,10 @@
#include <vector>
#include <string>
#include "Common/Math/expression_parser.h"
#include "Core/MemMap.h"
#include "GPU/GPU.h"
#include "GPU/GPUInterface.h"
#include "Core/MemMap.h"
struct GPUDebugOp {
u32 pc;
@ -251,3 +252,7 @@ public:
// get content of specific framebuffer / texture?
// vertex / texture decoding?
};
bool GPUDebugInitExpression(GPUDebugInterface *g, const char *str, PostfixExpression &exp);
bool GPUDebugExecExpression(GPUDebugInterface *g, PostfixExpression &exp, uint32_t &result);
bool GPUDebugExecExpression(GPUDebugInterface *g, const char *str, uint32_t &result);

View file

@ -1,5 +1,7 @@
#include <algorithm>
#include <tchar.h>
#include "Common/Data/Encoding/Utf8.h"
#include "Common/StringUtils.h"
#include "Common/System/Display.h"
#include "Windows/GEDebugger/CtrlDisplayListView.h"
#include "Windows/GEDebugger/GEDebugger.h"
@ -369,15 +371,23 @@ void CtrlDisplayListView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)
break;
case ID_GEDBG_GOTOADDR:
{
u32 newAddress = curAddress;
if (!InputBox_GetHex(GetModuleHandle(NULL), wnd, L"Address", curAddress, newAddress)) {
std::string expression = StringFromFormat("%08x", curAddress);
if (!InputBox_GetString(GetModuleHandle(NULL), wnd, L"Address", expression, expression, true)) {
break;
}
if (Memory::IsValidAddress(newAddress)) {
setCurAddress(newAddress);
scrollAddressIntoView();
redraw();
uint32_t newAddress = curAddress;
if (!GPUDebugExecExpression(gpuDebug, expression.c_str(), newAddress)) {
MessageBox(wnd, ConvertUTF8ToWString(getExpressionError()).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
break;
}
if (!Memory::IsValidAddress(newAddress)) {
MessageBox(wnd, L"Address not in valid memory", L"Invalid address", MB_OK | MB_ICONEXCLAMATION);
break;
}
setCurAddress(newAddress);
scrollAddressIntoView();
redraw();
}
break;
}