Skip to content

Commit

Permalink
Debugger: PCE - Split PCEAS symbol importer from wla-dx's importer code
Browse files Browse the repository at this point in the history
Added support for length value for data labels, added support for [definitions] section for constants
  • Loading branch information
SourMesen committed Nov 24, 2024
1 parent 3903845 commit af01175
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace Mesen.Debugger.Integration;

public class PceasSymbolFile
public class LegacyPceasSymbolFile
{
public static bool IsValidFile(string content)
{
Expand Down
372 changes: 372 additions & 0 deletions UI/Debugger/Integration/PceasSymbolImporter.cs
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;
}
}
}
Loading

0 comments on commit af01175

Please sign in to comment.