From a6c2b4f7d48fb7315b3d9242aabe1319c1aff3f1 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 25 Aug 2026 14:39:22 +0200 Subject: [PATCH] feat(analysis): add basic block model and SMC event type --- src/Chip8.Analysis/BasicBlock.cs | 90 ++++++++++++++++++++++++++++++++ src/Chip8.Analysis/SmcEvent.cs | 30 +++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/Chip8.Analysis/BasicBlock.cs create mode 100644 src/Chip8.Analysis/SmcEvent.cs diff --git a/src/Chip8.Analysis/BasicBlock.cs b/src/Chip8.Analysis/BasicBlock.cs new file mode 100644 index 0000000..7dacb7e --- /dev/null +++ b/src/Chip8.Analysis/BasicBlock.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Chip8.Analysis; + +public sealed class BasicBlock +{ + readonly List instructionList = new(); + readonly List successorList = new(); + readonly List predecessorList = new(); + + public BasicBlock(ushort startAddress) + { + this.startAddress = startAddress; + } + + public ushort startAddress { get; } + + public IReadOnlyList instructions => instructionList; + public IReadOnlyList successors => successorList; + public IReadOnlyList predecessors => predecessorList; + + public InstructionInfo terminator => instructionList[instructionList.Count - 1]; + + public InstructionInfo first => instructionList[0]; + + public ushort endAddress => terminator.address; + + public ushort nextAddress => terminator.nextAddress; + + public int byteLength + { + get + { + int total = 0; + foreach (var instruction in instructionList) + { + total += instruction.byteLength; + } + return total; + } + } + + public int instructionCount => instructionList.Count; + + public bool isReturn => terminator.flow == FlowEffect.Return; + + public bool isCall => terminator.flow == FlowEffect.Call; + + public bool isBranch => terminator.flow == FlowEffect.Branch; + + public bool isUnconditionalJump => terminator.flow == FlowEffect.Jump; + + public bool hasIndirectExit => terminator.flow == FlowEffect.IndirectJump; + + public bool isTerminal => successorList.Count == 0; + + internal void add(InstructionInfo instruction) => instructionList.Add(instruction); + + internal void addSuccessor(ControlFlowEdge edge) => successorList.Add(edge); + + internal void addPredecessor(ushort predecessor) + { + if (!predecessorList.Contains(predecessor)) + { + predecessorList.Add(predecessor); + } + } + + public string render() + { + var builder = new StringBuilder(); + builder.Append("block 0x").Append(startAddress.ToString("X4", CultureInfo.InvariantCulture)); + builder.Append(" (").Append(instructionCount).Append(" instr, ").Append(byteLength).Append(" bytes)\n"); + foreach (var instruction in instructionList) + { + builder.Append(" ").Append(instruction).Append('\n'); + } + foreach (var edge in successorList) + { + builder.Append(" => ").Append(edge).Append('\n'); + } + return builder.ToString(); + } + + public override string ToString() => + "block@0x" + startAddress.ToString("X4", CultureInfo.InvariantCulture) + + " [" + instructionCount + " instr]"; +} diff --git a/src/Chip8.Analysis/SmcEvent.cs b/src/Chip8.Analysis/SmcEvent.cs new file mode 100644 index 0000000..6b4cbbe --- /dev/null +++ b/src/Chip8.Analysis/SmcEvent.cs @@ -0,0 +1,30 @@ +using System.Globalization; + +namespace Chip8.Analysis; + +public sealed class SmcEvent +{ + public SmcEvent(int step, ushort writerAddress, ushort targetAddress, byte oldValue, byte newValue) + { + this.step = step; + this.writerAddress = writerAddress; + this.targetAddress = targetAddress; + this.oldValue = oldValue; + this.newValue = newValue; + } + + public int step { get; } + public ushort writerAddress { get; } + public ushort targetAddress { get; } + public byte oldValue { get; } + public byte newValue { get; } + + public override string ToString() + { + return "step " + step.ToString(CultureInfo.InvariantCulture) + + ": writer 0x" + writerAddress.ToString("X4", CultureInfo.InvariantCulture) + + " changed 0x" + targetAddress.ToString("X4", CultureInfo.InvariantCulture) + + " from 0x" + oldValue.ToString("X2", CultureInfo.InvariantCulture) + + " to 0x" + newValue.ToString("X2", CultureInfo.InvariantCulture); + } +}