Merge pull request #15974 from unknownbrackets/debugger-gecond

Add breakpoint conditions to GE debugger
This commit is contained in:
Henrik Rydgård 2022-09-06 23:39:26 +02:00 committed by GitHub
commit ae23c3c266
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1013 additions and 35 deletions

View file

@ -1594,6 +1594,8 @@ set(GPU_SOURCES
GPU/Debugger/Breakpoints.h
GPU/Debugger/Debugger.cpp
GPU/Debugger/Debugger.h
GPU/Debugger/GECommandTable.cpp
GPU/Debugger/GECommandTable.h
GPU/Debugger/Playback.cpp
GPU/Debugger/Playback.h
GPU/Debugger/Record.cpp

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,249 @@
// 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,
OP,
DATA,
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" },
};
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::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::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

@ -18,17 +18,27 @@
#include <functional>
#include <mutex>
#include <set>
#include <unordered_map>
#include <vector>
#include "Common/CommonFuncs.h"
#include "Common/Math/expression_parser.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/GPUState.h"
namespace GPUBreakpoints {
struct BreakpointInfo {
bool isConditional = false;
PostfixExpression expression;
std::string expressionString;
};
static std::mutex breaksLock;
static bool breakCmds[256];
static std::set<u32> breakPCs;
static BreakpointInfo breakCmdsInfo[256];
static std::unordered_map<u32, BreakpointInfo> breakPCs;
static std::set<u32> breakTextures;
static std::set<u32> breakRenderTargets;
// Small optimization to avoid a lock/lookup for the common case.
@ -158,8 +168,42 @@ bool IsRenderTargetCmdBreakpoint(u32 op) {
return false;
}
static bool HitAddressBreakpoint(u32 pc) {
if (breakPCsCount == 0)
return false;
std::lock_guard<std::mutex> guard(breaksLock);
auto entry = breakPCs.find(pc);
if (entry == breakPCs.end())
return false;
if (entry->second.isConditional) {
u32 result = 1;
if (!GPUDebugExecExpression(gpuDebug, breakPCs[pc].expression, result))
return false;
return result != 0;
}
return true;
}
static bool HitOpBreakpoint(u32 op) {
u8 cmd = op >> 24;
if (!IsCmdBreakpoint(cmd))
return false;
if (breakCmdsInfo[cmd].isConditional) {
std::lock_guard<std::mutex> guard(breaksLock);
u32 result = 1;
if (!GPUDebugExecExpression(gpuDebug, breakCmdsInfo[cmd].expression, result))
return false;
return result != 0;
}
return true;
}
bool IsBreakpoint(u32 pc, u32 op) {
if (IsAddressBreakpoint(pc) || IsOpBreakpoint(op)) {
if (HitAddressBreakpoint(pc) || HitOpBreakpoint(op)) {
return true;
}
@ -275,13 +319,13 @@ void AddAddressBreakpoint(u32 addr, bool temp) {
if (temp) {
if (breakPCs.find(addr) == breakPCs.end()) {
breakPCsTemp.insert(addr);
breakPCs.insert(addr);
breakPCs[addr].isConditional = false;
}
// Already normal breakpoint, let's not make it temporary.
} else {
// Remove the temporary marking.
breakPCsTemp.erase(addr);
breakPCs.insert(addr);
breakPCs.insert(std::make_pair(addr, BreakpointInfo{}));
}
breakPCsCount = breakPCs.size();
@ -293,12 +337,16 @@ void AddCmdBreakpoint(u8 cmd, bool temp) {
if (!breakCmds[cmd]) {
breakCmdsTemp[cmd] = true;
breakCmds[cmd] = true;
breakCmdsInfo[cmd].isConditional = false;
}
// Ignore adding a temp breakpoint when a normal one exists.
} else {
// This is no longer temporary.
breakCmdsTemp[cmd] = false;
breakCmds[cmd] = true;
if (!breakCmds[cmd]) {
breakCmds[cmd] = true;
breakCmdsInfo[cmd].isConditional = false;
}
}
notifyBreakpoints(true);
}
@ -398,6 +446,63 @@ void RemoveTextureChangeTempBreakpoint() {
notifyBreakpoints(HasAnyBreakpoints());
}
static bool SetupCond(BreakpointInfo &bp, const std::string &expression, std::string *error) {
bool success = true;
if (expression.length() != 0) {
if (GPUDebugInitExpression(gpuDebug, expression.c_str(), bp.expression)) {
bp.isConditional = true;
bp.expressionString = expression;
} else {
// Don't change if it failed.
if (error)
*error = getExpressionError();
success = false;
}
} else {
bp.isConditional = false;
}
return success;
}
bool SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error) {
// Must have one in the first place, make sure it's not temporary.
AddAddressBreakpoint(addr);
std::lock_guard<std::mutex> guard(breaksLock);
auto &bp = breakPCs[addr];
return SetupCond(breakPCs[addr], expression, error);
}
bool GetAddressBreakpointCond(u32 addr, std::string *expression) {
std::lock_guard<std::mutex> guard(breaksLock);
auto entry = breakPCs.find(addr);
if (entry != breakPCs.end() && entry->second.isConditional) {
if (expression)
*expression = entry->second.expressionString;
return true;
}
return false;
}
bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error) {
// Must have one in the first place, make sure it's not temporary.
AddCmdBreakpoint(cmd);
std::lock_guard<std::mutex> guard(breaksLock);
return SetupCond(breakCmdsInfo[cmd], expression, error);
}
bool GetCmdBreakpointCond(u8 cmd, std::string *expression) {
if (breakCmds[cmd] && breakCmdsInfo[cmd].isConditional) {
if (expression) {
std::lock_guard<std::mutex> guard(breaksLock);
*expression = breakCmdsInfo[cmd].expressionString;
}
return true;
}
return false;
}
void UpdateLastTexture(u32 addr) {
lastTexture = addr;
}

View file

@ -47,6 +47,11 @@ namespace GPUBreakpoints {
void RemoveTextureChangeTempBreakpoint();
void RemoveRenderTargetBreakpoint(u32 addr);
bool SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error);
bool GetAddressBreakpointCond(u32 addr, std::string *expression);
bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error);
bool GetCmdBreakpointCond(u8 cmd, std::string *expression);
void UpdateLastTexture(u32 addr);
void ClearAllBreakpoints();

View file

