diff --git a/M6502/CycleCountedEventArgs.cs b/M6502/CycleCountedEventArgs.cs new file mode 100644 index 0000000..8cb1eb4 --- /dev/null +++ b/M6502/CycleCountedEventArgs.cs @@ -0,0 +1,9 @@ +namespace EightBit +{ + public class CycleCountedEventArgs(long cycles, long count) : EventArgs + { + public long Cycles { get; } = cycles; + + public long Count { get; } = count; + } +} \ No newline at end of file diff --git a/M6502/M6502.Test/Board.cs b/M6502/M6502.Test/Board.cs index 0b66620..51954ff 100644 --- a/M6502/M6502.Test/Board.cs +++ b/M6502/M6502.Test/Board.cs @@ -95,8 +95,11 @@ namespace M6502.Test this.profiler.FinishedLineOutput += this.Profiler_FinishedLineOutput; this.profiler.StartingScopeOutput += this.Profiler_StartingScopeOutput; this.profiler.FinishedScopeOutput += this.Profiler_FinishedScopeOutput; + this.profiler.StartingInstructionOutput += this.Profiler_StartingInstructionOutput; + this.profiler.FinishedInstructionOutput += this.Profiler_FinishedInstructionOutput; this.profiler.EmitLine += this.Profiler_EmitLine; this.profiler.EmitScope += this.Profiler_EmitScope; + this.profiler.EmitInstruction += this.Profiler_EmitInstruction; } this.Poke(0x00, 0x4c); @@ -203,7 +206,7 @@ namespace M6502.Test private void Profiler_EmitScope(object? sender, ProfileScopeEventArgs e) { - var proportion = (double)e.Cycles / this.profiler.TotalCycleCount; + var proportion = (double)e.Cycles / this.profiler.TotalCycles; var scope = this.symbols.LookupScopeByID(e.ID); Debug.Assert(scope != null); Console.Out.Write(string.Format(CultureInfo.InvariantCulture, "\t[{0:P2}][{1:d9}][{2:d9}]\t{3}\n", proportion, e.Cycles, e.Count, scope.Name)); @@ -211,10 +214,16 @@ namespace M6502.Test private void Profiler_EmitLine(object? sender, ProfileLineEventArgs e) { - var proportion = (double)e.Cycles / this.profiler.TotalCycleCount; + var proportion = (double)e.Cycles / this.profiler.TotalCycles; Console.Out.Write(string.Format(CultureInfo.InvariantCulture, "\t[{0:P2}][{1:d9}][{2:d9}]\t{3}\n", proportion, e.Cycles, e.Count, e.Source)); } + private void Profiler_EmitInstruction(object? sender, ProfileInstructionEventArgs e) + { + var proportion = (double)e.Cycles / this.profiler.TotalCycles; + Console.Out.Write(string.Format(CultureInfo.InvariantCulture, "\t[{0:P2}][{1:d9}][{2:d9}]\t{3:X2}\n", proportion, e.Cycles, e.Count, e.Instruction)); + } + private void Profiler_FinishedScopeOutput(object? sender, EventArgs e) { Console.Out.Write("Finished profiler scope output...\n"); @@ -244,5 +253,15 @@ namespace M6502.Test { Console.Out.Write("Starting profiler output...\n"); } + + private void Profiler_FinishedInstructionOutput(object? sender, EventArgs e) + { + Console.Out.Write("Finished instruction output...\n"); + } + + private void Profiler_StartingInstructionOutput(object? sender, EventArgs e) + { + Console.Out.Write("Starting instruction output...\n"); + } } } diff --git a/M6502/ProfileInstructionEventArgs.cs b/M6502/ProfileInstructionEventArgs.cs new file mode 100644 index 0000000..adb4716 --- /dev/null +++ b/M6502/ProfileInstructionEventArgs.cs @@ -0,0 +1,11 @@ +namespace EightBit +{ + public class ProfileInstructionEventArgs(byte instruction, long cycles, long count) : EventArgs + { + public byte Instruction { get; } = instruction; + + public long Cycles { get; } = cycles; + + public long Count { get; } = count; + } +} \ No newline at end of file diff --git a/M6502/ProfileLineEventArgs.cs b/M6502/ProfileLineEventArgs.cs index 8667adb..97fce4b 100644 --- a/M6502/ProfileLineEventArgs.cs +++ b/M6502/ProfileLineEventArgs.cs @@ -1,11 +1,7 @@ namespace EightBit { - public class ProfileLineEventArgs(string source, int cycles, int count) : EventArgs + public class ProfileLineEventArgs(string source, long cycles, long count) : CycleCountedEventArgs(cycles, count) { public string Source { get; } = source; - - public int Cycles { get; } = cycles; - - public int Count { get; } = count; } } \ No newline at end of file diff --git a/M6502/ProfileScopeEventArgs.cs b/M6502/ProfileScopeEventArgs.cs index 883d4bd..b155e84 100644 --- a/M6502/ProfileScopeEventArgs.cs +++ b/M6502/ProfileScopeEventArgs.cs @@ -1,11 +1,7 @@ namespace EightBit { - public class ProfileScopeEventArgs(int id, int cycles, int count) : EventArgs + public class ProfileScopeEventArgs(int id, long cycles, long count) : CycleCountedEventArgs(cycles, count) { public int ID { get; } = id; - - public int Cycles { get; } = cycles; - - public int Count { get; } = count; } } \ No newline at end of file diff --git a/M6502/Profiler.cs b/M6502/Profiler.cs index 62feb85..9da5b5b 100644 --- a/M6502/Profiler.cs +++ b/M6502/Profiler.cs @@ -4,17 +4,22 @@ public sealed class Profiler { - private readonly int[] instructionCounts = new int[0x100]; - private readonly int[] addressProfiles = new int[0x10000]; - private readonly int[] addressCounts = new int[0x10000]; + private readonly long[] instructionCounts = new long[0x100]; + private readonly long[] instructionCycles = new long[0x100]; + private readonly long[] addressCycles = new long[0x10000]; + private readonly long[] addressCounts = new long[0x10000]; - private readonly Dictionary scopeCycles = []; // ID -> Cycles + private readonly Dictionary scopeCycles = []; // ID -> Cycles private readonly M6502 processor; private readonly Disassembler disassembler; private readonly Files.Symbols.Parser symbols; private ushort executingAddress; + private byte executingInstruction; + + private long totalCycles = -1; + private bool totalCyclesValid; public Profiler(M6502 processor, Disassembler disassembler, Files.Symbols.Parser symbols, bool activate) { @@ -43,14 +48,30 @@ public event EventHandler? EmitLine; + public event EventHandler? StartingInstructionOutput; + + public event EventHandler? FinishedInstructionOutput; + + public event EventHandler? EmitInstruction; + public event EventHandler? StartingScopeOutput; public event EventHandler? FinishedScopeOutput; public event EventHandler? EmitScope; - public long TotalCycleCount { get; private set; } - + public long TotalCycles + { + get + { + return this.totalCycles; + } + private set + { + Debug.Assert(!this.totalCyclesValid); + this.totalCycles = value; + } + } public void Generate() { this.OnStartingOutput(); @@ -66,31 +87,16 @@ private void EmitProfileInformation() { - this.OnStartingLineOutput(); - try - { - // For each memory address - for (var i = 0; i < 0x10000; ++i) - { - // If there are any cycles associated - var cycles = this.addressProfiles[i]; - if (cycles > 0) - { - var count = this.addressCounts[i]; - Debug.Assert(count > 0); - var address = (ushort)i; + this.TotalCycles = this.instructionCycles.Sum(); + this.totalCyclesValid = true; - // Dump a profile/disassembly line - var source = this.disassembler.Disassemble(address); - this.OnEmitLine(source, cycles, count); - } - } - } - finally - { - this.OnFinishedLineOutput(); - } + this.EmitProfileLineInformation(); + this.EmitProfileScopeInformation(); + this.EmitProfileInstructionInformation(); + } + private void EmitProfileScopeInformation() + { this.OnStartingScopeOutput(); try { @@ -108,10 +114,66 @@ } } + private void EmitProfileLineInformation() + { + this.OnStartingLineOutput(); + try + { + // For each memory address + for (var i = 0; i < 0x10000; ++i) + { + // If there are any cycles associated + var cycles = this.addressCycles[i]; + if (cycles > 0) + { + var count = this.addressCounts[i]; + Debug.Assert(count > 0); + var address = (ushort)i; + + // Dump a profile/disassembly line + var source = this.disassembler.Disassemble(address); + this.OnEmitLine(source, cycles, count); + } + } + } + finally + { + this.OnFinishedLineOutput(); + } + } + + private void EmitProfileInstructionInformation() + { + this.OnStartingInstructionOutput(); + try + { + // For each instruction + for (var i = 0; i < 0x100; ++i) + { + // If there are any cycles associated + var cycles = this.instructionCycles[i]; + if (cycles > 0) + { + var count = this.instructionCounts[i]; + Debug.Assert(count > 0); + var instruction = (byte)i; + + // Emit an instruction event + this.OnEmitInstruction(instruction, cycles, count); + } + } + } + finally + { + this.OnFinishedInstructionOutput(); + } + } + private void Processor_RaisingSYNC(object? sender, EventArgs e) { // Everything needs this this.executingAddress = this.processor.Bus.Address.Word; + this.executingInstruction = this.processor.Bus.Peek(this.executingAddress); ++this.addressCounts[this.executingAddress]; ++this.instructionCounts[this.processor.Bus.Data]; @@ -120,9 +182,9 @@ private void Processor_ExecutedInstruction(object? sender, EventArgs e) { var cycles = this.processor.Cycles; - this.TotalCycleCount += cycles; - this.addressProfiles[this.executingAddress] += cycles; + this.addressCycles[this.executingAddress] += cycles; + this.instructionCycles[this.executingInstruction] += cycles; var scope = this.symbols.LookupScopeByAddress(this.executingAddress); if (scope != null) @@ -154,6 +216,16 @@ this.FinishedLineOutput?.Invoke(this, EventArgs.Empty); } + private void OnStartingInstructionOutput() + { + this.StartingInstructionOutput?.Invoke(this, EventArgs.Empty); + } + + private void OnFinishedInstructionOutput() + { + this.FinishedInstructionOutput?.Invoke(this, EventArgs.Empty); + } + private void OnStartingScopeOutput() { this.StartingScopeOutput?.Invoke(this, EventArgs.Empty); @@ -164,14 +236,19 @@ this.FinishedScopeOutput?.Invoke(this, EventArgs.Empty); } - private void OnEmitLine(string source, int cycles, int count) + private void OnEmitLine(string source, long cycles, long count) { this.EmitLine?.Invoke(this, new ProfileLineEventArgs(source, cycles, count)); } - private void OnEmitScope(int id, int cycles, int count) + private void OnEmitScope(int id, long cycles, long count) { this.EmitScope?.Invoke(this, new ProfileScopeEventArgs(id, cycles, count)); } + + private void OnEmitInstruction(byte instruction, long cycles, long count) + { + this.EmitInstruction?.Invoke(this, new ProfileInstructionEventArgs(instruction, cycles, count)); + } } } \ No newline at end of file