feat(analysis): add ROM analyzer and profile aggregation
This commit is contained in:
1 parent
f7c48098ce
commit
05b0cfafbd
2 files changed
+201
No files matched your search
@@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Chip8.Core;
|
||||
|
||||
namespace Chip8.Analysis;
|
||||
|
||||
public sealed class RomAnalyzer
|
||||
{
|
||||
static readonly HashSet<InstructionKind> superChipMarkers = new()
|
||||
{
|
||||
InstructionKind.ScrollRight,
|
||||
InstructionKind.ScrollLeft,
|
||||
InstructionKind.ScrollDown,
|
||||
InstructionKind.Exit,
|
||||
InstructionKind.LowRes,
|
||||
InstructionKind.HiRes,
|
||||
InstructionKind.LoadBigFont,
|
||||
InstructionKind.StoreFlags,
|
||||
InstructionKind.LoadFlags,
|
||||
};
|
||||
|
||||
static readonly HashSet<InstructionKind> xoChipMarkers = new()
|
||||
{
|
||||
InstructionKind.LoadLong,
|
||||
InstructionKind.Plane,
|
||||
InstructionKind.Audio,
|
||||
InstructionKind.Pitch,
|
||||
InstructionKind.SaveRange,
|
||||
InstructionKind.LoadRange,
|
||||
InstructionKind.ScrollUp,
|
||||
};
|
||||
|
||||
public RomAnalyzer(ushort entry = InstructionDecoder.defaultBaseAddress, ushort baseAddress = InstructionDecoder.defaultBaseAddress)
|
||||
{
|
||||
this.entry = entry;
|
||||
this.baseAddress = baseAddress;
|
||||
}
|
||||
|
||||
public ushort entry { get; }
|
||||
public ushort baseAddress { get; }
|
||||
|
||||
public RomProfile analyze(byte[] program)
|
||||
{
|
||||
var cfg = ControlFlowGraph.build(program, entry, baseAddress);
|
||||
var callGraph = CallGraph.build(program, entry, baseAddress);
|
||||
|
||||
var histogram = new Dictionary<InstructionKind, int>();
|
||||
var superFeatures = new SortedSet<InstructionKind>();
|
||||
var xoFeatures = new SortedSet<InstructionKind>();
|
||||
int drawCount = 0;
|
||||
int callCount = 0;
|
||||
int jumpCount = 0;
|
||||
int skipCount = 0;
|
||||
|
||||
foreach (var block in cfg.blocksInOrder)
|
||||
{
|
||||
foreach (var info in block.instructions)
|
||||
{
|
||||
histogram[info.kind] = histogram.TryGetValue(info.kind, out int value) ? value + 1 : 1;
|
||||
|
||||
if (info.kind == InstructionKind.Draw)
|
||||
{
|
||||
drawCount++;
|
||||
if (info.n == 0)
|
||||
{
|
||||
superFeatures.Add(InstructionKind.Draw);
|
||||
}
|
||||
}
|
||||
else if (info.kind == InstructionKind.Call)
|
||||
{
|
||||
callCount++;
|
||||
}
|
||||
else if (info.kind == InstructionKind.Jump)
|
||||
{
|
||||
jumpCount++;
|
||||
}
|
||||
else if (info.category == InstructionCategory.Skip)
|
||||
{
|
||||
skipCount++;
|
||||
}
|
||||
|
||||
if (superChipMarkers.Contains(info.kind))
|
||||
{
|
||||
superFeatures.Add(info.kind);
|
||||
}
|
||||
if (xoChipMarkers.Contains(info.kind))
|
||||
{
|
||||
xoFeatures.Add(info.kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool usesXoChip = xoFeatures.Count > 0;
|
||||
bool usesSuperChip = superFeatures.Count > 0;
|
||||
Platform guessed = usesXoChip
|
||||
? Platform.XoChip
|
||||
: usesSuperChip ? Platform.SuperChip : Platform.Chip8;
|
||||
|
||||
return new RomProfile(
|
||||
program.Length,
|
||||
entry,
|
||||
cfg.instructionCount,
|
||||
cfg.blockCount,
|
||||
callGraph.subroutineCount,
|
||||
drawCount,
|
||||
callCount,
|
||||
jumpCount,
|
||||
skipCount,
|
||||
usesSuperChip,
|
||||
usesXoChip,
|
||||
guessed,
|
||||
histogram,
|
||||
superFeatures.ToList(),
|
||||
xoFeatures.ToList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Chip8.Core;
|
||||
|
||||
namespace Chip8.Analysis;
|
||||
|
||||
public sealed class RomProfile
|
||||
{
|
||||
public RomProfile(
|
||||
int size,
|
||||
ushort entry,
|
||||
int instructionCount,
|
||||
int blockCount,
|
||||
int subroutineCount,
|
||||
int drawCount,
|
||||
int callCount,
|
||||
int jumpCount,
|
||||
int skipCount,
|
||||
bool usesSuperChip,
|
||||
bool usesXoChip,
|
||||
Platform guessedPlatform,
|
||||
IReadOnlyDictionary<InstructionKind, int> kindHistogram,
|
||||
IReadOnlyList<InstructionKind> superChipFeatures,
|
||||
IReadOnlyList<InstructionKind> xoChipFeatures)
|
||||
{
|
||||
this.size = size;
|
||||
this.entry = entry;
|
||||
this.instructionCount = instructionCount;
|
||||
this.blockCount = blockCount;
|
||||
this.subroutineCount = subroutineCount;
|
||||
this.drawCount = drawCount;
|
||||
this.callCount = callCount;
|
||||
this.jumpCount = jumpCount;
|
||||
this.skipCount = skipCount;
|
||||
this.usesSuperChip = usesSuperChip;
|
||||
this.usesXoChip = usesXoChip;
|
||||
this.guessedPlatform = guessedPlatform;
|
||||
this.kindHistogram = kindHistogram;
|
||||
this.superChipFeatures = superChipFeatures;
|
||||
this.xoChipFeatures = xoChipFeatures;
|
||||
}
|
||||
|
||||
public int size { get; }
|
||||
public ushort entry { get; }
|
||||
public int instructionCount { get; }
|
||||
public int blockCount { get; }
|
||||
public int subroutineCount { get; }
|
||||
public int drawCount { get; }
|
||||
public int callCount { get; }
|
||||
public int jumpCount { get; }
|
||||
public int skipCount { get; }
|
||||
public bool usesSuperChip { get; }
|
||||
public bool usesXoChip { get; }
|
||||
public Platform guessedPlatform { get; }
|
||||
public IReadOnlyDictionary<InstructionKind, int> kindHistogram { get; }
|
||||
public IReadOnlyList<InstructionKind> superChipFeatures { get; }
|
||||
public IReadOnlyList<InstructionKind> xoChipFeatures { get; }
|
||||
|
||||
public int kindCount(InstructionKind kind) =>
|
||||
kindHistogram.TryGetValue(kind, out int value) ? value : 0;
|
||||
|
||||
public string render()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("rom profile\n");
|
||||
builder.Append(" size: ").Append(size).Append(" bytes\n");
|
||||
builder.Append(" entry: 0x").Append(entry.ToString("X4", CultureInfo.InvariantCulture)).Append('\n');
|
||||
builder.Append(" instructions: ").Append(instructionCount).Append('\n');
|
||||
builder.Append(" blocks: ").Append(blockCount).Append('\n');
|
||||
builder.Append(" subroutines: ").Append(subroutineCount).Append('\n');
|
||||
builder.Append(" draws: ").Append(drawCount).Append('\n');
|
||||
builder.Append(" calls: ").Append(callCount).Append('\n');
|
||||
builder.Append(" jumps: ").Append(jumpCount).Append('\n');
|
||||
builder.Append(" skips: ").Append(skipCount).Append('\n');
|
||||
builder.Append(" platform: ").Append(guessedPlatform).Append('\n');
|
||||
builder.Append(" histogram:\n");
|
||||
foreach (var pair in kindHistogram.OrderByDescending(entry => entry.Value).ThenBy(entry => entry.Key.ToString()))
|
||||
{
|
||||
builder.Append(" ").Append(pair.Key).Append(": ").Append(pair.Value).Append('\n');
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user