-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Debugger: PCE - Split PCEAS symbol importer from wla-dx's importer code
Added support for length value for data labels, added support for [definitions] section for constants
- Loading branch information
Showing
4 changed files
with
380 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,372 @@ | ||
using Mesen.Config; | ||
using Mesen.Debugger.Labels; | ||
using Mesen.Interop; | ||
using Mesen.Utilities; | ||
using Mesen.Windows; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using System.Threading.Tasks; | ||
|
||
namespace Mesen.Debugger.Integration; | ||
|
||
public class PceasSymbolImporter : ISymbolProvider | ||
{ | ||
private static Regex _definitionRegex = new Regex(@"^([0-9a-fA-F]{8}) ([^\s]*)", RegexOptions.Compiled); | ||
private static Regex _symbolRegex = new Regex(@"^([0-9a-fA-F]{2,4})[:]{0,1}([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) ([0-9a-fA-F]{4}):([0-9a-fA-F]{8}) ([^\s]*)", RegexOptions.Compiled); | ||
private static Regex _fileRegex = new Regex(@"^([0-9a-fA-F]{4}):([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) (.*)", RegexOptions.Compiled); | ||
private static Regex _filePathRegex = new Regex(@"^(""([^;""]*)""\s*;{0,1}\s*(.*))|(.*)", RegexOptions.Compiled); | ||
private static Regex _srcMappingRegex = new Regex(@"^([0-9a-fA-F]{2}):([0-9a-fA-F]{4}) ([0-9a-fA-F]{8}) ([0-9a-fA-F]{4}):([0-9a-fA-F]{8})", RegexOptions.Compiled); | ||
|
||
private Dictionary<int, SourceFileInfo> _sourceFiles = new(); | ||
private Dictionary<string, AddressInfo> _addressByLine = new(); | ||
private Dictionary<string, SourceCodeLocation> _linesByAddress = new(); | ||
private List<SymbolInfo> _symbols = new(); | ||
private RomFormat _format; | ||
|
||
public DateTime SymbolFileStamp { get; private set; } | ||
public string SymbolPath { get; private set; } = ""; | ||
|
||
public List<SourceFileInfo> SourceFiles { get { return _sourceFiles.Values.ToList(); } } | ||
|
||
public PceasSymbolImporter() | ||
{ | ||
_format = EmuApi.GetRomInfo().Format; | ||
} | ||
|
||
public static bool IsValidFile(string content) | ||
{ | ||
return content.Contains("; Generated by PCEAS"); | ||
} | ||
|
||
public AddressInfo? GetLineAddress(SourceFileInfo file, int lineIndex) | ||
{ | ||
AddressInfo address; | ||
if(_addressByLine.TryGetValue(file.Name.ToString() + "_" + lineIndex.ToString(), out address)) { | ||
return address; | ||
} | ||
return null; | ||
} | ||
|
||
public AddressInfo? GetLineEndAddress(SourceFileInfo file, int lineIndex) | ||
{ | ||
return null; | ||
} | ||
|
||
public string GetSourceCodeLine(int prgRomAddress) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public SourceCodeLocation? GetSourceCodeLineInfo(AddressInfo address) | ||
{ | ||
string key = address.Type.ToString() + address.Address.ToString(); | ||
SourceCodeLocation location; | ||
if(_linesByAddress.TryGetValue(key, out location)) { | ||
return location; | ||
} | ||
return null; | ||
} | ||
|
||
public SourceSymbol? GetSymbol(string word, int scopeStart, int scopeEnd) | ||
{ | ||
foreach(SymbolInfo symbol in _symbols) { | ||
if(symbol.Name == word) { | ||
//TODOv2 SCOPE | ||
return symbol.SourceSymbol; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public AddressInfo? GetSymbolAddressInfo(SourceSymbol symbol) | ||
{ | ||
if(symbol.InternalSymbol is SymbolInfo dbgSymbol) { | ||
return dbgSymbol.Address; | ||
} | ||
return null; | ||
} | ||
|
||
public SourceCodeLocation? GetSymbolDefinition(SourceSymbol symbol) | ||
{ | ||
return null; | ||
} | ||
|
||
public List<SourceSymbol> GetSymbols() | ||
{ | ||
return _symbols.Select(s => s.SourceSymbol).ToList(); | ||
} | ||
|
||
public int GetSymbolSize(SourceSymbol srcSymbol) | ||
{ | ||
return 1; | ||
} | ||
|
||
private string[] ProcessSourceFile(string filename, string comment, string[] data) | ||
{ | ||
int ignoreColumnCount = 0; | ||
Regex regex = new Regex("IgnoreColumns=(\\d*)"); | ||
Match m = regex.Match(comment); | ||
if(m.Success) { | ||
int.TryParse(m.Groups[1].Value, out ignoreColumnCount); | ||
} | ||
|
||
if(ignoreColumnCount > 0) { | ||
return data.Select(line => { | ||
if(line.StartsWith('#')) { | ||
//Columns that start with # in listing are converted to comments | ||
return ";" + line; | ||
} else if(line.Length >= ignoreColumnCount) { | ||
//Ignore first X columns (contains address, byte code, etc.) | ||
return line.Substring(ignoreColumnCount); | ||
} | ||
return line; | ||
}).ToArray(); | ||
} else { | ||
return data; | ||
} | ||
} | ||
|
||
private AddressInfo GetLabelAddress(int bank, int addr) | ||
{ | ||
if(bank == 0xF8 && bank <= 0xFB) { | ||
return new AddressInfo() { | ||
Address = (bank - 0xF8) * 0x2000 + (addr & 0x1FFF), | ||
Type = MemoryType.PceWorkRam | ||
}; | ||
} else if(bank == 0xF7) { | ||
return new AddressInfo() { | ||
Address = (addr & 0x1FFF), | ||
Type = MemoryType.PceSaveRam | ||
}; | ||
} else if(bank == 0xFF) { | ||
return new AddressInfo() { | ||
Address = (addr & 0x1FFF), | ||
Type = MemoryType.PceMemory | ||
}; | ||
} else if(_format == RomFormat.PceCdRom && (bank >= 0x68 && bank <= 0x7F)) { | ||
return new AddressInfo() { | ||
Address = (bank - 0x68) * 0x2000 + (addr & 0x1FFF), | ||
Type = MemoryType.PceCardRam | ||
}; | ||
} else if(bank > 0xFF) { | ||
return new AddressInfo() { | ||
Address = (bank - 0x80) * 0x2000 + (addr & 0x1FFF), | ||
Type = MemoryType.PcePrgRom | ||
}; | ||
} else if(bank < 0x80) { | ||
return new AddressInfo() { | ||
Address = bank * 0x2000 + (addr & 0x1FFF), | ||
Type = MemoryType.PcePrgRom | ||
}; | ||
} | ||
return default; | ||
} | ||
|
||
public void Import(string path, bool showResult) | ||
{ | ||
SymbolFileStamp = File.GetLastWriteTime(path); | ||
|
||
string basePath = Path.GetDirectoryName(path) ?? ""; | ||
SymbolPath = basePath; | ||
|
||
string[] lines = File.ReadAllLines(path); | ||
|
||
Dictionary<string, CodeLabel> labels = new Dictionary<string, CodeLabel>(); | ||
byte[] cdlData = new byte[DebugApi.GetMemorySize(MemoryType.PcePrgRom)]; | ||
int errorCount = 0; | ||
|
||
for(int i = 0; i < lines.Length; i++) { | ||
string str = lines[i].Trim(); | ||
if(str == "[symbols]") { | ||
for(; i < lines.Length; i++) { | ||
str = lines[i].Trim(); | ||
int commentStart = str.IndexOf(';'); | ||
if(commentStart >= 0) { | ||
str = str.Substring(0, commentStart); | ||
} | ||
|
||
if(str.Length > 0) { | ||
Match m = _symbolRegex.Match(str); | ||
if(m.Success) { | ||
int bank = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); | ||
int addr = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); | ||
long lengthFlags = long.Parse(m.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); | ||
uint length = (uint)(lengthFlags & ~0xC0000000); | ||
if((lengthFlags & 0x80000000) != 0) { | ||
//Code, always use length = 1 | ||
length = 1; | ||
} | ||
|
||
string originalLabel = m.Groups[6].Value; | ||
string label = LabelManager.InvalidLabelRegex.Replace(originalLabel, "_"); | ||
|
||
if(!LabelManager.LabelRegex.IsMatch(label)) { | ||
//ignore labels that don't respect the label naming restrictions | ||
errorCount++; | ||
continue; | ||
} | ||
|
||
AddressInfo absAddr = GetLabelAddress(bank, addr); | ||
|
||
if(absAddr.Address < 0) { | ||
errorCount++; | ||
continue; | ||
} | ||
|
||
SymbolInfo symbol = new(originalLabel, absAddr); | ||
_symbols.Add(symbol); | ||
|
||
string orgLabel = label; | ||
int j = 1; | ||
while(labels.ContainsKey(label)) { | ||
label = orgLabel + j.ToString(); | ||
j++; | ||
} | ||
|
||
if(ConfigManager.Config.Debug.Integration.IsMemoryTypeImportEnabled(absAddr.Type)) { | ||
labels[label] = new CodeLabel() { | ||
Label = label, | ||
Address = (UInt32)absAddr.Address, | ||
MemoryType = absAddr.Type, | ||
Comment = "", | ||
Flags = CodeLabelFlags.None, | ||
Length = length | ||
}; | ||
} | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
} else if(str == "[definitions]") { | ||
for(; i < lines.Length; i++) { | ||
str = lines[i].Trim(); | ||
if(str.Length > 0) { | ||
Match m = _definitionRegex.Match(str); | ||
if(m.Success) { | ||
int value = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); | ||
string label = m.Groups[2].Value; | ||
|
||
SymbolInfo symbol = new(label, value); | ||
_symbols.Add(symbol); | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
} else if(str == "[source files v2]") { | ||
for(; i < lines.Length; i++) { | ||
if(lines[i].Length > 0) { | ||
Match m = _fileRegex.Match(lines[i]); | ||
if(m.Success) { | ||
int fileId = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); | ||
//int fileCrc = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); | ||
|
||
Match fileMatch = _filePathRegex.Match(m.Groups[4].Value); | ||
if(fileMatch.Success) { | ||
string filePath = fileMatch.Groups[2].Success ? fileMatch.Groups[2].Value : fileMatch.Groups[4].Value; | ||
string comment = fileMatch.Groups[3].Value; | ||
string fullPath; | ||
if(Path.IsPathFullyQualified(filePath)) { | ||
fullPath = filePath; | ||
} else { | ||
string? srcBasePath = basePath; | ||
fullPath = Path.Combine(srcBasePath, filePath); | ||
while(!File.Exists(fullPath)) { | ||
//Go back up folder structure to attempt to find the file | ||
string oldPath = srcBasePath; | ||
srcBasePath = Path.GetDirectoryName(srcBasePath); | ||
if(srcBasePath == null || srcBasePath == oldPath) { | ||
break; | ||
} | ||
fullPath = Path.Combine(srcBasePath, filePath); | ||
} | ||
} | ||
string[] fileData = this.ProcessSourceFile(filePath, comment, File.Exists(fullPath) ? File.ReadAllLines(fullPath) : Array.Empty<string>()); | ||
_sourceFiles[fileId] = new SourceFileInfo(filePath, true, new PceasSourceFile() { Data = fileData }); | ||
} | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
} else if(str == "[bank-to-source]") { | ||
for(; i < lines.Length; i++) { | ||
if(lines[i].Length > 0) { | ||
Match m = _srcMappingRegex.Match(lines[i]); | ||
if(m.Success) { | ||
int bank = Int32.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); | ||
int addr = Int32.Parse(m.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); | ||
|
||
int fileId = Int32.Parse(m.Groups[4].Value, System.Globalization.NumberStyles.HexNumber); | ||
int lineNumber = Int32.Parse(m.Groups[5].Value, System.Globalization.NumberStyles.HexNumber); | ||
|
||
AddressInfo absAddr = GetLabelAddress(bank, addr); | ||
if(absAddr.Address >= 0) { | ||
if(absAddr.Type == MemoryType.PcePrgRom) { | ||
//Build CDL data based on the extra flags present in the mappings | ||
long lengthFlags = long.Parse(m.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); | ||
if((lengthFlags & 0x40000000) != 0) { | ||
cdlData[absAddr.Address] |= (byte)CdlFlags.SubEntryPoint; | ||
} | ||
|
||
byte cdlFlags = (lengthFlags & 0x80000000) != 0 ? (byte)CdlFlags.Code : (byte)CdlFlags.Data; | ||
for(long j = 0, len = (lengthFlags & ~0xC0000000); j < len; j++) { | ||
if(absAddr.Address + j < cdlData.Length) { | ||
cdlData[absAddr.Address + j] |= cdlFlags; | ||
} | ||
} | ||
} | ||
|
||
_addressByLine[_sourceFiles[fileId].Name + "_" + lineNumber.ToString()] = absAddr; | ||
_linesByAddress[absAddr.Type.ToString() + absAddr.Address.ToString()] = new SourceCodeLocation(_sourceFiles[fileId], lineNumber); | ||
} | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
DebugApi.SetCdlData(MemoryType.PcePrgRom, cdlData, cdlData.Length); | ||
LabelManager.SetLabels(labels.Values, true); | ||
|
||
if(showResult) { | ||
if(errorCount > 0) { | ||
MesenMsgBox.Show(null, "ImportLabelsWithErrors", MessageBoxButtons.OK, MessageBoxIcon.Warning, labels.Count.ToString(), errorCount.ToString()); | ||
} else { | ||
MesenMsgBox.Show(null, "ImportLabels", MessageBoxButtons.OK, MessageBoxIcon.Info, labels.Count.ToString()); | ||
} | ||
} | ||
} | ||
|
||
class PceasSourceFile : IFileDataProvider | ||
{ | ||
public string[] Data { get; init; } = Array.Empty<string>(); | ||
} | ||
|
||
private readonly struct SymbolInfo | ||
{ | ||
public string Name { get; } | ||
public int? Value { get; } | ||
public AddressInfo? Address { get; } | ||
public SourceSymbol SourceSymbol { get => new SourceSymbol(Name, Address?.Address ?? Value, this); } | ||
|
||
public SymbolInfo(string name, AddressInfo address) | ||
{ | ||
Name = name; | ||
Address = address; | ||
} | ||
|
||
public SymbolInfo(string name, int value) | ||
{ | ||
Name = name; | ||
Value = value; | ||
} | ||
} | ||
} |
Oops, something went wrong.