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