diff --git a/EightBitNet.sln b/EightBitNet.sln index d5c9d50..cc0890e 100644 --- a/EightBitNet.sln +++ b/EightBitNet.sln @@ -35,7 +35,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Z80.FuseTest", "Z80\Z80.Fus EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Z80.Test", "Z80\Z80.Test\Z80.Test.csproj", "{E6AE640E-4A42-4D8F-8ECE-2FBEBC29741B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M6502.HarteTest", "M6502\M6502.HarteTest\M6502.HarteTest.csproj", "{8686B0DC-A431-4239-836E-ABC90BD69920}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "M6502.HarteTest", "M6502\M6502.HarteTest\M6502.HarteTest.csproj", "{8686B0DC-A431-4239-836E-ABC90BD69920}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M6502.Symbols", "M6502\M6502.Symbols\M6502.Symbols.csproj", "{77B4729A-1899-44D3-92A6-334FF9CA8A65}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -111,6 +113,10 @@ Global {8686B0DC-A431-4239-836E-ABC90BD69920}.Debug|Any CPU.Build.0 = Debug|Any CPU {8686B0DC-A431-4239-836E-ABC90BD69920}.Release|Any CPU.ActiveCfg = Release|Any CPU {8686B0DC-A431-4239-836E-ABC90BD69920}.Release|Any CPU.Build.0 = Release|Any CPU + {77B4729A-1899-44D3-92A6-334FF9CA8A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77B4729A-1899-44D3-92A6-334FF9CA8A65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77B4729A-1899-44D3-92A6-334FF9CA8A65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77B4729A-1899-44D3-92A6-334FF9CA8A65}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/M6502/M6502.Symbols/File.cs b/M6502/M6502.Symbols/File.cs new file mode 100644 index 0000000..f16c8bc --- /dev/null +++ b/M6502/M6502.Symbols/File.cs @@ -0,0 +1,25 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + // file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0 + public class File : NamedSection + { + public int Size => this.TakeInteger("size"); + + public long ModificationTime => this.TakeLong("mtime"); + + public Symbols.Module Module => this.TakeModuleReference(); + + public File() + { + _ = this._integer_keys.Add("size"); + _ = this._hex_long_keys.Add("mtime"); + _ = this._integer_keys.Add("mod"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/IdentifiableSection.cs b/M6502/M6502.Symbols/IdentifiableSection.cs new file mode 100644 index 0000000..7c15d88 --- /dev/null +++ b/M6502/M6502.Symbols/IdentifiableSection.cs @@ -0,0 +1,77 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + public class IdentifiableSection : Section + { + public int ID => this.TakeInteger("id"); + + protected IdentifiableSection() => _ = this._integer_keys.Add("id"); + + #region Foreign key constraints + + #region Generic FK access + + protected static T TakeReference(int key, List from) where T : IdentifiableSection + { + var identifiable = from[key]; + if (identifiable.ID != key) + { + throw new ArgumentOutOfRangeException(nameof(key), "Retrieved object has a mismatched key"); + } + return identifiable; + } + + protected T TakeReference(string key, List from) where T : IdentifiableSection => TakeReference(this.TakeInteger(key), from); + + protected T? MaybeTakeReference(string key, List from) where T : IdentifiableSection + { + var id = this.MaybeTakeInteger(key); + return id == null ? null : TakeReference(id.Value, from); + } + + protected List TakeReferences(string key, List from) where T : IdentifiableSection + { + List returned = []; + var ids = this.MaybeTakeMultiple(key); + if (ids != null) + { + foreach (var id in ids) + { + returned.Add(from[id]); + } + } + return returned; + } + + #endregion + + #region Specific FK access + + protected Module TakeModuleReference(string key = "mod") => this.TakeReference(key, this._parent.Modules); + + protected File TakeFileReference(string key = "file") => this.TakeReference(key, this._parent.Files); + + protected Type TakeTypeReference(string key = "type") => this.TakeReference(key, this._parent.Types); + + protected Segment TakeSegmentReference(string key = "seg") => this.TakeReference(key, this._parent.Segments); + + protected Scope TakeScopeReference(string key = "scope") => this.TakeReference(key, this._parent.Scopes); + + protected Scope? MaybeTakeParentReference(string key = "parent") => this.MaybeTakeReference(key, this._parent.Scopes); + + protected Symbol? MaybeTakeSymbolReference(string key = "sym") => this.MaybeTakeReference(key, this._parent.Symbols); + + protected List TakeSpanReferences(string key = "span") => this.TakeReferences(key, this._parent.Spans); + + protected List TakeLineReferences(string key) => this.TakeReferences(key, this._parent.Lines); + + #endregion + + #endregion + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Information.cs b/M6502/M6502.Symbols/Information.cs new file mode 100644 index 0000000..a6a524b --- /dev/null +++ b/M6502/M6502.Symbols/Information.cs @@ -0,0 +1,28 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + //info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3 + public class Information : Section + { + public Information() + { + _ = this._integer_keys.Add("csym"); + _ = this._integer_keys.Add("file"); + _ = this._integer_keys.Add("lib"); + _ = this._integer_keys.Add("line"); + _ = this._integer_keys.Add("mod"); + _ = this._integer_keys.Add("scope"); + _ = this._integer_keys.Add("seg"); + _ = this._integer_keys.Add("span"); + _ = this._integer_keys.Add("sym"); + _ = this._integer_keys.Add("type"); + } + + public int Count(string key) => this.TakeInteger(key); + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Line.cs b/M6502/M6502.Symbols/Line.cs new file mode 100644 index 0000000..f92f6c2 --- /dev/null +++ b/M6502/M6502.Symbols/Line.cs @@ -0,0 +1,31 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + // line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195 + public class Line : IdentifiableSection + { + public Symbols.File File => this.TakeFileReference(); + + public int LineNumber => this.TakeInteger("line"); + + public Symbols.Type Type => this.TakeTypeReference(); + + public int Count => this.TakeInteger("count"); + + public List Spans => this.TakeSpanReferences(); + + public Line() + { + _ = this._integer_keys.Add("file"); + _ = this._integer_keys.Add("line"); + _ = this._integer_keys.Add("type"); + _ = this._integer_keys.Add("count"); + _ = this._multiple_keys.Add("span"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/M6502.Symbols.csproj b/M6502/M6502.Symbols/M6502.Symbols.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/M6502/M6502.Symbols/M6502.Symbols.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/M6502/M6502.Symbols/Module.cs b/M6502/M6502.Symbols/Module.cs new file mode 100644 index 0000000..81b8ed9 --- /dev/null +++ b/M6502/M6502.Symbols/Module.cs @@ -0,0 +1,16 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + // mod id=0,name="sudoku.o",file=0 + public class Module : NamedSection + { + public Symbols.File File => this.TakeFileReference(); + + public Module() => _ = this._integer_keys.Add("file"); + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/NamedSection.cs b/M6502/M6502.Symbols/NamedSection.cs new file mode 100644 index 0000000..1494b41 --- /dev/null +++ b/M6502/M6502.Symbols/NamedSection.cs @@ -0,0 +1,15 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + public class NamedSection : IdentifiableSection + { + public string Name => this.TakeString("name"); + + protected NamedSection() => _ = this._string_keys.Add("name"); + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Parser.cs b/M6502/M6502.Symbols/Parser.cs new file mode 100644 index 0000000..dce0ef6 --- /dev/null +++ b/M6502/M6502.Symbols/Parser.cs @@ -0,0 +1,384 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + using System.Diagnostics; + + public sealed class Parser + { + #region Variables, properties etc. + + public bool Parsed { get; private set; } + + // Section -> Unique ID list of dictionary entries + // Being sorted allows us to verify IDs as they arrive + private readonly Dictionary>> _parsed = []; + + private Version? _version; + private Information? _information; + + public List Files { get; } = []; + public List Lines { get; } = []; + public List Modules { get; } = []; + public List Segments { get; } = []; + public List Spans { get; } = []; + public List Scopes { get; } = []; + public List Symbols { get; } = []; + public List Types { get; } = []; + + // Symbol sub-types + public List Labels { get; } = []; + public List Equates { get; } = []; + + #endregion + + #region Lookups + + #region Label lookup + + public List LookupLabels(int address) + { + var returned = new List(); + foreach (var label in this.Labels) + { + if (label.Value == address) + { + returned.Add(label); + } + } + return returned; + } + + public Symbol? LookupLabel(int address) + { + var labels = this.LookupLabels(address); + return labels.Count > 0 ? labels[0] : null; + } + + public List LookupLabels(string name) + { + var returned = new List(); + foreach (var label in this.Labels) + { + if (label.Name == name) + { + returned.Add(label); + } + } + return returned; + } + + public Symbol? LookupLabel(string name) + { + var labels = this.LookupLabels(name); + return labels.Count > 0 ? labels[0] : null; + } + + #endregion + + #region Constant lookup + + public List LookupEquates(int constant) + { + var returned = new List(); + foreach (var equate in this.Equates) + { + if (equate.Value == constant) + { + returned.Add(equate); + } + } + return returned; + } + + public Symbol? LookupEquate(int constant) + { + var equates = this.LookupEquates(constant); + return equates.Count > 0 ? equates[0] : null; + } + + #endregion + + #region Scope lookup + + public Scope? LookupScope(string name) + { + foreach (var scope in this.Scopes) + { + if (scope.Name == name) + { + return scope; + } + } + return null; + } + + public Scope? LookupScope(int address) + { + foreach (var scope in this.Scopes) + { + var symbol = scope.Symbol; + if (symbol != null) + { + var symbolAddress = symbol.Value; + var size = scope.Size; + if ((address >= symbolAddress) && (address < symbolAddress + size)) + { + return scope; + } + } + } + return null; + } + + #endregion + + #region Scope evaluation + + public static List EvaluateScope(Scope start) + { + var returned = new List(); + for (var current = start; current.Parent != null; current = current.Parent) + { + returned.Add(current); + } + return returned; + } + + public static List EvaluateScope(Symbol symbol) => EvaluateScope(symbol.Scope); + + #endregion + + #region Namespace evaluation from scope + + public static string BuildNamespace(Symbol symbol) + { + var returned = string.Empty; + var scopes = EvaluateScope(symbol); + for (var i = scopes.Count - 1; i >= 0; i--) + { + var scope = scopes[i]; + var name = scope.Name; + Debug.Assert(name.Length > 0); + returned += name; + var last = i == 0; + if (!last) + { + returned += '.'; + } + } + return returned; + } + + public static string PrefixNamespace(Symbol symbol) + { + var prefix = BuildNamespace(symbol); + var name = symbol.Name; + return string.IsNullOrEmpty(prefix) ? name : $"{prefix}.{name}"; + } + + #endregion + + #region Qualified symbol lookup + + public bool TryGetQualifiedLabel(ushort absolute, out string label) + { + var symbol = this.LookupLabel(absolute); + var available = symbol != null; + label = available ? PrefixNamespace(symbol) : string.Empty; + return available; + } + + public string MaybeGetQualifiedLabel(ushort absolute) => this.TryGetQualifiedLabel(absolute, out var label) ? label : string.Empty; + + public bool TryGetQualifiedEquate(ushort value, out string name) + { + var symbol = this.LookupEquate(value); + var available = symbol != null; + name = available ? PrefixNamespace(symbol) : string.Empty; + return available; + } + + #endregion + + #endregion + + #region Metadata lookup + + private int InformationCount(string key) + { + if (this._information == null) + { + throw new InvalidOperationException("Information section has not been initialised"); + } + return this._information.Count(key); + } + + private void VerifyInformationCount(string key, int extracted) + { + if (extracted != this.InformationCount(key)) + { + throw new InvalidOperationException($"Invalid symbol file format (Information/{key} section count mismatch)"); + } + } + + #endregion + + #region Section extractors + + private void ExtractFiles() => this.Extract("file", this.Files); + + private void ExtractLines() => this.Extract("line", this.Lines); + + private void ExtractModules() => this.Extract("mod", this.Modules); + + private void ExtractSegments() => this.Extract("seg", this.Segments); + + private void ExtractSpans() => this.Extract("span", this.Spans); + + private void ExtractScopes() => this.Extract("scope", this.Scopes); + + private void ExtractSymbols() => this.Extract("sym", this.Symbols); + + private void ExtractTypes() => this.Extract("type", this.Types); + + private void Extract(string key, List into) where T : IdentifiableSection, new() + { + if (!this._parsed.TryGetValue(key, out var parsed)) + { + throw new InvalidOperationException($"Debugging section: '{key}' is unavailable"); + } + foreach (var element in parsed) + { + var id = element.Key; + Debug.Assert(into.Count == id); + var information = element.Value; + var entry = new T(); + entry.Parse(this, information); + into.Add(entry); + } + this.VerifyInformationCount(key, into.Count); + } + + #endregion + + #region Parser driver + + public void Parse(string path) + { + if (this.Parsed) + { + throw new InvalidOperationException("A file has already been parsed."); + } + + using var reader = new StreamReader(path); + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line == null) + { + break; + } + + this.ParseLine(line.Split(' ', '\t')); + } + + this.ExtractFiles(); + this.ExtractLines(); + this.ExtractModules(); + this.ExtractSegments(); + this.ExtractSpans(); + this.ExtractScopes(); + this.ExtractSymbols(); + this.ExtractTypes(); + + this.Parsed = true; + } + + private void ParseLine(string[] elements) + { + Debug.Assert(elements != null); + if (elements.Length != 2) + { + throw new InvalidOperationException("Invalid symbol file format (definition does not have section/values format)"); + } + + var key = elements[0]; + var parts = elements[1].Split(','); + + if (key is "version") + { + this._version = new Version(); + this._version.Parse(this, BuildDictionary(parts)); + } + else if (key is "info") + { + this._information = new Information(); + this._information.Parse(this, BuildDictionary(parts)); + } + else + { + this.Parse(key, parts); + } + } + + private void Parse(string key, string[] parts) + { + if (!this._parsed.TryGetValue(key, out var section)) + { + this._parsed[key] = []; + section = this._parsed[key]; + } + + var dictionary = BuildDictionary(parts); + if (!dictionary.TryGetValue("id", out var id)) + { + throw new InvalidOperationException("Invalid symbol file format (definition does not have id)"); + } + + var identifier = int.Parse(id); + if (section.ContainsKey(identifier)) + { + throw new InvalidOperationException("Invalid symbol file format (definition id has clashed)"); + } + + if (this._information == null) + { + throw new InvalidOperationException("Invalid symbol file format (info section has not been parsed)"); + } + + var count = this._information.Count(key); + if ((identifier + 1) > count) + { + throw new InvalidOperationException($"Invalid symbol file format (No count information available for {section})"); + } + + section.Add(identifier, dictionary); + } + + private static Dictionary BuildDictionary(string[] parts) + { + var dictionary = new Dictionary(); + foreach (var part in parts) + { + var definition = part.Split('='); + if (definition.Length != 2) + { + throw new InvalidOperationException("Invalid symbol file format (definition is missing equals)"); + } + + var key = definition[0]; + var value = definition[1]; + dictionary[key] = value; + } + + return dictionary; + } + + #endregion + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Scope.cs b/M6502/M6502.Symbols/Scope.cs new file mode 100644 index 0000000..0db53bc --- /dev/null +++ b/M6502/M6502.Symbols/Scope.cs @@ -0,0 +1,36 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + //scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354 + //scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15 + //scope id = 7, name = "print_box_break_vertical", mod = 0, type = scope, size = 6, parent = 0, sym = 33, span = 72 + public class Scope : NamedSection + { + public Symbols.Module Module => this.TakeModuleReference(); + + public string? Type => this.MaybeTakeString("type"); + + public int Size => this.TakeInteger("size"); + + public Scope? Parent => this.MaybeTakeParentReference(); + + public Symbols.Symbol? Symbol => this.MaybeTakeSymbolReference(); + + public List Spans => this.TakeSpanReferences(); + + public Scope() + { + _ = this._integer_keys.Add("mod"); + _ = this._enumeration_keys.Add("type"); + _ = this._integer_keys.Add("size"); + _ = this._integer_keys.Add("parent"); + _ = this._multiple_keys.Add("sym"); + _ = this._multiple_keys.Add("span"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Section.cs b/M6502/M6502.Symbols/Section.cs new file mode 100644 index 0000000..a64b4a3 --- /dev/null +++ b/M6502/M6502.Symbols/Section.cs @@ -0,0 +1,103 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + using System.Globalization; + + public class Section + { + protected Parser? _parent; + + protected readonly Dictionary _strings = []; + protected readonly Dictionary _integers = []; + protected readonly Dictionary _longs = []; + protected readonly Dictionary> _multiples = []; + + protected readonly HashSet _string_keys = []; + protected readonly HashSet _enumeration_keys = []; + protected readonly HashSet _integer_keys = []; + protected readonly HashSet _long_keys = []; + protected readonly HashSet _hex_integer_keys = []; + protected readonly HashSet _hex_long_keys = []; + protected readonly HashSet _multiple_keys = []; + + public virtual void Parse(Parser parent, Dictionary entries) + { + this._parent = parent; + foreach (var entry in entries) + { + this.Parse(entry); + } + } + + private void Parse(KeyValuePair entry) + { + var key = entry.Key; + var value = entry.Value; + if (_string_keys.Contains(key)) + { + this._strings[key] = ExtractString(value); + } + else if (_enumeration_keys.Contains(key)) + { + this._strings[key] = ExtractEnumeration(value); + } + else if (_integer_keys.Contains(key)) + { + this._integers[key] = ExtractInteger(value); + } + else if (_hex_integer_keys.Contains(key)) + { + this._integers[key] = ExtractHexInteger(value); + } + else if (_long_keys.Contains(key)) + { + this._longs[key] = ExtractLong(value); + } + else if (_hex_long_keys.Contains(key)) + { + this._longs[key] = ExtractHexLong(value); + } + else if (_multiple_keys.Contains(key)) + { + this._multiples[key] = ExtractCompoundInteger(value); + } + else + { + throw new InvalidOperationException($"Section: {key} has not been categorised"); + } + } + + protected int? MaybeTakeInteger(string key) => this._integers.TryGetValue(key, out var value) ? value : null; + protected long? MaybeTakeLong(string key) => this._longs.TryGetValue(key, out var value) ? value : null; + protected string? MaybeTakeString(string key) => this._strings.TryGetValue(key, out var value) ? value : null; + protected List? MaybeTakeMultiple(string key) => this._multiples.TryGetValue(key, out var value) ? value : null; + + protected int TakeInteger(string key) => this.MaybeTakeInteger(key) ?? throw new InvalidOperationException($"Section is missing an integer entry named {key}"); + protected long TakeLong(string key) => this.MaybeTakeLong(key) ?? throw new InvalidOperationException($"Section is missing an long integer entry named {key}"); + protected string TakeString(string key) => this.MaybeTakeString(key) ?? throw new InvalidOperationException($"Section is missing a string entry named {key}"); + protected List TakeMultiple(string key) => this.MaybeTakeMultiple(key) ?? throw new InvalidOperationException($"Section is missing a multiple entry named {key}"); + + protected static string ExtractString(string value) => value.Trim('"'); + protected static string ExtractEnumeration(string value) => value; + protected static int ExtractHexInteger(string value) => int.Parse(value.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + protected static long ExtractHexLong(string value) => long.Parse(value.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + protected static int ExtractInteger(string value) => int.Parse(value); + protected static long ExtractLong(string value) => long.Parse(value); + protected static List ExtractCompoundString(string value) => new(value.Split('+')); + protected static List ExtractCompoundInteger(string value) + { + var returned = new List(); + var elements = ExtractCompoundString(value); + foreach (var element in elements) + { + returned.Add(ExtractInteger(element)); + } + return returned; + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Segment.cs b/M6502/M6502.Symbols/Segment.cs new file mode 100644 index 0000000..cd76274 --- /dev/null +++ b/M6502/M6502.Symbols/Segment.cs @@ -0,0 +1,29 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + // seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137 + public class Segment : NamedSection + { + public int Start => this.TakeInteger("start"); + public int Size => this.TakeInteger("size"); + public string AddressSize => this.TakeString("addrsize"); + public string Type => this.TakeString("type"); + public string OName => this.TakeString("oname"); + public int OOFFS => this.TakeInteger("ooffs"); // ?? Offsets, perhaps? + + public Segment() + { + _ = this._hex_integer_keys.Add("start"); + _ = this._hex_integer_keys.Add("size"); + _ = this._enumeration_keys.Add("addrsize"); + _ = this._enumeration_keys.Add("type"); + _ = this._string_keys.Add("oname"); + _ = this._integer_keys.Add("ooffs"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Span.cs b/M6502/M6502.Symbols/Span.cs new file mode 100644 index 0000000..b3bc0c2 --- /dev/null +++ b/M6502/M6502.Symbols/Span.cs @@ -0,0 +1,25 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + //span id = 351, seg = 7, start = 0, size = 2, type = 2 + public class Span : IdentifiableSection + { + public Symbols.Segment Segment => this.TakeSegmentReference(); + public int Start => this.TakeInteger("start"); + public int Size => this.TakeInteger("size"); + public Symbols.Type Type => this.TakeTypeReference(); + + public Span() + { + _ = this._integer_keys.Add("seg"); + _ = this._integer_keys.Add("start"); + _ = this._integer_keys.Add("size"); + _ = this._integer_keys.Add("type"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Symbol.cs b/M6502/M6502.Symbols/Symbol.cs new file mode 100644 index 0000000..b1ccc80 --- /dev/null +++ b/M6502/M6502.Symbols/Symbol.cs @@ -0,0 +1,59 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + using System.Collections.Generic; + + // sym id = 16, name = "solve", addrsize = absolute, size = 274, scope = 0, def = 94,ref=144+17+351,val=0xF314,seg=6,type=lab + public class Symbol : NamedSection + { + public string AddressSize => this.TakeString("addrsize"); + + public int Size => this.TakeInteger("size"); + + public Symbols.Scope Scope => this.TakeScopeReference(); + + public List Definitions => this.TakeLineReferences("def"); // Guess + + public List References => this.TakeLineReferences("ref"); // Guess + + public int Value => this.TakeInteger("val"); + + public Symbols.Segment Segment => this.TakeSegmentReference(); + + public string Type => this.TakeString("type"); + + public Symbol() + { + _ = this._enumeration_keys.Add("addrsize"); + _ = this._integer_keys.Add("size"); + _ = this._integer_keys.Add("scope"); + _ = this._multiple_keys.Add("def"); + _ = this._multiple_keys.Add("ref"); + _ = this._hex_integer_keys.Add("val"); + _ = this._integer_keys.Add("seg"); + _ = this._enumeration_keys.Add("type"); + } + + public override void Parse(Parser parent, Dictionary entries) + { + base.Parse(parent, entries); + if (this.Type is "lab") + { + this._parent.Labels.Add(this); + } + else if (this.Type is "equ") + { + this._parent.Equates.Add(this); + } + else + { + throw new InvalidOperationException($"Unknown symbol type: {this.Type}"); + } + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Type.cs b/M6502/M6502.Symbols/Type.cs new file mode 100644 index 0000000..aad2952 --- /dev/null +++ b/M6502/M6502.Symbols/Type.cs @@ -0,0 +1,16 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + // type id = 0, val = "800920" + public class Type : IdentifiableSection + { + public string Value => this.TakeString("val"); + + public Type() => _ = this._string_keys.Add("val"); + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Symbols/Version.cs b/M6502/M6502.Symbols/Version.cs new file mode 100644 index 0000000..82c652a --- /dev/null +++ b/M6502/M6502.Symbols/Version.cs @@ -0,0 +1,22 @@ +namespace EightBit +{ + namespace Files + { + namespace Symbols + { + //version major = 2, minor = 0 + public class Version : Section + { + public int Major => this.TakeInteger("major"); + + public int Minor => this.TakeInteger("minor"); + + public Version() + { + _ = this._integer_keys.Add("major"); + _ = this._integer_keys.Add("minor"); + } + } + } + } +} \ No newline at end of file diff --git a/M6502/M6502.Test/M6502.Test.csproj b/M6502/M6502.Test/M6502.Test.csproj index 2d340d9..d613c14 100644 --- a/M6502/M6502.Test/M6502.Test.csproj +++ b/M6502/M6502.Test/M6502.Test.csproj @@ -21,6 +21,7 @@ + diff --git a/M6502/M6502.csproj b/M6502/M6502.csproj index 16b0421..34edfc5 100644 --- a/M6502/M6502.csproj +++ b/M6502/M6502.csproj @@ -25,15 +25,19 @@ + + + +