Debugger: Improved CA/CC65 integration
-Auto-detect all function entry points -Auto-detect all jump targets -Fixed a bug where jump targets were being disassembled when loading a CDL file, potentially causing data to be shown as code -Fixed some valid data block being shown as unidentified data
This commit is contained in:
parent
a3a91ede59
commit
2c9d6aabd7
14 changed files with 277 additions and 150 deletions
|
@ -151,6 +151,11 @@ bool CodeDataLogger::IsCode(uint32_t absoluteAddr)
|
|||
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code;
|
||||
}
|
||||
|
||||
bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr)
|
||||
{
|
||||
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::JumpTarget) == (uint8_t)CdlPrgFlags::JumpTarget;
|
||||
}
|
||||
|
||||
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
|
||||
{
|
||||
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint;
|
||||
|
|
|
@ -11,9 +11,16 @@ enum class CdlPrgFlags
|
|||
None = 0x00,
|
||||
Code = 0x01,
|
||||
Data = 0x02,
|
||||
IndirectCode = 0x10,
|
||||
|
||||
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
|
||||
//Repurposed to mean the address is the target of a jump instruction
|
||||
JumpTarget = 0x10,
|
||||
|
||||
IndirectData = 0x20,
|
||||
PcmData = 0x40,
|
||||
|
||||
//Unused bit in original CDL spec
|
||||
//Used to denote that the byte is the start of function (sub)
|
||||
SubEntryPoint = 0x80
|
||||
};
|
||||
|
||||
|
@ -74,6 +81,7 @@ public:
|
|||
CdlRatios GetRatios();
|
||||
|
||||
bool IsCode(uint32_t absoluteAddr);
|
||||
bool IsJumpTarget(uint32_t absoluteAddr);
|
||||
bool IsSubEntryPoint(uint32_t absoluteAddr);
|
||||
bool IsData(uint32_t absoluteAddr);
|
||||
bool IsRead(uint32_t absoluteAddr);
|
||||
|
|
|
@ -44,5 +44,5 @@ void CodeRunner::WriteRAM(uint16_t addr, uint8_t value)
|
|||
|
||||
DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress)
|
||||
{
|
||||
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false, false);
|
||||
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false);
|
||||
}
|
|
@ -765,14 +765,18 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin
|
|||
|
||||
bool isSubEntryPoint = _lastInstruction == 0x20; //Previous instruction was a JSR
|
||||
if(absoluteAddr >= 0) {
|
||||
bool isJumpTarget = _disassembler->IsJump(_lastInstruction);
|
||||
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
|
||||
if(isSubEntryPoint) {
|
||||
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::SubEntryPoint);
|
||||
_functionEntryPoints.emplace(absoluteAddr);
|
||||
} else if(isJumpTarget) {
|
||||
//Only mark as jump target if not marked as sub entry point
|
||||
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::JumpTarget);
|
||||
}
|
||||
}
|
||||
|
||||
_disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, false);
|
||||
_disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, true);
|
||||
|
||||
ProcessStepConditions(addr);
|
||||
|
||||
|
@ -1142,11 +1146,6 @@ const char* Debugger::GetCode(uint32_t &length)
|
|||
}
|
||||
}
|
||||
|
||||
void Debugger::GetJumpTargets(bool* jumpTargets)
|
||||
{
|
||||
_disassembler->GetJumpTargets(jumpTargets);
|
||||
}
|
||||
|
||||
int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type)
|
||||
{
|
||||
switch(type) {
|
||||
|
|
|
@ -170,8 +170,6 @@ public:
|
|||
|
||||
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
|
||||
|
||||
void ProcessMarkedBreakpoints(BreakpointType type, OperationInfo &operationInfo);
|
||||
|
||||
shared_ptr<LabelManager> GetLabelManager();
|
||||
|
||||
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
|
||||
|
@ -222,8 +220,6 @@ public:
|
|||
void GenerateCodeOutput();
|
||||
const char* GetCode(uint32_t &length);
|
||||
|
||||
void GetJumpTargets(bool* jumpTargets);
|
||||
|
||||
int32_t GetRelativeAddress(uint32_t addr, AddressType type);
|
||||
int32_t GetAbsoluteAddress(uint32_t addr);
|
||||
int32_t GetAbsoluteChrAddress(uint32_t addr);
|
||||
|
|
|
@ -146,12 +146,30 @@ bool Disassembler::IsUnofficialOpCode(uint8_t opCode)
|
|||
|
||||
bool Disassembler::IsJump(uint8_t opCode)
|
||||
{
|
||||
return opCode == 0x10 || opCode == 0x30|| opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20;
|
||||
return (
|
||||
opCode == 0x10 || //BPL
|
||||
opCode == 0x30 || //BMI
|
||||
opCode == 0x50 || //BVC
|
||||
opCode == 0x70 || //BVS
|
||||
opCode == 0x90 || //BCC
|
||||
opCode == 0xB0 || //BCS
|
||||
opCode == 0xD0 || //BNE
|
||||
opCode == 0xF0 || //BEQ
|
||||
opCode == 0x4C || //JMP (Absolute)
|
||||
opCode == 0x6C || //JMP (Indirect)
|
||||
opCode == 0x20 //JSR
|
||||
);
|
||||
}
|
||||
|
||||
bool Disassembler::IsUnconditionalJump(uint8_t opCode)
|
||||
{
|
||||
return opCode == 0x40 || opCode == 0x60 || opCode == 0x6C || opCode == 0x4C || opCode == 0x20;
|
||||
return (
|
||||
opCode == 0x40 || //RTI
|
||||
opCode == 0x60 || //RTS
|
||||
opCode == 0x6C || //JMP (Indirect)
|
||||
opCode == 0x4C || //JMP (Absolute)
|
||||
opCode == 0x20 //JSR
|
||||
);
|
||||
}
|
||||
|
||||
void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache)
|
||||
|
@ -188,7 +206,7 @@ void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &si
|
|||
}
|
||||
|
||||
|
||||
uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool isJumpTarget)
|
||||
uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool processJumps)
|
||||
{
|
||||
uint32_t mask = info.Type == AddressType::InternalRam ? 0x7FF : 0xFFFFFFFF;
|
||||
|
||||
|
@ -203,9 +221,8 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
|
|||
if(!disInfo) {
|
||||
while(absoluteAddr < (int32_t)size && !(*cache)[absoluteAddr]) {
|
||||
bool isJump = IsUnconditionalJump(source[absoluteAddr]);
|
||||
disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint, isJumpTarget);
|
||||
disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint);
|
||||
isSubEntryPoint = false;
|
||||
isJumpTarget = false;
|
||||
|
||||
(*cache)[absoluteAddr] = shared_ptr<DisassemblyInfo>(disInfo);
|
||||
|
||||
|
@ -219,22 +236,17 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
|
|||
if(isSubEntryPoint) {
|
||||
disInfo->SetSubEntryPoint();
|
||||
}
|
||||
if(isJumpTarget) {
|
||||
disInfo->SetJumpTarget();
|
||||
}
|
||||
|
||||
uint8_t opCode = source[absoluteAddr];
|
||||
if(IsJump(opCode)) {
|
||||
uint16_t jumpDest = disInfo->GetOpAddr(cpuAddress);
|
||||
if(processJumps && IsJump(opCode)) {
|
||||
uint16_t jumpDest = disInfo->GetJumpDestination(cpuAddress, _memoryManager);
|
||||
if(jumpDest != cpuAddress) {
|
||||
AddressTypeInfo addressInfo;
|
||||
_debugger->GetAbsoluteAddressAndType(jumpDest, &addressInfo);
|
||||
|
||||
const uint8_t jsrCode = 0x20;
|
||||
constexpr uint8_t jsrCode = 0x20;
|
||||
if(addressInfo.Address >= 0) {
|
||||
BuildCache(addressInfo, jumpDest, opCode == jsrCode, true);
|
||||
} else {
|
||||
disInfo->SetJumpTarget();
|
||||
BuildCache(addressInfo, jumpDest, opCode == jsrCode, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -539,7 +551,7 @@ string Disassembler::GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uin
|
|||
isVerifiedData = addressInfo.Type == AddressType::PrgRom && cdl->IsData(addr&mask);
|
||||
if(!info && ((disassembleUnidentifiedData && !isVerifiedData) || (disassembleVerifiedData && isVerifiedData))) {
|
||||
dataType = isVerifiedData ? DataType::VerifiedData : DataType::UnidentifiedData;
|
||||
tmpInfo->Initialize(source + (addr & mask), false, false);
|
||||
tmpInfo->Initialize(source + (addr & mask), false);
|
||||
info = tmpInfo;
|
||||
} else if(info) {
|
||||
dataType = DataType::VerifiedCode;
|
||||
|
@ -686,14 +698,3 @@ DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressTypeInfo &info)
|
|||
return DisassemblyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::GetJumpTargets(bool* jumpTargets)
|
||||
{
|
||||
memset(jumpTargets, 0, _disassembleCache.size());
|
||||
for(size_t i = 0, len = _disassembleCache.size(); i < len; i++) {
|
||||
shared_ptr<DisassemblyInfo> disInfo = _disassembleCache[i];
|
||||
if(disInfo && disInfo->IsJumpTarget()) {
|
||||
jumpTargets[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,8 +28,6 @@ private:
|
|||
vector<shared_ptr<DisassemblyInfo>> _disassembleSaveRamCache;
|
||||
vector<shared_ptr<DisassemblyInfo>> _disassembleMemoryCache;
|
||||
|
||||
bool IsJump(uint8_t opCode);
|
||||
bool IsUnconditionalJump(uint8_t opCode);
|
||||
void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1, DataType dataType = DataType::VerifiedCode, char memoryType = ' ');
|
||||
void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, DataType dataType, bool isIndented, char memoryType = ' ');
|
||||
void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
|
||||
|
@ -43,16 +41,16 @@ public:
|
|||
void BuildOpCodeTables(bool useLowerCase);
|
||||
void Reset();
|
||||
|
||||
uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool isJumpTarget);
|
||||
uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool processJumps);
|
||||
void InvalidateCache(AddressTypeInfo &info);
|
||||
|
||||
bool IsUnofficialOpCode(uint8_t opCode);
|
||||
bool IsJump(uint8_t opCode);
|
||||
bool IsUnconditionalJump(uint8_t opCode);
|
||||
|
||||
string GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uint16_t memoryAddr, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager);
|
||||
|
||||
DisassemblyInfo GetDisassemblyInfo(AddressTypeInfo &info);
|
||||
|
||||
void GetJumpTargets(bool* jumpTargets);
|
||||
|
||||
void RebuildPrgRomCache(uint32_t absoluteAddr, int32_t length);
|
||||
};
|
||||
|
|
|
@ -33,9 +33,9 @@ DisassemblyInfo::DisassemblyInfo()
|
|||
{
|
||||
}
|
||||
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget)
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint)
|
||||
{
|
||||
Initialize(opPointer, isSubEntryPoint, isJumpTarget);
|
||||
Initialize(opPointer, isSubEntryPoint);
|
||||
}
|
||||
|
||||
void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage)
|
||||
|
@ -148,10 +148,9 @@ uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr)
|
|||
return opAddr;
|
||||
}
|
||||
|
||||
void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget)
|
||||
void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint)
|
||||
{
|
||||
_isSubEntryPoint = isSubEntryPoint;
|
||||
_isJumpTarget = isJumpTarget;
|
||||
|
||||
uint8_t opCode = *opPointer;
|
||||
_opSize = DisassemblyInfo::OPSize[opCode];
|
||||
|
@ -169,11 +168,6 @@ void DisassemblyInfo::SetSubEntryPoint()
|
|||
_isSubEntryPoint = true;
|
||||
}
|
||||
|
||||
void DisassemblyInfo::SetJumpTarget()
|
||||
{
|
||||
_isJumpTarget = true;
|
||||
}
|
||||
|
||||
int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryManager)
|
||||
{
|
||||
int32_t address = -1;
|
||||
|
@ -192,6 +186,22 @@ int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryMa
|
|||
}
|
||||
}
|
||||
|
||||
int32_t DisassemblyInfo::GetJumpDestination(uint16_t pc, MemoryManager* memoryManager)
|
||||
{
|
||||
int32_t address = -1;
|
||||
if(_opMode == AddrMode::Rel || _opMode == AddrMode::Abs) {
|
||||
address = GetOpAddr(pc);
|
||||
} else if(_opMode == AddrMode::Ind) {
|
||||
address = GetIndirectJumpDestination(memoryManager);
|
||||
}
|
||||
|
||||
if(address >= 0 && address <= 0xFFFF) {
|
||||
return memoryManager->DebugRead(address);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t DisassemblyInfo::GetIndirectJumpDestination(MemoryManager* memoryManager)
|
||||
{
|
||||
uint16_t addr = _byteCode[1] | (_byteCode[2] << 8);
|
||||
|
@ -319,8 +329,3 @@ bool DisassemblyInfo::IsSubExitPoint()
|
|||
{
|
||||
return _isSubExitPoint;
|
||||
}
|
||||
|
||||
bool DisassemblyInfo::IsJumpTarget()
|
||||
{
|
||||
return _isJumpTarget;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ public:
|
|||
|
||||
private:
|
||||
uint8_t _byteCode[3];
|
||||
bool _isJumpTarget = false;
|
||||
bool _isSubEntryPoint = false;
|
||||
bool _isSubExitPoint = false;
|
||||
uint32_t _opSize = 0;
|
||||
|
@ -25,18 +24,17 @@ private:
|
|||
|
||||
public:
|
||||
DisassemblyInfo();
|
||||
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget);
|
||||
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint);
|
||||
|
||||
void Initialize(uint8_t * opPointer, bool isSubEntryPoint, bool isJumpTarget);
|
||||
void Initialize(uint8_t * opPointer, bool isSubEntryPoint);
|
||||
|
||||
void SetSubEntryPoint();
|
||||
|
||||
void SetJumpTarget();
|
||||
|
||||
int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager);
|
||||
|
||||
void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
|
||||
int32_t GetMemoryValue(State& cpuState, MemoryManager* memoryManager);
|
||||
int32_t GetJumpDestination(uint16_t pc, MemoryManager* memoryManager);
|
||||
uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager);
|
||||
void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage);
|
||||
void GetByteCode(string &out);
|
||||
|
@ -45,6 +43,5 @@ public:
|
|||
|
||||
bool IsSubEntryPoint();
|
||||
bool IsSubExitPoint();
|
||||
bool IsJumpTarget();
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
private static Regex _segmentRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+)", RegexOptions.Compiled);
|
||||
private static Regex _segmentPrgRomRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+),.*ooffs=([0-9]+)", RegexOptions.Compiled);
|
||||
private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9]+)){0,1}", RegexOptions.Compiled);
|
||||
private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9+]+)){0,1}", RegexOptions.Compiled);
|
||||
private static Regex _fileRegex = new Regex("^file\tid=([0-9]+),.*name=\"([^\"]+)\"", RegexOptions.Compiled);
|
||||
private static Regex _spanRegex = new Regex("^span\tid=([0-9]+),.*seg=([0-9]+),.*start=([0-9]+),.*size=([0-9]+)(,.*type=([0-9]+)){0,1}", RegexOptions.Compiled);
|
||||
private static Regex _symbolRegex = new Regex("^sym\tid=([0-9]+).*name=\"([0-9a-zA-Z@_-]+)\"(,.*def=([0-9+]+)){0,1}(,.*ref=([0-9+]+)){0,1}(,.*val=0x([0-9a-fA-F]+)){0,1}(,.*seg=([0-9]+)){0,1}(,.*exp=([0-9]+)){0,1}", RegexOptions.Compiled);
|
||||
|
@ -51,6 +51,7 @@ namespace Mesen.GUI.Debugger
|
|||
private static Regex _cPreviousLinesRegex = new Regex("^\\s*//(.*)", RegexOptions.Compiled);
|
||||
|
||||
private Dictionary<int, LineInfo> _linesByPrgAddress = new Dictionary<int, LineInfo>();
|
||||
private Dictionary<int, LineInfo[]> _linesByFile = new Dictionary<int, LineInfo[]>();
|
||||
private Dictionary<string, int> _prgAddressByLine = new Dictionary<string, int>();
|
||||
|
||||
public Dictionary<int, FileInfo> Files { get { return _files; } }
|
||||
|
@ -66,6 +67,17 @@ namespace Mesen.GUI.Debugger
|
|||
return -1;
|
||||
}
|
||||
|
||||
private int GetPrgAddress(SpanInfo span)
|
||||
{
|
||||
SegmentInfo segment;
|
||||
if(_segments.TryGetValue(span.SegmentID, out segment)) {
|
||||
if(!segment.IsRam && span.Size != segment.Size) {
|
||||
return span.Offset + segment.FileOffset - _headerSize;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public LineInfo GetSourceCodeLineInfo(int prgRomAddress)
|
||||
{
|
||||
LineInfo line;
|
||||
|
@ -110,24 +122,49 @@ namespace Mesen.GUI.Debugger
|
|||
return null;
|
||||
}
|
||||
|
||||
private SpanInfo GetSymbolDefinitionSpan(SymbolInfo symbol)
|
||||
{
|
||||
foreach(int definition in symbol.Definitions) {
|
||||
LineInfo definitionLine;
|
||||
FileInfo file;
|
||||
if(_lines.TryGetValue(definition, out definitionLine)) {
|
||||
if(_files.TryGetValue(definitionLine.FileID, out file)) {
|
||||
int lineNumber = definitionLine.LineNumber;
|
||||
while(!(definitionLine?.SpanIDs.Count > 0) && lineNumber < file.Data.Length - 1) {
|
||||
//Definition line contains no code, try the next line
|
||||
lineNumber++;
|
||||
definitionLine = _linesByFile[file.ID][lineNumber];
|
||||
}
|
||||
|
||||
if(definitionLine != null && definitionLine.SpanIDs.Count > 0) {
|
||||
SpanInfo span;
|
||||
if(_spans.TryGetValue(definitionLine.SpanIDs[0], out span)) {
|
||||
return span;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SymbolInfo GetMatchingSymbol(SymbolInfo symbol, int rangeStart, int rangeEnd)
|
||||
{
|
||||
foreach(int reference in symbol.References) {
|
||||
LineInfo line = _lines[reference];
|
||||
if(line.SpanID == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SpanInfo span = _spans[line.SpanID.Value];
|
||||
SegmentInfo seg = _segments[span.SegmentID];
|
||||
foreach(int spanID in line.SpanIDs) {
|
||||
SpanInfo span = _spans[spanID];
|
||||
SegmentInfo seg = _segments[span.SegmentID];
|
||||
|
||||
if(!seg.IsRam) {
|
||||
int spanPrgOffset = seg.FileOffset - _headerSize + span.Offset;
|
||||
if(rangeStart < spanPrgOffset + span.Size && rangeEnd >= spanPrgOffset) {
|
||||
if(symbol.ExportSymbolID != null && symbol.Address == null) {
|
||||
return _symbols[symbol.ExportSymbolID.Value];
|
||||
} else {
|
||||
return symbol;
|
||||
if(!seg.IsRam) {
|
||||
int spanPrgOffset = seg.FileOffset - _headerSize + span.Offset;
|
||||
if(rangeStart < spanPrgOffset + span.Size && rangeEnd >= spanPrgOffset) {
|
||||
if(symbol.ExportSymbolID != null && symbol.Address == null) {
|
||||
return _symbols[symbol.ExportSymbolID.Value];
|
||||
} else {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,9 +283,23 @@ namespace Mesen.GUI.Debugger
|
|||
FileID = Int32.Parse(match.Groups[2].Value),
|
||||
LineNumber = Int32.Parse(match.Groups[3].Value) - 1,
|
||||
Type = match.Groups[5].Success ? (LineType)Int32.Parse(match.Groups[5].Value) : LineType.Assembly,
|
||||
SpanID = match.Groups[7].Success ? (int?)Int32.Parse(match.Groups[7].Value) : null
|
||||
};
|
||||
|
||||
if(line.LineNumber < 0) {
|
||||
line.LineNumber = 0;
|
||||
}
|
||||
|
||||
if(match.Groups[7].Success) {
|
||||
string[] spanIDs = match.Groups[7].Value.Split('+');
|
||||
line.SpanIDs = new List<int>(spanIDs.Length);
|
||||
for(int i = spanIDs.Length - 1; i >= 0; i--) {
|
||||
//Read them backwards to get them in order
|
||||
line.SpanIDs.Add(Int32.Parse(spanIDs[i]));
|
||||
}
|
||||
} else {
|
||||
line.SpanIDs = new List<int>();
|
||||
}
|
||||
|
||||
_usedFileIds.Add(line.FileID);
|
||||
_lines.Add(line.ID, line);
|
||||
return true;
|
||||
|
@ -402,10 +453,13 @@ namespace Mesen.GUI.Debugger
|
|||
count++;
|
||||
}
|
||||
|
||||
int address = GetSymbolAddressInfo(symbol).Address;
|
||||
CodeLabel label = this.CreateLabel(address, segment.IsRam);
|
||||
if(label != null) {
|
||||
label.Label = newName;
|
||||
AddressTypeInfo addressInfo = GetSymbolAddressInfo(symbol);
|
||||
if(addressInfo != null) {
|
||||
int address = addressInfo.Address;
|
||||
CodeLabel label = this.CreateLabel(address, segment.IsRam);
|
||||
if(label != null) {
|
||||
label.Label = newName;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -419,11 +473,11 @@ namespace Mesen.GUI.Debugger
|
|||
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
|
||||
try {
|
||||
LineInfo line = kvp.Value;
|
||||
if(line.SpanID == null) {
|
||||
if(line.SpanIDs.Count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SpanInfo span = _spans[line.SpanID.Value];
|
||||
SpanInfo span = _spans[line.SpanIDs[0]];
|
||||
SegmentInfo segment = _segments[span.SegmentID];
|
||||
|
||||
if(_files[line.FileID].Data == null) {
|
||||
|
@ -479,6 +533,19 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
private void LoadFileData(string path)
|
||||
{
|
||||
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();
|
||||
|
||||
foreach(LineInfo line in _lines.Values) {
|
||||
int currentMax;
|
||||
if(maxLineCountByFile.TryGetValue(line.FileID, out currentMax)) {
|
||||
if(currentMax < line.LineNumber) {
|
||||
maxLineCountByFile[line.FileID] = line.LineNumber;
|
||||
}
|
||||
} else {
|
||||
maxLineCountByFile[line.FileID] = line.LineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(FileInfo file in _files.Values) {
|
||||
if(_usedFileIds.Contains(file.ID)) {
|
||||
try {
|
||||
|
@ -497,6 +564,14 @@ namespace Mesen.GUI.Debugger
|
|||
if(File.Exists(sourceFile)) {
|
||||
file.Data = File.ReadAllLines(sourceFile);
|
||||
}
|
||||
|
||||
LineInfo[] fileInfos = new LineInfo[maxLineCountByFile[file.ID] + 1];
|
||||
foreach(LineInfo line in _lines.Values) {
|
||||
if(line.FileID == file.ID) {
|
||||
fileInfos[line.LineNumber] = line;
|
||||
}
|
||||
}
|
||||
_linesByFile[file.ID] = fileInfos;
|
||||
} catch {
|
||||
_errorCount++;
|
||||
}
|
||||
|
@ -504,6 +579,74 @@ namespace Mesen.GUI.Debugger
|
|||
}
|
||||
}
|
||||
|
||||
private void BuildCdlData()
|
||||
{
|
||||
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom);
|
||||
if(prgSize <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] cdlFile = new byte[prgSize];
|
||||
|
||||
//Mark data/code regions
|
||||
foreach(SpanInfo span in _spans.Values) {
|
||||
int prgAddress = GetPrgAddress(span);
|
||||
if(prgAddress >= 0 && prgAddress < prgSize) {
|
||||
for(int i = 0; i < span.Size; i++) {
|
||||
if(cdlFile[prgAddress + i] != (byte)CdlPrgFlags.Data && !span.IsData && span.Size <= 3) {
|
||||
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Code;
|
||||
} else if(span.IsData) {
|
||||
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Data;
|
||||
} else if(cdlFile[prgAddress + i] == 0) {
|
||||
//Mark bytes as tentative data, until we know that the bytes are actually code
|
||||
cdlFile[prgAddress + i] = 0x04;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < cdlFile.Length; i++) {
|
||||
if(cdlFile[i] == 0x04) {
|
||||
//Mark all bytes marked as tentative data as data
|
||||
cdlFile[i] = (byte)CdlPrgFlags.Data;
|
||||
}
|
||||
}
|
||||
|
||||
//Find/identify functions and jump targets
|
||||
byte[] prgRomContent = InteropEmu.DebugGetMemoryState(DebugMemoryType.PrgRom);
|
||||
foreach(SymbolInfo symbol in _symbols.Values) {
|
||||
LineInfo line;
|
||||
if(!symbol.SegmentID.HasValue) {
|
||||
//This is a constant, ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(int reference in symbol.References) {
|
||||
if(_lines.TryGetValue(reference, out line) && line.SpanIDs.Count > 0) {
|
||||
SpanInfo span;
|
||||
if(_spans.TryGetValue(line.SpanIDs[0], out span) && !span.IsData && span.Size <= 3) {
|
||||
int referencePrgAddr = GetPrgAddress(span);
|
||||
if(referencePrgAddr >= 0 && referencePrgAddr < prgRomContent.Length) {
|
||||
byte opCode = prgRomContent[referencePrgAddr];
|
||||
if(opCode == 0x20 || opCode == 0x10 || opCode == 0x30 || opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20 || opCode == 0x4C || opCode == 0x6C) {
|
||||
//This symbol is used with a JSR/jump instruction, so it's either a function or jump target
|
||||
bool isJsr = opCode == 0x20;
|
||||
SpanInfo definitionSpan = GetSymbolDefinitionSpan(symbol);
|
||||
if(definitionSpan != null) {
|
||||
int definitionPrgAddr = GetPrgAddress(definitionSpan);
|
||||
if(definitionPrgAddr >= 0 && definitionPrgAddr < prgRomContent.Length) {
|
||||
cdlFile[definitionPrgAddr] |= (byte)(isJsr ? CdlPrgFlags.SubEntryPoint : CdlPrgFlags.JumpTarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
InteropEmu.DebugSetCdlData(cdlFile);
|
||||
}
|
||||
|
||||
public void Import(string path, bool silent = false)
|
||||
{
|
||||
RomInfo romInfo = InteropEmu.GetRomInfo();
|
||||
|
@ -537,51 +680,30 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
LoadFileData(basePath);
|
||||
|
||||
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom);
|
||||
if(prgSize > 0) {
|
||||
byte[] cdlFile = new byte[prgSize];
|
||||
foreach(KeyValuePair<int, SpanInfo> kvp in _spans) {
|
||||
SegmentInfo segment;
|
||||
if(_segments.TryGetValue(kvp.Value.SegmentID, out segment)) {
|
||||
if(!segment.IsRam && kvp.Value.Size != segment.Size) {
|
||||
int prgAddress = kvp.Value.Offset + segment.FileOffset - _headerSize;
|
||||
|
||||
if(prgAddress >= 0 && prgAddress < prgSize) {
|
||||
for(int i = 0; i < kvp.Value.Size; i++) {
|
||||
if(cdlFile[prgAddress + i] == 0 && !kvp.Value.IsData && kvp.Value.Size <= 3) {
|
||||
cdlFile[prgAddress + i] = (byte)0x01;
|
||||
} else if(kvp.Value.IsData) {
|
||||
cdlFile[prgAddress + i] = (byte)0x02;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
InteropEmu.DebugSetCdlData(cdlFile);
|
||||
}
|
||||
BuildCdlData();
|
||||
|
||||
foreach(LineInfo line in _lines.Values) {
|
||||
if(line.SpanID == null) {
|
||||
continue;
|
||||
}
|
||||
foreach(int spanID in line.SpanIDs) {
|
||||
SpanInfo span;
|
||||
if(_spans.TryGetValue(spanID, out span)) {
|
||||
SegmentInfo segment;
|
||||
if(_segments.TryGetValue(span.SegmentID, out segment) && !segment.IsRam) {
|
||||
for(int i = 0; i < span.Size; i++) {
|
||||
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
|
||||
|
||||
FileInfo file = _files[line.FileID];
|
||||
SpanInfo span = _spans[line.SpanID.Value];
|
||||
SegmentInfo segment = _segments[span.SegmentID];
|
||||
if(!segment.IsRam) {
|
||||
for(int i = 0; i < span.Size; i++) {
|
||||
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
|
||||
LineInfo existingLine;
|
||||
if(_linesByPrgAddress.TryGetValue(prgAddress, out existingLine) && existingLine.Type == LineType.External) {
|
||||
//Give priority to lines that come from C files
|
||||
continue;
|
||||
}
|
||||
|
||||
LineInfo existingLine;
|
||||
if(_linesByPrgAddress.TryGetValue(prgAddress, out existingLine) && existingLine.Type == LineType.External) {
|
||||
//Give priority to lines that come from C files
|
||||
continue;
|
||||
}
|
||||
|
||||
_linesByPrgAddress[prgAddress] = line;
|
||||
if(i == 0) {
|
||||
_prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
|
||||
_linesByPrgAddress[prgAddress] = line;
|
||||
if(i == 0 && spanID == line.SpanIDs[0]) {
|
||||
//Mark the first byte of the first span representing this line as the PRG address for this line of code
|
||||
FileInfo file = _files[line.FileID];
|
||||
_prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -606,7 +728,7 @@ namespace Mesen.GUI.Debugger
|
|||
labels.AddRange(_saveRamLabels.Values);
|
||||
labelCount += _ramLabels.Count + _workRamLabels.Count + _saveRamLabels.Count;
|
||||
}
|
||||
LabelManager.SetLabels(labels, !silent);
|
||||
LabelManager.SetLabels(labels, true);
|
||||
|
||||
if(!silent) {
|
||||
if(_errorCount > 0) {
|
||||
|
@ -660,7 +782,7 @@ namespace Mesen.GUI.Debugger
|
|||
{
|
||||
public int ID;
|
||||
public int FileID;
|
||||
public int? SpanID;
|
||||
public List<int> SpanIDs;
|
||||
public LineType Type;
|
||||
|
||||
public int LineNumber;
|
||||
|
|
|
@ -145,10 +145,10 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
public static void CreateAutomaticJumpLabels()
|
||||
{
|
||||
bool[] jumpTargets = InteropEmu.DebugGetJumpTargets();
|
||||
byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
|
||||
List<CodeLabel> labelsToAdd = new List<CodeLabel>();
|
||||
for(int i = 0; i < jumpTargets.Length; i++) {
|
||||
if(jumpTargets[i] && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
|
||||
for(int i = 0; i < cdlData.Length; i++) {
|
||||
if((cdlData[i] & (byte)CdlPrgFlags.JumpTarget) != 0 && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
|
||||
labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace Mesen.GUI.Debugger
|
|||
_resultCount = 0;
|
||||
|
||||
HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints());
|
||||
bool[] isJumpTargets = InteropEmu.DebugGetJumpTargets();
|
||||
byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
|
||||
|
||||
List<SearchResultInfo> searchResults = new List<SearchResultInfo>();
|
||||
|
||||
|
@ -201,7 +201,7 @@ namespace Mesen.GUI.Debugger
|
|||
SearchResultType resultType = SearchResultType.Data;
|
||||
if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) {
|
||||
resultType = SearchResultType.Function;
|
||||
} else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < isJumpTargets.Length && isJumpTargets[addressInfo.Address]) {
|
||||
} else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < cdlData.Length && (cdlData[addressInfo.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
|
||||
resultType = SearchResultType.JumpTarget;
|
||||
} else if(isConstant) {
|
||||
resultType = SearchResultType.Constant;
|
||||
|
@ -226,7 +226,7 @@ namespace Mesen.GUI.Debugger
|
|||
SearchResultType resultType = SearchResultType.Data;
|
||||
if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) {
|
||||
resultType = SearchResultType.Function;
|
||||
} else if(label.AddressType == AddressType.PrgRom && label.Address < isJumpTargets.Length && isJumpTargets[label.Address]) {
|
||||
} else if(label.AddressType == AddressType.PrgRom && label.Address < cdlData.Length && (cdlData[label.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
|
||||
resultType = SearchResultType.JumpTarget;
|
||||
}
|
||||
|
||||
|
|
|
@ -368,21 +368,6 @@ namespace Mesen.GUI
|
|||
return assembledCode;
|
||||
}
|
||||
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetJumpTargets")] private static extern void DebugGetJumpTargetsWrapper(IntPtr isJumpTargets);
|
||||
public static bool[] DebugGetJumpTargets()
|
||||
{
|
||||
bool[] isJumpTarget = new bool[InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom)];
|
||||
|
||||
GCHandle hJumpTarget = GCHandle.Alloc(isJumpTarget, GCHandleType.Pinned);
|
||||
try {
|
||||
InteropEmu.DebugGetJumpTargetsWrapper(hJumpTarget.AddrOfPinnedObject());
|
||||
} finally {
|
||||
hJumpTarget.Free();
|
||||
}
|
||||
|
||||
return isJumpTarget;
|
||||
}
|
||||
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer);
|
||||
public static byte[] DebugGetMemoryState(DebugMemoryType type)
|
||||
{
|
||||
|
@ -608,6 +593,12 @@ namespace Mesen.GUI
|
|||
}
|
||||
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetCdlData")] private static extern void DebugGetCdlDataWrapper(UInt32 offset, UInt32 length, DebugMemoryType type, IntPtr counts);
|
||||
|
||||
public static byte[] DebugGetPrgCdlData()
|
||||
{
|
||||
return DebugGetCdlData(0, (uint)InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom), DebugMemoryType.PrgRom);
|
||||
}
|
||||
|
||||
public static byte[] DebugGetCdlData(UInt32 offset, UInt32 length, DebugMemoryType type)
|
||||
{
|
||||
byte[] cdlData = new byte[length];
|
||||
|
@ -1180,9 +1171,16 @@ namespace Mesen.GUI
|
|||
None = 0x00,
|
||||
Code = 0x01,
|
||||
Data = 0x02,
|
||||
IndirectCode = 0x10,
|
||||
|
||||
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
|
||||
//Repurposed to mean the address is the target of a jump instruction
|
||||
JumpTarget = 0x10,
|
||||
|
||||
IndirectData = 0x20,
|
||||
PcmData = 0x40,
|
||||
|
||||
//Unused bit in original CDL spec
|
||||
//Used to denote that the byte is the start of function (sub)
|
||||
SubEntryPoint = 0x80
|
||||
}
|
||||
|
||||
|
|
|
@ -67,8 +67,6 @@ extern "C"
|
|||
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
|
||||
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
|
||||
|
||||
DllExport void __stdcall DebugGetJumpTargets(bool* isJumpTarget) { GetDebugger()->GetJumpTargets(isJumpTarget); }
|
||||
|
||||
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); }
|
||||
DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); }
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue