Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Debugger/DisassemblyManager.cpp
3187 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 "ppsspp_config.h"
19
20
#include <string>
21
#include <algorithm>
22
#include <map>
23
24
#include "ext/xxhash.h"
25
26
#include "Common/CommonTypes.h"
27
#include "Common/Data/Encoding/Utf8.h"
28
#include "Common/Log.h"
29
#include "Common/StringUtils.h"
30
#include "Core/MemMap.h"
31
#include "Core/System.h"
32
#include "Core/MIPS/MIPSDebugInterface.h"
33
#include "Core/MIPS/MIPSCodeUtils.h"
34
#include "Core/MIPS/MIPSTables.h"
35
#include "Core/Debugger/DebugInterface.h"
36
#include "Core/Debugger/SymbolMap.h"
37
#include "Core/Debugger/DisassemblyManager.h"
38
39
DisassemblyManager g_disassemblyManager;
40
41
bool isInInterval(u32 start, u32 size, u32 value) {
42
return start <= value && value <= (start+size-1);
43
}
44
45
bool IsLikelyStringAt(uint32_t addr) {
46
uint32_t maxLen = Memory::ValidSize(addr, 128);
47
if (maxLen <= 1)
48
return false;
49
const char *p = Memory::GetCharPointer(addr);
50
// If there's no terminator nearby, let's say no.
51
if (memchr(p, 0, maxLen) == nullptr)
52
return false;
53
54
// Allow tabs and newlines.
55
static constexpr bool validControl[] = {
56
false, false, false, false, false, false, false, false,
57
false, true, true, true, false, true, false, false,
58
false, false, false, false, false, false, false, false,
59
false, false, false, false, false, false, false, false,
60
};
61
62
// Check that there's some bytes before the terminator that look like a string.
63
UTF8 utf(p);
64
if (utf.end())
65
return false;
66
67
char verify[4];
68
while (!utf.end()) {
69
if (utf.invalid())
70
return false;
71
72
int pos = utf.byteIndex();
73
uint32_t c = utf.next();
74
int len = UTF8::encode(verify, c);
75
// Our decoder is a bit lax, so let's verify this is a normal encoding.
76
// This prevents us from trying to output invalid encodings in the debugger.
77
if (memcmp(p + pos, verify, len) != 0 || pos + len != utf.byteIndex())
78
return false;
79
80
if (c < ARRAY_SIZE(validControl) && !validControl[c])
81
return false;
82
if (c > 0x0010FFFF)
83
return false;
84
}
85
86
return true;
87
}
88
89
static HashType computeHash(u32 address, u32 size)
90
{
91
if (!Memory::IsValidAddress(address))
92
return 0;
93
94
size = Memory::ValidSize(address, size);
95
#if PPSSPP_ARCH(AMD64)
96
return XXH3_64bits(Memory::GetPointerUnchecked(address), size);
97
#else
98
return XXH3_64bits(Memory::GetPointerUnchecked(address), size) & 0xFFFFFFFF;
99
#endif
100
}
101
102
103
static void parseDisasm(const char *disasm, char *opcode, size_t opcodeSize, char *arguments, size_t argumentsSize, bool insertSymbols) {
104
// copy opcode
105
size_t opcodePos = 0;
106
while (*disasm != 0 && *disasm != '\t' && opcodePos + 1 < opcodeSize) {
107
opcode[opcodePos++] = *disasm++;
108
}
109
opcode[opcodePos] = 0;
110
111
// Otherwise it's a tab, and we skip intentionally.
112
if (*disasm++ == 0) {
113
*arguments = 0;
114
return;
115
}
116
117
const char* jumpAddress = strstr(disasm,"->$");
118
const char* jumpRegister = strstr(disasm,"->");
119
size_t argumentsPos = 0;
120
while (*disasm != 0 && argumentsPos + 1 < argumentsSize) {
121
// parse symbol
122
if (disasm == jumpAddress) {
123
u32 branchTarget = 0;
124
sscanf(disasm+3, "%08x", &branchTarget);
125
const std::string addressSymbol = g_symbolMap->GetLabelString(branchTarget);
126
if (!addressSymbol.empty() && insertSymbols) {
127
argumentsPos += snprintf(&arguments[argumentsPos], argumentsSize - argumentsPos, "%s", addressSymbol.c_str());
128
} else {
129
argumentsPos += snprintf(&arguments[argumentsPos], argumentsSize - argumentsPos, "0x%08X", branchTarget);
130
}
131
132
disasm += 3+8;
133
continue;
134
}
135
136
if (disasm == jumpRegister)
137
disasm += 2;
138
139
if (*disasm == ' ') {
140
disasm++;
141
continue;
142
}
143
arguments[argumentsPos++] = *disasm++;
144
}
145
146
arguments[argumentsPos] = 0;
147
}
148
149
std::map<u32,DisassemblyEntry*>::iterator findDisassemblyEntry(std::map<u32,DisassemblyEntry*>& entries, u32 address, bool exact)
150
{
151
if (exact)
152
return entries.find(address);
153
154
if (entries.size() == 0)
155
return entries.end();
156
157
// find first elem that's >= address
158
auto it = entries.lower_bound(address);
159
if (it != entries.end())
160
{
161
// it may be an exact match
162
if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address))
163
return it;
164
165
// otherwise it may point to the next
166
if (it != entries.begin())
167
{
168
it--;
169
if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address))
170
return it;
171
}
172
}
173
174
// check last entry manually
175
auto rit = entries.rbegin();
176
if (isInInterval(rit->second->getLineAddress(0),rit->second->getTotalSize(),address))
177
{
178
return (++rit).base();
179
}
180
181
// no match otherwise
182
return entries.end();
183
}
184
185
void DisassemblyManager::setCpu(DebugInterface *cpu) {
186
_dbg_assert_(cpu);
187
cpu_ = cpu;
188
};
189
190
void DisassemblyManager::analyze(u32 address, u32 size = 1024)
191
{
192
u32 end = address+size;
193
194
address &= ~3;
195
u32 start = address;
196
197
while (address < end && start <= address)
198
{
199
if (PSP_GetBootState() != BootState::Complete)
200
return;
201
202
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
203
auto it = findDisassemblyEntry(entries, address, false);
204
if (it != entries.end())
205
{
206
DisassemblyEntry* entry = it->second;
207
entry->recheck();
208
address = entry->getLineAddress(0)+entry->getTotalSize();
209
continue;
210
}
211
212
SymbolInfo info;
213
if (!g_symbolMap->GetSymbolInfo(&info,address,ST_ALL))
214
{
215
if (address % 4)
216
{
217
u32 next = std::min<u32>((address+3) & ~3,g_symbolMap->GetNextSymbolAddress(address,ST_ALL));
218
DisassemblyData* data = new DisassemblyData(address,next-address,DATATYPE_BYTE);
219
entries[address] = data;
220
address = next;
221
continue;
222
}
223
224
u32 next = g_symbolMap->GetNextSymbolAddress(address,ST_ALL);
225
226
if ((next % 4) && next != (u32)-1)
227
{
228
u32 alignedNext = next & ~3;
229
230
if (alignedNext != address)
231
{
232
DisassemblyOpcode* opcode = new DisassemblyOpcode(address,(alignedNext-address)/4);
233
entries[address] = opcode;
234
}
235
236
DisassemblyData* data = new DisassemblyData(address,next-alignedNext,DATATYPE_BYTE);
237
entries[alignedNext] = data;
238
} else {
239
DisassemblyOpcode* opcode = new DisassemblyOpcode(address,(next-address)/4);
240
entries[address] = opcode;
241
}
242
243
address = next;
244
continue;
245
}
246
247
switch (info.type)
248
{
249
case ST_FUNCTION:
250
{
251
DisassemblyFunction* function = new DisassemblyFunction(info.address,info.size);
252
entries[info.address] = function;
253
address = info.address+info.size;
254
}
255
break;
256
case ST_DATA:
257
{
258
DisassemblyData* data = new DisassemblyData(info.address,info.size,g_symbolMap->GetDataType(info.address));
259
entries[info.address] = data;
260
address = info.address+info.size;
261
}
262
break;
263
default:
264
break;
265
}
266
}
267
268
}
269
270
std::vector<BranchLine> DisassemblyManager::getBranchLines(u32 start, u32 size)
271
{
272
std::vector<BranchLine> result;
273
274
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
275
auto it = findDisassemblyEntry(entries,start,false);
276
if (it != entries.end())
277
{
278
do
279
{
280
it->second->getBranchLines(start,size,result);
281
it++;
282
} while (it != entries.end() && start+size > it->second->getLineAddress(0));
283
}
284
285
return result;
286
}
287
288
void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo &dest, DebugInterface *cpuDebug)
289
{
290
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
291
auto it = findDisassemblyEntry(entries,address,false);
292
if (it == entries.end()) {
293
analyze(address);
294
it = findDisassemblyEntry(entries,address,false);
295
}
296
297
if (it != entries.end()) {
298
DisassemblyEntry *entry = it->second;
299
if (entry->disassemble(address, dest, insertSymbols, cpuDebug))
300
return;
301
}
302
303
dest.type = DISTYPE_OTHER;
304
memset(&dest.info, 0, sizeof(dest.info));
305
dest.info.opcodeAddress = address;
306
if (address % 4)
307
dest.totalSize = ((address+3) & ~3)-address;
308
else
309
dest.totalSize = 4;
310
if (Memory::IsValidRange(address, 4)) {
311
dest.name = "ERROR";
312
dest.params = "Disassembly failure";
313
} else {
314
dest.name = "-";
315
dest.params.clear();
316
}
317
}
318
319
u32 DisassemblyManager::getStartAddress(u32 address)
320
{
321
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
322
auto it = findDisassemblyEntry(entries,address,false);
323
if (it == entries.end())
324
{
325
analyze(address);
326
it = findDisassemblyEntry(entries,address,false);
327
if (it == entries.end())
328
return address;
329
}
330
331
DisassemblyEntry* entry = it->second;
332
int line = entry->getLineNum(address,true);
333
return entry->getLineAddress(line);
334
}
335
336
u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n)
337
{
338
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
339
while (Memory::IsValidAddress(address))
340
{
341
auto it = findDisassemblyEntry(entries,address,false);
342
if (it == entries.end())
343
break;
344
while (it != entries.end()) {
345
DisassemblyEntry* entry = it->second;
346
int oldLineNum = entry->getLineNum(address,true);
347
if (n <= oldLineNum)
348
{
349
return entry->getLineAddress(oldLineNum-n);
350
}
351
352
address = entry->getLineAddress(0)-1;
353
n -= oldLineNum+1;
354
it = findDisassemblyEntry(entries,address,false);
355
}
356
357
analyze(address-127,128);
358
}
359
360
return (address - n * 4) & ~3;
361
}
362
363
u32 DisassemblyManager::getNthNextAddress(u32 address, int n)
364
{
365
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
366
while (Memory::IsValidAddress(address))
367
{
368
auto it = findDisassemblyEntry(entries,address,false);
369
if (it == entries.end()) {
370
break;
371
}
372
373
while (it != entries.end())
374
{
375
DisassemblyEntry* entry = it->second;
376
int oldLineNum = entry->getLineNum(address,true);
377
int oldNumLines = entry->getNumLines();
378
if (oldLineNum+n < oldNumLines)
379
{
380
return entry->getLineAddress(oldLineNum+n);
381
}
382
383
address = entry->getLineAddress(0)+entry->getTotalSize();
384
n -= (oldNumLines-oldLineNum);
385
it = findDisassemblyEntry(entries,address,false);
386
}
387
388
analyze(address);
389
}
390
391
return (address + n * 4) & ~3;
392
}
393
394
DisassemblyManager::~DisassemblyManager() {
395
}
396
397
void DisassemblyManager::clear()
398
{
399
std::lock_guard<std::recursive_mutex> guard(entriesLock_);
400
for (auto it = entries.begin(); it != entries.end(); it++)
401
{
402
delete it->second;
403
}
404
entries.clear();
405
}
406
407
DisassemblyFunction::DisassemblyFunction(u32 _address, u32 _size): address(_address), size(_size)
408
{
409
if (PSP_GetBootState() != BootState::Complete)
410
return;
411
412
hash = computeHash(address,size);
413
load();
414
}
415
416
DisassemblyFunction::~DisassemblyFunction() {
417
clear();
418
}
419
420
void DisassemblyFunction::recheck()
421
{
422
if (PSP_GetBootState() != BootState::Complete)
423
return;
424
425
HashType newHash = computeHash(address,size);
426
if (hash != newHash)
427
{
428
hash = newHash;
429
clear();
430
load();
431
}
432
}
433
434
int DisassemblyFunction::getNumLines()
435
{
436
std::lock_guard<std::recursive_mutex> guard(lock_);
437
return (int) lineAddresses.size();
438
}
439
440
int DisassemblyFunction::getLineNum(u32 address, bool findStart)
441
{
442
std::lock_guard<std::recursive_mutex> guard(lock_);
443
if (findStart)
444
{
445
int last = (int)lineAddresses.size() - 1;
446
for (int i = 0; i < last; i++)
447
{
448
u32 next = lineAddresses[i + 1];
449
if (lineAddresses[i] <= address && next > address)
450
return i;
451
}
452
if (lineAddresses[last] <= address && this->address + this->size > address)
453
return last;
454
}
455
else
456
{
457
int last = (int)lineAddresses.size() - 1;
458
for (int i = 0; i < last; i++)
459
{
460
if (lineAddresses[i] == address)
461
return i;
462
}
463
if (lineAddresses[last] == address)
464
return last;
465
}
466
467
return 0;
468
}
469
470
u32 DisassemblyFunction::getLineAddress(int line)
471
{
472
std::lock_guard<std::recursive_mutex> guard(lock_);
473
return lineAddresses[line];
474
}
475
476
bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug)
477
{
478
std::lock_guard<std::recursive_mutex> guard(lock_);
479
auto it = findDisassemblyEntry(entries,address,false);
480
if (it == entries.end())
481
return false;
482
483
return it->second->disassemble(address, dest, insertSymbols, cpuDebug);
484
}
485
486
void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
487
{
488
u32 end = start+size;
489
490
std::lock_guard<std::recursive_mutex> guard(lock_);
491
for (size_t i = 0; i < lines.size(); i++)
492
{
493
BranchLine& line = lines[i];
494
495
u32 first = line.first;
496
u32 second = line.second;
497
498
// skip branches that are entirely before or entirely after the window
499
if ((first < start && second < start) ||
500
(first > end && second > end))
501
continue;
502
503
dest.push_back(line);
504
}
505
}
506
507
#define NUM_LANES 16
508
509
void DisassemblyFunction::generateBranchLines()
510
{
511
struct LaneInfo
512
{
513
bool used;
514
u32 end;
515
};
516
517
LaneInfo lanes[NUM_LANES];
518
for (int i = 0; i < NUM_LANES; i++)
519
lanes[i].used = false;
520
521
u32 end = address+size;
522
523
std::lock_guard<std::recursive_mutex> guard(lock_);
524
DebugInterface *cpu = g_disassemblyManager.getCpu();
525
for (u32 funcPos = address; funcPos < end; funcPos += 4)
526
{
527
MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu, funcPos);
528
529
bool inFunction = (opInfo.branchTarget >= address && opInfo.branchTarget < end);
530
if (opInfo.isBranch && !opInfo.isBranchToRegister && !opInfo.isLinkedBranch && inFunction) {
531
if (!Memory::IsValidAddress(opInfo.branchTarget))
532
continue;
533
534
BranchLine line;
535
if (opInfo.branchTarget < funcPos) {
536
line.first = opInfo.branchTarget;
537
line.second = funcPos;
538
line.type = LINE_UP;
539
} else {
540
line.first = funcPos;
541
line.second = opInfo.branchTarget;
542
line.type = LINE_DOWN;
543
}
544
545
lines.push_back(line);
546
}
547
}
548
549
std::sort(lines.begin(),lines.end());
550
for (size_t i = 0; i < lines.size(); i++)
551
{
552
for (int l = 0; l < NUM_LANES; l++)
553
{
554
if (lines[i].first > lanes[l].end)
555
lanes[l].used = false;
556
}
557
558
int lane = -1;
559
for (int l = 0; l < NUM_LANES; l++)
560
{
561
if (lanes[l].used == false)
562
{
563
lane = l;
564
break;
565
}
566
}
567
568
if (lane == -1)
569
{
570
// Let's just pile on.
571
lines[i].laneIndex = 15;
572
continue;
573
}
574
575
lanes[lane].end = lines[i].second;
576
lanes[lane].used = true;
577
lines[i].laneIndex = lane;
578
}
579
}
580
581
void DisassemblyFunction::addOpcodeSequence(u32 start, u32 end)
582
{
583
DisassemblyOpcode* opcode = new DisassemblyOpcode(start,(end-start)/4);
584
std::lock_guard<std::recursive_mutex> guard(lock_);
585
entries[start] = opcode;
586
lineAddresses.reserve((end - start) / 4);
587
for (u32 pos = start; pos < end; pos += 4)
588
{
589
lineAddresses.push_back(pos);
590
}
591
}
592
593
void DisassemblyFunction::load()
594
{
595
generateBranchLines();
596
597
// gather all branch targets
598
std::set<u32> branchTargets;
599
{
600
std::lock_guard<std::recursive_mutex> guard(lock_);
601
for (size_t i = 0; i < lines.size(); i++)
602
{
603
switch (lines[i].type)
604
{
605
case LINE_DOWN:
606
branchTargets.insert(lines[i].second);
607
break;
608
case LINE_UP:
609
branchTargets.insert(lines[i].first);
610
break;
611
default:
612
break;
613
}
614
}
615
}
616
617
DebugInterface *cpu = g_disassemblyManager.getCpu();
618
u32 funcPos = address;
619
u32 funcEnd = address+size;
620
621
u32 nextData = g_symbolMap->GetNextSymbolAddress(funcPos-1,ST_DATA);
622
u32 opcodeSequenceStart = funcPos;
623
while (funcPos < funcEnd)
624
{
625
if (funcPos == nextData)
626
{
627
if (opcodeSequenceStart != funcPos)
628
addOpcodeSequence(opcodeSequenceStart,funcPos);
629
630
DisassemblyData* data = new DisassemblyData(funcPos,g_symbolMap->GetDataSize(funcPos),g_symbolMap->GetDataType(funcPos));
631
std::lock_guard<std::recursive_mutex> guard(lock_);
632
entries[funcPos] = data;
633
lineAddresses.push_back(funcPos);
634
funcPos += data->getTotalSize();
635
636
nextData = g_symbolMap->GetNextSymbolAddress(funcPos-1,ST_DATA);
637
opcodeSequenceStart = funcPos;
638
continue;
639
}
640
641
// force align
642
if (funcPos % 4)
643
{
644
u32 nextPos = (funcPos+3) & ~3;
645
646
DisassemblyComment* comment = new DisassemblyComment(funcPos,nextPos-funcPos,".align","4");
647
std::lock_guard<std::recursive_mutex> guard(lock_);
648
entries[funcPos] = comment;
649
lineAddresses.push_back(funcPos);
650
651
funcPos = nextPos;
652
opcodeSequenceStart = funcPos;
653
continue;
654
}
655
656
MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos);
657
u32 opAddress = funcPos;
658
funcPos += 4;
659
660
// skip branches and their delay slots
661
if (opInfo.isBranch)
662
{
663
funcPos += 4;
664
continue;
665
}
666
667
// lui
668
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData)
669
{
670
MIPSOpcode next = Memory::Read_Instruction(funcPos);
671
MIPSInfo nextInfo = MIPSGetInfo(next);
672
673
u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next.encoding & 0xFFFF);
674
int rt = MIPS_GET_RT(opInfo.encodedOpcode);
675
676
int nextRs = MIPS_GET_RS(next.encoding);
677
int nextRt = MIPS_GET_RT(next.encoding);
678
679
// both rs and rt of the second op have to match rt of the first,
680
// otherwise there may be hidden consequences if the macro is displayed.
681
// also, don't create a macro if something branches into the middle of it
682
if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end())
683
{
684
DisassemblyMacro* macro = NULL;
685
switch (MIPS_GET_OP(next.encoding))
686
{
687
case 0x09: // addiu
688
macro = new DisassemblyMacro(opAddress);
689
macro->setMacroLi(immediate,rt);
690
funcPos += 4;
691
break;
692
case 0x20: // lb
693
case 0x21: // lh
694
case 0x23: // lw
695
case 0x24: // lbu
696
case 0x25: // lhu
697
case 0x28: // sb
698
case 0x29: // sh
699
case 0x2B: // sw
700
macro = new DisassemblyMacro(opAddress);
701
702
int dataSize = MIPSGetMemoryAccessSize(next);
703
if (dataSize == 0) {
704
delete macro;
705
return;
706
}
707
708
macro->setMacroMemory(MIPSGetName(next),immediate,rt,dataSize);
709
funcPos += 4;
710
break;
711
}
712
713
if (macro != NULL)
714
{
715
if (opcodeSequenceStart != opAddress)
716
addOpcodeSequence(opcodeSequenceStart,opAddress);
717
718
std::lock_guard<std::recursive_mutex> guard(lock_);
719
entries[opAddress] = macro;
720
for (int i = 0; i < macro->getNumLines(); i++)
721
{
722
lineAddresses.push_back(macro->getLineAddress(i));
723
}
724
725
opcodeSequenceStart = funcPos;
726
continue;
727
}
728
}
729
}
730
731
// just a normal opcode
732
}
733
734
if (opcodeSequenceStart != funcPos)
735
addOpcodeSequence(opcodeSequenceStart,funcPos);
736
}
737
738
void DisassemblyFunction::clear()
739
{
740
std::lock_guard<std::recursive_mutex> guard(lock_);
741
for (auto it = entries.begin(); it != entries.end(); it++)
742
{
743
delete it->second;
744
}
745
746
entries.clear();
747
lines.clear();
748
lineAddresses.clear();
749
hash = 0;
750
}
751
752
bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) {
753
char opcode[64],arguments[256];
754
char dizz[512];
755
DisAsm(address, dizz, sizeof(dizz));
756
parseDisasm(dizz, opcode, sizeof(opcode), arguments, sizeof(arguments), insertSymbols);
757
dest.type = DISTYPE_OPCODE;
758
dest.name = opcode;
759
dest.params = arguments;
760
dest.totalSize = 4;
761
dest.info = MIPSAnalyst::GetOpcodeInfo(cpuDebug, address);
762
return true;
763
}
764
765
void DisassemblyOpcode::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
766
{
767
if (start < address)
768
{
769
size = start+size-address;
770
start = address;
771
}
772
773
if (start+size > address+num*4)
774
size = address+num*4-start;
775
776
int lane = 0;
777
for (u32 pos = start; pos < start+size; pos += 4)
778
{
779
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(g_disassemblyManager.getCpu(),pos);
780
if (info.isBranch && !info.isBranchToRegister && !info.isLinkedBranch) {
781
if (!Memory::IsValidAddress(info.branchTarget))
782
continue;
783
784
BranchLine line;
785
line.laneIndex = lane++;
786
787
if (info.branchTarget < pos) {
788
line.first = info.branchTarget;
789
line.second = pos;
790
line.type = LINE_UP;
791
} else {
792
line.first = pos;
793
line.second = info.branchTarget;
794
line.type = LINE_DOWN;
795
}
796
797
dest.push_back(line);
798
}
799
}
800
}
801
802
803
void DisassemblyMacro::setMacroLi(u32 _immediate, u8 _rt)
804
{
805
type = MACRO_LI;
806
name = "li";
807
immediate = _immediate;
808
rt = _rt;
809
numOpcodes = 2;
810
}
811
812
void DisassemblyMacro::setMacroMemory(std::string_view _name, u32 _immediate, u8 _rt, int _dataSize)
813
{
814
type = MACRO_MEMORYIMM;
815
name = _name;
816
immediate = _immediate;
817
rt = _rt;
818
dataSize = _dataSize;
819
numOpcodes = 2;
820
}
821
822
bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug)
823
{
824
char buffer[64];
825
dest.type = DISTYPE_MACRO;
826
dest.info = MIPSAnalyst::GetOpcodeInfo(cpuDebug, address);
827
828
std::string addressSymbol;
829
switch (type)
830
{
831
case MACRO_LI:
832
dest.name = name;
833
834
addressSymbol = g_symbolMap->GetLabelString(immediate);
835
if (!addressSymbol.empty() && insertSymbols) {
836
snprintf(buffer, sizeof(buffer), "%s,%s", MIPSDebugInterface::GetRegName(0, rt).c_str(), addressSymbol.c_str());
837
} else {
838
snprintf(buffer, sizeof(buffer), "%s,0x%08X", MIPSDebugInterface::GetRegName(0, rt).c_str(), immediate);
839
}
840
841
dest.params = buffer;
842
843
dest.info.hasRelevantAddress = true;
844
dest.info.relevantAddress = immediate;
845
break;
846
case MACRO_MEMORYIMM:
847
dest.name = name;
848
849
addressSymbol = g_symbolMap->GetLabelString(immediate);
850
if (!addressSymbol.empty() && insertSymbols) {
851
snprintf(buffer, sizeof(buffer), "%s,%s", MIPSDebugInterface::GetRegName(0, rt).c_str(), addressSymbol.c_str());
852
} else {
853
snprintf(buffer, sizeof(buffer), "%s,0x%08X", MIPSDebugInterface::GetRegName(0, rt).c_str(), immediate);
854
}
855
856
dest.params = buffer;
857
858
dest.info.isDataAccess = true;
859
dest.info.dataAddress = immediate;
860
dest.info.dataSize = dataSize;
861
862
dest.info.hasRelevantAddress = true;
863
dest.info.relevantAddress = immediate;
864
break;
865
default:
866
return false;
867
}
868
869
dest.totalSize = getTotalSize();
870
return true;
871
}
872
873
DisassemblyData::DisassemblyData(u32 _address, u32 _size, DataType _type): address(_address), size(_size), type(_type)
874
{
875
_dbg_assert_(PSP_GetBootState() == BootState::Complete);
876
hash = computeHash(address,size);
877
createLines();
878
}
879
880
void DisassemblyData::recheck()
881
{
882
if (PSP_GetBootState() != BootState::Complete)
883
return;
884
885
HashType newHash = computeHash(address,size);
886
if (newHash != hash)
887
{
888
hash = newHash;
889
createLines();
890
}
891
}
892
893
bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug)
894
{
895
dest.type = DISTYPE_DATA;
896
897
switch (type)
898
{
899
case DATATYPE_BYTE:
900
dest.name = ".byte";
901
break;
902
case DATATYPE_HALFWORD:
903
dest.name = ".half";
904
break;
905
case DATATYPE_WORD:
906
dest.name = ".word";
907
break;
908
case DATATYPE_ASCII:
909
dest.name = ".ascii";
910
break;
911
default:
912
return false;
913
}
914
915
std::lock_guard<std::recursive_mutex> guard(lock_);
916
auto it = lines.find(address);
917
if (it == lines.end())
918
return false;
919
920
dest.params = it->second.text;
921
dest.totalSize = it->second.size;
922
return true;
923
}
924
925
int DisassemblyData::getLineNum(u32 address, bool findStart)
926
{
927
std::lock_guard<std::recursive_mutex> guard(lock_);
928
auto it = lines.upper_bound(address);
929
if (it != lines.end())
930
{
931
if (it == lines.begin())
932
return 0;
933
it--;
934
return it->second.lineNum;
935
}
936
937
return lines.rbegin()->second.lineNum;
938
}
939
940
void DisassemblyData::createLines()
941
{
942
std::lock_guard<std::recursive_mutex> guard(lock_);
943
lines.clear();
944
lineAddresses.clear();
945
946
u32 pos = address;
947
const u32 end = address+size;
948
const u32 maxChars = g_disassemblyManager.getMaxParamChars();
949
950
std::string currentLine;
951
u32 currentLineStart = pos;
952
953
int lineCount = 0;
954
if (type == DATATYPE_ASCII)
955
{
956
bool inString = false;
957
while (pos < end)
958
{
959
u8 b = Memory::Read_U8(pos++);
960
if (b >= 0x20 && b <= 0x7F)
961
{
962
if (currentLine.size()+1 >= maxChars)
963
{
964
if (inString == true)
965
currentLine += "\"";
966
967
DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++};
968
lines[currentLineStart] = entry;
969
lineAddresses.push_back(currentLineStart);
970
971
currentLine.clear();
972
currentLineStart = pos-1;
973
inString = false;
974
}
975
976
if (inString == false)
977
currentLine += "\"";
978
currentLine += (char)b;
979
inString = true;
980
} else {
981
char buffer[64];
982
if (pos == end && b == 0)
983
truncate_cpy(buffer, "0");
984
else
985
snprintf(buffer, sizeof(buffer), "0x%02X", b);
986
987
if (currentLine.size()+strlen(buffer) >= maxChars)
988
{
989
if (inString == true)
990
currentLine += "\"";
991
992
DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++};
993
lines[currentLineStart] = entry;
994
lineAddresses.push_back(currentLineStart);
995
996
currentLine.clear();
997
currentLineStart = pos-1;
998
inString = false;
999
}
1000
1001
bool comma = false;
1002
if (currentLine.size() != 0)
1003
comma = true;
1004
1005
if (inString)
1006
currentLine += "\"";
1007
1008
if (comma)
1009
currentLine += ",";
1010
1011
currentLine += buffer;
1012
inString = false;
1013
}
1014
}
1015
1016
if (inString == true)
1017
currentLine += "\"";
1018
1019
if (currentLine.size() != 0)
1020
{
1021
DataEntry entry = {currentLine,pos-currentLineStart,lineCount++};
1022
lines[currentLineStart] = entry;
1023
lineAddresses.push_back(currentLineStart);
1024
}
1025
} else {
1026
while (pos < end)
1027
{
1028
char buffer[256];
1029
u32 value;
1030
1031
u32 currentPos = pos;
1032
1033
switch (type)
1034
{
1035
case DATATYPE_BYTE:
1036
value = Memory::Read_U8(pos);
1037
snprintf(buffer, sizeof(buffer), "0x%02X", value);
1038
pos++;
1039
break;
1040
case DATATYPE_HALFWORD:
1041
value = Memory::Read_U16(pos);
1042
snprintf(buffer, sizeof(buffer), "0x%04X", value);
1043
pos += 2;
1044
break;
1045
case DATATYPE_WORD:
1046
{
1047
value = Memory::Read_U32(pos);
1048
const std::string label = g_symbolMap->GetLabelString(value);
1049
if (!label.empty())
1050
snprintf(buffer, sizeof(buffer), "%s", label.c_str());
1051
else
1052
snprintf(buffer, sizeof(buffer), "0x%08X", value);
1053
pos += 4;
1054
}
1055
break;
1056
default:
1057
break;
1058
}
1059
1060
size_t len = strlen(buffer);
1061
if (currentLine.size() != 0 && currentLine.size()+len >= maxChars)
1062
{
1063
DataEntry entry = {currentLine,currentPos-currentLineStart,lineCount++};
1064
lines[currentLineStart] = entry;
1065
lineAddresses.push_back(currentLineStart);
1066
1067
currentLine.clear();
1068
currentLineStart = currentPos;
1069
}
1070
1071
if (currentLine.size() != 0)
1072
currentLine += ",";
1073
currentLine += buffer;
1074
}
1075
1076
if (currentLine.size() != 0) {
1077
DataEntry entry = {currentLine,pos-currentLineStart,lineCount++};
1078
lines[currentLineStart] = entry;
1079
lineAddresses.push_back(currentLineStart);
1080
}
1081
}
1082
}
1083
1084
1085
DisassemblyComment::DisassemblyComment(u32 _address, u32 _size, std::string_view _name, std::string_view _param)
1086
: address(_address), size(_size), name(_name), param(_param) {}
1087
1088
bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) {
1089
dest.type = DISTYPE_OTHER;
1090
dest.name = name;
1091
dest.params = param;
1092
dest.totalSize = size;
1093
return true;
1094
}
1095
1096
bool GetDisasmAddressText(u32 address, char *dest, size_t bufSize, bool abbreviateLabels, bool showData, bool displaySymbols) {
1097
if (!Memory::IsValid4AlignedAddress(address)) {
1098
snprintf(dest, bufSize, "(bad address!)");
1099
return true;
1100
}
1101
if (displaySymbols) {
1102
const std::string addressSymbol = g_symbolMap->GetLabelString(address);
1103
if (!addressSymbol.empty()) {
1104
for (int k = 0; addressSymbol[k] != 0; k++) {
1105
// abbreviate long names
1106
if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0) {
1107
*dest++ = '+';
1108
break;
1109
}
1110
*dest++ = addressSymbol[k];
1111
}
1112
*dest++ = ':';
1113
*dest = 0;
1114
return true;
1115
} else {
1116
snprintf(dest, bufSize, " %08X",address);
1117
return false;
1118
}
1119
} else {
1120
if (showData) {
1121
const u32 encoding = Memory::Read_Instruction(address, true).encoding;
1122
snprintf(dest, bufSize, "%08X %08X", address, encoding);
1123
} else {
1124
snprintf(dest, bufSize, "%08X", address);
1125
}
1126
return false;
1127
}
1128
}
1129
1130
// Utilify function from the old debugger.
1131
std::string DisassembleRange(u32 start, u32 size, bool displaySymbols, MIPSDebugInterface *debugger) {
1132
auto memLock = Memory::Lock();
1133
std::string result;
1134
1135
// gather all branch targets without labels
1136
std::set<u32> branchAddresses;
1137
for (u32 i = 0; i < size; i += debugger->getInstructionSize(0)) {
1138
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger, start + i);
1139
1140
if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) {
1141
if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) {
1142
branchAddresses.insert(info.branchTarget);
1143
}
1144
}
1145
}
1146
1147
u32 disAddress = start;
1148
bool previousLabel = true;
1149
DisassemblyLineInfo line;
1150
while (disAddress < start + size) {
1151
char addressText[64], buffer[512];
1152
1153
g_disassemblyManager.getLine(disAddress, displaySymbols, line, debugger);
1154
bool isLabel = GetDisasmAddressText(disAddress, addressText, sizeof(addressText), false, line.type == DISTYPE_OPCODE, displaySymbols);
1155
1156
if (isLabel) {
1157
if (!previousLabel)
1158
result += "\r\n";
1159
snprintf(buffer, sizeof(buffer), "%s\r\n\r\n", addressText);
1160
result += buffer;
1161
} else if (branchAddresses.find(disAddress) != branchAddresses.end()) {
1162
if (!previousLabel)
1163
result += "\r\n";
1164
snprintf(buffer, sizeof(buffer), "pos_%08X:\r\n\r\n", disAddress);
1165
result += buffer;
1166
}
1167
1168
if (line.info.isBranch && !line.info.isBranchToRegister
1169
&& g_symbolMap->GetLabelString(line.info.branchTarget).empty()
1170
&& branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) {
1171
snprintf(buffer, sizeof(buffer), "pos_%08X", line.info.branchTarget);
1172
line.params = line.params.substr(0, line.params.find("0x")) + buffer;
1173
}
1174
1175
snprintf(buffer, sizeof(buffer), "\t%s\t%s\r\n", line.name.c_str(), line.params.c_str());
1176
result += buffer;
1177
previousLabel = isLabel;
1178
disAddress += line.totalSize;
1179
}
1180
1181
return result;
1182
}
1183
1184