Debugger: Several performance improvements

This commit is contained in:
Souryo 2017-03-09 23:50:20 -05:00
parent fcb28fd5a6
commit 930b4f2641
19 changed files with 537 additions and 310 deletions

View file

@ -576,9 +576,10 @@ void Debugger::GenerateCodeOutput()
}
}
const char* Debugger::GetCode()
const char* Debugger::GetCode(uint32_t &length)
{
GenerateCodeOutput();
length = (uint32_t)_disassemblerOutput.size();
return _disassemblerOutput.c_str();
}
@ -650,12 +651,20 @@ void Debugger::GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelati
callstackRelative[_callstackRelative.size()] = -2;
}
void Debugger::GetFunctionEntryPoints(int32_t* entryPoints)
int32_t Debugger::GetFunctionEntryPointCount()
{
return (uint32_t)_functionEntryPoints.size();
}
void Debugger::GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount)
{
uint32_t i = 0;
for(auto itt = _functionEntryPoints.begin(); itt != _functionEntryPoints.end(); itt++) {
entryPoints[i] = *itt;
i++;
if(i == maxCount - 1) {
break;
}
}
entryPoints[i] = -1;
}
@ -765,11 +774,14 @@ int32_t Debugger::FindSubEntryPoint(uint16_t relativeAddress)
int32_t address = relativeAddress;
do {
GetAbsoluteAddressAndType(address, &info);
if(info.Address < 0 || info.Type != AddressType::PrgRom || !_codeDataLogger->IsCode(info.Address) || _codeDataLogger->IsSubEntryPoint(info.Address)) {
if(info.Address < 0 || info.Type != AddressType::PrgRom || _codeDataLogger->IsData(info.Address)) {
break;
}
address--;
if(_codeDataLogger->IsSubEntryPoint(info.Address)) {
break;
}
} while(address >= 0);
return address + 1;
return address > relativeAddress ? relativeAddress : (address + 1);
}

View file

@ -119,7 +119,9 @@ public:
shared_ptr<LabelManager> GetLabelManager();
void GetFunctionEntryPoints(int32_t* entryPoints);
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
int32_t GetFunctionEntryPointCount();
void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative);
void GetState(DebugState *state, bool includeMapperInfo = true);
@ -145,7 +147,7 @@ public:
bool IsExecutionStopped();
void GenerateCodeOutput();
const char* GetCode();
const char* GetCode(uint32_t &length);
int32_t GetRelativeAddress(uint32_t addr, AddressType type);
int32_t GetAbsoluteAddress(uint32_t addr);

View file

