Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add breakpoint conditions to GE debugger #15974

Merged
merged 4 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
GE Debugger: Allow expressions for goto address.
  • Loading branch information
unknownbrackets committed Sep 6, 2022
commit f595299fe535c266c08a34263a228ef7f83dfff8
3 changes: 1 addition & 2 deletions Common/Math/expression_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
229 changes: 228 additions & 1 deletion GPU/Common/GPUDebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion GPU/Common/GPUDebugInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -251,3 +252,7 @@ class GPUDebugInterface {
// 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);
22 changes: 16 additions & 6 deletions Windows/GEDebugger/CtrlDisplayListView.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down