CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/Debugger/Debugger_Lists.cpp
Views: 1401
1
#include "Windows/Debugger/Debugger_Lists.h"
2
#include "Common/CommonWindows.h"
3
#include <windowsx.h>
4
#include <commctrl.h>
5
#include "Windows/Debugger/BreakpointWindow.h"
6
#include "Windows/Debugger/CtrlDisAsmView.h"
7
#include "Windows/Debugger/DebuggerShared.h"
8
#include "Windows/Debugger/WatchItemWindow.h"
9
#include "Windows/W32Util/ContextMenu.h"
10
#include "Windows/MainWindow.h"
11
#include "Windows/resource.h"
12
#include "Windows/main.h"
13
#include "Common/Data/Encoding/Utf8.h"
14
#include "Core/HLE/sceKernelThread.h"
15
16
enum { TL_NAME, TL_PROGRAMCOUNTER, TL_ENTRYPOINT, TL_PRIORITY, TL_STATE, TL_WAITTYPE, TL_COLUMNCOUNT };
17
enum { BPL_ENABLED, BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_COLUMNCOUNT };
18
enum { SF_ENTRY, SF_ENTRYNAME, SF_CURPC, SF_CUROPCODE, SF_CURSP, SF_FRAMESIZE, SF_COLUMNCOUNT };
19
enum { ML_NAME, ML_ADDRESS, ML_SIZE, ML_ACTIVE, ML_COLUMNCOUNT };
20
enum { WL_NAME, WL_EXPRESSION, WL_VALUE, WL_COLUMNCOUNT };
21
22
GenericListViewColumn threadColumns[TL_COLUMNCOUNT] = {
23
{ L"Name", 0.20f },
24
{ L"PC", 0.15f },
25
{ L"Entry Point", 0.15f },
26
{ L"Priority", 0.15f },
27
{ L"State", 0.15f },
28
{ L"Wait type", 0.20f }
29
};
30
31
GenericListViewDef threadListDef = {
32
threadColumns, ARRAY_SIZE(threadColumns), NULL, false
33
};
34
35
GenericListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = {
36
{ L"", 0.03f }, // enabled
37
{ L"Type", 0.15f },
38
{ L"Offset", 0.12f },
39
{ L"Size/Label", 0.20f },
40
{ L"Opcode", 0.28f },
41
{ L"Condition", 0.17f },
42
{ L"Hits", 0.05f },
43
};
44
45
GenericListViewDef breakpointListDef = {
46
breakpointColumns, ARRAY_SIZE(breakpointColumns), NULL, true
47
};
48
49
GenericListViewColumn stackTraceColumns[SF_COLUMNCOUNT] = {
50
{ L"Entry", 0.12f },
51
{ L"Name", 0.24f },
52
{ L"PC", 0.12f },
53
{ L"Opcode", 0.28f },
54
{ L"SP", 0.12f },
55
{ L"Frame Size", 0.12f }
56
};
57
58
GenericListViewDef stackTraceListDef = {
59
stackTraceColumns, ARRAY_SIZE(stackTraceColumns), NULL, false
60
};
61
62
GenericListViewColumn moduleListColumns[ML_COLUMNCOUNT] = {
63
{ L"Name", 0.25f },
64
{ L"Address", 0.25f },
65
{ L"Size", 0.25f },
66
{ L"Active", 0.25f },
67
};
68
69
GenericListViewDef moduleListDef = {
70
moduleListColumns, ARRAY_SIZE(moduleListColumns), NULL, false
71
};
72
73
GenericListViewColumn watchListColumns[WL_COLUMNCOUNT] = {
74
{ L"Name", 0.25f },
75
{ L"Expression", 0.5f },
76
{ L"Value", 0.25f },
77
};
78
79
GenericListViewDef watchListDef = {
80
watchListColumns, ARRAY_SIZE(watchListColumns), nullptr, false,
81
};
82
83
//
84
// CtrlThreadList
85
//
86
87
CtrlThreadList::CtrlThreadList(HWND hwnd): GenericListControl(hwnd,threadListDef)
88
{
89
Update();
90
}
91
92
bool CtrlThreadList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue)
93
{
94
switch (msg)
95
{
96
case WM_KEYDOWN:
97
if (wParam == VK_TAB)
98
{
99
SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0);
100
returnValue = 0;
101
return true;
102
}
103
break;
104
case WM_GETDLGCODE:
105
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
106
{
107
if (wParam == VK_TAB)
108
{
109
returnValue = DLGC_WANTMESSAGE;
110
return true;
111
}
112
}
113
break;
114
}
115
116
return false;
117
}
118
119
void CtrlThreadList::showMenu(int itemIndex, const POINT &pt)
120
{
121
auto threadInfo = threads[itemIndex];
122
123
// Can't do it, sorry. Needs to not be running.
124
if (Core_IsActive())
125
return;
126
127
HMENU subMenu = GetContextMenu(ContextMenuID::THREADLIST);
128
switch (threadInfo.status) {
129
case THREADSTATUS_DEAD:
130
case THREADSTATUS_DORMANT:
131
case THREADSTATUS_RUNNING:
132
EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_DISABLED);
133
EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_DISABLED);
134
break;
135
case THREADSTATUS_READY:
136
EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_DISABLED);
137
EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_ENABLED);
138
break;
139
case THREADSTATUS_SUSPEND:
140
case THREADSTATUS_WAIT:
141
case THREADSTATUS_WAITSUSPEND:
142
default:
143
EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_ENABLED);
144
EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_ENABLED);
145
break;
146
}
147
148
switch (TriggerContextMenu(ContextMenuID::THREADLIST, GetHandle(), ContextPoint::FromClient(pt)))
149
{
150
case ID_DISASM_THREAD_FORCERUN:
151
__KernelResumeThreadFromWait(threadInfo.id, 0);
152
reloadThreads();
153
break;
154
case ID_DISASM_THREAD_KILL:
155
sceKernelTerminateThread(threadInfo.id);
156
reloadThreads();
157
break;
158
}
159
}
160
161
void CtrlThreadList::GetColumnText(wchar_t* dest, int row, int col)
162
{
163
if (row < 0 || row >= (int)threads.size()) {
164
return;
165
}
166
167
switch (col)
168
{
169
case TL_NAME:
170
wcscpy(dest, ConvertUTF8ToWString(threads[row].name).c_str());
171
break;
172
case TL_PROGRAMCOUNTER:
173
switch (threads[row].status)
174
{
175
case THREADSTATUS_DORMANT:
176
case THREADSTATUS_DEAD:
177
wcscpy(dest, L"N/A");
178
break;
179
default:
180
wsprintf(dest, L"0x%08X",threads[row].curPC);
181
break;
182
};
183
break;
184
case TL_ENTRYPOINT:
185
wsprintf(dest,L"0x%08X",threads[row].entrypoint);
186
break;
187
case TL_PRIORITY:
188
wsprintf(dest,L"%d",threads[row].priority);
189
break;
190
case TL_STATE:
191
switch (threads[row].status)
192
{
193
case THREADSTATUS_RUNNING:
194
wcscpy(dest,L"Running");
195
break;
196
case THREADSTATUS_READY:
197
wcscpy(dest,L"Ready");
198
break;
199
case THREADSTATUS_WAIT:
200
wcscpy(dest,L"Waiting");
201
break;
202
case THREADSTATUS_SUSPEND:
203
wcscpy(dest,L"Suspended");
204
break;
205
case THREADSTATUS_DORMANT:
206
wcscpy(dest,L"Dormant");
207
break;
208
case THREADSTATUS_DEAD:
209
wcscpy(dest,L"Dead");
210
break;
211
case THREADSTATUS_WAITSUSPEND:
212
wcscpy(dest,L"Waiting/Suspended");
213
break;
214
default:
215
wcscpy(dest,L"Invalid");
216
break;
217
}
218
break;
219
case TL_WAITTYPE:
220
wcscpy(dest, ConvertUTF8ToWString(getWaitTypeName(threads[row].waitType)).c_str());
221
break;
222
}
223
}
224
225
void CtrlThreadList::OnDoubleClick(int itemIndex, int column)
226
{
227
u32 address;
228
switch (threads[itemIndex].status)
229
{
230
case THREADSTATUS_DORMANT:
231
case THREADSTATUS_DEAD:
232
address = threads[itemIndex].entrypoint;
233
break;
234
default:
235
address = threads[itemIndex].curPC;
236
break;
237
}
238
239
SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,address,0);
240
}
241
242
void CtrlThreadList::OnRightClick(int itemIndex, int column, const POINT& point)
243
{
244
showMenu(itemIndex,point);
245
}
246
247
void CtrlThreadList::reloadThreads()
248
{
249
threads = GetThreadsInfo();
250
Update();
251
}
252
253
const char* CtrlThreadList::getCurrentThreadName()
254
{
255
for (size_t i = 0; i < threads.size(); i++)
256
{
257
if (threads[i].isCurrent) return threads[i].name;
258
}
259
260
return "N/A";
261
}
262
263
264
//
265
// CtrlBreakpointList
266
//
267
268
CtrlBreakpointList::CtrlBreakpointList(HWND hwnd, DebugInterface* cpu, CtrlDisAsmView* disasm)
269
: GenericListControl(hwnd,breakpointListDef),cpu(cpu),disasm(disasm)
270
{
271
SetSendInvalidRows(true);
272
Update();
273
}
274
275
bool CtrlBreakpointList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue)
276
{
277
switch(msg)
278
{
279
case WM_KEYDOWN:
280
returnValue = 0;
281
if(wParam == VK_RETURN)
282
{
283
int index = GetSelectedIndex();
284
editBreakpoint(index);
285
return true;
286
} else if (wParam == VK_DELETE)
287
{
288
int index = GetSelectedIndex();
289
removeBreakpoint(index);
290
return true;
291
} else if (wParam == VK_TAB)
292
{
293
SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0);
294
return true;
295
} else if (wParam == VK_SPACE)
296
{
297
int index = GetSelectedIndex();
298
toggleEnabled(index);
299
return true;
300
}
301
break;
302
case WM_GETDLGCODE:
303
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
304
{
305
if (wParam == VK_TAB || wParam == VK_RETURN)
306
{
307
returnValue = DLGC_WANTMESSAGE;
308
return true;
309
}
310
}
311
break;
312
}
313
314
return false;
315
}
316
317
void CtrlBreakpointList::reloadBreakpoints()
318
{
319
// Update the items we're displaying from the debugger.
320
displayedBreakPoints_ = CBreakPoints::GetBreakpoints();
321
displayedMemChecks_= CBreakPoints::GetMemChecks();
322
323
for (int i = 0; i < GetRowCount(); i++)
324
{
325
bool isMemory;
326
int index = getBreakpointIndex(i, isMemory);
327
if (index < 0)
328
continue;
329
330
if (isMemory)
331
SetCheckState(i, displayedMemChecks_[index].IsEnabled());
332
else
333
SetCheckState(i, displayedBreakPoints_[index].IsEnabled());
334
}
335
336
Update();
337
}
338
339
void CtrlBreakpointList::editBreakpoint(int itemIndex)
340
{
341
bool isMemory;
342
int index = getBreakpointIndex(itemIndex, isMemory);
343
if (index == -1) return;
344
345
BreakpointWindow win(GetHandle(),cpu);
346
if (isMemory)
347
{
348
auto mem = displayedMemChecks_[index];
349
win.loadFromMemcheck(mem);
350
if (win.exec())
351
{
352
CBreakPoints::RemoveMemCheck(mem.start,mem.end);
353
win.addBreakpoint();
354
}
355
} else {
356
auto bp = displayedBreakPoints_[index];
357
win.loadFromBreakpoint(bp);
358
if (win.exec())
359
{
360
CBreakPoints::RemoveBreakPoint(bp.addr);
361
win.addBreakpoint();
362
}
363
}
364
}
365
366
void CtrlBreakpointList::toggleEnabled(int itemIndex)
367
{
368
bool isMemory;
369
int index = getBreakpointIndex(itemIndex, isMemory);
370
if (index == -1) return;
371
372
if (isMemory) {
373
MemCheck mcPrev = displayedMemChecks_[index];
374
CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, BreakAction(mcPrev.result ^ BREAK_ACTION_PAUSE));
375
} else {
376
BreakPoint bpPrev = displayedBreakPoints_[index];
377
CBreakPoints::ChangeBreakPoint(bpPrev.addr, BreakAction(bpPrev.result ^ BREAK_ACTION_PAUSE));
378
}
379
}
380
381
void CtrlBreakpointList::gotoBreakpointAddress(int itemIndex)
382
{
383
bool isMemory;
384
int index = getBreakpointIndex(itemIndex, isMemory);
385
if (index == -1)
386
return;
387
388
if (isMemory) {
389
u32 address = displayedMemChecks_[index].start;
390
MainWindow::CreateMemoryWindow();
391
if (memoryWindow)
392
memoryWindow->Goto(address);
393
} else {
394
u32 address = displayedBreakPoints_[index].addr;
395
MainWindow::CreateDisasmWindow();
396
if (disasmWindow)
397
disasmWindow->Goto(address);
398
}
399
}
400
401
void CtrlBreakpointList::removeBreakpoint(int itemIndex)
402
{
403
bool isMemory;
404
int index = getBreakpointIndex(itemIndex,isMemory);
405
if (index == -1) return;
406
407
if (isMemory)
408
{
409
auto mc = displayedMemChecks_[index];
410
CBreakPoints::RemoveMemCheck(mc.start, mc.end);
411
} else {
412
u32 address = displayedBreakPoints_[index].addr;
413
CBreakPoints::RemoveBreakPoint(address);
414
}
415
}
416
417
int CtrlBreakpointList::getTotalBreakpointCount() {
418
int count = (int)displayedMemChecks_.size();
419
for (auto bp : displayedBreakPoints_) {
420
if (!bp.temporary)
421
++count;
422
}
423
424
return count;
425
}
426
427
int CtrlBreakpointList::getBreakpointIndex(int itemIndex, bool& isMemory)
428
{
429
// memory breakpoints first
430
if (itemIndex < (int)displayedMemChecks_.size())
431
{
432
isMemory = true;
433
return itemIndex;
434
}
435
436
itemIndex -= (int)displayedMemChecks_.size();
437
438
size_t i = 0;
439
while (i < displayedBreakPoints_.size())
440
{
441
if (displayedBreakPoints_[i].temporary)
442
{
443
i++;
444
continue;
445
}
446
447
// the index is 0 when there are no more breakpoints to skip
448
if (itemIndex == 0)
449
{
450
isMemory = false;
451
return (int)i;
452
}
453
454
i++;
455
itemIndex--;
456
}
457
458
return -1;
459
}
460
461
void CtrlBreakpointList::GetColumnText(wchar_t* dest, int row, int col)
462
{
463
if (!PSP_IsInited()) {
464
return;
465
}
466
bool isMemory;
467
int index = getBreakpointIndex(row,isMemory);
468
if (index == -1) return;
469
470
switch (col)
471
{
472
case BPL_TYPE:
473
{
474
if (isMemory) {
475
switch ((int)displayedMemChecks_[index].cond) {
476
case MEMCHECK_READ:
477
wcscpy(dest,L"Read");
478
break;
479
case MEMCHECK_WRITE:
480
wcscpy(dest,L"Write");
481
break;
482
case MEMCHECK_READWRITE:
483
wcscpy(dest,L"Read/Write");
484
break;
485
case MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE:
486
wcscpy(dest,L"Write Change");
487
break;
488
case MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE:
489
wcscpy(dest,L"Read/Write Change");
490
break;
491
}
492
} else {
493
wcscpy(dest,L"Execute");
494
}
495
}
496
break;
497
case BPL_OFFSET:
498
{
499
if (isMemory) {
500
wsprintf(dest,L"0x%08X",displayedMemChecks_[index].start);
501
} else {
502
wsprintf(dest,L"0x%08X",displayedBreakPoints_[index].addr);
503
}
504
}
505
break;
506
case BPL_SIZELABEL:
507
{
508
if (isMemory) {
509
auto mc = displayedMemChecks_[index];
510
if (mc.end == 0)
511
wsprintf(dest,L"0x%08X",1);
512
else
513
wsprintf(dest,L"0x%08X",mc.end-mc.start);
514
} else {
515
const std::string sym = g_symbolMap->GetLabelString(displayedBreakPoints_[index].addr);
516
if (!sym.empty())
517
{
518
std::wstring s = ConvertUTF8ToWString(sym);
519
wcscpy(dest,s.c_str());
520
} else {
521
wcscpy(dest,L"-");
522
}
523
}
524
}
525
break;
526
case BPL_OPCODE:
527
{
528
if (isMemory) {
529
wcscpy(dest,L"-");
530
} else {
531
char temp[256];
532
disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp, sizeof(temp));
533
std::wstring s = ConvertUTF8ToWString(temp);
534
wcscpy(dest,s.c_str());
535
}
536
}
537
break;
538
case BPL_CONDITION:
539
{
540
if (isMemory || displayedBreakPoints_[index].hasCond == false) {
541
wcscpy(dest,L"-");
542
} else {
543
std::wstring s = ConvertUTF8ToWString(displayedBreakPoints_[index].cond.expressionString);
544
wcscpy(dest,s.c_str());
545
}
546
}
547
break;
548
case BPL_HITS:
549
{
550
if (isMemory) {
551
wsprintf(dest,L"%d",displayedMemChecks_[index].numHits);
552
} else {
553
wsprintf(dest,L"-");
554
}
555
}
556
break;
557
case BPL_ENABLED:
558
{
559
wsprintf(dest,L"\xFFFE");
560
}
561
break;
562
}
563
}
564
565
void CtrlBreakpointList::OnDoubleClick(int itemIndex, int column)
566
{
567
gotoBreakpointAddress(itemIndex);
568
}
569
570
void CtrlBreakpointList::OnRightClick(int itemIndex, int column, const POINT& point)
571
{
572
showBreakpointMenu(itemIndex,point);
573
}
574
575
void CtrlBreakpointList::OnToggle(int item, bool newValue)
576
{
577
toggleEnabled(item);
578
}
579
580
void CtrlBreakpointList::showBreakpointMenu(int itemIndex, const POINT &pt)
581
{
582
bool isMemory;
583
int index = getBreakpointIndex(itemIndex, isMemory);
584
if (index == -1)
585
{
586
switch (TriggerContextMenu(ContextMenuID::NEWBREAKPOINT, GetHandle(), ContextPoint::FromClient(pt)))
587
{
588
case ID_DISASM_ADDNEWBREAKPOINT:
589
{
590
BreakpointWindow bpw(GetHandle(),cpu);
591
if (bpw.exec()) bpw.addBreakpoint();
592
}
593
break;
594
}
595
} else {
596
MemCheck mcPrev;
597
BreakPoint bpPrev;
598
if (isMemory) {
599
mcPrev = displayedMemChecks_[index];
600
} else {
601
bpPrev = displayedBreakPoints_[index];
602
}
603
604
HMENU subMenu = GetContextMenu(ContextMenuID::BREAKPOINTLIST);
605
if (isMemory) {
606
CheckMenuItem(subMenu, ID_DISASM_DISABLEBREAKPOINT, MF_BYCOMMAND | (mcPrev.IsEnabled() ? MF_CHECKED : MF_UNCHECKED));
607
} else {
608
CheckMenuItem(subMenu, ID_DISASM_DISABLEBREAKPOINT, MF_BYCOMMAND | (bpPrev.IsEnabled() ? MF_CHECKED : MF_UNCHECKED));
609
}
610
611
switch (TriggerContextMenu(ContextMenuID::BREAKPOINTLIST, GetHandle(), ContextPoint::FromClient(pt)))
612
{
613
case ID_DISASM_DISABLEBREAKPOINT:
614
if (isMemory) {
615
CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, BreakAction(mcPrev.result ^ BREAK_ACTION_PAUSE));
616
} else {
617
CBreakPoints::ChangeBreakPoint(bpPrev.addr, BreakAction(bpPrev.result ^ BREAK_ACTION_PAUSE));
618
}
619
break;
620
case ID_DISASM_EDITBREAKPOINT:
621
editBreakpoint(itemIndex);
622
break;
623
case ID_DISASM_ADDNEWBREAKPOINT:
624
{
625
BreakpointWindow bpw(GetHandle(),cpu);
626
if (bpw.exec()) bpw.addBreakpoint();
627
}
628
break;
629
case ID_DISASM_DELETEBREAKPOINT:
630
removeBreakpoint(itemIndex);
631
break;
632
}
633
}
634
}
635
636
//
637
// CtrlStackTraceView
638
//
639
640
CtrlStackTraceView::CtrlStackTraceView(HWND hwnd, DebugInterface* cpu, CtrlDisAsmView* disasm)
641
: GenericListControl(hwnd,stackTraceListDef),cpu(cpu),disasm(disasm)
642
{
643
Update();
644
}
645
646
bool CtrlStackTraceView::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue)
647
{
648
switch(msg)
649
{
650
case WM_KEYDOWN:
651
if (wParam == VK_TAB)
652
{
653
returnValue = 0;
654
SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0);
655
return true;
656
}
657
break;
658
case WM_GETDLGCODE:
659
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
660
{
661
if (wParam == VK_TAB || wParam == VK_RETURN)
662
{
663
returnValue = DLGC_WANTMESSAGE;
664
return true;
665
}
666
}
667
break;
668
}
669
670
return false;
671
}
672
673
void CtrlStackTraceView::GetColumnText(wchar_t* dest, int row, int col)
674
{
675
// We should have emptied the list if g_symbolMap is nullptr, but apparently we don't,
676
// so let's have a sanity check here.
677
if (row < 0 || row >= (int)frames.size() || !g_symbolMap) {
678
return;
679
}
680
681
switch (col)
682
{
683
case SF_ENTRY:
684
wsprintf(dest,L"%08X",frames[row].entry);
685
break;
686
case SF_ENTRYNAME:
687
{
688
const std::string sym = g_symbolMap->GetLabelString(frames[row].entry);
689
if (!sym.empty()) {
690
wcscpy(dest, ConvertUTF8ToWString(sym).c_str());
691
} else {
692
wcscpy(dest,L"-");
693
}
694
}
695
break;
696
case SF_CURPC:
697
wsprintf(dest,L"%08X",frames[row].pc);
698
break;
699
case SF_CUROPCODE:
700
{
701
char temp[512];
702
disasm->getOpcodeText(frames[row].pc, temp, sizeof(temp));
703
wcscpy(dest, ConvertUTF8ToWString(temp).c_str());
704
}
705
break;
706
case SF_CURSP:
707
wsprintf(dest,L"%08X",frames[row].sp);
708
break;
709
case SF_FRAMESIZE:
710
wsprintf(dest,L"%08X",frames[row].stackSize);
711
break;
712
}
713
}
714
715
void CtrlStackTraceView::OnDoubleClick(int itemIndex, int column)
716
{
717
SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,frames[itemIndex].pc,0);
718
}
719
720
void CtrlStackTraceView::loadStackTrace() {
721
auto memLock = Memory::Lock();
722
if (!PSP_IsInited())
723
return;
724
725
auto threads = GetThreadsInfo();
726
727
u32 entry = 0, stackTop = 0;
728
for (size_t i = 0; i < threads.size(); i++)
729
{
730
if (threads[i].isCurrent)
731
{
732
entry = threads[i].entrypoint;
733
stackTop = threads[i].initialStack;
734
break;
735
}
736
}
737
738
if (entry != 0) {
739
frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop);
740
} else {
741
frames.clear();
742
}
743
Update();
744
}
745
746
//
747
// CtrlModuleList
748
//
749
750
CtrlModuleList::CtrlModuleList(HWND hwnd, DebugInterface* cpu)
751
: GenericListControl(hwnd,moduleListDef),cpu(cpu)
752
{
753
Update();
754
}
755
756
bool CtrlModuleList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue)
757
{
758
switch(msg)
759
{
760
case WM_KEYDOWN:
761
if (wParam == VK_TAB)
762
{
763
returnValue = 0;
764
SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0);
765
return true;
766
}
767
break;
768
case WM_GETDLGCODE:
769
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
770
{
771
if (wParam == VK_TAB || wParam == VK_RETURN)
772
{
773
returnValue = DLGC_WANTMESSAGE;
774
return true;
775
}
776
}
777
break;
778
}
779
780
return false;
781
}
782
783
void CtrlModuleList::GetColumnText(wchar_t* dest, int row, int col)
784
{
785
if (row < 0 || row >= (int)modules.size()) {
786
return;
787
}
788
789
switch (col)
790
{
791
case ML_NAME:
792
wcscpy(dest,ConvertUTF8ToWString(modules[row].name).c_str());
793
break;
794
case ML_ADDRESS:
795
wsprintf(dest,L"%08X",modules[row].address);
796
break;
797
case ML_SIZE:
798
wsprintf(dest,L"%08X",modules[row].size);
799
break;
800
case ML_ACTIVE:
801
wcscpy(dest,modules[row].active ? L"true" : L"false");
802
break;
803
}
804
}
805
806
void CtrlModuleList::OnDoubleClick(int itemIndex, int column)
807
{
808
SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,modules[itemIndex].address,0);
809
}
810
811
void CtrlModuleList::loadModules()
812
{
813
if (g_symbolMap) {
814
modules = g_symbolMap->getAllModules();
815
} else {
816
modules.clear();
817
}
818
Update();
819
}
820
821
// In case you modify things in the memory view.
822
static constexpr UINT_PTR IDT_CHECK_REFRESH = 0xC0DE0044;
823
824
CtrlWatchList::CtrlWatchList(HWND hwnd, DebugInterface *cpu)
825
: GenericListControl(hwnd, watchListDef), cpu_(cpu) {
826
SetSendInvalidRows(true);
827
Update();
828
829
SetTimer(GetHandle(), IDT_CHECK_REFRESH, 1000U, nullptr);
830
}
831
832
void CtrlWatchList::RefreshValues() {
833
int steppingCounter = Core_GetSteppingCounter();
834
int changes = false;
835
for (auto &watch : watches_) {
836
if (watch.steppingCounter != steppingCounter) {
837
watch.lastValue = watch.currentValue;
838
watch.steppingCounter = steppingCounter;
839
changes = true;
840
}
841
842
uint32_t prevValue = watch.currentValue;
843
watch.evaluateFailed = !cpu_->parseExpression(watch.expression, watch.currentValue);
844
if (prevValue != watch.currentValue)
845
changes = true;
846
}
847
848
if (changes)
849
Update();
850
}
851
852
bool CtrlWatchList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT &returnValue) {
853
switch (msg) {
854
case WM_KEYDOWN:
855
switch (wParam) {
856
case VK_TAB:
857
returnValue = 0;
858
SendMessage(GetParent(GetHandle()), WM_DEB_TABPRESSED, 0, 0);
859
return true;
860
case VK_RETURN:
861
returnValue = 0;
862
EditWatch(GetSelectedIndex());
863
return true;
864
case VK_DELETE:
865
returnValue = 0;
866
DeleteWatch(GetSelectedIndex());
867
return true;
868
default:
869
break;
870
}
871
break;
872
case WM_GETDLGCODE:
873
if (lParam && ((MSG *)lParam)->message == WM_KEYDOWN) {
874
if (wParam == VK_TAB || wParam == VK_RETURN || wParam == VK_DELETE) {
875
returnValue = DLGC_WANTMESSAGE;
876
return true;
877
}
878
}
879
break;
880
case WM_TIMER:
881
if (wParam == IDT_CHECK_REFRESH) {
882
RefreshValues();
883
return true;
884
}
885
break;
886
}
887
888
return false;
889
}
890
891
void CtrlWatchList::GetColumnText(wchar_t *dest, int row, int col) {
892
const auto &watch = watches_[row];
893
switch (col) {
894
case WL_NAME:
895
wcsncpy(dest, ConvertUTF8ToWString(watch.name).c_str(), 255);
896
dest[255] = 0;
897
break;
898
case WL_EXPRESSION:
899
wcsncpy(dest, ConvertUTF8ToWString(watch.originalExpression).c_str(), 255);
900
dest[255] = 0;
901
break;
902
case WL_VALUE:
903
if (watch.evaluateFailed) {
904
wcscpy(dest, L"(failed to evaluate)");
905
} else {
906
const uint32_t &value = watch.currentValue;
907
float valuef = 0.0f;
908
switch (watch.format) {
909
case WatchFormat::HEX:
910
wsprintf(dest, L"0x%08X", value);
911
break;
912
case WatchFormat::INT:
913
wsprintf(dest, L"%d", (int32_t)value);
914
break;
915
case WatchFormat::FLOAT:
916
memcpy(&valuef, &value, sizeof(valuef));
917
swprintf_s(dest, 255, L"%f", valuef);
918
break;
919
case WatchFormat::STR:
920
if (Memory::IsValidAddress(value)) {
921
uint32_t len = Memory::ValidSize(value, 255);
922
swprintf_s(dest, 255, L"%.*S", len, Memory::GetCharPointer(value));
923
} else {
924
wsprintf(dest, L"(0x%08X)", value);
925
}
926
break;
927
}
928
}
929
break;
930
}
931
}
932
933
void CtrlWatchList::OnRightClick(int itemIndex, int column, const POINT &pt) {
934
if (itemIndex == -1) {
935
switch (TriggerContextMenu(ContextMenuID::CPUADDWATCH, GetHandle(), ContextPoint::FromClient(pt))) {
936
case ID_DISASM_ADDNEWBREAKPOINT:
937
AddWatch();
938
break;
939
}
940
} else {
941
switch (TriggerContextMenu(ContextMenuID::CPUWATCHLIST, GetHandle(), ContextPoint::FromClient(pt))) {
942
case ID_DISASM_EDITBREAKPOINT:
943
EditWatch(itemIndex);
944
break;
945
case ID_DISASM_DELETEBREAKPOINT:
946
DeleteWatch(itemIndex);
947
break;
948
case ID_DISASM_ADDNEWBREAKPOINT:
949
AddWatch();
950
break;
951
}
952
}
953
}
954
955
bool CtrlWatchList::OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) {
956
if (row >= 0 && HasWatchChanged(row)) {
957
msg->clrText = RGB(255, 0, 0);
958
return true;
959
}
960
return false;
961
}
962
963
void CtrlWatchList::AddWatch() {
964
WatchItemWindow win(nullptr, GetHandle(), cpu_);
965
if (win.Exec()) {
966
WatchInfo info;
967
if (cpu_->initExpression(win.GetExpression().c_str(), info.expression)) {
968
info.name = win.GetName();
969
info.originalExpression = win.GetExpression();
970
info.format = win.GetFormat();
971
watches_.push_back(info);
972
RefreshValues();
973
} else {
974
char errorMessage[512];
975
snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", win.GetExpression().c_str(), getExpressionError());
976
MessageBoxA(GetHandle(), errorMessage, "Error", MB_OK);
977
}
978
}
979
}
980
981
void CtrlWatchList::EditWatch(int pos) {
982
auto &watch = watches_[pos];
983
WatchItemWindow win(nullptr, GetHandle(), cpu_);
984
win.Init(watch.name, watch.originalExpression, watch.format);
985
if (win.Exec()) {
986
if (cpu_->initExpression(win.GetExpression().c_str(), watch.expression)) {
987
watch.name = win.GetName();
988
watch.originalExpression = win.GetExpression();
989
watch.format = win.GetFormat();
990
RefreshValues();
991
} else {
992
char errorMessage[512];
993
snprintf(errorMessage, sizeof(errorMessage), "Invalid expression \"%s\": %s", win.GetExpression().c_str(), getExpressionError());
994
MessageBoxA(GetHandle(), errorMessage, "Error", MB_OK);
995
}
996
}
997
}
998
999
void CtrlWatchList::DeleteWatch(int pos) {
1000
watches_.erase(watches_.begin() + pos);
1001
Update();
1002
}
1003
1004
bool CtrlWatchList::HasWatchChanged(int pos) {
1005
return watches_[pos].lastValue != watches_[pos].currentValue;
1006
}
1007
1008