1
// Copyright (c) 2013- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
20
#include "base/mutex.h"
21
#include "GPU/Debugger/Breakpoints.h"
22
#include "GPU/GPUState.h"
24
namespace GPUBreakpoints {
26
static recursive_mutex breaksLock;
27
static std::vector<bool> breakCmds;
28
static std::set<u32> breakPCs;
29
static std::set<u32> breakTextures;
30
static std::set<u32> breakRenderTargets;
31
// Small optimization to avoid a lock/lookup for the common case.
32
static size_t breakPCsCount = 0;
33
static size_t breakTexturesCount = 0;
34
static size_t breakRenderTargetsCount = 0;
36
// If these are set, the above are also, but they should be temporary.
37
static std::vector<bool> breakCmdsTemp;
38
static std::set<u32> breakPCsTemp;
39
static std::set<u32> breakTexturesTemp;
40
static std::set<u32> breakRenderTargetsTemp;
41
static bool textureChangeTemp = false;
43
static u32 lastTexture = 0xFFFFFFFF;
45
// These are commands we run before breaking on a texture.
46
// They are commands that affect the decoding of the texture.
47
const static u8 textureRelatedCmds[] = {
48
GE_CMD_TEXADDR0, GE_CMD_TEXADDR1, GE_CMD_TEXADDR2, GE_CMD_TEXADDR3, GE_CMD_TEXADDR4, GE_CMD_TEXADDR5, GE_CMD_TEXADDR6, GE_CMD_TEXADDR7,
49
GE_CMD_TEXBUFWIDTH0, GE_CMD_TEXBUFWIDTH1, GE_CMD_TEXBUFWIDTH2, GE_CMD_TEXBUFWIDTH3, GE_CMD_TEXBUFWIDTH4, GE_CMD_TEXBUFWIDTH5, GE_CMD_TEXBUFWIDTH6, GE_CMD_TEXBUFWIDTH7,
50
GE_CMD_TEXSIZE0, GE_CMD_TEXSIZE1, GE_CMD_TEXSIZE2, GE_CMD_TEXSIZE3, GE_CMD_TEXSIZE4, GE_CMD_TEXSIZE5, GE_CMD_TEXSIZE6, GE_CMD_TEXSIZE7,
52
GE_CMD_CLUTADDR, GE_CMD_CLUTADDRUPPER, GE_CMD_LOADCLUT, GE_CMD_CLUTFORMAT,
53
GE_CMD_TEXFORMAT, GE_CMD_TEXMODE, GE_CMD_TEXTUREMAPENABLE,
54
GE_CMD_TEXFILTER, GE_CMD_TEXWRAP,
57
// Sometimes found between clut/texture params.
58
GE_CMD_TEXFLUSH, GE_CMD_TEXSYNC,
60
static std::vector<bool> nonTextureCmds;
63
ClearAllBreakpoints();
65
nonTextureCmds.clear();
66
nonTextureCmds.resize(256, true);
67
for (size_t i = 0; i < ARRAY_SIZE(textureRelatedCmds); ++i) {
68
nonTextureCmds[textureRelatedCmds[i]] = false;
72
void AddNonTextureTempBreakpoints() {
73
for (int i = 0; i < 256; ++i) {
74
if (nonTextureCmds[i]) {
75
AddCmdBreakpoint(i, true);
80
u32 GetAdjustedTextureAddress(u32 op) {
81
const u8 cmd = op >> 24;
82
bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7);
83
interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7);
89
int level = cmd <= GE_CMD_TEXADDR7 ? cmd - GE_CMD_TEXADDR0 : cmd - GE_CMD_TEXBUFWIDTH0;
92
// Okay, so would this op modify the low or high part?
93
if (cmd <= GE_CMD_TEXADDR7) {
94
addr = (op & 0xFFFFF0) | ((gstate.texbufwidth[level] << 8) & 0x0F000000);
96
addr = (gstate.texaddr[level] & 0xFFFFF0) | ((op << 8) & 0x0F000000);
102
u32 GetAdjustedRenderTargetAddress(u32 op) {
103
const u8 cmd = op >> 24;
105
case GE_CMD_FRAMEBUFPTR:
107
return op & 0x003FFFF0;
113
bool IsTextureChangeBreakpoint(u32 op, u32 addr) {
114
if (!textureChangeTemp) {
118
const u8 cmd = op >> 24;
119
bool enabled = gstate.isTextureMapEnabled();
122
if (cmd != GE_CMD_TEXADDR0 && cmd != GE_CMD_TEXBUFWIDTH0) {
123
// But we don't break when it's not enabled.
124
if (cmd == GE_CMD_TEXTUREMAPENABLE) {
125
enabled = (op & 1) != 0;
130
if (enabled && addr != lastTexture) {
131
textureChangeTemp = false;
139
bool IsTextureCmdBreakpoint(u32 op) {
140
const u32 addr = GetAdjustedTextureAddress(op);
141
if (addr != (u32)-1) {
142
return IsTextureChangeBreakpoint(op, addr) || IsTextureBreakpoint(addr);
144
return IsTextureChangeBreakpoint(op, gstate.getTextureAddress(0));
148
bool IsRenderTargetCmdBreakpoint(u32 op) {
149
const u32 addr = GetAdjustedRenderTargetAddress(op);
150
if (addr != (u32)-1) {
151
return IsRenderTargetBreakpoint(addr);
156
bool IsBreakpoint(u32 pc, u32 op) {
157
if (IsAddressBreakpoint(pc) || IsOpBreakpoint(op)) {
161
if ((breakTexturesCount != 0 || textureChangeTemp) && IsTextureCmdBreakpoint(op)) {
162
// Break on the next non-texture.
163
AddNonTextureTempBreakpoints();
165
if (breakRenderTargetsCount != 0 && IsRenderTargetCmdBreakpoint(op)) {
172
bool IsAddressBreakpoint(u32 addr, bool &temp) {
173
if (breakPCsCount == 0) {
178
lock_guard guard(breaksLock);
179
temp = breakPCsTemp.find(addr) != breakPCsTemp.end();
180
return breakPCs.find(addr) != breakPCs.end();
183
bool IsAddressBreakpoint(u32 addr) {
184
if (breakPCsCount == 0) {
188
lock_guard guard(breaksLock);
189
return breakPCs.find(addr) != breakPCs.end();
192
bool IsTextureBreakpoint(u32 addr, bool &temp) {
193
if (breakTexturesCount == 0) {
198
lock_guard guard(breaksLock);
199
temp = breakTexturesTemp.find(addr) != breakTexturesTemp.end();
200
return breakTextures.find(addr) != breakTextures.end();
203
bool IsTextureBreakpoint(u32 addr) {
204
if (breakTexturesCount == 0) {
208
lock_guard guard(breaksLock);
209
return breakTextures.find(addr) != breakTextures.end();
212
bool IsRenderTargetBreakpoint(u32 addr, bool &temp) {
213
if (breakRenderTargetsCount == 0) {
220
lock_guard guard(breaksLock);
221
temp = breakRenderTargetsTemp.find(addr) != breakRenderTargetsTemp.end();
222
return breakRenderTargets.find(addr) != breakRenderTargets.end();
225
bool IsRenderTargetBreakpoint(u32 addr) {
226
if (breakRenderTargetsCount == 0) {
232
lock_guard guard(breaksLock);
233
return breakRenderTargets.find(addr) != breakRenderTargets.end();
236
bool IsOpBreakpoint(u32 op, bool &temp) {
237
return IsCmdBreakpoint(op >> 24, temp);
240
bool IsOpBreakpoint(u32 op) {
241
return IsCmdBreakpoint(op >> 24);
244
bool IsCmdBreakpoint(u8 cmd, bool &temp) {
245
temp = breakCmdsTemp[cmd];
246
return breakCmds[cmd];
249
bool IsCmdBreakpoint(u8 cmd) {
250
return breakCmds[cmd];
253
void AddAddressBreakpoint(u32 addr, bool temp) {
254
lock_guard guard(breaksLock);
257
if (breakPCs.find(addr) == breakPCs.end()) {
258
breakPCsTemp.insert(addr);
259
breakPCs.insert(addr);
261
// Already normal breakpoint, let's not make it temporary.
263
// Remove the temporary marking.
264
breakPCsTemp.erase(addr);
265
breakPCs.insert(addr);
268
breakPCsCount = breakPCs.size();
271
void AddCmdBreakpoint(u8 cmd, bool temp) {
273
if (!breakCmds[cmd]) {
274
breakCmdsTemp[cmd] = true;
275
breakCmds[cmd] = true;
277
// Ignore adding a temp breakpoint when a normal one exists.
279
// This is no longer temporary.
280
breakCmdsTemp[cmd] = false;
281
breakCmds[cmd] = true;
285
void AddTextureBreakpoint(u32 addr, bool temp) {
286
lock_guard guard(breaksLock);
289
if (breakTextures.find(addr) == breakTextures.end()) {
290
breakTexturesTemp.insert(addr);
291
breakTextures.insert(addr);
294
breakTexturesTemp.erase(addr);
295
breakTextures.insert(addr);
298
breakTexturesCount = breakTextures.size();
301
void AddRenderTargetBreakpoint(u32 addr, bool temp) {
302
lock_guard guard(breaksLock);
307
if (breakRenderTargets.find(addr) == breakRenderTargets.end()) {
308
breakRenderTargetsTemp.insert(addr);
309
breakRenderTargets.insert(addr);
312
breakRenderTargetsTemp.erase(addr);
313
breakRenderTargets.insert(addr);
316
breakRenderTargetsCount = breakRenderTargets.size();
319
void AddTextureChangeTempBreakpoint() {
320
textureChangeTemp = true;
323
void RemoveAddressBreakpoint(u32 addr) {
324
lock_guard guard(breaksLock);
326
breakPCsTemp.erase(addr);
327
breakPCs.erase(addr);
329
breakPCsCount = breakPCs.size();
332
void RemoveCmdBreakpoint(u8 cmd) {
333
breakCmdsTemp[cmd] = false;
334
breakCmds[cmd] = false;
337
void RemoveTextureBreakpoint(u32 addr) {
338
lock_guard guard(breaksLock);
340
breakTexturesTemp.erase(addr);
341
breakTextures.erase(addr);
343
breakTexturesCount = breakTextures.size();
346
void RemoveRenderTargetBreakpoint(u32 addr) {
347
lock_guard guard(breaksLock);
351
breakRenderTargetsTemp.erase(addr);
352
breakRenderTargets.erase(addr);
354
breakRenderTargetsCount = breakRenderTargets.size();
357
void RemoveTextureChangeTempBreakpoint() {
358
textureChangeTemp = false;
361
void UpdateLastTexture(u32 addr) {
365
void ClearAllBreakpoints() {
366
lock_guard guard(breaksLock);
369
breakCmds.resize(256, false);
371
breakTextures.clear();
372
breakRenderTargets.clear();
374
breakCmdsTemp.clear();
375
breakCmdsTemp.resize(256, false);
376
breakPCsTemp.clear();
377
breakTexturesTemp.clear();
378
breakRenderTargetsTemp.clear();
380
breakPCsCount = breakPCs.size();
381
breakTexturesCount = breakTextures.size();
382
breakRenderTargetsCount = breakRenderTargets.size();
384
textureChangeTemp = false;
387
void ClearTempBreakpoints() {
388
lock_guard guard(breaksLock);
390
// Reset ones that were temporary back to non-breakpoints in the primary arrays.
391
for (int i = 0; i < 256; ++i) {
392
if (breakCmdsTemp[i]) {
393
breakCmds[i] = false;
394
breakCmdsTemp[i] = false;
398
for (auto it = breakPCsTemp.begin(), end = breakPCsTemp.end(); it != end; ++it) {
401
breakPCsTemp.clear();
402
breakPCsCount = breakPCs.size();
404
for (auto it = breakTexturesTemp.begin(), end = breakTexturesTemp.end(); it != end; ++it) {
405
breakTextures.erase(*it);
407
breakTexturesTemp.clear();
408
breakTexturesCount = breakTextures.size();
410
for (auto it = breakRenderTargetsTemp.begin(), end = breakRenderTargetsTemp.end(); it != end; ++it) {
411
breakRenderTargets.erase(*it);
413
breakRenderTargetsTemp.clear();
414
breakRenderTargetsCount = breakRenderTargets.size();
416
textureChangeTemp = false;