@ -253,48 +253,130 @@ vector<string> Disassembler::SplitComment(string input)
return result;
}
string Disassembler::GetLine(string code, string comment, int32_t cpuAddress, int32_t absoluteAddress, string byteCode, string addressing, bool speculativeCode)
static const char* hexTable[256] = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
};
static string emptyString;
void Disassembler::GetLine(string &out, string code, string comment, int32_t cpuAddress, int32_t absoluteAddress)
{
string out;
out.reserve(40);
if(cpuAddress >= 0) {
if(speculativeCode) {
out += "2\x1";
} else {
out += (_debugger->IsMarkedAsCode(cpuAddress) || absoluteAddress == -1) ? "1\x1" : "0\x1";
}
out += HexUtilities::ToHex((uint16_t)cpuAddress);
} else {
out += "1\x1";
}
out += "\x1";
if(absoluteAddress >= 0) {
out += HexUtilities::ToHex((uint32_t)absoluteAddress);
}
out += "\x1" + byteCode + "\x1" + code + "\x2" + addressing + "\x2";
if(!comment.empty()) {
out += ";" + comment;
}
out += "\n";
return out;
GetCodeLine(out, code, comment, cpuAddress, absoluteAddress, emptyString, emptyString, false, false);
}
string Disassembler::GetSubHeader(DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector)
void Disassembler::GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, bool speculativeCode, bool isIndented)
{
char buffer[1000];
int pos = 0;
char* ptrBuf = buffer;
int* ptrPos = &pos;
auto writeChar = [=](char c) -> void {
if(*ptrPos < 999) {
ptrBuf[(*ptrPos)++] = c;
}
};
auto writeHex = [=](const char* hex) -> void {
if(*ptrPos < 950) {
ptrBuf[(*ptrPos)++] = hex[0];
ptrBuf[(*ptrPos)++] = hex[1];
}
};
auto writeStr = [=](string &str) -> void {
uint32_t len = (uint32_t)str.size();
if(*ptrPos + len < 950) {
memcpy(ptrBuf + (*ptrPos), str.c_str(), len);
(*ptrPos) += len;
} else {
len = 950 - *ptrPos;
memcpy(ptrBuf + (*ptrPos), str.c_str(), len);
(*ptrPos) += len;
}
};
//Fields:
//Flags | CpuAddress | AbsAddr | ByteCode | Code | Addressing | Comment
if(cpuAddress >= 0) {
if(speculativeCode) {
writeChar(isIndented ? '6' : '2');
writeChar('\x1');
} else {
writeChar((_debugger->IsMarkedAsCode(cpuAddress) || absoluteAddress == -1) ? (isIndented ? '5' : '1') : (isIndented ? '4' : '0'));
writeChar('\x1');
}
writeHex(hexTable[(cpuAddress >> 8) & 0xFF]);
writeHex(hexTable[cpuAddress & 0xFF]);
writeChar('\x1');
} else {
writeChar('1');
writeChar('\x1');
writeChar('\x1');
}
if(absoluteAddress >= 0) {
if(absoluteAddress > 0xFFFFFF) {
writeHex(hexTable[(absoluteAddress >> 24) & 0xFF]);
}
if(absoluteAddress > 0xFFFF) {
writeHex(hexTable[(absoluteAddress >> 16) & 0xFF]);
}
writeHex(hexTable[(absoluteAddress >> 8) & 0xFF]);
writeHex(hexTable[absoluteAddress & 0xFF]);
}
writeChar('\x1');
writeStr(byteCode);
writeChar('\x1');
writeStr(code);
writeChar('\x1');
writeStr(addressing);
writeChar('\x1');
if(!comment.empty()) {
writeChar(';');
writeStr(comment);
}
writeChar('\x1');
ptrBuf[(*ptrPos)++] = 0;
out.append(buffer, pos - 1);
}
void Disassembler::GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector)
{
if(info->IsSubEntryPoint()) {
if(label.empty()) {
return GetLine() + GetLine("__sub start__");
GetLine(out);
GetLine(out, "__sub start__");
} else {
return GetLine() + GetLine("__" + label + "()__");
GetLine(out);
GetLine(out, "__" + label + "()__");
}
} else if(relativeAddr == resetVector) {
return GetLine() + GetLine("--reset--");
GetLine(out);
GetLine(out, "--reset--");
} else if(relativeAddr == irqVector) {
return GetLine() + GetLine("--irq--");
GetLine(out);
GetLine(out, "--irq--");
} else if(relativeAddr == nmiVector) {
return GetLine() + GetLine("--nmi--");
GetLine(out);
GetLine(out, "--nmi--");
}
return "";
}
string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager)
@ -330,51 +412,67 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
uint32_t byteCount = 0;
bool skippingCode = false;
shared_ptr<CodeDataLogger> cdl = _debugger->GetCodeDataLogger();
string label;
string commentString;
string commentLines;
shared_ptr<DisassemblyInfo> infoRef;
DisassemblyInfo* info;
bool speculativeCode;
string spaces = " ";
string effAddress;
string code;
string byteCode;
while(addr <= endAddr) {
string label = labelManager->GetLabel(memoryAddr, false);
string commentString = labelManager->GetComment(memoryAddr);
string labelLine = label.empty() ? "" : GetLine(label + ":");
string commentLines = "";
bool speculativeCode = false;
labelManager->GetLabelAndComment(memoryAddr, label, commentString);
commentLines.clear();
speculativeCode = false;
if(commentString.find_first_of('\n') != string::npos) {
for(string &str : SplitComment(commentString)) {
commentLines += GetLine("", str);
GetLine(commentLines, "", str);
}
commentString = "";
commentString.clear();
}
shared_ptr<DisassemblyInfo> info = (*cache)[addr&mask];
infoRef = (*cache)[addr&mask];
info = infoRef.get();
if(!info && source == _prgRom) {
if(_debugger->CheckFlag(DebuggerFlags::DisassembleEverything) || _debugger->CheckFlag(DebuggerFlags::DisassembleEverythingButData) && !cdl->IsData(addr)) {
speculativeCode = true;
info = shared_ptr<DisassemblyInfo>(new DisassemblyInfo(source + addr, false));
info = new DisassemblyInfo(source + addr, false);
}
}
if(info && addr + info->GetSize() <= endAddr) {
if(byteCount > 0) {
output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
byteCount = 0;
}
if(skippingCode) {
output += GetLine(unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1);
GetLine(output, unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1);
skippingCode = false;
}
output += GetSubHeader(info.get(), label, memoryAddr, resetVector, nmiVector, irqVector);
GetSubHeader(output, info, label, memoryAddr, resetVector, nmiVector, irqVector);
output += commentLines;
output += labelLine;
char* effectiveAddress = info->GetEffectiveAddressString(cpuState, memoryManager.get(), labelManager.get());
if(!effectiveAddress) {
effectiveAddress = "";
if(!label.empty()) {
GetLine(output, label + ":");
}
output += GetLine(" " + string(info->ToString(memoryAddr, memoryManager.get(), labelManager.get())), commentString, memoryAddr, source != _internalRam ? addr : -1, info->GetByteCode(), effectiveAddress, speculativeCode);
byteCode.clear();
code.clear();
effAddress.clear();
info->GetEffectiveAddressString(effAddress, cpuState, memoryManager.get(), labelManager.get());
info->ToString(code, memoryAddr, memoryManager.get(), labelManager.get());
info->GetByteCode(byteCode);
GetCodeLine(output, code, commentString, memoryAddr, source != _internalRam ? addr : -1, byteCode, effAddress, speculativeCode, true);
if(info->IsSubExitPoint()) {
output += GetLine("__sub end__") + GetLine();
GetLine(output, "__sub end__");
GetLine(output);
}
if(speculativeCode) {
@ -394,20 +492,20 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
}
} else {
if((!label.empty() || !commentString.empty()) && skippingCode) {
output += GetLine(unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1);
GetLine(output, unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1);
skippingCode = false;
}
if(!skippingCode && showOnlyDiassembledCode) {
if(label.empty()) {
output += GetLine("__unknown block__", "", memoryAddr, addr);
GetLine(output, "__unknown block__", "", memoryAddr, addr);
if(!commentString.empty()) {
output += GetLine("", commentString);
GetLine(output, "", commentString);
}
} else {
output += GetLine("__" + label + "__", "", memoryAddr, addr);
GetLine(output, "__" + label + "__", "", memoryAddr, addr);
if(!commentString.empty()) {
output += GetLine("", commentString);
GetLine(output, "", commentString);
}
output += commentLines;
}
@ -416,14 +514,16 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
if(!showOnlyDiassembledCode) {
if(byteCount >= 8 || ((!label.empty() || !commentString.empty()) && byteCount > 0)) {
output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
byteCount = 0;
}
if(byteCount == 0) {
dbBuffer = ".db";
output += commentLines;
output += labelLine;
if(!label.empty()) {
GetLine(output, label + ":");
}
dbRelativeAddr = memoryAddr;
dbAbsoluteAddr = addr;
@ -432,7 +532,7 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
dbBuffer += " $" + HexUtilities::ToHex(source[addr&mask]);
if(!label.empty() || !commentString.empty()) {
output += GetLine(dbBuffer, commentString, dbRelativeAddr, dbAbsoluteAddr);
GetLine(output, dbBuffer, commentString, dbRelativeAddr, dbAbsoluteAddr);
byteCount = 0;
} else {
byteCount++;
@ -441,14 +541,18 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo
addr++;
memoryAddr++;
}
if(speculativeCode) {
delete info;
}
}
if(byteCount > 0) {
output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr);
}
if(skippingCode) {
output += GetLine("----", "", (uint16_t)(memoryAddr - 1), addr - 1);
GetLine(output, "----", "", (uint16_t)(memoryAddr - 1), addr - 1);
}
return output;

View file

@ -23,8 +23,9 @@ private:
bool IsJump(uint8_t opCode);
bool IsUnconditionalJump(uint8_t opCode);
vector<string> SplitComment(string input);
string GetLine(string code = "", string comment = "", int32_t cpuAddress = -1, int32_t absoluteAddress = -1, string byteCode = "", string addressing = "", bool speculativeCode = false);
string GetSubHeader(DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1);
void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, bool speculativeCode, bool isCode);
void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
public:
Disassembler(uint8_t* internalRam, uint8_t* prgRom, uint32_t prgSize, uint8_t* prgRam, uint32_t prgRamSize, Debugger* debugger);

View file

@ -28,19 +28,16 @@ static const char* hexTable[256] = {
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
};
char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager)
{
uint16_t length;
return ToString(memoryAddr, memoryManager, labelManager, length);
}
char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length)
void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager)
{
char buffer[500];
uint8_t opCode = *_opPointer;
length = (uint16_t)DisassemblyInfo::OPName[opCode].size();
memcpy(_toStringBuffer, DisassemblyInfo::OPName[opCode].c_str(), length);
uint16_t length = (uint16_t)DisassemblyInfo::OPName[opCode].size();
memcpy(buffer, DisassemblyInfo::OPName[opCode].c_str(), length);
uint16_t* ptrPos = &length;
char* ptrBuf = buffer;
uint16_t opAddr = GetOpAddr(memoryAddr);
@ -59,19 +56,19 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage
}
auto writeChar = [=](char c) -> void {
_toStringBuffer[(*ptrPos)++] = c;
ptrBuf[(*ptrPos)++] = c;
};
auto copyOperand = [=]() -> void {
if(labelManager && _opMode != AddrMode::Imm) {
string label = labelManager->GetLabel(opAddr, true);
if(!label.empty()) {
memcpy(_toStringBuffer + (*ptrPos), label.c_str(), label.size());
memcpy(ptrBuf + (*ptrPos), label.c_str(), label.size());
(*ptrPos) += (uint16_t)label.size();
return;
}
}
memcpy(_toStringBuffer + (*ptrPos), opBuffer, operandLength);
memcpy(ptrBuf + (*ptrPos), opBuffer, operandLength);
(*ptrPos) += operandLength;
};
@ -79,13 +76,13 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage
case AddrMode::Acc: writeChar('A'); break;
case AddrMode::Imm: writeChar('#'); copyOperand(); break;
case AddrMode::Ind: writeChar('('); copyOperand(); writeChar(')'); break;
case AddrMode::IndX: writeChar('('); copyOperand(); memcpy(_toStringBuffer + length, ",X)", 3); length += 3; break;
case AddrMode::IndX: writeChar('('); copyOperand(); memcpy(ptrBuf + length, ",X)", 3); length += 3; break;
case AddrMode::IndY:
case AddrMode::IndYW:
writeChar('(');
copyOperand();
memcpy(_toStringBuffer + length, "),Y", 3);
memcpy(ptrBuf + length, "),Y", 3);
length += 3;
break;
@ -99,7 +96,7 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage
case AddrMode::AbsXW:
case AddrMode::ZeroX:
copyOperand();
memcpy(_toStringBuffer + length, ",X", 2);
memcpy(ptrBuf + length, ",X", 2);
length += 2;
break;
@ -107,14 +104,14 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage
case AddrMode::AbsYW:
case AddrMode::ZeroY:
copyOperand();
memcpy(_toStringBuffer + length, ",Y", 2);
memcpy(ptrBuf + length, ",Y", 2);
length += 2;
break;
default: break;
}
_toStringBuffer[length] = 0;
return _toStringBuffer;
ptrBuf[length] = 0;
out.append(ptrBuf, length);
}
uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr)
@ -149,49 +146,45 @@ void DisassemblyInfo::SetSubEntryPoint()
_isSubEntryPoint = true;
}
char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager)
{
uint16_t length;
return GetEffectiveAddressString(cpuState, memoryManager, labelManager, length);
}
char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t& length)
void DisassemblyInfo::GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager)
{
if(_opMode <= AddrMode::Abs) {
length = 0;
return nullptr;
return;
} else {
int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager);
char buffer[500];
_effectiveAddressBuffer[0] = ' ';
_effectiveAddressBuffer[1] = '@';
_effectiveAddressBuffer[2] = ' ';
int length = 0;
buffer[0] = ' ';
buffer[1] = '@';
buffer[2] = ' ';
if(labelManager) {
string label = labelManager->GetLabel(effectiveAddress, true);
if(!label.empty()) {
memcpy(_effectiveAddressBuffer + 3, label.c_str(), label.size());
memcpy(buffer + 3, label.c_str(), label.size());
length = (uint16_t)label.size() + 3;
_effectiveAddressBuffer[length] = 0;
buffer[length] = 0;
return _effectiveAddressBuffer;
out.append(buffer, length);
return;
}
}
_effectiveAddressBuffer[3] = '$';
buffer[3] = '$';
if(_opMode == AddrMode::ZeroX || _opMode == AddrMode::ZeroY) {
memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress], 2);
_effectiveAddressBuffer[6] = 0;
memcpy(buffer + 4, hexTable[effectiveAddress], 2);
buffer[6] = 0;
length = 6;
} else {
memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress >> 8], 2);
memcpy(_effectiveAddressBuffer + 6, hexTable[effectiveAddress & 0xFF], 2);
_effectiveAddressBuffer[8] = 0;
memcpy(buffer + 4, hexTable[effectiveAddress >> 8], 2);
memcpy(buffer + 6, hexTable[effectiveAddress & 0xFF], 2);
buffer[8] = 0;
length = 8;
}
return _effectiveAddressBuffer;
out.append(buffer, length);
}
}
@ -232,19 +225,24 @@ int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, MemoryManager* mem
return -1;
}
string DisassemblyInfo::GetByteCode()
void DisassemblyInfo::GetByteCode(string &out)
{
//Raw byte code
string byteCode;
byteCode.reserve(12);
char byteCode[12];
int pos = 1;
byteCode[0] = '$';
for(uint32_t i = 0; i < _opSize; i++) {
if(!byteCode.empty()) {
byteCode += " ";
if(i != 0) {
byteCode[pos++] = ' ';
byteCode[pos++] = '$';
}
byteCode += "$" + HexUtilities::ToHex((uint8_t)*(_opPointer + i));
byteCode[pos++] = hexTable[(uint8_t)*(_opPointer + i)][0];
byteCode[pos++] = hexTable[(uint8_t)*(_opPointer + i)][1];
}
return byteCode;
byteCode[pos] = 0;
out.append(byteCode, pos);
}
uint32_t DisassemblyInfo::GetSize()

View file

@ -19,10 +19,7 @@ private:
bool _isSubExitPoint = false;
uint32_t _opSize = 0;
AddrMode _opMode;
char _toStringBuffer[1000];
char _effectiveAddressBuffer[1000];
public:
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint);
@ -30,12 +27,9 @@ public:
int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager);
char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length);
char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager);
char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length);
string GetByteCode();
void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager);
void GetByteCode(string &out);
uint32_t GetSize();
uint16_t GetOpAddr(uint16_t memoryAddr);

