Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Debugger/Breakpoints.cpp
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
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.
6
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.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <atomic>
19
#include <mutex>
20
21
#include "Common/System/System.h"
22
#include "Common/Log.h"
23
#include "Core/Core.h"
24
#include "Core/Debugger/Breakpoints.h"
25
#include "Core/Debugger/MemBlockInfo.h"
26
#include "Core/Debugger/SymbolMap.h"
27
#include "Core/MemMap.h"
28
#include "Core/MIPS/MIPSAnalyst.h"
29
#include "Core/MIPS/MIPSDebugInterface.h"
30
#include "Core/MIPS/JitCommon/JitCommon.h"
31
#include "Core/CoreTiming.h"
32
33
BreakpointManager g_breakpoints;
34
35
void MemCheck::Log(u32 addr, bool write, int size, u32 pc, const char *reason) const {
36
if (result & BREAK_ACTION_LOG) {
37
const char *type = write ? "Write" : "Read";
38
if (logFormat.empty()) {
39
NOTICE_LOG(Log::MemMap, "CHK %s%i(%s) at %08x (%s), PC=%08x (%s)", type, size * 8, reason, addr, g_symbolMap->GetDescription(addr).c_str(), pc, g_symbolMap->GetDescription(pc).c_str());
40
} else {
41
std::string formatted;
42
g_breakpoints.EvaluateLogFormat(currentDebugMIPS, logFormat, formatted);
43
NOTICE_LOG(Log::MemMap, "CHK %s%i(%s) at %08x: %s", type, size * 8, reason, addr, formatted.c_str());
44
}
45
}
46
}
47
48
BreakAction MemCheck::Apply(u32 addr, bool write, int size, u32 pc) {
49
int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ;
50
if (cond & mask) {
51
if (hasCondition) {
52
if (!condition.Evaluate())
53
return BREAK_ACTION_IGNORE;
54
}
55
56
++numHits;
57
return result;
58
}
59
60
return BREAK_ACTION_IGNORE;
61
}
62
63
BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc, const char *reason) {
64
// Conditions have always already been checked if we get here.
65
Log(addr, write, size, pc, reason);
66
if (result & BREAK_ACTION_PAUSE) {
67
Core_Break(BreakReason::MemoryBreakpoint, start);
68
}
69
return result;
70
}
71
72
// Note: must lock while calling this.
73
size_t BreakpointManager::FindBreakpoint(u32 addr, bool matchTemp, bool temp) {
74
size_t found = INVALID_BREAKPOINT;
75
for (size_t i = 0; i < breakPoints_.size(); ++i) {
76
const auto &bp = breakPoints_[i];
77
if (bp.addr == addr && (!matchTemp || bp.temporary == temp))
78
{
79
if (bp.IsEnabled())
80
return i;
81
// Hold out until the first enabled one.
82
if (found == INVALID_BREAKPOINT)
83
found = i;
84
}
85
}
86
87
return found;
88
}
89
90
size_t BreakpointManager::FindMemCheck(u32 start, u32 end) {
91
for (size_t i = 0; i < memChecks_.size(); ++i) {
92
if (memChecks_[i].start == start && memChecks_[i].end == end)
93
return i;
94
}
95
96
return INVALID_MEMCHECK;
97
}
98
99
bool BreakpointManager::IsAddressBreakPoint(u32 addr)
100
{
101
if (!anyBreakPoints_)
102
return false;
103
std::lock_guard<std::mutex> guard(breakPointsMutex_);
104
size_t bp = FindBreakpoint(addr);
105
return bp != INVALID_BREAKPOINT && breakPoints_[bp].result != BREAK_ACTION_IGNORE;
106
}
107
108
bool BreakpointManager::IsAddressBreakPoint(u32 addr, bool* enabled)
109
{
110
if (!anyBreakPoints_)
111
return false;
112
std::lock_guard<std::mutex> guard(breakPointsMutex_);
113
size_t bp = FindBreakpoint(addr);
114
if (bp == INVALID_BREAKPOINT) return false;
115
if (enabled != nullptr)
116
*enabled = breakPoints_[bp].IsEnabled();
117
return true;
118
}
119
120
bool BreakpointManager::IsTempBreakPoint(u32 addr)
121
{
122
std::lock_guard<std::mutex> guard(breakPointsMutex_);
123
size_t bp = FindBreakpoint(addr, true, true);
124
return bp != INVALID_BREAKPOINT;
125
}
126
127
bool BreakpointManager::RangeContainsBreakPoint(u32 addr, u32 size)
128
{
129
if (!anyBreakPoints_)
130
return false;
131
std::lock_guard<std::mutex> guard(breakPointsMutex_);
132
const u32 end = addr + size;
133
for (const auto &bp : breakPoints_)
134
{
135
if (bp.addr >= addr && bp.addr < end)
136
return true;
137
}
138
139
return false;
140
}
141
142
int BreakpointManager::AddBreakPoint(u32 addr, bool temp) {
143
std::unique_lock<std::mutex> guard(breakPointsMutex_);
144
size_t bp = FindBreakpoint(addr, true, temp);
145
if (bp == INVALID_BREAKPOINT) {
146
BreakPoint pt;
147
pt.result |= BREAK_ACTION_PAUSE;
148
pt.temporary = temp;
149
pt.addr = addr;
150
151
breakPoints_.push_back(pt);
152
anyBreakPoints_ = true;
153
Update(addr);
154
return (int)breakPoints_.size() - 1;
155
} else if (!breakPoints_[bp].IsEnabled()) {
156
breakPoints_[bp].result |= BREAK_ACTION_PAUSE;
157
breakPoints_[bp].hasCond = false;
158
Update(addr);
159
return (int)bp;
160
} else {
161
// nothing to do, just return the already-existing breakpoint index
162
return (int)bp;
163
}
164
}
165
166
void BreakpointManager::RemoveBreakPoint(u32 addr) {
167
std::unique_lock<std::mutex> guard(breakPointsMutex_);
168
size_t bp = FindBreakpoint(addr);
169
if (bp != INVALID_BREAKPOINT) {
170
breakPoints_.erase(breakPoints_.begin() + bp);
171
172
// Check again, there might've been an overlapping temp breakpoint.
173
bp = FindBreakpoint(addr);
174
if (bp != INVALID_BREAKPOINT)
175
breakPoints_.erase(breakPoints_.begin() + bp);
176
177
anyBreakPoints_ = !breakPoints_.empty();
178
Update(addr);
179
}
180
}
181
182
void BreakpointManager::ChangeBreakPoint(u32 addr, bool status) {
183
std::unique_lock<std::mutex> guard(breakPointsMutex_);
184
size_t bp = FindBreakpoint(addr);
185
if (bp != INVALID_BREAKPOINT) {
186
if (status)
187
breakPoints_[bp].result |= BREAK_ACTION_PAUSE;
188
else
189
breakPoints_[bp].result = BreakAction(breakPoints_[bp].result & ~BREAK_ACTION_PAUSE);
190
Update(addr);
191
}
192
}
193
194
void BreakpointManager::ChangeBreakPoint(u32 addr, BreakAction result) {
195
std::unique_lock<std::mutex> guard(breakPointsMutex_);
196
size_t bp = FindBreakpoint(addr);
197
if (bp != INVALID_BREAKPOINT) {
198
breakPoints_[bp].result = result;
199
Update(addr);
200
}
201
}
202
203
void BreakpointManager::ClearAllBreakPoints()
204
{
205
if (!anyBreakPoints_)
206
return;
207
std::unique_lock<std::mutex> guard(breakPointsMutex_);
208
if (!breakPoints_.empty())
209
{
210
breakPoints_.clear();
211
Update();
212
}
213
}
214
215
void BreakpointManager::ClearTemporaryBreakPoints()
216
{
217
if (!anyBreakPoints_)
218
return;
219
std::unique_lock<std::mutex> guard(breakPointsMutex_);
220
for (int i = (int)breakPoints_.size()-1; i >= 0; --i)
221
{
222
if (breakPoints_[i].temporary)
223
{
224
breakPoints_.erase(breakPoints_.begin() + i);
225
Update();
226
}
227
}
228
}
229
230
void BreakpointManager::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond)
231
{
232
std::unique_lock<std::mutex> guard(breakPointsMutex_);
233
size_t bp = FindBreakpoint(addr);
234
if (bp != INVALID_BREAKPOINT)
235
{
236
breakPoints_[bp].hasCond = true;
237
breakPoints_[bp].cond = cond;
238
Update(addr);
239
}
240
}
241
242
void BreakpointManager::ChangeBreakPointRemoveCond(u32 addr) {
243
std::unique_lock<std::mutex> guard(breakPointsMutex_);
244
size_t bp = FindBreakpoint(addr);
245
if (bp != INVALID_BREAKPOINT) {
246
breakPoints_[bp].hasCond = false;
247
Update(addr);
248
}
249
}
250
251
BreakPointCond *BreakpointManager::GetBreakPointCondition(u32 addr) {
252
std::lock_guard<std::mutex> guard(breakPointsMutex_);
253
size_t bp = FindBreakpoint(addr);
254
if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond)
255
return &breakPoints_[bp].cond;
256
return nullptr;
257
}
258
259
void BreakpointManager::ChangeBreakPointLogFormat(u32 addr, const std::string &fmt) {
260
std::unique_lock<std::mutex> guard(breakPointsMutex_);
261
size_t bp = FindBreakpoint(addr, true, false);
262
if (bp != INVALID_BREAKPOINT) {
263
breakPoints_[bp].logFormat = fmt;
264
Update(addr);
265
}
266
}
267
268
BreakAction BreakpointManager::ExecBreakPoint(u32 addr) {
269
if (!anyBreakPoints_)
270
return BREAK_ACTION_IGNORE;
271
std::unique_lock<std::mutex> guard(breakPointsMutex_);
272
size_t bp = FindBreakpoint(addr, false);
273
if (bp != INVALID_BREAKPOINT) {
274
const BreakPoint &info = breakPoints_[bp];
275
guard.unlock();
276
277
if (info.hasCond) {
278
// Evaluate the breakpoint and abort if necessary.
279
auto cond = BreakpointManager::GetBreakPointCondition(currentMIPS->pc);
280
if (cond && !cond->Evaluate())
281
return BREAK_ACTION_IGNORE;
282
}
283
284
if (info.result & BREAK_ACTION_LOG) {
285
if (info.logFormat.empty()) {
286
NOTICE_LOG(Log::JIT, "BKP PC=%08x (%s)", addr, g_symbolMap->GetDescription(addr).c_str());
287
} else {
288
std::string formatted;
289
BreakpointManager::EvaluateLogFormat(currentDebugMIPS, info.logFormat, formatted);
290
NOTICE_LOG(Log::JIT, "BKP PC=%08x: %s", addr, formatted.c_str());
291
}
292
}
293
if (info.result & BREAK_ACTION_PAUSE) {
294
Core_Break(BreakReason::CpuBreakpoint, info.addr);
295
}
296
297
return info.result;
298
}
299
300
return BREAK_ACTION_IGNORE;
301
}
302
303
int BreakpointManager::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result) {
304
std::unique_lock<std::mutex> guard(memCheckMutex_);
305
306
size_t mc = FindMemCheck(start, end);
307
if (mc == INVALID_MEMCHECK) {
308
MemCheck check;
309
check.start = start;
310
check.end = end;
311
check.cond = cond;
312
check.result = result;
313
314
memChecks_.push_back(check);
315
bool hadAny = anyMemChecks_.exchange(true);
316
if (!hadAny) {
317
MemBlockOverrideDetailed();
318
}
319
Update();
320
return (int)memChecks_.size() - 1;
321
} else {
322
memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond);
323
memChecks_[mc].result = (BreakAction)(memChecks_[mc].result | result);
324
bool hadAny = anyMemChecks_.exchange(true);
325
if (!hadAny) {
326
MemBlockOverrideDetailed();
327
}
328
Update();
329
return (int)mc;
330
}
331
}
332
333
void BreakpointManager::RemoveMemCheck(u32 start, u32 end)
334
{
335
std::unique_lock<std::mutex> guard(memCheckMutex_);
336
337
size_t mc = FindMemCheck(start, end);
338
if (mc != INVALID_MEMCHECK)
339
{
340
memChecks_.erase(memChecks_.begin() + mc);
341
bool hadAny = anyMemChecks_.exchange(!memChecks_.empty());
342
if (hadAny)
343
MemBlockReleaseDetailed();
344
Update();
345
}
346
}
347
348
void BreakpointManager::ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result)
349
{
350
std::unique_lock<std::mutex> guard(memCheckMutex_);
351
size_t mc = FindMemCheck(start, end);
352
if (mc != INVALID_MEMCHECK)
353
{
354
memChecks_[mc].cond = cond;
355
memChecks_[mc].result = result;
356
Update();
357
}
358
}
359
360
void BreakpointManager::ClearAllMemChecks()
361
{
362
std::unique_lock<std::mutex> guard(memCheckMutex_);
363
364
if (!memChecks_.empty())
365
{
366
memChecks_.clear();
367
bool hadAny = anyMemChecks_.exchange(false);
368
if (hadAny)
369
MemBlockReleaseDetailed();
370
Update();
371
}
372
}
373
374
375
void BreakpointManager::ChangeMemCheckAddCond(u32 start, u32 end, const BreakPointCond &cond) {
376
std::unique_lock<std::mutex> guard(memCheckMutex_);
377
size_t mc = FindMemCheck(start, end);
378
if (mc != INVALID_MEMCHECK) {
379
memChecks_[mc].hasCondition = true;
380
memChecks_[mc].condition = cond;
381
// No need to update jit for a condition add/remove, they're not baked in.
382
Update(-1);
383
}
384
}
385
386
void BreakpointManager::ChangeMemCheckRemoveCond(u32 start, u32 end) {
387
std::unique_lock<std::mutex> guard(memCheckMutex_);
388
size_t mc = FindMemCheck(start, end);
389
if (mc != INVALID_MEMCHECK) {
390
memChecks_[mc].hasCondition = false;
391
// No need to update jit for a condition add/remove, they're not baked in.
392
Update(-1);
393
}
394
}
395
396
BreakPointCond *BreakpointManager::GetMemCheckCondition(u32 start, u32 end) {
397
std::unique_lock<std::mutex> guard(memCheckMutex_);
398
size_t mc = FindMemCheck(start, end);
399
if (mc != INVALID_MEMCHECK && memChecks_[mc].hasCondition)
400
return &memChecks_[mc].condition;
401
return nullptr;
402
}
403
404
void BreakpointManager::ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt) {
405
std::unique_lock<std::mutex> guard(memCheckMutex_);
406
size_t mc = FindMemCheck(start, end);
407
if (mc != INVALID_MEMCHECK) {
408
memChecks_[mc].logFormat = fmt;
409
Update();
410
}
411
}
412
413
bool BreakpointManager::GetMemCheck(u32 start, u32 end, MemCheck *check) {
414
std::lock_guard<std::mutex> guard(memCheckMutex_);
415
size_t mc = FindMemCheck(start, end);
416
if (mc != INVALID_MEMCHECK) {
417
*check = memChecks_[mc];
418
return true;
419
}
420
return false;
421
}
422
423
static inline u32 NotCached(u32 val) {
424
// Remove the cached part of the address as well as any mirror.
425
if ((val & 0x3F800000) == 0x04000000)
426
return val & ~0x40600000;
427
return val & ~0x40000000;
428
}
429
430
bool BreakpointManager::GetMemCheckInRange(u32 address, int size, MemCheck *check) {
431
std::lock_guard<std::mutex> guard(memCheckMutex_);
432
auto result = GetMemCheckLocked(address, size);
433
if (result)
434
*check = *result;
435
return result != nullptr;
436
}
437
438
MemCheck *BreakpointManager::GetMemCheckLocked(u32 address, int size) {
439
std::vector<MemCheck>::iterator iter;
440
for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter)
441
{
442
MemCheck &check = *iter;
443
if (check.end != 0)
444
{
445
if (NotCached(address + size) > NotCached(check.start) && NotCached(address) < NotCached(check.end))
446
return &check;
447
}
448
else
449
{
450
if (NotCached(check.start) == NotCached(address))
451
return &check;
452
}
453
}
454
455
//none found
456
return 0;
457
}
458
459
BreakAction BreakpointManager::ExecMemCheck(u32 address, bool write, int size, u32 pc, const char *reason)
460
{
461
if (!anyMemChecks_)
462
return BREAK_ACTION_IGNORE;
463
std::unique_lock<std::mutex> guard(memCheckMutex_);
464
auto check = GetMemCheckLocked(address, size);
465
if (check) {
466
BreakAction applyAction = check->Apply(address, write, size, pc);
467
if (applyAction == BREAK_ACTION_IGNORE)
468
return applyAction;
469
470
auto copy = *check;
471
guard.unlock();
472
return copy.Action(address, write, size, pc, reason);
473
}
474
return BREAK_ACTION_IGNORE;
475
}
476
477
BreakAction BreakpointManager::ExecOpMemCheck(u32 address, u32 pc) {
478
// Note: currently, we don't check "on changed" for HLE (ExecMemCheck.)
479
// We'd need to more carefully specify memory changes in HLE for that.
480
int size = MIPSAnalyst::OpMemoryAccessSize(pc);
481
if (size == 0 && MIPSAnalyst::OpHasDelaySlot(pc)) {
482
// This means that the delay slot is what tripped us.
483
pc += 4;
484
size = MIPSAnalyst::OpMemoryAccessSize(pc);
485
}
486
487
bool write = MIPSAnalyst::IsOpMemoryWrite(pc);
488
std::unique_lock<std::mutex> guard(memCheckMutex_);
489
auto check = GetMemCheckLocked(address, size);
490
if (check) {
491
int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE;
492
bool apply = false;
493
if (write && (check->cond & mask) == mask) {
494
if (MIPSAnalyst::OpWouldChangeMemory(pc, address, size)) {
495
apply = true;
496
}
497
} else {
498
apply = true;
499
}
500
if (apply) {
501
BreakAction applyAction = check->Apply(address, write, size, pc);
502
if (applyAction == BREAK_ACTION_IGNORE)
503
return applyAction;
504
505
// Make a copy so we can safely unlock.
506
auto copy = *check;
507
guard.unlock();
508
return copy.Action(address, write, size, pc, "CPU");
509
}
510
}
511
return BREAK_ACTION_IGNORE;
512
}
513
514
void BreakpointManager::SetSkipFirst(u32 pc) {
515
breakSkipFirstAt_ = pc;
516
breakSkipFirstTicks_ = CoreTiming::GetTicks();
517
}
518
519
u32 BreakpointManager::CheckSkipFirst() {
520
u32 pc = breakSkipFirstAt_;
521
if (breakSkipFirstTicks_ == CoreTiming::GetTicks())
522
return pc;
523
return 0;
524
}
525
526
static MemCheck NotCached(MemCheck mc) {
527
// Toggle the cached part of the address.
528
mc.start ^= 0x40000000;
529
if (mc.end != 0)
530
mc.end ^= 0x40000000;
531
return mc;
532
}
533
534
static MemCheck VRAMMirror(uint8_t mirror, MemCheck mc) {
535
mc.start &= ~0x00600000;
536
mc.start += 0x00200000 * mirror;
537
if (mc.end != 0) {
538
mc.end &= ~0x00600000;
539
mc.end += 0x00200000 * mirror;
540
if (mc.end < mc.start)
541
mc.end += 0x00200000;
542
}
543
return mc;
544
}
545
546
void BreakpointManager::UpdateCachedMemCheckRanges() {
547
std::lock_guard<std::mutex> guard(memCheckMutex_);
548
memCheckRangesRead_.clear();
549
memCheckRangesWrite_.clear();
550
551
auto add = [&](bool read, bool write, const MemCheck &mc) {
552
if (read)
553
memCheckRangesRead_.push_back(mc);
554
if (write)
555
memCheckRangesWrite_.push_back(mc);
556
};
557
558
for (const auto &check : memChecks_) {
559
bool read = (check.cond & MEMCHECK_READ) != 0;
560
bool write = (check.cond & MEMCHECK_WRITE) != 0;
561
562
if (Memory::IsVRAMAddress(check.start) && (check.end == 0 || Memory::IsVRAMAddress(check.end))) {
563
for (uint8_t mirror = 0; mirror < 4; ++mirror) {
564
MemCheck copy = VRAMMirror(mirror, check);
565
add(read, write, copy);
566
add(read, write, NotCached(copy));
567
}
568
} else {
569
add(read, write, check);
570
add(read, write, NotCached(check));
571
}
572
}
573
}
574
575
std::vector<MemCheck> BreakpointManager::GetMemCheckRanges(bool write) {
576
std::lock_guard<std::mutex> guard(memCheckMutex_);
577
if (write)
578
return memCheckRangesWrite_;
579
return memCheckRangesRead_;
580
}
581
582
std::vector<MemCheck> BreakpointManager::GetMemChecks() {
583
std::lock_guard<std::mutex> guard(memCheckMutex_);
584
return memChecks_;
585
}
586
587
std::vector<BreakPoint> BreakpointManager::GetBreakpoints() {
588
std::lock_guard<std::mutex> guard(breakPointsMutex_);
589
return breakPoints_;
590
}
591
592
void BreakpointManager::Frame() {
593
// outside the lock here, should be ok.
594
if (!needsUpdate_) {
595
return;
596
}
597
598
std::lock_guard<std::mutex> guard(breakPointsMutex_);
599
if (MIPSComp::jit && updateAddr_ != -1) {
600
// In case this is a delay slot, clear the previous instruction too.
601
if (updateAddr_ != 0)
602
mipsr4k.InvalidateICache(updateAddr_ - 4, 8);
603
else
604
mipsr4k.ClearJitCache();
605
}
606
607
if (anyMemChecks_ && updateAddr_ != -1)
608
UpdateCachedMemCheckRanges();
609
610
// Redraw in order to show the breakpoint.
611
System_Notify(SystemNotification::DISASSEMBLY);
612
needsUpdate_ = false;
613
}
614
615
bool BreakpointManager::ValidateLogFormat(MIPSDebugInterface *cpu, const std::string &fmt) {
616
std::string ignore;
617
return EvaluateLogFormat(cpu, fmt, ignore);
618
}
619
620
bool BreakpointManager::EvaluateLogFormat(MIPSDebugInterface *cpu, const std::string &fmt, std::string &result) {
621
PostfixExpression exp;
622
result.clear();
623
624
size_t pos = 0;
625
while (pos < fmt.size()) {
626
size_t next = fmt.find_first_of('{', pos);
627
if (next == fmt.npos) {
628
// End of the string.
629
result += fmt.substr(pos);
630
break;
631
}
632
if (next != pos) {
633
result += fmt.substr(pos, next - pos);
634
pos = next;
635
}
636
637
size_t end = fmt.find_first_of('}', next + 1);
638
if (end == fmt.npos) {
639
// Invalid: every expression needs a { and a }.
640
return false;
641
}
642
643
std::string expression = fmt.substr(next + 1, end - next - 1);
644
if (expression.empty()) {
645
result += "{}";
646
} else {
647
int type = 'x';
648
if (expression.length() > 2 && expression[expression.length() - 2] == ':') {
649
switch (expression[expression.length() - 1]) {
650
case 'd':
651
case 'f':
652
case 'p':
653
case 's':
654
case 'x':
655
type = expression[expression.length() - 1];
656
expression.resize(expression.length() - 2);
657
break;
658
659
default:
660
// Assume a ternary.
661
break;
662
}
663
}
664
665
if (!initExpression(cpu, expression.c_str(), exp)) {
666
return false;
667
}
668
669
union {
670
int i;
671
u32 u;
672
float f;
673
} expResult;
674
char resultString[256];
675
if (!parseExpression(cpu, exp, expResult.u)) {
676
return false;
677
}
678
679
switch (type) {
680
case 'd':
681
snprintf(resultString, sizeof(resultString), "%d", expResult.i);
682
break;
683
case 'f':
684
snprintf(resultString, sizeof(resultString), "%f", expResult.f);
685
break;
686
case 'p':
687
snprintf(resultString, sizeof(resultString), "%08x[%08x]", expResult.u, Memory::IsValidAddress(expResult.u) ? Memory::Read_U32(expResult.u) : 0);
688
break;
689
case 's':
690
snprintf(resultString, sizeof(resultString) - 1, "%s", Memory::IsValidAddress(expResult.u) ? Memory::GetCharPointer(expResult.u) : "(invalid)");
691
break;
692
case 'x':
693
snprintf(resultString, sizeof(resultString), "%08x", expResult.u);
694
break;
695
}
696
result += resultString;
697
}
698
699
// Skip the }.
700
pos = end + 1;
701
}
702
703
return true;
704
}
705
706