@ -0,0 +1,415 @@
// Copyright (c) 2022- 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 <cstring>
#include "Common/Common.h"
#include "Common/Log.h"
#include "GPU/Debugger/GECommandTable.h"
#include "GPU/ge_constants.h"
struct GECmdAlias {
GECommand reg;
const char *aliases[3];
};
static constexpr GECmdInfo geCmdInfo[] = {
{ GE_CMD_NOP, "nop", GECmdFormat::NONE },
{ GE_CMD_VADDR, "setvaddr", GECmdFormat::RELATIVE_ADDR },
{ GE_CMD_IADDR, "setiaddr", GECmdFormat::RELATIVE_ADDR },
{ GE_CMD_UNKNOWN_03, "unknown03", GECmdFormat::NONE },
{ GE_CMD_PRIM, "prim", GECmdFormat::PRIM },
{ GE_CMD_BEZIER, "bezier", GECmdFormat::BEZIER },
{ GE_CMD_SPLINE, "spline", GECmdFormat::SPLINE },
{ GE_CMD_BOUNDINGBOX, "btest", GECmdFormat::PRIM },
{ GE_CMD_JUMP, "jump", GECmdFormat::JUMP },
{ GE_CMD_BJUMP, "bjump", GECmdFormat::JUMP },
{ GE_CMD_CALL, "call", GECmdFormat::JUMP },
{ GE_CMD_RET, "ret", GECmdFormat::NONE },
{ GE_CMD_END, "end", GECmdFormat::DATA16 },
{ GE_CMD_UNKNOWN_0D, "unknown0d", GECmdFormat::NONE },
{ GE_CMD_SIGNAL, "signal", GECmdFormat::SIGNAL },
{ GE_CMD_FINISH, "finish", GECmdFormat::NONE },
{ GE_CMD_BASE, "base", GECmdFormat::HIGH_ADDR_ONLY },
{ GE_CMD_UNKNOWN_11, "unknown11", GECmdFormat::NONE },
{ GE_CMD_VERTEXTYPE, "vtype", GECmdFormat::VERTEX_TYPE },
{ GE_CMD_OFFSETADDR, "setoffset", GECmdFormat::OFFSET_ADDR },
{ GE_CMD_ORIGIN, "origin", GECmdFormat::NONE },
{ GE_CMD_REGION1, "regionrate", GECmdFormat::X10_Y10 },
{ GE_CMD_REGION2, "regionstop", GECmdFormat::X10_Y10 },
{ GE_CMD_LIGHTINGENABLE, "lighting_on", GECmdFormat::FLAG },
{ GE_CMD_LIGHTENABLE0, "light0_on", GECmdFormat::FLAG },
{ GE_CMD_LIGHTENABLE1, "light1_on", GECmdFormat::FLAG },
{ GE_CMD_LIGHTENABLE2, "light2_on", GECmdFormat::FLAG },
{ GE_CMD_LIGHTENABLE3, "light3_on", GECmdFormat::FLAG },
{ GE_CMD_DEPTHCLAMPENABLE, "zclamp_on", GECmdFormat::FLAG },
{ GE_CMD_CULLFACEENABLE, "cull_on", GECmdFormat::FLAG },
{ GE_CMD_TEXTUREMAPENABLE, "tex_on", GECmdFormat::FLAG },
{ GE_CMD_FOGENABLE, "fog_on", GECmdFormat::FLAG },
{ GE_CMD_DITHERENABLE, "dither_on", GECmdFormat::FLAG },
{ GE_CMD_ALPHABLENDENABLE, "ablend_on", GECmdFormat::FLAG },
{ GE_CMD_ALPHABLENDENABLE, "atest_on", GECmdFormat::FLAG },
{ GE_CMD_ZTESTENABLE, "ztest_on", GECmdFormat::FLAG },
{ GE_CMD_STENCILTESTENABLE, "stest_on", GECmdFormat::FLAG },
{ GE_CMD_ANTIALIASENABLE, "antialias_on", GECmdFormat::FLAG },
{ GE_CMD_PATCHCULLENABLE, "patchcull_on", GECmdFormat::FLAG },
{ GE_CMD_COLORTESTENABLE, "ctest_on", GECmdFormat::FLAG },
{ GE_CMD_LOGICOPENABLE, "logicop_on", GECmdFormat::FLAG },
{ GE_CMD_UNKNOWN_29, "unknown29", GECmdFormat::NONE },
{ GE_CMD_BONEMATRIXNUMBER, "bonemtxnum", GECmdFormat::BONE_NUM },
{ GE_CMD_BONEMATRIXDATA, "bonemtxdata", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT0, "morph0", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT1, "morph1", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT2, "morph2", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT3, "morph3", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT4, "morph4", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT5, "morph5", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT6, "morph6", GECmdFormat::FLOAT },
{ GE_CMD_MORPHWEIGHT7, "morph7", GECmdFormat::FLOAT },
{ GE_CMD_UNKNOWN_34, "unknown34", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_35, "unknown35", GECmdFormat::NONE },
{ GE_CMD_PATCHDIVISION, "patchdivision", GECmdFormat::PATCH_DIVISION },
{ GE_CMD_PATCHPRIMITIVE, "patchprim", GECmdFormat::PATCH_PRIM },
{ GE_CMD_PATCHFACING, "patchreversenormals", GECmdFormat::FLAG },
{ GE_CMD_UNKNOWN_39, "unknown39", GECmdFormat::NONE },
{ GE_CMD_WORLDMATRIXNUMBER, "worldmtxnum", GECmdFormat::MATRIX_NUM },
{ GE_CMD_WORLDMATRIXDATA, "worldmtxdata", GECmdFormat::FLOAT },
{ GE_CMD_VIEWMATRIXNUMBER, "viewmtxnum", GECmdFormat::MATRIX_NUM },
{ GE_CMD_VIEWMATRIXDATA, "viewmtxdata", GECmdFormat::FLOAT },
{ GE_CMD_PROJMATRIXNUMBER, "projmtxnum", GECmdFormat::MATRIX_NUM },
{ GE_CMD_PROJMATRIXDATA, "projmtxdata", GECmdFormat::FLOAT },
{ GE_CMD_TGENMATRIXNUMBER, "texgenmtxnum", GECmdFormat::MATRIX_NUM },
{ GE_CMD_TGENMATRIXDATA, "texgenmtxdata", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTXSCALE, "vpxscale", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTYSCALE, "vpyscale", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTZSCALE, "vpzscale", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTXCENTER, "vpxcenter", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTYCENTER, "vpycenter", GECmdFormat::FLOAT },
{ GE_CMD_VIEWPORTZCENTER, "vpzcenter", GECmdFormat::FLOAT },
{ GE_CMD_TEXSCALEU, "texscaleu", GECmdFormat::FLOAT },
{ GE_CMD_TEXSCALEV, "texscalev", GECmdFormat::FLOAT },
{ GE_CMD_TEXOFFSETU, "texoffsetu", GECmdFormat::FLOAT },
{ GE_CMD_TEXOFFSETV, "texoffsetv", GECmdFormat::FLOAT },
{ GE_CMD_OFFSETX, "offsetx", GECmdFormat::SUBPIXEL_COORD },
{ GE_CMD_OFFSETY, "offsety", GECmdFormat::SUBPIXEL_COORD },
{ GE_CMD_UNKNOWN_4E, "unknown4e", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_4F, "unknown4f", GECmdFormat::NONE },
// Really shade mode, but using gouraud as the default so it can be 1/0.
{ GE_CMD_SHADEMODE, "gouraud", GECmdFormat::FLAG },
{ GE_CMD_REVERSENORMAL, "reversenormals", GECmdFormat::FLAG },
{ GE_CMD_UNKNOWN_52, "unknown52", GECmdFormat::NONE },
{ GE_CMD_MATERIALUPDATE, "materialupdate", GECmdFormat::MATERIAL_UPDATE },
{ GE_CMD_MATERIALEMISSIVE, "materialemissive", GECmdFormat::RGB },
{ GE_CMD_MATERIALAMBIENT, "materialambient", GECmdFormat::RGB },
{ GE_CMD_MATERIALDIFFUSE, "materialdiffuse", GECmdFormat::RGB },
{ GE_CMD_MATERIALSPECULAR, "materialspecular", GECmdFormat::RGB },
{ GE_CMD_MATERIALALPHA, "materialambienta", GECmdFormat::DATA8 },
{ GE_CMD_UNKNOWN_59, "unknown59", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_5A, "unknown5a", GECmdFormat::NONE },
{ GE_CMD_MATERIALSPECULARCOEF, "specularcoef", GECmdFormat::FLOAT },
{ GE_CMD_AMBIENTCOLOR, "ambient", GECmdFormat::RGB },
{ GE_CMD_AMBIENTALPHA, "ambienta", GECmdFormat::DATA8 },
{ GE_CMD_LIGHTMODE, "lightseparate", GECmdFormat::FLAG },
{ GE_CMD_LIGHTTYPE0, "ltype0", GECmdFormat::LIGHT_TYPE },
{ GE_CMD_LIGHTTYPE1, "ltype1", GECmdFormat::LIGHT_TYPE },
{ GE_CMD_LIGHTTYPE2, "ltype2", GECmdFormat::LIGHT_TYPE },
{ GE_CMD_LIGHTTYPE3, "ltype3", GECmdFormat::LIGHT_TYPE },
{ GE_CMD_LX0, "light0posx", GECmdFormat::FLOAT },
{ GE_CMD_LY0, "light0posy", GECmdFormat::FLOAT },
{ GE_CMD_LZ0, "light0posz", GECmdFormat::FLOAT },
{ GE_CMD_LX1, "light1posx", GECmdFormat::FLOAT },
{ GE_CMD_LY1, "light1posy", GECmdFormat::FLOAT },
{ GE_CMD_LZ1, "light1posz", GECmdFormat::FLOAT },
{ GE_CMD_LX2, "light2posx", GECmdFormat::FLOAT },
{ GE_CMD_LY2, "light2posy", GECmdFormat::FLOAT },
{ GE_CMD_LZ2, "light2posz", GECmdFormat::FLOAT },
{ GE_CMD_LX3, "light3posx", GECmdFormat::FLOAT },
{ GE_CMD_LY3, "light3posy", GECmdFormat::FLOAT },
{ GE_CMD_LZ3, "light3posz", GECmdFormat::FLOAT },
{ GE_CMD_LDX0, "light0dirx", GECmdFormat::FLOAT },
{ GE_CMD_LDY0, "light0diry", GECmdFormat::FLOAT },
{ GE_CMD_LDZ0, "light0dirz", GECmdFormat::FLOAT },
{ GE_CMD_LDX1, "light1dirx", GECmdFormat::FLOAT },
{ GE_CMD_LDY1, "light1diry", GECmdFormat::FLOAT },
{ GE_CMD_LDZ1, "light1dirz", GECmdFormat::FLOAT },
{ GE_CMD_LDX2, "light2dirx", GECmdFormat::FLOAT },
{ GE_CMD_LDY2, "light2diry", GECmdFormat::FLOAT },
{ GE_CMD_LDZ2, "light2dirz", GECmdFormat::FLOAT },
{ GE_CMD_LDX3, "light3dirx", GECmdFormat::FLOAT },
{ GE_CMD_LDY3, "light3diry", GECmdFormat::FLOAT },
{ GE_CMD_LDZ3, "light3dirz", GECmdFormat::FLOAT },
{ GE_CMD_LKA0, "light0attpow0", GECmdFormat::FLOAT },
{ GE_CMD_LKB0, "light0attpow1", GECmdFormat::FLOAT },
{ GE_CMD_LKC0, "light0attpow2", GECmdFormat::FLOAT },
{ GE_CMD_LKA1, "light1attpow0", GECmdFormat::FLOAT },
{ GE_CMD_LKB1, "light1attpow1", GECmdFormat::FLOAT },
{ GE_CMD_LKC1, "light1attpow2", GECmdFormat::FLOAT },
{ GE_CMD_LKA2, "light2attpow0", GECmdFormat::FLOAT },
{ GE_CMD_LKB2, "light2attpow1", GECmdFormat::FLOAT },
{ GE_CMD_LKC2, "light2attpow2", GECmdFormat::FLOAT },
{ GE_CMD_LKA3, "light3attpow0", GECmdFormat::FLOAT },
{ GE_CMD_LKB3, "light3attpow1", GECmdFormat::FLOAT },
{ GE_CMD_LKC3, "light3attpow2", GECmdFormat::FLOAT },
{ GE_CMD_LKS0, "light0spotexp", GECmdFormat::FLOAT },
{ GE_CMD_LKS1, "light1spotexp", GECmdFormat::FLOAT },
{ GE_CMD_LKS2, "light2spotexp", GECmdFormat::FLOAT },
{ GE_CMD_LKS3, "light3spotexp", GECmdFormat::FLOAT },
{ GE_CMD_LKO0, "light0spotcutoff", GECmdFormat::FLOAT },
{ GE_CMD_LKO1, "light1spotcutoff", GECmdFormat::FLOAT },
{ GE_CMD_LKO2, "light2spotcutoff", GECmdFormat::FLOAT },
{ GE_CMD_LKO3, "light3spotcutoff", GECmdFormat::FLOAT },
{ GE_CMD_LAC0, "light0ambient", GECmdFormat::RGB },
{ GE_CMD_LDC0, "light0diffuse", GECmdFormat::RGB },
{ GE_CMD_LSC0, "light0specular", GECmdFormat::RGB },
{ GE_CMD_LAC1, "light1ambient", GECmdFormat::RGB },
{ GE_CMD_LDC1, "light1diffuse", GECmdFormat::RGB },
{ GE_CMD_LSC1, "light1specular", GECmdFormat::RGB },
{ GE_CMD_LAC2, "light2ambient", GECmdFormat::RGB },
{ GE_CMD_LDC2, "light2diffuse", GECmdFormat::RGB },
{ GE_CMD_LSC2, "light2specular", GECmdFormat::RGB },
{ GE_CMD_LAC3, "light3ambient", GECmdFormat::RGB },
{ GE_CMD_LDC3, "light3diffuse", GECmdFormat::RGB },
{ GE_CMD_LSC3, "light3specular", GECmdFormat::RGB },
{ GE_CMD_CULL, "cullccw", GECmdFormat::FLAG },
{ GE_CMD_FRAMEBUFPTR, "fbptr", GECmdFormat::LOW_ADDR_ONLY },
{ GE_CMD_FRAMEBUFWIDTH, "fbstride", GECmdFormat::STRIDE },
{ GE_CMD_ZBUFPTR, "zbptr", GECmdFormat::LOW_ADDR_ONLY },
{ GE_CMD_ZBUFWIDTH, "zbstride", GECmdFormat::STRIDE },
{ GE_CMD_TEXADDR0, "texaddr0", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR1, "texaddr1", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR2, "texaddr2", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR3, "texaddr3", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR4, "texaddr4", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR5, "texaddr5", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR6, "texaddr6", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXADDR7, "texaddr7", GECmdFormat::LOW_ADDR },
{ GE_CMD_TEXBUFWIDTH0, "texbufw0", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH1, "texbufw1", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH2, "texbufw2", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH3, "texbufw3", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH4, "texbufw4", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH5, "texbufw5", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH6, "texbufw6", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TEXBUFWIDTH7, "texbufw7", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_CLUTADDR, "clutaddr", GECmdFormat::LOW_ADDR },
{ GE_CMD_CLUTADDRUPPER, "clutaddrhigh", GECmdFormat::HIGH_ADDR },
{ GE_CMD_TRANSFERSRC, "transfersrc", GECmdFormat::LOW_ADDR },
{ GE_CMD_TRANSFERSRCW, "transfersrcstride", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_TRANSFERDST, "transferdst", GECmdFormat::LOW_ADDR },
{ GE_CMD_TRANSFERDSTW, "transferdststride", GECmdFormat::STRIDE_HIGH_ADDR },
{ GE_CMD_UNKNOWN_B6, "unknownb6", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_B7, "unknownb7", GECmdFormat::NONE },
{ GE_CMD_TEXSIZE0, "texsize0", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE1, "texsize1", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE2, "texsize2", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE3, "texsize3", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE4, "texsize4", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE5, "texsize5", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE6, "texsize6", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXSIZE7, "texsize7", GECmdFormat::TEX_SIZE },
{ GE_CMD_TEXMAPMODE, "texmapmode", GECmdFormat::TEX_MAP_MODE },
{ GE_CMD_TEXSHADELS, "texlightsrc", GECmdFormat::TEX_LIGHT_SRC },
{ GE_CMD_TEXMODE, "texmode", GECmdFormat::TEX_MODE },
{ GE_CMD_TEXFORMAT, "texformat", GECmdFormat::TEX_FORMAT },
{ GE_CMD_LOADCLUT, "loadclut", GECmdFormat::CLUT_BLOCKS },
{ GE_CMD_CLUTFORMAT, "clutformat", GECmdFormat::CLUT_FORMAT },
{ GE_CMD_TEXFILTER, "texfilter", GECmdFormat::TEX_FILTER },
{ GE_CMD_TEXWRAP, "texclamp", GECmdFormat::TEX_CLAMP },
{ GE_CMD_TEXLEVEL, "texlevelmode", GECmdFormat::TEX_LEVEL_MODE },
{ GE_CMD_TEXFUNC, "texfunc", GECmdFormat::TEX_FUNC },
{ GE_CMD_TEXENVCOLOR, "texenv", GECmdFormat::RGB },
{ GE_CMD_TEXFLUSH, "texflush", GECmdFormat::NONE },
{ GE_CMD_TEXSYNC, "texsync", GECmdFormat::NONE },
{ GE_CMD_FOG1, "fogend", GECmdFormat::FLOAT },
{ GE_CMD_FOG2, "fogslope", GECmdFormat::FLOAT },
{ GE_CMD_FOGCOLOR, "fogcolor", GECmdFormat::RGB },
{ GE_CMD_TEXLODSLOPE, "texlodslope", GECmdFormat::FLOAT },
{ GE_CMD_UNKNOWN_D1, "unknownd1", GECmdFormat::NONE },
{ GE_CMD_FRAMEBUFPIXFORMAT, "fbformat", GECmdFormat::TEX_FORMAT },
{ GE_CMD_CLEARMODE, "clearmode", GECmdFormat::CLEAR_MODE },
{ GE_CMD_SCISSOR1, "scissor1", GECmdFormat::X10_Y10 },
{ GE_CMD_SCISSOR2, "scissor2", GECmdFormat::X10_Y10 },
{ GE_CMD_MINZ, "minz", GECmdFormat::DATA16 },
{ GE_CMD_MAXZ, "maxz", GECmdFormat::DATA16 },
{ GE_CMD_COLORTEST, "ctestfunc", GECmdFormat::COLOR_TEST_FUNC },
{ GE_CMD_COLORREF, "ctestref", GECmdFormat::RGB },
{ GE_CMD_COLORTESTMASK, "ctestmask", GECmdFormat::RGB },
{ GE_CMD_ALPHATEST, "atest", GECmdFormat::ALPHA_TEST },
{ GE_CMD_STENCILTEST, "stest", GECmdFormat::ALPHA_TEST },
{ GE_CMD_STENCILOP, "stencilop", GECmdFormat::STENCIL_OP },
{ GE_CMD_ZTEST, "ztest", GECmdFormat::DEPTH_TEST_FUNC },
{ GE_CMD_BLENDMODE, "blendmode", GECmdFormat::BLEND_MODE },
{ GE_CMD_BLENDFIXEDA, "blendfixa", GECmdFormat::RGB },
{ GE_CMD_BLENDFIXEDB, "blendfixb", GECmdFormat::RGB },
{ GE_CMD_DITH0, "dither0", GECmdFormat::DITHER_ROW },
{ GE_CMD_DITH1, "dither1", GECmdFormat::DITHER_ROW },
{ GE_CMD_DITH2, "dither2", GECmdFormat::DITHER_ROW },
{ GE_CMD_DITH3, "dither3", GECmdFormat::DITHER_ROW },
{ GE_CMD_LOGICOP, "logicop", GECmdFormat::LOGIC_OP },
{ GE_CMD_ZWRITEDISABLE, "zwrite_off", GECmdFormat::FLAG },
{ GE_CMD_MASKRGB, "rgbmask_block", GECmdFormat::RGB },
{ GE_CMD_MASKALPHA, "swritemask_block", GECmdFormat::DATA8 },
{ GE_CMD_TRANSFERSTART, "transferstart_bpp", GECmdFormat::FLAG },
{ GE_CMD_TRANSFERSRCPOS, "transfersrcpos", GECmdFormat::X10_Y10 },
{ GE_CMD_TRANSFERDSTPOS, "transferdstpos", GECmdFormat::X10_Y10 },
{ GE_CMD_UNKNOWN_ED, "unknowned", GECmdFormat::NONE },
{ GE_CMD_TRANSFERSIZE, "transfersize", GECmdFormat::X10_Y10 },
{ GE_CMD_UNKNOWN_EF, "unknownef", GECmdFormat::NONE },
{ GE_CMD_VSCX, "immx", GECmdFormat::SUBPIXEL_COORD },
{ GE_CMD_VSCY, "immy", GECmdFormat::SUBPIXEL_COORD },
{ GE_CMD_VSCZ, "immz", GECmdFormat::DATA16 },
{ GE_CMD_VTCS, "imms", GECmdFormat::FLOAT },
{ GE_CMD_VTCT, "immt", GECmdFormat::FLOAT },
{ GE_CMD_VTCQ, "immq", GECmdFormat::FLOAT },
{ GE_CMD_VCV, "immrgb", GECmdFormat::RGB },
// TODO: Confirm if any other bits are used?
{ GE_CMD_VAP, "imma_prim", GECmdFormat::ALPHA_PRIM },
// TODO: Confirm it's 8 bit?
{ GE_CMD_VFC, "immfog", GECmdFormat::DATA8 },
{ GE_CMD_VSCV, "immrgb1", GECmdFormat::RGB },
{ GE_CMD_UNKNOWN_FA, "unknownfa", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_FB, "unknownfb", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_FC, "unknownfc", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_FD, "unknownfd", GECmdFormat::NONE },
{ GE_CMD_UNKNOWN_FE, "unknownfe", GECmdFormat::NONE },
{ GE_CMD_NOP_FF, "nopff", GECmdFormat::NONE },
};
static constexpr GECmdAlias geCmdAliases[] = {
{ GE_CMD_VADDR, { "vertexaddr" } },
{ GE_CMD_IADDR, { "indexaddr" } },
{ GE_CMD_BOUNDINGBOX, { "boundingbox", "boundtest" } },
{ GE_CMD_BJUMP, { "boundjump" } },
{ GE_CMD_BASE, { "baseaddr" } },
{ GE_CMD_VERTEXTYPE, { "vertextype" } },
{ GE_CMD_OFFSETADDR, { "offsetaddr" } },
{ GE_CMD_REGION2, { "region2" } },
{ GE_CMD_LIGHTINGENABLE, { "lightingenable", "lighting" } },
{ GE_CMD_LIGHTENABLE0, { "light0enable" } },
{ GE_CMD_LIGHTENABLE1, { "light1enable" } },
{ GE_CMD_LIGHTENABLE2, { "light2enable" } },
{ GE_CMD_LIGHTENABLE3, { "light3enable" } },
{ GE_CMD_DEPTHCLAMPENABLE, { "zclampenable", "depthclamp_on", "depthclampenable" } },
{ GE_CMD_CULLFACEENABLE, { "cullenable", "cullface_on", "cullfaceenable" } },
{ GE_CMD_TEXTUREMAPENABLE, { "texenable", "texture_on", "textureenable" } },
{ GE_CMD_FOGENABLE, { "fogenable" } },
{ GE_CMD_DITHERENABLE, { "ditherenable" } },
{ GE_CMD_ALPHABLENDENABLE, { "ablendenable", "alphablend_on", "alphablendenable" } },
{ GE_CMD_ALPHABLENDENABLE, { "atestenable", "alphatest_on", "alphatestenable" } },
{ GE_CMD_ZTESTENABLE, { "ztestenable", "depthtest_on", "depthtest_enable" } },
{ GE_CMD_STENCILTESTENABLE, { "stestenable", "stenciltest_on", "stenciltestenable" } },
{ GE_CMD_ANTIALIASENABLE, { "antialiasenable", "antialias" } },
{ GE_CMD_PATCHCULLENABLE, { "patchcullenable" } },
{ GE_CMD_COLORTESTENABLE, { "ctestenable", "colortest_on", "colortestenable" } },
{ GE_CMD_LOGICOPENABLE, { "logicopenable" } },
{ GE_CMD_BONEMATRIXNUMBER, { "bonematrixnum" } },
{ GE_CMD_BONEMATRIXDATA, { "bonematrixdata" } },
{ GE_CMD_MORPHWEIGHT0, { "morphweight0" } },
{ GE_CMD_MORPHWEIGHT1, { "morphweight1" } },
{ GE_CMD_MORPHWEIGHT2, { "morphweight2" } },
{ GE_CMD_MORPHWEIGHT3, { "morphweight3" } },
{ GE_CMD_MORPHWEIGHT4, { "morphweight4" } },
{ GE_CMD_MORPHWEIGHT5, { "morphweight5" } },
{ GE_CMD_MORPHWEIGHT6, { "morphweight6" } },
{ GE_CMD_MORPHWEIGHT7, { "morphweight7" } },
{ GE_CMD_PATCHDIVISION, { "patchdiv" } },
{ GE_CMD_PATCHFACING, { "patchreversenormal" } },
{ GE_CMD_WORLDMATRIXNUMBER, { "worldmatrixnum" } },
{ GE_CMD_WORLDMATRIXDATA, { "worldmatrixdata" } },
{ GE_CMD_VIEWMATRIXNUMBER, { "viewmatrixnum" } },
{ GE_CMD_VIEWMATRIXDATA, { "viewmatrixdata" } },
{ GE_CMD_PROJMATRIXNUMBER, { "projmatrixnum" } },
{ GE_CMD_PROJMATRIXDATA, { "projmatrixdata" } },
{ GE_CMD_TGENMATRIXNUMBER, { "texgenmatrixnum", "tgenmtxnum", "tgenmatrixnum" } },
{ GE_CMD_TGENMATRIXDATA, { "texgenmatrixdata", "tgenmtxdata", "tgenmatrixdata" } },
{ GE_CMD_VIEWPORTXSCALE, { "viewportxscale" } },
{ GE_CMD_VIEWPORTYSCALE, { "viewportyscale" } },
{ GE_CMD_VIEWPORTZSCALE, { "viewportzscale" } },
{ GE_CMD_VIEWPORTXCENTER, { "viewportxcenter" } },
{ GE_CMD_VIEWPORTYCENTER, { "viewportycenter" } },
{ GE_CMD_VIEWPORTZCENTER, { "viewportzcenter" } },
{ GE_CMD_SHADEMODE, { "shademode", "shading" } },
{ GE_CMD_REVERSENORMAL, { "reversenormal" } },
{ GE_CMD_MATERIALAMBIENT, { "materialambientrgb" } },
{ GE_CMD_MATERIALALPHA, { "materialambientalpha" } },
{ GE_CMD_MATERIALSPECULARCOEF, { "materialspecularcoef" } },
{ GE_CMD_AMBIENTCOLOR, { "ambientrgb" } },
{ GE_CMD_AMBIENTALPHA, { "ambientalpha" } },
{ GE_CMD_LIGHTMODE, { "lmode", "secondarycolor" } },
{ GE_CMD_LIGHTTYPE0, { "lighttype0" } },
{ GE_CMD_LIGHTTYPE1, { "lighttype1" } },
{ GE_CMD_LIGHTTYPE2, { "lighttype2" } },
{ GE_CMD_LIGHTTYPE3, { "lighttype3" } },
{ GE_CMD_FRAMEBUFPTR, { "framebufptr" } },
{ GE_CMD_FRAMEBUFWIDTH, { "fbwidth", "framebufstride", "framebufwidth" } },
{ GE_CMD_ZBUFPTR, { "depthbufptr" } },
{ GE_CMD_ZBUFWIDTH, { "zbwidth", "depthbufstride", "depthbufwidth" } },
{ GE_CMD_TEXBUFWIDTH0, { "texbufwidth0", "texstride0" } },
{ GE_CMD_TEXBUFWIDTH1, { "texbufwidth1", "texstride1" } },
{ GE_CMD_TEXBUFWIDTH2, { "texbufwidth2", "texstride2" } },
{ GE_CMD_TEXBUFWIDTH3, { "texbufwidth3", "texstride3" } },
{ GE_CMD_TEXBUFWIDTH4, { "texbufwidth4", "texstride4" } },
{ GE_CMD_TEXBUFWIDTH5, { "texbufwidth5", "texstride5" } },
{ GE_CMD_TEXBUFWIDTH6, { "texbufwidth6", "texstride6" } },
{ GE_CMD_TEXBUFWIDTH7, { "texbufwidth7", "texstride7" } },
{ GE_CMD_CLUTADDRUPPER, { "clutaddrupper" } },
{ GE_CMD_TEXSHADELS, { "texshadels" } },
{ GE_CMD_TEXWRAP, { "texwrap" } },
{ GE_CMD_FOGCOLOR, { "fogrgb" } },
{ GE_CMD_FRAMEBUFPIXFORMAT, { "framebufformat" } },
{ GE_CMD_CLEARMODE, { "clear" } },
{ GE_CMD_SCISSOR1, { "scissortl" } },
{ GE_CMD_SCISSOR2, { "scissorbr" } },
{ GE_CMD_COLORTEST, { "colortestfunc" } },
{ GE_CMD_COLORREF, { "colortestref" } },
{ GE_CMD_COLORTESTMASK, { "colortestmask" } },
{ GE_CMD_ALPHATEST, { "alphatest" } },
{ GE_CMD_STENCILTEST, { "stenciltest" } },
{ GE_CMD_ZTEST, { "depthtest" } },
{ GE_CMD_BLENDFIXEDA, { "blendfixsrc" } },
{ GE_CMD_BLENDFIXEDB, { "blendfixdst" } },
{ GE_CMD_ZWRITEDISABLE, { "depthwrite_off", "zwritedisable", "depthwritedisable" } },
{ GE_CMD_MASKRGB, { "rgbmask" } },
{ GE_CMD_MASKALPHA, { "swritemask", "amask", "amask_block" } },
{ GE_CMD_TRANSFERSTART, { "transferstart" } },
{ GE_CMD_VCV, { "immrgb0" } },
{ GE_CMD_VSCV, { "immsecondaryrgb" } },
};
bool GECmdInfoByName(const char *name, GECmdInfo &result) {
for (const GECmdInfo &info : geCmdInfo) {
if (strcasecmp(info.name, name) == 0) {
result = info;
return true;
}
}
for (const GECmdAlias &entry : geCmdAliases) {
for (const char *alias : entry.aliases) {
if (alias && strcasecmp(alias, name) == 0) {
result = GECmdInfoByCmd(entry.reg);
return true;
}
}
}
return false;
}
GECmdInfo GECmdInfoByCmd(GECommand reg) {
_assert_msg_((reg & 0xFF) == reg, "Invalid reg");
return geCmdInfo[reg & 0xFF];
}

