Transition entirely to new symbols framework and wire profiler.

This commit is contained in:
Adrian Conlon 2024-06-05 23:52:35 +01:00
parent 81e9068310
commit 93e1e903f6
8 changed files with 105 additions and 261 deletions

View File

@ -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

View File

@ -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; }

View File

@ -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");
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -6,32 +6,29 @@
private readonly int[] addressProfiles;
private readonly int[] addressCounts;
private readonly string[] addressScopes;
private readonly Dictionary<string, int> 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<ProfileScopeEventArgs>? 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;
}
}

View File

@ -1,111 +0,0 @@
// <copyright file="Symbols.cs" company="Adrian Conlon">
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
namespace EightBit
{
using System.Globalization;
public class Symbols
{
private readonly Dictionary<string, Dictionary<string, Dictionary<string, string>>> 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<ushort, string> Labels { get; } = [];
public Dictionary<ushort, string> Constants { get; } = [];
public Dictionary<string, ushort> Scopes { get; } = [];
public Dictionary<string, ushort> 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<string, string>();
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;
}
}
}
}
}
}