mirror of
https://github.com/izzy2lost/ppsspp.git
synced 2026-03-10 12:43:04 -07:00
This means a conditional breakpoint can now use, for example, vtype.through == 1 to check the through mode flag. No constants, but this makes it easier to get right. Would be nice to have an autocomplete of the field names, of course...
866 lines
28 KiB
C++
866 lines
28 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,
|
|
|
|
FIELD_START = 0x1000,
|
|
FIELD_END = 0xFF000,
|
|
};
|
|
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" },
|
|
};
|
|
|
|
enum class GECmdField : uint8_t {
|
|
DATA, // Alias for the entire data.
|
|
LOW_FLAG,
|
|
LOW_U2,
|
|
LOW_U4,
|
|
LOW_U7,
|
|
LOW_U8,
|
|
LOW_U10,
|
|
LOW_U10_P1,
|
|
LOW_U11,
|
|
LOW_U16,
|
|
MID_U8,
|
|
MID_U10, // At 10, 10 bits.
|
|
MID_U10_P1, // At 10, 10 bits (add 1 to value.)
|
|
TOP_U8,
|
|
FLAG_AFTER_1, // At 1, 1 bit.
|
|
FLAG_AFTER_2, // At 2, 1 bit.
|
|
FLAG_AFTER_8, // At 8, 1 bit.
|
|
FLAG_AFTER_9, // At 9, 1 bit.
|
|
FLAG_AFTER_10, // At 10, 1 bit.
|
|
FLAG_AFTER_11, // At 11, 1 bit.
|
|
FLAG_AFTER_16, // At 16, 1 bit.
|
|
FLAG_AFTER_18, // At 18, 1 bit.
|
|
FLAG_AFTER_19, // At 19, 1 bit.
|
|
FLAG_AFTER_20, // At 20, 1 bit.
|
|
FLAG_AFTER_21, // At 21, 1 bit.
|
|
FLAG_AFTER_22, // At 22, 1 bit.
|
|
FLAG_AFTER_23, // At 23, 1 bit.
|
|
U2_AFTER_8, // At 8, 2 bits.
|
|
U3_AFTER_16, // At 16, 3 bits.
|
|
U12_AFTER_4, // At 4, 12 bits.
|
|
PRIM_TYPE, // At 16, 3 bits.
|
|
SPLINE_UTYPE, // At 16, 2 bits.
|
|
SPLINE_VTYPE, // At 18, 2 bits.
|
|
SIGNAL_TYPE, // At 16, 8 bits.
|
|
VTYPE_TC, // At 0, 2 bits.
|
|
VTYPE_COL, // At 2, 3 bits.
|
|
VTYPE_NRM, // At 5, 2 bits.
|
|
VTYPE_POS, // At 7, 2 bits.
|
|
VTYPE_WEIGHTTYPE, // At 9, 2 bits.
|
|
VTYPE_INDEX, // At 11, 2 bits.
|
|
VTYPE_WEIGHTCOUNT, // At 14, 3 bits.
|
|
VTYPE_MORPHCOUNT, // At 18, 3 bits.
|
|
PATCH_PRIM_TYPE, // At 0, 2 bits.
|
|
LIGHT_COMP, // At 0, 2 bits.
|
|
LIGHT_TYPE, // At 8, 2 bits.
|
|
LIGHT_TYPE_SPECULAR, // 1 if comp is 1 (but not 3.)
|
|
HIGH_ADDR, // At 16, 8 bits moved left to top 8 bits.
|
|
TEX_W, // At 0, 4 bits - 1 to this power.
|
|
TEX_H, // At 8, 4 bits - 1 to this power.
|
|
UVGEN_TYPE, // At 0, 2 bits.
|
|
UVGEN_PROJ, // At 8, 2 bits.
|
|
TEX_FORMAT, // At 0, 4 bits.
|
|
TEX_MINFILTER, // At 0, 3 bits.
|
|
TEX_MAGFILTER, // At 8, 1 bit.
|
|
TEX_LEVEL_MODE, // At 0, 2 bits.
|
|
LOW_U12_4_FLOAT, // At 0, 12.4 converted to float.
|
|
HIGH_S4_4_FLOAT, // At 16, s.3.4 converted to float.
|
|
TEX_FUNC, // At 0, 3 bits.
|
|
CLUT_BYTES, // At 0, 6 bits, multiplied by 8.
|
|
CLUT_FORMAT, // At 0, 2 bits.
|
|
CLUT_SHIFT, // At 2, 5 bits.
|
|
CLUT_OFFSET, // At 16, 5 bits, multiplied by 16.
|
|
COMPARE_FUNC2, // At 0, 2 bits.
|
|
COMPARE_FUNC3, // At 0, 3 bits.
|
|
STENCIL_OP_AT_0, // At 0, 3 bits.
|
|
STENCIL_OP_AT_8, // At 8, 3 bits.
|
|
STENCIL_OP_AT_16, // At 16, 3 bits.
|
|
BLEND_SRC, // At 0, 4 bits.
|
|
BLEND_DST, // At 4, 4 bits.
|
|
BLEND_EQUATION, // At 8, 3 bits.
|
|
LOGIC_OP, // At 0, 4 bits.
|
|
};
|
|
|
|
struct FieldName {
|
|
GECmdFormat fmt;
|
|
GECmdField field;
|
|
const char *name;
|
|
};
|
|
|
|
static constexpr FieldName fieldNames[] = {
|
|
{ GECmdFormat::PRIM, GECmdField::LOW_U16, "count" },
|
|
{ GECmdFormat::PRIM, GECmdField::PRIM_TYPE, "type" },
|
|
{ GECmdFormat::BEZIER, GECmdField::LOW_U8, "ucount" },
|
|
{ GECmdFormat::BEZIER, GECmdField::LOW_U8, "u" },
|
|
{ GECmdFormat::BEZIER, GECmdField::MID_U8, "vcount" },
|
|
{ GECmdFormat::BEZIER, GECmdField::MID_U8, "v" },
|
|
{ GECmdFormat::SPLINE, GECmdField::LOW_U8, "ucount" },
|
|
{ GECmdFormat::SPLINE, GECmdField::LOW_U8, "u" },
|
|
{ GECmdFormat::SPLINE, GECmdField::MID_U8, "vcount" },
|
|
{ GECmdFormat::SPLINE, GECmdField::MID_U8, "v" },
|
|
{ GECmdFormat::SPLINE, GECmdField::SPLINE_UTYPE, "utype" },
|
|
{ GECmdFormat::SPLINE, GECmdField::SPLINE_VTYPE, "vtype" },
|
|
{ GECmdFormat::SIGNAL, GECmdField::LOW_U16, "data" },
|
|
{ GECmdFormat::SIGNAL, GECmdField::SIGNAL_TYPE, "type" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_TC, "texcoord" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_TC, "tc" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_COL, "col" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_COL, "color" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_NRM, "normal" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_POS, "pos" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_POS, "position" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTTYPE, "weighttype" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTTYPE, "weight" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_INDEX, "index" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTCOUNT, "weightcount" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_MORPHCOUNT, "morphcount" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::FLAG_AFTER_23, "through" },
|
|
{ GECmdFormat::VERTEX_TYPE, GECmdField::FLAG_AFTER_23, "throughmode" },
|
|
{ GECmdFormat::X10_Y10, GECmdField::LOW_U10, "x" },
|
|
{ GECmdFormat::X10_Y10, GECmdField::MID_U10, "y" },
|
|
{ GECmdFormat::X10_Y10, GECmdField::LOW_U10_P1, "w" },
|
|
{ GECmdFormat::X10_Y10, GECmdField::MID_U10_P1, "h" },
|
|
{ GECmdFormat::FLAG, GECmdField::LOW_FLAG, "flag" },
|
|
{ GECmdFormat::BONE_NUM, GECmdField::LOW_U7, "num" },
|
|
{ GECmdFormat::MATRIX_NUM, GECmdField::LOW_U4, "num" },
|
|
{ GECmdFormat::FLOAT, GECmdField::DATA, "data" },
|
|
{ GECmdFormat::PATCH_DIVISION, GECmdField::LOW_U8, "u" },
|
|
{ GECmdFormat::PATCH_DIVISION, GECmdField::MID_U8, "v" },
|
|
{ GECmdFormat::PATCH_PRIM, GECmdField::PATCH_PRIM_TYPE, "type" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "frac" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "sub" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "subpixels" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::U12_AFTER_4, "int" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::U12_AFTER_4, "integer" },
|
|
{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U12_4_FLOAT, "pixels" },
|
|
{ GECmdFormat::MATERIAL_UPDATE, GECmdField::LOW_FLAG, "ambient" },
|
|
{ GECmdFormat::MATERIAL_UPDATE, GECmdField::FLAG_AFTER_1, "diffuse" },
|
|
{ GECmdFormat::MATERIAL_UPDATE, GECmdField::FLAG_AFTER_2, "specular" },
|
|
{ GECmdFormat::RGB, GECmdField::LOW_U8, "r" },
|
|
{ GECmdFormat::RGB, GECmdField::LOW_U8, "red" },
|
|
{ GECmdFormat::RGB, GECmdField::MID_U8, "g" },
|
|
{ GECmdFormat::RGB, GECmdField::MID_U8, "green" },
|
|
{ GECmdFormat::RGB, GECmdField::TOP_U8, "b" },
|
|
{ GECmdFormat::RGB, GECmdField::TOP_U8, "blue" },
|
|
{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_COMP, "computation" },
|
|
{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_TYPE, "type" },
|
|
{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_TYPE_SPECULAR, "specular" },
|
|
{ GECmdFormat::STRIDE, GECmdField::LOW_U11, "stride" },
|
|
{ GECmdFormat::STRIDE_HIGH_ADDR, GECmdField::HIGH_ADDR, "highaddr" },
|
|
{ GECmdFormat::HIGH_ADDR, GECmdField::HIGH_ADDR, "highaddr" },
|
|
{ GECmdFormat::HIGH_ADDR_ONLY, GECmdField::HIGH_ADDR, "highaddr" },
|
|
{ GECmdFormat::TEX_SIZE, GECmdField::TEX_W, "w" },
|
|
{ GECmdFormat::TEX_SIZE, GECmdField::TEX_W, "width" },
|
|
{ GECmdFormat::TEX_SIZE, GECmdField::TEX_H, "h" },
|
|
{ GECmdFormat::TEX_SIZE, GECmdField::TEX_H, "height" },
|
|
{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_TYPE, "type" },
|
|
{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_TYPE, "uv" },
|
|
{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_PROJ, "proj" },
|
|
{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_PROJ, "factor" },
|
|
{ GECmdFormat::TEX_LIGHT_SRC, GECmdField::LOW_U2, "u" },
|
|
{ GECmdFormat::TEX_LIGHT_SRC, GECmdField::U2_AFTER_8, "v" },
|
|
{ GECmdFormat::TEX_MODE, GECmdField::LOW_FLAG, "swizzle" },
|
|
{ GECmdFormat::TEX_MODE, GECmdField::FLAG_AFTER_8, "separateclut" },
|
|
{ GECmdFormat::TEX_MODE, GECmdField::FLAG_AFTER_8, "separate" },
|
|
{ GECmdFormat::TEX_MODE, GECmdField::U3_AFTER_16, "maxlevel" },
|
|
{ GECmdFormat::TEX_MODE, GECmdField::U3_AFTER_16, "level" },
|
|
{ GECmdFormat::TEX_FORMAT, GECmdField::TEX_FORMAT, "format" },
|
|
{ GECmdFormat::TEX_FORMAT, GECmdField::TEX_FORMAT, "fmt" },
|
|
{ GECmdFormat::TEX_FORMAT, GECmdField::FLAG_AFTER_2, "indexed" },
|
|
{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MINFILTER, "min" },
|
|
{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MINFILTER, "minify" },
|
|
{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MAGFILTER, "mag" },
|
|
{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MAGFILTER, "magnify" },
|
|
{ GECmdFormat::TEX_CLAMP, GECmdField::LOW_FLAG, "s" },
|
|
{ GECmdFormat::TEX_CLAMP, GECmdField::FLAG_AFTER_8, "t" },
|
|
{ GECmdFormat::TEX_LEVEL_MODE, GECmdField::TEX_LEVEL_MODE, "mode" },
|
|
{ GECmdFormat::TEX_LEVEL_MODE, GECmdField::HIGH_S4_4_FLOAT, "bias" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::TEX_FUNC, "func" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::TEX_FUNC, "function" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_8, "alpha" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_8, "a" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_16, "double" },
|
|
{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_16, "doubling" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_FORMAT, "format" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_FORMAT, "fmt" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_SHIFT, "shift" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::MID_U8, "mask" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_OFFSET, "offset" },
|
|
{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_OFFSET, "base" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "on" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "enable" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "flag" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_8, "color" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_9, "alpha" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_9, "stencil" },
|
|
{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_10, "depth" },
|
|
{ GECmdFormat::COLOR_TEST_FUNC, GECmdField::COMPARE_FUNC2, "func" },
|
|
{ GECmdFormat::ALPHA_TEST, GECmdField::COMPARE_FUNC3, "func" },
|
|
{ GECmdFormat::ALPHA_TEST, GECmdField::MID_U8, "ref" },
|
|
{ GECmdFormat::ALPHA_TEST, GECmdField::MID_U8, "reference" },
|
|
{ GECmdFormat::ALPHA_TEST, GECmdField::TOP_U8, "mask" },
|
|
{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_0, "sfail" },
|
|
{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_8, "zfail" },
|
|
{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_16, "zpass" },
|
|
{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_16, "pass" },
|
|
{ GECmdFormat::DEPTH_TEST_FUNC, GECmdField::COMPARE_FUNC3, "func" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_SRC, "src" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_SRC, "srcfactor" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dst" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dstfactor" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dest" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "destfactor" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "eq" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "equation" },
|
|
{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "mode" },
|
|
{ GECmdFormat::LOGIC_OP, GECmdField::LOGIC_OP, "op" },
|
|
{ GECmdFormat::LOGIC_OP, GECmdField::LOGIC_OP, "mode" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::LOW_U8, "alpha" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::PRIM_TYPE, "type" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::PRIM_TYPE, "prim" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_11, "antialias" },
|
|
// TODO: Clip bits?
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_18, "shading" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_18, "gouraud" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_19, "cull" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_20, "cullccw" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_21, "tex" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_22, "fog" },
|
|
{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_23, "dither" },
|
|
};
|
|
|
|
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:
|
|
bool parseFieldReference(const char *ref, const char *field, uint32_t &referenceIndex);
|
|
uint32_t getFieldValue(GECmdFormat fmt, GECmdField field, uint32_t value);
|
|
ExpressionType getFieldType(GECmdFormat fmt, GECmdField field);
|
|
|
|
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;
|
|
}
|
|
|
|
char *dot = strchr(str, '.');
|
|
if (dot != nullptr) {
|
|
*dot = '\0';
|
|
bool success = parseFieldReference(str, dot + 1, referenceIndex);
|
|
*dot = '.';
|
|
if (success)
|
|
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::parseFieldReference(const char *ref, const char *field, uint32_t &referenceIndex) {
|
|
GECmdInfo info;
|
|
if (!GECmdInfoByName(ref, info)) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto &entry : fieldNames) {
|
|
if (entry.fmt == info.fmt && strcasecmp(field, entry.name) == 0) {
|
|
referenceIndex = (info.reg << 12) | (uint32_t)entry.field;
|
|
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) {
|
|
GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex)).fmt;
|
|
uint32_t value = state.cmdmem[referenceIndex];
|
|
if (fmt == GECmdFormat::FLOAT)
|
|
return value << 8;
|
|
return value & 0x00FFFFFF;
|
|
}
|
|
|
|
if (referenceIndex >= (uint32_t)GEReferenceIndex::FIELD_START && referenceIndex <= (uint32_t)GEReferenceIndex::FIELD_END) {
|
|
uint32_t value = state.cmdmem[referenceIndex >> 12] & 0x00FFFFFF;
|
|
GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex >> 12)).fmt;
|
|
return getFieldValue(fmt, GECmdField(referenceIndex & 0xFF), value);
|
|
}
|
|
|
|
// 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:
|
|
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:
|
|
case GEReferenceIndex::FIELD_START:
|
|
case GEReferenceIndex::FIELD_END:
|
|
// Shouldn't have gotten here.
|
|
break;
|
|
}
|
|
|
|
_assert_msg_(false, "Invalid reference index");
|
|
return 0;
|
|
}
|
|
|
|
uint32_t GEExpressionFunctions::getFieldValue(GECmdFormat fmt, GECmdField field, uint32_t value) {
|
|
switch (field) {
|
|
case GECmdField::DATA:
|
|
return value;
|
|
case GECmdField::LOW_FLAG:
|
|
return value & 1;
|
|
case GECmdField::LOW_U2:
|
|
return value & 3;
|
|
case GECmdField::LOW_U4:
|
|
return value & 0xF;
|
|
case GECmdField::LOW_U7:
|
|
return value & 0x7F;
|
|
case GECmdField::LOW_U8:
|
|
return value & 0xFF;
|
|
case GECmdField::LOW_U10:
|
|
return value & 0x03FF;
|
|
case GECmdField::LOW_U10_P1:
|
|
return (value & 0x03FF) + 1;
|
|
case GECmdField::LOW_U11:
|
|
return value & 0x07FF;
|
|
case GECmdField::LOW_U16:
|
|
return value & 0xFFFF;
|
|
case GECmdField::MID_U8:
|
|
return (value >> 8) & 0xFF;
|
|
case GECmdField::MID_U10:
|
|
return (value >> 10) & 0x03FF;
|
|
case GECmdField::MID_U10_P1:
|
|
return ((value >> 10) & 0x03FF) + 1;
|
|
case GECmdField::TOP_U8:
|
|
return (value >> 16) & 0xFF;
|
|
case GECmdField::FLAG_AFTER_1:
|
|
return (value >> 1) & 1;
|
|
case GECmdField::FLAG_AFTER_2:
|
|
return (value >> 2) & 1;
|
|
case GECmdField::FLAG_AFTER_8:
|
|
return (value >> 8) & 1;
|
|
case GECmdField::FLAG_AFTER_9:
|
|
return (value >> 8) & 1;
|
|
case GECmdField::FLAG_AFTER_10:
|
|
return (value >> 10) & 1;
|
|
case GECmdField::FLAG_AFTER_11:
|
|
return (value >> 11) & 1;
|
|
case GECmdField::FLAG_AFTER_16:
|
|
return (value >> 16) & 1;
|
|
case GECmdField::FLAG_AFTER_18:
|
|
return (value >> 18) & 1;
|
|
case GECmdField::FLAG_AFTER_19:
|
|
return (value >> 19) & 1;
|
|
case GECmdField::FLAG_AFTER_20:
|
|
return (value >> 20) & 1;
|
|
case GECmdField::FLAG_AFTER_21:
|
|
return (value >> 21) & 1;
|
|
case GECmdField::FLAG_AFTER_22:
|
|
return (value >> 22) & 1;
|
|
case GECmdField::FLAG_AFTER_23:
|
|
return (value >> 23) & 1;
|
|
case GECmdField::U2_AFTER_8:
|
|
return (value >> 8) & 3;
|
|
case GECmdField::U3_AFTER_16:
|
|
return (value >> 16) & 7;
|
|
case GECmdField::U12_AFTER_4:
|
|
return (value >> 4) & 0x0FFF;
|
|
|
|
// Below here are "typed" values, maybe they'll be exposed differently than integers someday.
|
|
|
|
case GECmdField::PRIM_TYPE:
|
|
return (value >> 16) & 7;
|
|
case GECmdField::SPLINE_UTYPE:
|
|
return (value >> 16) & 3;
|
|
case GECmdField::SPLINE_VTYPE:
|
|
return (value >> 18) & 3;
|
|
case GECmdField::SIGNAL_TYPE:
|
|
return (value >> 16) & 0xFF;
|
|
case GECmdField::VTYPE_TC:
|
|
return value & 3;
|
|
case GECmdField::VTYPE_COL:
|
|
return (value >> 2) & 7;
|
|
case GECmdField::VTYPE_NRM:
|
|
return (value >> 5) & 3;
|
|
case GECmdField::VTYPE_POS:
|
|
return (value >> 7) & 3;
|
|
case GECmdField::VTYPE_WEIGHTTYPE:
|
|
return (value >> 9) & 3;
|
|
case GECmdField::VTYPE_INDEX:
|
|
return (value >> 11) & 3;
|
|
case GECmdField::VTYPE_WEIGHTCOUNT:
|
|
return ((value >> 14) & 7) + 1;
|
|
case GECmdField::VTYPE_MORPHCOUNT:
|
|
return ((value >> 18) & 7) + 1;
|
|
case GECmdField::PATCH_PRIM_TYPE:
|
|
return value & 3;
|
|
case GECmdField::LIGHT_COMP:
|
|
return value & 3;
|
|
case GECmdField::LIGHT_TYPE:
|
|
return (value >> 8) & 3;
|
|
case GECmdField::LIGHT_TYPE_SPECULAR:
|
|
return (value & 3) == 1;
|
|
case GECmdField::HIGH_ADDR:
|
|
return (value << 8) & 0xFF000000;
|
|
case GECmdField::TEX_W:
|
|
return 1 << (value & 0xF);
|
|
case GECmdField::TEX_H:
|
|
return 1 << ((value >> 8) & 0xF);
|
|
case GECmdField::UVGEN_TYPE:
|
|
return value & 3;
|
|
case GECmdField::UVGEN_PROJ:
|
|
return (value >> 8) & 3;
|
|
case GECmdField::TEX_FORMAT:
|
|
return value & 0xF;
|
|
case GECmdField::TEX_MINFILTER:
|
|
return value & 7;
|
|
case GECmdField::TEX_MAGFILTER:
|
|
return (value >> 8) & 1;
|
|
case GECmdField::TEX_LEVEL_MODE:
|
|
return value & 3;
|
|
case GECmdField::LOW_U12_4_FLOAT:
|
|
{
|
|
float f = (value & 0xFFFF) / 16.0f;
|
|
uint32_t result;
|
|
memcpy(&result, &f, sizeof(result));
|
|
return result;
|
|
}
|
|
case GECmdField::HIGH_S4_4_FLOAT: // At 16, s.3.4 converted to float.
|
|
{
|
|
int value8 = (int)(s8)((value >> 16) & 0xFF);
|
|
float f = value8 / 16.0f;
|
|
uint32_t result;
|
|
memcpy(&result, &f, sizeof(result));
|
|
return result;
|
|
}
|
|
case GECmdField::TEX_FUNC:
|
|
return value & 7;
|
|
case GECmdField::CLUT_BYTES:
|
|
return (value & 0x3F) * 8;
|
|
case GECmdField::CLUT_FORMAT:
|
|
return value & 3;
|
|
case GECmdField::CLUT_SHIFT:
|
|
return (value >> 2) & 0x1F;
|
|
case GECmdField::CLUT_OFFSET:
|
|
return ((value >> 16) & 0x1F) << 4;
|
|
case GECmdField::COMPARE_FUNC2:
|
|
return value & 3;
|
|
case GECmdField::COMPARE_FUNC3:
|
|
return value & 7;
|
|
case GECmdField::STENCIL_OP_AT_0:
|
|
return value & 7;
|
|
case GECmdField::STENCIL_OP_AT_8:
|
|
return (value >> 8) & 7;
|
|
case GECmdField::STENCIL_OP_AT_16:
|
|
return (value >> 16) & 7;
|
|
case GECmdField::BLEND_SRC:
|
|
return value & 0xF;
|
|
case GECmdField::BLEND_DST:
|
|
return (value >> 4) & 0xF;
|
|
case GECmdField::BLEND_EQUATION:
|
|
return (value >> 8) & 7;
|
|
case GECmdField::LOGIC_OP:
|
|
return value & 0xF;
|
|
}
|
|
|
|
_assert_msg_(false, "Invalid field type");
|
|
return 0;
|
|
}
|
|
|
|
ExpressionType GEExpressionFunctions::getReferenceType(uint32_t referenceIndex) {
|
|
if (referenceIndex < 0x100) {
|
|
GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex)).fmt;
|
|
if (fmt == GECmdFormat::FLOAT)
|
|
return EXPR_TYPE_FLOAT;
|
|
return EXPR_TYPE_UINT;
|
|
}
|
|
|
|
if (referenceIndex >= (uint32_t)GEReferenceIndex::FIELD_START && referenceIndex <= (uint32_t)GEReferenceIndex::FIELD_END) {
|
|
GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex >> 12)).fmt;
|
|
return getFieldType(fmt, GECmdField(referenceIndex & 0xFF));
|
|
}
|
|
|
|
if (referenceIndex >= (uint32_t)GEReferenceIndex::BONE_MATRIX && referenceIndex < (uint32_t)GEReferenceIndex::MATRIX_END)
|
|
return EXPR_TYPE_FLOAT;
|
|
return EXPR_TYPE_UINT;
|
|
}
|
|
|
|
ExpressionType GEExpressionFunctions::getFieldType(GECmdFormat fmt, GECmdField field) {
|
|
switch (field) {
|
|
case GECmdField::LOW_U12_4_FLOAT:
|
|
case GECmdField::HIGH_S4_4_FLOAT:
|
|
return EXPR_TYPE_FLOAT;
|
|
|
|
default:
|
|
// Almost all of these are uint, so not listing each one.
|
|
break;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|