Files
archived-pcsx2/pcsx2/DebugTools/DisassemblyManager.cpp
chaoticgd 44b50bee26 Debugger: Replace SymbolMap class with new SymbolGuardian class
This new class uses the CCC library I added in the last commit and
parses the symbol tables on a worker thread.
2024-08-27 12:48:40 -04:00

1064 lines
24 KiB
C++

// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include <string>
#include <algorithm>
#include <map>
#include "DisassemblyManager.h"
#include "Memory.h"
#include "Debug.h"
#include "MIPSAnalyst.h"
int DisassemblyManager::maxParamChars = 29;
bool isInInterval(u32 start, u32 size, u32 value)
{
return start <= value && value <= (start+size-1);
}
static u32 computeHash(u32 address, u32 size)
{
u32 end = address+size;
u32 hash = 0xBACD7814;
while (address < end)
{
hash += memRead32(address);
address += 4;
}
return hash;
}
static void parseDisasm(SymbolGuardian& guardian, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
{
if (*disasm == '(')
{
while (*disasm != ')' && *disasm != 0)
disasm++;
if (*disasm == ')')
disasm++;
}
// copy opcode
while (*disasm != 0 && *disasm != '\t')
{
*opcode++ = *disasm++;
}
*opcode = 0;
if (*disasm++ == 0)
{
*arguments = 0;
return;
}
char* arguments_start = arguments;
const char* jumpAddress = strstr(disasm,"->$");
const char* jumpRegister = strstr(disasm,"->");
while (*disasm != 0)
{
// parse symbol
if (disasm == jumpAddress)
{
u32 branchTarget;
sscanf(disasm+3,"0x%08x",&branchTarget);
const std::string addressSymbol = guardian.SymbolStartingAtAddress(branchTarget).name;
if (!addressSymbol.empty() && insertSymbols)
{
arguments += std::snprintf(arguments, arguments_size - (arguments - arguments_start), "%s",addressSymbol.c_str());
} else {
arguments += std::snprintf(arguments, arguments_size - (arguments - arguments_start), "0x%08X",branchTarget);
}
disasm += 3+2+8;
continue;
}
if (disasm == jumpRegister)
disasm += 2;
if (*disasm == ' ')
{
disasm++;
continue;
}
*arguments++ = *disasm++;
}
*arguments = 0;
}
std::map<u32,DisassemblyEntry*>::iterator findDisassemblyEntry(std::map<u32,DisassemblyEntry*>& entries, u32 address, bool exact)
{
if (exact)
return entries.find(address);
if (entries.empty())
return entries.end();
// find first elem that's >= address
auto it = entries.lower_bound(address);
if (it != entries.end())
{
// it may be an exact match
if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address))
return it;
// otherwise it may point to the next
if (it != entries.begin())
{
it--;
if (isInInterval(it->second->getLineAddress(0),it->second->getTotalSize(),address))
return it;
}
}
// check last entry manually
auto rit = entries.rbegin();
if (isInInterval(rit->second->getLineAddress(0),rit->second->getTotalSize(),address))
{
return (++rit).base();
}
// no match otherwise
return entries.end();
}
void DisassemblyManager::analyze(u32 address, u32 size = 1024)
{
if (!cpu->isAlive() || !cpu->isValidAddress(address))
return;
u32 end = address+size;
address &= ~3;
u32 start = address;
while (address < end && start <= address)
{
auto it = findDisassemblyEntry(entries,address,false);
if (it != entries.end())
{
DisassemblyEntry* entry = it->second;
entry->recheck();
address = entry->getLineAddress(0)+entry->getTotalSize();
continue;
}
SymbolInfo info = cpu->GetSymbolGuardian().SymbolOverlappingAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
if (info.descriptor.has_value())
{
switch (*info.descriptor)
{
case ccc::SymbolDescriptor::FUNCTION:
{
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address.value,info.size);
entries[info.address.value] = function;
address = info.address.value + info.size;
break;
}
case ccc::SymbolDescriptor::GLOBAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
case ccc::SymbolDescriptor::LOCAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
default:
break;
}
} else {
if (address % 4)
{
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value);
DisassemblyData* data = new DisassemblyData(cpu,address,next-address,DATATYPE_BYTE);
entries[address] = data;
address = next;
continue;
}
u32 next = cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value;
if ((next % 4) && next != 0xFFFFFFFF)
{
u32 alignedNext = next & ~3;
if (alignedNext != address)
{
DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,address,(alignedNext-address)/4);
entries[address] = opcode;
}
DisassemblyData* data = new DisassemblyData(cpu,address,next-alignedNext,DATATYPE_BYTE);
entries[alignedNext] = data;
} else {
DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,address,(next-address)/4);
entries[address] = opcode;
}
address = next;
continue;
}
}
}
std::vector<BranchLine> DisassemblyManager::getBranchLines(u32 start, u32 size)
{
std::vector<BranchLine> result;
auto it = findDisassemblyEntry(entries,start,false);
if (it != entries.end())
{
do
{
it->second->getBranchLines(start,size,result);
it++;
} while (it != entries.end() && start+size > it->second->getLineAddress(0));
}
return result;
}
void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest)
{
auto it = findDisassemblyEntry(entries,address,false);
if (it == entries.end())
{
analyze(address);
it = findDisassemblyEntry(entries,address,false);
if (it == entries.end())
{
if (address % 4)
dest.totalSize = ((address+3) & ~3)-address;
else
dest.totalSize = 4;
dest.name = "ERROR";
dest.params = "Disassembly failure";
return;
}
}
DisassemblyEntry* entry = it->second;
if (entry->disassemble(address,dest,insertSymbols))
return;
if (address % 4)
dest.totalSize = ((address+3) & ~3)-address;
else
dest.totalSize = 4;
dest.name = "ERROR";
dest.params = "Disassembly failure";
}
u32 DisassemblyManager::getStartAddress(u32 address)
{
auto it = findDisassemblyEntry(entries,address,false);
if (it == entries.end())
{
analyze(address);
it = findDisassemblyEntry(entries,address,false);
if (it == entries.end())
return address;
}
DisassemblyEntry* entry = it->second;
int line = entry->getLineNum(address,true);
return entry->getLineAddress(line);
}
u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n)
{
while (cpu->isValidAddress(address))
{
auto it = findDisassemblyEntry(entries,address,false);
while (it != entries.end())
{
DisassemblyEntry* entry = it->second;
int oldLineNum = entry->getLineNum(address,true);
if (n <= oldLineNum)
{
return entry->getLineAddress(oldLineNum-n);
}
address = entry->getLineAddress(0)-1;
n -= oldLineNum+1;
it = findDisassemblyEntry(entries,address,false);
}
analyze(address-127,128);
}
return address-n*4;
}
u32 DisassemblyManager::getNthNextAddress(u32 address, int n)
{
if (!cpu->isAlive())
return address + n * 4;
while (cpu->isValidAddress(address))
{
auto it = findDisassemblyEntry(entries,address,false);
while (it != entries.end())
{
DisassemblyEntry* entry = it->second;
int oldLineNum = entry->getLineNum(address,true);
int oldNumLines = entry->getNumLines();
if (oldLineNum+n < oldNumLines)
{
return entry->getLineAddress(oldLineNum+n);
}
address = entry->getLineAddress(0)+entry->getTotalSize();
n -= (oldNumLines-oldLineNum);
it = findDisassemblyEntry(entries,address,false);
}
if ((address + 0x400) < address) // Check for unsigned overflow, we are analysing too high!
break;
analyze(address);
}
return address+n*4;
}
void DisassemblyManager::clear()
{
for (auto it = entries.begin(); it != entries.end(); it++)
{
delete it->second;
}
entries.clear();
}
DisassemblyFunction::DisassemblyFunction(DebugInterface* _cpu, u32 _address, u32 _size): address(_address), size(_size)
{
cpu = _cpu;
hash = computeHash(address,size);
load();
}
void DisassemblyFunction::recheck()
{
u32 newHash = computeHash(address,size);
if (hash != newHash)
{
hash = newHash;
clear();
load();
}
}
int DisassemblyFunction::getNumLines()
{
return (int) lineAddresses.size();
}
int DisassemblyFunction::getLineNum(u32 address, bool findStart)
{
if (findStart)
{
int last = (int)lineAddresses.size() - 1;
for (int i = 0; i < last; i++)
{
u32 next = lineAddresses[i + 1];
if (lineAddresses[i] <= address && next > address)
return i;
}
if (lineAddresses[last] <= address && this->address + this->size > address)
return last;
}
else
{
int last = (int)lineAddresses.size() - 1;
for (int i = 0; i < last; i++)
{
if (lineAddresses[i] == address)
return i;
}
if (lineAddresses[last] == address)
return last;
}
return 0;
}
u32 DisassemblyFunction::getLineAddress(int line)
{
return lineAddresses[line];
}
bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, bool simplify)
{
auto it = findDisassemblyEntry(entries,address,false);
if (it == entries.end())
return false;
return it->second->disassemble(address,dest,insertSymbols,simplify);
}
void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
{
u32 end = start+size;
for (size_t i = 0; i < lines.size(); i++)
{
BranchLine& line = lines[i];
u32 first = line.first;
u32 second = line.second;
// skip branches that are entirely before or entirely after the window
if ((first < start && second < start) ||
(first > end && second > end))
continue;
dest.push_back(line);
}
}
#define NUM_LANES 16
void DisassemblyFunction::generateBranchLines()
{
struct LaneInfo
{
bool used;
u32 end;
};
LaneInfo lanes[NUM_LANES];
for (int i = 0; i < NUM_LANES; i++) {
lanes[i].used = false;
lanes[i].end = 0;
}
u32 end = address+size;
for (u32 funcPos = address; funcPos < end; funcPos += 4)
{
MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos);
bool inFunction = (opInfo.branchTarget >= address && opInfo.branchTarget < end);
if (opInfo.isBranch && !opInfo.isBranchToRegister && !opInfo.isLinkedBranch && inFunction)
{
BranchLine line;
line.laneIndex = 0;
if (opInfo.branchTarget < funcPos)
{
line.first = opInfo.branchTarget;
line.second = funcPos;
line.type = LINE_UP;
} else {
line.first = funcPos;
line.second = opInfo.branchTarget;
line.type = LINE_DOWN;
}
lines.push_back(line);
}
}
std::sort(lines.begin(),lines.end());
for (size_t i = 0; i < lines.size(); i++)
{
for (int l = 0; l < NUM_LANES; l++)
{
if (lines[i].first > lanes[l].end)
lanes[l].used = false;
}
int lane = -1;
for (int l = 0; l < NUM_LANES; l++)
{
if (!lanes[l].used)
{
lane = l;
break;
}
}
if (lane == -1)
{
// error
continue;
}
lanes[lane].end = lines[i].second;
lanes[lane].used = true;
lines[i].laneIndex = lane;
}
}
void DisassemblyFunction::addOpcodeSequence(u32 start, u32 end)
{
DisassemblyOpcode* opcode = new DisassemblyOpcode(cpu,start,(end-start)/4);
entries[start] = opcode;
for (u32 pos = start; pos < end; pos += 4)
{
lineAddresses.push_back(pos);
}
}
void DisassemblyFunction::load()
{
generateBranchLines();
// gather all branch targets
std::set<u32> branchTargets;
for (size_t i = 0; i < lines.size(); i++)
{
switch (lines[i].type)
{
case LINE_DOWN:
branchTargets.insert(lines[i].second);
break;
case LINE_UP:
branchTargets.insert(lines[i].first);
break;
}
}
u32 funcPos = address;
u32 funcEnd = address+size;
SymbolInfo nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(
funcPos-1, ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
u32 opcodeSequenceStart = funcPos;
while (funcPos < funcEnd)
{
if (funcPos == nextData.address.value && nextData.size > 0)
{
if (opcodeSequenceStart != funcPos)
addOpcodeSequence(opcodeSequenceStart,funcPos);
DisassemblyData* data = new DisassemblyData(cpu,funcPos,nextData.size,DATATYPE_WORD);
entries[funcPos] = data;
lineAddresses.push_back(funcPos);
funcPos += data->getTotalSize();
nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(funcPos-1,
ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
opcodeSequenceStart = funcPos;
continue;
}
// force align
if (funcPos % 4)
{
u32 nextPos = (funcPos+3) & ~3;
DisassemblyComment* comment = new DisassemblyComment(cpu,funcPos,nextPos-funcPos,".align","4");
entries[funcPos] = comment;
lineAddresses.push_back(funcPos);
funcPos = nextPos;
opcodeSequenceStart = funcPos;
continue;
}
MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos);
//u32 opAddress = funcPos;
funcPos += 4;
// skip branches and their delay slots
if (opInfo.isBranch)
{
if (funcPos < funcEnd) funcPos += 4; // only include delay slots within the function bounds
continue;
}
/*
The QT debugger doesn't follow the same logic as the disassembler
It _should_ follow the path of the disassembler, but instead it is naively reading the
disassembler output for every single instruction.
This causes issues disassembling:
0x1000 lui $t0, 0x1234
0x1004 ori $t0, $t0, 0x5678
0x1008 nop
Into:
0x1000 li $t0, 0x12345678
0x1004 li $t0, 0x12346789
0x1008 nop
Where it should be:
0x1000 li $t0, 0x12345678
0x1008 nop
As a quick remedy, I'm disabling the macro generation.
*/
#if 0
// lui
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData.address.value)
{
u32 next = cpu->read32(funcPos);
u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next & 0xFFFF);
u32 immediateOr = ((opInfo.encodedOpcode & 0xFFFF) << 16) | (u16)(next & 0xFFFF);
int rt = MIPS_GET_RT(opInfo.encodedOpcode);
int nextRs = MIPS_GET_RS(next);
int nextRt = MIPS_GET_RT(next);
// both rs and rt of the second op have to match rt of the first,
// otherwise there may be hidden consequences if the macro is displayed.
// also, don't create a macro if something branches into the middle of it
if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end())
{
DisassemblyMacro* macro = NULL;
switch (MIPS_GET_OP(next))
{
case 0x09: // addiu
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroLi(immediate,rt);
funcPos += 4;
break;
case 0x0D: // ori
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroLi(immediateOr,rt);
funcPos += 4;
break;
case 0x20: // lb
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("lb",immediate,rt,1);
funcPos += 4;
break;
case 0x21: // lh
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("lh",immediate,rt,2);
funcPos += 4;
break;
case 0x23: // lw
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("lw",immediate,rt,4);
funcPos += 4;
break;
case 0x24: // lbu
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("lbu",immediate,rt,1);
funcPos += 4;
break;
case 0x25: // lhu
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("lhu",immediate,rt,2);
funcPos += 4;
break;
case 0x28: // sb
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("sb",immediate,rt,1);
funcPos += 4;
break;
case 0x29: // sh
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("sh",immediate,rt,2);
funcPos += 4;
break;
case 0x2B: // sw
macro = new DisassemblyMacro(cpu,opAddress);
macro->setMacroMemory("sw",immediate,rt,4);
funcPos += 4;
break;
}
if (macro != NULL)
{
if (opcodeSequenceStart != opAddress)
addOpcodeSequence(opcodeSequenceStart,opAddress);
entries[opAddress] = macro;
for (int i = 0; i < macro->getNumLines(); i++)
{
lineAddresses.push_back(macro->getLineAddress(i));
}
opcodeSequenceStart = funcPos;
continue;
}
}
}
#endif
// just a normal opcode
}
if (opcodeSequenceStart != funcPos)
addOpcodeSequence(opcodeSequenceStart,funcPos);
}
void DisassemblyFunction::clear()
{
for (auto it = entries.begin(); it != entries.end(); it++)
{
delete it->second;
}
entries.clear();
lines.clear();
lineAddresses.clear();
hash = 0;
}
bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, bool simplify)
{
char opcode[64],arguments[256];
std::string dis = cpu->disasm(address,simplify);
parseDisasm(cpu->GetSymbolGuardian(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
dest.type = DISTYPE_OPCODE;
dest.name = opcode;
dest.params = arguments;
dest.totalSize = 4;
dest.info = MIPSAnalyst::GetOpcodeInfo(cpu,address);
return true;
}
void DisassemblyOpcode::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
{
if (start < address)
{
size = start+size-address;
start = address;
}
if (start+size > address+num*4)
size = address+num*4-start;
int lane = 0;
for (u32 pos = start; pos < start+size; pos += 4)
{
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,pos);
if (info.isBranch && !info.isBranchToRegister && !info.isLinkedBranch)
{
BranchLine line;
line.laneIndex = lane++;
if (info.branchTarget < pos)
{
line.first = info.branchTarget;
line.second = pos;
line.type = LINE_UP;
} else {
line.first = pos;
line.second = info.branchTarget;
line.type = LINE_DOWN;
}
dest.push_back(line);
}
}
}
void DisassemblyMacro::setMacroLi(u32 _immediate, u8 _rt)
{
type = MACRO_LI;
name = "li";
immediate = _immediate;
rt = _rt;
numOpcodes = 2;
}
void DisassemblyMacro::setMacroMemory(const std::string& _name, u32 _immediate, u8 _rt, int _dataSize)
{
type = MACRO_MEMORYIMM;
name = _name;
immediate = _immediate;
rt = _rt;
dataSize = _dataSize;
numOpcodes = 2;
}
bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, bool simplify)
{
char buffer[64];
dest.type = DISTYPE_MACRO;
dest.info = MIPSAnalyst::GetOpcodeInfo(cpu,address);
std::string addressSymbol;
switch (type)
{
case MACRO_LI:
dest.name = name;
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
} else {
std::snprintf(buffer,std::size(buffer),"%s,0x%08X",cpu->getRegisterName(0,rt),immediate);
}
dest.params = buffer;
dest.info.hasRelevantAddress = true;
dest.info.releventAddress = immediate;
break;
case MACRO_MEMORYIMM:
dest.name = name;
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
} else {
std::snprintf(buffer,std::size(buffer),"%s,0x%08X",cpu->getRegisterName(0,rt),immediate);
}
dest.params = buffer;
dest.info.isDataAccess = true;
dest.info.dataAddress = immediate;
dest.info.dataSize = dataSize;
dest.info.hasRelevantAddress = true;
dest.info.releventAddress = immediate;
break;
default:
return false;
}
dest.totalSize = getTotalSize();
return true;
}
DisassemblyData::DisassemblyData(DebugInterface* _cpu, u32 _address, u32 _size, DataType _type): address(_address), size(_size), type(_type)
{
cpu = _cpu;
hash = computeHash(address,size);
createLines();
}
void DisassemblyData::recheck()
{
u32 newHash = computeHash(address,size);
if (newHash != hash)
{
hash = newHash;
createLines();
}
}
bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, bool simplify)
{
dest.type = DISTYPE_DATA;
switch (type)
{
case DATATYPE_BYTE:
dest.name = ".byte";
break;
case DATATYPE_HALFWORD:
dest.name = ".half";
break;
case DATATYPE_WORD:
dest.name = ".word";
break;
case DATATYPE_ASCII:
dest.name = ".ascii";
break;
default:
return false;
}
auto it = lines.find(address);
if (it == lines.end())
return false;
dest.params = it->second.text;
dest.totalSize = it->second.size;
return true;
}
int DisassemblyData::getLineNum(u32 address, bool findStart)
{
auto it = lines.upper_bound(address);
if (it != lines.end())
{
if (it == lines.begin())
return 0;
it--;
return it->second.lineNum;
}
return lines.rbegin()->second.lineNum;
}
void DisassemblyData::createLines()
{
lines.clear();
lineAddresses.clear();
u32 pos = address;
u32 end = address+size;
u32 maxChars = DisassemblyManager::getMaxParamChars();
std::string currentLine;
u32 currentLineStart = pos;
int lineCount = 0;
if (type == DATATYPE_ASCII)
{
bool inString = false;
while (pos < end)
{
u8 b = memRead8(pos++);
if (b >= 0x20 && b <= 0x7F)
{
if (currentLine.size()+1 >= maxChars)
{
if (inString)
currentLine += "\"";
DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++};
lines[currentLineStart] = entry;
lineAddresses.push_back(currentLineStart);
currentLine = "";
currentLineStart = pos-1;
inString = false;
}
if (!inString)
currentLine += "\"";
currentLine += (char)b;
inString = true;
} else {
char buffer[64];
if (pos == end && b == 0)
StringUtil::Strlcpy(buffer, "0", std::size(buffer));
else
std::snprintf(buffer, std::size(buffer), "0x%02X", b);
if (currentLine.size()+strlen(buffer) >= maxChars)
{
if (inString)
currentLine += "\"";
DataEntry entry = {currentLine,pos-1-currentLineStart,lineCount++};
lines[currentLineStart] = entry;
lineAddresses.push_back(currentLineStart);
currentLine = "";
currentLineStart = pos-1;
inString = false;
}
bool comma = false;
if (currentLine.size() != 0)
comma = true;
if (inString)
currentLine += "\"";
if (comma)
currentLine += ",";
currentLine += buffer;
inString = false;
}
}
if (inString)
currentLine += "\"";
if (currentLine.size() != 0)
{
DataEntry entry = {currentLine,pos-currentLineStart,lineCount++};
lines[currentLineStart] = entry;
lineAddresses.push_back(currentLineStart);
}
} else {
while (pos < end)
{
char buffer[64];
u32 value;
u32 currentPos = pos;
switch (type)
{
case DATATYPE_BYTE:
value = memRead8(pos);
std::snprintf(buffer,std::size(buffer),"0x%02X",value);
pos++;
break;
case DATATYPE_HALFWORD:
value = memRead16(pos);
std::snprintf(buffer,std::size(buffer),"0x%04X",value);
pos += 2;
break;
case DATATYPE_WORD:
{
value = memRead32(pos);
const std::string label = cpu->GetSymbolGuardian().SymbolStartingAtAddress(value).name;
if (!label.empty())
std::snprintf(buffer,std::size(buffer),"%s",label.c_str());
else
std::snprintf(buffer,std::size(buffer),"0x%08X",value);
pos += 4;
}
break;
default:
// Avoid a call to strlen with random data
buffer[0] = 0;
break;
}
size_t len = strlen(buffer);
if (currentLine.size() != 0 && currentLine.size()+len >= maxChars)
{
DataEntry entry = {currentLine,currentPos-currentLineStart,lineCount++};
lines[currentLineStart] = entry;
lineAddresses.push_back(currentLineStart);
currentLine = "";
currentLineStart = currentPos;
}
if (currentLine.size() != 0)
currentLine += ",";
currentLine += buffer;
}
if (currentLine.size() != 0)
{
DataEntry entry = {currentLine,pos-currentLineStart,lineCount++};
lines[currentLineStart] = entry;
lineAddresses.push_back(currentLineStart);
}
}
}
DisassemblyComment::DisassemblyComment(DebugInterface* _cpu, u32 _address, u32 _size, const std::string& _name, const std::string& _param)
: cpu(_cpu), address(_address), size(_size), name(_name), param(_param)
{
}
bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, bool simplify)
{
dest.type = DISTYPE_OTHER;
dest.name = name;
dest.params = param;
dest.totalSize = size;
return true;
}