View file

@ -0,0 +1,79 @@
// Copyright (c) 2022- 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 "GPU/ge_constants.h"
enum class GECmdFormat {
DATA, // Just regular data.
DATA16, // Data, but only 16 bits of it used.
DATA8, // 8 bits value (alpha, etc.)
NONE, // Should have no arguments or value.
RELATIVE_ADDR, // 24 bits added to base + offset.
PRIM, // 16 bits count, 3 bits type.
BEZIER, // 8 bits ucount, 8 bits vcount.
SPLINE, // 8 bits ucount, 8 bits vcount, 2 bits utype, 2 bits vtype.
JUMP, // Like RELATIVE_ADDR, but lower 2 bits ignored.
SIGNAL, // 16 bits data, 8 bits signal type.
HIGH_ADDR_ONLY, // 16 bits ignored, 8 bits for a high address.
LOW_ADDR_ONLY, // 24 bits low address.
LOW_ADDR, // 24 bits low address.
HIGH_ADDR, // 16 bits ignored, 8 bits for a high address.
VERTEX_TYPE, // 24-bits of vtype flags (complex.)
OFFSET_ADDR, // 24 bits become the high bits of offset.
X10_Y10, // 10 bits X, 10 bits Y.
FLAG, // 1 bit on/off.
BONE_NUM, // 7 bits number.
MATRIX_NUM, // 4 bits number.
FLOAT, // 24 bits float data.
PATCH_DIVISION, // 8 bits divisionu, 8 bits divisionv.
PATCH_PRIM, // 2 bits primitive type.
SUBPIXEL_COORD, // 4 bits subpixel, 12 bits integer value.
MATERIAL_UPDATE, // 3 bits colors to update.
RGB, // 24 bits RGB color.
LIGHT_TYPE, // 8 bits computation (2 bits), 2 bits type.
STRIDE, // 11 bits.
STRIDE_HIGH_ADDR, // 11 bits stride, 5 ignored, 8 bits high address.
TEX_SIZE, // 4 bits width power, 4 bits ignored, 4 bits height power.
TEX_MAP_MODE, // 8 bits mode (2 bits), 2 bits environment factor.
TEX_LIGHT_SRC, // 8 bits lightu (2 bits), 2 bits lightv.
TEX_MODE, // 8 bits swizzle (1 bit), 8 bits separate clut4 (1 bit), 3 bits max level.
TEX_FORMAT, // 4 bits format.
TEX_FILTER, // 3 bits minify mode, 5 bits ignored, 1 bit magnify mode.
TEX_CLAMP, // 1 bit clampu, 7 bits ignored, 1 bit clampv.
TEX_LEVEL_MODE, // 2 bits mode, 15 bits ignored, 4.4 (8 bits) bias.
TEX_FUNC, // 3 bits function, 5 bits ignored, 1 bit use alpha, 7 bits ignored, 1 bit double color.
CLUT_BLOCKS, // 6 bits block count.
CLUT_FORMAT, // 2 bits format, 5 bits shift, 1 bit ignored, 8 bits mask, 5 bits base.
CLEAR_MODE, // 1 bit on, 7 bits ignored, 3 bits aspect.
COLOR_TEST_FUNC, // 2 bits test function.
ALPHA_TEST, // 3 bits function, 5 bits ignored, 8 bits ref, 8 bits mask.
STENCIL_OP, // 8 bits sfail (3 bits), 8 bits zfail (3 bits), 8 bits zpass (3 bits.)
DEPTH_TEST_FUNC, // 3 bits function.
BLEND_MODE, // 4 bits srcfactor, 4 bits dstfactor, 3 bits equation.
DITHER_ROW, // 4 s.3.0 fixed point dither offsets.
LOGIC_OP, // 4 bits logic operation.
ALPHA_PRIM, // 8 bits alpha, 3 bits primitive type.
};
struct GECmdInfo {
GECommand reg;
const char *name;
GECmdFormat fmt;
};
bool GECmdInfoByName(const char *name, GECmdInfo &info);
GECmdInfo GECmdInfoByCmd(GECommand reg);

