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