View file

@ -98,6 +98,27 @@ string LabelManager::GetComment(uint16_t relativeAddr)
return "";
}
void LabelManager::GetLabelAndComment(uint16_t relativeAddr, string &label, string &comment)
{
int32_t labelAddr = GetLabelAddress(relativeAddr, false);
if(labelAddr >= 0) {
auto result = _codeLabels.find(labelAddr);
if(result != _codeLabels.end()) {
label = result->second;
} else {
label.clear();
}
auto commentResult = _codeComments.find(labelAddr);
if(commentResult != _codeComments.end()) {
comment = commentResult->second;
} else {
comment.clear();
}
}
}
int32_t LabelManager::GetLabelRelativeAddress(string label)
{
auto result = _codeLabelReverseLookup.find(label);

View file

@ -1,6 +1,7 @@
#pragma once
#include "stdafx.h"
#include <unordered_map>
#include <functional>
using std::unordered_map;
class BaseMapper;
@ -9,9 +10,9 @@ enum class AddressType;
class LabelManager
{
private:
unordered_map<uint32_t, string> _codeLabels;
unordered_map<uint32_t, string, std::function<size_t(const uint32_t &addr)>> _codeLabels = unordered_map<uint32_t, string, std::function<size_t(const uint32_t &addr)>>(10000, [](const uint32_t & addr) { return addr; });
unordered_map<uint32_t, string, std::function<size_t(const uint32_t &addr)>> _codeComments = unordered_map<uint32_t, string, std::function<size_t(const uint32_t &addr)>>(10000, [](const uint32_t & addr) { return addr; });
unordered_map<string, uint32_t> _codeLabelReverseLookup;
unordered_map<uint32_t, string> _codeComments;
shared_ptr<BaseMapper> _mapper;
@ -26,4 +27,5 @@ public:
string GetLabel(uint16_t relativeAddr, bool checkRegisters);
string GetComment(uint16_t relativeAddr);
void GetLabelAndComment(uint16_t relativeAddr, string &label, string &comment);
};

View file

@ -84,7 +84,9 @@ void TraceLogger::GetTraceRow(string &output, State &cpuState, PPUDebugState &pp
output += HexUtilities::ToHex(cpuState.DebugPC) + " ";
if(_options.ShowByteCode) {
output += disassemblyInfo->GetByteCode() + std::string(13 - disassemblyInfo->GetByteCode().size(), ' ');
string byteCode;
disassemblyInfo->GetByteCode(byteCode);
output += byteCode + std::string(13 - byteCode.size(), ' ');
}
int indentLevel = 0;
@ -93,17 +95,12 @@ void TraceLogger::GetTraceRow(string &output, State &cpuState, PPUDebugState &pp
output += std::string(indentLevel, ' ');
}
uint16_t disassemblyLength = 0;
uint16_t effectiveAddressLength = 0;
string code;
LabelManager* labelManager = _options.UseLabels ? _labelManager.get() : nullptr;
char* disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager.get(), labelManager, disassemblyLength);
char* effectiveAddress = (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(cpuState, _memoryManager.get(), labelManager, effectiveAddressLength) : nullptr);
output += disassembly;
if(effectiveAddress) {
output += effectiveAddress;
}
output += std::string(32 - disassemblyLength - effectiveAddressLength, ' ');
disassemblyInfo->ToString(code, cpuState.DebugPC, _memoryManager.get(), labelManager);
disassemblyInfo->GetEffectiveAddressString(code, cpuState, _memoryManager.get(), labelManager);
code += std::string(32 - code.size(), ' ');
output += code;
if(_options.ShowRegisters) {
output += " A:" + HexUtilities::ToHex(cpuState.A) +

View file

@ -97,10 +97,14 @@ namespace Mesen.GUI.Debugger
private void UpdateStack(UInt16 stackPointer)
{
lstStack.BeginUpdate();
lstStack.Items.Clear();
for(UInt32 i = (UInt32)0x100 + stackPointer; i < 0x200; i++) {
lstStack.Items.Add("$" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, i).ToString("X2"));
ListViewItem[] itemsToAdd = new ListViewItem[256 - stackPointer];
for(UInt32 i = (UInt32)0x100 + stackPointer; i < 0x200; i++) {
itemsToAdd[i-stackPointer-0x100] = new ListViewItem("$" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, i).ToString("X2"));
}
lstStack.Items.AddRange(itemsToAdd);
lstStack.EndUpdate();
}
public void UpdateStatus(ref DebugState state)

View file

@ -21,10 +21,23 @@ namespace Mesen.GUI.Debugger
public event AssemblerEventHandler OnEditCode;
public event AddressEventHandler OnSetNextStatement;
private DebugViewInfo _config;
List<int> _lineNumbers = new List<int>(10000);
List<string> _lineNumberNotes = new List<string>(10000);
List<string> _codeNotes = new List<string>(10000);
List<string> _codeLines = new List<string>(10000);
private HashSet<int> _unexecutedAddresses = new HashSet<int>();
private HashSet<int> _speculativeCodeAddreses = new HashSet<int>();
Dictionary<int, string> _codeContent = new Dictionary<int, string>(10000);
Dictionary<int, string> _codeComments = new Dictionary<int, string>(10000);
Dictionary<int, string> _codeByteCode = new Dictionary<int, string>(10000);
List<string> _addressing = new List<string>(10000);
List<string> _comments = new List<string>(10000);
List<int> _lineIndentations = new List<int>(10000);
private Color _unexecutedColor = Color.FromArgb(183, 229, 190);
private Color _speculativeColor = Color.FromArgb(240, 220, 220);
private UInt32? _currentActiveAddress { get; set; } = null;
private frmCodeTooltip _codeTooltip = null;
@ -124,7 +137,6 @@ namespace Mesen.GUI.Debugger
}
}
private UInt32? _currentActiveAddress = null;
public void SelectActiveAddress(UInt32 address)
{
this.SetActiveAddress(address);
@ -144,30 +156,10 @@ namespace Mesen.GUI.Debugger
public void UpdateLineColors()
{
this.ctrlCodeViewer.BeginUpdate();
this.ctrlCodeViewer.ClearLineStyles();
if(_currentActiveAddress.HasValue) {
this.ctrlCodeViewer.SetLineColor((int)_currentActiveAddress, Color.Black, Color.Yellow, null, LineSymbol.Arrow);
}
if(ConfigManager.Config.DebugInfo.HighlightUnexecutedCode) {
foreach(int relativeAddress in _unexecutedAddresses) {
this.ctrlCodeViewer.SetLineColor(relativeAddress, null, _unexecutedColor);
}
}
foreach(int relativeAddress in _speculativeCodeAddreses) {
this.ctrlCodeViewer.SetLineColor(relativeAddress, null, _speculativeColor);
}
this.HighlightBreakpoints();
this.ctrlCodeViewer.StyleProvider = new LineStyleProvider(this);
this.ctrlCodeViewer.EndUpdate();
}
Dictionary<int, string> _codeContent = new Dictionary<int, string>();
Dictionary<int, int> _codeByteContentLength = new Dictionary<int, int>();
public List<string> GetCode(out int byteLength, ref int startAddress, int endAddress = -1)
{
List<string> result = new List<string>();
@ -184,7 +176,10 @@ namespace Mesen.GUI.Debugger
//TODO: Invalidate disassembly info cache / CDL file (not needed?)
//TODO: Support .data syntax in assembler
for(int i = startAddress; (i <= endAddress || endAddress == -1) && endAddress < 65536; ) {
if(_codeContent.TryGetValue(i, out string code)) {
string code;
if(_codeContent.TryGetValue(i, out code)) {
code = code.Split('\x2')[0].Trim();
if(code.StartsWith("--") || code.StartsWith("__") || code.StartsWith("[[")) {
//Stop adding code when we find a new section (new function, data blocks, etc.)
break;
@ -196,6 +191,16 @@ namespace Mesen.GUI.Debugger
string comment = codeLabel?.Comment;
string label = codeLabel?.Label;
if(code == "STP*" || code == "NOP*") {
//Transform unofficial opcodes that can't be reassembled properly into .byte statements
if(comment != null) {
comment.Insert(1, code + " - ");
} else {
comment = code;
}
code = ".byte " + string.Join(",", _codeByteCode[i].Split(' ').Select((hexByte) => "$" + hexByte));
}
if(!string.IsNullOrWhiteSpace(comment) && comment.Contains("\n")) {
result.AddRange(comment.Replace("\r", "").Split('\n').Select(cmt => ";" + cmt));
comment = null;
@ -205,7 +210,7 @@ namespace Mesen.GUI.Debugger
}
result.Add(" " + code + (!string.IsNullOrWhiteSpace(comment) ? (" ;" + comment) : ""));
int length = _codeByteContentLength[i];
int length = _codeByteCode[i].Count(c => c == ' ') + 1;
byteLength += length;
i += length;
@ -223,46 +228,84 @@ namespace Mesen.GUI.Debugger
public bool UpdateCode(bool forceUpdate = false)
{
if(_codeChanged || forceUpdate) {
List<int> lineNumbers = new List<int>();
List<string> lineNumberNotes = new List<string>();
List<string> codeNotes = new List<string>();
List<string> codeLines = new List<string>();
_codeContent = new Dictionary<int, string>();
_codeByteContentLength = new Dictionary<int, int>();
_unexecutedAddresses = new HashSet<int>();
_speculativeCodeAddreses = new HashSet<int>();
int index = -1;
int previousIndex = -1;
while((index = _code.IndexOf('\n', index + 1)) >= 0) {
string line = _code.Substring(previousIndex + 1, index - previousIndex - 1);
string[] lineParts = line.Split('\x1');
_codeContent.Clear();
_codeComments.Clear();
_codeByteCode.Clear();
_unexecutedAddresses.Clear();
_speculativeCodeAddreses.Clear();
if(lineParts.Length >= 5) {
int relativeAddress = ParseHexAddress(lineParts[1]);
string[] token = new string[7];
int tokenIndex = 0;
int startPos = 0;
int endPos = 0;
if(lineParts[0] == "0" && lineParts[4].StartsWith(" ")) {
_unexecutedAddresses.Add(relativeAddress);
} else if(lineParts[0] == "2" && lineParts[4].StartsWith(" ")) {
_speculativeCodeAddreses.Add(relativeAddress);
}
Action readToken = () => {
endPos = _code.IndexOf('\x1', endPos) + 1;
token[tokenIndex++] = _code.Substring(startPos, endPos - startPos - 1);
startPos = endPos;
};
Action readLine = () => {
tokenIndex = 0;
readToken(); readToken(); readToken(); readToken(); readToken(); readToken(); readToken();
};
lineNumbers.Add(relativeAddress);
lineNumberNotes.Add(string.IsNullOrWhiteSpace(lineParts[2]) ? "" : lineParts[2].TrimStart('0').PadLeft(4, '0'));
codeNotes.Add(lineParts[3]);
codeLines.Add(lineParts[4]);
_codeByteContentLength[relativeAddress] = lineParts[3].Count(c => c == ' ') + 1;
Func<bool> processLine = () => {
readLine();
_codeContent[relativeAddress] = lineParts[4].Split('\x2')[0].Trim();
int relativeAddress = ParseHexAddress(token[1]);
//Flags:
//1: Executed code
//2: Speculative Code
//4: Indented line
if(token[0] == "4") {
_unexecutedAddresses.Add(relativeAddress);
_lineIndentations.Add(20);
} else if(token[0] == "6") {
_speculativeCodeAddreses.Add(relativeAddress);
_lineIndentations.Add(20);
} else if(token[0] == "5") {
_lineIndentations.Add(20);
} else {
_lineIndentations.Add(0);
}
previousIndex = index;
}
_lineNumbers.Add(relativeAddress);
_lineNumberNotes.Add(string.IsNullOrWhiteSpace(token[2]) ? "" : (token[2].Length > 5 ? token[2].TrimStart('0').PadLeft(4, '0') : token[2]));
_codeNotes.Add(token[3]);
_codeLines.Add(token[4]);
ctrlCodeViewer.TextLines = codeLines.ToArray();
ctrlCodeViewer.LineNumbers = lineNumbers.ToArray();
ctrlCodeViewer.TextLineNotes = codeNotes.ToArray();
ctrlCodeViewer.LineNumberNotes = lineNumberNotes.ToArray();
_addressing.Add(token[5]);
_comments.Add(token[6]);
//Used by assembler
_codeByteCode[relativeAddress] = token[3];
_codeContent[relativeAddress] = token[4];
_codeComments[relativeAddress] = token[6];
return endPos < _code.Length;
};
while(processLine());
ctrlCodeViewer.LineIndentations = _lineIndentations.ToArray();
ctrlCodeViewer.Addressing = _addressing.ToArray();
ctrlCodeViewer.Comments = _comments.ToArray();
ctrlCodeViewer.TextLines = _codeLines.ToArray();
ctrlCodeViewer.LineNumbers = _lineNumbers.ToArray();
ctrlCodeViewer.TextLineNotes = _codeNotes.ToArray();
ctrlCodeViewer.LineNumberNotes = _lineNumberNotes.ToArray();
//These are all temporary and can be cleared right away
_lineNumbers.Clear();
_lineNumberNotes.Clear();
_codeNotes.Clear();
_codeLines.Clear();
_addressing.Clear();
_comments.Clear();
_lineIndentations.Clear();
_codeChanged = false;
UpdateLineColors();
@ -281,37 +324,6 @@ namespace Mesen.GUI.Debugger
}
}
private void HighlightBreakpoints()
{
foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) {
Color? fgColor = Color.White;
Color? bgColor = null;
Color? outlineColor = Color.FromArgb(140, 40, 40);
LineSymbol symbol;
if(breakpoint.Enabled) {
bgColor = Color.FromArgb(140, 40, 40);
symbol = LineSymbol.Circle;
} else {
fgColor = Color.Black;
symbol = LineSymbol.CircleOutline;
}
if(breakpoint.Address == (UInt32)(_currentActiveAddress.HasValue ? (int)_currentActiveAddress.Value : -1)) {
fgColor = Color.Black;
bgColor = Color.Yellow;
symbol |= LineSymbol.Arrow;
} else if(_unexecutedAddresses.Contains((Int32)breakpoint.Address)) {
fgColor = Color.Black;
bgColor = _unexecutedColor;
} else if(_speculativeCodeAddreses.Contains((Int32)breakpoint.Address)) {
fgColor = Color.Black;
bgColor = _speculativeColor;
}
ctrlCodeViewer.SetLineColor((int)breakpoint.Address, fgColor, bgColor, outlineColor, symbol);
}
}
#region Events
private Point _previousLocation;
private bool _preventCloseTooltip = false;
@ -766,6 +778,58 @@ namespace Mesen.GUI.Debugger
this.OnEditCode?.Invoke(new AssemblerEventArgs() { Code = string.Join(Environment.NewLine, code), StartAddress = (UInt16)startAddress, BlockLength = (UInt16)byteLength });
}
}
class LineStyleProvider : ctrlTextbox.ILineStyleProvider
{
ctrlDebuggerCode _code;
public LineStyleProvider(ctrlDebuggerCode code)
{
_code = code;
}
public LineProperties GetLineStyle(int cpuAddress)
{
foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) {
if(!breakpoint.IsAbsoluteAddress && breakpoint.Address == cpuAddress) {
Color? fgColor = Color.White;
Color? bgColor = null;
Color? outlineColor = Color.FromArgb(140, 40, 40);
LineSymbol symbol;
if(breakpoint.Enabled) {
bgColor = Color.FromArgb(140, 40, 40);
symbol = LineSymbol.Circle;
} else {
fgColor = Color.Black;
symbol = LineSymbol.CircleOutline;
}
if(breakpoint.Address == (UInt32)(_code._currentActiveAddress.HasValue ? (int)_code._currentActiveAddress.Value : -1)) {
fgColor = Color.Black;
bgColor = Color.Yellow;
symbol |= LineSymbol.Arrow;
} else if(_code._unexecutedAddresses.Contains((Int32)breakpoint.Address)) {
fgColor = Color.Black;
bgColor = _code._unexecutedColor;
} else if(_code._speculativeCodeAddreses.Contains((Int32)breakpoint.Address)) {
fgColor = Color.Black;
bgColor = _code._speculativeColor;
}
return new LineProperties() { FgColor = fgColor, BgColor = bgColor, OutlineColor = outlineColor, Symbol = symbol };
}
}
if(_code._currentActiveAddress.HasValue && cpuAddress == _code._currentActiveAddress) {
return new LineProperties() { FgColor = Color.Black, BgColor = Color.Yellow, OutlineColor = null, Symbol = LineSymbol.Arrow };
} else if(ConfigManager.Config.DebugInfo.HighlightUnexecutedCode && _code._unexecutedAddresses.Contains(cpuAddress)) {
return new LineProperties() { FgColor = null, BgColor = _code._unexecutedColor, OutlineColor = null, Symbol = LineSymbol.None };
} else if(_code._speculativeCodeAddreses.Contains(cpuAddress)) {
return new LineProperties() { FgColor = null, BgColor = _code._speculativeColor, OutlineColor = null, Symbol = LineSymbol.None };
}
return null;
}
}
}
public class WatchEventArgs : EventArgs

View file

@ -59,7 +59,7 @@ namespace Mesen.GUI.Debugger.Controls
Int32[] entryPoints = InteropEmu.DebugGetFunctionEntryPoints();
bool updating = false;
for(int i = 0; entryPoints[i] >= 0; i++) {
for(int i = 0; entryPoints[i] >= 0 && i < entryPoints.Length; i++) {
Int32 entryPoint = entryPoints[i];
ListViewItem item;
if(!_functions.TryGetValue(entryPoint, out item)) {

View file

@ -32,8 +32,6 @@ namespace Mesen.GUI.Debugger.Controls
public void RefreshData()
{
ctrlScrollableTextbox.ClearLineStyles();
int[] readCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, false);
int[] writeCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Write, false);
int[] execCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Exec, false);
@ -74,16 +72,31 @@ namespace Mesen.GUI.Debugger.Controls
content[i] = data[i].Content;
}
if(chkHighlightUninitRead.Checked) {
ctrlScrollableTextbox.StyleProvider = new LineStyleProvider(new HashSet<int>(data.Where((e) => e.UninitRead).Select((e) => e.Address)));
} else {
ctrlScrollableTextbox.StyleProvider = null;
}
ctrlScrollableTextbox.Header = "Read".PadRight(12) + "Write".PadRight(12) + "Execute".PadRight(12);
ctrlScrollableTextbox.LineNumbers = addresses;
ctrlScrollableTextbox.TextLines = content;
}
if(chkHighlightUninitRead.Checked) {
ctrlScrollableTextbox.BeginUpdate();
foreach(int address in data.Where((e) => e.UninitRead).Select((e) => e.Address)) {
ctrlScrollableTextbox.SetLineColor(address, null, Color.LightCoral);
private class LineStyleProvider : ctrlTextbox.ILineStyleProvider
{
HashSet<int> _addresses = new HashSet<int>();
public LineStyleProvider(HashSet<int> addresses)
{
_addresses = addresses;
}
public LineProperties GetLineStyle(int cpuAddress)
{
if(_addresses.Contains(cpuAddress)) {
return new LineProperties() { BgColor = Color.LightCoral };
}
ctrlScrollableTextbox.EndUpdate();
return null;
}
}

View file

@ -105,11 +105,6 @@ namespace Mesen.GUI.Debugger
this.hScrollBar.Maximum = newMax;
}
public void ClearLineStyles()
{
this.ctrlTextbox.ClearLineStyles();
}
public void BeginUpdate()
{
this.ctrlTextbox.BeginUpdate();
@ -120,10 +115,7 @@ namespace Mesen.GUI.Debugger
this.ctrlTextbox.EndUpdate();
}
public void SetLineColor(int lineNumber, Color? fgColor = null, Color? bgColor = null, Color? outlineColor = null, LineSymbol symbol = LineSymbol.None)
{
this.ctrlTextbox.SetLineColor(lineNumber, fgColor, bgColor, outlineColor, symbol);
}
public ctrlTextbox.ILineStyleProvider StyleProvider { set { this.ctrlTextbox.StyleProvider = value; } }
public int GetLineIndex(int lineNumber)
{
@ -267,6 +259,10 @@ namespace Mesen.GUI.Debugger
this.ctrlTextbox.HorizontalScrollPosition = this.hScrollBar.Value;
}
public string[] Addressing { set { this.ctrlTextbox.Addressing = value; } }
public string[] Comments { set { this.ctrlTextbox.Comments = value; } }
public int[] LineIndentations{ set { this.ctrlTextbox.LineIndentations = value; } }
public string[] TextLines
{
set

View file

@ -79,6 +79,10 @@ namespace Mesen.GUI.Debugger
this.DoubleBuffered = true;
}
public string[] Comments;
public string[] Addressing;
public int[] LineIndentations;
public string[] TextLines
{
set
@ -86,18 +90,17 @@ namespace Mesen.GUI.Debugger
int maxLength = 0;
_maxLineWidthIndex = 0;
_contents = new string[value.Length];
_lineMargins = new int[value.Length];
_contents = value;
for(int i = 0, len = value.Length; i < len; i++) {
_contents[i] = value[i].TrimStart();
string stringToMeasure = GetFullWidthString(_contents[i]);
if(stringToMeasure.Length > maxLength) {
maxLength = stringToMeasure.Length;
int length = _contents[i].Length + (Addressing != null ? Addressing[i].Length : 0);
if(Comments?[i].Length > 0) {
length = Math.Max(length, length > 0 ? CommentSpacingCharCount : 0) + Comments[i].Length;
}
if(length > maxLength) {
maxLength = length;
_maxLineWidthIndex = i;
}
_lineMargins[i] = (value[i].Length - _contents[i].Length) * 10;
}
UpdateHorizontalScrollWidth();
@ -243,9 +246,8 @@ namespace Mesen.GUI.Debugger
}
for(int i = 0, len = _contents.Length; i < len; i++) {
if(Regex.IsMatch(_contents[i], regex, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase)) {
string line = _contents[i].Replace("\x2", "\t").Trim();
string line = _contents[i] + Addressing?[i] + (Comments != null ? ("\t" + Comments[i]) : null);
if(Regex.IsMatch(line, regex, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase)) {
if(line.StartsWith("__") && line.EndsWith("__") || line.StartsWith("[[") && line.EndsWith("]]")) {
line = "Block: " + line.Substring(2, line.Length - 4);
}
@ -335,22 +337,20 @@ namespace Mesen.GUI.Debugger
this.Invalidate();
}
public void SetLineColor(int lineNumber, Color? fgColor = null, Color? bgColor = null, Color? outlineColor = null, LineSymbol symbol = LineSymbol.None)
public interface ILineStyleProvider
{
if(lineNumber != -1) {
if(_lineNumberIndex.ContainsKey(lineNumber)) {
LineProperties properties = new LineProperties() {
BgColor = bgColor,
FgColor = fgColor,
OutlineColor = outlineColor,
Symbol = symbol
};
LineProperties GetLineStyle(int cpuAddress);
}
_lineProperties[_lineNumberIndex[lineNumber]] = properties;
if(!_updating) {
this.Invalidate();
}
}
public ILineStyleProvider StyleProvider { get; set; }
public LineProperties GetLineStyle(int lineNumber)
{
if(StyleProvider != null && _lineNumbers[lineNumber] >= 0) {
return StyleProvider.GetLineStyle(_lineNumbers[lineNumber]);
} else {
return null;
}
}
@ -419,7 +419,7 @@ namespace Mesen.GUI.Debugger
private int GetMargin(Graphics g, bool getExtendedMargin)
{
int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth;
return this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font).Width) : 0;
return (this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font).Width) : 0) - 1;
}
public int GetLineIndexAtPosition(int yPos)
@ -430,14 +430,12 @@ namespace Mesen.GUI.Debugger
return lineIndex;
}
private string GetFullWidthString(string lineContent)
private string GetFullWidthString(int lineIndex)
{
string text = lineContent.Replace("\x2", "");
int commentIndex = text.IndexOf(";");
if(commentIndex > 0 && commentIndex < CommentSpacingCharCount) {
text = text.Insert(commentIndex, "".PadLeft(CommentSpacingCharCount - commentIndex));
string text = _contents[lineIndex] + Addressing?[lineIndex];
if(Comments?[lineIndex].Length > 0) {
return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + Comments[lineIndex];
}
return text;
}
@ -453,9 +451,9 @@ namespace Mesen.GUI.Debugger
}
if(positionX >= 0 && lineIndex < _contents.Length) {
string text = this.GetFullWidthString(_contents[lineIndex]);
string text = this.GetFullWidthString(lineIndex);
//Adjust background color highlights based on number of spaces in front of content
positionX -= _lineMargins[lineIndex];
positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0);
int previousWidth = 0;
for(int i = 0, len = text.Length; i < len; i++) {
@ -476,7 +474,7 @@ namespace Mesen.GUI.Debugger
int charIndex;
int lineIndex;
if(this.GetCharIndex(position, out charIndex, out lineIndex)) {
string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : this.GetFullWidthString(_contents[lineIndex]);
string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : this.GetFullWidthString(lineIndex);
List<char> wordDelimiters = new List<char>(new char[] { ' ', ',', '|', ';', '(', ')', '.', '-', ':' });
if(wordDelimiters.Contains(text[charIndex])) {
return string.Empty;
@ -656,7 +654,7 @@ namespace Mesen.GUI.Debugger
{
if(_contents.Length > _maxLineWidthIndex) {
using(Graphics g = this.CreateGraphics()) {
_maxLineWidth = _lineMargins[_maxLineWidthIndex] + g.MeasureString(GetFullWidthString(_contents[_maxLineWidthIndex]), this.Font).Width;
_maxLineWidth = (LineIndentations != null ? LineIndentations[_maxLineWidthIndex] : 0) + g.MeasureString(GetFullWidthString(_maxLineWidthIndex), this.Font).Width;
HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor);
}
}
@ -738,10 +736,9 @@ namespace Mesen.GUI.Debugger
private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight)
{
string[] lineContent = _contents[currentLine].Split('\x2');
string codeString = lineContent[0].TrimStart();
string addressString = lineContent.Length > 1 ? lineContent[1] : "";
string commentString = lineContent.Length > 2 ? lineContent[2] : "";
string codeString = _contents[currentLine];
string addressString = this.Addressing?[currentLine];
string commentString = this.Comments?[currentLine];
float codeStringLength = g.MeasureString(codeString, this.Font).Width;
float addressStringLength = g.MeasureString(addressString, this.Font).Width;
@ -758,12 +755,12 @@ namespace Mesen.GUI.Debugger
}
//Adjust background color highlights based on number of spaces in front of content
marginLeft += _lineMargins[currentLine];
marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0);
Color textColor = Color.Black;
if(_lineProperties.ContainsKey(currentLine)) {
LineProperties lineProperties = GetLineStyle(currentLine);
if(lineProperties != null) {
//Process background, foreground, outline color and line symbol
LineProperties lineProperties = _lineProperties[currentLine];
textColor = lineProperties.FgColor ?? Color.Black;
if(lineProperties.BgColor.HasValue) {
@ -963,10 +960,11 @@ namespace Mesen.GUI.Debugger
}
//Adjust background color highlights based on number of spaces in front of content
marginLeft += _lineMargins[currentLine];
marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0);
if(_lineProperties.ContainsKey(currentLine)) {
this.DrawLineSymbols(g, positionY, _lineProperties[currentLine], lineHeight);
LineProperties lineProperties = GetLineStyle(currentLine);
if(lineProperties != null) {
this.DrawLineSymbols(g, positionY, lineProperties, lineHeight);
}
}

View file

@ -47,7 +47,7 @@ namespace Mesen.GUI.Debugger
(sameLabel == null || sameLabel == _originalLabel)
&& (sameAddress == null || sameAddress == _originalLabel)
&& (_originalLabel != null || txtLabel.Text.Length > 0 || txtComment.Text.Length > 0)
&& !txtComment.Text.Contains('\x1') && !txtComment.Text.Contains('\x2')
&& !txtComment.Text.Contains('\x1')
&& (txtLabel.Text.Length == 0 || Regex.IsMatch(txtLabel.Text, "^[@_a-zA-Z]+[@_a-zA-Z0-9]*$"));
}

View file

@ -544,6 +544,8 @@ namespace Mesen.GUI.Forms
InteropEmu.SetFlag(EmulationFlags.InBackground, !hasFocus);
}
Image _pauseButton = Mesen.GUI.Properties.Resources.Pause;
Image _playButton = Mesen.GUI.Properties.Resources.Play;
private void UpdateMenus()
{
try {
@ -570,7 +572,7 @@ namespace Mesen.GUI.Forms
mnuPause.Enabled &= !InteropEmu.DebugIsDebuggerRunning();
mnuPause.Text = InteropEmu.IsPaused() ? ResourceHelper.GetMessage("Resume") : ResourceHelper.GetMessage("Pause");
mnuPause.Image = InteropEmu.IsPaused() ? Mesen.GUI.Properties.Resources.Play : Mesen.GUI.Properties.Resources.Pause;
mnuPause.Image = InteropEmu.IsPaused() ? _playButton : _pauseButton;
bool netPlay = InteropEmu.IsServerRunning() || isNetPlayClient;

View file

@ -211,8 +211,12 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void DebugSaveRomToDisk([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename);
[DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper();
public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); }
[DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(out UInt32 length);
public static string DebugGetCode()
{
UInt32 length;
return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper(out length), length);
}
[DllImport(DLLPath, EntryPoint="DebugAssembleCode")] private static extern UInt32 DebugAssembleCodeWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string code, UInt16 startAddress, IntPtr assembledCodeBuffer);
public static Int16[] DebugAssembleCode(string code, UInt16 startAddress)
@ -436,16 +440,16 @@ namespace Mesen.GUI
hRelative.Free();
}
}
[DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")]
private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute);
[DllImport(DLLPath)] private static extern Int32 DebugGetFunctionEntryPointCount();
[DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute, Int32 maxCount);
public static Int32[] DebugGetFunctionEntryPoints()
{
Int32[] entryPoints = new Int32[32768];
int maxCount = DebugGetFunctionEntryPointCount();
Int32[] entryPoints = new Int32[maxCount+1];
GCHandle hEntryPoints = GCHandle.Alloc(entryPoints, GCHandleType.Pinned);
try {
InteropEmu.DebugGetFunctionEntryPointsWrapper(hEntryPoints.AddrOfPinnedObject());
InteropEmu.DebugGetFunctionEntryPointsWrapper(hEntryPoints.AddrOfPinnedObject(), maxCount+1);
} finally {
hEntryPoints.Free();
}
@ -521,24 +525,38 @@ namespace Mesen.GUI
return new List<string>(PtrToStringUtf8(InteropEmu.GetAudioDevicesWrapper()).Split(new string[1] { "||" }, StringSplitOptions.RemoveEmptyEntries ));
}
private static string PtrToStringUtf8(IntPtr ptr)
private static byte[] _codeByteArray = new byte[0];
private static string PtrToStringUtf8(IntPtr ptr, UInt32 length = 0)
{
if(ptr == IntPtr.Zero) {
return "";
}
int len = 0;
while(System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) {
len++;
if(length == 0) {
while(System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) {
len++;
}
} else {
len = (int)length;
}
if(len == 0) {
return "";
}
byte[] array = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
return System.Text.Encoding.UTF8.GetString(array);
if(length == 0) {
byte[] array = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
return System.Text.Encoding.UTF8.GetString(array);
} else {
//For the code window, reuse the same buffer to reduce allocations
if(_codeByteArray.Length < len) {
Array.Resize(ref _codeByteArray, len);
}
System.Runtime.InteropServices.Marshal.Copy(ptr, _codeByteArray, 0, len);
return System.Text.Encoding.UTF8.GetString(_codeByteArray, 0, len);
}
}
public enum ConsoleNotificationType

View file

@ -46,7 +46,7 @@ extern "C"
DllExport void __stdcall DebugStepOver() { GetDebugger()->StepOver(); }
DllExport void __stdcall DebugStepOut() { GetDebugger()->StepOut(); }
DllExport void __stdcall DebugPpuStep(uint32_t count) { GetDebugger()->PpuStep(count); }
DllExport const char* __stdcall DebugGetCode() { return GetDebugger()->GetCode(); }
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(scanline, cycle); }
@ -60,7 +60,8 @@ extern "C"
DllExport void __stdcall DebugGetPalette(uint32_t *frameBuffer) { GetDebugger()->GetMemoryDumper()->GetPalette(frameBuffer); }
DllExport void __stdcall DebugGetCallstack(int32_t *callstackAbsolute, int32_t *callstackRelative) { GetDebugger()->GetCallstack(callstackAbsolute, callstackRelative); }
DllExport void __stdcall DebugGetFunctionEntryPoints(int32_t *entryPoints) { GetDebugger()->GetFunctionEntryPoints(entryPoints); }
DllExport int32_t __stdcall DebugGetFunctionEntryPointCount() { return GetDebugger()->GetFunctionEntryPointCount(); }
DllExport void __stdcall DebugGetFunctionEntryPoints(int32_t *entryPoints, int32_t maxCount) { GetDebugger()->GetFunctionEntryPoints(entryPoints, maxCount); }
DllExport int32_t __stdcall DebugGetRelativeAddress(uint32_t addr, AddressType type) { return GetDebugger()->GetRelativeAddress(addr, type); }
DllExport int32_t __stdcall DebugGetAbsoluteAddress(uint32_t addr) { return GetDebugger()->GetAbsoluteAddress(addr); }