ppsspp/GPU/Common/GPUDebugInterface.cpp
Unknown W. Brackets aa377662dd GE Debugger: Provide addrs in breakpoint conds.
Easier to just refer to the full address.
2022-09-11 10:57:04 -07:00

417 lines
11 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#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,
OP,
DATA,
CLUTADDR,
TRANSFERSRC,
TRANSFERDST,
TEXADDR0,
TEXADDR1,
TEXADDR2,
TEXADDR3,
TEXADDR4,
TEXADDR5,
TEXADDR6,
TEXADDR7,
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" },
{ GEReferenceIndex::OP, "op" },
{ GEReferenceIndex::DATA, "data" },
{ GEReferenceIndex::CLUTADDR, "clutaddr" },
{ GEReferenceIndex::TRANSFERSRC, "transfersrc" },
{ GEReferenceIndex::TRANSFERDST, "transferdst" },
{ GEReferenceIndex::TEXADDR0, "texaddr0" },
{ GEReferenceIndex::TEXADDR1, "texaddr1" },
{ GEReferenceIndex::TEXADDR2, "texaddr2" },
{ GEReferenceIndex::TEXADDR3, "texaddr3" },
{ GEReferenceIndex::TEXADDR4, "texaddr4" },
{ GEReferenceIndex::TEXADDR5, "texaddr5" },
{ GEReferenceIndex::TEXADDR6, "texaddr6" },
{ GEReferenceIndex::TEXADDR7, "texaddr7" },
};
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) {
GPUgstate state = gpu_->GetGState();
if (referenceIndex < 0x100) {
uint32_t value = state.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) {
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::OP:
// TODO: Support fields under this as per the cmd format?
if (gpu_->GetCurrentDisplayList(list)) {
return Memory::Read_U32(list.pc);
}
return 0;
case GEReferenceIndex::DATA:
if (gpu_->GetCurrentDisplayList(list)) {
return Memory::Read_U32(list.pc) & 0x00FFFFFF;
}
return 0;
case GEReferenceIndex::CLUTADDR:
return state.getClutAddress();
case GEReferenceIndex::TRANSFERSRC:
return state.getTransferSrcAddress();
case GEReferenceIndex::TRANSFERDST:
return state.getTransferDstAddress();
case GEReferenceIndex::TEXADDR0:
case GEReferenceIndex::TEXADDR1:
case GEReferenceIndex::TEXADDR2:
case GEReferenceIndex::TEXADDR3:
case GEReferenceIndex::TEXADDR4:
case GEReferenceIndex::TEXADDR5:
case GEReferenceIndex::TEXADDR6:
case GEReferenceIndex::TEXADDR7:
return state.getTextureAddress((int)ref - (int)GEReferenceIndex::TEXADDR0);
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);
if (reversed && actualFmt < GPU_DBG_FORMAT_8888) {
actualFmt |= GPU_DBG_FORMAT_REVERSE_FLAG;
}
Allocate(stride, height, actualFmt, flipped);
}
void GPUDebugBuffer::Allocate(u32 stride, u32 height, GPUDebugBufferFormat fmt, bool flipped) {
if (alloc_ && stride_ == stride && height_ == height && fmt_ == fmt) {
// Already allocated the right size.
flipped_ = flipped;
return;
}
Free();
alloc_ = true;
height_ = height;
stride_ = stride;
fmt_ = fmt;
flipped_ = flipped;
u32 pixelSize = PixelSize();
data_ = new u8[pixelSize * stride * height];
}
void GPUDebugBuffer::Free() {
if (alloc_ && data_ != NULL) {
delete [] data_;
}
data_ = NULL;
}
u32 GPUDebugBuffer::PixelSize() const {
switch (fmt_) {
case GPU_DBG_FORMAT_8888:
case GPU_DBG_FORMAT_8888_BGRA:
case GPU_DBG_FORMAT_FLOAT:
case GPU_DBG_FORMAT_24BIT_8X:
case GPU_DBG_FORMAT_24X_8BIT:
case GPU_DBG_FORMAT_FLOAT_DIV_256:
case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
return 4;
case GPU_DBG_FORMAT_888_RGB:
return 3;
case GPU_DBG_FORMAT_8BIT:
return 1;
default:
return 2;
}
}
u32 GPUDebugBuffer::GetRawPixel(int x, int y) const {
if (data_ == nullptr) {
return 0;
}
if (flipped_) {
y = height_ - y - 1;
}
u32 pixelSize = PixelSize();
u32 byteOffset = pixelSize * (stride_ * y + x);
const u8 *ptr = &data_[byteOffset];
switch (pixelSize) {
case 4:
return *(const u32 *)ptr;
case 3:
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
case 2:
return *(const u16 *)ptr;
case 1:
return *(const u8 *)ptr;
default:
return 0;
}
}
void GPUDebugBuffer::SetRawPixel(int x, int y, u32 c) {
if (data_ == nullptr) {
return;
}
if (flipped_) {
y = height_ - y - 1;
}
u32 pixelSize = PixelSize();
u32 byteOffset = pixelSize * (stride_ * y + x);
u8 *ptr = &data_[byteOffset];
switch (pixelSize) {
case 4:
*(u32 *)ptr = c;
break;
case 3:
ptr[0] = (c >> 0) & 0xFF;
ptr[1] = (c >> 8) & 0xFF;
ptr[2] = (c >> 16) & 0xFF;
break;
case 2:
*(u16 *)ptr = (u16)c;
break;
case 1:
*ptr = (u8)c;
break;
default:
break;
}
}