diff --git a/Common/Math/expression_parser.cpp b/Common/Math/expression_parser.cpp index 1b9ed7ba90..970c9c4a20 100644 --- a/Common/Math/expression_parser.cpp +++ b/Common/Math/expression_parser.cpp @@ -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); diff --git a/GPU/Common/GPUDebugInterface.cpp b/GPU/Common/GPUDebugInterface.cpp index e4ce10ea44..d1e0098e44 100644 --- a/GPU/Common/GPUDebugInterface.cpp +++ b/GPU/Common/GPUDebugInterface.cpp @@ -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); diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index 97e97c9ea3..09254ddf53 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -20,9 +20,10 @@ #include #include +#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); diff --git a/Windows/GEDebugger/CtrlDisplayListView.cpp b/Windows/GEDebugger/CtrlDisplayListView.cpp index 6b521c4996..1152e4f126 100644 --- a/Windows/GEDebugger/CtrlDisplayListView.cpp +++ b/Windows/GEDebugger/CtrlDisplayListView.cpp @@ -1,5 +1,7 @@ #include #include +#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; }