View file

@ -370,6 +370,7 @@
<ClInclude Include="D3D11\TextureCacheD3D11.h" />
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\Debugger.h" />
<ClInclude Include="Debugger\GECommandTable.h" />
<ClInclude Include="Debugger\Playback.h" />
<ClInclude Include="Debugger\Record.h" />
<ClInclude Include="Debugger\RecordFormat.h" />
@ -510,6 +511,7 @@
<ClCompile Include="D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\Debugger.cpp" />
<ClCompile Include="Debugger\GECommandTable.cpp" />
<ClCompile Include="Debugger\Playback.cpp" />
<ClCompile Include="Debugger\Record.cpp" />
<ClCompile Include="Debugger\Stepping.cpp" />

View file

@ -258,6 +258,9 @@
<ClInclude Include="Common\TextureShaderCommon.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Debugger\GECommandTable.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -506,6 +509,9 @@
<ClCompile Include="Common\TextureShaderCommon.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Debugger\GECommandTable.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<FxCompile Include="..\assets\shaders\tex_4xbrz.csh">

View file

@ -407,7 +407,7 @@ struct GPUgstate {
float getViewportYCenter() const { return getFloat24(viewportycenter); }
float getViewportZCenter() const { return getFloat24(viewportzcenter); }
// Fixed 16 point.
// Fixed 12.4 point.
int getOffsetX16() const { return offsetx & 0xFFFF; }
int getOffsetY16() const { return offsety & 0xFFFF; }
float getOffsetX() const { return (float)getOffsetX16() / 16.0f; }

View file

@ -413,6 +413,7 @@
<ClInclude Include="..\..\GPU\D3D11\TextureCacheD3D11.h" />
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
<ClInclude Include="..\..\GPU\Debugger\GECommandTable.h" />
<ClInclude Include="..\..\GPU\Debugger\Playback.h" />
<ClInclude Include="..\..\GPU\Debugger\Record.h" />
<ClInclude Include="..\..\GPU\Debugger\RecordFormat.h" />
@ -475,6 +476,7 @@
<ClCompile Include="..\..\GPU\D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
<ClCompile Include="..\..\GPU\Debugger\GECommandTable.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Playback.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Record.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp" />

View file

@ -32,6 +32,7 @@
<ClCompile Include="..\..\GPU\D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
<ClCompile Include="..\..\GPU\Debugger\GECommandTable.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Playback.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Record.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Stepping.cpp" />
@ -89,6 +90,7 @@
<ClInclude Include="..\..\GPU\D3D11\TextureCacheD3D11.h" />
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
<ClInclude Include="..\..\GPU\Debugger\GECommandTable.h" />
<ClInclude Include="..\..\GPU\Debugger\Playback.h" />
<ClInclude Include="..\..\GPU\Debugger\Record.h" />
<ClInclude Include="..\..\GPU\Debugger\RecordFormat.h" />

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"
@ -265,6 +267,17 @@ void CtrlDisplayListView::toggleBreakpoint()
SendMessage(GetParent(wnd),WM_GEDBG_TOGGLEPCBREAKPOINT,curAddress,0);
}
void CtrlDisplayListView::PromptBreakpointCond() {
std::string expression;
GPUBreakpoints::GetAddressBreakpointCond(curAddress, &expression);
if (!InputBox_GetString(GetModuleHandle(NULL), wnd, L"Expression", expression, expression))
return;
std::string error;
if (!GPUBreakpoints::SetAddressBreakpointCond(curAddress, expression, &error))
MessageBox(wnd, ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
}
void CtrlDisplayListView::onMouseDown(WPARAM wParam, LPARAM lParam, int button)
{
int y = HIWORD(lParam);
@ -296,6 +309,9 @@ void CtrlDisplayListView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)
{
if (button == 2)
{
HMENU menu = GetContextMenu(ContextMenuID::DISPLAYLISTVIEW);
EnableMenuItem(menu, ID_GEDBG_SETCOND, GPUBreakpoints::IsAddressBreakpoint(curAddress) ? MF_ENABLED : MF_GRAYED);
switch (TriggerContextMenu(ContextMenuID::DISPLAYLISTVIEW, wnd, ContextPoint::FromEvent(lParam)))
{
case ID_DISASM_GOTOINMEMORYVIEW:
@ -306,6 +322,9 @@ void CtrlDisplayListView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)
toggleBreakpoint();
redraw();
break;
case ID_GEDBG_SETCOND:
PromptBreakpointCond();
break;
case ID_DISASM_COPYINSTRUCTIONDISASM:
{
int space = 256 * (selectRangeEnd - selectRangeStart) / instructionSize;
@ -369,15 +388,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;
}

View file

@ -30,6 +30,8 @@ class CtrlDisplayListView
} pixelPositions;
void toggleBreakpoint();
void PromptBreakpointCond();
public:
CtrlDisplayListView(HWND _wnd);
~CtrlDisplayListView();

View file

@ -21,7 +21,7 @@
#include <string>
#include <vector>
#include <windowsx.h>
#include "Common/CommonWindows.h"
#include <commctrl.h>
#include "Common/Data/Convert/ColorConv.h"
@ -1114,7 +1114,13 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
bool temp;
bool isBreak = IsAddressBreakpoint(pc, temp);
if (isBreak && !temp) {
RemoveAddressBreakpoint(pc);
if (GetAddressBreakpointCond(pc, nullptr)) {
int ret = MessageBox(m_hDlg, L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
if (ret == IDYES)
RemoveAddressBreakpoint(pc);
} else {
RemoveAddressBreakpoint(pc);
}
} else {
AddAddressBreakpoint(pc);
}

View file

@ -18,8 +18,9 @@
#include <commctrl.h>
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/Log.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Windows/resource.h"
#include "Windows/InputBox.h"
@ -900,7 +901,13 @@ void CtrlStateValues::OnDoubleClick(int row, int column) {
const auto info = rows_[row];
if (column == STATEVALUES_COL_BREAKPOINT) {
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
bool proceed = true;
if (IsCmdBreakpoint(info.cmd)) {
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
proceed = ret == IDYES;
}
if (proceed)
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
return;
}
@ -960,6 +967,7 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_STATE);
SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
EnableMenuItem(subMenu, ID_GEDBG_SETCOND, GPUBreakpoints::IsCmdBreakpoint(info.cmd) ? MF_ENABLED : MF_GRAYED);
// Ehh, kinda ugly.
if (!watchList.empty() && rows_ == &watchList[0]) {
@ -975,8 +983,19 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
switch (TriggerContextMenu(ContextMenuID::GEDBG_STATE, GetHandle(), ContextPoint::FromClient(point)))
{
case ID_DISASM_TOGGLEBREAKPOINT:
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
case ID_DISASM_TOGGLEBREAKPOINT: {
bool proceed = true;
if (IsCmdBreakpoint(info.cmd)) {
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
proceed = ret == IDYES;
}
if (proceed)
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
break;
}
case ID_GEDBG_SETCOND:
PromptBreakpointCond(info);
break;
case ID_DISASM_COPYINSTRUCTIONHEX: {
@ -1043,6 +1062,24 @@ bool CtrlStateValues::RowValuesChanged(int row) {
return false;
}
void CtrlStateValues::PromptBreakpointCond(const TabStateRow &info) {
std::string expression;
GPUBreakpoints::GetCmdBreakpointCond(info.cmd, &expression);
if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression))
return;
std::string error;
if (!GPUBreakpoints::SetCmdBreakpointCond(info.cmd, expression, &error)) {
MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
} else {
if (info.otherCmd)
GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd, expression, &error);
if (info.otherCmd2)
GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd2, expression, &error);
}
}
TabStateValues::TabStateValues(const TabStateRow *rows, int rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent)
: Dialog(dialogID, _hInstance, _hParent) {
values = new CtrlStateValues(rows, rowCount, GetDlgItem(m_hDlg, IDC_GEDBG_VALUES));

View file

@ -47,6 +47,7 @@ protected:
private:
bool RowValuesChanged(int row);
void SetCmdValue(u32 op);
void PromptBreakpointCond(const TabStateRow &info);
const TabStateRow *rows_;
int rowCount_;

View file

@ -17,6 +17,7 @@
#include <commctrl.h>
#include "Common/CommonTypes.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/StringUtils.h"
#include "Core/System.h"
#include "Windows/resource.h"
@ -550,23 +551,50 @@ static constexpr MatrixCmdPair matrixCmds[] = {
{ MATRIXLIST_ROW_COUNT, GE_CMD_NOP },
};
void CtrlMatrixList::ToggleBreakpoint(int row) {
static const MatrixCmdPair *FindCmdPair(int row) {
for (int i = 0; i < ARRAY_SIZE(matrixCmds) - 1; ++i) {
if (row < matrixCmds[i].row || row >= matrixCmds[i + 1].row)
continue;
// Okay, this command is in range. Toggle the actual breakpoint.
auto &info = matrixCmds[i];
bool state = !GPUBreakpoints::IsCmdBreakpoint(info.cmd);
if (state)
GPUBreakpoints::AddCmdBreakpoint(info.cmd);
else
GPUBreakpoints::RemoveCmdBreakpoint(info.cmd);
for (int r = matrixCmds[i].row; r < matrixCmds[i + 1].row; ++r) {
SetItemState(r, state ? 1 : 0);
}
return &matrixCmds[i];
}
return nullptr;
}
void CtrlMatrixList::ToggleBreakpoint(int row) {
const MatrixCmdPair *info = FindCmdPair(row);
if (!info)
return;
// Okay, this command is in range. Toggle the actual breakpoint.
bool state = !GPUBreakpoints::IsCmdBreakpoint(info->cmd);
if (state) {
GPUBreakpoints::AddCmdBreakpoint(info->cmd);
} else {
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
if (ret != IDYES)
return;
GPUBreakpoints::RemoveCmdBreakpoint(info->cmd);
}
for (int r = info->row; r < (info + 1)->row; ++r) {
SetItemState(r, state ? 1 : 0);
}
}
void CtrlMatrixList::PromptBreakpointCond(int row) {
const MatrixCmdPair *info = FindCmdPair(row);
if (!info)
return;
std::string expression;
GPUBreakpoints::GetCmdBreakpointCond(info->cmd, &expression);
if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression))
return;
std::string error;
if (!GPUBreakpoints::SetCmdBreakpointCond(info->cmd, expression, &error))
MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
}
void CtrlMatrixList::OnDoubleClick(int row, int column) {
@ -624,18 +652,24 @@ void CtrlMatrixList::OnDoubleClick(int row, int column) {
void CtrlMatrixList::OnRightClick(int row, int column, const POINT &point) {
if (row >= GetRowCount())
return;
const MatrixCmdPair *info = FindCmdPair(row);
POINT screenPt(point);
ClientToScreen(GetHandle(), &screenPt);
HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_MATRIX);
SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
EnableMenuItem(subMenu, ID_GEDBG_SETCOND, info && GPUBreakpoints::IsCmdBreakpoint(info->cmd) ? MF_ENABLED : MF_GRAYED);
switch (TriggerContextMenu(ContextMenuID::GEDBG_MATRIX, GetHandle(), ContextPoint::FromClient(point))) {
case ID_DISASM_TOGGLEBREAKPOINT:
ToggleBreakpoint(row);
break;
case ID_GEDBG_SETCOND:
PromptBreakpointCond(row);
break;
case ID_DISASM_COPYINSTRUCTIONDISASM:
{
float val;

View file

@ -90,6 +90,7 @@ private:
bool GetValue(const GPUgstate &state, int row, int col, float &val);
bool ColChanged(const GPUgstate &lastState, const GPUgstate &state, int row, int col);
void ToggleBreakpoint(int row);
void PromptBreakpointCond(int row);
};
class TabMatrices : public Dialog {

View file

@ -763,6 +763,7 @@ BEGIN
MENUITEM "Jump to Cursor", ID_DISASM_SETPCTOHERE
MENUITEM "Set Stall", ID_GEDBG_SETSTALLADDR
MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT
MENUITEM "Set Breakpoint Condition", ID_GEDBG_SETCOND
MENUITEM SEPARATOR
MENUITEM "Go to Displaylist PC", ID_GEDBG_GOTOPC
MENUITEM "Go to Address...", ID_GEDBG_GOTOADDR
@ -777,6 +778,7 @@ BEGIN
MENUITEM "Copy Entire Tab (Formatted)",ID_GEDBG_COPYALL
MENUITEM SEPARATOR
MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT
MENUITEM "Set Breakpoint Condition", ID_GEDBG_SETCOND
MENUITEM "Add Watch", ID_GEDBG_WATCH
END
POPUP "gepreviewoptions"
@ -792,6 +794,7 @@ BEGIN
MENUITEM "Copy Entire Tab (Formatted)",ID_GEDBG_COPYALL
MENUITEM SEPARATOR
MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT
MENUITEM "Set Breakpoint Condition", ID_GEDBG_SETCOND
END
POPUP "getaboptions"
BEGIN

View file

@ -332,6 +332,7 @@
#define ID_GEDBG_SHOWONRIGHT 40220
#define ID_GEDBG_SHOWONTOPRIGHT 40221
#define IDC_GEDBG_STEPVSYNC 40222
#define ID_GEDBG_SETCOND 40223
// Dummy option to let the buffered rendering hotkey cycle through all the options.
@ -345,7 +346,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 256
#define _APS_NEXT_COMMAND_VALUE 40223
#define _APS_NEXT_COMMAND_VALUE 40224
#define _APS_NEXT_CONTROL_VALUE 1202
#define _APS_NEXT_SYMED_VALUE 101
#endif

View file

@ -351,6 +351,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/GPU/Common/VertexShaderGenerator.cpp \
$(SRC)/GPU/Debugger/Breakpoints.cpp \
$(SRC)/GPU/Debugger/Debugger.cpp \
$(SRC)/GPU/Debugger/GECommandTable.cpp \
$(SRC)/GPU/Debugger/Playback.cpp \
$(SRC)/GPU/Debugger/Record.cpp \
$(SRC)/GPU/Debugger/Stepping.cpp \

View file

@ -333,6 +333,7 @@ SOURCES_CXX += \
$(COMMONDIR)/Data/Convert/ColorConv.cpp \
$(GPUDIR)/Debugger/Breakpoints.cpp \
$(GPUDIR)/Debugger/Debugger.cpp \
$(GPUDIR)/Debugger/GECommandTable.cpp \
$(GPUDIR)/Debugger/Playback.cpp \
$(GPUDIR)/Debugger/Record.cpp \
$(GPUDIR)/Debugger/Stepping.cpp \