feat(analysis): add instruction info and call graph node types
This commit is contained in:
1 parent
95dcacd18a
commit
a3e9a99c27
2 files changed
+149
No files matched your search
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Chip8.Analysis;
|
||||
|
||||
public sealed class CallNode
|
||||
{
|
||||
readonly SortedSet<ushort> calleeSet = new();
|
||||
readonly SortedSet<ushort> callerSet = new();
|
||||
|
||||
public CallNode(ushort address, bool isEntry)
|
||||
{
|
||||
this.address = address;
|
||||
this.isEntry = isEntry;
|
||||
}
|
||||
|
||||
public ushort address { get; }
|
||||
public bool isEntry { get; }
|
||||
public bool isRecursive { get; internal set; }
|
||||
|
||||
public IReadOnlyCollection<ushort> callees => calleeSet;
|
||||
public IReadOnlyCollection<ushort> callers => callerSet;
|
||||
|
||||
public int outDegree => calleeSet.Count;
|
||||
public int inDegree => callerSet.Count;
|
||||
|
||||
public bool isLeaf => calleeSet.Count == 0;
|
||||
public bool isSelfRecursive => calleeSet.Contains(address);
|
||||
|
||||
internal void addCallee(ushort callee) => calleeSet.Add(callee);
|
||||
|
||||
internal void addCaller(ushort caller) => callerSet.Add(caller);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string label = "sub@0x" + address.ToString("X4", CultureInfo.InvariantCulture);
|
||||
if (isEntry)
|
||||
{
|
||||
label += " (entry)";
|
||||
}
|
||||
if (isRecursive)
|
||||
{
|
||||
label += " (recursive)";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Chip8.Analysis;
|
||||
|
||||
public sealed class InstructionInfo
|
||||
{
|
||||
public InstructionInfo(
|
||||
ushort address,
|
||||
ushort opcode,
|
||||
ushort? extraWord,
|
||||
int byteLength,
|
||||
InstructionKind kind,
|
||||
InstructionCategory category,
|
||||
FlowEffect flow,
|
||||
string mnemonic)
|
||||
{
|
||||
this.address = address;
|
||||
this.opcode = opcode;
|
||||
this.extraWord = extraWord;
|
||||
this.byteLength = byteLength;
|
||||
this.kind = kind;
|
||||
this.category = category;
|
||||
this.flow = flow;
|
||||
this.mnemonic = mnemonic;
|
||||
fields = new OpcodeFields(opcode);
|
||||
}
|
||||
|
||||
public ushort address { get; }
|
||||
public ushort opcode { get; }
|
||||
public ushort? extraWord { get; }
|
||||
public int byteLength { get; }
|
||||
public InstructionKind kind { get; }
|
||||
public InstructionCategory category { get; }
|
||||
public FlowEffect flow { get; }
|
||||
public string mnemonic { get; }
|
||||
public OpcodeFields fields { get; }
|
||||
|
||||
public int x => fields.x;
|
||||
public int y => fields.y;
|
||||
public int n => fields.n;
|
||||
public int nn => fields.nn;
|
||||
public ushort nnn => fields.nnn;
|
||||
|
||||
public ushort nextAddress => (ushort)(address + byteLength);
|
||||
|
||||
public bool isTerminator =>
|
||||
flow is FlowEffect.Jump
|
||||
or FlowEffect.Branch
|
||||
or FlowEffect.Call
|
||||
or FlowEffect.Return
|
||||
or FlowEffect.IndirectJump
|
||||
or FlowEffect.Terminate;
|
||||
|
||||
public bool isBranch => flow == FlowEffect.Branch;
|
||||
|
||||
public bool fallsThrough => flow is FlowEffect.Fallthrough or FlowEffect.Branch or FlowEffect.Call;
|
||||
|
||||
public bool hasStaticTarget => flow is FlowEffect.Jump or FlowEffect.Call;
|
||||
|
||||
public bool writesMemory =>
|
||||
kind is InstructionKind.StoreRegisters
|
||||
or InstructionKind.StoreBcd
|
||||
or InstructionKind.SaveRange;
|
||||
|
||||
public bool readsMemory =>
|
||||
kind is InstructionKind.LoadRegisters or InstructionKind.LoadRange;
|
||||
|
||||
public bool loadsIndex => kind is InstructionKind.LoadIndex or InstructionKind.LoadLong;
|
||||
|
||||
public ushort? jumpTarget => kind == InstructionKind.Jump ? nnn : null;
|
||||
|
||||
public ushort? callTarget => kind == InstructionKind.Call ? nnn : null;
|
||||
|
||||
public ushort? indexValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (kind == InstructionKind.LoadIndex)
|
||||
{
|
||||
return nnn;
|
||||
}
|
||||
if (kind == InstructionKind.LoadLong)
|
||||
{
|
||||
return extraWord;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int? spriteRows => kind == InstructionKind.Draw ? (n == 0 ? 16 : n) : null;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string body = extraWord.HasValue
|
||||
? opcode.ToString("X4", CultureInfo.InvariantCulture) + " " + extraWord.Value.ToString("X4", CultureInfo.InvariantCulture)
|
||||
: opcode.ToString("X4", CultureInfo.InvariantCulture);
|
||||
return "0x" + address.ToString("X4", CultureInfo.InvariantCulture)
|
||||
+ " " + body.PadRight(9)
|
||||
+ " " + mnemonic.PadRight(18)
|
||||
+ " [" + kind + "/" + flow + "]";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user