diff --git a/M6502/Disassembler.cs b/M6502/Disassembler.cs index a34588a..3cb965d 100644 --- a/M6502/Disassembler.cs +++ b/M6502/Disassembler.cs @@ -7,11 +7,11 @@ namespace EightBit using System.Globalization; using System.Text; - public class Disassembler(Bus bus, M6502 processor, Symbols symbols) + public class Disassembler(Bus bus, M6502 processor, Files.Symbols.Parser symbols) { private readonly Bus bus = bus; private readonly M6502 processor = processor; - private readonly Symbols symbols = symbols; + private readonly Files.Symbols.Parser symbols = symbols; private ushort address; public static string DumpFlags(byte value) @@ -59,55 +59,6 @@ namespace EightBit switch (bbb) { case 0b000: // BRK - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - output.Append(this.Disassemble_Implied("BRK")); break; case 0b001: // DOP/NOP (0x04) @@ -552,23 +503,19 @@ namespace EightBit private string ConvertZPAddress(byte absolute) => this.TryGetLabel(absolute, out var label) ? label : "$" + DumpByteValue(absolute); - private bool TryGetLabel(ushort absolute, out string label) + private bool TryGetLabel(ushort absolute, out string name) { - return this.symbols.Labels.TryGetValue(absolute, value: out label); + return this.symbols.TryGetQualifiedLabel(absolute, out name); } - private string MayeGetLabel(ushort absolute) + private string MaybeGetLabel(ushort absolute) { - if (this.TryGetLabel(absolute, out var label)) - { - return label; - } - return string.Empty; + return this.symbols.MaybeGetQualifiedLabel(absolute); } private string MaybeGetCodeLabel(ushort absolute) { - var label = this.MayeGetLabel(absolute); + var label = this.MaybeGetLabel(absolute); if (string.IsNullOrEmpty(label)) { return string.Empty; @@ -578,20 +525,25 @@ namespace EightBit private string MaybeGetCodeLabel() { - return Pad(this.MaybeGetCodeLabel(this.address), 20); + return Pad(this.MaybeGetCodeLabel(this.address), 30); } #endregion #region Constants conversion + private bool TryGetConstant(ushort value, out string name) + { + return this.symbols.TryGetQualifiedEquate(value, out name); + } + private string ConvertConstantWord(ushort address) => this.ConvertConstant(this.GetWord(address)); - private string ConvertConstant(ushort constant) => this.symbols.Constants.TryGetValue(constant, out var label) ? label : this.Dump_DByte(constant); + private string ConvertConstant(ushort constant) => this.TryGetConstant(constant, out var label) ? label : this.Dump_DByte(constant); private string ConvertConstantByte(ushort address) => this.ConvertConstant(this.GetByte(address)); - private string ConvertConstant(byte constant) => this.symbols.Constants.TryGetValue(constant, out var label) ? label : "$" + DumpByteValue(constant); + private string ConvertConstant(byte constant) => this.TryGetConstant(constant, out var label) ? label : "$" + DumpByteValue(constant); #endregion diff --git a/M6502/M6502.HarteTest/Checker.cs b/M6502/M6502.HarteTest/Checker.cs index 402bab2..305fdd2 100644 --- a/M6502/M6502.HarteTest/Checker.cs +++ b/M6502/M6502.HarteTest/Checker.cs @@ -4,7 +4,7 @@ { private TestRunner Runner { get; } - private EightBit.Symbols Symbols { get; } = new(); + private EightBit.Files.Symbols.Parser Symbols { get; } = new(); private EightBit.Disassembler Disassembler { get; } diff --git a/M6502/M6502.Test/Board.cs b/M6502/M6502.Test/Board.cs index 0537513..1687fdb 100644 --- a/M6502/M6502.Test/Board.cs +++ b/M6502/M6502.Test/Board.cs @@ -4,6 +4,7 @@ namespace M6502.Test { + using System.Globalization; using System.Text; using EightBit; @@ -11,8 +12,9 @@ namespace M6502.Test { private readonly Configuration configuration; private readonly Ram ram; - private readonly Symbols symbols; + private readonly EightBit.Files.Symbols.Parser symbols; private readonly Disassembler disassembler; + private readonly Profiler profiler; private readonly MemoryMapping mapping; private ushort oldPC; @@ -27,11 +29,26 @@ namespace M6502.Test this.configuration = configuration; this.ram = new Ram(0x10000); this.CPU = new M6502(this); - this.symbols = new Symbols(this.configuration.RomDirectory + "/" + this.configuration.Symbols); + this.symbols = new EightBit.Files.Symbols.Parser(); this.disassembler = new Disassembler(this, this.CPU, this.symbols); this.mapping = new MemoryMapping(this.ram, 0x0000, (ushort)Mask.Sixteen, AccessLevel.ReadWrite); this.oldPC = (ushort)Mask.Sixteen; + + this.symbols.Parse(string.IsNullOrEmpty(this.configuration.Symbols) ? string.Empty : this.configuration.RomDirectory + "/" + this.configuration.Symbols); + + this.profiler = new Profiler(this.CPU, this.disassembler, this.symbols, this.configuration.Profile, this.configuration.Profile); + if (this.configuration.Profile) + { + this.profiler.StartingOutput += this.Profiler_StartingOutput; + this.profiler.FinishedOutput += this.Profiler_FinishedOutput; + this.profiler.StartingLineOutput += this.Profiler_StartingLineOutput; + this.profiler.FinishedLineOutput += this.Profiler_FinishedLineOutput; + this.profiler.StartingScopeOutput += this.Profiler_StartingScopeOutput; + this.profiler.FinishedScopeOutput += this.Profiler_FinishedScopeOutput; + this.profiler.EmitLine += this.Profiler_EmitLine; + this.profiler.EmitScope += this.Profiler_EmitScope; + } } public M6502 CPU { get; } @@ -51,6 +68,10 @@ namespace M6502.Test { this.CPU.LowerPOWER(); base.LowerPOWER(); + if (this.configuration.Profile) + { + this.profiler.Generate(); + } } public override void Initialize() @@ -200,7 +221,49 @@ namespace M6502.Test output.Append(this.disassembler.Disassemble(address)); - System.Console.Out.WriteLine(output.ToString()); + Console.Out.WriteLine(output.ToString()); + } + + private void Profiler_EmitScope(object? sender, ProfileScopeEventArgs e) + { + var proportion = (double)e.Cycles / this.CPU.Cycles; + Console.Out.Write(string.Format(CultureInfo.InvariantCulture, "\t[{0:P2}][{1:d9}][{2:d9}]\t{3}\n", proportion, e.Cycles, e.Count, e.Scope)); + } + + private void Profiler_EmitLine(object? sender, ProfileLineEventArgs e) + { + var proportion = (double)e.Cycles / this.profiler.TotalCycleCount; + Console.Out.Write(string.Format(CultureInfo.InvariantCulture, "\t[{0:P2}][{1:d9}]\t{2}\n", proportion, e.Cycles, e.Source)); + } + + private void Profiler_FinishedScopeOutput(object? sender, EventArgs e) + { + Console.Out.Write("Finished profiler scope output...\n"); + } + + private void Profiler_StartingScopeOutput(object? sender, EventArgs e) + { + Console.Out.Write("Starting profiler scope output...\n"); + } + + private void Profiler_FinishedLineOutput(object? sender, EventArgs e) + { + Console.Out.Write("Finished profiler line output...\n"); + } + + private void Profiler_StartingLineOutput(object? sender, EventArgs e) + { + Console.Out.Write("Starting profiler line output...\n"); + } + + private void Profiler_FinishedOutput(object? sender, EventArgs e) + { + Console.Out.Write("Finished profiler output...\n"); + } + + private void Profiler_StartingOutput(object? sender, EventArgs e) + { + Console.Out.Write("Starting profiler output...\n"); } } } diff --git a/M6502/ProfileEventArgs.cs b/M6502/ProfileEventArgs.cs index 66dd544..5dafc15 100644 --- a/M6502/ProfileEventArgs.cs +++ b/M6502/ProfileEventArgs.cs @@ -2,14 +2,6 @@ { public class ProfileEventArgs(string output) : EventArgs { - private readonly string output = output; - - public string Output - { - get - { - return this.output; - } - } + public string Output { get; } = output; } } \ No newline at end of file diff --git a/M6502/ProfileLineEventArgs.cs b/M6502/ProfileLineEventArgs.cs index 4148c58..c245f8a 100644 --- a/M6502/ProfileLineEventArgs.cs +++ b/M6502/ProfileLineEventArgs.cs @@ -2,23 +2,8 @@ { public class ProfileLineEventArgs(string source, int cycles) : EventArgs { - private readonly string source = source; - private readonly int cycles = cycles; + public string Source { get; } = source; - public string Source - { - get - { - return this.source; - } - } - - public int Cycles - { - get - { - return this.cycles; - } - } + public int Cycles { get; } = cycles; } } \ No newline at end of file diff --git a/M6502/ProfileScopeEventArgs.cs b/M6502/ProfileScopeEventArgs.cs index 16b3f80..689cba4 100644 --- a/M6502/ProfileScopeEventArgs.cs +++ b/M6502/ProfileScopeEventArgs.cs @@ -2,32 +2,10 @@ { public class ProfileScopeEventArgs(string scope, int cycles, int count) : EventArgs { - private readonly string scope = scope; - private readonly int cycles = cycles; - private readonly int count = count; + public string Scope { get; } = scope; - public string Scope - { - get - { - return this.scope; - } - } + public int Cycles { get; } = cycles; - public int Cycles - { - get - { - return this.cycles; - } - } - - public int Count - { - get - { - return this.count; - } - } + public int Count { get; } = count; } } \ No newline at end of file diff --git a/M6502/Profiler.cs b/M6502/Profiler.cs index 7757413..53b04d0 100644 --- a/M6502/Profiler.cs +++ b/M6502/Profiler.cs @@ -6,32 +6,29 @@ private readonly int[] addressProfiles; private readonly int[] addressCounts; - private readonly string[] addressScopes; private readonly Dictionary scopeCycles; private readonly M6502 processor; private readonly Disassembler disassembler; - private readonly Symbols symbols; - - private readonly bool countInstructions; - private readonly bool profileAddresses; + private readonly Files.Symbols.Parser symbols; private int priorCycleCount; private ushort executingAddress; - public Profiler(M6502 processor, Disassembler disassembler, Symbols symbols, bool countInstructions, bool profileAddresses) + public Profiler(M6502 processor, Disassembler disassembler, Files.Symbols.Parser symbols, bool countInstructions, bool profileAddresses) { ArgumentNullException.ThrowIfNull(processor); + ArgumentNullException.ThrowIfNull(disassembler); + ArgumentNullException.ThrowIfNull(symbols); this.processor = processor; this.disassembler = disassembler; this.symbols = symbols; - this.countInstructions = countInstructions; - this.profileAddresses = profileAddresses; if (profileAddresses || countInstructions) { this.processor.ExecutingInstruction += this.Processor_ExecutingInstruction_Prequel; + this.processor.ExecutedInstruction += this.Processor_ExecutedInstruction_Sequal; } if (profileAddresses) { @@ -48,10 +45,7 @@ this.addressProfiles = new int[0x10000]; this.addressCounts = new int[0x10000]; - this.addressScopes = new string[0x10000]; this.scopeCycles = []; - - this.BuildAddressScopes(); } @@ -71,6 +65,8 @@ public event EventHandler? EmitScope; + public long TotalCycleCount { get; private set; } + public void Generate() { this.OnStartingOutput(); @@ -116,7 +112,7 @@ { var name = scopeCycle.Key; var cycles = scopeCycle.Value; - var count = this.addressCounts[this.symbols.Addresses[name]]; + var count = this.addressCounts[this.symbols.LookupLabel(name).Value]; this.OnEmitScope(name, cycles, count); } } @@ -131,6 +127,11 @@ this.executingAddress = this.processor.PC.Word; } + private void Processor_ExecutedInstruction_Sequal(object? sender, EventArgs e) + { + this.TotalCycleCount += this.processor.Cycles; + } + private void Processor_ExecutingInstruction_ProfileAddresses(object? sender, EventArgs e) { this.priorCycleCount = this.processor.Cycles; @@ -149,31 +150,15 @@ this.addressProfiles[address] += cycles; - var addressScope = this.addressScopes[address]; + var addressScope = this.symbols.LookupScope(address); if (addressScope != null) { - if (!this.scopeCycles.ContainsKey(addressScope)) + if (!this.scopeCycles.ContainsKey(addressScope.Name)) { - this.scopeCycles[addressScope] = 0; + this.scopeCycles[addressScope.Name] = 0; } - this.scopeCycles[addressScope] += cycles; - } - } - - private void BuildAddressScopes() - { - foreach (var label in this.symbols.Labels) - { - var key = label.Value; - if (this.symbols.Scopes.TryGetValue(key, out var scope)) - { - var address = label.Key; - for (ushort i = address; i < address + scope; ++i) - { - this.addressScopes[i] = key; - } - } + this.scopeCycles[addressScope.Name] += cycles; } } diff --git a/M6502/Symbols.cs b/M6502/Symbols.cs deleted file mode 100644 index e4f6100..0000000 --- a/M6502/Symbols.cs +++ /dev/null @@ -1,111 +0,0 @@ -// -// Copyright (c) Adrian Conlon. All rights reserved. -// - -namespace EightBit -{ - using System.Globalization; - - public class Symbols - { - private readonly Dictionary>> parsed = []; - - public Symbols() - : this(string.Empty) - { - } - - public Symbols(string? path) - { - ArgumentNullException.ThrowIfNull(path); - if (path.Length > 0) - { - this.Parse(path); - this.AssignSymbols(); - this.AssignScopes(); - } - } - - public Dictionary Labels { get; } = []; - - public Dictionary Constants { get; } = []; - - public Dictionary Scopes { get; } = []; - - public Dictionary Addresses { get; } = []; - - private void AssignScopes() - { - var parsedScopes = this.parsed["scope"]; - foreach (var parsedScopeElement in parsedScopes) - { - var parsedScope = parsedScopeElement.Value; - var name = parsedScope["name"]; - var trimmedName = name[1..^1]; - var size = parsedScope["size"]; - this.Scopes[trimmedName] = ushort.Parse(size, CultureInfo.InvariantCulture); - } - } - - private void AssignSymbols() - { - var symbols = this.parsed["sym"]; - foreach (var symbolElement in symbols) - { - var symbol = symbolElement.Value; - var name = symbol["name"]; - var trimmedName = name[1..^1]; - var value = symbol["val"][2..]; - var number = Convert.ToUInt16(value, 16); - var symbolType = symbol["type"]; - if (symbolType == "lab") - { - this.Labels[number] = trimmedName; - this.Addresses[trimmedName] = number; - } - else if (symbolType == "equ") - { - this.Constants[number] = trimmedName; - } - } - } - - private void Parse(string path) - { - using var reader = new StreamReader(path); - while (!reader.EndOfStream) - { - var line = reader.ReadLine(); - if (line == null) - break; - var lineElements = line.Split(' ', '\t'); - if (lineElements.Length == 2) - { - var type = lineElements[0]; - var dataElements = lineElements[1].Split(','); - var data = new Dictionary(); - foreach (var dataElement in dataElements) - { - var definition = dataElement.Split('='); - if (definition.Length == 2) - { - data[definition[0]] = definition[1]; - } - } - - if (data.ContainsKey("id")) - { - if (!this.parsed.ContainsKey(type)) - { - this.parsed[type] = []; - } - - var id = data["id"]; - data.Remove("id"); - this.parsed[type][id] = data; - } - } - } - } - } -}