Add MC6809 profiler (mainly so I can pin down problem printing numbers)

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2019-05-07 00:46:30 +01:00
parent d21afdc1c7
commit c4d52512b2
7 changed files with 198 additions and 21 deletions

View File

@ -52,25 +52,20 @@
////private static string Dump_RelativeValue(Register16 value) => Dump_RelativeValue(value);
private string Disassemble(ushort current)
public string Disassemble(ushort current)
{
if (this.CPU.Powered)
this.address = current;
if (this.prefix10)
{
this.address = current;
if (this.prefix10)
{
return this.Disassemble10();
}
if (this.prefix11)
{
return this.Disassemble11();
}
return this.DisassembleUnprefixed();
return this.Disassemble10();
}
return string.Empty;
if (this.prefix11)
{
return this.Disassemble11();
}
return this.DisassembleUnprefixed();
}
private string Disassemble(int current) => this.Disassemble((ushort)current);
@ -764,8 +759,8 @@
var type8 = (reg1 & (byte)Bits.Bit3) != 0; // 8 bit?
return type8
? $"{ReferenceTransfer8(reg1)},{ReferenceTransfer8(reg2)}"
: $"{ReferenceTransfer16(reg1)},{ReferenceTransfer16(reg2)}";
? $"{output}{ReferenceTransfer8(reg1)},{ReferenceTransfer8(reg2)}"
: $"{output}{ReferenceTransfer16(reg1)},{ReferenceTransfer16(reg2)}";
}
//
@ -781,7 +776,7 @@
private string PulX(string mnemomic, string upon)
{
var data = this.GetByte(++this.address);
var output = $"data:x2\t{mnemomic}\t";
var output = $"{data:x2}\t{mnemomic}\t";
var registers = new List<string>();
if ((data & (byte)Bits.Bit0) != 0)
@ -830,7 +825,7 @@
private string PshX(string mnemomic, string upon)
{
var data = this.GetByte(++this.address);
var output = $"data:x2\t{mnemomic}\t";
var output = $"{data:x2}\t{mnemomic}\t";
var registers = new List<string>();
if ((data & (byte)Bits.Bit7) != 0)

View File

@ -15,6 +15,7 @@ namespace EightBit
private readonly Rom rom = new Rom(0x4000); // C000 - FFFF, 16K ROM
private readonly Disassembler disassembler;
private readonly Profiler profiler;
private ulong totalCycleCount = 0UL;
private long frameCycleCount = 0L;
@ -28,6 +29,7 @@ namespace EightBit
this.configuration = configuration;
this.CPU = new MC6809(this);
this.disassembler = new Disassembler(this, this.CPU);
this.profiler = new Profiler(this, this.CPU, this.disassembler);
}
public MC6809 CPU { get; }
@ -78,6 +80,8 @@ namespace EightBit
public override void LowerPOWER()
{
////this.profiler.Generate();
this.ACIA.LowerPOWER();
this.CPU.LowerPOWER();
base.LowerPOWER();
@ -109,6 +113,9 @@ namespace EightBit
// Early termination condition for CPU timing code
this.CPU.ExecutedInstruction += this.CPU_ExecutedInstruction_Termination;
}
////this.profiler.Enable();
////this.profiler.EmitLine += this.Profiler_EmitLine;
}
// Marshal data from ACIA -> memory
@ -137,6 +144,13 @@ namespace EightBit
}
}
private void Profiler_EmitLine(object sender, ProfileLineEventArgs e)
{
var cycles = e.Cycles;
var disassembled = e.Source;
System.Console.Error.WriteLine(disassembled);
}
private void CPU_ExecutedInstruction_Termination(object sender, EventArgs e)
{
this.totalCycleCount += (ulong)this.CPU.Cycles;
@ -151,14 +165,14 @@ namespace EightBit
if (!this.ignoreDisassembly)
{
var disassembled = $"{this.disassembler.Trace(this.disassembleAt)}\t{this.ACIA.DumpStatus()}";
System.Console.Out.WriteLine(disassembled);
System.Console.Error.WriteLine(disassembled);
}
}
private void CPU_ExecutingInstruction(object sender, EventArgs e)
{
this.disassembleAt = this.CPU.PC.Word;
this.ignoreDisassembly = this.disassembler.Ignore;
this.ignoreDisassembly = this.disassembler.Ignore || this.disassembler.Pause;
}
private void CPU_ExecutedInstruction(object sender, EventArgs e)
@ -169,6 +183,11 @@ namespace EightBit
if (System.Console.KeyAvailable)
{
var key = System.Console.ReadKey(true);
if (key.Key == ConsoleKey.F12)
{
this.LowerPOWER();
}
this.ACIA.RDR = System.Convert.ToByte(key.KeyChar);
this.ACIA.MarkReceiveStarting();
}

