mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2025-01-08 00:32:57 +00:00
Transition entirely to new symbols framework and wire profiler.
This commit is contained in:
parent
81e9068310
commit
93e1e903f6
@ -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
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
111
M6502/Symbols.cs
111
M6502/Symbols.cs
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user