View File

@ -42,6 +42,10 @@
<ItemGroup>
<Compile Include="Disassembler.cs" />
<Compile Include="MC6809.cs" />
<Compile Include="ProfileEventArgs.cs" />
<Compile Include="ProfileLineEventArgs.cs" />
<Compile Include="Profiler.cs" />
<Compile Include="ProfileScopeEventArgs.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StatusBits.cs" />
</ItemGroup>

View File

@ -0,0 +1,11 @@
namespace EightBit
{
using System;
public class ProfileEventArgs : EventArgs
{
public ProfileEventArgs(string output) => this.Output = output;
public string Output { get; }
}
}

View File

@ -0,0 +1,17 @@
namespace EightBit
{
using System;
public class ProfileLineEventArgs : EventArgs
{
public ProfileLineEventArgs(string source, ulong cycles)
{
this.Source = source;
this.Cycles = cycles;
}
public string Source { get; }
public ulong Cycles { get; }
}
}

View File

@ -0,0 +1,20 @@
namespace EightBit
{
using System;
public class ProfileScopeEventArgs : EventArgs
{
public ProfileScopeEventArgs(string scope, ulong cycles, ulong count)
{
this.Scope = scope;
this.Cycles = cycles;
this.Count = count;
}
public string Scope { get; }
public ulong Cycles { get; }
public ulong Count { get; }
}
}

111
MC6809/Profiler.cs Normal file
View File

@ -0,0 +1,111 @@
namespace EightBit
{
using System;
public sealed class Profiler
{
private readonly ulong[] instructionCounts;
private readonly ulong[] addressProfiles;
private readonly ulong[] addressCounts;
private ushort address;
private readonly Bus board;
private readonly MC6809 processor;
private readonly Disassembler disassembler;
public Profiler(Bus board, MC6809 processor, Disassembler disassembler)
{
this.board = board;
this.processor = processor;
this.disassembler = disassembler;
this.instructionCounts = new ulong[0x10000];
this.addressProfiles = new ulong[0x10000];
this.addressCounts = new ulong[0x10000];
}
public event EventHandler<EventArgs> StartingOutput;
public event EventHandler<EventArgs> FinishedOutput;
public event EventHandler<EventArgs> StartingLineOutput;
public event EventHandler<EventArgs> FinishedLineOutput;
public event EventHandler<ProfileLineEventArgs> EmitLine;
public void Enable()
{
this.processor.ExecutingInstruction += this.Processor_ExecutingInstruction;
this.processor.ExecutedInstruction += this.Processor_ExecutedInstruction;
}
public void Generate()
{
this.OnStartingOutput();
try
{
this.EmitProfileInformation();
}
finally
{
this.OnFinishedOutput();
}
}
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 address = (ushort)i;
// Dump a profile/disassembly line
var disassembled = this.disassembler.Disassemble(address);
var output = $"{address:x4}|{disassembled}";
this.OnEmitLine(output, cycles);
}
}
}
finally
{
this.OnFinishedLineOutput();
}
}
private void Processor_ExecutingInstruction(object sender, EventArgs e) => this.address = this.processor.PC.Word;
private void Processor_ExecutedInstruction(object sender, EventArgs e)
{
ushort opcode = this.board.Peek(this.address);
if (opcode == 0x10 || opcode == 0x11)
{
opcode *= 0x100;
opcode += this.board.Peek((ushort)(this.address + 1));
}
this.addressCounts[this.address]++;
this.instructionCounts[opcode]++;
this.addressProfiles[this.address] += (ulong)this.processor.Cycles;
}
private void OnStartingOutput() => this.StartingOutput?.Invoke(this, EventArgs.Empty);
private void OnFinishedOutput() => this.FinishedOutput?.Invoke(this, EventArgs.Empty);
private void OnStartingLineOutput() => this.StartingLineOutput?.Invoke(this, EventArgs.Empty);
private void OnFinishedLineOutput() => this.FinishedLineOutput?.Invoke(this, EventArgs.Empty);
private void OnEmitLine(string source, ulong cycles) => this.EmitLine?.Invoke(this, new ProfileLineEventArgs(source, cycles));
}
}